Ver código fonte

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 <mrjana@docker.com>
Jana Radhakrishnan 10 anos atrás
pai
commit
aac063b4b6

+ 4 - 4
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()
 	n.Lock()
 	defer n.Unlock()
 	defer n.Unlock()
 
 
 	for _, i := range n.iFaces {
 	for _, i := range n.iFaces {
 		// The master should match the srcname of the interface and the
 		// 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()
 			return i.DstName()
 		}
 		}
 	}
 	}
@@ -173,7 +173,7 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
 	i.processInterfaceOptions(options...)
 	i.processInterfaceOptions(options...)
 
 
 	if i.master != "" {
 	if i.master != "" {
-		i.dstMaster = n.findDstMaster(i.master)
+		i.dstMaster = n.findDst(i.master, true)
 		if i.dstMaster == "" {
 		if i.dstMaster == "" {
 			return fmt.Errorf("could not find an appropriate master %q for %q",
 			return fmt.Errorf("could not find an appropriate master %q for %q",
 				i.master, i.srcName)
 				i.master, i.srcName)

+ 12 - 0
libnetwork/sandbox/namespace_linux.go

@@ -37,6 +37,7 @@ type networkNamespace struct {
 	gw           net.IP
 	gw           net.IP
 	gwv6         net.IP
 	gwv6         net.IP
 	staticRoutes []*types.StaticRoute
 	staticRoutes []*types.StaticRoute
+	neighbors    []*neigh
 	nextIfIndex  int
 	nextIfIndex  int
 	sync.Mutex
 	sync.Mutex
 }
 }
@@ -150,6 +151,10 @@ func (n *networkNamespace) InterfaceOptions() IfaceOptionSetter {
 	return n
 	return n
 }
 }
 
 
+func (n *networkNamespace) NeighborOptions() NeighborOptionSetter {
+	return n
+}
+
 func reexecCreateNamespace() {
 func reexecCreateNamespace() {
 	if len(os.Args) < 2 {
 	if len(os.Args) < 2 {
 		log.Fatal("no namespace path provided")
 		log.Fatal("no namespace path provided")
@@ -230,6 +235,13 @@ func loopbackUp() error {
 	return netlink.LinkSetUp(iface)
 	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 {
 func nsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD int) error) error {
 	runtime.LockOSThread()
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 	defer runtime.UnlockOSThread()

+ 138 - 0
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
+	})
+}

+ 20 - 0
libnetwork/sandbox/options_linux.go

@@ -2,6 +2,26 @@ package sandbox
 
 
 import "net"
 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) {
 func (i *nwIface) processInterfaceOptions(options ...IfaceOption) {
 	for _, opt := range options {
 	for _, opt := range options {
 		if opt != nil {
 		if opt != nil {

+ 23 - 0
libnetwork/sandbox/sandbox.go

@@ -37,9 +37,21 @@ type Sandbox interface {
 	// Remove a static route from the sandbox.
 	// Remove a static route from the sandbox.
 	RemoveStaticRoute(*types.StaticRoute) error
 	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.
 	// Returns an interface with methods to set interface options.
 	InterfaceOptions() IfaceOptionSetter
 	InterfaceOptions() IfaceOptionSetter
 
 
+	//Invoke
+	InvokeFunc(func()) error
+
 	// Returns an interface with methods to get sandbox state.
 	// Returns an interface with methods to get sandbox state.
 	Info() Info
 	Info() Info
 
 
@@ -47,6 +59,17 @@ type Sandbox interface {
 	Destroy() error
 	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.
 // IfaceOptionSetter interface defines the option setter methods for interface options.
 type IfaceOptionSetter interface {
 type IfaceOptionSetter interface {
 	// Bridge returns an option setter to set if the interface is a bridge.
 	// Bridge returns an option setter to set if the interface is a bridge.