diff --git a/net/veth.go b/net/veth.go index b7708ccc..76c0932e 100644 --- a/net/veth.go +++ b/net/veth.go @@ -106,7 +106,7 @@ func interfaceExistsInNamespace(netNSPath string, ifName string) bool { // NB: This function can be used only by a process that terminates immediately // after calling the function as it changes netns via WithNetNSLinkUnsafe. -func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool) error { +func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool, hairpinMode bool) error { ns, err := netns.GetFromPath(netNSPath) if err != nil { return err @@ -124,7 +124,7 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult id = id[:maxIDLen] // trim passed ID if too long } name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id - _, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, func(veth netlink.Link) error { + veth, 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) } @@ -136,6 +136,10 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult if err != nil { return err } + if err = netlink.LinkSetHairpin(veth, hairpinMode); err != nil { + return fmt.Errorf("unable to set hairpin mode to %t for bridge side of veth %s: %s", hairpinMode, name, err) + } + } if err := WithNetNSLinkUnsafe(ns, ifName, func(veth netlink.Link) error { diff --git a/plugin/net/cni.go b/plugin/net/cni.go index 8265bf45..bfc4f45e 100644 --- a/plugin/net/cni.go +++ b/plugin/net/cni.go @@ -34,7 +34,8 @@ func NewCNIPlugin(weave *weaveapi.Client) *CNIPlugin { func loadNetConf(bytes []byte) (*NetConf, error) { n := &NetConf{ - BrName: weavenet.WeaveBridgeName, + BrName: weavenet.WeaveBridgeName, + HairpinMode: true, } if err := json.Unmarshal(bytes, n); err != nil { return nil, fmt.Errorf("failed to load netconf: %v", err) @@ -128,7 +129,7 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error { id = fmt.Sprintf("%x", data) } - if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false); err != nil { + if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false, conf.HairpinMode); err != nil { return err } if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error { @@ -218,8 +219,9 @@ func logOnStderr(err error) { type NetConf struct { types.NetConf - BrName string `json:"bridge"` - IsGW bool `json:"isGateway"` - IPMasq bool `json:"ipMasq"` - MTU int `json:"mtu"` + BrName string `json:"bridge"` + IsGW bool `json:"isGateway"` + IPMasq bool `json:"ipMasq"` + MTU int `json:"mtu"` + HairpinMode bool `json:"hairpinMode"` } diff --git a/prog/weaveutil/attach.go b/prog/weaveutil/attach.go index d88c6ba6..d1f636b2 100644 --- a/prog/weaveutil/attach.go +++ b/prog/weaveutil/attach.go @@ -17,11 +17,12 @@ import ( func attach(args []string) error { if len(args) < 4 { - cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] ...") + cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] ...") } keepTXOn := false withMulticastRoute := true + hairpinMode := true for i := 0; i < len(args); { switch args[i] { case "--no-multicast-route": @@ -30,6 +31,9 @@ func attach(args []string) error { case "--keep-tx-on": keepTXOn = true args = append(args[:i], args[i+1:]...) + case "--hairpin-mode=false": + hairpinMode = false + args = append(args[:i], args[i+1:]...) default: i++ } @@ -58,7 +62,7 @@ func attach(args []string) error { return err } - err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn) + err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn, hairpinMode) // 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]) diff --git a/proxy/proxy.go b/proxy/proxy.go index d4469b70..d991794c 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -515,7 +515,7 @@ func (proxy *Proxy) attach(containerID string) error { pid := container.State.Pid // Passing 0 for mtu means it will be taken from the bridge - err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, weavenet.WeaveBridgeName, 0, !proxy.NoMulticastRoute, ips, proxy.KeepTXOn) + err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, weavenet.WeaveBridgeName, 0, !proxy.NoMulticastRoute, ips, proxy.KeepTXOn, true) if err != nil { return err } diff --git a/site/kube-addon.md b/site/kube-addon.md index 6ed8a105..522f52b3 100644 --- a/site/kube-addon.md +++ b/site/kube-addon.md @@ -221,6 +221,10 @@ The list of variables you can set is: * `CHECKPOINT_DISABLE` - if set to 1, disable checking for new Weave Net versions (default is blank, i.e. check is enabled) +* `HAIRPIN_MODE` - Weave Net defaults to enabling hairpin on the bridge side of + the `veth` pair for containers attached. If you need to disable hairpin, e.g. your + kernel is one of those that can panic if hairpin is enabled, then you can disable it + by setting `HAIRPIN_MODE=false`. * `IPALLOC_RANGE` - the range of IP addresses used by Weave Net and the subnet they are placed in (CIDR format; default `10.32.0.0/12`) * `EXPECT_NPC` - set to 0 to disable Network Policy Controller (default is on) diff --git a/test/830_cni_plugin_test.sh b/test/830_cni_plugin_test.sh index 92a0bc1c..e53e988c 100755 --- a/test/830_cni_plugin_test.sh +++ b/test/830_cni_plugin_test.sh @@ -8,7 +8,7 @@ cni_connect() { pid=$(container_pid $1 $2) id=$(docker_on $1 inspect -f '{{.Id}}' $2) run_on $1 sudo CNI_COMMAND=ADD CNI_CONTAINERID=$id CNI_IFNAME=eth0 \ - CNI_NETNS=/proc/$pid/ns/net CNI_PATH=/opt/cni/bin /opt/cni/bin/weave-net + CNI_NETNS=/proc/$pid/ns/net CNI_PATH=/opt/cni/bin /opt/cni/bin/weave-net } run_on $HOST1 sudo mkdir -p /opt/cni/bin @@ -19,6 +19,11 @@ weave_on $HOST1 launch C0=$(docker_on $HOST1 run --net=none --name=c0 -dt $SMALL_IMAGE /bin/sh) C1=$(docker_on $HOST1 run --net=none --privileged --name=c1 -dt $SMALL_IMAGE /bin/sh) C2=$(docker_on $HOST1 run --net=none --name=c2 -dt $SMALL_IMAGE /bin/sh) +CH0=$(docker_on $HOST1 run --net=none --name=ch0 -dt $SMALL_IMAGE /bin/sh) +CH1=$(docker_on $HOST1 run --net=none --name=ch1 -dt $SMALL_IMAGE /bin/sh) +CH2=$(docker_on $HOST1 run --net=none --name=ch2 -dt $SMALL_IMAGE /bin/sh) + + # Enable unsolicited ARPs so that ping after the address reuse does not time out exec_on $HOST1 c1 sysctl -w net.ipv4.conf.all.arp_accept=1 @@ -52,6 +57,29 @@ cni_connect $HOST1 c2 <"$1" <