diff --git a/net/veth.go b/net/veth.go index a95553e8..62756e8f 100644 --- a/net/veth.go +++ b/net/veth.go @@ -113,7 +113,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 @@ -131,7 +131,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) } @@ -143,6 +143,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 6de93ce4..6bd5e3bf 100644 --- a/prog/weaveutil/attach.go +++ b/prog/weaveutil/attach.go @@ -14,11 +14,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": @@ -27,6 +28,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++ } @@ -55,7 +59,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/site/kube-addon.md b/site/kube-addon.md index 4e664d10..27c069e7 100644 --- a/site/kube-addon.md +++ b/site/kube-addon.md @@ -241,3 +241,7 @@ The list of variables you can set is: a larger size for better performance if your network supports jumbo frames - see [here](/site/using-weave/fastdp.md#mtu) for more details. +* `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`. diff --git a/test/830_cni_plugin_test.sh b/test/830_cni_plugin_test.sh index 623bb93f..d0f1b934 100755 --- a/test/830_cni_plugin_test.sh +++ b/test/830_cni_plugin_test.sh @@ -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" <