1
0
Fork 0
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:
Alessandro Boch 2015-04-24 15:13:44 -07:00
parent 5c442d3c76
commit 35693a1a47
9 changed files with 285 additions and 8 deletions

View file

@ -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

View file

@ -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)
}
}

View file

@ -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

View file

@ -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

View file

@ -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())
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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)
}
}