diff --git a/drivers/bridge/bridge.go b/drivers/bridge/bridge.go index b2cac90..f72933c 100644 --- a/drivers/bridge/bridge.go +++ b/drivers/bridge/bridge.go @@ -40,6 +40,8 @@ type Configuration struct { EnableIPForwarding bool AllowNonDefaultBridge bool Mtu int + DefaultGatewayIPv4 net.IP + DefaultGatewayIPv6 net.IP } // EndpointConfiguration represents the user specified configuration for the sandbox endpoint @@ -76,6 +78,46 @@ func New() (string, driverapi.Driver) { return networkType, &driver{} } +// Validate performs a static validation on the configuration parameters. +// Whatever can be assessed a priori before attempting any programming. +func (c *Configuration) Validate() error { + if c.Mtu < 0 { + return ErrInvalidMtu + } + + // If bridge v4 subnet is specified + if c.AddressIPv4 != nil { + // If Container restricted subnet is specified, it must be a subset of bridge subnet + if c.FixedCIDR != nil { + // Check Network address + if !c.AddressIPv4.Contains(c.FixedCIDR.IP) { + return ErrInvalidContainerSubnet + } + // Check it is effectively a subset + brNetLen, _ := c.AddressIPv4.Mask.Size() + cnNetLen, _ := c.FixedCIDR.Mask.Size() + if brNetLen > cnNetLen { + return ErrInvalidContainerSubnet + } + } + // If default gw is specified, it must be part of bridge subnet + if c.DefaultGatewayIPv4 != nil { + if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) { + return ErrInvalidGateway + } + } + } + + // If default v6 gw is specified, FixedCIDRv6 must be specified and gw must belong to FixedCIDRv6 subnet + if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil { + if c.FixedCIDRv6 == nil || !c.FixedCIDRv6.Contains(c.DefaultGatewayIPv6) { + return ErrInvalidGateway + } + } + + return nil +} + func (n *bridgeNetwork) getEndpoint(eid types.UUID) (string, *bridgeEndpoint, error) { n.Lock() defer n.Unlock() @@ -114,7 +156,12 @@ func (d *driver) Config(option interface{}) error { config = opt } + if err := config.Validate(); err != nil { + return err + } + d.config = config + return nil } @@ -192,6 +239,12 @@ func (d *driver) CreateNetwork(id types.UUID, option interface{}) error { // Setup IP forwarding. {config.EnableIPForwarding, setupIPForwarding}, + + // Setup DefaultGatewayIPv4 + {config.DefaultGatewayIPv4 != nil, setupGatewayIPv4}, + + // Setup DefaultGatewayIPv6 + {config.DefaultGatewayIPv6 != nil, setupGatewayIPv6}, } { if step.Condition { bridgeSetup.queueStep(step.Fn) @@ -295,6 +348,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i // Try to convert the options to endpoint configuration epConfig, err := parseEndpointOptions(epOptions) if err != nil { + n.Unlock() return nil, err } @@ -408,10 +462,12 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i // Generate the sandbox info to return sinfo := &sandbox.Info{Interfaces: []*sandbox.Interface{intf}} - sinfo.Gateway = n.bridge.bridgeIPv4.IP + + // Set the default gateway(s) for the sandbox + sinfo.Gateway = n.bridge.gatewayIPv4 if config.EnableIPv6 { intf.AddressIPv6 = ipv6Addr - sinfo.GatewayIPv6 = n.bridge.bridgeIPv6.IP + sinfo.GatewayIPv6 = n.bridge.gatewayIPv6 } return sinfo, nil diff --git a/drivers/bridge/bridge_test.go b/drivers/bridge/bridge_test.go index 8eac37a..787ef09 100644 --- a/drivers/bridge/bridge_test.go +++ b/drivers/bridge/bridge_test.go @@ -91,3 +91,132 @@ func TestCreateLinkWithOptions(t *testing.T) { t.Fatalf("Failed to parse and program endpoint configuration") } } + +func TestValidateConfig(t *testing.T) { + + // Test mtu + c := Configuration{Mtu: -2} + err := c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid MTU number") + } + + c.Mtu = 9000 + err = c.Validate() + if err != nil { + t.Fatalf("unexpected validation error on MTU number") + } + + // Bridge network + _, network, _ := net.ParseCIDR("172.28.0.0/16") + + // Test FixedCIDR + _, containerSubnet, _ := net.ParseCIDR("172.27.0.0/16") + c = Configuration{ + AddressIPv4: network, + FixedCIDR: containerSubnet, + } + + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid FixedCIDR network") + } + + _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/16") + c.FixedCIDR = containerSubnet + err = c.Validate() + if err != nil { + t.Fatalf("Unexpected validation error on FixedCIDR network") + } + + _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/15") + c.FixedCIDR = containerSubnet + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid FixedCIDR network") + } + + _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/17") + c.FixedCIDR = containerSubnet + err = c.Validate() + if err != nil { + t.Fatalf("Unexpected validation error on FixedCIDR network") + } + + // Test v4 gw + c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234") + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid default gateway") + } + + c.DefaultGatewayIPv4 = net.ParseIP("172.28.30.234") + err = c.Validate() + if err != nil { + t.Fatalf("Unexpected validation error on default gateway") + } + + // Test v6 gw + _, containerSubnet, _ = net.ParseCIDR("2001:1234:ae:b004::/64") + c = Configuration{ + EnableIPv6: true, + FixedCIDRv6: containerSubnet, + DefaultGatewayIPv6: net.ParseIP("2001:1234:ac:b004::bad:a55"), + } + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid v6 default gateway") + } + + c.DefaultGatewayIPv6 = net.ParseIP("2001:1234:ae:b004::bad:a55") + err = c.Validate() + if err != nil { + t.Fatalf("Unexpected validation error on v6 default gateway") + } + + c.FixedCIDRv6 = nil + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid v6 default gateway") + } +} + +func TestSetDefaultGw(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + _, d := New() + + _, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80") + gw4 := bridgeNetworks[0].IP.To4() + gw4[3] = 254 + gw6 := net.ParseIP("2001:db8:ea9:9abc:b0c4::254") + + config := &Configuration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true, + FixedCIDRv6: subnetv6, + DefaultGatewayIPv4: gw4, + DefaultGatewayIPv6: gw6, + } + + if err := d.Config(config); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + err := d.CreateNetwork("dummy", nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + sinfo, err := d.CreateEndpoint("dummy", "ep", "sb2", nil) + if err != nil { + t.Fatalf("Failed to create endpoint: %v", err) + } + + if !gw4.Equal(sinfo.Gateway) { + t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, sinfo.Gateway) + } + + if !gw6.Equal(sinfo.GatewayIPv6) { + t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, sinfo.GatewayIPv6) + } +} diff --git a/drivers/bridge/error.go b/drivers/bridge/error.go index 6edec9a..5c074e3 100644 --- a/drivers/bridge/error.go +++ b/drivers/bridge/error.go @@ -24,6 +24,15 @@ var ( // ErrNoIPAddr error is returned when bridge has no IPv4 address configured. ErrNoIPAddr = errors.New("bridge has no IPv4 address configured") + + // ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid. + ErrInvalidGateway = errors.New("default gateway ip must be part of the network") + + // ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid. + ErrInvalidContainerSubnet = errors.New("container subnet must be a subset of bridge network") + + // ErrInvalidMtu is returned when the user provided MTU is not valid + ErrInvalidMtu = errors.New("invalid MTU number") ) // ActiveEndpointsError is returned when there are diff --git a/drivers/bridge/interface.go b/drivers/bridge/interface.go index 013d548..b4283c3 100644 --- a/drivers/bridge/interface.go +++ b/drivers/bridge/interface.go @@ -14,9 +14,11 @@ const ( // Interface models the bridge network device. type bridgeInterface struct { - Link netlink.Link - bridgeIPv4 *net.IPNet - bridgeIPv6 *net.IPNet + Link netlink.Link + bridgeIPv4 *net.IPNet + bridgeIPv6 *net.IPNet + gatewayIPv4 net.IP + gatewayIPv6 net.IP } // newInterface creates a new bridge interface structure. It attempts to find diff --git a/drivers/bridge/network_test.go b/drivers/bridge/network_test.go index 96662d6..b7d1111 100644 --- a/drivers/bridge/network_test.go +++ b/drivers/bridge/network_test.go @@ -17,7 +17,8 @@ func TestLinkCreate(t *testing.T) { config := &Configuration{ BridgeName: DefaultBridgeName, Mtu: mtu, - EnableIPv6: true} + EnableIPv6: true, + } if err := d.Config(config); err != nil { t.Fatalf("Failed to setup driver config: %v", err) } @@ -97,12 +98,12 @@ func TestLinkCreate(t *testing.T) { t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String()) } - if sinfo.Gateway.String() != n.bridge.bridgeIPv4.IP.String() { + if !sinfo.Gateway.Equal(n.bridge.bridgeIPv4.IP) { t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(), sinfo.Gateway.String()) } - if sinfo.GatewayIPv6.String() != n.bridge.bridgeIPv6.IP.String() { + if !sinfo.GatewayIPv6.Equal(n.bridge.bridgeIPv6.IP) { t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(), sinfo.GatewayIPv6.String()) } diff --git a/drivers/bridge/setup_ipv4.go b/drivers/bridge/setup_ipv4.go index 20c0427..01137fb 100644 --- a/drivers/bridge/setup_ipv4.go +++ b/drivers/bridge/setup_ipv4.go @@ -51,7 +51,9 @@ func setupBridgeIPv4(config *Configuration, i *bridgeInterface) error { return &IPv4AddrAddError{ip: bridgeIPv4, err: err} } + // Store bridge network and default gateway i.bridgeIPv4 = bridgeIPv4 + i.gatewayIPv4 = i.bridgeIPv4.IP return nil } @@ -81,3 +83,17 @@ func electBridgeIPv4(config *Configuration) (*net.IPNet, error) { return nil, IPv4AddrRangeError(config.BridgeName) } + +func setupGatewayIPv4(config *Configuration, i *bridgeInterface) error { + if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) { + return ErrInvalidGateway + } + if _, err := ipAllocator.RequestIP(i.bridgeIPv4, config.DefaultGatewayIPv4); err != nil { + return err + } + + // Store requested default gateway + i.gatewayIPv4 = config.DefaultGatewayIPv4 + + return nil +} diff --git a/drivers/bridge/setup_ipv4_test.go b/drivers/bridge/setup_ipv4_test.go index 67f44af..9cb65fb 100644 --- a/drivers/bridge/setup_ipv4_test.go +++ b/drivers/bridge/setup_ipv4_test.go @@ -76,3 +76,25 @@ func TestSetupBridgeIPv4Auto(t *testing.T) { t.Fatalf("Bridge device does not have the automatic IPv4 address %v", bridgeNetworks[0].String()) } } + +func TestSetupGatewayIPv4(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + ip, nw, _ := net.ParseCIDR("192.168.0.24/16") + nw.IP = ip + gw := net.ParseIP("192.168.0.254") + + config := &Configuration{ + BridgeName: DefaultBridgeName, + DefaultGatewayIPv4: gw} + + br := &bridgeInterface{bridgeIPv4: nw} + + if err := setupGatewayIPv4(config, br); err != nil { + t.Fatalf("Set Default Gateway failed: %v", err) + } + + if !gw.Equal(br.gatewayIPv4) { + t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv4) + } +} diff --git a/drivers/bridge/setup_ipv6.go b/drivers/bridge/setup_ipv6.go index 07c5cd5..ca284a7 100644 --- a/drivers/bridge/setup_ipv6.go +++ b/drivers/bridge/setup_ipv6.go @@ -33,7 +33,26 @@ func setupBridgeIPv6(config *Configuration, i *bridgeInterface) error { return &IPv6AddrAddError{ip: bridgeIPv6, err: err} } + // Store bridge network and default gateway i.bridgeIPv6 = bridgeIPv6 + i.gatewayIPv6 = i.bridgeIPv6.IP + + return nil +} + +func setupGatewayIPv6(config *Configuration, i *bridgeInterface) error { + if config.FixedCIDRv6 == nil { + return ErrInvalidContainerSubnet + } + if !config.FixedCIDRv6.Contains(config.DefaultGatewayIPv6) { + return ErrInvalidGateway + } + if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, config.DefaultGatewayIPv6); err != nil { + return err + } + + // Store requested default gateway + i.gatewayIPv6 = config.DefaultGatewayIPv6 return nil } diff --git a/drivers/bridge/setup_ipv6_test.go b/drivers/bridge/setup_ipv6_test.go index a13c5c0..99d506f 100644 --- a/drivers/bridge/setup_ipv6_test.go +++ b/drivers/bridge/setup_ipv6_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io/ioutil" + "net" "testing" "github.com/docker/libnetwork/netutils" @@ -45,3 +46,25 @@ func TestSetupIPv6(t *testing.T) { } } + +func TestSetupGatewayIPv6(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + _, nw, _ := net.ParseCIDR("2001:db8:ea9:9abc:ffff::/80") + gw := net.ParseIP("2001:db8:ea9:9abc:ffff::254") + + config := &Configuration{ + BridgeName: DefaultBridgeName, + FixedCIDRv6: nw, + DefaultGatewayIPv6: gw} + + br := &bridgeInterface{} + + if err := setupGatewayIPv6(config, br); err != nil { + t.Fatalf("Set Default Gateway failed: %v", err) + } + + if !gw.Equal(br.gatewayIPv6) { + t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv6) + } +}