From 4077d162c78f97c1dba3ae311ad2ca96340a089d Mon Sep 17 00:00:00 2001 From: Vikas Bhardwaj Date: Mon, 26 Apr 2021 09:52:31 -0700 Subject: [PATCH] Initial changes for ipv6 support for l2bridge network --- cni/cni.go | 105 +++++++++++-- common/core/network.go | 139 ++++++++++++++---- common/utils.go | 7 + .../l2bridgedualstack_host-local_ipam.conf | 96 ++++++++++++ network/endpoint.go | 111 ++++++++++++-- network/manager.go | 15 +- 6 files changed, 409 insertions(+), 64 deletions(-) create mode 100644 example/l2bridgedualstack_host-local_ipam.conf diff --git a/cni/cni.go b/cni/cni.go index 88f73a7..1ea9e33 100644 --- a/cni/cni.go +++ b/cni/cni.go @@ -107,7 +107,7 @@ type OptionalFlags struct { LocalRoutePortMapping bool `json:"localRoutedPortMapping"` AllowAclPortMapping bool `json:"allowAclPortMapping"` ForceBridgeGateway bool `json:"forceBridgeGateway"` // Intended to be temporary workaround - + EnableDualStack bool `json:"enableDualStack"` } func (r *Result) Print() { @@ -194,6 +194,7 @@ func (config *NetworkConfig) Serialize() []byte { // GetNetworkInfo from the NetworkConfig func (config *NetworkConfig) GetNetworkInfo(podNamespace string) *network.NetworkInfo { var subnets []network.SubnetInfo + // Note the code below is looking inside the ipam specific configuration. if config.Ipam.Subnet != "" { ip, s, _ := net.ParseCIDR(config.Ipam.Subnet) gatewayIP := ip.To4() @@ -313,7 +314,13 @@ func (config *NetworkConfig) GetEndpointInfo( } if len(networkInfo.Subnets) > 0 { + // This subnet is not used when constructing + // hcn.HostComputeEndpoint from EndpointInfo epInfo.Subnet = networkInfo.Subnets[0].AddressPrefix + // Gateway field (below) will be updated to the ipam allocated value + // (if applicable) in allocateIpam + // The Gateway6 field (ipv6 gatweay) is not derived like this and + // must be supplied through the ipam. epInfo.Gateway = networkInfo.Subnets[0].GatewayAddress } @@ -346,24 +353,64 @@ func (config *NetworkConfig) GetEndpointInfo( } // GetCurrResult gets the result object -func GetCurrResult(network *network.NetworkInfo, endpoint *network.EndpointInfo, ifname string) cniTypesCurr.Result { +func GetCurrResult(network *network.NetworkInfo, endpoint *network.EndpointInfo, ifname string, cniConfig *NetworkConfig) cniTypesCurr.Result { result := cniTypesCurr.Result{ IPs: []*cniTypesCurr.IPConfig{}, Routes: []*cniTypes.Route{}} var iFace = GetInterface(endpoint) - var ip = GetIP(network, endpoint) - ip.InterfaceIndex = 0 - cIP := cniTypesCurr.IPConfig{ - Version: ip.Version, - Address: net.IPNet{ - IP: ip.Address.IP, - Mask: ip.Address.Mask}, - Gateway: ip.Gateway, - Interface: &ip.InterfaceIndex, - } - result.IPs = append(result.IPs, &cIP) + if cniConfig.OptionalFlags.EnableDualStack == false { + + var ip = GetIP(network, endpoint) + ip.InterfaceIndex = 0 + + cIP := cniTypesCurr.IPConfig{ + Version: ip.Version, + Address: net.IPNet{ + IP: ip.Address.IP, + Mask: ip.Address.Mask}, + Gateway: ip.Gateway, + Interface: &ip.InterfaceIndex, + } + result.IPs = append(result.IPs, &cIP) + + } else { + + ip4, ip6 := GetDualStackAddresses(endpoint) + + if ip4 != nil { + + ip4.InterfaceIndex = 0 + + cIP4 := cniTypesCurr.IPConfig{ + Version: ip4.Version, + Address: net.IPNet{ + IP: ip4.Address.IP, + Mask: ip4.Address.Mask}, + Gateway: ip4.Gateway, + Interface: &ip4.InterfaceIndex, + } + + result.IPs = append(result.IPs, &cIP4) + } + + if ip6 != nil { + + ip6.InterfaceIndex = 0 + + cIP6 := cniTypesCurr.IPConfig{ + Version: ip6.Version, + Address: net.IPNet{ + IP: ip6.Address.IP, + Mask: ip6.Address.Mask}, + Gateway: ip6.Gateway, + Interface: &ip6.InterfaceIndex, + } + + result.IPs = append(result.IPs, &cIP6) + } + } // Add Interfaces to result. iface := &cniTypesCurr.Interface{ @@ -390,6 +437,38 @@ func GetIP(network *network.NetworkInfo, endpoint *network.EndpointInfo) IP { } } +// GetDualStackAddresses returns the IPv4 and IPv6 addresses for the endpoint +func GetDualStackAddresses(endpoint *network.EndpointInfo) (*IP, *IP) { + + var ip4 *IP + var ip6 *IP + + if endpoint.IPAddress != nil { + + ip4address := net.IPNet{} + ip4address.IP = endpoint.IPAddress + ip4address.Mask = endpoint.IP4Mask + + ip4 = &IP{ + Version: "4", + Address: cniTypes.IPNet(ip4address), + Gateway: endpoint.Gateway, + InterfaceIndex: 0, + } + } + + if endpoint.IPAddress6.IP != nil { + ip6 = &IP{ + Version: "6", + Address: cniTypes.IPNet(endpoint.IPAddress6), + Gateway: endpoint.Gateway6, + InterfaceIndex: 0, + } + } + + return ip4, ip6 +} + // GetInterface returns the interface for endpoint func GetInterface(endpoint *network.EndpointInfo) Interface { return Interface{ diff --git a/common/core/network.go b/common/core/network.go index d956d87..64ee3ef 100644 --- a/common/core/network.go +++ b/common/core/network.go @@ -7,11 +7,12 @@ import ( "context" "errors" "fmt" + "os" + "github.com/Microsoft/windows-container-networking/cni" "github.com/Microsoft/windows-container-networking/common" "github.com/Microsoft/windows-container-networking/network" "github.com/sirupsen/logrus" - "os" "github.com/Microsoft/hcsshim/hcn" "github.com/containernetworking/cni/pkg/invoke" @@ -94,6 +95,9 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { logrus.Debugf("[cni-net] Processing ADD command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.", args.ContainerID, args.Netns, args.IfName, args.Args, args.Path) + var err error + var nwConfig *network.NetworkInfo + podConfig, err := cni.ParseCniArgs(args.Args) k8sNamespace := "" if err == nil { @@ -109,6 +113,12 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { logrus.Debugf("[cni-net] Read network configuration %+v.", cniConfig) + if cniConfig.OptionalFlags.EnableDualStack == false { + logrus.Infof("[cni-net] Dual stack is disabled") + } else { + logrus.Infof("[cni-net] Dual stack is enabled") + } + // Convert cniConfig to NetworkInfo // We don't set namespace, setting namespace is not valid for EP creation networkInfo := cniConfig.GetNetworkInfo(k8sNamespace) @@ -118,18 +128,30 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { return err } + epInfo.DualStack = cniConfig.OptionalFlags.EnableDualStack + // Check for missing namespace if args.Netns == "" { logrus.Errorf("[cni-net] Missing Namespace, cannot add, endpoint : [%v].", epInfo) return errors.New("cannot create endpoint without a namespace") } - nwConfig, err := getOrCreateNetwork(plugin, networkInfo, cniConfig) + if cniConfig.OptionalFlags.EnableDualStack == false { + nwConfig, err = getOrCreateNetwork(plugin, networkInfo, cniConfig) + } else { + // The network must be created beforehand + nwConfig, err = plugin.nm.GetNetworkByName(cniConfig.Name) + + if nwConfig.Type != network.L2Bridge { + logrus.Errorf("[cni-net] Dual stack can only be specified with l2bridge network: [%v].", nwConfig.Type) + return errors.New("Dual stack specified with non l2bridge network") + } + } if err != nil { return err } - hnsEndpoint, err := plugin.nm.GetEndpointByName(epInfo.Name) + hnsEndpoint, err := plugin.nm.GetEndpointByName(epInfo.Name, cniConfig.OptionalFlags.EnableDualStack) if hnsEndpoint != nil { logrus.Infof("[cni-net] Endpoint %+v already exists for network %v.", hnsEndpoint, nwConfig.ID) // Endpoint exists @@ -139,7 +161,7 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { // Do not allow creation of more endpoints on same network logrus.Debugf("[cni-net] Endpoint exists on same network, ignoring add : [%v].", epInfo) // Convert result to the requested CNI version. - res := cni.GetCurrResult(nwConfig, hnsEndpoint, args.IfName) + res := cni.GetCurrResult(nwConfig, hnsEndpoint, args.IfName, cniConfig) result, err := res.GetAsVersion(cniConfig.CniVersion) if err != nil { return err @@ -152,9 +174,14 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { logrus.Debugf("[cni-net] Creating a new Endpoint") } - // If Ipam was provided, allocate a pool and obtain V4 address + // If Ipam was provided, allocate a pool and obtain address if cniConfig.Ipam.Type != "" { - err = allocateIpam(networkInfo, epInfo, cniConfig, cniConfig.OptionalFlags.ForceBridgeGateway) + err = allocateIpam( + networkInfo, + epInfo, + cniConfig, + cniConfig.OptionalFlags.ForceBridgeGateway, + args.StdinData) if err != nil { // Error was logged by allocateIpam. return err @@ -163,7 +190,8 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { if resultError != nil { logrus.Debugf("[cni-net] failure during ADD cleaning-up ipam, %v", err) os.Setenv("CNI_COMMAND", "DEL") - err := deallocateIpam(cniConfig) + err := deallocateIpam(cniConfig, args.StdinData) + os.Setenv("CNI_COMMAND", "ADD") if err != nil { logrus.Debugf("[cni-net] failed during ADD command for clean-up delegate delete call, %v", err) @@ -182,13 +210,12 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { } // Convert result to the requested CNI version. - res := cni.GetCurrResult(nwConfig, epInfo, args.IfName) + res := cni.GetCurrResult(nwConfig, epInfo, args.IfName, cniConfig) result, err := res.GetAsVersion(cniConfig.CniVersion) if err != nil { return err } - // result := cni.GetResult020(nwConfig, epInfo) result.Print() logrus.Debugf("[cni-net] result: %v", result.String()) return nil @@ -199,11 +226,21 @@ func allocateIpam( networkInfo *network.NetworkInfo, endpointInfo *network.EndpointInfo, cniConfig *cni.NetworkConfig, - forceBridgeGateway bool) error { + forceBridgeGateway bool, + networkConfByteStream []byte) error { var result cniTypes.Result var resultImpl *cniTypesImpl.Result + var err error + + if cniConfig.OptionalFlags.EnableDualStack == false { + // It seems the right thing would be to pass the original byte stream instead of the one + // which cni parsed into NetworkConfig. However to preserve compatibility continue + // the current behavior when dual stack is not enabled + result, err = invoke.DelegateAdd(context.TODO(), cniConfig.Ipam.Type, cniConfig.Serialize(), nil) + } else { + result, err = invoke.DelegateAdd(context.TODO(), cniConfig.Ipam.Type, networkConfByteStream, nil) + } - result, err := invoke.DelegateAdd(context.TODO(), cniConfig.Ipam.Type, cniConfig.Serialize(), nil) if err != nil { logrus.Infof("[cni-net] Failed to allocate pool, err:%v.", err) return err @@ -216,33 +253,74 @@ func allocateIpam( } logrus.Debugf("[cni-net] IPAM plugin returned result %v.", resultImpl) - // Derive the subnet from allocated IP address. - if resultImpl.IP4 != nil { - var subnetInfo = network.SubnetInfo{ - AddressPrefix: resultImpl.IP4.IP, - GatewayAddress: resultImpl.IP4.Gateway, - } - networkInfo.Subnets = append(networkInfo.Subnets, subnetInfo) - endpointInfo.IPAddress = resultImpl.IP4.IP.IP - endpointInfo.Gateway = resultImpl.IP4.Gateway + if cniConfig.OptionalFlags.EnableDualStack == false { + // Derive the subnet from allocated IP address. + if resultImpl.IP4 != nil { + var subnetInfo = network.SubnetInfo{ + AddressPrefix: resultImpl.IP4.IP, + GatewayAddress: resultImpl.IP4.Gateway, + } - if forceBridgeGateway == true { - endpointInfo.Gateway = resultImpl.IP4.IP.IP.Mask(resultImpl.IP4.IP.Mask) - endpointInfo.Gateway[3] = 2 + networkInfo.Subnets = append(networkInfo.Subnets, subnetInfo) + endpointInfo.IPAddress = resultImpl.IP4.IP.IP + endpointInfo.Gateway = resultImpl.IP4.Gateway + + if forceBridgeGateway == true { + endpointInfo.Gateway = resultImpl.IP4.IP.IP.Mask(resultImpl.IP4.IP.Mask) + endpointInfo.Gateway[3] = 2 + } + + endpointInfo.Subnet = resultImpl.IP4.IP + + for _, route := range resultImpl.IP4.Routes { + // Only default route is populated when calling HNS, and the below information is not passed + endpointInfo.Routes = append(endpointInfo.Routes, network.RouteInfo{Destination: route.Dst, Gateway: route.GW}) + } + } + } else { + if resultImpl.IP4 != nil { + + endpointInfo.IPAddress = resultImpl.IP4.IP.IP + endpointInfo.IP4Mask = resultImpl.IP4.IP.Mask + endpointInfo.Gateway = resultImpl.IP4.Gateway + + if forceBridgeGateway == true { + endpointInfo.Gateway = resultImpl.IP4.IP.IP.Mask(resultImpl.IP4.IP.Mask) + endpointInfo.Gateway[3] = 2 + } + + for _, route := range resultImpl.IP4.Routes { + // Only default route is populated when calling HNS, and the below information is not being passed right now + endpointInfo.Routes = append(endpointInfo.Routes, network.RouteInfo{Destination: route.Dst, Gateway: route.GW}) + } } - endpointInfo.Subnet = resultImpl.IP4.IP + if resultImpl.IP6 != nil { - for _, route := range resultImpl.IP4.Routes { - endpointInfo.Routes = append(endpointInfo.Routes, network.RouteInfo{Destination: route.Dst, Gateway: route.GW}) + endpointInfo.IPAddress6 = resultImpl.IP6.IP + endpointInfo.Gateway6 = resultImpl.IP6.Gateway + + for _, route := range resultImpl.IP6.Routes { + // Only default route is populated when calling HNS, and the below information is not being passed right now + endpointInfo.Routes = append(endpointInfo.Routes, network.RouteInfo{Destination: route.Dst, Gateway: route.GW}) + } } } + return nil } // deallocateIpam performs the cleanup necessary for removing an ipam -func deallocateIpam(cniConfig *cni.NetworkConfig) error { - return invoke.DelegateDel(context.TODO(), cniConfig.Ipam.Type, cniConfig.Serialize(), nil) +func deallocateIpam(cniConfig *cni.NetworkConfig, networkConfByteStream []byte) error { + + if cniConfig.OptionalFlags.EnableDualStack == false { + logrus.Infof("[cni-net] Delete from ipam when dual stack is disabled") + return invoke.DelegateDel(context.TODO(), cniConfig.Ipam.Type, cniConfig.Serialize(), nil) + } else { + logrus.Infof("[cni-net] Delete from ipam when dual stack is enabled") + return invoke.DelegateDel(context.TODO(), cniConfig.Ipam.Type, networkConfByteStream, nil) + } + } // getOrCreateNetwork @@ -294,7 +372,8 @@ func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error { if cniConfig.Ipam.Type != "" { logrus.Debugf("[cni-net] Ipam detected, executing delegate call to delete ipam, %v", cniConfig.Ipam) - err := deallocateIpam(cniConfig) + err := deallocateIpam(cniConfig, args.StdinData) + if err != nil { logrus.Debugf("[cni-net] Failed during delete call for ipam, %v", err) return fmt.Errorf("ipam deletion failed, %v", err) @@ -307,7 +386,7 @@ func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error { if err != nil { return err } - endpointInfo, err := plugin.nm.GetEndpointByName(epInfo.Name) + endpointInfo, err := plugin.nm.GetEndpointByName(epInfo.Name, cniConfig.OptionalFlags.EnableDualStack) if err != nil { if hcn.IsNotFoundError(err) { logrus.Debugf("[cni-net] Endpoint was not found error, err:%v", err) diff --git a/common/utils.go b/common/utils.go index 930be4d..441f94b 100644 --- a/common/utils.go +++ b/common/utils.go @@ -5,6 +5,7 @@ package common import ( "net" + "strconv" "github.com/sirupsen/logrus" ) @@ -22,3 +23,9 @@ func LogNetworkInterfaces() { logrus.Debugf("[net] Network interface: %+v with IP addresses: %+v", iface, addrs) } } + +func GetAddressAsCidr(ip string, prefix uint8) string { + + return ip + string('/') + strconv.FormatUint(uint64(prefix), 10) + +} diff --git a/example/l2bridgedualstack_host-local_ipam.conf b/example/l2bridgedualstack_host-local_ipam.conf new file mode 100644 index 0000000..9114ec8 --- /dev/null +++ b/example/l2bridgedualstack_host-local_ipam.conf @@ -0,0 +1,96 @@ +{ + "cniVersion": "0.2.0", + "name": "winl2bridge", + "type": "sdnbridge", + "master": "Ethernet", + "capabilities": { + "portMappings": true, + "dnsCapabilities": true + }, + "optionalFlags": { + "localRoutedPortMapping": true, + "allowAclPortMapping": true, + "enableDualStack": true + }, + "ipam": { + "type": "host-local", + "ranges": [ + [ + { + "subnet": "192.168.1.0/24", + "rangeStart": "192.168.1.10", + "rangeEnd": "192.168.1.50", + "gateway": "192.168.1.2" + } + ], + [ + { + "subnet": "fd00::100/120", + "rangeStart": "fd00::102", + "rangeEnd": "fd00::1fe", + "gateway": "fd00::101" + } + ] + ] + }, + "dns": { + "Nameservers": [ + "10.127.130.7" + ], + "Search": [ + "svc.cluster.local" + ] + }, + "AdditionalArgs": [ + { + "Name": "EndpointPolicy", + "Value": { + "Type": "OutBoundNAT", + "Settings": { + "Exceptions": [ + "192.168.0.0/16", + "11.0.0.0/8", + "10.124.24.0/23" + ] + } + } + }, + { + "Name": "EndpointPolicy", + "Value": { + "Type": "OutBoundNAT", + "Settings": { + "VirtualIP": "fc00::123", + "Destinations": [ + "fd01::192", + "fd01::193" + ], + "Exceptions": [ + "fd03::100/120", + "fd04::100/120" + ] + } + } + }, + { + "Name": "EndpointPolicy", + "Value": { + "Type": "SDNRoute", + "Settings": { + "DestinationPrefix": "11.0.0.0/8", + "NeedEncap": true + } + } + }, + { + "Name": "EndpointPolicy", + "Value": { + "Type": "SDNRoute", + "Settings": { + "DestinationPrefix": "fd00::200/120", + "NeedEncap": true + } + } + } + ] +} \ No newline at end of file diff --git a/network/endpoint.go b/network/endpoint.go index 04742cc..56c7b1c 100644 --- a/network/endpoint.go +++ b/network/endpoint.go @@ -6,6 +6,7 @@ package network import ( "encoding/json" "net" + "github.com/Microsoft/windows-container-networking/common" "github.com/Microsoft/hcsshim/hcn" ) @@ -18,13 +19,17 @@ type EndpointInfo struct { NetworkID string NamespaceID string IPAddress net.IP + IP4Mask net.IPMask // Used when dual stack is enabled + IPAddress6 net.IPNet MacAddress net.HardwareAddr Gateway net.IP + Gateway6 net.IP Routes []RouteInfo Policies []Policy Subnet net.IPNet DNS DNSInfo ContainerID string + DualStack bool } // RouteInfo contains information about an IP route. @@ -38,20 +43,45 @@ func (endpoint *EndpointInfo) GetHostComputeEndpoint() *hcn.HostComputeEndpoint // Check for nil on address objects. ipAddr := "" ipConfig := []hcn.IpConfig{} + routes := []hcn.Route{} + if endpoint.IPAddress != nil { ipAddr = endpoint.IPAddress.String() ipConfig = append(ipConfig, hcn.IpConfig{ IpAddress: ipAddr, }) } + + if endpoint.IPAddress6.IP != nil { + ipAddr = endpoint.IPAddress6.IP.String() + ipConfig = append(ipConfig, hcn.IpConfig{ + IpAddress: ipAddr, + }) + } + macAddr := "" if endpoint.MacAddress != nil { macAddr = endpoint.MacAddress.String() } + gwAddr := "" if endpoint.Gateway != nil { gwAddr = endpoint.Gateway.String() } + + routes = append(routes, hcn.Route{ + NextHop: gwAddr, + DestinationPrefix: "0.0.0.0/0", + }) + + if endpoint.Gateway6 != nil { + gwAddr6 := endpoint.Gateway6.String() + routes = append(routes, hcn.Route{ + NextHop: gwAddr6, + DestinationPrefix: "::/0", + }) + } + return &hcn.HostComputeEndpoint{ Name: endpoint.Name, Id: endpoint.ID, @@ -64,12 +94,7 @@ func (endpoint *EndpointInfo) GetHostComputeEndpoint() *hcn.HostComputeEndpoint Options: endpoint.DNS.Options, }, MacAddress: macAddr, - Routes: []hcn.Route{ - { - NextHop: gwAddr, - DestinationPrefix: "0.0.0.0/0", - }, - }, + Routes: routes, IpConfigurations: ipConfig, SchemaVersion: hcn.SchemaVersion{ Major: 2, @@ -80,17 +105,71 @@ func (endpoint *EndpointInfo) GetHostComputeEndpoint() *hcn.HostComputeEndpoint } // GetEndpointInfoFromHostComputeEndpoint converts HostComputeEndpoint to CNI EndpointInfo. -func GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint *hcn.HostComputeEndpoint) *EndpointInfo { +func GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint *hcn.HostComputeEndpoint, withIpv6 bool) *EndpointInfo { // Ignore empty MAC, GW, and IP. macAddr, _ := net.ParseMAC(hcnEndpoint.MacAddress) var gwAddr net.IP - if len(hcnEndpoint.Routes) > 0 { - gwAddr = net.ParseIP(hcnEndpoint.Routes[0].NextHop) - } - var ipAddr net.IP - if len(hcnEndpoint.IpConfigurations) > 0 { - ipAddr = net.ParseIP(hcnEndpoint.IpConfigurations[0].IpAddress) + var gwAddr6 net.IP + var ipAddr4 net.IPNet + var ipAddr6 net.IPNet + + if withIpv6 == false { + + if len(hcnEndpoint.Routes) > 0 { + gwAddr = net.ParseIP(hcnEndpoint.Routes[0].NextHop) + } + + if len(hcnEndpoint.IpConfigurations) > 0 { + ipAddr4.IP = net.ParseIP(hcnEndpoint.IpConfigurations[0].IpAddress) + } + } else { + var ip4found bool + var ip6found bool + + for _, addr := range hcnEndpoint.IpConfigurations { + if net.ParseIP(addr.IpAddress).To4() == nil && + ip6found == false { + ip, mask, _ := net.ParseCIDR(common.GetAddressAsCidr(addr.IpAddress, addr.PrefixLength)) + ipAddr6.IP = ip + ipAddr6.Mask = mask.Mask + ip6found = true + } else { + if ip4found == false { + ip, mask, _ := net.ParseCIDR(common.GetAddressAsCidr(addr.IpAddress, addr.PrefixLength)) + ipAddr4.IP = ip + ipAddr4.Mask = mask.Mask + ip4found = true + } + } + + if ip4found && ip6found { + break + } + } + + ip4found = false + ip6found = false + + for _, r := range hcnEndpoint.Routes { + + if net.ParseIP(r.NextHop).To4() == nil && + ip6found == false { + gwAddr6 = net.ParseIP(r.NextHop) + ip6found = true + } else { + if ip4found == false { + gwAddr = net.ParseIP(r.NextHop) + ip4found = true + } + } + + if ip4found && ip6found { + break + } + + } } + return &EndpointInfo{ Name: hcnEndpoint.Name, ID: hcnEndpoint.Id, @@ -104,9 +183,13 @@ func GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint *hcn.HostComputeEndpoint }, MacAddress: macAddr, Gateway: gwAddr, - IPAddress: ipAddr, + IPAddress: ipAddr4.IP, + IP4Mask: ipAddr4.Mask, + Gateway6: gwAddr6, + IPAddress6: ipAddr6, Policies: GetEndpointPoliciesFromHostComputePolicies(hcnEndpoint.Policies), } + } // GetEndpointPoliciesFromHostComputePolicies converts HCN Endpoint policy into CNI Policy objects. diff --git a/network/manager.go b/network/manager.go index fe230ca..d9e1d6a 100644 --- a/network/manager.go +++ b/network/manager.go @@ -31,8 +31,8 @@ type Manager interface { // Endpoint CreateEndpoint(networkID string, epInfo *EndpointInfo, namespaceID string) (*EndpointInfo, error) DeleteEndpoint(endpointID string) error - GetEndpoint(endpointID string) (*EndpointInfo, error) - GetEndpointByName(endpointName string) (*EndpointInfo, error) + GetEndpoint(endpointID string, withIpv6 bool) (*EndpointInfo, error) + GetEndpointByName(endpointName string, withIpv6 bool) (*EndpointInfo, error) } // NewManager creates a new networkManager. @@ -118,6 +118,7 @@ func (nm *networkManager) GetNetworkByName(networkName string) (*NetworkInfo, er // CreateEndpoint creates a new container endpoint. func (nm *networkManager) CreateEndpoint(networkID string, epInfo *EndpointInfo, namespaceID string) (*EndpointInfo, error) { + nm.Lock() defer nm.Unlock() @@ -134,7 +135,7 @@ func (nm *networkManager) CreateEndpoint(networkID string, epInfo *EndpointInfo, return nil, fmt.Errorf("error adding endpoint to namespace %v : endpoint %v", err, hcnEndpoint) } - return GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint), err + return GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint, epInfo.DualStack), err } // DeleteEndpoint deletes an existing container endpoint. @@ -185,7 +186,7 @@ func (nm *networkManager) DeleteEndpoint(endpointID string) error { } // GetEndpoint returns the endpoint matching the Id. -func (nm *networkManager) GetEndpoint(endpointID string) (*EndpointInfo, error) { +func (nm *networkManager) GetEndpoint(endpointID string, withIpv6 bool) (*EndpointInfo, error) { nm.Lock() defer nm.Unlock() @@ -194,11 +195,11 @@ func (nm *networkManager) GetEndpoint(endpointID string) (*EndpointInfo, error) return nil, err } - return GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint), nil + return GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint, withIpv6), nil } // GetEndpointByName returns the endpoint matching the Name. -func (nm *networkManager) GetEndpointByName(endpointName string) (*EndpointInfo, error) { +func (nm *networkManager) GetEndpointByName(endpointName string, withIpv6 bool) (*EndpointInfo, error) { nm.Lock() defer nm.Unlock() @@ -207,5 +208,5 @@ func (nm *networkManager) GetEndpointByName(endpointName string) (*EndpointInfo, return nil, err } - return GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint), nil + return GetEndpointInfoFromHostComputeEndpoint(hcnEndpoint, withIpv6), nil }