diff --git a/pkg/k8sclient/k8sclient.go b/pkg/k8sclient/k8sclient.go index e0f9e0d8b..f0bf8c0ac 100644 --- a/pkg/k8sclient/k8sclient.go +++ b/pkg/k8sclient/k8sclient.go @@ -366,10 +366,13 @@ func TryLoadPodDelegates(pod *v1.Pod, conf *types.NetConf, clientInfo *ClientInf } if isGatewayConfigured == true { - types.CheckGatewayConfig(conf.Delegates) + err = types.CheckGatewayConfig(conf.Delegates) + if err != nil { + return 0, nil, err + } } - return len(delegates), clientInfo, nil + return len(delegates), clientInfo, err } if _, ok := err.(*NoK8sNetworkError); ok { diff --git a/pkg/multus/multus.go b/pkg/multus/multus.go index 826b3f23d..294102c98 100644 --- a/pkg/multus/multus.go +++ b/pkg/multus/multus.go @@ -636,10 +636,11 @@ func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c } } + netName := "" tmpResult, err = delegateAdd(exec, kubeClient, pod, ifName, delegate, rt, n, cniArgs) if err != nil { // If the add failed, tear down all networks we already added - netName := delegate.Conf.Name + netName = delegate.Conf.Name if netName == "" { netName = delegate.ConfList.Name } @@ -649,11 +650,12 @@ func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c } // Remove gateway from routing table if the gateway is not used - deletegateway := false + deleteV4gateway := false + deleteV6gateway := false adddefaultgateway := false - if delegate.IsFilterGateway { - deletegateway = true - logging.Debugf("Marked interface %v for gateway deletion", ifName) + if delegate.IsFilterV4Gateway { + deleteV4gateway = true + logging.Debugf("Marked interface %v for v4 gateway deletion", ifName) } else { // Otherwise, determine if this interface now gets our default route. // According to @@ -661,25 +663,54 @@ func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c // the list can be empty; if it is, we'll assume the CNI's config for the default gateway holds, // else we'll update the defaultgateway to the one specified. if delegate.GatewayRequest != nil && delegate.GatewayRequest[0] != nil { - deletegateway = true + deleteV4gateway = true adddefaultgateway = true logging.Debugf("Detected gateway override on interface %v to %v", ifName, delegate.GatewayRequest) } } - if deletegateway { - tmpResult, err = netutils.DeleteDefaultGW(args, ifName, &tmpResult) - if err != nil { - return nil, cmdErr(k8sArgs, "error deleting default gateway: %v", err) + if delegate.IsFilterV6Gateway { + deleteV6gateway = true + logging.Debugf("Marked interface %v for v6 gateway deletion", ifName) + } else { + // Otherwise, determine if this interface now gets our default route. + // According to + // https://docs.google.com/document/d/1Ny03h6IDVy_e_vmElOqR7UdTPAG_RNydhVE1Kx54kFQ (4.1.2.1.9) + // the list can be empty; if it is, we'll assume the CNI's config for the default gateway holds, + // else we'll update the defaultgateway to the one specified. + if delegate.GatewayRequest != nil && delegate.GatewayRequest[0] != nil { + deleteV6gateway = true + adddefaultgateway = true + logging.Debugf("Detected gateway override on interface %v to %v", ifName, delegate.GatewayRequest) } } - // Here we'll set the default gateway + // Remove namespace from delegate.Name for Add/Del CNI cache + nameSlice := strings.Split(delegate.Name, "/") + netName = nameSlice[len(nameSlice) - 1] + + // Remove gateway if `default-route` network selection is specified + if deleteV4gateway || deleteV6gateway { + err = netutils.DeleteDefaultGW(args, ifName) + if err != nil { + return nil, cmdErr(k8sArgs, "error deleting default gateway: %v", err) + } + err = netutils.DeleteDefaultGWCache(n.CNIDir, rt, netName, ifName, deleteV4gateway, deleteV6gateway) + if err != nil { + return nil, cmdErr(k8sArgs, "error deleting default gateway in cache: %v", err) + } + } + + // Here we'll set the default gateway which specified in `default-route` network selection if adddefaultgateway { - tmpResult, err = netutils.SetDefaultGW(args, ifName, delegate.GatewayRequest, &tmpResult) + err = netutils.SetDefaultGW(args, ifName, delegate.GatewayRequest) if err != nil { return nil, cmdErr(k8sArgs, "error setting default gateway: %v", err) } + err = netutils.AddDefaultGWCache(n.CNIDir, rt, netName, ifName, delegate.GatewayRequest) + if err != nil { + return nil, cmdErr(k8sArgs, "error setting default gateway in cache: %v", err) + } } // Master plugin result is always used if present diff --git a/pkg/netutils/netutils.go b/pkg/netutils/netutils.go index f7f769be0..c2a76c6db 100644 --- a/pkg/netutils/netutils.go +++ b/pkg/netutils/netutils.go @@ -16,26 +16,24 @@ package netutils import ( + "encoding/json" + "fmt" + "io/ioutil" + "net" + "path/filepath" + + "github.com/containernetworking/cni/libcni" "github.com/containernetworking/cni/pkg/skel" - cnitypes "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/vishvananda/netlink" "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging" - "net" - "strings" ) // DeleteDefaultGW removes the default gateway from marked interfaces. -func DeleteDefaultGW(args *skel.CmdArgs, ifName string, res *cnitypes.Result) (*current.Result, error) { - result, err := current.NewResultFromResult(*res) - if err != nil { - return nil, logging.Errorf("DeleteDefaultGW: Error creating new from current CNI result: %v", err) - } - +func DeleteDefaultGW(args *skel.CmdArgs, ifName string) error { netns, err := ns.GetNS(args.Netns) if err != nil { - return nil, logging.Errorf("DeleteDefaultGW: Error getting namespace %v", err) + return logging.Errorf("DeleteDefaultGW: Error getting namespace %v", err) } defer netns.Close() @@ -50,40 +48,27 @@ func DeleteDefaultGW(args *skel.CmdArgs, ifName string, res *cnitypes.Result) (* } return err }) - var newRoutes []*cnitypes.Route - for _, route := range result.Routes { - if mask, _ := route.Dst.Mask.Size(); mask != 0 { - newRoutes = append(newRoutes, route) - } - } - result.Routes = newRoutes - return result, err + return err } // SetDefaultGW adds a default gateway on a specific interface -func SetDefaultGW(args *skel.CmdArgs, ifName string, gateways []net.IP, res *cnitypes.Result) (*current.Result, error) { - - // Use the current CNI result... - result, err := current.NewResultFromResult(*res) - if err != nil { - return nil, logging.Errorf("SetDefaultGW: Error creating new CNI result from current: %v", err) - } - +func SetDefaultGW(args *skel.CmdArgs, ifName string, gateways []net.IP) error { // This ensures we're acting within the net namespace for the pod. netns, err := ns.GetNS(args.Netns) if err != nil { - return nil, logging.Errorf("SetDefaultGW: Error getting namespace %v", err) + return logging.Errorf("SetDefaultGW: Error getting namespace %v", err) } defer netns.Close() - var newResultDefaultRoutes []*cnitypes.Route - // Do this within the net namespace. err = netns.Do(func(_ ns.NetNS) error { var err error // Pick up the link info as we need the index. - link, _ := netlink.LinkByName(ifName) + link, err := netlink.LinkByName(ifName) + if err != nil { + return logging.Errorf("SetDefaultGW: Error getting link %v", err) + } // Cycle through all the desired gateways. for _, gw := range gateways { @@ -95,15 +80,6 @@ func SetDefaultGW(args *skel.CmdArgs, ifName string, gateways []net.IP, res *cni Gw: gw, } - // Build a new element for the results route - - // Set a correct CIDR depending on IP type - _, dstipnet, _ := net.ParseCIDR("::0/0") - if strings.Count(gw.String(), ":") < 2 { - _, dstipnet, _ = net.ParseCIDR("0.0.0.0/0") - } - newResultDefaultRoutes = append(newResultDefaultRoutes, &cnitypes.Route{Dst: *dstipnet, GW: gw}) - // Perform the creation of the default route.... err = netlink.RouteAdd(&newDefaultRoute) if err != nil { @@ -113,7 +89,317 @@ func SetDefaultGW(args *skel.CmdArgs, ifName string, gateways []net.IP, res *cni return err }) - result.Routes = newResultDefaultRoutes - return result, err - + return err +} + +// DeleteDefaultGWCache updates libcni cache to remove default gateway routes in result +func DeleteDefaultGWCache(cacheDir string, rt *libcni.RuntimeConf, netName string, ifName string, ipv4, ipv6 bool) error { + cacheFile := filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)) + + cache, err := ioutil.ReadFile(cacheFile) + if err != nil { + return err + } + logging.Debugf("DeleteDefaultGWCache: update cache to delete GW from: %s", string(cache)) + newCache, err := deleteDefaultGWCacheBytes(cache, ipv4, ipv6) + if err != nil { + return err + } + + logging.Debugf("DeleteDefaultGWCache: update cache to delete GW: %s", string(newCache)) + return ioutil.WriteFile(cacheFile, newCache, 0600) +} + +func deleteDefaultGWCacheBytes(cacheFile []byte, ipv4, ipv6 bool) ([]byte, error) { + var cachedInfo map[string]interface{} + if err := json.Unmarshal(cacheFile, &cachedInfo); err != nil { + return nil, err + } + + // try to get result + _, ok := cachedInfo["result"] + if !ok { + return nil, fmt.Errorf("cannot get result from cache") + } + + resultJSON, ok := cachedInfo["result"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("wrong result type: %v", cachedInfo["result"]) + } + newResult, err := deleteDefaultGWResult(resultJSON, ipv4, ipv6) + if err != nil { + return nil, err + } + cachedInfo["result"] = newResult + + newCache, err := json.Marshal(cachedInfo) + if err != nil { + return nil, fmt.Errorf("failed to encode json: %v", err) + } + return newCache, nil +} + +func deleteDefaultGWResultRoutes(routes []interface{}, dstGW string) ([]interface{}, error) { + for i, r := range routes { + route, ok := r.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("wrong route format: %v", r) + } + _, ok = route["dst"] + if ok { + dst, ok := route["dst"].(string) + if !ok { + return nil, fmt.Errorf("wrong dst format: %v", route["dst"]) + } + if dst == dstGW { + routes = append(routes[:i], routes[i+1:]...) + } + } + } + return routes, nil +} + +func deleteDefaultGWResult(result map[string]interface{}, ipv4, ipv6 bool) (map[string]interface{}, error) { + // try to get cniVersion from result + _, ok := result["cniVersion"] + if !ok { + // fallback to processing result for old cni version(0.1.0/0.2.0) + return deleteDefaultGWResult020(result, ipv4, ipv6) + } + + cniVersion, ok := result["cniVersion"].(string) + if !ok { + return nil, fmt.Errorf("wrong cniVersion format: %v", result["cniVersion"]) + } + + if cniVersion == "0.1.0" || cniVersion == "0.2.0" { + // fallback to processing result for old cni version(0.1.0/0.2.0) + return deleteDefaultGWResult020(result, ipv4, ipv6) + } + + if cniVersion != "0.3.0" && cniVersion != "0.3.1" && cniVersion != "0.4.0" && cniVersion != "1.0.0" { + return nil, fmt.Errorf("not supported version: %s", cniVersion) + } + + _, ok = result["routes"] + if !ok { + // No route in result, hence we do nothing + return result, nil + } + routes, ok := result["routes"].([]interface{}) + if !ok { + return nil, fmt.Errorf("wrong routes format: %v", result["routes"]) + } + + var err error + // delete IPv4 default routes + if ipv4 { + routes, err = deleteDefaultGWResultRoutes(routes, "0.0.0.0/0") + if err != nil { + return nil, err + } + } + + if ipv6 { + routes, err = deleteDefaultGWResultRoutes(routes, "::0/0") + if err != nil { + return nil, err + } + } + result["routes"] = routes + + return result, nil +} + +func deleteDefaultGWResult020(result map[string]interface{}, ipv4, ipv6 bool) (map[string]interface{}, error) { + var err error + if ipv4 { + _, ok := result["ip4"] + if ok { + ip4, ok := result["ip4"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("wrong ip4 format: %v", result["ip4"]) + } + + _, ok = ip4["routes"] + if ok { + routes, ok := ip4["routes"].([]interface{}) + if !ok { + return nil, fmt.Errorf("wrong ip4 routes format: %v", ip4["routes"]) + } + + routes, err = deleteDefaultGWResultRoutes(routes, "0.0.0.0/0") + if err != nil { + return nil, err + } + ip4["routes"] = routes + } + } + } + + if ipv6 { + _, ok := result["ip6"] + if ok { + ip6, ok := result["ip6"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("wrong ip6 format: %v", result["ip6"]) + } + + _, ok = ip6["routes"] + if ok { + routes, ok := ip6["routes"].([]interface{}) + if !ok { + return nil, fmt.Errorf("wrong ip6 routes format: %v", ip6["routes"]) + } + + routes, err = deleteDefaultGWResultRoutes(routes, "::0/0") + if err != nil { + return nil, err + } + ip6["routes"] = routes + } + } + } + + return result, nil +} + +// AddDefaultGWCache updates libcni cache to add default gateway result +func AddDefaultGWCache(cacheDir string, rt *libcni.RuntimeConf, netName string, ifName string, gw []net.IP) error { + cacheFile := filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)) + + cache, err := ioutil.ReadFile(cacheFile) + if err != nil { + return err + } + logging.Debugf("AddDefaultGWCache: update cache to add GW from: %s", string(cache)) + newCache, err := addDefaultGWCacheBytes(cache, gw) + if err != nil { + return err + } + + logging.Debugf("AddDefaultGWCache: update cache to add GW: %s", string(newCache)) + return ioutil.WriteFile(cacheFile, newCache, 0600) +} + +func addDefaultGWCacheBytes(cacheFile []byte, gw []net.IP) ([]byte, error) { + var cachedInfo map[string]interface{} + if err := json.Unmarshal(cacheFile, &cachedInfo); err != nil { + return nil, err + } + + // try to get result + _, ok := cachedInfo["result"] + if !ok { + return nil, fmt.Errorf("cannot get result from cache") + } + + resultJSON, ok := cachedInfo["result"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("wrong result type: %v", cachedInfo["result"]) + } + newResult, err := addDefaultGWResult(resultJSON, gw) + if err != nil { + return nil, err + } + cachedInfo["result"] = newResult + + newCache, err := json.Marshal(cachedInfo) + if err != nil { + return nil, fmt.Errorf("failed to encode json: %v", err) + } + return newCache, nil +} + +func addDefaultGWResult(result map[string]interface{}, gw []net.IP) (map[string]interface{}, error) { + // try to get cniVersion from result + _, ok := result["cniVersion"] + if !ok { + // fallback to processing result for old cni version(0.1.0/0.2.0) + return addDefaultGWResult020(result, gw) + } + + cniVersion, ok := result["cniVersion"].(string) + if !ok { + return nil, fmt.Errorf("wrong cniVersion format: %v", result["cniVersion"]) + } + + if cniVersion == "0.1.0" || cniVersion == "0.2.0" { + // fallback to processing result for old cni version(0.1.0/0.2.0) + return addDefaultGWResult020(result, gw) + } + + if cniVersion != "0.3.0" && cniVersion != "0.3.1" && cniVersion != "0.4.0" && cniVersion != "1.0.0" { + return nil, fmt.Errorf("not supported version: %s", cniVersion) + } + + routes := []interface{}{} + _, ok = result["routes"] + if ok { + routes, ok = result["routes"].([]interface{}) + if !ok { + return nil, fmt.Errorf("wrong routes format: %v", result["routes"]) + } + } + + for _, g := range gw { + dst := "0.0.0.0/0" + if g.To4() == nil { + dst = "::0/0" + } + routes = append(routes, map[string]string{ + "dst": dst, + "gw": g.String(), + }) + } + result["routes"] = routes + + return result, nil +} + +func addDefaultGWResult020(result map[string]interface{}, gw []net.IP) (map[string]interface{}, error) { + for _, g := range gw { + if g.To4() != nil { + _, ok := result["ip4"] + if ok { + ip4, ok := result["ip4"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("wrong ip4 format: %v", result["ip4"]) + } + routes := []interface{}{} + _, ok = ip4["routes"] + if ok { + routes, ok = ip4["routes"].([]interface{}) + if !ok { + return nil, fmt.Errorf("wrong ip4 routes format: %v", ip4["routes"]) + } + } + ip4["routes"] = append(routes, map[string]string{ + "dst": "0.0.0.0/0", + "gw": g.String(), + }) + } + } else { + _, ok := result["ip6"] + if ok { + ip6, ok := result["ip6"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("wrong ip6 format: %v", result["ip4"]) + } + routes := []interface{}{} + _, ok = ip6["routes"] + if ok { + routes, ok = ip6["routes"].([]interface{}) + if !ok { + return nil, fmt.Errorf("wrong ip6 routes format: %v", ip6["routes"]) + } + } + ip6["routes"] = append(routes, map[string]string{ + "dst": "::/0", + "gw": g.String(), + }) + } + } + } + return result, nil } diff --git a/pkg/netutils/netutils_test.go b/pkg/netutils/netutils_test.go new file mode 100644 index 000000000..da075b479 --- /dev/null +++ b/pkg/netutils/netutils_test.go @@ -0,0 +1,1024 @@ +// Copyright (c) 2019 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package netutils + +import ( + "encoding/json" + "net" + "testing" + + "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" + + "github.com/vishvananda/netlink" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestNetutils(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "netutils") +} + +// helper function +func testAddRoute(link netlink.Link, ip net.IP, mask net.IPMask, gw net.IP) error { + dst := &net.IPNet{ + IP: ip, + Mask: mask, + } + route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: gw} + return netlink.RouteAdd(&route) +} + +func testAddAddr(link netlink.Link, ip net.IP, mask net.IPMask) error { + return netlink.AddrAdd(link, &netlink.Addr{IPNet: &net.IPNet{IP: ip, Mask: mask}}) +} + +func testGetResultFromCache(data []byte) []byte { + var cachedInfo map[string]interface{} + ExpectWithOffset(1, json.Unmarshal(data, &cachedInfo)).NotTo(HaveOccurred()) + + // try to get result + _, ok := cachedInfo["result"] + ExpectWithOffset(1, ok).To(BeTrue()) + + resultJSON, ok := cachedInfo["result"].(map[string]interface{}) + ExpectWithOffset(1, ok).To(BeTrue()) + + resultByte, err := json.Marshal(resultJSON) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + return resultByte +} + +func test020ResultHasIPv4DefaultRoute(data []byte) bool { + resultRaw, err := types020.NewResult(data) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + result, err := types020.GetResult(resultRaw) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + for _, r := range result.IP4.Routes { + if r.Dst.String() == "0.0.0.0/0" { + return true + } + } + return false +} + +func test020ResultHasIPv6DefaultRoute(data []byte) bool { + resultRaw, err := types020.NewResult(data) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + result, err := types020.GetResult(resultRaw) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + for _, r := range result.IP6.Routes { + if r.Dst.String() == "::/0" { + return true + } + } + return false +} + +func testResultHasIPv4DefaultRoute(data []byte) bool { + resultRaw, err := current.NewResult(data) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + result, err := current.GetResult(resultRaw) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + for _, r := range result.Routes { + if r.Dst.String() == "0.0.0.0/0" { + return true + } + } + return false +} + +func testResultHasIPv6DefaultRoute(data []byte) bool { + resultRaw, err := current.NewResult(data) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + result, err := current.GetResult(resultRaw) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + for _, r := range result.Routes { + if r.Dst.String() == "::/0" { + return true + } + } + return false +} + +var _ = Describe("netutil netlink function testing", func() { + const IFNAME string = "dummy0" + var IFMAC net.HardwareAddr = net.HardwareAddr([]byte{0x02, 0x66, 0x7d, 0xe3, 0x14, 0x1c}) + var originalNS ns.NetNS + var targetNS ns.NetNS + + BeforeEach(func() { + // Create a new NetNS so we don't modify the host + var err error + originalNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + Expect(targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + Expect(netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: IFNAME, + HardwareAddr: IFMAC, + Index: 10, + }, + })).Should(Succeed()) + + _, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + + return nil + })).Should(Succeed()) + }) + + AfterEach(func() { + Expect(originalNS.Close()).To(Succeed()) + }) + + Context("test DeleteDefaultGW", func() { + It("verify default gateway is removed", func() { + Expect(targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(netlink.LinkSetUp(link)).NotTo(HaveOccurred()) + + // addr 10.0.0.2/24 + Expect(testAddAddr(link, net.IPv4(10, 0, 0, 2), net.CIDRMask(24, 32))).Should(Succeed()) + + // add default gateway into IFNAME + Expect(testAddRoute(link, + net.IPv4(0, 0, 0, 0), net.CIDRMask(0, 0), + net.IPv4(10, 0, 0, 1))).Should(Succeed()) + + //"dst": "10.0.0.0/16" + Expect(testAddRoute(link, + net.IPv4(10, 0, 0, 0), net.CIDRMask(16, 32), + net.IPv4(10, 0, 0, 1))).Should(Succeed()) + + return nil + })).Should(Succeed()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + } + + Expect(originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + Expect(DeleteDefaultGW(args, IFNAME)).Should(Succeed()) + return nil + })).Should(Succeed()) + }) + }) + + Context("test SetDefaultGW", func() { + It("verify default gateway is removed", func() { + Expect(targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(netlink.LinkSetUp(link)).Should(Succeed()) + + // addr 10.0.0.2/24 + Expect(testAddAddr(link, net.IPv4(10, 0, 0, 2), net.CIDRMask(24, 32))).Should(Succeed()) + + //"dst": "10.0.0.0/16" + Expect(testAddRoute(link, + net.IPv4(10, 0, 0, 0), net.CIDRMask(16, 32), + net.IPv4(10, 0, 0, 1))).Should(Succeed()) + + return nil + })).Should(Succeed()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + } + + Expect(originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + Expect(SetDefaultGW(args, IFNAME, []net.IP{net.ParseIP("10.0.0.1")})).Should(Succeed()) + return nil + })).Should(Succeed()) + }) + }) + +}) + +var _ = Describe("netutil cnicache function testing", func() { + Context("test DeleteDefaultGWCache", func() { + It("verify ipv4 default gateway is removed from CNI 0.1.0/0.2.0 results", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "dns": {}, + "ip4": { + "ip": "10.1.1.103/24", + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "0.0.0.0/0", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + } + ] + }, + "ip6": { + "ip": "10::1:1:103/64", + "routes": [ + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "::0/0", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } + } +}`) + newResult, err := deleteDefaultGWCacheBytes(origResult, true, false) + Expect(err).NotTo(HaveOccurred()) + + Expect(test020ResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeFalse()) + Expect(test020ResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + + // Simplified CNI Cache with 010/020 Result + type CNICacheResult020 struct { + Kind string `json:"kind"` + Result struct { + IP4 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip4"` + IP6 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip6"` + } `json:"result"` + } + result := CNICacheResult020{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.IP4.Routes)).To(Equal(2)) + Expect(len(result.Result.IP6.Routes)).To(Equal(3)) + }) + + It("verify ipv6 default gateway is removed from CNI 0.1.0/0.2.0 results", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "dns": {}, + "ip4": { + "ip": "10.1.1.103/24", + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "0.0.0.0/0", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + } + ] + }, + "ip6": { + "ip": "10::1:1:103/64", + "routes": [ + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "::0/0", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } + } +}`) + newResult, err := deleteDefaultGWCacheBytes(origResult, false, true) + Expect(err).NotTo(HaveOccurred()) + + Expect(test020ResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(test020ResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeFalse()) + + // Simplified CNI Cache with 010/020 Result + type CNICacheResult020 struct { + Kind string `json:"kind"` + Result struct { + IP4 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip4"` + IP6 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip6"` + } `json:"result"` + } + result := CNICacheResult020{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.IP4.Routes)).To(Equal(3)) + Expect(len(result.Result.IP6.Routes)).To(Equal(2)) + }) + + It("verify ipv4 default gateway is removed from CNI 0.3.0/0.3.1/0.4.0 results", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "cniVersion": "0.3.1", + "dns": {}, + "interfaces": [ + { + "mac": "0a:c2:e6:3d:45:17", + "name": "net1", + "sandbox": "/var/run/netns/bb74fcb9-989a-4589-b2df-ddd0384a8ee5" + } + ], + "ips": [ + { + "address": "10.1.1.103/24", + "interface": 0, + "version": "4" + }, + { + "address": "10::1:1:103/64", + "interface": 0, + "version": "6" + } + ], + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "0.0.0.0/0", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "::0/0", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } +}`) + newResult, err := deleteDefaultGWCacheBytes(origResult, true, false) + Expect(err).NotTo(HaveOccurred()) + + Expect(testResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeFalse()) + Expect(testResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + + // Simplified CNI Cache with 0.3.0/0.3.1/0.4.0 Result + type CNICacheResult030_040 struct { + Kind string `json:"kind"` + Result struct { + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"result"` + } + result := CNICacheResult030_040{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.Routes)).To(Equal(5)) + }) + + It("verify ipv6 default gateway is removed from CNI 0.3.0/0.3.1/0.4.0 results", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "cniVersion": "0.3.1", + "dns": {}, + "interfaces": [ + { + "mac": "0a:c2:e6:3d:45:17", + "name": "net1", + "sandbox": "/var/run/netns/bb74fcb9-989a-4589-b2df-ddd0384a8ee5" + } + ], + "ips": [ + { + "address": "10.1.1.103/24", + "interface": 0, + "version": "4" + }, + { + "address": "10::1:1:103/64", + "interface": 0, + "version": "6" + } + ], + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "0.0.0.0/0", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "::0/0", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } +}`) + newResult, err := deleteDefaultGWCacheBytes(origResult, false, true) + Expect(err).NotTo(HaveOccurred()) + + Expect(testResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(testResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeFalse()) + + // Simplified CNI Cache with 0.3.0/0.3.1/0.4.0 Result + type CNICacheResult030_040 struct { + Kind string `json:"kind"` + Result struct { + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"result"` + } + result := CNICacheResult030_040{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.Routes)).To(Equal(5)) + }) + + It("verify ipv4 default gateway is added to CNI 0.1.0/0.2.0 results without routes", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "dns": {}, + "ip4": { + "ip": "10.1.1.103/24" + }, + "ip6": { + "ip": "10::1:1:103/64", + "routes": [ + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "::0/0", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } + } +}`) + newResult, err := addDefaultGWCacheBytes(origResult, []net.IP{ net.ParseIP("10.1.1.1") }) + Expect(err).NotTo(HaveOccurred()) + + Expect(test020ResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(test020ResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + + // Simplified CNI Cache with 010/020 Result + type CNICacheResult020 struct { + Kind string `json:"kind"` + Result struct { + IP4 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip4"` + IP6 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip6"` + } `json:"result"` + } + result := CNICacheResult020{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.IP4.Routes)).To(Equal(1)) + }) + + It("verify ipv4 default gateway is added to CNI 0.1.0/0.2.0 results", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "dns": {}, + "ip4": { + "ip": "10.1.1.103/24", + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + } + ] + }, + "ip6": { + "ip": "10::1:1:103/64", + "routes": [ + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "::0/0", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } + } +}`) + newResult, err := addDefaultGWCacheBytes(origResult, []net.IP{ net.ParseIP("10.1.1.1") }) + Expect(err).NotTo(HaveOccurred()) + + Expect(test020ResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(test020ResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + + // Simplified CNI Cache with 010/020 Result + type CNICacheResult020 struct { + Kind string `json:"kind"` + Result struct { + IP4 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip4"` + IP6 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip6"` + } `json:"result"` + } + result := CNICacheResult020{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.IP4.Routes)).To(Equal(3)) + }) + + It("verify ipv6 default gateway is added to CNI 0.1.0/0.2.0 results without routes", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "dns": {}, + "ip4": { + "ip": "10.1.1.103/24", + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "0.0.0.0/0", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + } + ] + }, + "ip6": { + "ip": "10::1:1:103/64" + } + } +}`) + newResult, err := addDefaultGWCacheBytes(origResult, []net.IP{ net.ParseIP("10::1:1:1") }) + Expect(err).NotTo(HaveOccurred()) + + Expect(test020ResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(test020ResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + + // Simplified CNI Cache with 010/020 Result + type CNICacheResult020 struct { + Kind string `json:"kind"` + Result struct { + IP4 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip4"` + IP6 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip6"` + } `json:"result"` + } + result := CNICacheResult020{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.IP6.Routes)).To(Equal(1)) + }) + + It("verify ipv6 default gateway is added to CNI 0.1.0/0.2.0 results", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "dns": {}, + "ip4": { + "ip": "10.1.1.103/24", + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "0.0.0.0/0", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + } + ] + }, + "ip6": { + "ip": "10::1:1:103/64", + "routes": [ + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } + } +}`) + newResult, err := addDefaultGWCacheBytes(origResult, []net.IP{ net.ParseIP("10::1:1:1") }) + Expect(err).NotTo(HaveOccurred()) + + Expect(test020ResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(test020ResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + + // Simplified CNI Cache with 010/020 Result + type CNICacheResult020 struct { + Kind string `json:"kind"` + Result struct { + IP4 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip4"` + IP6 struct { + IP string `json:"ip"` + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"ip6"` + } `json:"result"` + } + result := CNICacheResult020{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.IP6.Routes)).To(Equal(3)) + }) + + It("verify ipv4 default gateway is added to CNI 0.3.0/0.3.1/0.4.0 results without routes", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "cniVersion": "0.3.1", + "dns": {}, + "interfaces": [ + { + "mac": "0a:c2:e6:3d:45:17", + "name": "net1", + "sandbox": "/var/run/netns/bb74fcb9-989a-4589-b2df-ddd0384a8ee5" + } + ], + "ips": [ + { + "address": "10.1.1.103/24", + "interface": 0, + "version": "4" + }, + { + "address": "10::1:1:103/64", + "interface": 0, + "version": "6" + } + ] + } +}`) + newResult, err := addDefaultGWCacheBytes(origResult, []net.IP{ net.ParseIP("10.1.1.1") }) + Expect(err).NotTo(HaveOccurred()) + + Expect(testResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(testResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeFalse()) + + // Simplified CNI Cache with 0.3.0/0.3.1/0.4.0 Result + type CNICacheResult030_040 struct { + Kind string `json:"kind"` + Result struct { + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"result"` + } + result := CNICacheResult030_040{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.Routes)).To(Equal(1)) + }) + + It("verify ipv4 default gateway is added to CNI 0.3.0/0.3.1/0.4.0 results", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "cniVersion": "0.3.1", + "dns": {}, + "interfaces": [ + { + "mac": "0a:c2:e6:3d:45:17", + "name": "net1", + "sandbox": "/var/run/netns/bb74fcb9-989a-4589-b2df-ddd0384a8ee5" + } + ], + "ips": [ + { + "address": "10.1.1.103/24", + "interface": 0, + "version": "4" + }, + { + "address": "10::1:1:103/64", + "interface": 0, + "version": "6" + } + ], + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } +}`) + newResult, err := addDefaultGWCacheBytes(origResult, []net.IP{ net.ParseIP("10.1.1.1") }) + Expect(err).NotTo(HaveOccurred()) + + Expect(testResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(testResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeFalse()) + + // Simplified CNI Cache with 0.3.0/0.3.1/0.4.0 Result + type CNICacheResult030_040 struct { + Kind string `json:"kind"` + Result struct { + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"result"` + } + result := CNICacheResult030_040{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.Routes)).To(Equal(5)) + }) + + It("verify ipv6 default gateway is added to CNI 0.3.0/0.3.1/0.4.0 results without routes", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "cniVersion": "0.3.1", + "dns": {}, + "interfaces": [ + { + "mac": "0a:c2:e6:3d:45:17", + "name": "net1", + "sandbox": "/var/run/netns/bb74fcb9-989a-4589-b2df-ddd0384a8ee5" + } + ], + "ips": [ + { + "address": "10.1.1.103/24", + "interface": 0, + "version": "4" + }, + { + "address": "10::1:1:103/64", + "interface": 0, + "version": "6" + } + ] + } +}`) + newResult, err := addDefaultGWCacheBytes(origResult, []net.IP{ net.ParseIP("10::1:1:1") }) + Expect(err).NotTo(HaveOccurred()) + + Expect(testResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeFalse()) + Expect(testResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + + // Simplified CNI Cache with 0.3.0/0.3.1/0.4.0 Result + type CNICacheResult030_040 struct { + Kind string `json:"kind"` + Result struct { + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"result"` + } + result := CNICacheResult030_040{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.Routes)).To(Equal(1)) + }) + + It("verify ipv6 default gateway is added to CNI 0.3.0/0.3.1/0.4.0 results", func() { + origResult := []byte(`{ + "kind": "cniCacheV1", + "result": { + "cniVersion": "0.3.1", + "dns": {}, + "interfaces": [ + { + "mac": "0a:c2:e6:3d:45:17", + "name": "net1", + "sandbox": "/var/run/netns/bb74fcb9-989a-4589-b2df-ddd0384a8ee5" + } + ], + "ips": [ + { + "address": "10.1.1.103/24", + "interface": 0, + "version": "4" + }, + { + "address": "10::1:1:103/64", + "interface": 0, + "version": "6" + } + ], + "routes": [ + { + "dst": "20.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "30.0.0.0/24", + "gw": "10.1.1.1" + }, + { + "dst": "0.0.0.0/0", + "gw": "10.1.1.1" + }, + { + "dst": "20::0:0:0/56", + "gw": "10::1:1:1" + }, + { + "dst": "30::0:0:0/64", + "gw": "10::1:1:1" + } + ] + } +}`) + newResult, err := addDefaultGWCacheBytes(origResult, []net.IP{ net.ParseIP("10::1:1:1") }) + Expect(err).NotTo(HaveOccurred()) + + Expect(testResultHasIPv4DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + Expect(testResultHasIPv6DefaultRoute(testGetResultFromCache(newResult))).To(BeTrue()) + + // Simplified CNI Cache with 0.3.0/0.3.1/0.4.0 Result + type CNICacheResult030_040 struct { + Kind string `json:"kind"` + Result struct { + Routes []struct { + Dst string `json:"dst"` + Gw string `json:"gw"` + } `json:"routes"` + } `json:"result"` + } + result := CNICacheResult030_040{} + Expect(json.Unmarshal(newResult, &result)).NotTo(HaveOccurred()) + Expect(len(result.Result.Routes)).To(Equal(6)) + }) + + }) +}) diff --git a/pkg/types/conf.go b/pkg/types/conf.go index ab36ccf25..a4f624883 100644 --- a/pkg/types/conf.go +++ b/pkg/types/conf.go @@ -504,15 +504,45 @@ func addCNIArgsInConfList(inBytes []byte, cniArgs *map[string]interface{}) ([]by return configBytes, nil } -// CheckGatewayConfig check gatewayRequest and mark IsFilterGateway flag if +// CheckGatewayConfig check gatewayRequest and mark IsFilter{V4,V6}Gateway flag if // gw filtering is required -func CheckGatewayConfig(delegates []*DelegateNetConf) { - // Check the Gateway - for i, delegate := range delegates { - if delegate.GatewayRequest == nil { - delegates[i].IsFilterGateway = true +func CheckGatewayConfig(delegates []*DelegateNetConf) error { + + v4Gateways := 0 + v6Gateways := 0 + + // Check the gateway + for _, delegate := range delegates { + for _, gw := range delegate.GatewayRequest { + if gw.To4() != nil { + v4Gateways++ + } else { + v6Gateways++ + } } } + + if v4Gateways > 1 || v6Gateways > 1 { + return fmt.Errorf("multus does not support ECMP for default-route") + } + + // set filter flag for each delegate + for i, delegate := range delegates { + // no GatewayRequest + if delegate.GatewayRequest == nil { + delegates[i].IsFilterV4Gateway = true + delegates[i].IsFilterV6Gateway = true + } else { + for _, gw := range delegate.GatewayRequest { + if gw.To4() != nil { + delegates[i].IsFilterV6Gateway = true + } else { + delegates[i].IsFilterV4Gateway = true + } + } + } + } + return nil } // CheckSystemNamespaces checks whether given namespace is in systemNamespaces or not. diff --git a/pkg/types/types.go b/pkg/types/types.go index e3f6e8ea7..c6d3d5096 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -98,7 +98,8 @@ type DelegateNetConf struct { PortMappingsRequest []*PortMapEntry `json:"-"` BandwidthRequest *BandwidthEntry `json:"-"` GatewayRequest []net.IP `json:"default-route,omitempty"` - IsFilterGateway bool + IsFilterV4Gateway bool + IsFilterV6Gateway bool // MasterPlugin is only used internal housekeeping MasterPlugin bool `json:"-"` // Conflist plugin is only used internal housekeeping