--- /dev/null
+{
+ "cniVersion": "0.2.0",
+ "name": "mynet6",
+ "type": "vz-bridge",
+ "bridge": "cni0",
+ "ipam": {
+ "type": "vz-local",
+ "subnet": "3ffe:ffff:0:01ff::/64",
+ "rangeStart": "3ffe:ffff:0:01ff::0010",
+ "rangeEnd": "3ffe:ffff:0:01ff::0020",
+ "routes": [
+ { "dst": "3ffe:ffff:0:01ff::1/64" }
+ ]
+ }
+}
+
{ "key" : "app", "value" : "myapp" },
{ "key" : "mccKey1", "value" : "mccVal1" },
{ "key" : "bs", "value" : "bsVal" },
- { "key" : "StaticIP", "value" : "172.19.0.109" },
+ { "key" : "StaticIP", "value" : "172.19.0.211" },
{ "key" : "Uplink", "value" : "eth1p0" },
{ "key" : "viaIP", "value" : "172.19.0.1" },
{ "key" : "mccKey2", "value" : "mccVal2" },
--- /dev/null
+{
+ "cniVersion": "0.2.0",
+ "name": "macvlan6",
+ "type": "vz-macvlan",
+ "master": "eth1p0",
+ "args" : {
+ "org.apache.mesos" : {
+ "network_info" : {
+ "name" : "mynet",
+ "labels" : {
+ "labels" : [
+ { "key" : "app", "value" : "myapp" },
+ { "key" : "mccKey1", "value" : "mccVal1" },
+ { "key" : "bs", "value" : "bsVal" },
+ { "key" : "v4StaticIP", "value" : "172.19.0.209" },
+ { "key" : "v4Uplink", "value" : "eth1p0" },
+ { "key" : "v4viaIP", "value" : "172.19.0.1" },
+ { "key" : "mccKey2", "value" : "mccVal2" },
+ { "key" : "env", "value" : "prod" }
+ ]
+ }
+ }
+ }
+ },
+ "ipam": {
+ "type": "vz-local",
+ "subnet": "3ffe:ffff:0:02ff::/64",
+ "rangeStart": "3ffe:ffff:0:02ff::0010",
+ "rangeEnd": "3ffe:ffff:0:02ff::0020",
+ "routes": [
+ { "dst": "3ffe:ffff:0:02ff::1/64" }
+ ]
+ }
+}
+
#/bin/bash
export DEBUG=1
export NETCONFPATH=/etc/cni/net.d/
-export CNI_PATH=/home/mcambria/go/src/github.com/containernetworking/cni/bin/
-#export CNI_PATH=/home/mcambria/go2/src/stash.verizon.com/cni/bin
+#export CNI_PATH=/home/mcambria/go/src/github.com/containernetworking/cni/bin/
+export CNI_PATH=/home/mcambria/go2/src/stash.verizon.com/cni/bin
export NETCONFPATH=${NETCONFPATH-/etc/cni/net.d}
export CNI_COMMAND=ADD
export CNI_NETNS=/var/run/netns/mcc-cni-test1
#/bin/bash
export DEBUG=1
export NETCONFPATH=/etc/cni/net.d/
-export CNI_PATH=/home/mcambria/go/src/github.com/containernetworking/cni/bin/
-#export CNI_PATH=/home/mcambria/go2/src/stash.verizon.com/cni/bin
+#export CNI_PATH=/home/mcambria/go/src/github.com/containernetworking/cni/bin/
+export CNI_PATH=/home/mcambria/go2/src/stash.verizon.com/cni/bin
export NETCONFPATH=${NETCONFPATH-/etc/cni/net.d}
export CNI_COMMAND=DEL
export CNI_NETNS=/var/run/netns/mcc-cni-test1
--- /dev/null
+#/bin/bash
+export DEBUG=1
+export NETCONFPATH=/etc/cni/net.d/
+export CNI_PATH=/home/mcambria/go2/src/stash.verizon.com/cni/bin
+export NETCONFPATH=${NETCONFPATH-/etc/cni/net.d}
+export CNI_COMMAND=ADD
+export CNI_NETNS=/var/run/netns/mcc-cni-ipv6
+export CNI_CONTAINERID=mcc-cni-ipv6
+
+export PATH=$CNI_PATH:$PATH
+export CNI_IFNAME=eth2
+
+export MCCVAL="Jamal Env Value"
+
+vz-bridge < 16-mynet.conf
+
+
--- /dev/null
+#/bin/bash
+export DEBUG=1
+export NETCONFPATH=/etc/cni/net.d/
+export CNI_PATH=/home/mcambria/go2/src/stash.verizon.com/cni/bin
+export NETCONFPATH=${NETCONFPATH-/etc/cni/net.d}
+export CNI_COMMAND=DEL
+export CNI_NETNS=/var/run/netns/mcc-cni-ipv6
+export CNI_CONTAINERID=mcc-cni-ipv6
+
+export PATH=$CNI_PATH:$PATH
+export CNI_IFNAME=eth2
+
+export MCCVAL="Jamal Env Value"
+
+vz-bridge < 16-mynet.conf
+
+
"fmt"
"net"
"os"
- "os/exec"
+ //"os/exec"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/ip"
/*
* Begin
- */
+ *
var (
cmdOut []byte
result string
result = string(cmdOut)
fmt.Println("The result of qdisc show is: ",result)
-/*
- ["ip", "netns", "exec", str(pid), "tc", "filter", "add", "dev", "eth0", "parent", "ffff:", "protocol", "ip",
- "prio", priority, "u32", "match", "u8", hexgid, "0xf0", "at", "15", "flowid", ":2", "action", "pass”])
-*/
-
// cmdName = "tc"
cmdArgs = []string{"filter", "add", "dev", ifName, "parent", "ffff:", "protocol", "ip", "prio", "2", "u32", "match", "u8", "0xd0", "0xf0", "at", "15", "flowid", ":10", "action", "pass" }
if cmdOut, err = exec.Command(cmdName, cmdArgs...).Output(); err != nil {
result = string(cmdOut)
fmt.Println("The result of show filter is: ", result)
- /*
- * Example of getting an environment variable supplied to plugin
- */
+ //
+ // Example of getting an environment variable supplied to plugin
+ //
mccval := os.Getenv("MCCVAL")
fmt.Println("mccval is: ", mccval)
-/*
+*
* End
*/
}
}
+ var ip_ver = "4"
+ if requestedIP.To16() != nil {
+ ip_ver = "6"
+ }
+
ipConfig := ¤t.IPConfig{
- Version: "4",
+ // Version: "4",
+ Version: ip_ver,
Address: net.IPNet{IP: requestedIP, Mask: a.conf.Subnet.Mask},
Gateway: gw,
}
if err != nil {
return nil, nil, err
}
+
if reserved {
+ var ip_ver string
+ if cur.To4() != nil {
+ ip_ver = "4"
+ } else if cur.To16() != nil {
+ ip_ver = "6"
+ } else {
+ return nil, nil, fmt.Errorf("IP %s not v4 nor v6", cur)
+ }
+
ipConfig := ¤t.IPConfig{
- Version: "4",
+ //Version: "4",
+ Version: ip_ver,
Address: net.IPNet{IP: cur, Mask: a.conf.Subnet.Mask},
Gateway: gw,
}
--- /dev/null
+// Copyright 2014 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 (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net"
+ "runtime"
+ "syscall"
+
+ "github.com/containernetworking/cni/pkg/ip"
+ "github.com/containernetworking/cni/pkg/ipam"
+ "github.com/containernetworking/cni/pkg/ns"
+ "github.com/containernetworking/cni/pkg/skel"
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containernetworking/cni/pkg/utils"
+ "github.com/containernetworking/cni/pkg/version"
+ "github.com/vishvananda/netlink"
+)
+
+const defaultBrName = "cni0"
+
+type NetConf struct {
+ types.NetConf
+ BrName string `json:"bridge"`
+ IsGW bool `json:"isGateway"`
+ IsDefaultGW bool `json:"isDefaultGateway"`
+ ForceAddress bool `json:"forceAddress"`
+ IPMasq bool `json:"ipMasq"`
+ MTU int `json:"mtu"`
+ HairpinMode bool `json:"hairpinMode"`
+}
+
+func init() {
+ // this ensures that main runs only on main thread (thread group leader).
+ // since namespace ops (unshare, setns) are done for a single thread, we
+ // must ensure that the goroutine does not jump from OS thread to thread
+ runtime.LockOSThread()
+}
+
+func loadNetConf(bytes []byte) (*NetConf, string, error) {
+ n := &NetConf{
+ BrName: defaultBrName,
+ }
+ if err := json.Unmarshal(bytes, n); err != nil {
+ return nil, "", fmt.Errorf("failed to load netconf: %v", err)
+ }
+ return n, n.CNIVersion, nil
+}
+
+func ensureBridgeAddr(br *netlink.Bridge, ipn *net.IPNet, forceAddress bool) error {
+ addrs, err := netlink.AddrList(br, syscall.AF_INET)
+ if err != nil && err != syscall.ENOENT {
+ return fmt.Errorf("could not get list of IP addresses: %v", err)
+ }
+
+ // if there're no addresses on the bridge, it's ok -- we'll add one
+ if len(addrs) > 0 {
+ ipnStr := ipn.String()
+ for _, a := range addrs {
+ // string comp is actually easiest for doing IPNet comps
+ if a.IPNet.String() == ipnStr {
+ return nil
+ }
+
+ // If forceAddress is set to true then reconfigure IP address otherwise throw error
+ if forceAddress {
+ if err = deleteBridgeAddr(br, a.IPNet); err != nil {
+ return err
+ }
+ } else {
+ return fmt.Errorf("%q already has an IP address different from %v", br.Name, ipn.String())
+ }
+ }
+ }
+
+ addr := &netlink.Addr{IPNet: ipn, Label: ""}
+ if err := netlink.AddrAdd(br, addr); err != nil {
+ return fmt.Errorf("could not add IP address to %q: %v", br.Name, err)
+ }
+ return nil
+}
+
+func deleteBridgeAddr(br *netlink.Bridge, ipn *net.IPNet) error {
+ addr := &netlink.Addr{IPNet: ipn, Label: ""}
+
+ if err := netlink.LinkSetDown(br); err != nil {
+ return fmt.Errorf("could not set down bridge %q: %v", br.Name, err)
+ }
+
+ if err := netlink.AddrDel(br, addr); err != nil {
+ return fmt.Errorf("could not remove IP address from %q: %v", br.Name, err)
+ }
+
+ if err := netlink.LinkSetUp(br); err != nil {
+ return fmt.Errorf("could not set up bridge %q: %v", br.Name, err)
+ }
+
+ return nil
+}
+
+func bridgeByName(name string) (*netlink.Bridge, error) {
+ l, err := netlink.LinkByName(name)
+ if err != nil {
+ return nil, fmt.Errorf("could not lookup %q: %v", name, err)
+ }
+ br, ok := l.(*netlink.Bridge)
+ if !ok {
+ return nil, fmt.Errorf("%q already exists but is not a bridge", name)
+ }
+ return br, nil
+}
+
+func ensureBridge(brName string, mtu int) (*netlink.Bridge, error) {
+ br := &netlink.Bridge{
+ LinkAttrs: netlink.LinkAttrs{
+ Name: brName,
+ MTU: mtu,
+ // Let kernel use default txqueuelen; leaving it unset
+ // means 0, and a zero-length TX queue messes up FIFO
+ // traffic shapers which use TX queue length as the
+ // default packet limit
+ TxQLen: -1,
+ },
+ }
+
+ err := netlink.LinkAdd(br)
+ if err != nil && err != syscall.EEXIST {
+ return nil, fmt.Errorf("could not add %q: %v", brName, err)
+ }
+
+ // Re-fetch link to read all attributes and if it already existed,
+ // ensure it's really a bridge with similar configuration
+ br, err = bridgeByName(brName)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := netlink.LinkSetUp(br); err != nil {
+ return nil, err
+ }
+
+ return br, nil
+}
+
+func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool) (*current.Interface, *current.Interface, error) {
+ contIface := ¤t.Interface{}
+ hostIface := ¤t.Interface{}
+
+ err := netns.Do(func(hostNS ns.NetNS) error {
+ // create the veth pair in the container and move host end into host netns
+ hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)
+ if err != nil {
+ return err
+ }
+ contIface.Name = containerVeth.Attrs().Name
+ contIface.Mac = containerVeth.Attrs().HardwareAddr.String()
+ contIface.Sandbox = netns.Path()
+ hostIface.Name = hostVeth.Attrs().Name
+ return nil
+ })
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // need to lookup hostVeth again as its index has changed during ns move
+ hostVeth, err := netlink.LinkByName(hostIface.Name)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to lookup %q: %v", hostIface.Name, err)
+ }
+ hostIface.Mac = hostVeth.Attrs().HardwareAddr.String()
+
+ // connect host veth end to the bridge
+ if err := netlink.LinkSetMaster(hostVeth, br); err != nil {
+ return nil, nil, fmt.Errorf("failed to connect %q to bridge %v: %v", hostVeth.Attrs().Name, br.Attrs().Name, err)
+ }
+
+ // set hairpin mode
+ if err = netlink.LinkSetHairpin(hostVeth, hairpinMode); err != nil {
+ return nil, nil, fmt.Errorf("failed to setup hairpin mode for %v: %v", hostVeth.Attrs().Name, err)
+ }
+
+ return hostIface, contIface, nil
+}
+
+func calcGatewayIP(ipn *net.IPNet) net.IP {
+ nid := ipn.IP.Mask(ipn.Mask)
+ return ip.NextIP(nid)
+}
+
+func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
+ // create bridge if necessary
+ br, err := ensureBridge(n.BrName, n.MTU)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to create bridge %q: %v", n.BrName, err)
+ }
+
+ return br, ¤t.Interface{
+ Name: br.Attrs().Name,
+ Mac: br.Attrs().HardwareAddr.String(),
+ }, nil
+}
+
+func cmdAdd(args *skel.CmdArgs) error {
+ n, cniVersion, err := loadNetConf(args.StdinData)
+ if err != nil {
+ return err
+ }
+
+ if n.IsDefaultGW {
+ n.IsGW = true
+ }
+
+ br, brInterface, err := setupBridge(n)
+ if err != nil {
+ return err
+ }
+
+ netns, err := ns.GetNS(args.Netns)
+ if err != nil {
+ return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
+ }
+ defer netns.Close()
+
+ hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode)
+ if err != nil {
+ return err
+ }
+
+ // 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
+ }
+
+ // Convert whatever the IPAM result was into the current Result type
+ result, err := current.NewResultFromResult(r)
+ if err != nil {
+ return err
+ }
+
+ if len(result.IPs) == 0 {
+ return errors.New("IPAM plugin returned missing IP config")
+ }
+
+ result.Interfaces = []*current.Interface{brInterface, hostInterface, containerInterface}
+
+ for _, ipc := range result.IPs {
+ // All IPs currently refer to the container interface
+ ipc.Interface = 2
+ if ipc.Gateway == nil && n.IsGW {
+ ipc.Gateway = calcGatewayIP(&ipc.Address)
+ }
+ }
+
+ if err := netns.Do(func(_ ns.NetNS) error {
+ // set the default gateway if requested
+ if n.IsDefaultGW {
+ for _, ipc := range result.IPs {
+ defaultNet := &net.IPNet{}
+ switch {
+ case ipc.Address.IP.To4() != nil:
+ defaultNet.IP = net.IPv4zero
+ defaultNet.Mask = net.IPMask(net.IPv4zero)
+ case len(ipc.Address.IP) == net.IPv6len && ipc.Address.IP.To4() == nil:
+ defaultNet.IP = net.IPv6zero
+ defaultNet.Mask = net.IPMask(net.IPv6zero)
+ default:
+ return fmt.Errorf("Unknown IP object: %v", ipc)
+ }
+
+ for _, route := range result.Routes {
+ if defaultNet.String() == route.Dst.String() {
+ if route.GW != nil && !route.GW.Equal(ipc.Gateway) {
+ return fmt.Errorf(
+ "isDefaultGateway ineffective because IPAM sets default route via %q",
+ route.GW,
+ )
+ }
+ }
+ }
+
+ result.Routes = append(
+ result.Routes,
+ &types.Route{Dst: *defaultNet, GW: ipc.Gateway},
+ )
+ }
+ }
+
+ if err := ipam.ConfigureIface(args.IfName, result); err != nil {
+ return err
+ }
+
+ //if err := ip.SetHWAddrByIP(args.IfName, result.IPs[0].Address.IP, nil /* TODO IPv6 */); err != nil {
+ // return err
+ //}
+
+ // Refetch the veth since its MAC address may changed
+ link, err := netlink.LinkByName(args.IfName)
+ if err != nil {
+ return fmt.Errorf("could not lookup %q: %v", args.IfName, err)
+ }
+ containerInterface.Mac = link.Attrs().HardwareAddr.String()
+
+ return nil
+ }); err != nil {
+ return err
+ }
+
+ if n.IsGW {
+ var firstV4Addr net.IP
+ for _, ipc := range result.IPs {
+ gwn := &net.IPNet{
+ IP: ipc.Gateway,
+ Mask: ipc.Address.Mask,
+ }
+ if ipc.Gateway.To4() != nil && firstV4Addr == nil {
+ firstV4Addr = ipc.Gateway
+ }
+
+ if err = ensureBridgeAddr(br, gwn, n.ForceAddress); err != nil {
+ return err
+ }
+ }
+
+ if firstV4Addr != nil {
+ if err := ip.SetHWAddrByIP(n.BrName, firstV4Addr, nil /* TODO IPv6 */); err != nil {
+ return err
+ }
+ }
+
+ if err := ip.EnableIP4Forward(); err != nil {
+ return fmt.Errorf("failed to enable forwarding: %v", err)
+ }
+ }
+
+ if n.IPMasq {
+ chain := utils.FormatChainName(n.Name, args.ContainerID)
+ comment := utils.FormatComment(n.Name, args.ContainerID)
+ for _, ipc := range result.IPs {
+ if err = ip.SetupIPMasq(ip.Network(&ipc.Address), chain, comment); err != nil {
+ return err
+ }
+ }
+ }
+
+ // Refetch the bridge since its MAC address may change when the first
+ // veth is added or after its IP address is set
+ br, err = bridgeByName(n.BrName)
+ if err != nil {
+ return err
+ }
+ brInterface.Mac = br.Attrs().HardwareAddr.String()
+
+ result.DNS = n.DNS
+
+ return types.PrintResult(result, cniVersion)
+}
+
+func cmdDel(args *skel.CmdArgs) error {
+ n, _, err := loadNetConf(args.StdinData)
+ if err != nil {
+ return err
+ }
+
+ if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
+ return err
+ }
+
+ if args.Netns == "" {
+ return nil
+ }
+
+ var ipn *net.IPNet
+ err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
+ var err error
+ // ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
+ err = ip.DelLinkByName(args.IfName)
+ return err
+ })
+ if err != nil {
+ return err
+ }
+
+ if n.IPMasq {
+ chain := utils.FormatChainName(n.Name, args.ContainerID)
+ comment := utils.FormatComment(n.Name, args.ContainerID)
+ if err = ip.TeardownIPMasq(ipn, chain, comment); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func main() {
+ skel.PluginMain(cmdAdd, cmdDel, version.All)
+}
--- /dev/null
+// Copyright 2016 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 (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+
+ "testing"
+)
+
+func TestBridge(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "bridge Suite")
+}
--- /dev/null
+// Copyright 2015 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 (
+ "fmt"
+ "net"
+ "strings"
+ "syscall"
+
+ "github.com/containernetworking/cni/pkg/ns"
+ "github.com/containernetworking/cni/pkg/skel"
+ "github.com/containernetworking/cni/pkg/testutils"
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/020"
+ "github.com/containernetworking/cni/pkg/types/current"
+
+ "github.com/containernetworking/cni/pkg/utils/hwaddr"
+
+ "github.com/vishvananda/netlink"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("bridge Operations", func() {
+ var originalNS ns.NetNS
+
+ BeforeEach(func() {
+ // Create a new NetNS so we don't modify the host
+ var err error
+ originalNS, err = ns.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+ })
+
+ AfterEach(func() {
+ Expect(originalNS.Close()).To(Succeed())
+ })
+
+ It("creates a bridge", func() {
+ const IFNAME = "bridge0"
+
+ conf := &NetConf{
+ NetConf: types.NetConf{
+ CNIVersion: "0.3.0",
+ Name: "testConfig",
+ Type: "bridge",
+ },
+ BrName: IFNAME,
+ IsGW: false,
+ IPMasq: false,
+ MTU: 5000,
+ }
+
+ err := originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ bridge, _, err := setupBridge(conf)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(bridge.Attrs().Name).To(Equal(IFNAME))
+
+ // Double check that the link was added
+ link, err := netlink.LinkByName(IFNAME)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal(IFNAME))
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+ })
+
+ It("handles an existing bridge", func() {
+ const IFNAME = "bridge0"
+
+ err := originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ err := netlink.LinkAdd(&netlink.Bridge{
+ LinkAttrs: netlink.LinkAttrs{
+ Name: IFNAME,
+ },
+ })
+ Expect(err).NotTo(HaveOccurred())
+ link, err := netlink.LinkByName(IFNAME)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal(IFNAME))
+ ifindex := link.Attrs().Index
+
+ conf := &NetConf{
+ NetConf: types.NetConf{
+ CNIVersion: "0.3.0",
+ Name: "testConfig",
+ Type: "bridge",
+ },
+ BrName: IFNAME,
+ IsGW: false,
+ IPMasq: false,
+ }
+
+ bridge, _, err := setupBridge(conf)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(bridge.Attrs().Name).To(Equal(IFNAME))
+ Expect(bridge.Attrs().Index).To(Equal(ifindex))
+
+ // Double check that the link has the same ifindex
+ link, err = netlink.LinkByName(IFNAME)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal(IFNAME))
+ Expect(link.Attrs().Index).To(Equal(ifindex))
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+ })
+
+ It("configures and deconfigures a bridge and veth with default route with ADD/DEL", func() {
+ const BRNAME = "cni0"
+ const IFNAME = "eth0"
+
+ gwaddr, subnet, err := net.ParseCIDR("10.1.2.1/24")
+ Expect(err).NotTo(HaveOccurred())
+
+ conf := fmt.Sprintf(`{
+ "cniVersion": "0.3.0",
+ "name": "mynet",
+ "type": "bridge",
+ "bridge": "%s",
+ "isDefaultGateway": true,
+ "ipMasq": false,
+ "ipam": {
+ "type": "host-local",
+ "subnet": "%s"
+ }
+}`, BRNAME, subnet.String())
+
+ targetNs, err := ns.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+ defer targetNs.Close()
+
+ args := &skel.CmdArgs{
+ ContainerID: "dummy",
+ Netns: targetNs.Path(),
+ IfName: IFNAME,
+ StdinData: []byte(conf),
+ }
+
+ var result *current.Result
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ r, raw, err := testutils.CmdAddWithResult(targetNs.Path(), IFNAME, []byte(conf), func() error {
+ return cmdAdd(args)
+ })
+ Expect(err).NotTo(HaveOccurred())
+ Expect(strings.Index(string(raw), "\"interfaces\":")).Should(BeNumerically(">", 0))
+
+ result, err = current.GetResult(r)
+ Expect(err).NotTo(HaveOccurred())
+
+ Expect(len(result.Interfaces)).To(Equal(3))
+ Expect(result.Interfaces[0].Name).To(Equal(BRNAME))
+ Expect(result.Interfaces[2].Name).To(Equal(IFNAME))
+
+ // Make sure bridge link exists
+ link, err := netlink.LinkByName(result.Interfaces[0].Name)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal(BRNAME))
+ Expect(link).To(BeAssignableToTypeOf(&netlink.Bridge{}))
+ Expect(link.Attrs().HardwareAddr.String()).To(Equal(result.Interfaces[0].Mac))
+ hwAddr := fmt.Sprintf("%s", link.Attrs().HardwareAddr)
+ Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString))
+
+ // Ensure bridge has gateway address
+ addrs, err := netlink.AddrList(link, syscall.AF_INET)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(addrs)).To(BeNumerically(">", 0))
+ found := false
+ subnetPrefix, subnetBits := subnet.Mask.Size()
+ for _, a := range addrs {
+ aPrefix, aBits := a.IPNet.Mask.Size()
+ if a.IPNet.IP.Equal(gwaddr) && aPrefix == subnetPrefix && aBits == subnetBits {
+ found = true
+ break
+ }
+ }
+ Expect(found).To(Equal(true))
+
+ // Check for the veth link in the main namespace
+ links, err := netlink.LinkList()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
+
+ link, err = netlink.LinkByName(result.Interfaces[1].Name)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // Find the veth peer in the container namespace and the default route
+ err = targetNs.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ link, err := netlink.LinkByName(IFNAME)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal(IFNAME))
+ Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
+
+ addrs, err := netlink.AddrList(link, syscall.AF_INET)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(addrs)).To(Equal(1))
+
+ hwAddr := fmt.Sprintf("%s", link.Attrs().HardwareAddr)
+ Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString))
+
+ // Ensure the default route
+ routes, err := netlink.RouteList(link, 0)
+ Expect(err).NotTo(HaveOccurred())
+
+ var defaultRouteFound bool
+ for _, route := range routes {
+ defaultRouteFound = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwaddr))
+ if defaultRouteFound {
+ break
+ }
+ }
+ Expect(defaultRouteFound).To(Equal(true))
+
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ err := testutils.CmdDelWithResult(targetNs.Path(), IFNAME, func() error {
+ return cmdDel(args)
+ })
+ Expect(err).NotTo(HaveOccurred())
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // Make sure the host veth has been deleted
+ err = targetNs.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ link, err := netlink.LinkByName(IFNAME)
+ Expect(err).To(HaveOccurred())
+ Expect(link).To(BeNil())
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // Make sure the container veth has been deleted
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ link, err := netlink.LinkByName(result.Interfaces[1].Name)
+ Expect(err).To(HaveOccurred())
+ Expect(link).To(BeNil())
+ return nil
+ })
+ })
+
+ It("configures and deconfigures a bridge and veth with default route with ADD/DEL for 0.1.0 config", func() {
+ const BRNAME = "cni0"
+ const IFNAME = "eth0"
+
+ gwaddr, subnet, err := net.ParseCIDR("10.1.2.1/24")
+ Expect(err).NotTo(HaveOccurred())
+
+ conf := fmt.Sprintf(`{
+ "cniVersion": "0.1.0",
+ "name": "mynet",
+ "type": "bridge",
+ "bridge": "%s",
+ "isDefaultGateway": true,
+ "ipMasq": false,
+ "ipam": {
+ "type": "host-local",
+ "subnet": "%s"
+ }
+}`, BRNAME, subnet.String())
+
+ targetNs, err := ns.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+ defer targetNs.Close()
+
+ args := &skel.CmdArgs{
+ ContainerID: "dummy",
+ Netns: targetNs.Path(),
+ IfName: IFNAME,
+ StdinData: []byte(conf),
+ }
+
+ var result *types020.Result
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ r, raw, err := testutils.CmdAddWithResult(targetNs.Path(), IFNAME, []byte(conf), func() error {
+ return cmdAdd(args)
+ })
+ Expect(err).NotTo(HaveOccurred())
+ Expect(strings.Index(string(raw), "\"ip4\":")).Should(BeNumerically(">", 0))
+
+ // We expect a version 0.1.0 result
+ result, err = types020.GetResult(r)
+ Expect(err).NotTo(HaveOccurred())
+
+ // Make sure bridge link exists
+ link, err := netlink.LinkByName(BRNAME)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal(BRNAME))
+ Expect(link).To(BeAssignableToTypeOf(&netlink.Bridge{}))
+ hwAddr := fmt.Sprintf("%s", link.Attrs().HardwareAddr)
+ Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString))
+
+ // Ensure bridge has gateway address
+ addrs, err := netlink.AddrList(link, syscall.AF_INET)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(addrs)).To(BeNumerically(">", 0))
+ found := false
+ subnetPrefix, subnetBits := subnet.Mask.Size()
+ for _, a := range addrs {
+ aPrefix, aBits := a.IPNet.Mask.Size()
+ if a.IPNet.IP.Equal(gwaddr) && aPrefix == subnetPrefix && aBits == subnetBits {
+ found = true
+ break
+ }
+ }
+ Expect(found).To(Equal(true))
+
+ // Check for the veth link in the main namespace; can't
+ // check the for the specific link since version 0.1.0
+ // doesn't report interfaces
+ links, err := netlink.LinkList()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // Find the veth peer in the container namespace and the default route
+ err = targetNs.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ link, err := netlink.LinkByName(IFNAME)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal(IFNAME))
+ Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
+
+ addrs, err := netlink.AddrList(link, syscall.AF_INET)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(addrs)).To(Equal(1))
+
+ hwAddr := fmt.Sprintf("%s", link.Attrs().HardwareAddr)
+ Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString))
+
+ // Ensure the default route
+ routes, err := netlink.RouteList(link, 0)
+ Expect(err).NotTo(HaveOccurred())
+
+ var defaultRouteFound bool
+ for _, route := range routes {
+ defaultRouteFound = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwaddr))
+ if defaultRouteFound {
+ break
+ }
+ }
+ Expect(defaultRouteFound).To(Equal(true))
+
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ err := testutils.CmdDelWithResult(targetNs.Path(), IFNAME, func() error {
+ return cmdDel(args)
+ })
+ Expect(err).NotTo(HaveOccurred())
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // Make sure the container veth has been deleted; cannot check
+ // host veth as version 0.1.0 can't report its name
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ link, err := netlink.LinkByName(IFNAME)
+ Expect(err).To(HaveOccurred())
+ Expect(link).To(BeNil())
+ return nil
+ })
+ })
+
+ It("ensure bridge address", func() {
+ const IFNAME = "bridge0"
+ const EXPECTED_IP = "10.0.0.0/8"
+ const CHANGED_EXPECTED_IP = "10.1.2.3/16"
+
+ conf := &NetConf{
+ NetConf: types.NetConf{
+ CNIVersion: "0.3.0",
+ Name: "testConfig",
+ Type: "bridge",
+ },
+ BrName: IFNAME,
+ IsGW: true,
+ IPMasq: false,
+ MTU: 5000,
+ }
+
+ gwnFirst := &net.IPNet{
+ IP: net.IPv4(10, 0, 0, 0),
+ Mask: net.CIDRMask(8, 32),
+ }
+
+ gwnSecond := &net.IPNet{
+ IP: net.IPv4(10, 1, 2, 3),
+ Mask: net.CIDRMask(16, 32),
+ }
+
+ err := originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ bridge, _, err := setupBridge(conf)
+ Expect(err).NotTo(HaveOccurred())
+ // Check if ForceAddress has default value
+ Expect(conf.ForceAddress).To(Equal(false))
+
+ err = ensureBridgeAddr(bridge, gwnFirst, conf.ForceAddress)
+ Expect(err).NotTo(HaveOccurred())
+
+ //Check if IP address is set correctly
+ addrs, err := netlink.AddrList(bridge, syscall.AF_INET)
+ Expect(len(addrs)).To(Equal(1))
+ addr := addrs[0].IPNet.String()
+ Expect(addr).To(Equal(EXPECTED_IP))
+
+ //The bridge IP address has been changed. Error expected when ForceAddress is set to false.
+ err = ensureBridgeAddr(bridge, gwnSecond, false)
+ Expect(err).To(HaveOccurred())
+
+ //The IP address should stay the same.
+ addrs, err = netlink.AddrList(bridge, syscall.AF_INET)
+ Expect(len(addrs)).To(Equal(1))
+ addr = addrs[0].IPNet.String()
+ Expect(addr).To(Equal(EXPECTED_IP))
+
+ //Reconfigure IP when ForceAddress is set to true and IP address has been changed.
+ err = ensureBridgeAddr(bridge, gwnSecond, true)
+ Expect(err).NotTo(HaveOccurred())
+
+ //Retrieve the IP address after reconfiguration
+ addrs, err = netlink.AddrList(bridge, syscall.AF_INET)
+ Expect(len(addrs)).To(Equal(1))
+ addr = addrs[0].IPNet.String()
+ Expect(addr).To(Equal(CHANGED_EXPECTED_IP))
+
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+ })
+})
--- /dev/null
+// Copyright 2015 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 (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net"
+ "runtime"
+
+ "github.com/containernetworking/cni/pkg/ip"
+ "github.com/containernetworking/cni/pkg/ipam"
+ "github.com/containernetworking/cni/pkg/ns"
+ "github.com/containernetworking/cni/pkg/skel"
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containernetworking/cni/pkg/utils/sysctl"
+ "github.com/containernetworking/cni/pkg/version"
+ "github.com/vishvananda/netlink"
+)
+
+const (
+ IPv4InterfaceArpProxySysctlTemplate = "net.ipv4.conf.%s.proxy_arp"
+)
+
+type NetConf struct {
+ types.NetConf
+ Master string `json:"master"`
+ Mode string `json:"mode"`
+ MTU int `json:"mtu"`
+}
+
+func init() {
+ // this ensures that main runs only on main thread (thread group leader).
+ // since namespace ops (unshare, setns) are done for a single thread, we
+ // must ensure that the goroutine does not jump from OS thread to thread
+ runtime.LockOSThread()
+}
+
+func loadConf(bytes []byte) (*NetConf, string, error) {
+ n := &NetConf{}
+ if err := json.Unmarshal(bytes, n); err != nil {
+ return nil, "", fmt.Errorf("failed to load netconf: %v", err)
+ }
+ if n.Master == "" {
+ return nil, "", fmt.Errorf(`"master" field is required. It specifies the host interface name to virtualize`)
+ }
+ return n, n.CNIVersion, nil
+}
+
+func modeFromString(s string) (netlink.MacvlanMode, error) {
+ switch s {
+ case "", "bridge":
+ return netlink.MACVLAN_MODE_BRIDGE, nil
+ case "private":
+ return netlink.MACVLAN_MODE_PRIVATE, nil
+ case "vepa":
+ return netlink.MACVLAN_MODE_VEPA, nil
+ case "passthru":
+ return netlink.MACVLAN_MODE_PASSTHRU, nil
+ default:
+ return 0, fmt.Errorf("unknown macvlan mode: %q", s)
+ }
+}
+
+func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interface, error) {
+ macvlan := ¤t.Interface{}
+
+ mode, err := modeFromString(conf.Mode)
+ if err != nil {
+ return nil, err
+ }
+
+println("createMacvlan called")
+ m, err := netlink.LinkByName(conf.Master)
+ if err != nil {
+ return nil, fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
+ }
+
+println("master found")
+ // due to kernel bug we have to create with tmpName or it might
+ // collide with the name on the host and error out
+ tmpName, err := ip.RandomVethName()
+ if err != nil {
+ return nil, err
+ }
+
+ mv := &netlink.Macvlan{
+ LinkAttrs: netlink.LinkAttrs{
+ MTU: conf.MTU,
+ Name: tmpName,
+ ParentIndex: m.Attrs().Index,
+ Namespace: netlink.NsFd(int(netns.Fd())),
+ },
+ Mode: mode,
+ }
+
+ if err := netlink.LinkAdd(mv); err != nil {
+ return nil, fmt.Errorf("failed to create macvlan: %v", err)
+ }
+println("add worked")
+ err = netns.Do(func(_ ns.NetNS) error {
+ // TODO: duplicate following lines for ipv6 support, when it will be added in other places
+ ipv4SysctlValueName := fmt.Sprintf(IPv4InterfaceArpProxySysctlTemplate, tmpName)
+ if _, err := sysctl.Sysctl(ipv4SysctlValueName, "1"); err != nil {
+ // remove the newly added link and ignore errors, because we already are in a failed state
+ _ = netlink.LinkDel(mv)
+ return fmt.Errorf("failed to set proxy_arp on newly added interface %q: %v", tmpName, err)
+ }
+println("ipv4 stuff worked")
+ err := ip.RenameLink(tmpName, ifName)
+ if err != nil {
+ _ = netlink.LinkDel(mv)
+ return fmt.Errorf("failed to rename macvlan to %q: %v", ifName, err)
+ }
+ macvlan.Name = ifName
+println("macvlan name is ", ifName)
+
+ // Re-fetch macvlan to get all properties/attributes
+ contMacvlan, err := netlink.LinkByName(ifName)
+ if err != nil {
+ return fmt.Errorf("failed to refetch macvlan %q: %v", ifName, err)
+ }
+ macvlan.Mac = contMacvlan.Attrs().HardwareAddr.String()
+ macvlan.Sandbox = netns.Path()
+println("return after re-fetch")
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+println("end of function return")
+
+ return macvlan, nil
+}
+
+func cmdAdd(args *skel.CmdArgs) error {
+ n, cniVersion, err := loadConf(args.StdinData)
+ if err != nil {
+ return err
+ }
+
+ netns, err := ns.GetNS(args.Netns)
+ if err != nil {
+ return fmt.Errorf("failed to open netns %q: %v", netns, err)
+ }
+ defer netns.Close()
+
+ macvlanInterface, err := createMacvlan(n, args.IfName, netns)
+ if err != nil {
+ return err
+ }
+
+ // 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
+ }
+ // Convert whatever the IPAM result was into the current Result type
+ result, err := current.NewResultFromResult(r)
+ if err != nil {
+ return err
+ }
+
+println("IPAM returned ok")
+ if len(result.IPs) == 0 {
+ return errors.New("IPAM plugin returned missing IP config")
+ }
+ result.Interfaces = []*current.Interface{macvlanInterface}
+
+ var firstV4Addr net.IP
+ var firstV6Addr net.IP
+ for _, ipc := range result.IPs {
+ // All addresses apply to the container macvlan interface
+ ipc.Interface = 0
+
+ if ipc.Address.IP.To4() != nil && firstV4Addr == nil {
+ firstV4Addr = ipc.Address.IP
+ }
+
+ if ipc.Address.IP.To16() != nil && firstV6Addr == nil {
+ firstV6Addr = ipc.Address.IP
+ }
+ }
+
+ println("looking at first V4 addr")
+ if firstV4Addr != nil {
+ err = netns.Do(func(_ ns.NetNS) error {
+ if err := ip.SetHWAddrByIP(args.IfName, firstV4Addr, nil /* TODO IPv6 */); err != nil {
+ return err
+ }
+
+ println("return ipv4 config interface")
+ return ipam.ConfigureIface(args.IfName, result)
+ })
+ if err != nil {
+ return err
+ }
+ }
+ println("done with first V4 addr")
+
+ println("looking at first V6 addr")
+ if firstV6Addr != nil {
+ err = netns.Do(func(_ ns.NetNS) error {
+
+ println("return ipv6 config interface")
+ return ipam.ConfigureIface(args.IfName, result)
+ })
+ if err != nil {
+ return err
+ }
+ }
+ println("done with first V6 addr")
+
+ // Re-fetch macvlan interface as its MAC address may have changed
+ err = netns.Do(func(_ ns.NetNS) error {
+ link, err := netlink.LinkByName(args.IfName)
+ if err != nil {
+ return fmt.Errorf("failed to re-fetch macvlan interface: %v", err)
+ }
+ macvlanInterface.Mac = link.Attrs().HardwareAddr.String()
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+
+ result.DNS = n.DNS
+
+println("end of function return")
+
+ return types.PrintResult(result, cniVersion)
+}
+
+func cmdDel(args *skel.CmdArgs) error {
+ n, _, err := loadConf(args.StdinData)
+ if err != nil {
+ return err
+ }
+
+ err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
+ if err != nil {
+ return err
+ }
+
+ if args.Netns == "" {
+ return nil
+ }
+
+ return ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
+ return ip.DelLinkByName(args.IfName)
+ })
+}
+
+func main() {
+ skel.PluginMain(cmdAdd, cmdDel, version.All)
+}
--- /dev/null
+// Copyright 2016 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 (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+
+ "testing"
+)
+
+func TestMacvlan(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "macvlan Suite")
+}
--- /dev/null
+// Copyright 2015 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 (
+ "fmt"
+ "net"
+ "syscall"
+
+ "github.com/containernetworking/cni/pkg/ns"
+ "github.com/containernetworking/cni/pkg/skel"
+ "github.com/containernetworking/cni/pkg/testutils"
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containernetworking/cni/pkg/utils/hwaddr"
+
+ "github.com/vishvananda/netlink"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+const MASTER_NAME = "eth0"
+
+var _ = Describe("macvlan Operations", func() {
+ var originalNS ns.NetNS
+
+ BeforeEach(func() {
+ // Create a new NetNS so we don't modify the host
+ var err error
+ originalNS, err = ns.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ // Add master
+ err = netlink.LinkAdd(&netlink.Dummy{
+ LinkAttrs: netlink.LinkAttrs{
+ Name: MASTER_NAME,
+ },
+ })
+ Expect(err).NotTo(HaveOccurred())
+ _, err = netlink.LinkByName(MASTER_NAME)
+ Expect(err).NotTo(HaveOccurred())
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+ })
+
+ AfterEach(func() {
+ Expect(originalNS.Close()).To(Succeed())
+ })
+
+ It("creates an macvlan link in a non-default namespace", func() {
+ conf := &NetConf{
+ NetConf: types.NetConf{
+ CNIVersion: "0.3.0",
+ Name: "testConfig",
+ Type: "macvlan",
+ },
+ Master: MASTER_NAME,
+ Mode: "bridge",
+ MTU: 1500,
+ }
+
+ targetNs, err := ns.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+ defer targetNs.Close()
+
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ _, err = createMacvlan(conf, "foobar0", targetNs)
+ Expect(err).NotTo(HaveOccurred())
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // Make sure macvlan link exists in the target namespace
+ err = targetNs.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ link, err := netlink.LinkByName("foobar0")
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal("foobar0"))
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+ })
+
+ It("configures and deconfigures a macvlan link with ADD/DEL", func() {
+ const IFNAME = "macvl0"
+
+ conf := fmt.Sprintf(`{
+ "cniVersion": "0.3.0",
+ "name": "mynet",
+ "type": "macvlan",
+ "master": "%s",
+ "ipam": {
+ "type": "host-local",
+ "subnet": "10.1.2.0/24"
+ }
+}`, MASTER_NAME)
+
+ targetNs, err := ns.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+ defer targetNs.Close()
+
+ args := &skel.CmdArgs{
+ ContainerID: "dummy",
+ Netns: targetNs.Path(),
+ IfName: IFNAME,
+ StdinData: []byte(conf),
+ }
+
+ var result *current.Result
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ r, _, err := testutils.CmdAddWithResult(targetNs.Path(), 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(IFNAME))
+ Expect(len(result.IPs)).To(Equal(1))
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // Make sure macvlan link exists in the target namespace
+ err = targetNs.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ link, err := netlink.LinkByName(IFNAME)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().Name).To(Equal(IFNAME))
+
+ hwaddrString := fmt.Sprintf("%s", link.Attrs().HardwareAddr)
+ Expect(hwaddrString).To(HavePrefix(hwaddr.PrivateMACPrefixString))
+
+ hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
+
+ addrs, err := netlink.AddrList(link, syscall.AF_INET)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(addrs)).To(Equal(1))
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ err = originalNS.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ err := testutils.CmdDelWithResult(targetNs.Path(), IFNAME, func() error {
+ return cmdDel(args)
+ })
+ Expect(err).NotTo(HaveOccurred())
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // Make sure macvlan link has been deleted
+ err = targetNs.Do(func(ns.NetNS) error {
+ defer GinkgoRecover()
+
+ link, err := netlink.LinkByName(IFNAME)
+ Expect(err).To(HaveOccurred())
+ Expect(link).To(BeNil())
+ return nil
+ })
+ Expect(err).NotTo(HaveOccurred())
+ })
+})
--- /dev/null
+#/bin/bash
+export DEBUG=1
+export NETCONFPATH=/etc/cni/net.d/
+export CNI_PATH=/home/mcambria/go2/src/stash.verizon.com/cni/bin
+export NETCONFPATH=${NETCONFPATH-/etc/cni/net.d}
+export CNI_COMMAND=ADD
+export CNI_NETNS=/var/run/netns/mcc-cni-ipv6
+export CNI_CONTAINERID=mcc-cni-ipv6
+
+export PATH=$CNI_PATH:$PATH
+export CNI_IFNAME=eth1
+
+export MCCVAL="Jamal Env Value"
+
+vz-macvlan < 26-thing2-macvlan0.conf
+
+
--- /dev/null
+#/bin/bash
+export DEBUG=1
+export NETCONFPATH=/etc/cni/net.d/
+export CNI_PATH=/home/mcambria/go2/src/stash.verizon.com/cni/bin
+export NETCONFPATH=${NETCONFPATH-/etc/cni/net.d}
+export CNI_COMMAND=DEL
+export CNI_NETNS=/var/run/netns/mcc-cni-ipv6
+export CNI_CONTAINERID=mcc-cni-ipv6
+
+export PATH=$CNI_PATH:$PATH
+export CNI_IFNAME=eth1
+
+export MCCVAL="Jamal Env Value"
+
+vz-macvlan < 26-thing2-macvlan0.conf
+
+