diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 6cfd362..e872bda 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -21,35 +21,39 @@ }, { "ImportPath": "github.com/appc/cni/pkg/ip", - "Rev": "88377fa3466eea30817b038c69ac06686e7655b7" + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" }, { "ImportPath": "github.com/appc/cni/pkg/ns", - "Rev": "88377fa3466eea30817b038c69ac06686e7655b7" + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" }, { "ImportPath": "github.com/appc/cni/pkg/plugin", - "Rev": "88377fa3466eea30817b038c69ac06686e7655b7" + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" }, { "ImportPath": "github.com/appc/cni/pkg/skel", - "Rev": "88377fa3466eea30817b038c69ac06686e7655b7" + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" }, { "ImportPath": "github.com/appc/cni/plugins/ipam/host-local", - "Rev": "88377fa3466eea30817b038c69ac06686e7655b7" + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" }, { "ImportPath": "github.com/appc/cni/plugins/main/bridge", - "Rev": "88377fa3466eea30817b038c69ac06686e7655b7" + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" + }, + { + "ImportPath": "github.com/appc/cni/plugins/main/ipvlan", + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" }, { "ImportPath": "github.com/appc/cni/plugins/main/macvlan", - "Rev": "88377fa3466eea30817b038c69ac06686e7655b7" + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" }, { "ImportPath": "github.com/appc/cni/plugins/main/veth", - "Rev": "88377fa3466eea30817b038c69ac06686e7655b7" + "Rev": "49a018fa85ac216fdc76c835371807bdcf3b8ead" }, { "ImportPath": "github.com/appc/docker2aci/lib", diff --git a/Godeps/_workspace/src/github.com/appc/cni/pkg/ip/link.go b/Godeps/_workspace/src/github.com/appc/cni/pkg/ip/link.go index e59e49d..c2ecef0 100644 --- a/Godeps/_workspace/src/github.com/appc/cni/pkg/ip/link.go +++ b/Godeps/_workspace/src/github.com/appc/cni/pkg/ip/link.go @@ -15,7 +15,7 @@ package ip import ( - "crypto/sha512" + "crypto/rand" "fmt" "net" "os" @@ -23,7 +23,7 @@ import ( "github.com/coreos/rkt/Godeps/_workspace/src/github.com/vishvananda/netlink" ) -func makeVeth(name, peer string, mtu int) (netlink.Link, error) { +func makeVethPair(name, peer string, mtu int) (netlink.Link, error) { veth := &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{ Name: name, @@ -39,33 +39,50 @@ func makeVeth(name, peer string, mtu int) (netlink.Link, error) { return veth, nil } +func makeVeth(name string, mtu int) (peerName string, veth netlink.Link, err error) { + for i := 0; i < 10; i++ { + peerName, err = RandomVethName() + if err != nil { + return + } + + veth, err = makeVethPair(name, peerName, mtu) + switch { + case err == nil: + return + + case os.IsExist(err): + continue + + default: + err = fmt.Errorf("failed to make veth pair: %v", err) + return + } + } + + // should really never be hit + err = fmt.Errorf("failed to find a unique veth name") + return +} + // RandomVethName returns string "veth" with random prefix (hashed from entropy) -func RandomVethName(entropy string) string { - h := sha512.New() - h.Write([]byte(entropy)) - return fmt.Sprintf("veth%x", h.Sum(nil)[:5]) +func RandomVethName() (string, error) { + entropy := make([]byte, 4) + _, err := rand.Reader.Read(entropy) + if err != nil { + return "", fmt.Errorf("failed to generate random veth name: %v", err) + } + + // NetworkManager (recent versions) will ignore veth devices that start with "veth" + return fmt.Sprintf("veth%x", entropy), nil } // SetupVeth sets up a virtual ethernet link. // Should be in container netns. -// TODO(eyakubovich): get rid of entropy and ask kernel to pick name via pattern -func SetupVeth(entropy, contVethName string, mtu int, hostNS *os.File) (hostVeth, contVeth netlink.Link, err error) { - // NetworkManager (recent versions) will ignore veth devices that start with "veth" - hostVethName := RandomVethName(entropy) - hostVeth, err = makeVeth(hostVethName, contVethName, mtu) +func SetupVeth(contVethName string, mtu int, hostNS *os.File) (hostVeth, contVeth netlink.Link, err error) { + var hostVethName string + hostVethName, contVeth, err = makeVeth(contVethName, mtu) if err != nil { - err = fmt.Errorf("failed to make veth pair: %v", err) - return - } - - if err = netlink.LinkSetUp(hostVeth); err != nil { - err = fmt.Errorf("failed to set %q up: %v", hostVethName, err) - return - } - - contVeth, err = netlink.LinkByName(contVethName) - if err != nil { - err = fmt.Errorf("failed to lookup %q: %v", contVethName, err) return } @@ -74,6 +91,17 @@ func SetupVeth(entropy, contVethName string, mtu int, hostNS *os.File) (hostVeth return } + hostVeth, err = netlink.LinkByName(hostVethName) + if err != nil { + err = fmt.Errorf("failed to lookup %q: %v", hostVethName, err) + return + } + + if err = netlink.LinkSetUp(hostVeth); err != nil { + err = fmt.Errorf("failed to set %q up: %v", contVethName, err) + return + } + if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil { err = fmt.Errorf("failed to move veth to host netns: %v", err) return diff --git a/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin/ipam.go b/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin/ipam.go index 4665011..9d0c0e3 100644 --- a/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin/ipam.go +++ b/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin/ipam.go @@ -124,9 +124,18 @@ func ConfigureIface(ifName string, res *Result) error { return nil } -// PrintResult writes out prettified Result to stdout +// PrintResult writes out prettified Result JSON to stdout func PrintResult(res *Result) error { - data, err := json.MarshalIndent(res, "", " ") + return prettyPrint(res) +} + +// PrintError writes out prettified Error JSON to stdout +func PrintError(err *Error) error { + return prettyPrint(err) +} + +func prettyPrint(obj interface{}) error { + data, err := json.MarshalIndent(obj, "", " ") if err != nil { return err } diff --git a/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin/types.go b/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin/types.go index 428f0c7..15ab408 100644 --- a/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin/types.go +++ b/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin/types.go @@ -48,6 +48,12 @@ type Route struct { GW net.IP } +type Error struct { + Code uint `json:"code"` + Msg string `json:"msg"` + Details string `json:"details,omitempty"` +} + // net.IPNet is not JSON (un)marshallable so this duality is needed // for our custom ip.IPNet type diff --git a/Godeps/_workspace/src/github.com/appc/cni/pkg/skel/skel.go b/Godeps/_workspace/src/github.com/appc/cni/pkg/skel/skel.go index 9f03335..c370ba3 100644 --- a/Godeps/_workspace/src/github.com/appc/cni/pkg/skel/skel.go +++ b/Godeps/_workspace/src/github.com/appc/cni/pkg/skel/skel.go @@ -17,9 +17,12 @@ package skel import ( + "fmt" "io/ioutil" "log" "os" + + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin" ) // CmdArgs captures all the arguments passed in to the plugin @@ -61,13 +64,12 @@ func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) { } if argsMissing { - os.Exit(1) + die("required env variables missing") } stdinData, err := ioutil.ReadAll(os.Stdin) if err != nil { - log.Printf("Error reading from stdin: %v", err) - os.Exit(1) + die("error reading from stdin: %v", err) } cmdArgs := &CmdArgs{ @@ -87,12 +89,18 @@ func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) { err = cmdDel(cmdArgs) default: - log.Printf("Unknown CNI_COMMAND: %v", cmd) - os.Exit(1) + die("unknown CNI_COMMAND: %v", cmd) } if err != nil { - log.Printf("%v: %v", cmd, err) - os.Exit(1) + die(err.Error()) } } + +func die(f string, args ...interface{}) { + plugin.PrintError(&plugin.Error{ + Code: 100, + Msg: fmt.Sprintf(f, args...), + }) + os.Exit(1) +} diff --git a/Godeps/_workspace/src/github.com/appc/cni/plugins/ipam/host-local/main.go b/Godeps/_workspace/src/github.com/appc/cni/plugins/ipam/host-local/main.go index be07f06..b47b934 100644 --- a/Godeps/_workspace/src/github.com/appc/cni/plugins/ipam/host-local/main.go +++ b/Godeps/_workspace/src/github.com/appc/cni/plugins/ipam/host-local/main.go @@ -47,9 +47,9 @@ func cmdAdd(args *skel.CmdArgs) error { switch ipamConf.Type { case "host-local": - ipConf, err = allocator.Get(args.Netns) + ipConf, err = allocator.Get(args.ContainerID) case "host-local-ptp": - ipConf, err = allocator.GetPtP(args.Netns) + ipConf, err = allocator.GetPtP(args.ContainerID) default: return errors.New("Unsupported IPAM plugin type") } @@ -80,5 +80,5 @@ func cmdDel(args *skel.CmdArgs) error { return err } - return allocator.Release(args.Netns) + return allocator.Release(args.ContainerID) } diff --git a/Godeps/_workspace/src/github.com/appc/cni/plugins/main/bridge/bridge.go b/Godeps/_workspace/src/github.com/appc/cni/plugins/main/bridge/bridge.go index 3544e7a..3cb4f0f 100644 --- a/Godeps/_workspace/src/github.com/appc/cni/plugins/main/bridge/bridge.go +++ b/Godeps/_workspace/src/github.com/appc/cni/plugins/main/bridge/bridge.go @@ -130,7 +130,7 @@ func setupVeth(netns string, br *netlink.Bridge, ifName string, mtu int, pr *plu err := ns.WithNetNSPath(netns, func(hostNS *os.File) error { // create the veth pair in the container and move host end into host netns - hostVeth, _, err := ip.SetupVeth(netns, ifName, mtu, hostNS) + hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS) if err != nil { return err } diff --git a/Godeps/_workspace/src/github.com/appc/cni/plugins/main/ipvlan/ipvlan.go b/Godeps/_workspace/src/github.com/appc/cni/plugins/main/ipvlan/ipvlan.go new file mode 100644 index 0000000..1856954 --- /dev/null +++ b/Godeps/_workspace/src/github.com/appc/cni/plugins/main/ipvlan/ipvlan.go @@ -0,0 +1,177 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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" + "os" + "runtime" + + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/pkg/ip" + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/pkg/ns" + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/pkg/plugin" + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/pkg/skel" + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/vishvananda/netlink" +) + +type NetConf struct { + plugin.NetConf + Master string `json:"master"` + Mode string `json:"mode"` + IPMasq bool `json:"ipMasq"` + 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, 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, nil +} + +func modeFromString(s string) (netlink.IPVlanMode, error) { + switch s { + case "": + return netlink.IPVLAN_MODE_L2, nil + case "l2": + return netlink.IPVLAN_MODE_L2, nil + case "l3": + return netlink.IPVLAN_MODE_L3, nil + default: + return 0, fmt.Errorf("unknown ipvlan mode: %q", s) + } +} + +func createIpvlan(conf *NetConf, ifName string, netns *os.File) error { + mode, err := modeFromString(conf.Mode) + if err != nil { + return err + } + + m, err := netlink.LinkByName(conf.Master) + if err != nil { + return fmt.Errorf("failed to lookup master %q: %v", conf.Master, err) + } + + mv := &netlink.IPVlan{ + LinkAttrs: netlink.LinkAttrs{ + MTU: conf.MTU, + Name: ifName, + ParentIndex: m.Attrs().Index, + Namespace: netlink.NsFd(int(netns.Fd())), + }, + Mode: mode, + } + + if err := netlink.LinkAdd(mv); err != nil { + return fmt.Errorf("failed to create ipvlan: %v", err) + } + + return err +} + +func cmdAdd(args *skel.CmdArgs) error { + n, err := loadConf(args.StdinData) + if err != nil { + return err + } + + netns, err := os.Open(args.Netns) + if err != nil { + return fmt.Errorf("failed to open netns %q: %v", netns, err) + } + defer netns.Close() + + tmpName, err := ip.RandomVethName() + if err != nil { + return err + } + + if err = createIpvlan(n, tmpName, netns); err != nil { + return err + } + + // run the IPAM plugin and get back the config to apply + result, err := plugin.ExecAdd(n.IPAM.Type, args.StdinData) + if err != nil { + return err + } + if result.IP4 == nil { + return errors.New("IPAM plugin returned missing IPv4 config") + } + + err = ns.WithNetNS(netns, func(_ *os.File) error { + err := renameLink(tmpName, args.IfName) + if err != nil { + return fmt.Errorf("failed to rename ipvlan to %q: %v", args.IfName, err) + } + + return plugin.ConfigureIface(args.IfName, result) + }) + if err != nil { + return err + } + + if n.IPMasq { + chain := "CNI-" + n.Name + if err = ip.SetupIPMasq(ip.Network(&result.IP4.IP), chain); err != nil { + return err + } + } + + return plugin.PrintResult(result) +} + +func cmdDel(args *skel.CmdArgs) error { + n, err := loadConf(args.StdinData) + if err != nil { + return err + } + + err = plugin.ExecDel(n.IPAM.Type, args.StdinData) + if err != nil { + return err + } + + return ns.WithNetNSPath(args.Netns, func(hostNS *os.File) error { + return ip.DelLinkByName(args.IfName) + }) +} + +func renameLink(curName, newName string) error { + link, err := netlink.LinkByName(curName) + if err != nil { + return err + } + + return netlink.LinkSetName(link, newName) +} + +func main() { + skel.PluginMain(cmdAdd, cmdDel) +} diff --git a/Godeps/_workspace/src/github.com/appc/cni/plugins/main/macvlan/macvlan.go b/Godeps/_workspace/src/github.com/appc/cni/plugins/main/macvlan/macvlan.go index cbab389..d3603d1 100644 --- a/Godeps/_workspace/src/github.com/appc/cni/plugins/main/macvlan/macvlan.go +++ b/Godeps/_workspace/src/github.com/appc/cni/plugins/main/macvlan/macvlan.go @@ -111,7 +111,11 @@ func cmdAdd(args *skel.CmdArgs) error { } defer netns.Close() - tmpName := ip.RandomVethName(args.Netns) + tmpName, err := ip.RandomVethName() + if err != nil { + return err + } + if err = createMacvlan(n, tmpName, netns); err != nil { return err } diff --git a/Godeps/_workspace/src/github.com/appc/cni/plugins/main/veth/veth.go b/Godeps/_workspace/src/github.com/appc/cni/plugins/main/veth/veth.go index ddc26a2..5b094b2 100644 --- a/Godeps/_workspace/src/github.com/appc/cni/plugins/main/veth/veth.go +++ b/Godeps/_workspace/src/github.com/appc/cni/plugins/main/veth/veth.go @@ -46,9 +46,7 @@ type NetConf struct { func setupContainerVeth(netns, ifName string, mtu int, pr *plugin.Result) (string, error) { var hostVethName string err := ns.WithNetNSPath(netns, func(hostNS *os.File) error { - entropy := netns + ifName - - hostVeth, _, err := ip.SetupVeth(entropy, ifName, mtu, hostNS) + hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS) if err != nil { return err } @@ -115,7 +113,7 @@ func cmdAdd(args *skel.CmdArgs) error { } if conf.IPMasq { - h := sha512.Sum512([]byte(args.Netns)) + h := sha512.Sum512([]byte(args.ContainerID)) chain := fmt.Sprintf("CNI-%s-%x", conf.Name, h[:8]) if err = ip.SetupIPMasq(&result.IP4.IP, chain); err != nil { return err @@ -142,7 +140,7 @@ func cmdDel(args *skel.CmdArgs) error { } if conf.IPMasq { - h := sha512.Sum512([]byte(args.Netns)) + h := sha512.Sum512([]byte(args.ContainerID)) chain := fmt.Sprintf("CNI-%s-%x", conf.Name, h[:8]) if err = ip.TeardownIPMasq(ipn, chain); err != nil { return err diff --git a/stage1/dummy.go b/stage1/dummy.go index 9accd51..2f1aaf4 100644 --- a/stage1/dummy.go +++ b/stage1/dummy.go @@ -11,6 +11,7 @@ import ( import ( _ "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/plugins/ipam/host-local" _ "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/plugins/main/bridge" + _ "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/plugins/main/ipvlan" _ "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/plugins/main/macvlan" _ "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/plugins/main/veth" )