Skip to content

Commit 672ced7

Browse files
committed
Merge pull request #26 from aboch/ipam
Issue #18: IP Allocator rework
2 parents ed6b8ce + 56832d6 commit 672ced7

11 files changed

Lines changed: 1163 additions & 77 deletions

drivers/bridge/setup_device.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ package bridge
22

33
import (
44
"fmt"
5-
"math/rand"
6-
"net"
75

86
log "github.com/Sirupsen/logrus"
97
"github.com/docker/docker/pkg/parsers/kernel"
8+
"github.com/docker/libnetwork"
109
"github.com/vishvananda/netlink"
1110
)
1211

@@ -29,7 +28,7 @@ func setupDevice(i *bridgeInterface) error {
2928
// was not supported before that.
3029
kv, err := kernel.GetKernelVersion()
3130
if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) {
32-
i.Link.Attrs().HardwareAddr = generateRandomMAC()
31+
i.Link.Attrs().HardwareAddr = libnetwork.GenerateRandomMAC()
3332
log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr)
3433
}
3534

@@ -51,13 +50,3 @@ func setupDeviceUp(i *bridgeInterface) error {
5150
}
5251
return nil
5352
}
54-
55-
func generateRandomMAC() net.HardwareAddr {
56-
hw := make(net.HardwareAddr, 6)
57-
for i := 0; i < 6; i++ {
58-
hw[i] = byte(rand.Intn(255))
59-
}
60-
hw[0] &^= 0x1 // clear multicast bit
61-
hw[0] |= 0x2 // set local assignment bit (IEEE802)
62-
return hw
63-
}

drivers/bridge/setup_device_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ func TestSetupDeviceUp(t *testing.T) {
6969
func TestGenerateRandomMAC(t *testing.T) {
7070
defer libnetwork.SetupTestNetNS(t)()
7171

72-
mac1 := generateRandomMAC()
73-
mac2 := generateRandomMAC()
72+
mac1 := libnetwork.GenerateRandomMAC()
73+
mac2 := libnetwork.GenerateRandomMAC()
7474
if bytes.Compare(mac1, mac2) == 0 {
7575
t.Fatalf("Generated twice the same MAC address %v", mac1)
7676
}

drivers/bridge/setup_fixedcidrv4.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"fmt"
55

66
log "github.com/Sirupsen/logrus"
7-
"github.com/docker/docker/daemon/networkdriver/ipallocator"
7+
"github.com/docker/libnetwork/ipallocator"
88
)
99

1010
func setupFixedCIDRv4(i *bridgeInterface) error {

drivers/bridge/setup_fixedcidrv4_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"net"
55
"testing"
66

7-
"github.com/docker/docker/daemon/networkdriver/ipallocator"
87
"github.com/docker/libnetwork"
8+
"github.com/docker/libnetwork/ipallocator"
99
)
1010

1111
func TestSetupFixedCIDRv4(t *testing.T) {

drivers/bridge/setup_fixedcidrv6.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"fmt"
55

66
log "github.com/Sirupsen/logrus"
7-
"github.com/docker/docker/daemon/networkdriver/ipallocator"
7+
"github.com/docker/libnetwork/ipallocator"
88
)
99

1010
func setupFixedCIDRv6(i *bridgeInterface) error {

drivers/bridge/setup_fixedcidrv6_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"net"
55
"testing"
66

7-
"github.com/docker/docker/daemon/networkdriver/ipallocator"
87
"github.com/docker/libnetwork"
8+
"github.com/docker/libnetwork/ipallocator"
99
)
1010

1111
func TestSetupFixedCIDRv6(t *testing.T) {

drivers/bridge/setup_ipv4.go

Lines changed: 4 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net"
66

77
log "github.com/Sirupsen/logrus"
8+
"github.com/docker/libnetwork"
89
"github.com/vishvananda/netlink"
910
)
1011

@@ -70,67 +71,12 @@ func electBridgeIPv4(config *Configuration) (*net.IPNet, error) {
7071

7172
// Try to automatically elect appropriate brige IPv4 settings.
7273
for _, n := range bridgeNetworks {
73-
if err := checkNameserverOverlaps(nameservers, n); err == nil {
74-
if err := checkRouteOverlaps(n); err == nil {
74+
if err := libnetwork.CheckNameserverOverlaps(nameservers, n); err == nil {
75+
if err := libnetwork.CheckRouteOverlaps(n); err == nil {
7576
return n, nil
7677
}
7778
}
7879
}
7980

80-
return nil, fmt.Errorf("Couldn't find an address range for interface %q", config.BridgeName)
81-
}
82-
83-
func checkNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
84-
for _, ns := range nameservers {
85-
_, nsNetwork, err := net.ParseCIDR(ns)
86-
if err != nil {
87-
return err
88-
}
89-
if networkOverlaps(toCheck, nsNetwork) {
90-
return fmt.Errorf("Requested network %s overlaps with name server", toCheck.String())
91-
}
92-
}
93-
return nil
94-
}
95-
96-
func checkRouteOverlaps(toCheck *net.IPNet) error {
97-
networks, err := netlink.RouteList(nil, netlink.FAMILY_V4)
98-
if err != nil {
99-
return err
100-
}
101-
102-
for _, network := range networks {
103-
// TODO Is that right?
104-
if network.Dst != nil && networkOverlaps(toCheck, network.Dst) {
105-
return fmt.Errorf("Requested network %s overlaps with an existing network", toCheck.String())
106-
}
107-
}
108-
return nil
109-
}
110-
111-
func networkOverlaps(netX *net.IPNet, netY *net.IPNet) bool {
112-
if firstIP, _ := networkRange(netX); netY.Contains(firstIP) {
113-
return true
114-
}
115-
if firstIP, _ := networkRange(netY); netX.Contains(firstIP) {
116-
return true
117-
}
118-
return false
119-
}
120-
121-
func networkRange(network *net.IPNet) (net.IP, net.IP) {
122-
var netIP net.IP
123-
if network.IP.To4() != nil {
124-
netIP = network.IP.To4()
125-
} else if network.IP.To16() != nil {
126-
netIP = network.IP.To16()
127-
} else {
128-
return nil, nil
129-
}
130-
131-
lastIP := make([]byte, len(netIP), len(netIP))
132-
for i := 0; i < len(netIP); i++ {
133-
lastIP[i] = netIP[i] | ^network.Mask[i]
134-
}
135-
return netIP.Mask(network.Mask), net.IP(lastIP)
81+
return nil, fmt.Errorf("'t find an address range for interface %q", config.BridgeName)
13682
}

ipallocator/allocator.go

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// Package ipallocator defines the default IP allocator. It will move out of libnetwork as an external IPAM plugin.
2+
// This has been imported unchanged from Docker, besides additon of registration logic
3+
package ipallocator
4+
5+
import (
6+
"errors"
7+
"math/big"
8+
"net"
9+
"sync"
10+
11+
log "github.com/Sirupsen/logrus"
12+
"github.com/docker/libnetwork"
13+
)
14+
15+
// allocatedMap is thread-unsafe set of allocated IP
16+
type allocatedMap struct {
17+
p map[string]struct{}
18+
last *big.Int
19+
begin *big.Int
20+
end *big.Int
21+
}
22+
23+
func newAllocatedMap(network *net.IPNet) *allocatedMap {
24+
firstIP, lastIP := libnetwork.NetworkRange(network)
25+
begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1))
26+
end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1))
27+
28+
return &allocatedMap{
29+
p: make(map[string]struct{}),
30+
begin: begin,
31+
end: end,
32+
last: big.NewInt(0).Sub(begin, big.NewInt(1)), // so first allocated will be begin
33+
}
34+
}
35+
36+
type networkSet map[string]*allocatedMap
37+
38+
var (
39+
// ErrNoAvailableIPs preformatted error
40+
ErrNoAvailableIPs = errors.New("no available ip addresses on network")
41+
// ErrIPAlreadyAllocated preformatted error
42+
ErrIPAlreadyAllocated = errors.New("ip already allocated")
43+
// ErrIPOutOfRange preformatted error
44+
ErrIPOutOfRange = errors.New("requested ip is out of range")
45+
// ErrNetworkAlreadyRegistered preformatted error
46+
ErrNetworkAlreadyRegistered = errors.New("network already registered")
47+
// ErrBadSubnet preformatted error
48+
ErrBadSubnet = errors.New("network does not contain specified subnet")
49+
)
50+
51+
var (
52+
lock = sync.Mutex{}
53+
allocatedIPs = networkSet{}
54+
)
55+
56+
// RegisterSubnet registers network in global allocator with bounds
57+
// defined by subnet. If you want to use network range you must call
58+
// this method before first RequestIP, otherwise full network range will be used
59+
func RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error {
60+
lock.Lock()
61+
defer lock.Unlock()
62+
key := network.String()
63+
if _, ok := allocatedIPs[key]; ok {
64+
return ErrNetworkAlreadyRegistered
65+
}
66+
n := newAllocatedMap(network)
67+
beginIP, endIP := libnetwork.NetworkRange(subnet)
68+
begin := big.NewInt(0).Add(ipToBigInt(beginIP), big.NewInt(1))
69+
end := big.NewInt(0).Sub(ipToBigInt(endIP), big.NewInt(1))
70+
71+
// Check that subnet is within network
72+
if !(begin.Cmp(n.begin) >= 0 && end.Cmp(n.end) <= 0 && begin.Cmp(end) == -1) {
73+
return ErrBadSubnet
74+
}
75+
n.begin.Set(begin)
76+
n.end.Set(end)
77+
n.last.Sub(begin, big.NewInt(1))
78+
allocatedIPs[key] = n
79+
return nil
80+
}
81+
82+
// RequestIP requests an available ip from the given network. It
83+
// will return the next available ip if the ip provided is nil. If the
84+
// ip provided is not nil it will validate that the provided ip is available
85+
// for use or return an error
86+
func RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) {
87+
lock.Lock()
88+
defer lock.Unlock()
89+
key := network.String()
90+
allocated, ok := allocatedIPs[key]
91+
if !ok {
92+
allocated = newAllocatedMap(network)
93+
allocatedIPs[key] = allocated
94+
}
95+
96+
if ip == nil {
97+
return allocated.getNextIP()
98+
}
99+
return allocated.checkIP(ip)
100+
}
101+
102+
// ReleaseIP adds the provided ip back into the pool of
103+
// available ips to be returned for use.
104+
func ReleaseIP(network *net.IPNet, ip net.IP) error {
105+
lock.Lock()
106+
defer lock.Unlock()
107+
if allocated, exists := allocatedIPs[network.String()]; exists {
108+
delete(allocated.p, ip.String())
109+
}
110+
return nil
111+
}
112+
113+
func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) {
114+
if _, ok := allocated.p[ip.String()]; ok {
115+
return nil, ErrIPAlreadyAllocated
116+
}
117+
118+
pos := ipToBigInt(ip)
119+
// Verify that the IP address is within our network range.
120+
if pos.Cmp(allocated.begin) == -1 || pos.Cmp(allocated.end) == 1 {
121+
return nil, ErrIPOutOfRange
122+
}
123+
124+
// Register the IP.
125+
allocated.p[ip.String()] = struct{}{}
126+
127+
return ip, nil
128+
}
129+
130+
// return an available ip if one is currently available. If not,
131+
// return the next available ip for the nextwork
132+
func (allocated *allocatedMap) getNextIP() (net.IP, error) {
133+
pos := big.NewInt(0).Set(allocated.last)
134+
allRange := big.NewInt(0).Sub(allocated.end, allocated.begin)
135+
for i := big.NewInt(0); i.Cmp(allRange) <= 0; i.Add(i, big.NewInt(1)) {
136+
pos.Add(pos, big.NewInt(1))
137+
if pos.Cmp(allocated.end) == 1 {
138+
pos.Set(allocated.begin)
139+
}
140+
if _, ok := allocated.p[bigIntToIP(pos).String()]; ok {
141+
continue
142+
}
143+
allocated.p[bigIntToIP(pos).String()] = struct{}{}
144+
allocated.last.Set(pos)
145+
return bigIntToIP(pos), nil
146+
}
147+
return nil, ErrNoAvailableIPs
148+
}
149+
150+
// Converts a 4 bytes IP into a 128 bit integer
151+
func ipToBigInt(ip net.IP) *big.Int {
152+
x := big.NewInt(0)
153+
if ip4 := ip.To4(); ip4 != nil {
154+
return x.SetBytes(ip4)
155+
}
156+
if ip6 := ip.To16(); ip6 != nil {
157+
return x.SetBytes(ip6)
158+
}
159+
160+
log.Errorf("ipToBigInt: Wrong IP length! %s", ip)
161+
return nil
162+
}
163+
164+
// Converts 128 bit integer into a 4 bytes IP address
165+
func bigIntToIP(v *big.Int) net.IP {
166+
return net.IP(v.Bytes())
167+
}

0 commit comments

Comments
 (0)