Skip to content

Commit 8a56efb

Browse files
committed
leverage autoconfig ipv4 address for dhcp (tested)
1 parent 84acbc9 commit 8a56efb

File tree

3 files changed

+63
-51
lines changed

3 files changed

+63
-51
lines changed

cni/network/network.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,9 @@ func NewPlugin(name string,
132132

133133
nl := netlink.NewNetlink()
134134
plc := platform.NewExecClient(logger)
135+
netio := &netio.NetIO{}
135136
// Setup network manager.
136-
nm, err := network.NewNetworkManager(nl, plc, &netio.NetIO{}, network.NewNamespaceClient(), iptables.NewClient(), dhcp.New(logger, plc))
137+
nm, err := network.NewNetworkManager(nl, plc, netio, network.NewNamespaceClient(), iptables.NewClient(), dhcp.New(logger, netio))
137138
if err != nil {
138139
return nil, err
139140
}
@@ -145,7 +146,7 @@ func NewPlugin(name string,
145146
nm: nm,
146147
nnsClient: client,
147148
multitenancyClient: multitenancyClient,
148-
netClient: &netio.NetIO{},
149+
netClient: netio,
149150
}, nil
150151
}
151152

dhcp/dhcp.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,20 @@ type ExecClient interface {
5151
ExecuteCommand(ctx context.Context, command string, args ...string) (string, error)
5252
}
5353

54+
type NetIOClient interface {
55+
GetNetworkInterfaceByName(name string) (*net.Interface, error)
56+
GetNetworkInterfaceAddrs(iface *net.Interface) ([]net.Addr, error)
57+
}
58+
5459
type DHCP struct {
55-
logger *zap.Logger
56-
execClient ExecClient
60+
logger *zap.Logger
61+
netioClient NetIOClient
5762
}
5863

59-
func New(logger *zap.Logger, plc ExecClient) *DHCP {
64+
func New(logger *zap.Logger, netio NetIOClient) *DHCP {
6065
return &DHCP{
61-
logger: logger,
62-
execClient: plc,
66+
logger: logger,
67+
netioClient: netio,
6368
}
6469
}
6570

dhcp/dhcp_windows.go

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,18 @@ package dhcp
33
import (
44
"context"
55
"net"
6-
"regexp"
7-
"time"
86

9-
"github.com/Azure/azure-container-networking/cni/log"
107
"github.com/Azure/azure-container-networking/retry"
118
"github.com/pkg/errors"
129
"go.uber.org/zap"
1310
"golang.org/x/sys/windows"
1411
)
1512

1613
const (
17-
dummyIPAddressStr = "169.254.128.10"
18-
dummySubnetMask = "255.255.128.0"
19-
addIPAddressDelay = 4 * time.Second
20-
deleteIPAddressDelay = 2 * time.Second
21-
returnDelay = 8 * time.Second // time to wait before returning from DiscoverRequest
22-
retryCount = 5
23-
retryDelayMillis = 500
24-
socketTimeoutMillis = 1000
25-
)
26-
27-
var (
28-
dummyIPAddress = net.IPv4(169, 254, 128, 10) // nolint
29-
// matches if the string fully consists of zero or more alphanumeric, dots, dashes, parentheses, spaces, or underscores
30-
allowedInput = regexp.MustCompile(`^[a-zA-Z0-9._\-\(\) ]*$`)
14+
retryCount = 5
15+
retryDelayMillis = 500
16+
ipAssignRetryDelayMillis = 2000
17+
socketTimeoutMillis = 1000
3118
)
3219

3320
type Socket struct {
@@ -91,38 +78,57 @@ func (s *Socket) Close() error {
9178
return nil
9279
}
9380

94-
// issues a dhcp discover request on an interface by assigning an ip to that interface
95-
// then, sends a packet with that interface's dummy ip, and then unassigns the dummy ip
96-
func (c *DHCP) DiscoverRequest(ctx context.Context, macAddress net.HardwareAddr, ifName string) error {
97-
// validate interface name
98-
if !allowedInput.MatchString(ifName) {
99-
return errors.New("invalid dhcp discover request interface name")
81+
func (c *DHCP) getIPv4InterfaceAddresses(ifName string) ([]net.IP, error) {
82+
nic, err := c.netioClient.GetNetworkInterfaceByName(ifName)
83+
if err != nil {
84+
return []net.IP{}, err
10085
}
101-
// delete dummy ip off the interface if it already exists
102-
ret, err := c.execClient.ExecuteCommand(ctx, "netsh", "interface", "ipv4", "delete", "address", ifName, dummyIPAddressStr)
86+
addresses, err := c.netioClient.GetNetworkInterfaceAddrs(nic)
10387
if err != nil {
104-
c.logger.Info("Could not remove dummy ip, likely because it doesn't exist", zap.String("output", ret), zap.Error(log.NewErrorWithoutStackTrace(err)))
88+
return []net.IP{}, err
89+
}
90+
ret := []net.IP{}
91+
for _, address := range addresses {
92+
// check if the ip is ipv4 and parse it
93+
ip, _, err := net.ParseCIDR(address.String())
94+
if err != nil || ip.To4() == nil {
95+
continue
96+
}
97+
ret = append(ret, ip)
10598
}
106-
time.Sleep(deleteIPAddressDelay)
10799

108-
// create dummy ip so we can direct the packet to the correct interface
109-
ret, err = c.execClient.ExecuteCommand(ctx, "netsh", "interface", "ipv4", "add", "address", ifName, dummyIPAddressStr, dummySubnetMask)
100+
c.logger.Info("Interface addresses found", zap.Any("foundIPs", addresses), zap.Any("selectedIPs", ret))
101+
return ret, err
102+
}
103+
104+
func (c *DHCP) verifyIPv4InterfaceAddressCount(ifName string, count, maxRuns, sleepMs int) error {
105+
addressCountErr := retry.Do(func() error {
106+
addresses, err := c.getIPv4InterfaceAddresses(ifName)
107+
if err != nil || len(addresses) != count {
108+
return errors.New("address count found not equal to expected")
109+
}
110+
return nil
111+
}, maxRuns, sleepMs)
112+
return addressCountErr
113+
}
114+
115+
// issues a dhcp discover request on an interface by finding the secondary's ip and sending on its ip
116+
func (c *DHCP) DiscoverRequest(ctx context.Context, macAddress net.HardwareAddr, ifName string) error {
117+
// Find the ipv4 address of the secondary interface (we're betting that this gets autoconfigured)
118+
err := c.verifyIPv4InterfaceAddressCount(ifName, 1, retryCount, ipAssignRetryDelayMillis)
110119
if err != nil {
111-
return errors.Wrap(err, "failed to add dummy ip to interface: "+ret)
120+
return errors.Wrap(err, "failed to get auto ip config assigned in apipa range in time")
112121
}
113-
// ensure we always remove the dummy ip we added from the interface
114-
defer func() {
115-
// we always want to try to remove the dummy ip, even if the deadline was reached
116-
// so we have context.Background()
117-
ret, cleanupErr := c.execClient.ExecuteCommand(context.Background(), "netsh", "interface", "ipv4", "delete", "address", ifName, dummyIPAddressStr)
118-
if cleanupErr != nil {
119-
c.logger.Info("Failed to remove dummy ip on leaving function", zap.String("output", ret), zap.Error(err))
120-
}
121-
// wait for nic to retrieve autoconfiguration ip
122-
time.Sleep(returnDelay)
123-
}()
124-
// it takes time for the address to be assigned
125-
time.Sleep(addIPAddressDelay)
122+
ipv4Addresses, err := c.getIPv4InterfaceAddresses(ifName)
123+
if err != nil || len(ipv4Addresses) == 0 {
124+
return errors.Wrap(err, "failed to get ipv4 addresses on interface")
125+
}
126+
uniqueAddress := ipv4Addresses[0].To4()
127+
if uniqueAddress == nil {
128+
return errors.New("invalid ipv4 address")
129+
}
130+
uniqueAddressStr := uniqueAddress.String()
131+
c.logger.Info("Retrieved automatic ip configuration: ", zap.Any("ip", uniqueAddress), zap.String("ipStr", uniqueAddressStr))
126132

127133
// now begin the dhcp request
128134
txid, err := GenerateTransactionID()
@@ -132,7 +138,7 @@ func (c *DHCP) DiscoverRequest(ctx context.Context, macAddress net.HardwareAddr,
132138

133139
// Prepare an IP and UDP header
134140
raddr := &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpServerPort}
135-
laddr := &net.UDPAddr{IP: dummyIPAddress, Port: dhcpClientPort}
141+
laddr := &net.UDPAddr{IP: uniqueAddress, Port: dhcpClientPort}
136142

137143
dhcpDiscover, err := buildDHCPDiscover(macAddress, txid)
138144
if err != nil {

0 commit comments

Comments
 (0)