Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package cni-plugins for openSUSE:Factory 
checked in at 2021-04-29 22:44:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/cni-plugins (Old)
 and      /work/SRC/openSUSE:Factory/.cni-plugins.new.1947 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "cni-plugins"

Thu Apr 29 22:44:25 2021 rev:15 rq:888696 version:0.9.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/cni-plugins/cni-plugins.changes  2021-01-11 
17:13:10.808333356 +0100
+++ /work/SRC/openSUSE:Factory/.cni-plugins.new.1947/cni-plugins.changes        
2021-04-29 22:44:31.088267774 +0200
@@ -1,0 +2,17 @@
+Mon Apr 26 17:17:41 UTC 2021 - [email protected]
+
+- Update to version 0.9.1:
+  * ipam/dhcp: Add broadcast flag
+  * add flannel to support dual stack ip
+  * bandwidth: fix panic in tests
+  * host-device: Add support for DPDK device
+  * [main/vlan] Fix error handling for delegate IPAM plugin
+  * dhcp: default dhcp clien timeout is 10s
+  * vlan: fix error message text by removing ptp references
+  * dhcp: daemon dhcp client timeout is configurable
+  * dhcp: timeout value is set in DHCP daemon
+  * remove unused function
+  * deps: go mod tidy coreos/go-iptables
+  * deps: bump coreos/go-iptables
+
+-------------------------------------------------------------------

Old:
----
  cni-plugins-0.9.0.tar.xz

New:
----
  cni-plugins-0.9.1.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ cni-plugins.spec ++++++
--- /var/tmp/diff_new_pack.6g7zL8/_old  2021-04-29 22:44:31.640265316 +0200
+++ /var/tmp/diff_new_pack.6g7zL8/_new  2021-04-29 22:44:31.644265299 +0200
@@ -19,7 +19,7 @@
 %define         cni_bin_dir  %{_libexecdir}/cni
 %define         cni_doc_dir  %{_docdir}/cni-plugins
 Name:           cni-plugins
-Version:        0.9.0
+Version:        0.9.1
 Release:        0
 Summary:        Container Network Interface plugins
 License:        Apache-2.0
@@ -66,7 +66,7 @@
 
 # TODO: copy the READMEs
 #for i in plugins/main/*/README.md ; do
-#      cp Documentation/* %{buildroot}%{cni_doc_dir}/plugins/
+#      cp Documentation/* %%{buildroot}%%{cni_doc_dir}/plugins/
 #done
 
 %post
@@ -79,6 +79,6 @@
 %license LICENSE
 %dir %{cni_bin_dir}
 %{cni_bin_dir}/*
-# %{cni_doc_dir}/plugins/*
+# %%{cni_doc_dir}/plugins/*
 
 %changelog

++++++ _service ++++++
--- /var/tmp/diff_new_pack.6g7zL8/_old  2021-04-29 22:44:31.668265191 +0200
+++ /var/tmp/diff_new_pack.6g7zL8/_new  2021-04-29 22:44:31.672265174 +0200
@@ -4,8 +4,8 @@
     <param name="scm">git</param>
     <param name="filename">cni-plugins</param>
     <param name="exclude">.git</param>
-    <param name="versionformat">0.9.0</param>
-    <param name="revision">v0.9.0</param>
+    <param name="versionformat">0.9.1</param>
+    <param name="revision">v0.9.1</param>
     <param name="changesgenerate">enable</param>
   </service>
   <service name="recompress" mode="disabled">

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.6g7zL8/_old  2021-04-29 22:44:31.688265103 +0200
+++ /var/tmp/diff_new_pack.6g7zL8/_new  2021-04-29 22:44:31.688265103 +0200
@@ -1,4 +1,4 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/containernetworking/plugins.git</param>
-              <param 
name="changesrevision">e13bab99e54b4a34375450518d7db7a3da825e44</param></service></servicedata>
\ No newline at end of file
+              <param 
name="changesrevision">fa48f7515b50272b7106702a662fadbf2ead3d18</param></service></servicedata>
\ No newline at end of file

++++++ cni-plugins-0.9.0.tar.xz -> cni-plugins-0.9.1.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/go.mod new/cni-plugins-0.9.1/go.mod
--- old/cni-plugins-0.9.0/go.mod        2020-12-09 18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/go.mod        2021-02-05 16:38:32.000000000 +0100
@@ -7,8 +7,8 @@
        github.com/Microsoft/hcsshim v0.8.6
        github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae
        github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
-       github.com/containernetworking/cni v0.8.0
-       github.com/coreos/go-iptables v0.4.5
+       github.com/containernetworking/cni v0.8.1
+       github.com/coreos/go-iptables v0.5.0
        github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
        github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
        github.com/d2g/dhcp4client v1.0.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/go.sum new/cni-plugins-0.9.1/go.sum
--- old/cni-plugins-0.9.0/go.sum        2020-12-09 18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/go.sum        2021-02-05 16:38:32.000000000 +0100
@@ -6,10 +6,10 @@
 github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod 
h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
 github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 
h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA=
 github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod 
h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
-github.com/containernetworking/cni v0.8.0 
h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjMCbgybcKI=
-github.com/containernetworking/cni v0.8.0/go.mod 
h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/coreos/go-iptables v0.4.5 
h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
-github.com/coreos/go-iptables v0.4.5/go.mod 
h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/containernetworking/cni v0.8.1 
h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI=
+github.com/containernetworking/cni v0.8.1/go.mod 
h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/coreos/go-iptables v0.5.0 
h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
+github.com/coreos/go-iptables v0.5.0/go.mod 
h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 
h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM=
 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod 
h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c 
h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/dhcp/client.go 
new/cni-plugins-0.9.1/plugins/ipam/dhcp/client.go
--- old/cni-plugins-0.9.0/plugins/ipam/dhcp/client.go   2020-12-09 
18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/ipam/dhcp/client.go   2021-02-05 
16:38:32.000000000 +0100
@@ -1,3 +1,17 @@
+// Copyright 2021 CNI 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 main
 
 import (
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/dhcp/daemon.go 
new/cni-plugins-0.9.1/plugins/ipam/dhcp/daemon.go
--- old/cni-plugins-0.9.0/plugins/ipam/dhcp/daemon.go   2020-12-09 
18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/ipam/dhcp/daemon.go   2021-02-05 
16:38:32.000000000 +0100
@@ -26,6 +26,7 @@
        "path/filepath"
        "runtime"
        "sync"
+       "time"
 
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
@@ -41,11 +42,14 @@
        mux             sync.Mutex
        leases          map[string]*DHCPLease
        hostNetnsPrefix string
+       clientTimeout   time.Duration
+       broadcast       bool
 }
 
-func newDHCP() *DHCP {
+func newDHCP(clientTimeout time.Duration) *DHCP {
        return &DHCP{
-               leases: make(map[string]*DHCPLease),
+               leases:        make(map[string]*DHCPLease),
+               clientTimeout: clientTimeout,
        }
 }
 
@@ -63,7 +67,7 @@
 
        clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
        hostNetns := d.hostNetnsPrefix + args.Netns
-       l, err := AcquireLease(clientID, hostNetns, args.IfName)
+       l, err := AcquireLease(clientID, hostNetns, args.IfName, 
d.clientTimeout, d.broadcast)
        if err != nil {
                return err
        }
@@ -156,7 +160,10 @@
        }
 }
 
-func runDaemon(pidfilePath string, hostPrefix string, socketPath string) error 
{
+func runDaemon(
+       pidfilePath, hostPrefix, socketPath string,
+       dhcpClientTimeout time.Duration, broadcast bool,
+) error {
        // since other goroutines (on separate threads) will change namespaces,
        // ensure the RPC server does not get scheduled onto those
        runtime.LockOSThread()
@@ -176,8 +183,9 @@
                return fmt.Errorf("Error getting listener: %v", err)
        }
 
-       dhcp := newDHCP()
+       dhcp := newDHCP(dhcpClientTimeout)
        dhcp.hostNetnsPrefix = hostPrefix
+       dhcp.broadcast = broadcast
        rpc.Register(dhcp)
        rpc.HandleHTTP()
        http.Serve(l, nil)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/dhcp/lease.go 
new/cni-plugins-0.9.1/plugins/ipam/dhcp/lease.go
--- old/cni-plugins-0.9.0/plugins/ipam/dhcp/lease.go    2020-12-09 
18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/ipam/dhcp/lease.go    2021-02-05 
16:38:32.000000000 +0100
@@ -56,6 +56,8 @@
        renewalTime   time.Time
        rebindingTime time.Time
        expireTime    time.Time
+       timeout       time.Duration
+       broadcast     bool
        stopping      uint32
        stop          chan struct{}
        wg            sync.WaitGroup
@@ -64,11 +66,16 @@
 // AcquireLease gets an DHCP lease and then maintains it in the background
 // by periodically renewing it. The acquired lease can be released by
 // calling DHCPLease.Stop()
-func AcquireLease(clientID, netns, ifName string) (*DHCPLease, error) {
+func AcquireLease(
+       clientID, netns, ifName string,
+       timeout time.Duration, broadcast bool,
+) (*DHCPLease, error) {
        errCh := make(chan error, 1)
        l := &DHCPLease{
-               clientID: clientID,
-               stop:     make(chan struct{}),
+               clientID:  clientID,
+               stop:      make(chan struct{}),
+               timeout:   timeout,
+               broadcast: broadcast,
        }
 
        log.Printf("%v: acquiring lease", clientID)
@@ -115,7 +122,7 @@
 }
 
 func (l *DHCPLease) acquire() error {
-       c, err := newDHCPClient(l.link, l.clientID)
+       c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast)
        if err != nil {
                return err
        }
@@ -242,7 +249,7 @@
 }
 
 func (l *DHCPLease) renew() error {
-       c, err := newDHCPClient(l.link, l.clientID)
+       c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast)
        if err != nil {
                return err
        }
@@ -273,7 +280,7 @@
 func (l *DHCPLease) release() error {
        log.Printf("%v: releasing lease", l.clientID)
 
-       c, err := newDHCPClient(l.link, l.clientID)
+       c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast)
        if err != nil {
                return err
        }
@@ -361,7 +368,11 @@
        return nil, errNoMoreTries
 }
 
-func newDHCPClient(link netlink.Link, clientID string) (*dhcp4client.Client, 
error) {
+func newDHCPClient(
+       link netlink.Link, clientID string,
+       timeout time.Duration,
+       broadcast bool,
+) (*dhcp4client.Client, error) {
        pktsock, err := dhcp4client.NewPacketSock(link.Attrs().Index)
        if err != nil {
                return nil, err
@@ -369,8 +380,8 @@
 
        return dhcp4client.New(
                dhcp4client.HardwareAddr(link.Attrs().HardwareAddr),
-               dhcp4client.Timeout(5*time.Second),
-               dhcp4client.Broadcast(false),
+               dhcp4client.Timeout(timeout),
+               dhcp4client.Broadcast(broadcast),
                dhcp4client.Connection(pktsock),
        )
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/dhcp/main.go 
new/cni-plugins-0.9.1/plugins/ipam/dhcp/main.go
--- old/cni-plugins-0.9.0/plugins/ipam/dhcp/main.go     2020-12-09 
18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/ipam/dhcp/main.go     2021-02-05 
16:38:32.000000000 +0100
@@ -22,6 +22,7 @@
        "net/rpc"
        "os"
        "path/filepath"
+       "time"
 
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
@@ -37,17 +38,21 @@
                var pidfilePath string
                var hostPrefix string
                var socketPath string
+               var broadcast bool
+               var timeout time.Duration
                daemonFlags := flag.NewFlagSet("daemon", flag.ExitOnError)
                daemonFlags.StringVar(&pidfilePath, "pidfile", "", "optional 
path to write daemon PID to")
                daemonFlags.StringVar(&hostPrefix, "hostprefix", "", "optional 
prefix to host root")
                daemonFlags.StringVar(&socketPath, "socketpath", "", "optional 
dhcp server socketpath")
+               daemonFlags.BoolVar(&broadcast, "broadcast", false, "broadcast 
DHCP leases")
+               daemonFlags.DurationVar(&timeout, "timeout", 10*time.Second, 
"optional dhcp client timeout duration")
                daemonFlags.Parse(os.Args[2:])
 
                if socketPath == "" {
                        socketPath = defaultSocketPath
                }
 
-               if err := runDaemon(pidfilePath, hostPrefix, socketPath); err 
!= nil {
+               if err := runDaemon(pidfilePath, hostPrefix, socketPath, 
timeout, broadcast); err != nil {
                        log.Printf(err.Error())
                        os.Exit(1)
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/host-local/main.go 
new/cni-plugins-0.9.1/plugins/ipam/host-local/main.go
--- old/cni-plugins-0.9.0/plugins/ipam/host-local/main.go       2020-12-09 
18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/ipam/host-local/main.go       2021-02-05 
16:38:32.000000000 +0100
@@ -15,7 +15,6 @@
 package main
 
 import (
-       "encoding/json"
        "fmt"
        "net"
        "strings"
@@ -34,14 +33,6 @@
        skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, 
bv.BuildString("host-local"))
 }
 
-func loadNetConf(bytes []byte) (*types.NetConf, string, error) {
-       n := &types.NetConf{}
-       if err := json.Unmarshal(bytes, n); err != nil {
-               return nil, "", fmt.Errorf("failed to load netconf: %v", err)
-       }
-       return n, n.CNIVersion, nil
-}
-
 func cmdCheck(args *skel.CmdArgs) error {
 
        ipamConf, _, err := allocator.LoadIPAMConfig(args.StdinData, args.Args)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cni-plugins-0.9.0/plugins/main/host-device/host-device.go 
new/cni-plugins-0.9.1/plugins/main/host-device/host-device.go
--- old/cni-plugins-0.9.0/plugins/main/host-device/host-device.go       
2020-12-09 18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/main/host-device/host-device.go       
2021-02-05 16:38:32.000000000 +0100
@@ -43,6 +43,9 @@
        sysBusPCI = "/sys/bus/pci/devices"
 )
 
+// Array of different linux drivers bound to network device needed for DPDK
+var userspaceDrivers = []string{"vfio-pci", "uio_pci_generic", "igb_uio"}
+
 //NetConf for host-device config, look the README to learn how to use those 
parameters
 type NetConf struct {
        types.NetConf
@@ -91,6 +94,16 @@
        }
        defer containerNs.Close()
 
+       if len(cfg.PCIAddr) > 0 {
+               isDpdkMode, err := hasDpdkDriver(cfg.PCIAddr)
+               if err != nil {
+                       return fmt.Errorf("error with host device: %v", err)
+               }
+               if isDpdkMode {
+                       return types.PrintResult(&current.Result{}, 
cfg.CNIVersion)
+               }
+       }
+
        hostDev, err := getLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, 
cfg.PCIAddr)
        if err != nil {
                return fmt.Errorf("failed to find host device: %v", err)
@@ -168,6 +181,16 @@
        }
        defer containerNs.Close()
 
+       if len(cfg.PCIAddr) > 0 {
+               isDpdkMode, err := hasDpdkDriver(cfg.PCIAddr)
+               if err != nil {
+                       return fmt.Errorf("error with host device: %v", err)
+               }
+               if isDpdkMode {
+                       return nil
+               }
+       }
+
        if err := moveLinkOut(containerNs, args.IfName); err != nil {
                return err
        }
@@ -255,6 +278,25 @@
        })
 }
 
+func hasDpdkDriver(pciaddr string) (bool, error) {
+       driverLink := filepath.Join(sysBusPCI, pciaddr, "driver")
+       driverPath, err := filepath.EvalSymlinks(driverLink)
+       if err != nil {
+               return false, err
+       }
+       driverStat, err := os.Stat(driverPath)
+       if err != nil {
+               return false, err
+       }
+       driverName := driverStat.Name()
+       for _, drv := range userspaceDrivers {
+               if driverName == drv {
+                       return true, nil
+               }
+       }
+       return false, nil
+}
+
 func printLink(dev netlink.Link, cniVersion string, containerNs ns.NetNS) 
error {
        result := current.Result{
                CNIVersion: current.ImplementedSpecVersion,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/main/vlan/vlan.go 
new/cni-plugins-0.9.1/plugins/main/vlan/vlan.go
--- old/cni-plugins-0.9.0/plugins/main/vlan/vlan.go     2020-12-09 
18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/main/vlan/vlan.go     2021-02-05 
16:38:32.000000000 +0100
@@ -152,7 +152,7 @@
        // run the IPAM plugin and get back the config to apply
        r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
        if err != nil {
-               return err
+               return fmt.Errorf("failed to execute IPAM delegate: %v", err)
        }
 
        // Invoke ipam del if err to avoid ip leak
@@ -238,7 +238,7 @@
                return err
        }
        if conf.NetConf.RawPrevResult == nil {
-               return fmt.Errorf("ptp: Required prevResult missing")
+               return fmt.Errorf("vlan: Required prevResult missing")
        }
        if err := version.ParsePrevResult(&conf.NetConf); err != nil {
                return err
@@ -308,10 +308,10 @@
        }
        link, err = netlink.LinkByName(intf.Name)
        if err != nil {
-               return fmt.Errorf("ptp: Container Interface name in prevResult: 
%s not found", intf.Name)
+               return fmt.Errorf("vlan: Container Interface name in 
prevResult: %s not found", intf.Name)
        }
        if intf.Sandbox == "" {
-               return fmt.Errorf("ptp: Error: Container interface %s should 
not be in host namespace", link.Attrs().Name)
+               return fmt.Errorf("vlan: Error: Container interface %s should 
not be in host namespace", link.Attrs().Name)
        }
 
        vlan, isVlan := link.(*netlink.Vlan)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cni-plugins-0.9.0/plugins/meta/bandwidth/bandwidth_linux_test.go 
new/cni-plugins-0.9.1/plugins/meta/bandwidth/bandwidth_linux_test.go
--- old/cni-plugins-0.9.0/plugins/meta/bandwidth/bandwidth_linux_test.go        
2020-12-09 18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/meta/bandwidth/bandwidth_linux_test.go        
2021-02-05 16:38:32.000000000 +0100
@@ -936,6 +936,7 @@
 
                                containerWithTbfRes, _, err = 
testutils.CmdAdd(containerWithTbfNS.Path(), "dummy", containerWithTbfIFName, 
[]byte(ptpConf), func() error {
                                        r, err := 
invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
+                                       Expect(err).NotTo(HaveOccurred())
                                        Expect(r.Print()).To(Succeed())
 
                                        return err
@@ -944,6 +945,7 @@
 
                                containerWithoutTbfRes, _, err = 
testutils.CmdAdd(containerWithoutTbfNS.Path(), "dummy2", 
containerWithoutTbfIFName, []byte(ptpConf), func() error {
                                        r, err := 
invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
+                                       Expect(err).NotTo(HaveOccurred())
                                        Expect(r.Print()).To(Succeed())
 
                                        return err
@@ -1081,6 +1083,7 @@
 
                                containerWithTbfRes, _, err = 
testutils.CmdAdd(containerWithTbfNS.Path(), "dummy", containerWithTbfIFName, 
[]byte(ptpConf), func() error {
                                        r, err := 
invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
+                                       Expect(err).NotTo(HaveOccurred())
                                        Expect(r.Print()).To(Succeed())
 
                                        return err
@@ -1089,6 +1092,7 @@
 
                                containerWithoutTbfRes, _, err = 
testutils.CmdAdd(containerWithoutTbfNS.Path(), "dummy2", 
containerWithoutTbfIFName, []byte(ptpConf), func() error {
                                        r, err := 
invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
+                                       Expect(err).NotTo(HaveOccurred())
                                        Expect(r.Print()).To(Succeed())
 
                                        return err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/meta/flannel/flannel.go 
new/cni-plugins-0.9.1/plugins/meta/flannel/flannel.go
--- old/cni-plugins-0.9.0/plugins/meta/flannel/flannel.go       2020-12-09 
18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/meta/flannel/flannel.go       2021-02-05 
16:38:32.000000000 +0100
@@ -57,6 +57,8 @@
 type subnetEnv struct {
        nw     *net.IPNet
        sn     *net.IPNet
+       ip6Nw  *net.IPNet
+       ip6Sn  *net.IPNet
        mtu    *uint
        ipmasq *bool
 }
@@ -64,11 +66,11 @@
 func (se *subnetEnv) missing() string {
        m := []string{}
 
-       if se.nw == nil {
-               m = append(m, "FLANNEL_NETWORK")
+       if se.nw == nil && se.ip6Nw == nil {
+               m = append(m, []string{"FLANNEL_NETWORK", 
"FLANNEL_IPV6_NETWORK"}...)
        }
-       if se.sn == nil {
-               m = append(m, "FLANNEL_SUBNET")
+       if se.sn == nil && se.ip6Sn == nil {
+               m = append(m, []string{"FLANNEL_SUBNET", 
"FLANNEL_IPV6_SUBNET"}...)
        }
        if se.mtu == nil {
                m = append(m, "FLANNEL_MTU")
@@ -127,6 +129,18 @@
                        if err != nil {
                                return nil, err
                        }
+
+               case "FLANNEL_IPV6_NETWORK":
+                       _, se.ip6Nw, err = net.ParseCIDR(parts[1])
+                       if err != nil {
+                               return nil, err
+                       }
+
+               case "FLANNEL_IPV6_SUBNET":
+                       _, se.ip6Sn, err = net.ParseCIDR(parts[1])
+                       if err != nil {
+                               return nil, err
+                       }
 
                case "FLANNEL_MTU":
                        mtu, err := strconv.ParseUint(parts[1], 10, 32)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cni-plugins-0.9.0/plugins/meta/flannel/flannel_linux.go 
new/cni-plugins-0.9.1/plugins/meta/flannel/flannel_linux.go
--- old/cni-plugins-0.9.0/plugins/meta/flannel/flannel_linux.go 2020-12-09 
18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/meta/flannel/flannel_linux.go 2021-02-05 
16:38:32.000000000 +0100
@@ -40,13 +40,33 @@
        if !hasKey(ipam, "type") {
                ipam["type"] = "host-local"
        }
-       ipam["subnet"] = fenv.sn.String()
+
+       var rangesSlice [][]map[string]interface{}
+
+       if fenv.sn != nil && fenv.sn.String() != "" {
+               rangesSlice = append(rangesSlice, []map[string]interface{}{
+                       {"subnet": fenv.sn.String()},
+               },
+               )
+       }
+       if fenv.ip6Sn != nil && fenv.ip6Sn.String() != "" {
+               rangesSlice = append(rangesSlice, []map[string]interface{}{
+                       {"subnet": fenv.ip6Sn.String()},
+               },
+               )
+       }
+       ipam["ranges"] = rangesSlice
 
        rtes, err := getIPAMRoutes(n)
        if err != nil {
                return nil, fmt.Errorf("failed to read IPAM routes: %w", err)
        }
-       rtes = append(rtes, types.Route{Dst: *fenv.nw})
+       if fenv.nw != nil {
+               rtes = append(rtes, types.Route{Dst: *fenv.nw})
+       }
+       if fenv.ip6Nw != nil {
+               rtes = append(rtes, types.Route{Dst: *fenv.ip6Nw})
+       }
        ipam["routes"] = rtes
 
        return ipam, nil
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cni-plugins-0.9.0/plugins/meta/flannel/flannel_linux_test.go 
new/cni-plugins-0.9.1/plugins/meta/flannel/flannel_linux_test.go
--- old/cni-plugins-0.9.0/plugins/meta/flannel/flannel_linux_test.go    
2020-12-09 18:16:52.000000000 +0100
+++ new/cni-plugins-0.9.1/plugins/meta/flannel/flannel_linux_test.go    
2021-02-05 16:38:32.000000000 +0100
@@ -30,10 +30,14 @@
 
 var _ = Describe("Flannel", func() {
        var (
-               originalNS ns.NetNS
-               input      string
-               subnetFile string
-               dataDir    string
+               originalNS          ns.NetNS
+               onlyIpv4Input       string
+               onlyIpv6Input       string
+               dualStackInput      string
+               onlyIpv4SubnetFile  string
+               onlyIpv6SubnetFile  string
+               dualStackSubnetFile string
+               dataDir             string
        )
 
        BeforeEach(func() {
@@ -48,10 +52,10 @@
 
        const inputTemplate = `
 {
-  "name": "cni-flannel",
-  "type": "flannel",
-       "subnetFile": "%s",
-       "dataDir": "%s"%s
+    "name": "cni-flannel",
+    "type": "flannel",
+    "subnetFile": "%s",
+    "dataDir": "%s"%s
 }`
 
        const inputIPAMTemplate = `
@@ -70,12 +74,26 @@
       { "dst": "10.96.0.0/12" },
       { "dst": "192.168.244.0/24", "gw": "10.1.17.20" }`
 
-       const flannelSubnetEnv = `
+       const onlyIpv4FlannelSubnetEnv = `
 FLANNEL_NETWORK=10.1.0.0/16
 FLANNEL_SUBNET=10.1.17.1/24
 FLANNEL_MTU=1472
 FLANNEL_IPMASQ=true
 `
+       const onlyIpv6FlannelSubnetEnv = `
+FLANNEL_IPV6_NETWORK=fc00::/48
+FLANNEL_IPV6_SUBNET=fc00::1/64
+FLANNEL_MTU=1472
+FLANNEL_IPMASQ=true
+`
+       const dualStackFlannelSubnetEnv = `
+FLANNEL_NETWORK=10.1.0.0/16
+FLANNEL_SUBNET=10.1.17.1/24
+FLANNEL_IPV6_NETWORK=fc00::/48
+FLANNEL_IPV6_SUBNET=fc00::1/64
+FLANNEL_MTU=1472
+FLANNEL_IPMASQ=true
+`
 
        var writeSubnetEnv = func(contents string) string {
                file, err := ioutil.TempFile("", "subnet.env")
@@ -96,7 +114,7 @@
                return c
        }
 
-       var makeInput = func(inputIPAM string) string {
+       var makeInput = func(inputIPAM string, subnetFile string) string {
                ipamPart := ""
                if len(inputIPAM) > 0 {
                        ipamPart = ",\n  \"ipam\":\n" + inputIPAM
@@ -108,116 +126,350 @@
        BeforeEach(func() {
                var err error
                // flannel subnet.env
-               subnetFile = writeSubnetEnv(flannelSubnetEnv)
+               onlyIpv4SubnetFile = writeSubnetEnv(onlyIpv4FlannelSubnetEnv)
+               onlyIpv6SubnetFile = writeSubnetEnv(onlyIpv6FlannelSubnetEnv)
+               dualStackSubnetFile = writeSubnetEnv(dualStackFlannelSubnetEnv)
 
                // flannel state dir
                dataDir, err = ioutil.TempDir("", "dataDir")
                Expect(err).NotTo(HaveOccurred())
-               input = makeInput("")
+               onlyIpv4Input = makeInput("", onlyIpv4SubnetFile)
+               onlyIpv6Input = makeInput("", onlyIpv6SubnetFile)
+               dualStackInput = makeInput("", dualStackSubnetFile)
        })
 
        AfterEach(func() {
-               os.Remove(subnetFile)
+               os.Remove(onlyIpv4SubnetFile)
+               os.Remove(onlyIpv6SubnetFile)
+               os.Remove(dualStackSubnetFile)
                os.Remove(dataDir)
        })
 
        Describe("CNI lifecycle", func() {
-               It("uses dataDir for storing network configuration", func() {
-                       const IFNAME = "eth0"
+               Context("when using only ipv4 stack", func() {
+                       It("uses dataDir for storing network configuration with 
ipv4 stack", func() {
+                               const IFNAME = "eth0"
+
+                               targetNs, err := testutils.NewNS()
+                               Expect(err).NotTo(HaveOccurred())
+                               defer targetNs.Close()
 
-                       targetNs, err := testutils.NewNS()
-                       Expect(err).NotTo(HaveOccurred())
-                       defer targetNs.Close()
-
-                       args := &skel.CmdArgs{
-                               ContainerID: "some-container-id",
-                               Netns:       targetNs.Path(),
-                               IfName:      IFNAME,
-                               StdinData:   []byte(input),
-                       }
-
-                       err = originalNS.Do(func(ns.NetNS) error {
-                               defer GinkgoRecover()
-
-                               By("calling ADD")
-                               resI, _, err := testutils.CmdAddWithArgs(args, 
func() error {
-                                       return cmdAdd(args)
+                               args := &skel.CmdArgs{
+                                       ContainerID: "some-container-id-ipv4",
+                                       Netns:       targetNs.Path(),
+                                       IfName:      IFNAME,
+                                       StdinData:   []byte(onlyIpv4Input),
+                               }
+
+                               err = originalNS.Do(func(ns.NetNS) error {
+                                       defer GinkgoRecover()
+
+                                       By("calling ADD with ipv4 stack")
+                                       resI, _, err := 
testutils.CmdAddWithArgs(args, func() error {
+                                               return cmdAdd(args)
+                                       })
+                                       Expect(err).NotTo(HaveOccurred())
+
+                                       By("check that plugin writes the net 
config to dataDir with ipv4 stack")
+                                       path := fmt.Sprintf("%s/%s", dataDir, 
"some-container-id-ipv4")
+                                       Expect(path).Should(BeAnExistingFile())
+
+                                       netConfBytes, err := 
ioutil.ReadFile(path)
+                                       Expect(err).NotTo(HaveOccurred())
+                                       expected := `{
+    "ipMasq": false,
+    "ipam": {
+        "routes": [
+            {
+                "dst": "10.1.0.0/16"
+            }
+        ],
+        "ranges": [
+            [{
+                "subnet": "10.1.17.0/24"
+            }]
+        ],
+        "type": "host-local"
+    },
+    "isGateway": true,
+    "mtu": 1472,
+    "name": "cni-flannel",
+    "type": "bridge"
+}
+`
+                                       
Expect(netConfBytes).Should(MatchJSON(expected))
+
+                                       result, err := 
current.NewResultFromResult(resI)
+                                       Expect(err).NotTo(HaveOccurred())
+                                       Expect(result.IPs).To(HaveLen(1))
+
+                                       By("calling DEL with ipv4 stack")
+                                       err = testutils.CmdDelWithArgs(args, 
func() error {
+                                               return cmdDel(args)
+                                       })
+                                       Expect(err).NotTo(HaveOccurred())
+
+                                       By("check that plugin removes net 
config from state dir with ipv4 stack")
+                                       
Expect(path).ShouldNot(BeAnExistingFile())
+
+                                       By("calling DEL again with ipv4 stack")
+                                       err = testutils.CmdDelWithArgs(args, 
func() error {
+                                               return cmdDel(args)
+                                       })
+                                       By("check that plugin does not fail due 
to missing net config with ipv4 stack")
+                                       Expect(err).NotTo(HaveOccurred())
+
+                                       return nil
                                })
                                Expect(err).NotTo(HaveOccurred())
+                       })
+               })
 
-                               By("check that plugin writes the net config to 
dataDir")
-                               path := fmt.Sprintf("%s/%s", dataDir, 
"some-container-id")
-                               Expect(path).Should(BeAnExistingFile())
+               Context("when using only ipv6 stack", func() {
+                       It("uses dataDir for storing network configuration with 
ipv6 stack", func() {
+                               const IFNAME = "eth0"
 
-                               netConfBytes, err := ioutil.ReadFile(path)
+                               targetNs, err := testutils.NewNS()
                                Expect(err).NotTo(HaveOccurred())
-                               expected := `{
-   "ipMasq" : false,
-   "ipam" : {
-      "routes" : [
-         {
-            "dst" : "10.1.0.0/16"
-         }
-      ],
-      "subnet" : "10.1.17.0/24",
-      "type" : "host-local"
-   },
-   "isGateway": true,
-   "mtu" : 1472,
-   "name" : "cni-flannel",
-   "type" : "bridge"
+                               defer targetNs.Close()
+
+                               args := &skel.CmdArgs{
+                                       ContainerID: "some-container-id-ipv6",
+                                       Netns:       targetNs.Path(),
+                                       IfName:      IFNAME,
+                                       StdinData:   []byte(onlyIpv6Input),
+                               }
+
+                               err = originalNS.Do(func(ns.NetNS) error {
+                                       defer GinkgoRecover()
+
+                                       By("calling ADD with ipv6 stack")
+                                       resI, _, err := 
testutils.CmdAddWithArgs(args, func() error {
+                                               return cmdAdd(args)
+                                       })
+                                       Expect(err).NotTo(HaveOccurred())
+
+                                       By("check that plugin writes the net 
config to dataDir with ipv6 stack")
+                                       path := fmt.Sprintf("%s/%s", dataDir, 
"some-container-id-ipv6")
+                                       Expect(path).Should(BeAnExistingFile())
+
+                                       netConfBytes, err := 
ioutil.ReadFile(path)
+                                       Expect(err).NotTo(HaveOccurred())
+                                       expected := `{
+    "ipMasq": false,
+    "ipam": {
+        "routes": [
+            {
+                "dst": "fc00::/48"
+            }
+        ],
+        "ranges": [
+            [{
+                "subnet": "fc00::/64"
+            }]
+        ],
+        "type": "host-local"
+    },
+    "isGateway": true,
+    "mtu": 1472,
+    "name": "cni-flannel",
+    "type": "bridge"
 }
 `
-                               Expect(netConfBytes).Should(MatchJSON(expected))
+                                       
Expect(netConfBytes).Should(MatchJSON(expected))
 
-                               result, err := current.NewResultFromResult(resI)
-                               Expect(err).NotTo(HaveOccurred())
-                               Expect(result.IPs).To(HaveLen(1))
+                                       result, err := 
current.NewResultFromResult(resI)
+                                       Expect(err).NotTo(HaveOccurred())
+                                       Expect(result.IPs).To(HaveLen(1))
+
+                                       By("calling DEL with ipv6 stack")
+                                       err = testutils.CmdDelWithArgs(args, 
func() error {
+                                               return cmdDel(args)
+                                       })
+                                       Expect(err).NotTo(HaveOccurred())
+
+                                       By("check that plugin removes net 
config from state dir with ipv6 stack")
+                                       
Expect(path).ShouldNot(BeAnExistingFile())
+
+                                       By("calling DEL again with ipv6 stack")
+                                       err = testutils.CmdDelWithArgs(args, 
func() error {
+                                               return cmdDel(args)
+                                       })
+                                       By("check that plugin does not fail due 
to missing net config with ipv6 stack")
+                                       Expect(err).NotTo(HaveOccurred())
 
-                               By("calling DEL")
-                               err = testutils.CmdDelWithArgs(args, func() 
error {
-                                       return cmdDel(args)
+                                       return nil
                                })
                                Expect(err).NotTo(HaveOccurred())
+                       })
+               })
 
-                               By("check that plugin removes net config from 
state dir")
-                               Expect(path).ShouldNot(BeAnExistingFile())
+               Context("when using dual stack", func() {
+                       It("uses dataDir for storing network configuration with 
dual stack", func() {
+                               const IFNAME = "eth0"
 
-                               By("calling DEL again")
-                               err = testutils.CmdDelWithArgs(args, func() 
error {
-                                       return cmdDel(args)
-                               })
-                               By("check that plugin does not fail due to 
missing net config")
+                               targetNs, err := testutils.NewNS()
                                Expect(err).NotTo(HaveOccurred())
+                               defer targetNs.Close()
+
+                               args := &skel.CmdArgs{
+                                       ContainerID: 
"some-container-id-dual-stack",
+                                       Netns:       targetNs.Path(),
+                                       IfName:      IFNAME,
+                                       StdinData:   []byte(dualStackInput),
+                               }
+
+                               err = originalNS.Do(func(ns.NetNS) error {
+                                       defer GinkgoRecover()
+
+                                       By("calling ADD with dual stack")
+                                       resI, _, err := 
testutils.CmdAddWithArgs(args, func() error {
+                                               return cmdAdd(args)
+                                       })
+                                       Expect(err).NotTo(HaveOccurred())
+
+                                       By("check that plugin writes the net 
config to dataDir with dual stack")
+                                       path := fmt.Sprintf("%s/%s", dataDir, 
"some-container-id-dual-stack")
+                                       Expect(path).Should(BeAnExistingFile())
+
+                                       netConfBytes, err := 
ioutil.ReadFile(path)
+                                       Expect(err).NotTo(HaveOccurred())
+                                       expected := `{
+    "ipMasq": false,
+    "ipam": {
+        "routes": [
+            {
+                "dst": "10.1.0.0/16"
+            },
+            {
+                "dst": "fc00::/48"
+            }
+        ],
+        "ranges": [
+            [{
+                "subnet": "10.1.17.0/24"
+            }],
+            [{
+                "subnet": "fc00::/64"
+            }]
+        ],
+        "type": "host-local"
+    },
+    "isGateway": true,
+    "mtu": 1472,
+    "name": "cni-flannel",
+    "type": "bridge"
+}
+`
+                                       
Expect(netConfBytes).Should(MatchJSON(expected))
+
+                                       result, err := 
current.NewResultFromResult(resI)
+                                       Expect(err).NotTo(HaveOccurred())
+                                       Expect(result.IPs).To(HaveLen(2))
+
+                                       By("calling DEL with dual stack")
+                                       err = testutils.CmdDelWithArgs(args, 
func() error {
+                                               return cmdDel(args)
+                                       })
+                                       Expect(err).NotTo(HaveOccurred())
+
+                                       By("check that plugin removes net 
config from state dir with dual stack")
+                                       
Expect(path).ShouldNot(BeAnExistingFile())
+
+                                       By("calling DEL again with dual stack")
+                                       err = testutils.CmdDelWithArgs(args, 
func() error {
+                                               return cmdDel(args)
+                                       })
+                                       By("check that plugin does not fail due 
to missing net config with dual stack")
+                                       Expect(err).NotTo(HaveOccurred())
 
-                               return nil
+                                       return nil
+                               })
+                               Expect(err).NotTo(HaveOccurred())
                        })
-                       Expect(err).NotTo(HaveOccurred())
                })
        })
 
        Describe("loadFlannelNetConf", func() {
-               Context("when subnetFile and dataDir are specified", func() {
-                       It("loads flannel network config", func() {
-                               conf, err := loadFlannelNetConf([]byte(input))
+               Context("when subnetFile and dataDir are specified with ipv4 
stack", func() {
+                       It("loads flannel network config with ipv4 stack", 
func() {
+                               conf, err := 
loadFlannelNetConf([]byte(onlyIpv4Input))
+                               Expect(err).ShouldNot(HaveOccurred())
+                               Expect(conf.Name).To(Equal("cni-flannel"))
+                               Expect(conf.Type).To(Equal("flannel"))
+                               
Expect(conf.SubnetFile).To(Equal(onlyIpv4SubnetFile))
+                               Expect(conf.DataDir).To(Equal(dataDir))
+                       })
+               })
+
+               Context("when subnetFile and dataDir are specified with ipv6 
stack", func() {
+                       It("loads flannel network config with ipv6 stack", 
func() {
+                               conf, err := 
loadFlannelNetConf([]byte(onlyIpv6Input))
+                               Expect(err).ShouldNot(HaveOccurred())
+                               Expect(conf.Name).To(Equal("cni-flannel"))
+                               Expect(conf.Type).To(Equal("flannel"))
+                               
Expect(conf.SubnetFile).To(Equal(onlyIpv6SubnetFile))
+                               Expect(conf.DataDir).To(Equal(dataDir))
+                       })
+               })
+
+               Context("when subnetFile and dataDir are specified with dual 
stack", func() {
+                       It("loads flannel network config with dual stack", 
func() {
+                               conf, err := 
loadFlannelNetConf([]byte(dualStackInput))
                                Expect(err).ShouldNot(HaveOccurred())
                                Expect(conf.Name).To(Equal("cni-flannel"))
                                Expect(conf.Type).To(Equal("flannel"))
-                               Expect(conf.SubnetFile).To(Equal(subnetFile))
+                               
Expect(conf.SubnetFile).To(Equal(dualStackSubnetFile))
                                Expect(conf.DataDir).To(Equal(dataDir))
                        })
                })
 
-               Context("when defaulting subnetFile and dataDir", func() {
+               Context("when defaulting subnetFile and dataDir with ipv4 
stack", func() {
+                       BeforeEach(func() {
+                               onlyIpv4Input = `{
+"name": "cni-flannel",
+"type": "flannel"
+}`
+                       })
+
+                       It("loads flannel network config with defaults with 
ipv4 stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(onlyIpv4Input))
+                               Expect(err).ShouldNot(HaveOccurred())
+                               Expect(conf.Name).To(Equal("cni-flannel"))
+                               Expect(conf.Type).To(Equal("flannel"))
+                               
Expect(conf.SubnetFile).To(Equal(defaultSubnetFile))
+                               Expect(conf.DataDir).To(Equal(defaultDataDir))
+                       })
+               })
+
+               Context("when defaulting subnetFile and dataDir with ipv6 
stack", func() {
                        BeforeEach(func() {
-                               input = `{
+                               onlyIpv6Input = `{
 "name": "cni-flannel",
 "type": "flannel"
 }`
                        })
 
-                       It("loads flannel network config with defaults", func() 
{
-                               conf, err := loadFlannelNetConf([]byte(input))
+                       It("loads flannel network config with defaults with 
ipv6 stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(onlyIpv6Input))
+                               Expect(err).ShouldNot(HaveOccurred())
+                               Expect(conf.Name).To(Equal("cni-flannel"))
+                               Expect(conf.Type).To(Equal("flannel"))
+                               
Expect(conf.SubnetFile).To(Equal(defaultSubnetFile))
+                               Expect(conf.DataDir).To(Equal(defaultDataDir))
+                       })
+               })
+
+               Context("when defaulting subnetFile and dataDir with dual 
stack", func() {
+                       BeforeEach(func() {
+                               dualStackInput = `{
+"name": "cni-flannel",
+"type": "flannel"
+}`
+                       })
+
+                       It("loads flannel network config with defaults with 
dual stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(dualStackInput))
                                Expect(err).ShouldNot(HaveOccurred())
                                Expect(conf.Name).To(Equal("cni-flannel"))
                                Expect(conf.Type).To(Equal("flannel"))
@@ -227,9 +479,9 @@
                })
 
                Describe("loadFlannelSubnetEnv", func() {
-                       Context("when flannel subnet env is valid", func() {
-                               It("loads flannel subnet config", func() {
-                                       conf, err := 
loadFlannelSubnetEnv(subnetFile)
+                       Context("when flannel subnet env is valid with ipv4 
stack", func() {
+                               It("loads flannel subnet config with ipv4 
stack", func() {
+                                       conf, err := 
loadFlannelSubnetEnv(onlyIpv4SubnetFile)
                                        Expect(err).ShouldNot(HaveOccurred())
                                        
Expect(conf.nw.String()).To(Equal("10.1.0.0/16"))
                                        
Expect(conf.sn.String()).To(Equal("10.1.17.0/24"))
@@ -239,56 +491,188 @@
                                })
                        })
 
-                       Context("when flannel subnet env is invalid", func() {
+                       Context("when flannel subnet env is valid with ipv6 
stack", func() {
+                               It("loads flannel subnet config with ipv6 
stack", func() {
+                                       conf, err := 
loadFlannelSubnetEnv(onlyIpv6SubnetFile)
+                                       Expect(err).ShouldNot(HaveOccurred())
+                                       
Expect(conf.ip6Nw.String()).To(Equal("fc00::/48"))
+                                       
Expect(conf.ip6Sn.String()).To(Equal("fc00::/64"))
+                                       var mtu uint = 1472
+                                       Expect(*conf.mtu).To(Equal(mtu))
+                                       Expect(*conf.ipmasq).To(BeTrue())
+                               })
+                       })
+
+                       Context("when flannel subnet env is valid with dual 
stack", func() {
+                               It("loads flannel subnet config with dual 
stack", func() {
+                                       conf, err := 
loadFlannelSubnetEnv(dualStackSubnetFile)
+                                       Expect(err).ShouldNot(HaveOccurred())
+                                       
Expect(conf.nw.String()).To(Equal("10.1.0.0/16"))
+                                       
Expect(conf.sn.String()).To(Equal("10.1.17.0/24"))
+                                       
Expect(conf.ip6Nw.String()).To(Equal("fc00::/48"))
+                                       
Expect(conf.ip6Sn.String()).To(Equal("fc00::/64"))
+                                       var mtu uint = 1472
+                                       Expect(*conf.mtu).To(Equal(mtu))
+                                       Expect(*conf.ipmasq).To(BeTrue())
+                               })
+                       })
+
+                       Context("when flannel subnet env is invalid with ipv4 
stack", func() {
+                               BeforeEach(func() {
+                                       onlyIpv4SubnetFile = 
writeSubnetEnv("foo=bar")
+                               })
+                               It("returns an error", func() {
+                                       _, err := 
loadFlannelSubnetEnv(onlyIpv4SubnetFile)
+                                       
Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, 
FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, 
FLANNEL_IPMASQ")))
+                               })
+                       })
+
+                       Context("when flannel subnet env is invalid with ipv6 
stack", func() {
+                               BeforeEach(func() {
+                                       onlyIpv6SubnetFile = 
writeSubnetEnv("foo=bar")
+                               })
+                               It("returns an error", func() {
+                                       _, err := 
loadFlannelSubnetEnv(onlyIpv6SubnetFile)
+                                       
Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, 
FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, 
FLANNEL_IPMASQ")))
+                               })
+                       })
+
+                       Context("when flannel subnet env is invalid with dual 
stack", func() {
                                BeforeEach(func() {
-                                       subnetFile = writeSubnetEnv("foo=bar")
+                                       dualStackSubnetFile = 
writeSubnetEnv("foo=bar")
                                })
                                It("returns an error", func() {
-                                       _, err := 
loadFlannelSubnetEnv(subnetFile)
-                                       
Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, 
FLANNEL_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ")))
+                                       _, err := 
loadFlannelSubnetEnv(dualStackSubnetFile)
+                                       
Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, 
FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, 
FLANNEL_IPMASQ")))
                                })
                        })
                })
        })
 
        Describe("getDelegateIPAM", func() {
-               Context("when input IPAM is provided", func() {
+               Context("when input IPAM is provided with ipv4 stack", func() {
                        BeforeEach(func() {
                                inputIPAM := makeInputIPAM(inputIPAMType, 
inputIPAMRoutes, "")
-                               input = makeInput(inputIPAM)
+                               onlyIpv4Input = makeInput(inputIPAM, 
onlyIpv4SubnetFile)
                        })
-                       It("configures Delegate IPAM accordingly", func() {
-                               conf, err := loadFlannelNetConf([]byte(input))
+                       It("configures Delegate IPAM accordingly with ipv4 
stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(onlyIpv4Input))
                                Expect(err).ShouldNot(HaveOccurred())
-                               fenv, err := loadFlannelSubnetEnv(subnetFile)
+                               fenv, err := 
loadFlannelSubnetEnv(onlyIpv4SubnetFile)
                                Expect(err).ShouldNot(HaveOccurred())
 
                                ipam, err := getDelegateIPAM(conf, fenv)
                                Expect(err).ShouldNot(HaveOccurred())
 
                                podsRoute := "{ \"dst\": \"10.1.0.0/16\" }\n"
-                               subnet := "\"subnet\": \"10.1.17.0/24\""
+                               subnet := "\"ranges\": [[{\"subnet\": 
\"10.1.17.0/24\"}]]"
                                expected := makeInputIPAM(inputIPAMType, 
inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
                                buf, _ := json.Marshal(ipam)
                                Expect(buf).Should(MatchJSON(expected))
                        })
                })
 
-               Context("when input IPAM is provided without 'type'", func() {
+               Context("when input IPAM is provided with ipv6 stack", func() {
+                       BeforeEach(func() {
+                               inputIPAM := makeInputIPAM(inputIPAMType, 
inputIPAMRoutes, "")
+                               onlyIpv6Input = makeInput(inputIPAM, 
onlyIpv6SubnetFile)
+                       })
+                       It("configures Delegate IPAM accordingly with ipv6 
stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(onlyIpv6Input))
+                               Expect(err).ShouldNot(HaveOccurred())
+                               fenv, err := 
loadFlannelSubnetEnv(onlyIpv6SubnetFile)
+                               Expect(err).ShouldNot(HaveOccurred())
+
+                               ipam, err := getDelegateIPAM(conf, fenv)
+                               Expect(err).ShouldNot(HaveOccurred())
+
+                               podsRoute := "{ \"dst\": \"fc00::/48\" }\n"
+                               subnet := "\"ranges\": [[{ \"subnet\": 
\"fc00::/64\" }]]"
+                               expected := makeInputIPAM(inputIPAMType, 
inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
+                               buf, _ := json.Marshal(ipam)
+                               Expect(buf).Should(MatchJSON(expected))
+                       })
+               })
+
+               Context("when input IPAM is provided with dual stack", func() {
+                       BeforeEach(func() {
+                               inputIPAM := makeInputIPAM(inputIPAMType, 
inputIPAMRoutes, "")
+                               dualStackInput = makeInput(inputIPAM, 
dualStackSubnetFile)
+                       })
+                       It("configures Delegate IPAM accordingly with dual 
stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(dualStackInput))
+                               Expect(err).ShouldNot(HaveOccurred())
+                               fenv, err := 
loadFlannelSubnetEnv(dualStackSubnetFile)
+                               Expect(err).ShouldNot(HaveOccurred())
+
+                               ipam, err := getDelegateIPAM(conf, fenv)
+                               Expect(err).ShouldNot(HaveOccurred())
+
+                               podsRoute := "{ \"dst\": \"10.1.0.0/16\" }" + 
",\n" + "{ \"dst\": \"fc00::/48\" }\n"
+                               subnet := "\"ranges\": [[{ \"subnet\": 
\"10.1.17.0/24\" }],\n[{ \"subnet\": \"fc00::/64\" }]]"
+                               expected := makeInputIPAM(inputIPAMType, 
inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
+                               buf, _ := json.Marshal(ipam)
+                               Expect(buf).Should(MatchJSON(expected))
+                       })
+               })
+
+               Context("when input IPAM is provided without 'type' with ipv4 
stack", func() {
                        BeforeEach(func() {
                                inputIPAM := makeInputIPAM("", inputIPAMRoutes, 
"")
-                               input = makeInput(inputIPAM)
+                               onlyIpv4Input = makeInput(inputIPAM, 
onlyIpv4SubnetFile)
                        })
-                       It("configures Delegate IPAM with 'host-local' ipam", 
func() {
-                               conf, err := loadFlannelNetConf([]byte(input))
+                       It("configures Delegate IPAM with 'host-local' ipam 
with ipv4 stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(onlyIpv4Input))
                                Expect(err).ShouldNot(HaveOccurred())
-                               fenv, err := loadFlannelSubnetEnv(subnetFile)
+                               fenv, err := 
loadFlannelSubnetEnv(onlyIpv4SubnetFile)
                                Expect(err).ShouldNot(HaveOccurred())
                                ipam, err := getDelegateIPAM(conf, fenv)
                                Expect(err).ShouldNot(HaveOccurred())
 
                                podsRoute := "{ \"dst\": \"10.1.0.0/16\" }\n"
-                               subnet := "\"subnet\": \"10.1.17.0/24\""
+                               subnet := "\"ranges\": [[{\"subnet\": 
\"10.1.17.0/24\"}]]"
+                               expected := makeInputIPAM("host-local", 
inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
+                               buf, _ := json.Marshal(ipam)
+                               Expect(buf).Should(MatchJSON(expected))
+                       })
+               })
+
+               Context("when input IPAM is provided without 'type' with ipv6 
stack", func() {
+                       BeforeEach(func() {
+                               inputIPAM := makeInputIPAM("", inputIPAMRoutes, 
"")
+                               onlyIpv6Input = makeInput(inputIPAM, 
onlyIpv6SubnetFile)
+                       })
+                       It("configures Delegate IPAM with 'host-local' ipam 
with ipv6 stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(onlyIpv6Input))
+                               Expect(err).ShouldNot(HaveOccurred())
+                               fenv, err := 
loadFlannelSubnetEnv(onlyIpv6SubnetFile)
+                               Expect(err).ShouldNot(HaveOccurred())
+                               ipam, err := getDelegateIPAM(conf, fenv)
+                               Expect(err).ShouldNot(HaveOccurred())
+
+                               podsRoute := "{ \"dst\": \"fc00::/48\" }\n"
+                               subnet := "\"ranges\": [[{ \"subnet\": 
\"fc00::/64\" }]]"
+                               expected := makeInputIPAM("host-local", 
inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
+                               buf, _ := json.Marshal(ipam)
+                               Expect(buf).Should(MatchJSON(expected))
+                       })
+               })
+
+               Context("when input IPAM is provided without 'type' with dual 
stack", func() {
+                       BeforeEach(func() {
+                               inputIPAM := makeInputIPAM("", inputIPAMRoutes, 
"")
+                               dualStackInput = makeInput(inputIPAM, 
dualStackSubnetFile)
+                       })
+                       It("configures Delegate IPAM with 'host-local' ipam 
with dual stack", func() {
+                               conf, err := 
loadFlannelNetConf([]byte(dualStackInput))
+                               Expect(err).ShouldNot(HaveOccurred())
+                               fenv, err := 
loadFlannelSubnetEnv(dualStackSubnetFile)
+                               Expect(err).ShouldNot(HaveOccurred())
+                               ipam, err := getDelegateIPAM(conf, fenv)
+                               Expect(err).ShouldNot(HaveOccurred())
+
+                               podsRoute := "{ \"dst\": \"10.1.0.0/16\" }" + 
",\n" + "{ \"dst\": \"fc00::/48\" }\n"
+                               subnet := "\"ranges\": [[{ \"subnet\": 
\"10.1.17.0/24\" }],\n[{ \"subnet\": \"fc00::/64\" }]]"
                                expected := makeInputIPAM("host-local", 
inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
                                buf, _ := json.Marshal(ipam)
                                Expect(buf).Should(MatchJSON(expected))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cni-plugins-0.9.0/vendor/github.com/containernetworking/cni/pkg/invoke/find.go
 
new/cni-plugins-0.9.1/vendor/github.com/containernetworking/cni/pkg/invoke/find.go
--- 
old/cni-plugins-0.9.0/vendor/github.com/containernetworking/cni/pkg/invoke/find.go
  2020-12-09 18:16:52.000000000 +0100
+++ 
new/cni-plugins-0.9.1/vendor/github.com/containernetworking/cni/pkg/invoke/find.go
  2021-02-05 16:38:32.000000000 +0100
@@ -18,6 +18,7 @@
        "fmt"
        "os"
        "path/filepath"
+       "strings"
 )
 
 // FindInPath returns the full path of the plugin by searching in the provided 
path
@@ -26,6 +27,10 @@
                return "", fmt.Errorf("no plugin name provided")
        }
 
+       if strings.ContainsRune(plugin, os.PathSeparator) {
+               return "", fmt.Errorf("invalid plugin name: %s", plugin)
+       }
+
        if len(paths) == 0 {
                return "", fmt.Errorf("no paths provided")
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cni-plugins-0.9.0/vendor/github.com/coreos/go-iptables/iptables/iptables.go 
new/cni-plugins-0.9.1/vendor/github.com/coreos/go-iptables/iptables/iptables.go
--- 
old/cni-plugins-0.9.0/vendor/github.com/coreos/go-iptables/iptables/iptables.go 
    2020-12-09 18:16:52.000000000 +0100
+++ 
new/cni-plugins-0.9.1/vendor/github.com/coreos/go-iptables/iptables/iptables.go 
    2021-02-05 16:38:32.000000000 +0100
@@ -31,7 +31,6 @@
        exec.ExitError
        cmd        exec.Cmd
        msg        string
-       proto      Protocol
        exitStatus *int //for overriding
 }
 
@@ -51,9 +50,8 @@
        if e.ExitStatus() != 1 {
                return false
        }
-       cmdIptables := getIptablesCommand(e.proto)
-       msgNoRuleExist := fmt.Sprintf("%s: Bad rule (does a matching rule exist 
in that chain?).\n", cmdIptables)
-       msgNoChainExist := fmt.Sprintf("%s: No chain/target/match by that 
name.\n", cmdIptables)
+       msgNoRuleExist := "Bad rule (does a matching rule exist in that 
chain?).\n"
+       msgNoChainExist := "No chain/target/match by that name.\n"
        return strings.Contains(e.msg, msgNoRuleExist) || 
strings.Contains(e.msg, msgNoChainExist)
 }
 
@@ -75,6 +73,7 @@
        v2             int
        v3             int
        mode           string // the underlying iptables operating mode, e.g. 
nf_tables
+       timeout        int    // time to wait for the iptables lock, default 
waits forever
 }
 
 // Stat represents a structured statistic entry.
@@ -91,19 +90,42 @@
        Options     string     `json:"options"`
 }
 
-// New creates a new IPTables.
-// For backwards compatibility, this always uses IPv4, i.e. "iptables".
-func New() (*IPTables, error) {
-       return NewWithProtocol(ProtocolIPv4)
+type option func(*IPTables)
+
+func IPFamily(proto Protocol) option {
+       return func(ipt *IPTables) {
+               ipt.proto = proto
+       }
 }
 
-// New creates a new IPTables for the given proto.
-// The proto will determine which command is used, either "iptables" or 
"ip6tables".
-func NewWithProtocol(proto Protocol) (*IPTables, error) {
-       path, err := exec.LookPath(getIptablesCommand(proto))
+func Timeout(timeout int) option {
+       return func(ipt *IPTables) {
+               ipt.timeout = timeout
+       }
+}
+
+// New creates a new IPTables configured with the options passed as parameter.
+// For backwards compatibility, by default always uses IPv4 and timeout 0.
+// i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing
+// the IPFamily and Timeout options as follow:
+//     ip6t := New(IPFamily(ProtocolIPv6), Timeout(5))
+func New(opts ...option) (*IPTables, error) {
+
+       ipt := &IPTables{
+               proto:   ProtocolIPv4,
+               timeout: 0,
+       }
+
+       for _, opt := range opts {
+               opt(ipt)
+       }
+
+       path, err := exec.LookPath(getIptablesCommand(ipt.proto))
        if err != nil {
                return nil, err
        }
+       ipt.path = path
+
        vstring, err := getIptablesVersionString(path)
        if err != nil {
                return nil, fmt.Errorf("could not get iptables version: %v", 
err)
@@ -112,21 +134,23 @@
        if err != nil {
                return nil, fmt.Errorf("failed to extract iptables version from 
[%s]: %v", vstring, err)
        }
+       ipt.v1 = v1
+       ipt.v2 = v2
+       ipt.v3 = v3
+       ipt.mode = mode
 
        checkPresent, waitPresent, randomFullyPresent := 
getIptablesCommandSupport(v1, v2, v3)
+       ipt.hasCheck = checkPresent
+       ipt.hasWait = waitPresent
+       ipt.hasRandomFully = randomFullyPresent
 
-       ipt := IPTables{
-               path:           path,
-               proto:          proto,
-               hasCheck:       checkPresent,
-               hasWait:        waitPresent,
-               hasRandomFully: randomFullyPresent,
-               v1:             v1,
-               v2:             v2,
-               v3:             v3,
-               mode:           mode,
-       }
-       return &ipt, nil
+       return ipt, nil
+}
+
+// New creates a new IPTables for the given proto.
+// The proto will determine which command is used, either "iptables" or 
"ip6tables".
+func NewWithProtocol(proto Protocol) (*IPTables, error) {
+       return New(IPFamily(proto), Timeout(0))
 }
 
 // Proto returns the protocol used by this IPTables.
@@ -185,6 +209,14 @@
        return ipt.run(cmd...)
 }
 
+func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) 
error {
+       exists, err := ipt.Exists(table, chain, rulespec...)
+       if err == nil && exists {
+               err = ipt.Delete(table, chain, rulespec...)
+       }
+       return err
+}
+
 // List rules in specified table/chain
 func (ipt *IPTables) List(table, chain string) ([]string, error) {
        args := []string{"-t", table, "-S", chain}
@@ -222,6 +254,21 @@
        return chains, nil
 }
 
+// '-S' is fine with non existing rule index as long as the chain exists
+// therefore pass index 1 to reduce overhead for large chains
+func (ipt *IPTables) ChainExists(table, chain string) (bool, error) {
+       err := ipt.run("-t", table, "-S", chain, "1")
+       eerr, eok := err.(*Error)
+       switch {
+       case err == nil:
+               return true, nil
+       case eok && eerr.ExitStatus() == 1:
+               return false, nil
+       default:
+               return false, err
+       }
+}
+
 // Stats lists rules including the byte and packet counts
 func (ipt *IPTables) Stats(table, chain string) ([][]string, error) {
        args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"}
@@ -401,6 +448,18 @@
        return ipt.run("-t", table, "-X", chain)
 }
 
+func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error {
+       exists, err := ipt.ChainExists(table, chain)
+       if err != nil || !exists {
+               return err
+       }
+       err = ipt.run("-t", table, "-F", chain)
+       if err == nil {
+               err = ipt.run("-t", table, "-X", chain)
+       }
+       return err
+}
+
 // ChangePolicy changes policy on chain to target
 func (ipt *IPTables) ChangePolicy(table, chain, target string) error {
        return ipt.run("-t", table, "-P", chain, target)
@@ -428,6 +487,9 @@
        args = append([]string{ipt.path}, args...)
        if ipt.hasWait {
                args = append(args, "--wait")
+               if ipt.timeout != 0 {
+                       args = append(args, strconv.Itoa(ipt.timeout))
+               }
        } else {
                fmu, err := newXtablesFileLock()
                if err != nil {
@@ -452,7 +514,7 @@
        if err := cmd.Run(); err != nil {
                switch e := err.(type) {
                case *exec.ExitError:
-                       return &Error{*e, cmd, stderr.String(), ipt.proto, nil}
+                       return &Error{*e, cmd, stderr.String(), nil}
                default:
                        return err
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cni-plugins-0.9.0/vendor/modules.txt 
new/cni-plugins-0.9.1/vendor/modules.txt
--- old/cni-plugins-0.9.0/vendor/modules.txt    2020-12-09 18:16:52.000000000 
+0100
+++ new/cni-plugins-0.9.1/vendor/modules.txt    2021-02-05 16:38:32.000000000 
+0100
@@ -28,7 +28,7 @@
 # github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
 ## explicit
 github.com/buger/jsonparser
-# github.com/containernetworking/cni v0.8.0
+# github.com/containernetworking/cni v0.8.1
 ## explicit
 github.com/containernetworking/cni/libcni
 github.com/containernetworking/cni/pkg/invoke
@@ -38,7 +38,7 @@
 github.com/containernetworking/cni/pkg/types/current
 github.com/containernetworking/cni/pkg/utils
 github.com/containernetworking/cni/pkg/version
-# github.com/coreos/go-iptables v0.4.5
+# github.com/coreos/go-iptables v0.5.0
 ## explicit
 github.com/coreos/go-iptables/iptables
 # github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7

Reply via email to