Browse code

Refactor to support multiple ip addresses

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/01/24 00:46:42
Showing 2 changed files
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"fmt"
5 5
 	"github.com/dotcloud/docker/networkdriver"
6 6
 	"github.com/dotcloud/docker/networkdriver/ipallocator"
7
+	"github.com/dotcloud/docker/networkdriver/portallocator"
7 8
 	"github.com/dotcloud/docker/pkg/iptables"
8 9
 	"github.com/dotcloud/docker/pkg/netlink"
9 10
 	"github.com/dotcloud/docker/proxy"
... ...
@@ -20,8 +21,6 @@ const (
20 20
 	DefaultNetworkBridge = "docker0"
21 21
 	DisableNetworkBridge = "none"
22 22
 	DefaultNetworkMtu    = 1500
23
-	portRangeStart       = 49153
24
-	portRangeEnd         = 65535
25 23
 	siocBRADDBR          = 0x89a0
26 24
 )
27 25
 
... ...
@@ -3,11 +3,19 @@ package portallocator
3 3
 import (
4 4
 	"errors"
5 5
 	"github.com/dotcloud/docker/pkg/collections"
6
+	"net"
6 7
 	"sync"
7 8
 )
8 9
 
9 10
 type portMappings map[string]*collections.OrderedIntSet
10 11
 
12
+type ipData struct {
13
+	allocatedPorts portMappings
14
+	availablePorts portMappings
15
+}
16
+
17
+type ipMapping map[net.IP]*ipData
18
+
11 19
 const (
12 20
 	BeginPortRange = 49153
13 21
 	EndPortRange   = 65535
... ...
@@ -20,22 +28,62 @@ var (
20 20
 )
21 21
 
22 22
 var (
23
-	lock           = sync.Mutex{}
24
-	allocatedPorts = portMappings{}
25
-	availablePorts = portMappings{}
23
+	defaultIPData *ipData
24
+
25
+	lock      = sync.Mutex{}
26
+	ips       = ipMapping{}
27
+	defaultIP = net.ParseIP("0.0.0.0")
26 28
 )
27 29
 
28 30
 func init() {
29
-	allocatedPorts["udp"] = collections.NewOrderedIntSet()
30
-	availablePorts["udp"] = collections.NewOrderedIntSet()
31
-	allocatedPorts["tcp"] = collections.NewOrderedIntSet()
32
-	availablePorts["tcp"] = collections.NewOrderedIntSet()
31
+	defaultIPData = newIpData()
32
+	ips[defaultIP] = defaultIP
33
+}
34
+
35
+func newIpData() {
36
+	data := &ipData{
37
+		allocatedPorts: portMappings{},
38
+		availablePorts: portMappings{},
39
+	}
40
+
41
+	data.allocatedPorts["udp"] = collections.NewOrderedIntSet()
42
+	data.availablePorts["udp"] = collections.NewOrderedIntSet()
43
+	data.allocatedPorts["tcp"] = collections.NewOrderedIntSet()
44
+	data.availablePorts["tcp"] = collections.NewOrderedIntSet()
45
+
46
+	return data
47
+}
48
+
49
+func getData(ip net.IP) *ipData {
50
+	data, exists := ips[ip]
51
+	if !exists {
52
+		data = newIpData()
53
+		ips[ip] = data
54
+	}
55
+	return data
56
+}
57
+
58
+func validateMapping(data *ipData, proto string, port int) error {
59
+	allocated := data.allocatedPorts[proto]
60
+	if allocated.Exists(proto) {
61
+		return ErrPortAlreadyAllocated
62
+	}
63
+	return nil
64
+}
65
+
66
+func usePort(data *ipData, proto string, port int) {
67
+	allocated, available := data.allocatedPorts[proto], data.availablePorts[proto]
68
+	for i := 0; i < 2; i++ {
69
+		allocated.Push(port)
70
+		available.Remove(port)
71
+		allocated, available = defaultIPData.allocatedPorts[proto], defaultIPData.availablePorts[proto]
72
+	}
33 73
 }
34 74
 
35 75
 // RequestPort returns an available port if the port is 0
36 76
 // If the provided port is not 0 then it will be checked if
37 77
 // it is available for allocation
38
-func RequestPort(proto string, port int) (int, error) {
78
+func RequestPort(ip net.IP, proto string, port int) (int, error) {
39 79
 	lock.Lock()
40 80
 	defer lock.Unlock()
41 81
 
... ...
@@ -43,20 +91,28 @@ func RequestPort(proto string, port int) (int, error) {
43 43
 		return 0, err
44 44
 	}
45 45
 
46
-	var (
47
-		allocated = allocatedPorts[proto]
48
-		available = availablePorts[proto]
49
-	)
46
+	data := getData(ip)
47
+	allocated, available := data.allocatedPorts[proto], data.availablePorts[proto]
50 48
 
49
+	// If the user requested a specific port to be allocated
51 50
 	if port != 0 {
52
-		if allocated.Exists(port) {
53
-			return 0, ErrPortAlreadyAllocated
51
+		if err := validateMapping(defaultIP, proto, port); err != nil {
52
+			return 0, err
53
+		}
54
+
55
+		if !defaultIP.Equal(ip) {
56
+			if err := validateMapping(data, proto, port); err != nil {
57
+				return 0, err
58
+			}
54 59
 		}
60
+
55 61
 		available.Remove(port)
56 62
 		allocated.Push(port)
63
+
57 64
 		return port, nil
58 65
 	}
59 66
 
67
+	// Dynamic allocation
60 68
 	next := available.Pop()
61 69
 	if next == 0 {
62 70
 		next = allocated.PullBack()
... ...
@@ -76,7 +132,7 @@ func RequestPort(proto string, port int) (int, error) {
76 76
 
77 77
 // ReleasePort will return the provided port back into the
78 78
 // pool for reuse
79
-func ReleasePort(proto string, port int) error {
79
+func ReleasePort(ip net.IP, proto string, port int) error {
80 80
 	lock.Lock()
81 81
 	defer lock.Unlock()
82 82
 
... ...
@@ -84,10 +140,7 @@ func ReleasePort(proto string, port int) error {
84 84
 		return err
85 85
 	}
86 86
 
87
-	var (
88
-		allocated = allocatedPorts[proto]
89
-		available = availablePorts[proto]
90
-	)
87
+	allocated, available := getCollection(ip, proto)
91 88
 
92 89
 	allocated.Remove(port)
93 90
 	available.Push(port)