diff --git a/sandbox/interface_linux.go b/sandbox/interface_linux.go index 0933985..73bd1af 100644 --- a/sandbox/interface_linux.go +++ b/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 { diff --git a/sandbox/options_linux.go b/sandbox/options_linux.go index 40199e0..4064848 100644 --- a/sandbox/options_linux.go +++ b/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 diff --git a/sandbox/sandbox.go b/sandbox/sandbox.go index 92bf4cc..f08e074 100644 --- a/sandbox/sandbox.go +++ b/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 diff --git a/sandbox/sandbox_linux_test.go b/sandbox/sandbox_linux_test.go index e47ea78..7f769c9 100644 --- a/sandbox/sandbox_linux_test.go +++ b/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 + intf3 := &nwIface{} + intf3.srcName = vethName4 + intf3.dstName = sboxIfaceName + intf3.master = "testbridge" - 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 - - 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") diff --git a/sandbox/sandbox_test.go b/sandbox/sandbox_test.go index 19e87ca..6dd4d5e 100644 --- a/sandbox/sandbox_test.go +++ b/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()