mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Issue #88: Handle default v4/v6 gw setting
- Basically this is porting docker PR #9381 to libnetwork - Added a Config.Validate() method where to consolidate a priori validation of bridge configuration - Have bridgeInterface store the current v4/v6 default gateways - Introduced two setupStep functions to set the requested def gateways Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
parent
5c442d3c76
commit
35693a1a47
9 changed files with 285 additions and 8 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue