From 42ff61f97c05cc5e9ac231ffa0ef9badfd09df1a Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 8 Mar 2016 16:50:39 +0000 Subject: [PATCH] CNI net plugin --- .gitmodules | 3 + plugin/net/cni.go | 158 +++++++++++++++++++++++++++ prog/plugin/main.go | 8 +- vendor/github.com/coreos/go-iptables | 1 + 4 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 plugin/net/cni.go create mode 160000 vendor/github.com/coreos/go-iptables diff --git a/.gitmodules b/.gitmodules index f2a0e10d..58ec72d5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -67,3 +67,6 @@ [submodule "vendor/github.com/appc/cni"] path = vendor/github.com/appc/cni url = https://github.com/appc/cni +[submodule "vendor/github.com/coreos/go-iptables"] + path = vendor/github.com/coreos/go-iptables + url = https://github.com/coreos/go-iptables diff --git a/plugin/net/cni.go b/plugin/net/cni.go new file mode 100644 index 00000000..4ab8bd9c --- /dev/null +++ b/plugin/net/cni.go @@ -0,0 +1,158 @@ +package plugin + +import ( + "crypto/rand" + "encoding/json" + "fmt" + + "github.com/appc/cni/pkg/ipam" + "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" + weaveapi "github.com/weaveworks/weave/api" + "github.com/weaveworks/weave/common" + ipamplugin "github.com/weaveworks/weave/plugin/ipam" +) + +type CNIPlugin struct { + weave *weaveapi.Client +} + +func NewCNIPlugin(weave *weaveapi.Client) *CNIPlugin { + return &CNIPlugin{weave: weave} +} + +func loadNetConf(bytes []byte) (*NetConf, error) { + n := &NetConf{ + BrName: "weave", + } + if err := json.Unmarshal(bytes, n); err != nil { + return nil, errorf("failed to load netconf: %v", err) + } + return n, nil +} + +func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error { + conf, err := loadNetConf(args.StdinData) + if err != nil { + return err + } + + if conf.IsGW { + return errorf("Gateway functionality not supported") + } + if conf.IPMasq { + return errorf("IP Masquerading functionality not supported") + } + + ns, err := netns.GetFromPath(args.Netns) + if err != nil { + return err + } + defer ns.Close() + + id := args.ContainerID + if len(id) < 5 { + data := make([]byte, 5) + _, err := rand.Reader.Read(data) + if err != nil { + return err + } + id = fmt.Sprintf("%x", data) + } + + local, err := createAndAttach(id, conf.BrName, conf.MTU) + if err != nil { + return err + } + + cleanup := func(err error) error { + netlink.LinkDel(local) + return err + } + guest, err := netlink.LinkByName(local.PeerName) + if err != nil { + return cleanup(err) + } + if err = netlink.LinkSetNsFd(guest, int(ns)); err != nil { + return cleanup(errorf("failed to move veth to container netns: %s", err)) + } + + if err := netlink.LinkSetUp(local); err != nil { + return cleanup(errorf("unable to bring veth up: %s", err)) + } + + var result *types.Result + // Default IPAM is Weave's own + if conf.IPAM.Type == "" { + result, err = ipamplugin.NewIpam(c.weave).Allocate(args) + } else { + result, err = ipam.ExecAdd(conf.IPAM.Type, args.StdinData) + } + if err != nil { + return cleanup(errorf("unable to allocate IP address: %s", err)) + } + if result.IP4 == nil { + return cleanup(errorf("IPAM plugin failed to allocate IP address")) + } + + err = common.WithNetNS(ns, func() error { + guest, err := netlink.LinkByName(local.PeerName) + if err != nil { + return cleanup(err) + } + if err = netlink.LinkSetName(guest, args.IfName); err != nil { + return err + } + return ipam.ConfigureIface(args.IfName, result) + }) + if err != nil { + return cleanup(errorf("error setting up interface: %s", err)) + } + + result.DNS = conf.DNS + return result.Print() +} + +func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error { + conf, err := loadNetConf(args.StdinData) + if err != nil { + return err + } + + ns, err := netns.GetFromPath(args.Netns) + if err != nil { + return err + } + defer ns.Close() + err = common.WithNetNS(ns, func() error { + link, err := netlink.LinkByName(args.IfName) + if err != nil { + return err + } + return netlink.LinkDel(link) + }) + if err != nil { + return errorf("error removing interface: %s", err) + } + + // Default IPAM is Weave's own + if conf.IPAM.Type == "" { + err = ipamplugin.NewIpam(c.weave).Release(args) + } else { + err = ipam.ExecDel(conf.IPAM.Type, args.StdinData) + } + if err != nil { + return errorf("unable to release IP address: %s", err) + } + return nil +} + +type NetConf struct { + types.NetConf + BrName string `json:"bridge"` + IsGW bool `json:"isGateway"` + IPMasq bool `json:"ipMasq"` + MTU int `json:"mtu"` +} diff --git a/prog/plugin/main.go b/prog/plugin/main.go index bbb818ec..38dc04ee 100644 --- a/prog/plugin/main.go +++ b/prog/plugin/main.go @@ -7,6 +7,7 @@ import ( "net/http" "os" "os/signal" + "strings" "syscall" cni "github.com/appc/cni/pkg/skel" @@ -48,10 +49,15 @@ func main() { weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), Log) - if os.Args[0] == "weave-ipam" { + switch { + case strings.HasSuffix(os.Args[0], "weave-ipam"): i := ipamplugin.NewIpam(weave) cni.PluginMain(i.CmdAdd, i.CmdDel) os.Exit(0) + case strings.HasSuffix(os.Args[0], "weave"): + n := netplugin.NewCNIPlugin(weave) + cni.PluginMain(n.CmdAdd, n.CmdDel) + os.Exit(0) } // API 1.21 is the first version that supports docker network commands diff --git a/vendor/github.com/coreos/go-iptables b/vendor/github.com/coreos/go-iptables new file mode 160000 index 00000000..90456be5 --- /dev/null +++ b/vendor/github.com/coreos/go-iptables @@ -0,0 +1 @@ +Subproject commit 90456be57fcb8185b264b77ce42a9539df42df25