Large diffs are not rendered by default.

@@ -106,7 +106,7 @@ func makeTcpClientInNS(netns string, address string, port int, numBytes int) {
Expect(string(out)).To(Equal(message))
}

func createVeth(hostNamespace string, hostVethIfName string, containerNamespace string, containerVethIfName string, hostIP []byte, containerIP []byte, hostIfaceMTU int) {
func createVeth(hostNs ns.NetNS, hostVethIfName string, containerNs ns.NetNS, containerVethIfName string, hostIP []byte, containerIP []byte, hostIfaceMTU int) {
vethDeviceRequest := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: hostVethIfName,
@@ -116,10 +116,7 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
PeerName: containerVethIfName,
}

hostNs, err := ns.GetNS(hostNamespace)
Expect(err).NotTo(HaveOccurred())

err = hostNs.Do(func(_ ns.NetNS) error {
err := hostNs.Do(func(_ ns.NetNS) error {
if err := netlink.LinkAdd(vethDeviceRequest); err != nil {
return fmt.Errorf("creating veth pair: %s", err)
}
@@ -129,11 +126,6 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
return fmt.Errorf("failed to find newly-created veth device %q: %v", containerVethIfName, err)
}

containerNs, err := ns.GetNS(containerNamespace)
if err != nil {
return err
}

err = netlink.LinkSetNsFd(containerVeth, int(containerNs.Fd()))
if err != nil {
return fmt.Errorf("failed to move veth to container namespace: %s", err)
@@ -169,8 +161,6 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
})
Expect(err).NotTo(HaveOccurred())

containerNs, err := ns.GetNS(containerNamespace)
Expect(err).NotTo(HaveOccurred())
err = containerNs.Do(func(_ ns.NetNS) error {
peerAddr := &net.IPNet{
IP: hostIP,
@@ -203,7 +193,7 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
Expect(err).NotTo(HaveOccurred())
}

func createVethInOneNs(namespace, vethName, peerName string) {
func createVethInOneNs(netNS ns.NetNS, vethName, peerName string) {
vethDeviceRequest := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: vethName,
@@ -212,10 +202,7 @@ func createVethInOneNs(namespace, vethName, peerName string) {
PeerName: peerName,
}

netNS, err := ns.GetNS(namespace)
Expect(err).NotTo(HaveOccurred())

err = netNS.Do(func(_ ns.NetNS) error {
err := netNS.Do(func(_ ns.NetNS) error {
if err := netlink.LinkAdd(vethDeviceRequest); err != nil {
return fmt.Errorf("failed to create veth pair: %v", err)
}
@@ -229,11 +216,8 @@ func createVethInOneNs(namespace, vethName, peerName string) {
Expect(err).NotTo(HaveOccurred())
}

func createMacvlan(namespace, master, macvlanName string) {
netNS, err := ns.GetNS(namespace)
Expect(err).NotTo(HaveOccurred())

err = netNS.Do(func(_ ns.NetNS) error {
func createMacvlan(netNS ns.NetNS, master, macvlanName string) {
err := netNS.Do(func(_ ns.NetNS) error {
m, err := netlink.LinkByName(master)
if err != nil {
return fmt.Errorf("failed to lookup master %q: %v", master, err)
@@ -23,7 +23,7 @@ import (

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"

"github.com/containernetworking/plugins/pkg/ip"
@@ -1,5 +1,5 @@

This document has moved to the [containernetworking/cni.dev](https://github.com/containernetworking/cni.dev) repo.

You can find it online here: https://cni.dev/plugins/meta/firewall/
You can find it online here: https://cni.dev/plugins/current/meta/firewall/

@@ -24,7 +24,7 @@ import (

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"

bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
@@ -130,7 +130,9 @@ func cmdAdd(args *skel.CmdArgs) error {
}

if result == nil {
result = &current.Result{}
result = &current.Result{
CNIVersion: current.ImplementedSpecVersion,
}
}
return types.PrintResult(result, conf.CNIVersion)
}
@@ -24,40 +24,17 @@ import (

"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"

"github.com/godbus/dbus"
"github.com/godbus/dbus/v5"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

const (
confTmpl = `{
"cniVersion": "0.3.1",
"name": "firewalld-test",
"type": "firewall",
"backend": "firewalld",
"zone": "trusted",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{"name": "%s", "sandbox": "%s"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`
ifname = "eth0"
)
const ifname = "eth0"

type fakeFirewalld struct {
zone string
@@ -125,6 +102,30 @@ func spawnSessionDbus(wg *sync.WaitGroup) (string, *exec.Cmd) {
return busAddr, cmd
}

func makeFirewalldConf(ver, ifname string, ns ns.NetNS) []byte {
return []byte(fmt.Sprintf(`{
"cniVersion": "%s",
"name": "firewalld-test",
"type": "firewall",
"backend": "firewalld",
"zone": "trusted",
"prevResult": {
"cniVersion": "%s",
"interfaces": [
{"name": "%s", "sandbox": "%s"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`, ver, ver, ifname, ns.Path()))
}

var _ = Describe("firewalld test", func() {
var (
targetNs ns.NetNS
@@ -177,167 +178,119 @@ var _ = Describe("firewalld test", func() {
Expect(err).NotTo(HaveOccurred())

wg.Wait()
})

It("works with a 0.3.1 config", func() {
Expect(isFirewalldRunning()).To(BeTrue())

conf := fmt.Sprintf(confTmpl, ifname, targetNs.Path())
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
fwd.clear()

err = testutils.CmdDel(targetNs.Path(), args.ContainerID, ifname, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
})

It("defaults to the firewalld backend", func() {
conf := `{
"cniVersion": "0.3.1",
"name": "firewalld-test",
"type": "firewall",
"zone": "trusted",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{"name": "eth0", "sandbox": "/foobar"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`

Expect(isFirewalldRunning()).To(BeTrue())

args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
Expect(targetNs.Close()).To(Succeed())
Expect(testutils.UnmountNS(targetNs)).To(Succeed())
})

It("passes through the prevResult", func() {
conf := `{
"cniVersion": "0.3.1",
"name": "firewalld-test",
"type": "firewall",
"zone": "trusted",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{"name": "eth0", "sandbox": "/foobar"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`

Expect(isFirewalldRunning()).To(BeTrue())

args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
// firewall plugin requires a prevResult and thus only supports 0.3.0
// and later CNI versions
for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
ver := ver

It(fmt.Sprintf("[%s] works with a config", ver), func() {
Expect(isFirewalldRunning()).To(BeTrue())

conf := makeFirewalldConf(ver, ifname, targetNs)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
fwd.clear()

err = testutils.CmdDel(targetNs.Path(), args.ContainerID, ifname, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
})
Expect(err).NotTo(HaveOccurred())

result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())

Expect(len(result.Interfaces)).To(Equal(1))
Expect(result.Interfaces[0].Name).To(Equal("eth0"))
Expect(len(result.IPs)).To(Equal(1))
Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24"))
})

It("works with a 0.4.0 config, including Check", func() {
Expect(isFirewalldRunning()).To(BeTrue())

conf := `{
"cniVersion": "0.4.0",
"name": "firewalld-test",
"type": "firewall",
"backend": "firewalld",
"zone": "trusted",
"prevResult": {
"cniVersion": "0.4.0",
"interfaces": [
{"name": "eth0", "sandbox": "/foobar"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`

args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
It(fmt.Sprintf("[%s] defaults to the firewalld backend", ver), func() {
Expect(isFirewalldRunning()).To(BeTrue())

conf := makeFirewalldConf(ver, ifname, targetNs)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))

_, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())

err = testutils.CmdCheckWithArgs(args, func() error {
return cmdCheck(args)
It(fmt.Sprintf("[%s] passes through the prevResult", ver), func() {
Expect(isFirewalldRunning()).To(BeTrue())

conf := makeFirewalldConf(ver, ifname, targetNs)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())

result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())

Expect(len(result.Interfaces)).To(Equal(1))
Expect(result.Interfaces[0].Name).To(Equal("eth0"))
Expect(len(result.IPs)).To(Equal(1))
Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24"))
})
Expect(err).NotTo(HaveOccurred())

err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
It(fmt.Sprintf("[%s] works with Check", ver), func() {
Expect(isFirewalldRunning()).To(BeTrue())

conf := makeFirewalldConf(ver, ifname, targetNs)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))

if testutils.SpecVersionHasCHECK(ver) {
_, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())

err = testutils.CmdCheckWithArgs(args, func() error {
return cmdCheck(args)
})
Expect(err).NotTo(HaveOccurred())
}

err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
})
}
})

Large diffs are not rendered by default.

@@ -18,8 +18,8 @@ import (
"fmt"
"strings"

"github.com/containernetworking/cni/pkg/types/current"
"github.com/godbus/dbus"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/godbus/dbus/v5"
)

const (
@@ -21,7 +21,7 @@ import (
"fmt"
"net"

"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/utils"
"github.com/coreos/go-iptables/iptables"
)

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

@@ -1,5 +1,5 @@

This document has moved to the [containernetworking/cni.dev](https://github.com/containernetworking/cni.dev) repo.

You can find it online here: https://cni.dev/plugins/meta/portmap/
You can find it online here: https://cni.dev/plugins/current/meta/portmap/

@@ -33,7 +33,7 @@ import (

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
"golang.org/x/sys/unix"

@@ -223,9 +223,10 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er

if conf.PrevResult != nil {
for _, ip := range result.IPs {
if ip.Version == "6" && conf.ContIPv6.IP != nil {
isIPv4 := ip.Address.IP.To4() != nil
if !isIPv4 && conf.ContIPv6.IP != nil {
continue
} else if ip.Version == "4" && conf.ContIPv4.IP != nil {
} else if isIPv4 && conf.ContIPv4.IP != nil {
continue
}

@@ -239,11 +240,10 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
continue
}
}
switch ip.Version {
case "6":
conf.ContIPv6 = ip.Address
case "4":
if ip.Address.IP.To4() != nil {
conf.ContIPv4 = ip.Address
} else {
conf.ContIPv6 = ip.Address
}
}
}
@@ -337,7 +337,7 @@ func genMarkMasqChain(markBit int) chain {
// enableLocalnetRouting tells the kernel not to treat 127/8 as a martian,
// so that connections with a source ip of 127/8 can cross a routing boundary.
func enableLocalnetRouting(ifName string) error {
routeLocalnetPath := "net.ipv4.conf." + ifName + ".route_localnet"
routeLocalnetPath := "net/ipv4/conf/" + ifName + "/route_localnet"
_, err := sysctl.Sysctl(routeLocalnetPath, "1")
return err
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -1,5 +1,5 @@

This document has moved to the [containernetworking/cni.dev](https://github.com/containernetworking/cni.dev) repo.

You can find it online here: https://cni.dev/plugins/meta/sbr/
You can find it online here: https://cni.dev/plugins/current/meta/sbr/

@@ -26,7 +26,7 @@ import (

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"

"github.com/containernetworking/plugins/pkg/ns"
@@ -176,23 +176,9 @@ func cmdAdd(args *skel.CmdArgs) error {
return types.PrintResult(conf.PrevResult, conf.CNIVersion)
}

// doRoutes does all the work to set up routes and rules during an add.
func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface string) error {
// Get a list of rules and routes ready.
rules, err := netlink.RuleList(netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("Failed to list all rules: %v", err)
}

routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("Failed to list all routes: %v", err)
}

// Pick a table ID to use. We pick the first table ID from firstTableID
// on that has no existing rules mapping to it and no existing routes in
// it.
table := firstTableID
// getNextTableID picks the first free table id from a giveen candidate id
func getNextTableID(rules []netlink.Rule, routes []netlink.Route, candidateID int) int {
table := candidateID
for {
foundExisting := false
for _, rule := range rules {
@@ -215,7 +201,26 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
break
}
}
return table
}

// doRoutes does all the work to set up routes and rules during an add.
func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface string) error {
// Get a list of rules and routes ready.
rules, err := netlink.RuleList(netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("Failed to list all rules: %v", err)
}

routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("Failed to list all routes: %v", err)
}

// Pick a table ID to use. We pick the first table ID from firstTableID
// on that has no existing rules mapping to it and no existing routes in
// it.
table := getNextTableID(rules, routes, firstTableID)
log.Printf("First unreferenced table: %d", table)

link, err := netlink.LinkByName(iface)
@@ -225,6 +230,12 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin

linkIndex := link.Attrs().Index

// Get all routes for the interface in the default routing table
routes, err = netlink.RouteList(link, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("Unable to list routes: %v", err)
}

// Loop through setting up source based rules and default routes.
for _, ipCfg := range ipCfgs {
log.Printf("Set rule for source %s", ipCfg.String())
@@ -234,7 +245,7 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
// Source must be restricted to a single IP, not a full subnet
var src net.IPNet
src.IP = ipCfg.Address.IP
if ipCfg.Version == "4" {
if src.IP.To4() != nil {
src.Mask = net.CIDRMask(32, 32)
} else {
src.Mask = net.CIDRMask(128, 128)
@@ -253,7 +264,7 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
log.Printf("Adding default route to gateway %s", ipCfg.Gateway.String())

var dest net.IPNet
if ipCfg.Version == "4" {
if ipCfg.Address.IP.To4() != nil {
dest.IP = net.IPv4zero
dest.Mask = net.CIDRMask(0, 32)
} else {
@@ -274,38 +285,48 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
err)
}
}
}

// Move all routes into the correct table. We are taking a shortcut; all
// the routes have been added to the interface anyway but in the wrong
// table, so instead of removing them we just move them to the table we
// want them in.
routes, err = netlink.RouteList(link, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("Unable to list routes: %v", err)
// Copy the previously added routes for the interface to the correct
// table; all the routes have been added to the interface anyway but
// in the wrong table, so instead of removing them we just move them
// to the table we want them in.
for _, r := range routes {
if ipCfg.Address.Contains(r.Src) || ipCfg.Address.Contains(r.Gw) ||
(r.Src == nil && r.Gw == nil) {
// (r.Src == nil && r.Gw == nil) is inferred as a generic route
log.Printf("Copying route %s from table %d to %d",
r.String(), r.Table, table)

r.Table = table

// Reset the route flags since if it is dynamically created,
// adding it to the new table will fail with "invalid argument"
r.Flags = 0

// We use route replace in case the route already exists, which
// is possible for the default gateway we added above.
err = netlink.RouteReplace(&r)
if err != nil {
return fmt.Errorf("Failed to readd route: %v", err)
}
}
}

// Use a different table for each ipCfg
table++
table = getNextTableID(rules, routes, table)
}

// Delete all the interface routes in the default routing table, which were
// copied to source based routing tables.
// Not deleting them while copying to accommodate for multiple ipCfgs from
// the same subnet. Else, (error for network is unreachable while adding gateway)
for _, route := range routes {
log.Printf("Moving route %s from table %d to %d",
route.String(), route.Table, table)

log.Printf("Deleting route %s from table %d", route.String(), route.Table)
err := netlink.RouteDel(&route)
if err != nil {
return fmt.Errorf("Failed to delete route: %v", err)
}

route.Table = table

// Reset the route flags since if it is dynamically created,
// adding it to the new table will fail with "invalid argument"
route.Flags = 0

// We use route replace in case the route already exists, which
// is possible for the default gateway we added above.
err = netlink.RouteReplace(&route)
if err != nil {
return fmt.Errorf("Failed to readd route: %v", err)
}
}

return nil
@@ -241,6 +241,7 @@ var _ = Describe("sbr test", func() {
"name": "cni-plugin-sbr-test",
"type": "sbr",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{
"name": "%s",
@@ -332,6 +333,7 @@ var _ = Describe("sbr test", func() {
"name": "cni-plugin-sbr-test",
"type": "sbr",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{
"name": "%s",
@@ -399,20 +401,129 @@ var _ = Describe("sbr test", func() {
Expect(equalRoutes(expNet1.Routes, devNet1.Routes)).To(BeTrue())
})

It("works with a 0.2.0 config", func() {
It("Works with multiple IPs", func() {
ifname := "net1"
conf := `{
"cniVersion": "0.2.0",
"cniVersion": "0.3.0",
"name": "cni-plugin-sbr-test",
"type": "sbr",
"anotherAwesomeArg": "foo",
"prevResult": {
"ip4": {
"ip": "192.168.1.209/24",
"gateway": "192.168.1.1",
"routes": []
}
"cniVersion": "0.3.0",
"interfaces": [
{
"name": "%s",
"sandbox": "%s"
}
],
"ips": [
{
"version": "4",
"address": "192.168.1.209/24",
"gateway": "192.168.1.1",
"interface": 0
},
{
"version": "4",
"address": "192.168.101.209/24",
"gateway": "192.168.101.1",
"interface": 1
}
],
"routes": []
}
}`
conf = fmt.Sprintf(conf, ifname, targetNs.Path())
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}

preStatus := createDefaultStatus()

// Add the second IP on net1 interface
preStatus.Devices[1].Addrs = append(preStatus.Devices[1].Addrs,
net.IPNet{
IP: net.IPv4(192, 168, 101, 209),
Mask: net.IPv4Mask(255, 255, 255, 0),
})

err := setup(targetNs, preStatus)
Expect(err).NotTo(HaveOccurred())

oldStatus, err := readback(targetNs, []string{"net1", "eth0"})
Expect(err).NotTo(HaveOccurred())

_, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred())

newStatus, err := readback(targetNs, []string{"net1", "eth0"})
Expect(err).NotTo(HaveOccurred())

// Check results. We expect all the routes on net1 to have moved to
// table 100 except for local routes (table 255); a new default gateway
// route to have been created; and 2 rules to exist.
expNet1 := oldStatus.Devices[0]
expEth0 := oldStatus.Devices[1]

for i := range expNet1.Routes {
if expNet1.Routes[i].Table != 255 {
if expNet1.Routes[i].Src.String() == "192.168.101.209" {
// All 192.168.101.x routes expected in table 101
expNet1.Routes[i].Table = 101
} else if expNet1.Routes[i].Src == nil && expNet1.Routes[i].Gw == nil {
// Generic Link Local Addresses assigned. They should exist in all
// route tables
expNet1.Routes[i].Table = 100
expNet1.Routes = append(expNet1.Routes,
netlink.Route{
Dst: expNet1.Routes[i].Dst,
Table: 101,
LinkIndex: expNet1.Routes[i].LinkIndex})
} else {
// All 192.168.1.x routes expected in table 100
expNet1.Routes[i].Table = 100
}
}
}
expNet1.Routes = append(expNet1.Routes,
netlink.Route{
Gw: net.IPv4(192, 168, 1, 1),
Table: 100,
LinkIndex: expNet1.Routes[0].LinkIndex})

expNet1.Routes = append(expNet1.Routes,
netlink.Route{
Gw: net.IPv4(192, 168, 101, 1),
Table: 101,
LinkIndex: expNet1.Routes[0].LinkIndex})

// 2 Rules will be created for each IP address. (100, 101)
Expect(len(newStatus.Rules)).To(Equal(2))

// First entry corresponds to last table
Expect(newStatus.Rules[0].Table).To(Equal(101))
Expect(newStatus.Rules[0].Src.String()).To(Equal("192.168.101.209/32"))

// Second entry corresponds to first table (100)
Expect(newStatus.Rules[1].Table).To(Equal(100))
Expect(newStatus.Rules[1].Src.String()).To(Equal("192.168.1.209/32"))

devNet1 := newStatus.Devices[0]
devEth0 := newStatus.Devices[1]
Expect(equalRoutes(expNet1.Routes, devNet1.Routes)).To(BeTrue())
Expect(equalRoutes(expEth0.Routes, devEth0.Routes)).To(BeTrue())

})

It("fails with CNI spec versions that don't support plugin chaining", func() {
conf := `{
"cniVersion": "0.2.0",
"name": "cni-plugin-sbr-test",
"type": "sbr",
"anotherAwesomeArg": "foo"
}`

args := &skel.CmdArgs{
ContainerID: "dummy",
@@ -424,7 +535,7 @@ var _ = Describe("sbr test", func() {
Expect(err).NotTo(HaveOccurred())

_, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred())
Expect(err).To(MatchError("This plugin must be called as chained plugin"))
})

})
@@ -1,5 +1,5 @@

This document has moved to the [containernetworking/cni.dev](https://github.com/containernetworking/cni.dev) repo.

You can find it online here: https://cni.dev/plugins/meta/tuning/
You can find it online here: https://cni.dev/plugins/current/meta/tuning/

@@ -29,10 +29,11 @@ import (

"github.com/j-keck/arping"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"

"github.com/containernetworking/plugins/pkg/ns"
@@ -44,11 +45,12 @@ const defaultDataDir = "/run/cni/tuning"
// TuningConf represents the network tuning configuration.
type TuningConf struct {
types.NetConf
DataDir string `json:"dataDir,omitempty"`
SysCtl map[string]string `json:"sysctl"`
Mac string `json:"mac,omitempty"`
Promisc bool `json:"promisc,omitempty"`
Mtu int `json:"mtu,omitempty"`
DataDir string `json:"dataDir,omitempty"`
SysCtl map[string]string `json:"sysctl"`
Mac string `json:"mac,omitempty"`
Promisc bool `json:"promisc,omitempty"`
Mtu int `json:"mtu,omitempty"`
Allmulti *bool `json:"allmulti,omitempty"`

RuntimeConfig struct {
Mac string `json:"mac,omitempty"`
@@ -59,17 +61,19 @@ type TuningConf struct {
}

type IPAMArgs struct {
SysCtl *map[string]string `json:"sysctl"`
Mac *string `json:"mac,omitempty"`
Promisc *bool `json:"promisc,omitempty"`
Mtu *int `json:"mtu,omitempty"`
SysCtl *map[string]string `json:"sysctl"`
Mac *string `json:"mac,omitempty"`
Promisc *bool `json:"promisc,omitempty"`
Mtu *int `json:"mtu,omitempty"`
Allmulti *bool `json:"allmulti,omitempty"`
}

// configToRestore will contain interface attributes that should be restored on cmdDel
type configToRestore struct {
Mac string `json:"mac,omitempty"`
Promisc *bool `json:"promisc,omitempty"`
Mtu int `json:"mtu,omitempty"`
Mac string `json:"mac,omitempty"`
Promisc *bool `json:"promisc,omitempty"`
Mtu int `json:"mtu,omitempty"`
Allmulti *bool `json:"allmulti,omitempty"`
}

// MacEnvArgs represents CNI_ARG
@@ -126,6 +130,9 @@ func parseConf(data []byte, envArgs string) (*TuningConf, error) {
conf.Mtu = *conf.Args.A.Mtu
}

if conf.Args.A.Allmulti != nil {
conf.Allmulti = conf.Args.A.Allmulti
}
}

return &conf, nil
@@ -145,7 +152,7 @@ func changeMacAddr(ifName string, newMacAddr string) error {
return netlink.LinkSetHardwareAddr(link, addr)
}

func updateResultsMacAddr(config TuningConf, ifName string, newMacAddr string) {
func updateResultsMacAddr(config *TuningConf, ifName string, newMacAddr string) {
// Parse previous result.
if config.PrevResult == nil {
return
@@ -159,6 +166,7 @@ func updateResultsMacAddr(config TuningConf, ifName string, newMacAddr string) {
i.Mac = newMacAddr
}
}
config.PrevResult = result
}

func changePromisc(ifName string, val bool) error {
@@ -181,6 +189,18 @@ func changeMtu(ifName string, mtu int) error {
return netlink.LinkSetMTU(link, mtu)
}

func changeAllmulti(ifName string, val bool) error {
link, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to get %q: %v", ifName, err)
}

if val {
return netlink.LinkSetAllmulticastOn(link)
}
return netlink.LinkSetAllmulticastOff(link)
}

func createBackup(ifName, containerID, backupPath string, tuningConf *TuningConf) error {
config := configToRestore{}
link, err := netlink.LinkByName(ifName)
@@ -197,6 +217,10 @@ func createBackup(ifName, containerID, backupPath string, tuningConf *TuningConf
if tuningConf.Mtu != 0 {
config.Mtu = link.Attrs().MTU
}
if tuningConf.Allmulti != nil {
config.Allmulti = new(bool)
*config.Allmulti = (link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0)
}

if _, err := os.Stat(backupPath); os.IsNotExist(err) {
if err = os.MkdirAll(backupPath, 0600); err != nil {
@@ -258,6 +282,12 @@ func restoreBackup(ifName, containerID, backupPath string) error {
errStr = append(errStr, err.Error())
}
}
if config.Allmulti != nil {
if err = changeAllmulti(ifName, *config.Allmulti); err != nil {
err = fmt.Errorf("failed to restore all-multicast mode: %v", err)
errStr = append(errStr, err.Error())
}
}

if len(errStr) > 0 {
return fmt.Errorf(strings.Join(errStr, "; "))
@@ -310,7 +340,7 @@ func cmdAdd(args *skel.CmdArgs) error {
}
}

if tuningConf.Mac != "" || tuningConf.Mtu != 0 || tuningConf.Promisc {
if tuningConf.Mac != "" || tuningConf.Mtu != 0 || tuningConf.Promisc || tuningConf.Allmulti != nil {
if err = createBackup(args.IfName, args.ContainerID, tuningConf.DataDir, tuningConf); err != nil {
return err
}
@@ -322,12 +352,12 @@ func cmdAdd(args *skel.CmdArgs) error {
}

for _, ipc := range result.IPs {
if ipc.Version == "4" {
if ipc.Address.IP.To4() != nil {
_ = arping.GratuitousArpOverIfaceByName(ipc.Address.IP, args.IfName)
}
}

updateResultsMacAddr(*tuningConf, args.IfName, tuningConf.Mac)
updateResultsMacAddr(tuningConf, args.IfName, tuningConf.Mac)
}

if tuningConf.Promisc != false {
@@ -341,6 +371,12 @@ func cmdAdd(args *skel.CmdArgs) error {
return err
}
}

if tuningConf.Allmulti != nil {
if err = changeAllmulti(args.IfName, *tuningConf.Allmulti); err != nil {
return err
}
}
return nil
})
if err != nil {
@@ -358,7 +394,7 @@ func cmdDel(args *skel.CmdArgs) error {
}

ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
// MAC address, MTU and promiscuous mode settings will be restored
// MAC address, MTU, promiscuous and all-multicast mode settings will be restored
return restoreBackup(args.IfName, args.ContainerID, tuningConf.DataDir)
})
return nil
@@ -434,6 +470,14 @@ func cmdCheck(args *skel.CmdArgs) error {
args.IfName, tuningConf.Mtu, link.Attrs().MTU)
}
}

if tuningConf.Allmulti != nil {
allmulti := (link.Attrs().RawFlags&unix.IFF_ALLMULTI != 0)
if allmulti != *tuningConf.Allmulti {
return fmt.Errorf("Error: Tuning configured all-multicast mode of %s is %v, current value is %v",
args.IfName, tuningConf.Allmulti, allmulti)
}
}
return nil
})
if err != nil {

Large diffs are not rendered by default.

@@ -1,5 +1,5 @@

This document has moved to the [containernetworking/cni.dev](https://github.com/containernetworking/cni.dev) repo.

You can find it online here: https://cni.dev/plugins/meta/vrf/
You can find it online here: https://cni.dev/plugins/current/meta/vrf/

@@ -22,7 +22,7 @@ import (

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"

"github.com/containernetworking/plugins/pkg/ns"
@@ -20,7 +20,7 @@ import (

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"

@@ -19,11 +19,10 @@ package main
import (
"encoding/json"
"fmt"
"net"

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"

bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
@@ -33,21 +32,15 @@ import (
// is passed in on stdin. Your plugin may wish to expose its functionality via
// runtime args, see CONVENTIONS.md in the CNI spec.
type PluginConf struct {
types.NetConf // You may wish to not nest this type
// This embeds the standard NetConf structure which allows your plugin
// to more easily parse standard fields like Name, Type, CNIVersion,
// and PrevResult.
types.NetConf

RuntimeConfig *struct {
SampleConfig map[string]interface{} `json:"sample"`
} `json:"runtimeConfig"`

// This is the previous result, when called in the context of a chained
// plugin. Because this plugin supports multiple versions, we'll have to
// parse this in two passes. If your plugin is not chained, this can be
// removed (though you may wish to error if a non-chainable plugin is
// chained.
// If you need to modify the result before returning it, you will need
// to actually convert it to a concrete versioned struct.
RawPrevResult *map[string]interface{} `json:"prevResult"`
PrevResult *current.Result `json:"-"`

// Add plugin-specifc flags here
MyAwesomeFlag bool `json:"myAwesomeFlag"`
AnotherAwesomeArg string `json:"anotherAwesomeArg"`
@@ -61,21 +54,12 @@ func parseConfig(stdin []byte) (*PluginConf, error) {
return nil, fmt.Errorf("failed to parse network configuration: %v", err)
}

// Parse previous result. Remove this if your plugin is not chained.
if conf.RawPrevResult != nil {
resultBytes, err := json.Marshal(conf.RawPrevResult)
if err != nil {
return nil, fmt.Errorf("could not serialize prevResult: %v", err)
}
res, err := version.NewResult(conf.CNIVersion, resultBytes)
if err != nil {
return nil, fmt.Errorf("could not parse prevResult: %v", err)
}
conf.RawPrevResult = nil
conf.PrevResult, err = current.NewResultFromResult(res)
if err != nil {
return nil, fmt.Errorf("could not convert result to current version: %v", err)
}
// Parse previous result. This will parse, validate, and place the
// previous result object into conf.PrevResult. If you need to modify
// or inspect the PrevResult you will need to convert it to a concrete
// versioned Result struct.
if err := version.ParsePrevResult(&conf.NetConf); err != nil {
return nil, fmt.Errorf("could not parse prevResult: %v", err)
}
// End previous result parsing

@@ -94,50 +78,62 @@ func cmdAdd(args *skel.CmdArgs) error {
return err
}

// Remove this if this is an "originating" plugin
// A plugin can be either an "originating" plugin or a "chained" plugin.
// Originating plugins perform initial sandbox setup and do not require
// any result from a previous plugin in the chain. A chained plugin
// modifies sandbox configuration that was previously set up by an
// originating plugin and may optionally require a PrevResult from
// earlier plugins in the chain.

// START chained plugin code
if conf.PrevResult == nil {
return fmt.Errorf("must be called as chained plugin")
}

// Uncomment if this is an "originating" plugin

//if conf.PrevResult != nil {
// return fmt.Errorf("must be called as the first plugin")
// }

// This is some sample code to generate the list of container-side IPs.
// We're casting the prevResult to a 0.3.0 response, which can also include
// host-side IPs (but doesn't when converted from a 0.2.0 response).
//
// You don't need this if you are writing an "originating" plugin.
containerIPs := make([]net.IP, 0, len(conf.PrevResult.IPs))
if conf.CNIVersion != "0.3.0" {
for _, ip := range conf.PrevResult.IPs {
containerIPs = append(containerIPs, ip.Address.IP)
}
} else {
for _, ip := range conf.PrevResult.IPs {
if ip.Interface == nil {
continue
}
intIdx := *ip.Interface
// Every IP is indexed in to the interfaces array, with "-1" standing
// for an unknown interface (which we'll assume to be Container-side
// Skip all IPs we know belong to an interface with the wrong name.
if intIdx >= 0 && intIdx < len(conf.PrevResult.Interfaces) && conf.PrevResult.Interfaces[intIdx].Name != args.IfName {
continue
}
containerIPs = append(containerIPs, ip.Address.IP)
}
// Convert the PrevResult to a concrete Result type that can be modified.
prevResult, err := current.GetResult(conf.PrevResult)
if err != nil {
return fmt.Errorf("failed to convert prevResult: %v", err)
}
if len(containerIPs) == 0 {

if len(prevResult.IPs) == 0 {
return fmt.Errorf("got no container IPs")
}

// Pass the prevResult through this plugin to the next one
result := prevResult

// END chained plugin code

// START originating plugin code
// if conf.PrevResult != nil {
// return fmt.Errorf("must be called as the first plugin")
// }

// Generate some fake container IPs and add to the result
// result := &current.Result{CNIVersion: current.ImplementedSpecVersion}
// result.Interfaces = []*current.Interface{
// {
// Name: "intf0",
// Sandbox: args.Netns,
// Mac: "00:11:22:33:44:55",
// },
// }
// result.IPs = []*current.IPConfig{
// {
// Address: "1.2.3.4/24",
// Gateway: "1.2.3.1",
// // Interface is an index into the Interfaces array
// // of the Interface element this IP applies to
// Interface: current.Int(0),
// }
// }
// END originating plugin code

// Implement your plugin here

// Pass through the result for the next plugin
return types.PrintResult(conf.PrevResult, conf.CNIVersion)
return types.PrintResult(result, conf.CNIVersion)
}

// cmdDel is called for DELETE requests
@@ -45,6 +45,7 @@ var _ = Describe("sample test", func() {
"type": "sample",
"anotherAwesomeArg": "awesome",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{
"name": "%s",
@@ -71,7 +72,6 @@ var _ = Describe("sample test", func() {
}
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred())

})

It("fails an invalid config", func() {
@@ -106,22 +106,14 @@ var _ = Describe("sample test", func() {
}
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).To(MatchError("anotherAwesomeArg must be specified"))

})

It("works with a 0.2.0 config", func() {
It("fails with CNI spec versions that don't support plugin chaining", func() {
conf := `{
"cniVersion": "0.2.0",
"name": "cni-plugin-sample-test",
"type": "sample",
"anotherAwesomeArg": "foo",
"prevResult": {
"ip4": {
"ip": "10.0.0.2/24",
"gateway": "10.0.0.1",
"routes": []
}
}
"anotherAwesomeArg": "foo"
}`

args := &skel.CmdArgs{
@@ -131,8 +123,7 @@ var _ = Describe("sample test", func() {
StdinData: []byte(conf),
}
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred())

Expect(err).To(MatchError("must be called as chained plugin"))
})

})
@@ -1,4 +1,3 @@
plugins/ipam/host-local
plugins/main/windows/win-bridge
plugins/main/windows/win-overlay
plugins/meta/flannel
@@ -16,7 +16,7 @@ rm -Rf ${SRC_DIR}/${RELEASE_DIR}
mkdir -p ${SRC_DIR}/${RELEASE_DIR}
mkdir -p ${OUTPUT_DIR}

$DOCKER run -ti -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins --rm golang:1.15-alpine \
$DOCKER run -ti -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins:z --rm golang:1.16-alpine \
/bin/sh -xe -c "\
apk --no-cache add bash tar;
cd /go/src/github.com/containernetworking/plugins; umask 0022;