mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Implement allocating IPs from CIDR within bridge network
Fixes #4986 Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com>
This commit is contained in:
parent
42dd48315f
commit
b101022dbe
6 changed files with 125 additions and 5 deletions
|
@ -28,6 +28,7 @@ type Config struct {
|
||||||
DefaultIp net.IP
|
DefaultIp net.IP
|
||||||
BridgeIface string
|
BridgeIface string
|
||||||
BridgeIP string
|
BridgeIP string
|
||||||
|
FixedCIDR string
|
||||||
InterContainerCommunication bool
|
InterContainerCommunication bool
|
||||||
GraphDriver string
|
GraphDriver string
|
||||||
GraphOptions []string
|
GraphOptions []string
|
||||||
|
@ -50,6 +51,7 @@ func (config *Config) InstallFlags() {
|
||||||
flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
|
flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
|
||||||
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
|
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
|
||||||
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
|
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
|
||||||
|
flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)\nthis subnet must be nested in bridge subnet (which is defined by -b or --bip)")
|
||||||
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
|
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
|
||||||
flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver")
|
flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver")
|
||||||
flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver")
|
flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver")
|
||||||
|
|
|
@ -803,6 +803,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error)
|
||||||
job.SetenvBool("EnableIpForward", config.EnableIpForward)
|
job.SetenvBool("EnableIpForward", config.EnableIpForward)
|
||||||
job.Setenv("BridgeIface", config.BridgeIface)
|
job.Setenv("BridgeIface", config.BridgeIface)
|
||||||
job.Setenv("BridgeIP", config.BridgeIP)
|
job.Setenv("BridgeIP", config.BridgeIP)
|
||||||
|
job.Setenv("FixedCIDR", config.FixedCIDR)
|
||||||
job.Setenv("DefaultBindingIP", config.DefaultIp.String())
|
job.Setenv("DefaultBindingIP", config.DefaultIp.String())
|
||||||
|
|
||||||
if err := job.Run(); err != nil {
|
if err := job.Run(); err != nil {
|
||||||
|
|
|
@ -83,6 +83,7 @@ func InitDriver(job *engine.Job) engine.Status {
|
||||||
icc = job.GetenvBool("InterContainerCommunication")
|
icc = job.GetenvBool("InterContainerCommunication")
|
||||||
ipForward = job.GetenvBool("EnableIpForward")
|
ipForward = job.GetenvBool("EnableIpForward")
|
||||||
bridgeIP = job.Getenv("BridgeIP")
|
bridgeIP = job.Getenv("BridgeIP")
|
||||||
|
fixedCIDR = job.Getenv("FixedCIDR")
|
||||||
)
|
)
|
||||||
|
|
||||||
if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
|
if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
|
||||||
|
@ -157,6 +158,16 @@ func InitDriver(job *engine.Job) engine.Status {
|
||||||
}
|
}
|
||||||
|
|
||||||
bridgeNetwork = network
|
bridgeNetwork = network
|
||||||
|
if fixedCIDR != "" {
|
||||||
|
_, subnet, err := net.ParseCIDR(fixedCIDR)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
log.Debugf("Subnet: %v", subnet)
|
||||||
|
if err := ipallocator.RegisterSubnet(bridgeNetwork, subnet); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/docker/docker/issues/2768
|
// https://github.com/docker/docker/issues/2768
|
||||||
job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeNetwork.IP)
|
job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeNetwork.IP)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/dotcloud/docker/daemon/networkdriver"
|
"github.com/docker/docker/daemon/networkdriver"
|
||||||
)
|
)
|
||||||
|
|
||||||
// allocatedMap is thread-unsafe set of allocated IP
|
// allocatedMap is thread-unsafe set of allocated IP
|
||||||
|
@ -23,8 +23,8 @@ func newAllocatedMap(network *net.IPNet) *allocatedMap {
|
||||||
end := ipToInt(lastIP) - 1
|
end := ipToInt(lastIP) - 1
|
||||||
return &allocatedMap{
|
return &allocatedMap{
|
||||||
p: make(map[uint32]struct{}),
|
p: make(map[uint32]struct{}),
|
||||||
begin: begin, // - network
|
begin: begin,
|
||||||
end: end, // - broadcast
|
end: end,
|
||||||
last: begin - 1, // so first allocated will be begin
|
last: begin - 1, // so first allocated will be begin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,8 @@ type networkSet map[string]*allocatedMap
|
||||||
var (
|
var (
|
||||||
ErrNoAvailableIPs = errors.New("no available ip addresses on network")
|
ErrNoAvailableIPs = errors.New("no available ip addresses on network")
|
||||||
ErrIPAlreadyAllocated = errors.New("ip already allocated")
|
ErrIPAlreadyAllocated = errors.New("ip already allocated")
|
||||||
|
ErrNetworkAlreadyRegistered = errors.New("network already registered")
|
||||||
|
ErrBadSubnet = errors.New("network not contains specified subnet")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -41,6 +43,29 @@ var (
|
||||||
allocatedIPs = networkSet{}
|
allocatedIPs = networkSet{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RegisterSubnet registers network in global allocator with bounds
|
||||||
|
// defined by subnet. If you want to use network range you must call
|
||||||
|
// this method before first RequestIP, otherwise full network range will be used
|
||||||
|
func RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
key := network.String()
|
||||||
|
if _, ok := allocatedIPs[key]; ok {
|
||||||
|
return ErrNetworkAlreadyRegistered
|
||||||
|
}
|
||||||
|
n := newAllocatedMap(network)
|
||||||
|
beginIP, endIP := networkdriver.NetworkRange(subnet)
|
||||||
|
begin, end := ipToInt(beginIP)+1, ipToInt(endIP)-1
|
||||||
|
if !(begin >= n.begin && end <= n.end && begin < end) {
|
||||||
|
return ErrBadSubnet
|
||||||
|
}
|
||||||
|
n.begin = begin
|
||||||
|
n.end = end
|
||||||
|
n.last = begin - 1
|
||||||
|
allocatedIPs[key] = n
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RequestIP requests an available ip from the given network. It
|
// RequestIP requests an available ip from the given network. It
|
||||||
// will return the next available ip if the ip provided is nil. If the
|
// will return the next available ip if the ip provided is nil. If the
|
||||||
// ip provided is not nil it will validate that the provided ip is available
|
// ip provided is not nil it will validate that the provided ip is available
|
||||||
|
|
|
@ -318,6 +318,86 @@ func TestAllocateDifferentSubnets(t *testing.T) {
|
||||||
assertIPEquals(t, expectedIPs[2], ip21)
|
assertIPEquals(t, expectedIPs[2], ip21)
|
||||||
assertIPEquals(t, expectedIPs[3], ip22)
|
assertIPEquals(t, expectedIPs[3], ip22)
|
||||||
}
|
}
|
||||||
|
func TestRegisterBadTwice(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
subnet := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 8},
|
||||||
|
Mask: []byte{255, 255, 255, 248},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := RegisterSubnet(network, subnet); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
subnet = &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 16},
|
||||||
|
Mask: []byte{255, 255, 255, 248},
|
||||||
|
}
|
||||||
|
if err := RegisterSubnet(network, subnet); err != ErrNetworkAlreadyRegistered {
|
||||||
|
t.Fatalf("Expecteded ErrNetworkAlreadyRegistered error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterBadRange(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
subnet := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 1},
|
||||||
|
Mask: []byte{255, 255, 0, 0},
|
||||||
|
}
|
||||||
|
if err := RegisterSubnet(network, subnet); err != ErrBadSubnet {
|
||||||
|
t.Fatalf("Expected ErrBadSubnet error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocateFromRange(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
// 192.168.1.9 - 192.168.1.14
|
||||||
|
subnet := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 8},
|
||||||
|
Mask: []byte{255, 255, 255, 248},
|
||||||
|
}
|
||||||
|
if err := RegisterSubnet(network, subnet); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectedIPs := []net.IP{
|
||||||
|
0: net.IPv4(192, 168, 0, 9),
|
||||||
|
1: net.IPv4(192, 168, 0, 10),
|
||||||
|
2: net.IPv4(192, 168, 0, 11),
|
||||||
|
3: net.IPv4(192, 168, 0, 12),
|
||||||
|
4: net.IPv4(192, 168, 0, 13),
|
||||||
|
5: net.IPv4(192, 168, 0, 14),
|
||||||
|
}
|
||||||
|
for _, ip := range expectedIPs {
|
||||||
|
rip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assertIPEquals(t, ip, rip)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
|
||||||
|
t.Fatalf("Expected ErrNoAvailableIPs error, got %v", err)
|
||||||
|
}
|
||||||
|
for _, ip := range expectedIPs {
|
||||||
|
ReleaseIP(network, ip)
|
||||||
|
rip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assertIPEquals(t, ip, rip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func assertIPEquals(t *testing.T, ip1, ip2 net.IP) {
|
func assertIPEquals(t *testing.T, ip1, ip2 net.IP) {
|
||||||
if !ip1.Equal(ip2) {
|
if !ip1.Equal(ip2) {
|
||||||
|
|
|
@ -54,6 +54,7 @@ expect an integer, and they can only be specified once.
|
||||||
-b, --bridge="" Attach containers to a pre-existing network bridge
|
-b, --bridge="" Attach containers to a pre-existing network bridge
|
||||||
use 'none' to disable container networking
|
use 'none' to disable container networking
|
||||||
--bip="" Use this CIDR notation address for the network bridge's IP, not compatible with -b
|
--bip="" Use this CIDR notation address for the network bridge's IP, not compatible with -b
|
||||||
|
--fixed-cidr="" IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)
|
||||||
-D, --debug=false Enable debug mode
|
-D, --debug=false Enable debug mode
|
||||||
-d, --daemon=false Enable daemon mode
|
-d, --daemon=false Enable daemon mode
|
||||||
--dns=[] Force Docker to use specific DNS servers
|
--dns=[] Force Docker to use specific DNS servers
|
||||||
|
|
Loading…
Reference in a new issue