Skip to content

Commit

Permalink
[Windows] Use IP and MAC to find virtual management adatper
Browse files Browse the repository at this point in the history
1. After creating HNSNetwork, Windows host creates a virtual management
   network adapter which takes over the uplink's IP and MAC. Originally
   the name with a format "vEthernet ($uplink_name)" is used to get the
   virtual adapter, but it might fail when the name is taken by other
   adapters. In this change, uses the uplink's IP and MAC to find the
   adpter, and uses the prefix "vEthernet" as a filter.
2. Remove the virtual adapter name from the name list to search the
   Windows Node transport interface's IP configuration in agent restart
   case. This is because the IP is finally moved to OVS bridge
   interface, which is renamed from the virtual network adapter. So in a
   restart case, a virtual network adapter with the name format "vEthernet ($uplink_name)" should not exist.

Signed-off-by: wenyingd <wenyingd@vmware.com>
  • Loading branch information
wenyingd committed Apr 14, 2022
1 parent d7b1eed commit 185e0c2
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 18 deletions.
6 changes: 3 additions & 3 deletions pkg/agent/agent_windows.go
Expand Up @@ -252,9 +252,9 @@ func (i *Initializer) restoreHostRoutes() error {
}

func GetTransportIPNetDeviceByName(ifaceName string, ovsBridgeName string) (*net.IPNet, *net.IPNet, *net.Interface, error) {
// Find transport Interface in the order: ifaceName -> "vEthernet (ifaceName)" -> br-int. Return immediately if
// an interface using the specified name exists. Using "vEthernet (ifaceName)" or br-int is for restart agent case.
for _, name := range []string{ifaceName, util.VirtualAdapterName(ifaceName), ovsBridgeName} {
// Find transport Interface in the order: ifaceName -> br-int. Return immediately if
// an interface using the specified name exists. Using br-int is for restart agent case.
for _, name := range []string{ifaceName, ovsBridgeName} {
ipNet, _, link, err := util.GetIPNetDeviceByName(name)
if err == nil {
return ipNet, nil, link, nil
Expand Down
57 changes: 42 additions & 15 deletions pkg/agent/util/net_windows.go
Expand Up @@ -19,6 +19,7 @@ package util

import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"net"
Expand Down Expand Up @@ -172,7 +173,17 @@ func EnableIPForwarding(ifaceName string) error {
}

func RenameVMNetworkAdapter(networkName string, macStr, newName string, renameNetAdapter bool) error {
cmd := fmt.Sprintf(`$name=$(Get-VMNetworkAdapter -ManagementOS -ComputerName "$(hostname)" -SwitchName "%s" | ? MacAddress -EQ "%s").Name; Rename-VMNetworkAdapter -ManagementOS -ComputerName "$(hostname)" -Name "$name" -NewName "%s"`, networkName, macStr, newName)
cmd := fmt.Sprintf(`Get-VMNetworkAdapter -ManagementOS -ComputerName "$(hostname)" -SwitchName "%s" | ? MacAddress -EQ "%s" | Select-Object -Property Name | Format-Table -HideTableHeaders`, networkName, macStr)
stdout, err := ps.RunCommand(cmd)
if err != nil {
return err
}
stdout = strings.TrimSpace(stdout)
if len(stdout) == 0 {
return fmt.Errorf("unable to find vmnetwork adapter configured with uplink MAC address %s", macStr)
}
vmNetworkAdapterName := stdout
cmd = fmt.Sprintf(`Get-VMNetworkAdapter -ManagementOS -ComputerName "$(hostname)" -Name "%s" | Rename-VMNetworkAdapter -NewName "%s"`, vmNetworkAdapterName, newName)
if _, err := ps.RunCommand(cmd); err != nil {
return err
}
Expand Down Expand Up @@ -385,15 +396,15 @@ func PrepareHNSNetwork(subnetCIDR *net.IPNet, nodeIPNet *net.IPNet, uplinkAdapte
}
}()

vNicName := VirtualAdapterName(uplinkAdapter.Name)
index, found, err := adapterIPExists(nodeIPNet.IP, vNicName)
adapter, ipFound, err := adapterIPExists(nodeIPNet.IP, uplinkAdapter.HardwareAddr, ContainerVNICPrefix)
if err != nil {
return err
}
// By default, "found" should be true after Windows creates the HNSNetwork. The following check is for some corner
vNicName, index := adapter.Name, adapter.Index
// By default, "ipFound" should be true after Windows creates the HNSNetwork. The following check is for some corner
// cases that Windows fails to move the physical adapter's IP address to the virtual network adapter, e.g., DHCP
// Server fails to allocate IP to new virtual network.
if !found {
if !ipFound {
klog.InfoS("Moving uplink configuration to the management virtual network adapter", "adapter", vNicName)
if err := ConfigureInterfaceAddressWithDefaultGateway(vNicName, nodeIPNet, nodeGateway); err != nil {
klog.ErrorS(err, "Failed to configure IP and gateway on the management virtual network adapter", "adapter", vNicName, "ip", nodeIPNet.String())
Expand Down Expand Up @@ -443,19 +454,35 @@ func PrepareHNSNetwork(subnetCIDR *net.IPNet, nodeIPNet *net.IPNet, uplinkAdapte
return nil
}

func adapterIPExists(ip net.IP, adapter string) (int, bool, error) {
iface, err := net.InterfaceByName(adapter)
// 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.
// 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) {
adapters, err := net.Interfaces()
if err != nil {
return 0, false, err
}
cmd := fmt.Sprintf(`Get-NetIPAddress -IPAddress %s -InterfaceIndex %d`, ip.String(), iface.Index)
if _, err = ps.RunCommand(cmd); err != nil {
if strings.Contains(err.Error(), "No matching MSFT_NetIPAddress objects found") {
return iface.Index, false, nil
return nil, false, err
}
ipExists := false
for _, adapter := range adapters {
if bytes.Equal(adapter.HardwareAddr, mac) {
if namePrefix == "" || strings.Contains(adapter.Name, namePrefix) {
addrList, err := adapter.Addrs()
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
}
}
}
return &adapter, ipExists, nil
}
}
return 0, false, err
}
return iface.Index, true, nil
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)
}

// EnableRSCOnVSwitch enables RSC in the vSwitch to reduce host CPU utilization and increase throughput for virtual
Expand Down

0 comments on commit 185e0c2

Please sign in to comment.