Kaynağa Gözat

Add support to add bridge to the sandbox.

Added support to add a bridge the same way as any other
interface into the namespace. The only difference is linux
does not support creating the bridge in one namespace and
moving it into another namespace. So for a bridge the sandbox
code also does the creation of the bridge inside the sandbox.
Also added an optional argument to interface which can now
select one of the already existing interfaces as it's master.
For this option to succeed the master interface should be of type
bridge.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
Jana Radhakrishnan 10 yıl önce
ebeveyn
işleme
4ceec05f1b

+ 29 - 0
libnetwork/sandbox/interface_linux.go

@@ -15,9 +15,12 @@ type IfaceOption func(i *nwIface)
 type nwIface struct {
 	srcName     string
 	dstName     string
+	master      string
+	dstMaster   string
 	address     *net.IPNet
 	addressIPv6 *net.IPNet
 	routes      []*net.IPNet
+	bridge      bool
 	ns          *networkNamespace
 	sync.Mutex
 }
@@ -169,6 +172,14 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
 	i := &nwIface{srcName: srcName, dstName: dstPrefix, ns: n}
 	i.processInterfaceOptions(options...)
 
+	if i.master != "" {
+		i.dstMaster = n.findDstMaster(i.master)
+		if i.dstMaster == "" {
+			return fmt.Errorf("could not find an appropriate master %q for %q",
+				i.master, i.srcName)
+		}
+	}
+
 	n.Lock()
 	i.dstName = fmt.Sprintf("%s%d", i.dstName, n.nextIfIndex)
 	n.nextIfIndex++
@@ -176,6 +187,12 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
 	n.Unlock()
 
 	return nsInvoke(path, func(nsFD int) error {
+		// If it is a bridge interface we have to create the bridge inside
+		// the namespace so don't try to lookup the interface using srcName
+		if i.bridge {
+			return nil
+		}
+
 		// Find the network interface identified by the SrcName attribute.
 		iface, err := netlink.LinkByName(i.srcName)
 		if err != nil {
@@ -189,6 +206,18 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
 
 		return nil
 	}, func(callerFD int) error {
+		if i.bridge {
+			link := &netlink.Bridge{
+				LinkAttrs: netlink.LinkAttrs{
+					Name: i.srcName,
+				},
+			}
+
+			if err := netlink.LinkAdd(link); err != nil {
+				return fmt.Errorf("failed to create bridge %q: %v", i.srcName, err)
+			}
+		}
+
 		// Find the network interface identified by the SrcName attribute.
 		iface, err := netlink.LinkByName(i.srcName)
 		if err != nil {

+ 12 - 0
libnetwork/sandbox/options_linux.go

@@ -10,6 +10,18 @@ func (i *nwIface) processInterfaceOptions(options ...IfaceOption) {
 	}
 }
 
+func (n *networkNamespace) Bridge(isBridge bool) IfaceOption {
+	return func(i *nwIface) {
+		i.bridge = isBridge
+	}
+}
+
+func (n *networkNamespace) Master(name string) IfaceOption {
+	return func(i *nwIface) {
+		i.master = name
+	}
+}
+
 func (n *networkNamespace) Address(addr *net.IPNet) IfaceOption {
 	return func(i *nwIface) {
 		i.address = addr

+ 14 - 0
libnetwork/sandbox/sandbox.go

@@ -49,12 +49,20 @@ type Sandbox interface {
 
 // 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.
+	Bridge(bool) IfaceOption
+
 	// Address returns an option setter to set IPv4 address.
 	Address(*net.IPNet) IfaceOption
 
 	// Address returns an option setter to set IPv6 address.
 	AddressIPv6(*net.IPNet) IfaceOption
 
+	// Master returns an option setter to set the master interface if any for this
+	// interface. The master interface name should refer to the srcname of a
+	// previously added interface of type bridge.
+	Master(string) IfaceOption
+
 	// Address returns an option setter to set interface routes.
 	Routes([]*net.IPNet) IfaceOption
 }
@@ -106,6 +114,12 @@ type Interface interface {
 	// IP routes for the interface.
 	Routes() []*net.IPNet
 
+	// Bridge returns true if the interface is a bridge
+	Bridge() bool
+
+	// Master returns the srcname of the master interface for this interface.
+	Master() string
+
 	// Remove an interface from the sandbox by renaming to original name
 	// and moving it out of the sandbox.
 	Remove() error

+ 10 - 20
libnetwork/sandbox/sandbox_linux_test.go

@@ -76,6 +76,11 @@ func newInfo(t *testing.T) (Sandbox, error) {
 
 	intf1.routes = []*net.IPNet{route}
 
+	intf2 := &nwIface{}
+	intf2.srcName = "testbridge"
+	intf2.dstName = sboxIfaceName
+	intf2.bridge = true
+
 	veth = &netlink.Veth{
 		LinkAttrs: netlink.LinkAttrs{Name: vethName3, TxQLen: 0},
 		PeerName:  vethName4}
@@ -84,27 +89,12 @@ func newInfo(t *testing.T) (Sandbox, error) {
 		return nil, err
 	}
 
-	intf2 := &nwIface{}
-	intf2.srcName = vethName4
-	intf2.dstName = sboxIfaceName
-
-	ip4, addr, err = net.ParseCIDR("192.168.2.100/24")
-	if err != nil {
-		return nil, err
-	}
-	intf2.address = addr
-	intf2.address.IP = ip4
-
-	// ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
-	ip6, addrv6, err = net.ParseCIDR("fe80::3/64")
-
-	if err != nil {
-		return nil, err
-	}
-	intf2.addressIPv6 = addrv6
-	intf2.addressIPv6.IP = ip6
+	intf3 := &nwIface{}
+	intf3.srcName = vethName4
+	intf3.dstName = sboxIfaceName
+	intf3.master = "testbridge"
 
-	info := &networkNamespace{iFaces: []*nwIface{intf1, intf2}}
+	info := &networkNamespace{iFaces: []*nwIface{intf1, intf2, intf3}}
 
 	info.gw = net.ParseIP("192.168.1.1")
 	// sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")

+ 7 - 4
libnetwork/sandbox/sandbox_test.go

@@ -41,6 +41,7 @@ func TestSandboxCreate(t *testing.T) {
 
 	for _, i := range tbox.Info().Interfaces() {
 		err = s.AddInterface(i.SrcName(), i.DstName(),
+			tbox.InterfaceOptions().Bridge(i.Bridge()),
 			tbox.InterfaceOptions().Address(i.Address()),
 			tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6()))
 		if err != nil {
@@ -61,7 +62,7 @@ func TestSandboxCreate(t *testing.T) {
 	}
 	runtime.LockOSThread()
 
-	verifySandbox(t, s, []string{"0", "1"})
+	verifySandbox(t, s, []string{"0", "1", "2"})
 	runtime.LockOSThread()
 
 	s.Destroy()
@@ -134,6 +135,7 @@ func TestAddRemoveInterface(t *testing.T) {
 
 	for _, i := range tbox.Info().Interfaces() {
 		err = s.AddInterface(i.SrcName(), i.DstName(),
+			tbox.InterfaceOptions().Bridge(i.Bridge()),
 			tbox.InterfaceOptions().Address(i.Address()),
 			tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6()))
 		if err != nil {
@@ -142,7 +144,7 @@ func TestAddRemoveInterface(t *testing.T) {
 		runtime.LockOSThread()
 	}
 
-	verifySandbox(t, s, []string{"0", "1"})
+	verifySandbox(t, s, []string{"0", "1", "2"})
 	runtime.LockOSThread()
 
 	interfaces := s.Info().Interfaces()
@@ -151,18 +153,19 @@ func TestAddRemoveInterface(t *testing.T) {
 	}
 	runtime.LockOSThread()
 
-	verifySandbox(t, s, []string{"1"})
+	verifySandbox(t, s, []string{"1", "2"})
 	runtime.LockOSThread()
 
 	i := tbox.Info().Interfaces()[0]
 	if err := s.AddInterface(i.SrcName(), i.DstName(),
+		tbox.InterfaceOptions().Bridge(i.Bridge()),
 		tbox.InterfaceOptions().Address(i.Address()),
 		tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6())); err != nil {
 		t.Fatalf("Failed to add interfaces to sandbox: %v", err)
 	}
 	runtime.LockOSThread()
 
-	verifySandbox(t, s, []string{"1", "2"})
+	verifySandbox(t, s, []string{"1", "2", "3"})
 	runtime.LockOSThread()
 
 	s.Destroy()