From aac063b4b69e675a391dd571a3096babe9e87520 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Mon, 15 Jun 2015 11:35:13 -0700 Subject: [PATCH] Add neighbor support to sandbox Add support to add/delete neighbor entries to the sandbox. Both L3 and L2(fdb) neighbor table additions are supported. Signed-off-by: Jana Radhakrishnan --- libnetwork/sandbox/interface_linux.go | 8 +- libnetwork/sandbox/namespace_linux.go | 12 +++ libnetwork/sandbox/neigh_linux.go | 138 ++++++++++++++++++++++++++ libnetwork/sandbox/options_linux.go | 20 ++++ libnetwork/sandbox/sandbox.go | 23 +++++ 5 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 libnetwork/sandbox/neigh_linux.go diff --git a/libnetwork/sandbox/interface_linux.go b/libnetwork/sandbox/interface_linux.go index 73bd1af773..a89b3e7824 100644 --- a/libnetwork/sandbox/interface_linux.go +++ b/libnetwork/sandbox/interface_linux.go @@ -153,14 +153,14 @@ func (i *nwIface) Remove() error { }) } -func (n *networkNamespace) findDstMaster(srcName string) string { +func (n *networkNamespace) findDst(srcName string, isBridge bool) string { n.Lock() defer n.Unlock() for _, i := range n.iFaces { // The master should match the srcname of the interface and the - // master interface should be of type bridge. - if i.SrcName() == srcName && i.Bridge() { + // master interface should be of type bridge, if searching for a bridge type + if i.SrcName() == srcName && (!isBridge || i.Bridge()) { return i.DstName() } } @@ -173,7 +173,7 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If i.processInterfaceOptions(options...) if i.master != "" { - i.dstMaster = n.findDstMaster(i.master) + i.dstMaster = n.findDst(i.master, true) if i.dstMaster == "" { return fmt.Errorf("could not find an appropriate master %q for %q", i.master, i.srcName) diff --git a/libnetwork/sandbox/namespace_linux.go b/libnetwork/sandbox/namespace_linux.go index 9d827f0b03..3ab011cd1c 100644 --- a/libnetwork/sandbox/namespace_linux.go +++ b/libnetwork/sandbox/namespace_linux.go @@ -37,6 +37,7 @@ type networkNamespace struct { gw net.IP gwv6 net.IP staticRoutes []*types.StaticRoute + neighbors []*neigh nextIfIndex int sync.Mutex } @@ -150,6 +151,10 @@ func (n *networkNamespace) InterfaceOptions() IfaceOptionSetter { return n } +func (n *networkNamespace) NeighborOptions() NeighborOptionSetter { + return n +} + func reexecCreateNamespace() { if len(os.Args) < 2 { log.Fatal("no namespace path provided") @@ -230,6 +235,13 @@ func loopbackUp() error { return netlink.LinkSetUp(iface) } +func (n *networkNamespace) InvokeFunc(f func()) error { + return nsInvoke(n.nsPath(), func(nsFD int) error { return nil }, func(callerFD int) error { + f() + return nil + }) +} + func nsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD int) error) error { runtime.LockOSThread() defer runtime.UnlockOSThread() diff --git a/libnetwork/sandbox/neigh_linux.go b/libnetwork/sandbox/neigh_linux.go new file mode 100644 index 0000000000..873f14f193 --- /dev/null +++ b/libnetwork/sandbox/neigh_linux.go @@ -0,0 +1,138 @@ +package sandbox + +import ( + "bytes" + "fmt" + "net" + + "github.com/vishvananda/netlink" +) + +// NeighOption is a function option type to set interface options +type NeighOption func(nh *neigh) + +type neigh struct { + dstIP net.IP + dstMac net.HardwareAddr + linkName string + linkDst string + family int +} + +func (n *networkNamespace) findNeighbor(dstIP net.IP, dstMac net.HardwareAddr) *neigh { + n.Lock() + defer n.Unlock() + + for _, nh := range n.neighbors { + if nh.dstIP.Equal(dstIP) && bytes.Equal(nh.dstMac, dstMac) { + return nh + } + } + + return nil +} + +func (n *networkNamespace) DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr) error { + nh := n.findNeighbor(dstIP, dstMac) + if nh == nil { + return fmt.Errorf("could not find the neighbor entry to delete") + } + + return nsInvoke(n.nsPath(), func(nsFD int) error { return nil }, func(callerFD int) error { + var iface netlink.Link + + if nh.linkDst != "" { + var err error + iface, err = netlink.LinkByName(nh.linkDst) + if err != nil { + return fmt.Errorf("could not find interface with destination name %s: %v", + nh.linkDst, err) + } + } + + nlnh := &netlink.Neigh{ + IP: dstIP, + State: netlink.NUD_PERMANENT, + Family: nh.family, + } + + if nlnh.Family > 0 { + nlnh.HardwareAddr = dstMac + nlnh.Flags = netlink.NTF_SELF + } + + if nh.linkDst != "" { + nlnh.LinkIndex = iface.Attrs().Index + } + + if err := netlink.NeighDel(nlnh); err != nil { + return fmt.Errorf("could not delete neighbor entry: %v", err) + } + + for i, nh := range n.neighbors { + if nh.dstIP.Equal(dstIP) && bytes.Equal(nh.dstMac, dstMac) { + n.neighbors = append(n.neighbors[:i], n.neighbors[i+1:]...) + } + } + + return nil + }) +} + +func (n *networkNamespace) AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, options ...NeighOption) error { + nh := n.findNeighbor(dstIP, dstMac) + if nh != nil { + // If it exists silently return + return nil + } + + nh = &neigh{ + dstIP: dstIP, + dstMac: dstMac, + } + + nh.processNeighOptions(options...) + + if nh.linkName != "" { + nh.linkDst = n.findDst(nh.linkName, false) + if nh.linkDst == "" { + return fmt.Errorf("could not find the interface with name %s", nh.linkName) + } + } + + return nsInvoke(n.nsPath(), func(nsFD int) error { return nil }, func(callerFD int) error { + var iface netlink.Link + + if nh.linkDst != "" { + var err error + iface, err = netlink.LinkByName(nh.linkDst) + if err != nil { + return fmt.Errorf("could not find interface with destination name %s: %v", + nh.linkDst, err) + } + } + + nlnh := &netlink.Neigh{ + IP: dstIP, + HardwareAddr: dstMac, + State: netlink.NUD_PERMANENT, + Family: nh.family, + } + + if nlnh.Family > 0 { + nlnh.Flags = netlink.NTF_SELF + } + + if nh.linkDst != "" { + nlnh.LinkIndex = iface.Attrs().Index + } + + if err := netlink.NeighSet(nlnh); err != nil { + return fmt.Errorf("could not add neighbor entry: %v", err) + } + + n.neighbors = append(n.neighbors, nh) + + return nil + }) +} diff --git a/libnetwork/sandbox/options_linux.go b/libnetwork/sandbox/options_linux.go index 40648485c6..e34699c790 100644 --- a/libnetwork/sandbox/options_linux.go +++ b/libnetwork/sandbox/options_linux.go @@ -2,6 +2,26 @@ package sandbox import "net" +func (nh *neigh) processNeighOptions(options ...NeighOption) { + for _, opt := range options { + if opt != nil { + opt(nh) + } + } +} + +func (n *networkNamespace) LinkName(name string) NeighOption { + return func(nh *neigh) { + nh.linkName = name + } +} + +func (n *networkNamespace) Family(family int) NeighOption { + return func(nh *neigh) { + nh.family = family + } +} + func (i *nwIface) processInterfaceOptions(options ...IfaceOption) { for _, opt := range options { if opt != nil { diff --git a/libnetwork/sandbox/sandbox.go b/libnetwork/sandbox/sandbox.go index f08e074668..13d940d7aa 100644 --- a/libnetwork/sandbox/sandbox.go +++ b/libnetwork/sandbox/sandbox.go @@ -37,9 +37,21 @@ type Sandbox interface { // Remove a static route from the sandbox. RemoveStaticRoute(*types.StaticRoute) error + // AddNeighbor adds a neighbor entry into the sandbox. + AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, option ...NeighOption) error + + // DeleteNeighbor deletes neighbor entry from the sandbox. + DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr) error + + // Returns an interface with methods to set neighbor options. + NeighborOptions() NeighborOptionSetter + // Returns an interface with methods to set interface options. InterfaceOptions() IfaceOptionSetter + //Invoke + InvokeFunc(func()) error + // Returns an interface with methods to get sandbox state. Info() Info @@ -47,6 +59,17 @@ type Sandbox interface { Destroy() error } +// NeighborOptionSetter interfaces defines the option setter methods for interface options +type NeighborOptionSetter interface { + // LinkName returns an option setter to set the srcName of the link that should + // be used in the neighbor entry + LinkName(string) NeighOption + + // Family returns an option setter to set the address family for the neighbor + // entry. eg. AF_BRIDGE + Family(int) NeighOption +} + // IfaceOptionSetter interface defines the option setter methods for interface options. type IfaceOptionSetter interface { // Bridge returns an option setter to set if the interface is a bridge.