diff --git a/net/veth.go b/net/veth.go index fbca71c3..df95852b 100644 --- a/net/veth.go +++ b/net/veth.go @@ -3,8 +3,10 @@ package net import ( "fmt" "net" + "strings" "time" + "github.com/coreos/go-iptables/iptables" "github.com/j-keck/arping" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" @@ -112,6 +114,11 @@ func interfaceExistsInNamespace(ns netns.NsHandle, ifName string) bool { } func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool) error { + ipt, err := iptables.New() + if err != nil { + return err + } + if !interfaceExistsInNamespace(ns, ifName) { maxIDLen := IFNAMSIZ - 1 - len(vethPrefix+"pl") if len(id) > maxIDLen { @@ -129,6 +136,9 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, if err := ConfigureARPCache(ifName); err != nil { return err } + if err := ipt.Append("filter", "INPUT", "-i", ifName, "-d", "224.0.0.0/4", "-j", "DROP"); err != nil { + return err + } return nil }); err != nil { return fmt.Errorf("error setting up interface: %s", err) @@ -145,6 +155,21 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, if err != nil { return err } + + // Add multicast ACCEPT rules for new subnets + for _, ipnet := range newAddresses { + acceptRule := []string{"-i", ifName, "-s", subnet(ipnet), "-d", "224.0.0.0/4", "-j", "ACCEPT"} + exists, err := ipt.Exists("filter", "INPUT", acceptRule...) + if err != nil { + return err + } + if !exists { + if err := ipt.Insert("filter", "INPUT", 1, acceptRule...); err != nil { + return err + } + } + } + if err := netlink.LinkSetUp(veth); err != nil { return err } @@ -177,6 +202,11 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, } func DetachContainer(ns netns.NsHandle, id, ifName string, cidrs []*net.IPNet) error { + ipt, err := iptables.New() + if err != nil { + return err + } + return WithNetNSLink(ns, ifName, func(veth netlink.Link) error { existingAddrs, err := netlink.AddrList(veth, netlink.FAMILY_V4) if err != nil { @@ -194,7 +224,31 @@ func DetachContainer(ns netns.NsHandle, id, ifName string, cidrs []*net.IPNet) e if err != nil { return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err) } + + // Remove multicast ACCEPT rules for subnets we no longer have addresses in + subnets := subnets(addrs) + rules, err := ipt.List("filter", "INPUT") + if err != nil { + return err + } + for _, rule := range rules { + ps := strings.Split(rule, " ") + if len(ps) == 10 && + ps[0] == "-A" && ps[2] == "-s" && ps[4] == "-d" && ps[5] == "224.0.0.0/4" && + ps[6] == "-i" && ps[7] == ifName && ps[8] == "-j" && ps[9] == "ACCEPT" { + + if _, found := subnets[ps[3]]; !found { + if err := ipt.Delete("filter", "INPUT", ps[2:]...); err != nil { + return err + } + } + } + } + if len(addrs) == 0 { // all addresses gone: remove the interface + if err := ipt.Delete("filter", "INPUT", "-i", ifName, "-d", "224.0.0.0/4", "-j", "DROP"); err != nil { + return err + } if err := netlink.LinkDel(veth); err != nil { return err } @@ -202,3 +256,16 @@ func DetachContainer(ns netns.NsHandle, id, ifName string, cidrs []*net.IPNet) e return nil }) } + +func subnet(ipn *net.IPNet) string { + ones, _ := ipn.Mask.Size() + return fmt.Sprintf("%s/%d", ipn.IP.Mask(ipn.Mask).String(), ones) +} + +func subnets(addrs []netlink.Addr) map[string]struct{} { + subnets := make(map[string]struct{}) + for _, addr := range addrs { + subnets[subnet(addr.IPNet)] = struct{}{} + } + return subnets +}