diff --git a/cns/hnsclient/hnsclient_windows.go b/cns/hnsclient/hnsclient_windows.go index 6b2c055526..ccc8d73a8c 100644 --- a/cns/hnsclient/hnsclient_windows.go +++ b/cns/hnsclient/hnsclient_windows.go @@ -1,3 +1,6 @@ +//go:build windows +// +build windows + package hnsclient import ( @@ -53,6 +56,9 @@ const ( // Name of the loopback adapter needed to create Host NC apipa network hostNCLoopbackAdapterName = "LoopbackAdapterHostNCConnectivity" + // Name of the loopback adapter created by HNS for Host NC apipa network + vEthernethostNCLoopbackAdapterName = "vEthernet (" + hostNCLoopbackAdapterName + ")" + // HNS rehydration issue requires this GW to be different than the loopback adapter ip, so we set it to .2 defaultHnsGwIPAddress = "169.254.128.2" hnsLoopbackAdapterIPAddress = "169.254.128.1" @@ -301,7 +307,12 @@ func createHostNCApipaNetwork( } // Create loopback adapter needed for this HNS network - if interfaceExists, _ := networkcontainers.InterfaceExists(hostNCLoopbackAdapterName); !interfaceExists { + // We need to first check the existence of either "LoopbackAdapterHostNCConnectivity" or the vEthernet(LoopbackAdapterHostNCConnectivity) interfaces + // If neither exists, we create the loopback adapter with the specified IP configuration. + shouldCreate, logMessage := shouldCreateLoopbackAdapter(networkcontainers.InterfaceExists) + logger.Printf(logMessage) + + if shouldCreate { ipconfig := cns.IPConfiguration{ IPSubnet: cns.IPSubnet{ IPAddress: hnsLoopbackAdapterIPAddress, @@ -339,6 +350,24 @@ func createHostNCApipaNetwork( return network, err } +// shouldCreateLoopbackAdapter determines whether a loopback adapter should be created +// based on the existence of either the hostNCLoopbackAdapterName or vEthernethostNCLoopbackAdapterName interfaces +func shouldCreateLoopbackAdapter( + interfaceExistsFunc func(string) (bool, error)) (bool, string) { + loopbackInterfaceExists, _ := interfaceExistsFunc(hostNCLoopbackAdapterName) + vethernetLoopbackInterfaceExists, _ := interfaceExistsFunc(vEthernethostNCLoopbackAdapterName) + + if loopbackInterfaceExists { + return false, hostNCLoopbackAdapterName + " already created, skipping loopback interface creation" + } + if vethernetLoopbackInterfaceExists { + return false, vEthernethostNCLoopbackAdapterName + " already created, skipping loopback interface creation" + } + + // Neither interface exists, so we should create the loopback adapter + return true, "Creating loopback adapter" +} + // LogNetworkInterfaces logs the host's network interfaces in the default namespace. func LogNetworkInterfaces() { interfaces, err := net.Interfaces() diff --git a/cns/hnsclient/hnsclient_windows_test.go b/cns/hnsclient/hnsclient_windows_test.go index 8b01d2f839..64f7a01010 100644 --- a/cns/hnsclient/hnsclient_windows_test.go +++ b/cns/hnsclient/hnsclient_windows_test.go @@ -1,3 +1,6 @@ +//go:build windows +// +build windows + package hnsclient import ( @@ -33,3 +36,73 @@ func TestAdhocAdjustIPConfig(t *testing.T) { }) } } + +func TestShouldCreateLoopbackAdapter(t *testing.T) { + tests := []struct { + name string + hostNCExists bool + vEthernetHostNCExists bool + expectedShouldCreate bool + expectedLogMessagePrefix string + }{ + { + name: "should create when neither interface exists", + hostNCExists: false, + vEthernetHostNCExists: false, + expectedShouldCreate: true, + expectedLogMessagePrefix: "Creating loopback adapter", + }, + { + name: "should not create when hostNCLoopbackAdapterName exists", + hostNCExists: true, + vEthernetHostNCExists: false, + expectedShouldCreate: false, + expectedLogMessagePrefix: "LoopbackAdapterHostNCConnectivity already created", + }, + { + name: "should not create when vEthernethostNCLoopbackAdapterName exists", + hostNCExists: false, + vEthernetHostNCExists: true, + expectedShouldCreate: false, + expectedLogMessagePrefix: "vEthernet (LoopbackAdapterHostNCConnectivity) already created", + }, + { + name: "should not create when both interfaces exist - prioritizes hostNCLoopbackAdapterName", + hostNCExists: true, + vEthernetHostNCExists: true, + expectedShouldCreate: false, + expectedLogMessagePrefix: "LoopbackAdapterHostNCConnectivity already created", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + // Create mock interface exists function + mockInterfaceExists := func(interfaceName string) (bool, error) { + switch interfaceName { + case hostNCLoopbackAdapterName: + return tt.hostNCExists, nil + case vEthernethostNCLoopbackAdapterName: + return tt.vEthernetHostNCExists, nil + default: + return false, nil + } + } + + shouldCreate, logMessage := shouldCreateLoopbackAdapter(mockInterfaceExists) + + assert.Equal(t, tt.expectedShouldCreate, shouldCreate) + assert.Contains(t, logMessage, tt.expectedLogMessagePrefix) + }) + } +} + +func TestConstants(t *testing.T) { + // Test that the vEthernet constant is constructed correctly + expectedVEthernetName := "vEthernet (LoopbackAdapterHostNCConnectivity)" + assert.Equal(t, expectedVEthernetName, vEthernethostNCLoopbackAdapterName) + + // Test that the hostNCLoopbackAdapterName constant is as expected + assert.Equal(t, "LoopbackAdapterHostNCConnectivity", hostNCLoopbackAdapterName) +}