mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
Use default ULA prefix if fixed-cidr-v6 is not specified
Use the same logic to generate IPAMConf for IPv6 as for IPv4.
- When no fixed-cidr-v6 is specified, rather than error out, use
the default address pools (as for an IPv4 default bridge with no
fixed-cidr, and as for user-defined networks).
- Add daemon option --bip6, similar to --bip.
- Necessary because it's the only way to override an old address
on docker0 (daemon-managed default bridge), as illustrated by
test cases.
- For a user-managed default bridge (--bridge), use IPv6 addresses
on the user's bridge to determine the pool, sub-pool and gateway.
Following the same rules as IPv4.
- Don't set up IPv6 IPAMConf if IPv6 is not enabled.
Signed-off-by: Rob Murray <rob.murray@docker.com>
This commit is contained in:
@@ -31,7 +31,8 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
|
||||
flags.BoolVar(&conf.BridgeConfig.DisableFilterForwardDrop, "ip-forward-no-drop", false, "Do not set the filter-FORWARD policy to DROP when enabling IP forwarding")
|
||||
flags.BoolVar(&conf.BridgeConfig.EnableIPMasq, "ip-masq", true, "Enable IP masquerading")
|
||||
flags.BoolVar(&conf.BridgeConfig.EnableIPv6, "ipv6", false, "Enable IPv6 networking")
|
||||
flags.StringVar(&conf.BridgeConfig.IP, "bip", "", "Specify network bridge IP")
|
||||
flags.StringVar(&conf.BridgeConfig.IP, "bip", "", "Specify default-bridge IPv4 network")
|
||||
flags.StringVar(&conf.BridgeConfig.IP6, "bip6", "", "Specify default-bridge IPv6 network")
|
||||
flags.StringVarP(&conf.BridgeConfig.Iface, "bridge", "b", "", "Attach containers to a network bridge")
|
||||
flags.StringVar(&conf.BridgeConfig.FixedCIDR, "fixed-cidr", "", "IPv4 subnet for fixed IPs")
|
||||
flags.StringVar(&conf.BridgeConfig.FixedCIDRv6, "fixed-cidr-v6", "", "IPv6 subnet for fixed IPs")
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestLoadDaemonCliConfigWithDaemonFlags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadDaemonConfigWithNetwork(t *testing.T) {
|
||||
content := `{"bip": "127.0.0.2", "ip": "127.0.0.1"}`
|
||||
content := `{"bip": "127.0.0.2/8", "bip6": "fd98:e5f2:e637::1/64", "ip": "127.0.0.1"}`
|
||||
tempFile := fs.NewFile(t, "config", fs.WithContent(content))
|
||||
defer tempFile.Remove()
|
||||
|
||||
@@ -42,8 +42,9 @@ func TestLoadDaemonConfigWithNetwork(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, loadedConfig != nil)
|
||||
|
||||
assert.Check(t, is.Equal("127.0.0.2", loadedConfig.IP))
|
||||
assert.Check(t, is.Equal("127.0.0.1", loadedConfig.DefaultIP.String()))
|
||||
assert.Check(t, is.Equal(loadedConfig.IP, "127.0.0.2/8"))
|
||||
assert.Check(t, is.Equal(loadedConfig.IP6, "fd98:e5f2:e637::1/64"))
|
||||
assert.Check(t, is.Equal(loadedConfig.DefaultIP.String(), "127.0.0.1"))
|
||||
}
|
||||
|
||||
func TestLoadDaemonConfigWithMapOptions(t *testing.T) {
|
||||
|
||||
@@ -60,6 +60,7 @@ type DefaultBridgeConfig struct {
|
||||
MTU int `json:"mtu,omitempty"`
|
||||
DefaultIP net.IP `json:"ip,omitempty"`
|
||||
IP string `json:"bip,omitempty"`
|
||||
IP6 string `json:"bip6,omitempty"`
|
||||
DefaultGatewayIPv4 net.IP `json:"default-gateway,omitempty"`
|
||||
DefaultGatewayIPv6 net.IP `json:"default-gateway-v6,omitempty"`
|
||||
InterContainerCommunication bool `json:"icc,omitempty"`
|
||||
|
||||
@@ -737,6 +737,9 @@ func verifyDaemonSettings(conf *config.Config) error {
|
||||
if conf.BridgeConfig.Iface != "" && conf.BridgeConfig.IP != "" {
|
||||
return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one")
|
||||
}
|
||||
if conf.BridgeConfig.Iface != "" && conf.BridgeConfig.IP6 != "" {
|
||||
return fmt.Errorf("You specified -b & --bip6, mutually exclusive options. Please specify only one")
|
||||
}
|
||||
if !conf.BridgeConfig.InterContainerCommunication {
|
||||
if !conf.BridgeConfig.EnableIPTables {
|
||||
return fmt.Errorf("You specified --iptables=false with --icc=false. ICC=false uses iptables to function. Please set --icc or --iptables to true")
|
||||
@@ -926,6 +929,47 @@ func driverOptions(config *config.Config) nwconfig.Option {
|
||||
})
|
||||
}
|
||||
|
||||
type defBrOptsV4 struct {
|
||||
cfg config.BridgeConfig
|
||||
}
|
||||
|
||||
func (o defBrOptsV4) nlFamily() int {
|
||||
return netlink.FAMILY_V4
|
||||
}
|
||||
func (o defBrOptsV4) fixedCIDR() (fCIDR, optName string) {
|
||||
return o.cfg.FixedCIDR, "fixed-cidr"
|
||||
}
|
||||
func (o defBrOptsV4) bip() (bip, optName string) {
|
||||
return o.cfg.IP, "bip"
|
||||
}
|
||||
func (o defBrOptsV4) defGw() (gw net.IP, optName, auxAddrLabel string) {
|
||||
return o.cfg.DefaultGatewayIPv4, "default-gateway", "DefaultGatewayIPv4"
|
||||
}
|
||||
|
||||
type defBrOptsV6 struct {
|
||||
cfg config.BridgeConfig
|
||||
}
|
||||
|
||||
func (o defBrOptsV6) nlFamily() int {
|
||||
return netlink.FAMILY_V6
|
||||
}
|
||||
func (o defBrOptsV6) fixedCIDR() (fCIDR, optName string) {
|
||||
return o.cfg.FixedCIDRv6, "fixed-cidr-v6"
|
||||
}
|
||||
func (o defBrOptsV6) bip() (bip, optName string) {
|
||||
return o.cfg.IP6, "bip6"
|
||||
}
|
||||
func (o defBrOptsV6) defGw() (gw net.IP, optName, auxAddrLabel string) {
|
||||
return o.cfg.DefaultGatewayIPv6, "default-gateway-v6", "DefaultGatewayIPv6"
|
||||
}
|
||||
|
||||
type defBrOpts interface {
|
||||
nlFamily() int
|
||||
fixedCIDR() (fCIDR, optName string)
|
||||
bip() (bip, optName string)
|
||||
defGw() (gw net.IP, optName, auxAddrLabel string)
|
||||
}
|
||||
|
||||
func initBridgeDriver(controller *libnetwork.Controller, cfg config.BridgeConfig) error {
|
||||
bridgeName, userManagedBridge := getDefaultBridgeName(cfg)
|
||||
netOption := map[string]string{
|
||||
@@ -940,70 +984,43 @@ func initBridgeDriver(controller *libnetwork.Controller, cfg config.BridgeConfig
|
||||
netOption[bridge.DefaultBindingIP] = cfg.DefaultIP.String()
|
||||
}
|
||||
|
||||
ipamV4Conf, err := getDefaultBridgeIPAMConf(bridgeName, userManagedBridge, cfg, netlink.FAMILY_V4)
|
||||
ipamV4Conf, err := getDefaultBridgeIPAMConf(bridgeName, userManagedBridge, defBrOptsV4{cfg})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
deferIPv6Alloc bool
|
||||
ipamV6Conf *libnetwork.IpamConf
|
||||
)
|
||||
|
||||
if cfg.EnableIPv6 && cfg.FixedCIDRv6 == "" {
|
||||
return errdefs.InvalidParameter(errors.New("IPv6 is enabled for the default bridge, but no subnet is configured. Specify an IPv6 subnet using --fixed-cidr-v6"))
|
||||
} else if cfg.FixedCIDRv6 != "" {
|
||||
_, fCIDRv6, err := net.ParseCIDR(cfg.FixedCIDRv6)
|
||||
var deferIPv6Alloc bool
|
||||
var ipamV6Conf []*libnetwork.IpamConf
|
||||
if cfg.EnableIPv6 {
|
||||
ipamV6Conf, err = getDefaultBridgeIPAMConf(bridgeName, userManagedBridge, defBrOptsV6{cfg})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has
|
||||
// at least 48 host bits, we need to guarantee the current behavior where the containers'
|
||||
// IPv6 addresses will be constructed based on the containers' interface MAC address.
|
||||
// We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints
|
||||
// on this network until after the driver has created the endpoint and returned the
|
||||
// constructed address. Libnetwork will then reserve this address with the ipam driver.
|
||||
ones, _ := fCIDRv6.Mask.Size()
|
||||
deferIPv6Alloc = ones <= 80
|
||||
|
||||
ipamV6Conf = &libnetwork.IpamConf{
|
||||
AuxAddresses: make(map[string]string),
|
||||
PreferredPool: fCIDRv6.String(),
|
||||
}
|
||||
|
||||
// In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6
|
||||
// address belongs to the same network, we need to inform libnetwork about it, so
|
||||
// that it can be reserved with IPAM and it will not be given away to somebody else
|
||||
nw6List, err := ifaceAddrs(bridgeName, netlink.FAMILY_V6)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "list bridge IPv6 addresses failed")
|
||||
}
|
||||
for _, nw6 := range nw6List {
|
||||
if fCIDRv6.Contains(nw6.IP) {
|
||||
ipamV6Conf.Gateway = nw6.IP.String()
|
||||
break
|
||||
// If the subnet has at least 48 host bits, preserve the legacy default bridge
|
||||
// behaviour of constructing a MAC address from the IPv4 address, then
|
||||
// constructing an IPv6 addresses based on that MAC address. Tell libnetwork to
|
||||
// defer the IPv6 address allocation for endpoints on this network until after
|
||||
// the driver has created the endpoint and proposed an IPv4 address. Libnetwork
|
||||
// will then reserve this address with the ipam driver. If no preferred pool has
|
||||
// been set the built-in ULA prefix will be used, assume it has at-least 48-bits.
|
||||
if len(ipamV6Conf) == 0 || ipamV6Conf[0].PreferredPool == "" {
|
||||
deferIPv6Alloc = true
|
||||
} else {
|
||||
_, ppNet, err := net.ParseCIDR(ipamV6Conf[0].PreferredPool)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ones, _ := ppNet.Mask.Size()
|
||||
deferIPv6Alloc = ones <= 80
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.DefaultGatewayIPv6 != nil {
|
||||
if ipamV6Conf == nil {
|
||||
ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
|
||||
}
|
||||
ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = cfg.DefaultGatewayIPv6.String()
|
||||
}
|
||||
|
||||
v6Conf := []*libnetwork.IpamConf{}
|
||||
if ipamV6Conf != nil {
|
||||
v6Conf = append(v6Conf, ipamV6Conf)
|
||||
}
|
||||
// Initialize default network on "bridge" with the same name
|
||||
_, err = controller.NewNetwork("bridge", network.NetworkBridge, "",
|
||||
libnetwork.NetworkOptionEnableIPv4(true),
|
||||
libnetwork.NetworkOptionEnableIPv6(cfg.EnableIPv6),
|
||||
libnetwork.NetworkOptionDriverOpts(netOption),
|
||||
libnetwork.NetworkOptionIpam("default", "", ipamV4Conf, v6Conf, nil),
|
||||
libnetwork.NetworkOptionIpam("default", "", ipamV4Conf, ipamV6Conf, nil),
|
||||
libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
|
||||
if err != nil {
|
||||
return fmt.Errorf(`error creating default %q network: %v`, network.NetworkBridge, err)
|
||||
@@ -1084,8 +1101,7 @@ func getDefaultBridgeName(cfg config.BridgeConfig) (bridgeName string, userManag
|
||||
func getDefaultBridgeIPAMConf(
|
||||
bridgeName string,
|
||||
userManagedBridge bool,
|
||||
cfg config.BridgeConfig,
|
||||
family int,
|
||||
opts defBrOpts,
|
||||
) ([]*libnetwork.IpamConf, error) {
|
||||
var (
|
||||
fCidrIP, bIP net.IP
|
||||
@@ -1093,18 +1109,18 @@ func getDefaultBridgeIPAMConf(
|
||||
err error
|
||||
)
|
||||
|
||||
if cfg.FixedCIDR != "" {
|
||||
if fCidrIP, fCidrIPNet, err = net.ParseCIDR(cfg.FixedCIDR); err != nil {
|
||||
return nil, errors.Wrap(err, "parse fixed-cidr failed")
|
||||
if fixedCIDR, fixedCIDROpt := opts.fixedCIDR(); fixedCIDR != "" {
|
||||
if fCidrIP, fCidrIPNet, err = net.ParseCIDR(fixedCIDR); err != nil {
|
||||
return nil, errors.Wrap(err, "parse "+fixedCIDROpt+" failed")
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.IP != "" {
|
||||
if bIP, bIPNet, err = net.ParseCIDR(cfg.IP); err != nil {
|
||||
return nil, err
|
||||
if cfgBIP, cfgBIPOpt := opts.bip(); cfgBIP != "" {
|
||||
if bIP, bIPNet, err = net.ParseCIDR(cfgBIP); err != nil {
|
||||
return nil, errors.Wrap(err, "parse "+cfgBIPOpt+" failed")
|
||||
}
|
||||
} else {
|
||||
if bIP, bIPNet, err = selectBIP(userManagedBridge, bridgeName, family, fCidrIP, fCidrIPNet); err != nil {
|
||||
if bIP, bIPNet, err = selectBIP(userManagedBridge, bridgeName, opts.nlFamily(), fCidrIP, fCidrIPNet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -1114,7 +1130,8 @@ func getDefaultBridgeIPAMConf(
|
||||
ipamConf.PreferredPool = bIPNet.String()
|
||||
ipamConf.Gateway = bIP.String()
|
||||
} else if !userManagedBridge && ipamConf.PreferredPool != "" {
|
||||
log.G(context.TODO()).Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamConf.PreferredPool)
|
||||
_, bipOptName := opts.bip()
|
||||
log.G(context.TODO()).Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --"+bipOptName+" can be used to set a preferred IP address", bridgeName, ipamConf.PreferredPool)
|
||||
}
|
||||
|
||||
if fCidrIP != nil && fCidrIPNet != nil {
|
||||
@@ -1128,21 +1145,27 @@ func getDefaultBridgeIPAMConf(
|
||||
// Don't allow SubPool (the range of allocatable addresses) to be outside, or
|
||||
// bigger than, the network itself. This is a configuration error, either the
|
||||
// user-managed bridge is missing an address to match fixed-cidr, or fixed-cidr
|
||||
// is wrong. But, just log rather than raise an error that would cause daemon
|
||||
// is wrong.
|
||||
fixedCIDR, fixedCIDROpt := opts.fixedCIDR()
|
||||
if opts.nlFamily() == netlink.FAMILY_V6 {
|
||||
return nil, fmt.Errorf("%s=%s is outside any subnet implied by addresses on the user-managed default bridge",
|
||||
fixedCIDROpt, fixedCIDR)
|
||||
}
|
||||
// For IPv4, just log rather than raise an error that would cause daemon
|
||||
// startup to fail, because this has been allowed by earlier versions. Remove
|
||||
// the SubPool, so that addresses are allocated from the whole of PreferredPool.
|
||||
log.G(context.TODO()).WithFields(log.Fields{
|
||||
"bridge": bridgeName,
|
||||
"fixed-cidr": cfg.FixedCIDR,
|
||||
fixedCIDROpt: fixedCIDR,
|
||||
"bridge-network": bIPNet.String(),
|
||||
}).Warn("fixed-cidr is outside the subnet implied by addresses on the user-managed default bridge, this may be treated as an error in a future release")
|
||||
}).Warn(fixedCIDROpt + " is outside any subnet implied by addresses on the user-managed default bridge, this may be treated as an error in a future release")
|
||||
ipamConf.SubPool = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.DefaultGatewayIPv4 != nil {
|
||||
ipamConf.AuxAddresses["DefaultGatewayIPv4"] = cfg.DefaultGatewayIPv4.String()
|
||||
if defGw, _, auxAddrLabel := opts.defGw(); defGw != nil {
|
||||
ipamConf.AuxAddresses[auxAddrLabel] = defGw.String()
|
||||
}
|
||||
|
||||
return []*libnetwork.IpamConf{ipamConf}, nil
|
||||
|
||||
@@ -44,39 +44,55 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
|
||||
testcases := []defaultBridgeIPAMTestCase{
|
||||
{
|
||||
name: "fixed-cidr only",
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.176.0/24"},
|
||||
name: "no config",
|
||||
// No config for the bridge, but override default-address-pools to
|
||||
// get a predictable result for IPv6 (rather than the daemon's ULA).
|
||||
daemonArgs: []string{
|
||||
"--default-address-pool", `base=192.168.176.0/20,size=24`,
|
||||
"--default-address-pool", `base=fdd1:8161:2d2c::/56,size=64`,
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/24",
|
||||
IPRange: "192.168.176.0/24",
|
||||
},
|
||||
{Subnet: "192.168.176.0/24", Gateway: "192.168.176.1"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::1/64"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bip only",
|
||||
daemonArgs: []string{"--bip", "192.168.176.88/24"},
|
||||
name: "fixed-cidr only",
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.176.0/24",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/24",
|
||||
Gateway: "192.168.176.88",
|
||||
},
|
||||
{Subnet: "192.168.176.0/24", IPRange: "192.168.176.0/24"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", IPRange: "fdd1:8161:2d2c::/64"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bip only",
|
||||
daemonArgs: []string{
|
||||
"--bip", "192.168.176.88/24",
|
||||
"--bip6", "fdd1:8161:2d2c::8888/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing bridge address only",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24"},
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24", "fdd1:8161:2d2c::8888/64"},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/24",
|
||||
Gateway: "192.168.176.88",
|
||||
},
|
||||
{Subnet: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fixed-cidr within old bridge subnet",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/20"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.176.0/24"},
|
||||
initialBridgeAddrs: []string{"192.168.176.88/20", "fdd1:8161:2d2c::8888/56"},
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.176.0/24",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/64",
|
||||
},
|
||||
// There's no --bip to dictate the subnet, so it's derived from an
|
||||
// existing bridge address. If fixed-cidr's subnet is made smaller
|
||||
// following a daemon restart, a user might reasonably expect the
|
||||
@@ -89,67 +105,65 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
// and it'd be a breaking change for anyone relying on the existing
|
||||
// behaviour.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/20",
|
||||
IPRange: "192.168.176.0/24",
|
||||
Gateway: "192.168.176.88",
|
||||
},
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/56", IPRange: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fixed-cidr within old bridge subnet with new bip",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/20"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.176.0/24", "--bip", "192.168.176.99/24"},
|
||||
initialBridgeAddrs: []string{"192.168.176.88/20", "fdd1:8161:2d2c::/56"},
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.176.0/24", "--bip", "192.168.176.99/24",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/64", "--bip6", "fdd1:8161:2d2c::9999/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/24",
|
||||
IPRange: "192.168.176.0/24",
|
||||
Gateway: "192.168.176.99",
|
||||
},
|
||||
{Subnet: "192.168.176.0/24", IPRange: "192.168.176.0/24", Gateway: "192.168.176.99"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", IPRange: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::9999"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "old bridge subnet within fixed-cidr",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.176.0/20"},
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24", "fdd1:8161:2d2c::8888/64"},
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.176.0/20",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/56",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/20",
|
||||
IPRange: "192.168.176.0/20",
|
||||
Gateway: "192.168.176.88",
|
||||
},
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/20", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/56", IPRange: "fdd1:8161:2d2c::/56", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "old bridge subnet outside fixed-cidr",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.177.0/24"},
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24", "fdd1:8161:2d2c::8888/64"},
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.177.0/24",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c:1::/64",
|
||||
},
|
||||
// The bridge's address/subnet should be ignored, this is a change
|
||||
// of fixed-cidr.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.177.0/24",
|
||||
IPRange: "192.168.177.0/24",
|
||||
// No Gateway is configured, because the address could not be learnt from the
|
||||
// bridge. An address will have been allocated but, because there's config (the
|
||||
// fixed-cidr), inspect shows just the config. (Surprisingly, when there's no
|
||||
// config at all, the inspect output still says its showing config but actually
|
||||
// shows the running state.) When the daemon is restarted, after a gateway
|
||||
// address has been assigned to the bridge, that address will become config - so
|
||||
// a Gateway address will show up in the inspect output.
|
||||
},
|
||||
{Subnet: "192.168.177.0/24", IPRange: "192.168.177.0/24"},
|
||||
{Subnet: "fdd1:8161:2d2c:1::/64", IPRange: "fdd1:8161:2d2c:1::/64"},
|
||||
// No Gateway is configured, because the address could not be learnt from the
|
||||
// bridge. An address will have been allocated but, because there's config (the
|
||||
// fixed-cidr), inspect shows just the config. (Surprisingly, when there's no
|
||||
// config at all, the inspect output still says its showing config but actually
|
||||
// shows the running state.) When the daemon is restarted, after a gateway
|
||||
// address has been assigned to the bridge, that address will become config - so
|
||||
// a Gateway address will show up in the inspect output.
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "old bridge subnet outside fixed-cidr with bip",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.177.0/24", "--bip", "192.168.177.99/24"},
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24", "fdd1:8161:2d2c::8888/64"},
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.177.0/24", "--bip", "192.168.177.99/24",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c:1::/64", "--bip6", "fdd1:8161:2d2c:1::9999/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.177.0/24",
|
||||
IPRange: "192.168.177.0/24",
|
||||
Gateway: "192.168.177.99",
|
||||
},
|
||||
{Subnet: "192.168.177.0/24", IPRange: "192.168.177.0/24", Gateway: "192.168.177.99"},
|
||||
{Subnet: "fdd1:8161:2d2c:1::/64", IPRange: "fdd1:8161:2d2c:1::/64", Gateway: "fdd1:8161:2d2c:1::9999"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -167,83 +181,80 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
testcases := []defaultBridgeIPAMTestCase{
|
||||
{
|
||||
name: "bridge only",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/20"},
|
||||
initialBridgeAddrs: []string{"192.168.176.88/20", "fdd1:8161:2d2c::8888/64"},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/20",
|
||||
Gateway: "192.168.176.88",
|
||||
},
|
||||
{Subnet: "192.168.176.0/20", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fixed-cidr only",
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.176.0/24"},
|
||||
name: "fixed-cidr only",
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.176.0/24",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/24",
|
||||
IPRange: "192.168.176.0/24",
|
||||
},
|
||||
{Subnet: "192.168.176.0/24", IPRange: "192.168.176.0/24"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", IPRange: "fdd1:8161:2d2c::/64"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fcidr in bridge subnet and bridge ip in fcidr",
|
||||
initialBridgeAddrs: []string{"192.168.160.88/20", "192.168.176.88/20", "192.168.192.88/20"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.176.0/24"},
|
||||
name: "fcidr in bridge subnet and bridge ip in fcidr",
|
||||
initialBridgeAddrs: []string{
|
||||
"192.168.160.88/20", "192.168.176.88/20", "192.168.192.88/20",
|
||||
"fdd1:8161:2d2c::8888/60", "fdd1:8161:2d2c:10::8888/60", "fdd1:8161:2d2c:20::8888/60",
|
||||
},
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.176.0/24",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c:10::/64",
|
||||
},
|
||||
// Selected bip should be the one within fixed-cidr
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/20",
|
||||
IPRange: "192.168.176.0/24",
|
||||
Gateway: "192.168.176.88",
|
||||
},
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c:10::/60", IPRange: "fdd1:8161:2d2c:10::/64", Gateway: "fdd1:8161:2d2c:10::8888"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fcidr in bridge subnet and bridge ip not in fcidr",
|
||||
initialBridgeAddrs: []string{"192.168.160.88/20", "192.168.176.88/20", "192.168.192.88/20"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.177.0/24"},
|
||||
name: "fcidr in bridge subnet and bridge ip not in fcidr",
|
||||
initialBridgeAddrs: []string{
|
||||
"192.168.160.88/20", "192.168.176.88/20", "192.168.192.88/20",
|
||||
"fdd1:8161:2d2c::8888/60", "fdd1:8161:2d2c:10::8888/60", "fdd1:8161:2d2c:20::8888/60",
|
||||
},
|
||||
daemonArgs: []string{
|
||||
"--fixed-cidr", "192.168.177.0/24",
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c:11::8888/64",
|
||||
},
|
||||
// Selected bridge subnet should be the one that encompasses fixed-cidr.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/20",
|
||||
IPRange: "192.168.177.0/24",
|
||||
Gateway: "192.168.176.88",
|
||||
},
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.177.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c:10::/60", IPRange: "fdd1:8161:2d2c:11::/64", Gateway: "fdd1:8161:2d2c:10::8888"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fixed-cidr bigger than bridge subnet",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.176.0/20"},
|
||||
ipv4Only: true,
|
||||
// fixed-cidr (the range of allocatable addresses) is bigger than the
|
||||
// bridge subnet - this is a configuration error, but has historically
|
||||
// been allowed. Because IPRange is treated as an offset into Subnet, it
|
||||
// would normally result in a docker network that allocated addresses
|
||||
// within the selected subnet. So, fixed-cidr is dropped, making the
|
||||
// whole subnet allocatable.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.176.0/24",
|
||||
Gateway: "192.168.176.88",
|
||||
},
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: "192.168.176.0/24", Gateway: "192.168.176.88"}},
|
||||
},
|
||||
{
|
||||
name: "no bridge ip within fixed-cidr",
|
||||
initialBridgeAddrs: []string{"192.168.160.88/20", "192.168.192.88/20"},
|
||||
initialBridgeAddrs: []string{"192.168.160.88/20"},
|
||||
daemonArgs: []string{"--fixed-cidr", "192.168.176.0/24"},
|
||||
ipv4Only: true,
|
||||
// fixed-cidr (the range of allocatable addresses) is outside the bridge
|
||||
// subnet - this is a configuration error, but has historically been
|
||||
// allowed. Because IPRange is treated as an offset into Subnet, it
|
||||
// would normally result in a docker network that allocated addresses
|
||||
// within the selected subnet. So, fixed-cidr is dropped, making the
|
||||
// whole subnet allocatable.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.160.0/20",
|
||||
Gateway: "192.168.160.88",
|
||||
},
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: "192.168.160.0/20", Gateway: "192.168.160.88"}},
|
||||
},
|
||||
{
|
||||
name: "fixed-cidr contains bridge subnet",
|
||||
@@ -256,12 +267,37 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
// normally result in a docker network that allocated addresses
|
||||
// within the selected subnet. So, fixed-cidr is dropped, making the
|
||||
// whole subnet allocatable.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.177.0/24",
|
||||
Gateway: "192.168.177.1",
|
||||
},
|
||||
},
|
||||
ipv4Only: true,
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: "192.168.177.0/24", Gateway: "192.168.177.1"}},
|
||||
},
|
||||
|
||||
{
|
||||
name: "fixed-cidr-v6 bigger than bridge subnet",
|
||||
initialBridgeAddrs: []string{"fdd1:8161:2d2c::8888/64"},
|
||||
daemonArgs: []string{"--fixed-cidr-v6", "fdd1:8161:2d2c::/60"},
|
||||
// fixed-cidr-v6 (the range of allocatable addresses) is bigger than the bridge
|
||||
// subnet - this is a configuration error. Unlike IPv4, it has not historically
|
||||
// been allowed, so it will prevent daemon startup.
|
||||
expStartErr: true,
|
||||
},
|
||||
{
|
||||
name: "no bridge ip within fixed-cidr-v6",
|
||||
initialBridgeAddrs: []string{"fdd1:8161:2d2c::8888/60"},
|
||||
daemonArgs: []string{"--fixed-cidr-v6", "fdd1:8161:2d2c:10::/64"},
|
||||
// fixed-cidr-v6 (the range of allocatable addresses) is outside the bridge subnet -
|
||||
// this is a configuration error. Unlike IPv4, it has not historically been
|
||||
// allowed, so it will prevent daemon startup.
|
||||
expStartErr: true,
|
||||
},
|
||||
{
|
||||
name: "fixed-cidr-v6 contains bridge subnet",
|
||||
initialBridgeAddrs: []string{"fdd1:8161:2d2c:10::1/64"},
|
||||
daemonArgs: []string{"--fixed-cidr-v6", "fdd1:8161:2d2c:10::/60"},
|
||||
// fixed-cidr-v6 (the range of allocatable addresses) is bigger than the
|
||||
// bridge subnet, and the bridge's address is not within fixed-cidr.
|
||||
// This is a configuration error, Unlike IPv4, it has not historically been
|
||||
// allowed, so it will prevent daemon startup.
|
||||
expStartErr: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
@@ -275,6 +311,8 @@ type defaultBridgeIPAMTestCase struct {
|
||||
userDefinedBridge bool
|
||||
initialBridgeAddrs []string
|
||||
daemonArgs []string
|
||||
ipv4Only bool
|
||||
expStartErr bool
|
||||
expIPAMConfig []network.IPAMConfig
|
||||
}
|
||||
|
||||
@@ -287,11 +325,14 @@ func testDefaultBridgeIPAM(ctx context.Context, t *testing.T, tc defaultBridgeIP
|
||||
defer deleteInterface(t, bridgeName)
|
||||
|
||||
var dOpts []daemon.Option
|
||||
dArgs := tc.daemonArgs
|
||||
var dArgs []string
|
||||
if !tc.ipv4Only {
|
||||
dArgs = append(tc.daemonArgs, "--ipv6")
|
||||
}
|
||||
if tc.userDefinedBridge {
|
||||
// If a bridge is supplied by the user, the daemon should use its addresses
|
||||
// to infer --bip (which cannot be specified).
|
||||
dArgs = append(dArgs, []string{"--bridge", bridgeName}...)
|
||||
dArgs = append(dArgs, "--bridge", bridgeName)
|
||||
} else {
|
||||
// The bridge is created and managed by docker, it's always called "docker0",
|
||||
// unless this test-only env var is set - to avoid conflict with the docker0
|
||||
@@ -304,7 +345,14 @@ func testDefaultBridgeIPAM(ctx context.Context, t *testing.T, tc defaultBridgeIP
|
||||
d.Stop(t)
|
||||
d.Cleanup(t)
|
||||
}()
|
||||
d.StartWithBusybox(ctx, t, dArgs...)
|
||||
|
||||
if tc.expStartErr {
|
||||
err := d.StartWithError(dArgs...)
|
||||
assert.Check(t, is.ErrorContains(err, "daemon exited during startup"))
|
||||
return
|
||||
}
|
||||
|
||||
d.Start(t, dArgs...)
|
||||
c := d.NewClientT(t)
|
||||
defer c.Close()
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -496,6 +497,9 @@ func TestDefaultBridgeIPv6(t *testing.T) {
|
||||
name string
|
||||
fixed_cidr_v6 string
|
||||
}{
|
||||
{
|
||||
name: "built in ULA prefix",
|
||||
},
|
||||
{
|
||||
name: "IPv6 ULA",
|
||||
fixed_cidr_v6: "fd00:1234::/64",
|
||||
@@ -515,10 +519,11 @@ func TestDefaultBridgeIPv6(t *testing.T) {
|
||||
ctx := testutil.StartSpan(ctx, t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t,
|
||||
"--ipv6",
|
||||
"--fixed-cidr-v6", tc.fixed_cidr_v6,
|
||||
)
|
||||
if tc.fixed_cidr_v6 == "" {
|
||||
d.StartWithBusybox(ctx, t, "--ipv6")
|
||||
} else {
|
||||
d.StartWithBusybox(ctx, t, "--ipv6", "--fixed-cidr-v6", tc.fixed_cidr_v6)
|
||||
}
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
@@ -532,15 +537,26 @@ func TestDefaultBridgeIPv6(t *testing.T) {
|
||||
Force: true,
|
||||
})
|
||||
|
||||
networkName := "bridge"
|
||||
const networkName = "bridge"
|
||||
inspect := container.Inspect(ctx, t, c, cID)
|
||||
pingHost := inspect.NetworkSettings.Networks[networkName].GlobalIPv6Address
|
||||
gIPv6 := inspect.NetworkSettings.Networks[networkName].GlobalIPv6Address
|
||||
|
||||
// The container's MAC and IPv6 addresses should be derived from the
|
||||
// IPAM-allocated IPv4 address.
|
||||
addr4, err := netip.ParseAddr(inspect.NetworkSettings.Networks[networkName].IPAddress)
|
||||
assert.NilError(t, err)
|
||||
mac, err := net.ParseMAC(inspect.NetworkSettings.Networks[networkName].MacAddress)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(addr4.AsSlice(), []byte(mac)[2:]))
|
||||
addr6, err := netip.ParseAddr(gIPv6)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(addr4.AsSlice(), addr6.AsSlice()[12:]))
|
||||
|
||||
attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
res := container.RunAttach(attachCtx, t, c,
|
||||
container.WithImage("busybox:latest"),
|
||||
container.WithCmd("ping", "-c1", "-W3", pingHost),
|
||||
container.WithCmd("ping", "-c1", "-W3", gIPv6),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{
|
||||
Force: true,
|
||||
|
||||
@@ -10,6 +10,7 @@ dockerd - Enable daemon mode
|
||||
[**--authorization-plugin**[=*[]*]]
|
||||
[**-b**|**--bridge**[=*BRIDGE*]]
|
||||
[**--bip**[=*BIP*]]
|
||||
[**--bip6**[=*BIP*]]
|
||||
[**--cgroup-parent**[=*[]*]]
|
||||
[**--config-file**[=*path*]]
|
||||
[**--containerd**[=*SOCKET-PATH*]]
|
||||
@@ -146,7 +147,11 @@ $ sudo dockerd --add-runtime runc=runc --add-runtime custom=/usr/local/bin/my-ru
|
||||
container networking
|
||||
|
||||
**--bip**=""
|
||||
Use the provided CIDR notation address for the dynamically created bridge
|
||||
Use the provided CIDR notation IPv4 address for the dynamically created bridge
|
||||
(docker0); Mutually exclusive of \-b
|
||||
|
||||
**--bip6**=""
|
||||
Use the provided CIDR notation IPv6 address for the dynamically created bridge
|
||||
(docker0); Mutually exclusive of \-b
|
||||
|
||||
**--cgroup-parent**=""
|
||||
|
||||
Reference in New Issue
Block a user