Skip to content

Commit 3bf4229

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

File tree

3 files changed

+63
-50
lines changed

3 files changed

+63
-50
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 & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,19 @@ package dhcp
33
import (
44
"context"
55
"net"
6-
"regexp"
76
"time"
87

9-
"github.com/Azure/azure-container-networking/cni/log"
108
"github.com/Azure/azure-container-networking/retry"
119
"github.com/pkg/errors"
1210
"go.uber.org/zap"
1311
"golang.org/x/sys/windows"
1412
)
1513

1614
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._\-\(\) ]*$`)
15+
retryCount = 5
16+
retryDelayMillis = 500
17+
ipAssignRetryDelayMillis = 2000
18+
socketTimeoutMillis = 1000
3119
)
3220

3321
type Socket struct {
@@ -91,38 +79,57 @@ func (s *Socket) Close() error {
9179
return nil
9280
}
9381

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")
82+
func (c *DHCP) getIPv4InterfaceAddresses(ifName string) ([]net.IP, error) {
83+
nic, err := c.netioClient.GetNetworkInterfaceByName(ifName)
84+
if err != nil {
85+
return []net.IP{}, err
10086
}
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)
87+
addresses, err := c.netioClient.GetNetworkInterfaceAddrs(nic)
10388
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)))
89+
return []net.IP{}, err
90+
}
91+
ret := []net.IP{}
92+
for _, address := range addresses {
93+
// check if the ip is ipv4 and parse it
94+
ip, _, err := net.ParseCIDR(address.String())
95+
if err != nil || ip.To4() == nil {
96+
continue
97+
}
98+
ret = append(ret, ip)
10599
}
106-
time.Sleep(deleteIPAddressDelay)
107100

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)
101+
c.logger.Info("Interface addresses found", zap.Any("foundIPs", addresses), zap.Any("selectedIPs", ret))
102+
return ret, err
103+
}
104+
105+
func (c *DHCP) verifyIPv4InterfaceAddressCount(ifName string, count, maxRuns, sleepMs int) error {
106+
addressCountErr := retry.Do(func() error {
107+
addresses, err := c.getIPv4InterfaceAddresses(ifName)
108+
if err != nil || len(addresses) != count {
109+
return errors.New("address count found not equal to expected")
110+
}
111+
return nil
112+
}, maxRuns, sleepMs)
113+
return addressCountErr
114+
}
115+
116+
// issues a dhcp discover request on an interface by finding the secondary's ip and sending on its ip
117+
func (c *DHCP) DiscoverRequest(ctx context.Context, macAddress net.HardwareAddr, ifName string) error {
118+
// Find the ipv4 address of the secondary interface (we're betting that this gets autoconfigured)
119+
err := c.verifyIPv4InterfaceAddressCount(ifName, 1, retryCount, ipAssignRetryDelayMillis)
110120
if err != nil {
111-
return errors.Wrap(err, "failed to add dummy ip to interface: "+ret)
121+
return errors.Wrap(err, "failed to get auto ip config assigned in apipa range in time")
112122
}
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)
123+
ipv4Addresses, err := c.getIPv4InterfaceAddresses(ifName)
124+
if err != nil || len(ipv4Addresses) == 0 {
125+
return errors.Wrap(err, "failed to get ipv4 addresses on interface")
126+
}
127+
uniqueAddress := ipv4Addresses[0].To4()
128+
if uniqueAddress == nil {
129+
return errors.New("invalid ipv4 address")
130+
}
131+
uniqueAddressStr := uniqueAddress.String()
132+
c.logger.Info("Retrieved automatic ip configuration: ", zap.Any("ip", uniqueAddress), zap.String("ipStr", uniqueAddressStr))
126133

127134
// now begin the dhcp request
128135
txid, err := GenerateTransactionID()
@@ -132,7 +139,7 @@ func (c *DHCP) DiscoverRequest(ctx context.Context, macAddress net.HardwareAddr,
132139

133140
// Prepare an IP and UDP header
134141
raddr := &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpServerPort}
135-
laddr := &net.UDPAddr{IP: dummyIPAddress, Port: dhcpClientPort}
142+
laddr := &net.UDPAddr{IP: uniqueAddress, Port: dhcpClientPort}
136143

137144
dhcpDiscover, err := buildDHCPDiscover(macAddress, txid)
138145
if err != nil {

0 commit comments

Comments
 (0)