Skip to content

Commit

Permalink
Add DHCP IP Retries in PrepareHNSNetwork
Browse files Browse the repository at this point in the history
To address the potential race condition issue where obtaining a DHCP IP address fails after CreateHNSNetwork,
we implemented a retry mechanism to wait for an available IP. If the DHCP IP cannot be acquired within three
seconds, a warning message will be returned.

Signed-off-by: Shuyang Xin <gavinx@vmware.com>
  • Loading branch information
XinShuYang committed Dec 21, 2023
1 parent 0c6d471 commit a765a81
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 20 deletions.
7 changes: 6 additions & 1 deletion pkg/agent/agent_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ func (i *Initializer) prepareHNSNetworkAndOVSExtension() error {
return err
}
i.nodeConfig.UplinkNetConfig.DNSServers = dnsServers
Dhcp, err := util.GetDHCPByInterfaceIndex(adapter.Index)
if err != nil {
return err
}
i.nodeConfig.UplinkNetConfig.DHCP = Dhcp
// Save routes which are configured on the uplink interface, and configure them on the management virtual adapter
// if Windows host doesn't move the configuration automatically.
if err = i.saveHostRoutes(); err != nil {
Expand All @@ -117,7 +122,7 @@ func (i *Initializer) prepareHNSNetworkAndOVSExtension() error {
if subnetCIDR == nil {
return fmt.Errorf("failed to find valid IPv4 PodCIDR")
}
return util.PrepareHNSNetwork(subnetCIDR, i.nodeConfig.NodeTransportIPv4Addr, adapter, i.nodeConfig.UplinkNetConfig.Gateway, dnsServers, i.nodeConfig.UplinkNetConfig.Routes, i.ovsBridge)
return util.PrepareHNSNetwork(subnetCIDR, i.nodeConfig.NodeTransportIPv4Addr, adapter, i.nodeConfig.UplinkNetConfig.Gateway, dnsServers, i.nodeConfig.UplinkNetConfig.Routes, i.ovsBridge, i.nodeConfig.UplinkNetConfig.DHCP)
}

func (i *Initializer) prepareVMNetworkAndOVSExtension() error {
Expand Down
1 change: 1 addition & 0 deletions pkg/agent/config/node_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ type AdapterNetConfig struct {
Routes []interface{}
// OFPort is the OpenFlow port number of the uplink interface allocated by OVS.
OFPort uint32
DHCP bool
}

type WireGuardConfig struct {
Expand Down
64 changes: 46 additions & 18 deletions pkg/agent/util/net_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ func ConfigureLinkAddresses(idx int, ipNets []*net.IPNet) error {
}

// PrepareHNSNetwork creates HNS Network for containers.
func PrepareHNSNetwork(subnetCIDR *net.IPNet, nodeIPNet *net.IPNet, uplinkAdapter *net.Interface, nodeGateway string, dnsServers string, routes []interface{}, newName string) error {
func PrepareHNSNetwork(subnetCIDR *net.IPNet, nodeIPNet *net.IPNet, uplinkAdapter *net.Interface, nodeGateway string, dnsServers string, routes []interface{}, newName string, dhcp bool) error {
klog.InfoS("Creating HNSNetwork", "name", LocalHNSNetwork, "subnet", subnetCIDR, "nodeIP", nodeIPNet, "adapter", uplinkAdapter)
hnsNet, err := CreateHNSNetwork(LocalHNSNetwork, subnetCIDR, nodeIPNet, uplinkAdapter)
if err != nil {
Expand All @@ -461,7 +461,7 @@ func PrepareHNSNetwork(subnetCIDR *net.IPNet, nodeIPNet *net.IPNet, uplinkAdapte
}
}()

adapter, ipFound, err := adapterIPExists(nodeIPNet.IP, uplinkAdapter.HardwareAddr, ContainerVNICPrefix)
adapter, ipFound, err := adapterIPExists(nodeIPNet.IP, uplinkAdapter.HardwareAddr, ContainerVNICPrefix, dhcp)
if err != nil {
return err
}
Expand Down Expand Up @@ -521,33 +521,45 @@ func PrepareHNSNetwork(subnetCIDR *net.IPNet, nodeIPNet *net.IPNet, uplinkAdapte

// adapterIPExists finds the network adapter configured with the provided IP, MAC and its name has the given prefix.
// If "namePrefix" is empty, it returns the first network adapter with the provided IP and MAC.
// If DHCP is enabled on the original adapter, it will attempt to acquire an IP multiple times.
// It returns true if the IP is found on the adapter, otherwise it returns false.
func adapterIPExists(ip net.IP, mac net.HardwareAddr, namePrefix string) (*net.Interface, bool, error) {
func adapterIPExists(ip net.IP, mac net.HardwareAddr, namePrefix string, dhcp bool) (*net.Interface, bool, error) {
adapters, err := netInterfaces()
if err != nil {
return nil, false, err
}
ipExists := false
for idx, adapter := range adapters {
if bytes.Equal(adapter.HardwareAddr, mac) {
if namePrefix == "" || strings.Contains(adapter.Name, namePrefix) {
addrList, err := netInterfaceAddrs(&adapters[idx])
if err != nil {
return nil, false, err
}
for _, addr := range addrList {
if ipNet, ok := addr.(*net.IPNet); ok {
if ipNet.IP.Equal(ip) {
ipExists = true
break
// To avoid the issue of insufficient time for the Windows adapter to obtain an dhcp IP after the creation of the HNS network,
// wait for up to three seconds and retry IP acquisition in case of failures.
for retry := 0; retry < 4; retry++ {
ipExists := false
for idx, adapter := range adapters {
if bytes.Equal(adapter.HardwareAddr, mac) {
if namePrefix == "" || strings.Contains(adapter.Name, namePrefix) {
addrList, err := netInterfaceAddrs(&adapters[idx])
if err != nil {
return nil, false, err
}
for _, addr := range addrList {
if ipNet, ok := addr.(*net.IPNet); ok {
if ipNet.IP.Equal(ip) {
ipExists = true
break
}
}
}
if ipExists {
return &adapter, true, nil
}
}
return &adapter, ipExists, nil
}
}
if dhcp == false {
return nil, false, fmt.Errorf("unable to find a network adapter with MAC %s, IP %s, and name prefix %s", mac.String(), ip.String(), namePrefix)
} else if !ipExists && retry < 4 {
time.Sleep(1 * time.Second)
}
}
return nil, false, fmt.Errorf("unable to find a network adapter with MAC %s, IP %s, and name prefix %s", mac.String(), ip.String(), namePrefix)
return nil, false, fmt.Errorf("warning: unable to find a network adapter with MAC %s, IP %s, and name prefix %s after retries", mac.String(), ip.String(), namePrefix)
}

// EnableRSCOnVSwitch enables RSC in the vSwitch to reduce host CPU utilization and increase throughput for virtual
Expand Down Expand Up @@ -619,6 +631,22 @@ func SetAdapterDNSServers(adapterName, dnsServers string) error {
return nil
}

// GetDNServersByInterfaceIndex returns the DNS servers configured on the specified interface.
func GetDHCPByInterfaceIndex(ifIndex int) (bool, error) {
cmd := fmt.Sprintf("$(Get-NetIPInterface -InterfaceIndex %d -AddressFamily IPv4).Dhcp", ifIndex)
Dhcp, err := runCommand(cmd)
if err != nil {
return false, err
}
Dhcp = strings.TrimSpace(Dhcp)
if Dhcp == "Enabled" {
klog.Infof("IPv4 DHCP is Enabled for Interface %d", ifIndex)
return true, nil
}
klog.Infof("IPv4 DHCP is Disabled for Interface%d", ifIndex)
return false, nil
}

// ListenLocalSocket creates a listener on a Unix domain socket or a Windows named pipe.
// - If the specified address starts with "\\.\pipe\", create a listener on the a Windows named pipe path.
// - Else create a listener on a local Unix domain socket.
Expand Down
2 changes: 1 addition & 1 deletion pkg/agent/util/net_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func TestPrepareHNSNetwork(t *testing.T) {
defer mockHNSNetworkCreate(tc.hnsNetworkCreateErr)()
defer mockHNSNetworkDelete(nil)()
defer mockAntreaNetIO(&antreasyscalltest.MockNetIO{CreateIPForwardEntryErr: tc.createRowErr})()
gotErr := PrepareHNSNetwork(testSubnetCIDR, tc.nodeIPNet, testUplinkAdapter, "testGateway", tc.dnsServers, testRoutes, tc.newName)
gotErr := PrepareHNSNetwork(testSubnetCIDR, tc.nodeIPNet, testUplinkAdapter, "testGateway", tc.dnsServers, testRoutes, tc.newName, false)
assert.Equal(t, tc.wantErr, gotErr)
})
}
Expand Down

0 comments on commit a765a81

Please sign in to comment.