diff --git a/cni/network/invoker_cns.go b/cni/network/invoker_cns.go index 928096b361..2754b2d886 100644 --- a/cni/network/invoker_cns.go +++ b/cni/network/invoker_cns.go @@ -44,19 +44,21 @@ type CNSIPAMInvoker struct { } type IPResultInfo struct { - podIPAddress string - ncSubnetPrefix uint8 - ncPrimaryIP string - ncGatewayIPAddress string - hostSubnet string - hostPrimaryIP string - hostGateway string - nicType cns.NICType - macAddress string - skipDefaultRoutes bool - routes []cns.Route - pnpID string - endpointPolicies []policy.Policy + podIPAddress string + ncSubnetPrefix uint8 + ncPrimaryIP string + ncGatewayIPAddress string + ncGatewayIPv6Address string + hostSubnet string + hostPrimaryIP string + hostGateway string + nicType cns.NICType + macAddress string + skipDefaultRoutes bool + routes []cns.Route + pnpID string + endpointPolicies []policy.Policy + secondaryIPs map[string]cns.SecondaryIPConfig } func (i IPResultInfo) MarshalLogObject(encoder zapcore.ObjectEncoder) error { @@ -149,19 +151,21 @@ func (invoker *CNSIPAMInvoker) Add(addConfig IPAMAddConfig) (IPAMAddResult, erro for i := 0; i < len(response.PodIPInfo); i++ { info := IPResultInfo{ - podIPAddress: response.PodIPInfo[i].PodIPConfig.IPAddress, - ncSubnetPrefix: response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.PrefixLength, - ncPrimaryIP: response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.IPAddress, - ncGatewayIPAddress: response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPAddress, - hostSubnet: response.PodIPInfo[i].HostPrimaryIPInfo.Subnet, - hostPrimaryIP: response.PodIPInfo[i].HostPrimaryIPInfo.PrimaryIP, - hostGateway: response.PodIPInfo[i].HostPrimaryIPInfo.Gateway, - nicType: response.PodIPInfo[i].NICType, - macAddress: response.PodIPInfo[i].MacAddress, - skipDefaultRoutes: response.PodIPInfo[i].SkipDefaultRoutes, - routes: response.PodIPInfo[i].Routes, - pnpID: response.PodIPInfo[i].PnPID, - endpointPolicies: response.PodIPInfo[i].EndpointPolicies, + podIPAddress: response.PodIPInfo[i].PodIPConfig.IPAddress, + ncSubnetPrefix: response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.PrefixLength, + ncPrimaryIP: response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.IPAddress, + ncGatewayIPAddress: response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPAddress, + ncGatewayIPv6Address: response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPv6Address, + hostSubnet: response.PodIPInfo[i].HostPrimaryIPInfo.Subnet, + hostPrimaryIP: response.PodIPInfo[i].HostPrimaryIPInfo.PrimaryIP, + hostGateway: response.PodIPInfo[i].HostPrimaryIPInfo.Gateway, + nicType: response.PodIPInfo[i].NICType, + macAddress: response.PodIPInfo[i].MacAddress, + skipDefaultRoutes: response.PodIPInfo[i].SkipDefaultRoutes, + routes: response.PodIPInfo[i].Routes, + pnpID: response.PodIPInfo[i].PnPID, + endpointPolicies: response.PodIPInfo[i].EndpointPolicies, + secondaryIPs: response.PodIPInfo[i].SecondaryIPConfigs, } logger.Info("Received info for pod", @@ -505,9 +509,53 @@ func configureSecondaryAddResult(info *IPResultInfo, addResult *IPAMAddResult, p SkipDefaultRoutes: info.skipDefaultRoutes, } + if len(info.secondaryIPs) > 0 { + // assumtion that first address is the only important one and that it is IPv6 + secIPConfig, err := BuildIPConfigForV6(info.secondaryIPs, info.ncGatewayIPv6Address) + + if err == nil { + // If BuildIPConfigForV6 returns a value, take its address + ifaceInfo := addResult.interfaceInfo[key] + ifaceInfo.IPConfigs = append(ifaceInfo.IPConfigs, &secIPConfig) + addResult.interfaceInfo[key] = ifaceInfo + } + } + return nil } +// BuildIPConfigForV6 takes SecondaryIPConfigs and returns an IPConfig. +// Assumes map has at least one element and uses the first one found. +func BuildIPConfigForV6(secondaryIPs map[string]cns.SecondaryIPConfig, gatewayIPv6 string) (network.IPConfig, error) { + for _, v := range secondaryIPs { + ip, ipnet, err := net.ParseCIDR(v.IPAddress) + if err != nil { + return network.IPConfig{}, fmt.Errorf("invalid IPAddress %q: %w", v.IPAddress, err) + } + if ip.To4() != nil { + return network.IPConfig{}, fmt.Errorf("expected IPv6, got IPv4: %q", v.IPAddress) + } + + gwIP := net.ParseIP(gatewayIPv6) + if gwIP == nil { + return network.IPConfig{}, fmt.Errorf("invalid Gateway IPAddress %q: %w", gatewayIPv6, err) + } + if gwIP.To4() != nil { + return network.IPConfig{}, fmt.Errorf("expected IPv6 Gateway, got IPv4 Gateway: %q", gatewayIPv6) + } + + return network.IPConfig{ + Address: net.IPNet{ + IP: ip, + Mask: ipnet.Mask, + }, + Gateway: gwIP, // derived from /64 base + }, nil + } + + return network.IPConfig{}, fmt.Errorf("map is empty") +} + func addBackendNICToResult(info *IPResultInfo, addResult *IPAMAddResult, key string) error { macAddress, err := net.ParseMAC(info.macAddress) if err != nil { diff --git a/cns/NetworkContainerContract.go b/cns/NetworkContainerContract.go index 8f5939c28e..1795bb607b 100644 --- a/cns/NetworkContainerContract.go +++ b/cns/NetworkContainerContract.go @@ -490,6 +490,7 @@ type GetNetworkContainerRequest struct { type GetNetworkContainerResponse struct { NetworkContainerID string IPConfiguration IPConfiguration + SecondaryIPConfigs map[string]SecondaryIPConfig // uuid is key Routes []Route CnetAddressSpace []IPSubnet MultiTenancyInfo MultiTenancyInfo @@ -503,6 +504,7 @@ type GetNetworkContainerResponse struct { type PodIpInfo struct { PodIPConfig IPSubnet + SecondaryIPConfigs map[string]SecondaryIPConfig // uuid is key NetworkContainerPrimaryIPConfig IPConfiguration HostPrimaryIPInfo HostIPInfo NICType NICType diff --git a/cns/restserver/ipam.go b/cns/restserver/ipam.go index 7c1366149d..cbc663e9e9 100644 --- a/cns/restserver/ipam.go +++ b/cns/restserver/ipam.go @@ -156,6 +156,7 @@ func (service *HTTPRestService) requestIPConfigHandlerHelperStandalone(ctx conte MacAddress: resp[i].NetworkInterfaceInfo.MACAddress, NICType: resp[i].NetworkInterfaceInfo.NICType, NetworkContainerPrimaryIPConfig: resp[i].IPConfiguration, + SecondaryIPConfigs: resp[i].SecondaryIPConfigs, } podIPInfoList = append(podIPInfoList, podIPInfo) } diff --git a/cns/restserver/util.go b/cns/restserver/util.go index a84eb8cef0..e95d3c535b 100644 --- a/cns/restserver/util.go +++ b/cns/restserver/util.go @@ -523,6 +523,7 @@ func (service *HTTPRestService) getAllNetworkContainerResponses( getNetworkContainerResponse = cns.GetNetworkContainerResponse{ NetworkContainerID: savedReq.NetworkContainerid, IPConfiguration: savedReq.IPConfiguration, + SecondaryIPConfigs: savedReq.SecondaryIPConfigs, Routes: savedReq.Routes, CnetAddressSpace: savedReq.CnetAddressSpace, MultiTenancyInfo: savedReq.MultiTenancyInfo, diff --git a/network/network_windows.go b/network/network_windows.go index a467b20983..3107800eed 100644 --- a/network/network_windows.go +++ b/network/network_windows.go @@ -289,13 +289,19 @@ func (nm *networkManager) configureHcnNetwork(nwInfo *EndpointInfo, extIf *exter // Populate subnets. for _, subnet := range nwInfo.Subnets { + // Choose route based on IP family + routeDest := defaultRouteCIDR + if subnet.Prefix.IP.To4() == nil { + routeDest = defaultIPv6Route + } + hnsSubnet := hcn.Subnet{ IpAddressPrefix: subnet.Prefix.String(), // Set the Gateway route Routes: []hcn.Route{ { NextHop: subnet.Gateway.String(), - DestinationPrefix: defaultRouteCIDR, + DestinationPrefix: routeDest, }, }, }