From 7670fed72c110346cdc5927dd32be40b7803a084 Mon Sep 17 00:00:00 2001 From: Martynas Pumputis Date: Tue, 7 Jun 2016 14:09:45 +0100 Subject: [PATCH] Add AWSVPC setup routines to the attach script When running in the AWSVPC mode, we need to do the following: - Keep the HW TX offloading on. - Check that the allocated address belongs to the default subnet, because only one subnet is allowed and it should be the default one. If it is not possible to determine whether AWSVPC is on, we ignore all the steps, because the attachment routines have to work regardless whether the router is running and the AWSVPC detection is done by querying the /ipinfo/tracker endpoint served by the router. --- net/veth.go | 8 +++---- plugin/net/cni.go | 2 +- plugin/net/driver.go | 3 ++- prog/weaveutil/attach.go | 45 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/net/veth.go b/net/veth.go index eea00485..fbca71c3 100644 --- a/net/veth.go +++ b/net/veth.go @@ -13,7 +13,7 @@ import ( ) // create and attach a veth to the Weave bridge -func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, init func(peer netlink.Link) error) (*netlink.Veth, error) { +func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, init func(peer netlink.Link) error) (*netlink.Veth, error) { bridge, err := netlink.LinkByName(bridgeName) if err != nil { return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName) @@ -42,7 +42,7 @@ func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, init func(p if err := netlink.LinkSetMasterByIndex(veth, bridge.Attrs().Index); err != nil { return cleanup(`unable to set master of %s: %s`, name, err) } - if bridgeType == Bridge { + if bridgeType == Bridge && !keepTXOn { if err := EthtoolTXOff(peerName); err != nil { return cleanup(`unable to set tx off on %q: %s`, peerName, err) } @@ -111,14 +111,14 @@ func interfaceExistsInNamespace(ns netns.NsHandle, ifName string) bool { return err == nil } -func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet) error { +func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool) error { if !interfaceExistsInNamespace(ns, ifName) { maxIDLen := IFNAMSIZ - 1 - len(vethPrefix+"pl") if len(id) > maxIDLen { id = id[:maxIDLen] // trim passed ID if too long } name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id - _, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, func(veth netlink.Link) error { + _, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, func(veth netlink.Link) error { if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil { return fmt.Errorf("failed to move veth to container netns: %s", err) } diff --git a/plugin/net/cni.go b/plugin/net/cni.go index f43ef967..eddd7dc7 100644 --- a/plugin/net/cni.go +++ b/plugin/net/cni.go @@ -92,7 +92,7 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error { id = fmt.Sprintf("%x", data) } - if err := weavenet.AttachContainer(ns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}); err != nil { + if err := weavenet.AttachContainer(ns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}, false); err != nil { return err } if err := weavenet.WithNetNSLink(ns, args.IfName, func(link netlink.Link) error { diff --git a/plugin/net/driver.go b/plugin/net/driver.go index 675ca93d..d8132529 100644 --- a/plugin/net/driver.go +++ b/plugin/net/driver.go @@ -134,8 +134,9 @@ func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error if err != nil { return nil, driver.error("JoinEndpoint", "unable to find network info: %s", err) } + name, peerName := vethPair(j.EndpointID) - if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, nil); err != nil { + if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, nil); err != nil { return nil, driver.error("JoinEndpoint", "%s", err) } diff --git a/prog/weaveutil/attach.go b/prog/weaveutil/attach.go index 2dbe037f..83a3085a 100644 --- a/prog/weaveutil/attach.go +++ b/prog/weaveutil/attach.go @@ -3,12 +3,15 @@ package main import ( "fmt" "net" + "os" "strconv" "syscall" docker "github.com/fsouza/go-dockerclient" "github.com/vishvananda/netns" + "github.com/weaveworks/weave/api" + "github.com/weaveworks/weave/common" weavenet "github.com/weaveworks/weave/net" ) @@ -17,11 +20,26 @@ func attach(args []string) error { cmdUsage("attach-container", "[--no-multicast-route] ...") } + client := api.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log) + keepTXOn := false + isAWSVPC := false + // In a case of an error, we skip applying necessary steps for AWSVPC, because + // "attach" should work without the weave router running. + if t, err := client.LocalRangeTracker(); err != nil { + fmt.Fprintf(os.Stderr, "unable to determine tracker: %s; skipping AWSVPC initialization\n", err) + } else if t == "awsvpc" { + isAWSVPC = true + keepTXOn = true + } + withMulticastRoute := true if args[0] == "--no-multicast-route" { withMulticastRoute = false args = args[1:] } + if isAWSVPC { + withMulticastRoute = false + } pid, nsContainer, err := containerPidAndNs(args[0]) if err != nil { @@ -40,7 +58,22 @@ func attach(args []string) error { if err != nil { return err } - err = weavenet.AttachContainer(nsContainer, fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs) + + if isAWSVPC { + // Currently, we allow only IP addresses from the default subnet + if defaultSubnet, err := client.DefaultSubnet(); err != nil { + fmt.Fprintf(os.Stderr, "unable to retrieve default subnet: %s; skipping AWSVPC checks\n", err) + } else { + for _, cidr := range cidrs { + if !sameSubnet(cidr, defaultSubnet) { + format := "AWSVPC constraints violation: %s does not belong to the default subnet %s" + return fmt.Errorf(format, cidr, defaultSubnet) + } + } + } + } + + err = weavenet.AttachContainer(nsContainer, fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn) // If we detected an error but the container has died, tell the user that instead. if err != nil && !processExists(pid) { err = fmt.Errorf("Container %s died", args[0]) @@ -48,6 +81,16 @@ func attach(args []string) error { return err } +// sameSubnet checks whether ip belongs to network's subnet +func sameSubnet(ip *net.IPNet, network *net.IPNet) bool { + if network.Contains(ip.IP) { + i1, i2 := ip.Mask.Size() + n1, n2 := network.Mask.Size() + return i1 == n1 && i2 == n2 + } + return false +} + func containerPidAndNs(containerID string) (int, netns.NsHandle, error) { c, err := docker.NewVersionedClientFromEnv("1.18") if err != nil {