Browse code

Simplify logic for registering ports

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

Michael Crosby authored on 2014/01/24 05:17:28
Showing 5 changed files
... ...
@@ -12,7 +12,6 @@ import (
12 12
 	"log"
13 13
 	"net"
14 14
 	"strconv"
15
-	"sync"
16 15
 	"syscall"
17 16
 	"unsafe"
18 17
 )
... ...
@@ -282,76 +281,6 @@ func newPortMapper(config *DaemonConfig) (*PortMapper, error) {
282 282
 	return mapper, nil
283 283
 }
284 284
 
285
-// Port allocator: Automatically allocate and release networking ports
286
-type PortAllocator struct {
287
-	sync.Mutex
288
-	inUse    map[string]struct{}
289
-	fountain chan int
290
-	quit     chan bool
291
-}
292
-
293
-func (alloc *PortAllocator) runFountain() {
294
-	for {
295
-		for port := portRangeStart; port < portRangeEnd; port++ {
296
-			select {
297
-			case alloc.fountain <- port:
298
-			case quit := <-alloc.quit:
299
-				if quit {
300
-					return
301
-				}
302
-			}
303
-		}
304
-	}
305
-}
306
-
307
-// FIXME: Release can no longer fail, change its prototype to reflect that.
308
-func (alloc *PortAllocator) Release(addr net.IP, port int) error {
309
-	mapKey := (&net.TCPAddr{Port: port, IP: addr}).String()
310
-	utils.Debugf("Releasing %d", port)
311
-	alloc.Lock()
312
-	delete(alloc.inUse, mapKey)
313
-	alloc.Unlock()
314
-	return nil
315
-}
316
-
317
-func (alloc *PortAllocator) Acquire(addr net.IP, port int) (int, error) {
318
-	mapKey := (&net.TCPAddr{Port: port, IP: addr}).String()
319
-	utils.Debugf("Acquiring %s", mapKey)
320
-	if port == 0 {
321
-		// Allocate a port from the fountain
322
-		for port := range alloc.fountain {
323
-			if _, err := alloc.Acquire(addr, port); err == nil {
324
-				return port, nil
325
-			}
326
-		}
327
-		return -1, fmt.Errorf("Port generator ended unexpectedly")
328
-	}
329
-	alloc.Lock()
330
-	defer alloc.Unlock()
331
-	if _, inUse := alloc.inUse[mapKey]; inUse {
332
-		return -1, fmt.Errorf("Port already in use: %d", port)
333
-	}
334
-	alloc.inUse[mapKey] = struct{}{}
335
-	return port, nil
336
-}
337
-
338
-func (alloc *PortAllocator) Close() error {
339
-	alloc.quit <- true
340
-	close(alloc.quit)
341
-	close(alloc.fountain)
342
-	return nil
343
-}
344
-
345
-func newPortAllocator() (*PortAllocator, error) {
346
-	allocator := &PortAllocator{
347
-		inUse:    make(map[string]struct{}),
348
-		fountain: make(chan int),
349
-		quit:     make(chan bool),
350
-	}
351
-	go allocator.runFountain()
352
-	return allocator, nil
353
-}
354
-
355 285
 // Network interface represents the networking stack of a container
356 286
 type NetworkInterface struct {
357 287
 	IPNet   net.IPNet
... ...
@@ -389,30 +318,24 @@ func (iface *NetworkInterface) AllocatePort(port Port, binding PortBinding) (*Na
389 389
 
390 390
 	hostPort, _ := parsePort(nat.Binding.HostPort)
391 391
 
392
-	if nat.Port.Proto() == "tcp" {
393
-		extPort, err := iface.manager.tcpPortAllocator.Acquire(ip, hostPort)
394
-		if err != nil {
395
-			return nil, err
396
-		}
392
+	extPort, err := portallocator.RequestPort(ip, nat.Port.Proto(), hostPort)
393
+	if err != nil {
394
+		return nil, err
395
+	}
397 396
 
398
-		backend := &net.TCPAddr{IP: iface.IPNet.IP, Port: containerPort}
399
-		if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
400
-			iface.manager.tcpPortAllocator.Release(ip, extPort)
401
-			return nil, err
402
-		}
403
-		nat.Binding.HostPort = strconv.Itoa(extPort)
397
+	var backend net.Addr
398
+	if nat.Port.Proto() == "tcp" {
399
+		backend = &net.TCPAddr{IP: iface.IPNet.IP, Port: containerPort}
404 400
 	} else {
405
-		extPort, err := iface.manager.udpPortAllocator.Acquire(ip, hostPort)
406
-		if err != nil {
407
-			return nil, err
408
-		}
409
-		backend := &net.UDPAddr{IP: iface.IPNet.IP, Port: containerPort}
410
-		if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
411
-			iface.manager.udpPortAllocator.Release(ip, extPort)
412
-			return nil, err
413
-		}
414
-		nat.Binding.HostPort = strconv.Itoa(extPort)
401
+		backend = &net.UDPAddr{IP: iface.IPNet.IP, Port: containerPort}
415 402
 	}
403
+
404
+	if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
405
+		portallocator.ReleasePort(ip, nat.Port.Proto(), extPort)
406
+		return nil, err
407
+	}
408
+
409
+	nat.Binding.HostPort = strconv.Itoa(extPort)
416 410
 	iface.extPorts = append(iface.extPorts, nat)
417 411
 
418 412
 	return nat, nil
... ...
@@ -445,14 +368,8 @@ func (iface *NetworkInterface) Release() {
445 445
 			log.Printf("Unable to unmap port %s: %s", nat, err)
446 446
 		}
447 447
 
448
-		if nat.Port.Proto() == "tcp" {
449
-			if err := iface.manager.tcpPortAllocator.Release(ip, hostPort); err != nil {
450
-				log.Printf("Unable to release port %s", nat)
451
-			}
452
-		} else if nat.Port.Proto() == "udp" {
453
-			if err := iface.manager.udpPortAllocator.Release(ip, hostPort); err != nil {
454
-				log.Printf("Unable to release port %s: %s", nat, err)
455
-			}
448
+		if err := portallocator.ReleasePort(ip, nat.Port.Proto(), hostPort); err != nil {
449
+			log.Printf("Unable to release port %s", nat)
456 450
 		}
457 451
 	}
458 452
 
... ...
@@ -467,9 +384,7 @@ type NetworkManager struct {
467 467
 	bridgeIface   string
468 468
 	bridgeNetwork *net.IPNet
469 469
 
470
-	tcpPortAllocator *PortAllocator
471
-	udpPortAllocator *PortAllocator
472
-	portMapper       *PortMapper
470
+	portMapper *PortMapper
473 471
 
474 472
 	disabled bool
475 473
 }
... ...
@@ -497,21 +412,6 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) {
497 497
 	return iface, nil
498 498
 }
499 499
 
500
-func (manager *NetworkManager) Close() error {
501
-	if manager.disabled {
502
-		return nil
503
-	}
504
-	err1 := manager.tcpPortAllocator.Close()
505
-	err2 := manager.udpPortAllocator.Close()
506
-	if err1 != nil {
507
-		return err1
508
-	}
509
-	if err2 != nil {
510
-		return err2
511
-	}
512
-	return nil
513
-}
514
-
515 500
 func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) {
516 501
 	if config.BridgeIface == DisableNetworkBridge {
517 502
 		manager := &NetworkManager{
... ...
@@ -599,27 +499,15 @@ func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) {
599 599
 		}
600 600
 	}
601 601
 
602
-	tcpPortAllocator, err := newPortAllocator()
603
-	if err != nil {
604
-		return nil, err
605
-	}
606
-
607
-	udpPortAllocator, err := newPortAllocator()
608
-	if err != nil {
609
-		return nil, err
610
-	}
611
-
612 602
 	portMapper, err := newPortMapper(config)
613 603
 	if err != nil {
614 604
 		return nil, err
615 605
 	}
616 606
 
617 607
 	manager := &NetworkManager{
618
-		bridgeIface:      config.BridgeIface,
619
-		bridgeNetwork:    network,
620
-		tcpPortAllocator: tcpPortAllocator,
621
-		udpPortAllocator: udpPortAllocator,
622
-		portMapper:       portMapper,
608
+		bridgeIface:   config.BridgeIface,
609
+		bridgeNetwork: network,
610
+		portMapper:    portMapper,
623 611
 	}
624 612
 
625 613
 	return manager, nil
... ...
@@ -7,50 +7,6 @@ import (
7 7
 	"testing"
8 8
 )
9 9
 
10
-func TestPortAllocation(t *testing.T) {
11
-	ip := net.ParseIP("192.168.0.1")
12
-	ip2 := net.ParseIP("192.168.0.2")
13
-	allocator, err := newPortAllocator()
14
-	if err != nil {
15
-		t.Fatal(err)
16
-	}
17
-	if port, err := allocator.Acquire(ip, 80); err != nil {
18
-		t.Fatal(err)
19
-	} else if port != 80 {
20
-		t.Fatalf("Acquire(80) should return 80, not %d", port)
21
-	}
22
-	port, err := allocator.Acquire(ip, 0)
23
-	if err != nil {
24
-		t.Fatal(err)
25
-	}
26
-	if port <= 0 {
27
-		t.Fatalf("Acquire(0) should return a non-zero port")
28
-	}
29
-	if _, err := allocator.Acquire(ip, port); err == nil {
30
-		t.Fatalf("Acquiring a port already in use should return an error")
31
-	}
32
-	if newPort, err := allocator.Acquire(ip, 0); err != nil {
33
-		t.Fatal(err)
34
-	} else if newPort == port {
35
-		t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
36
-	}
37
-	if _, err := allocator.Acquire(ip, 80); err == nil {
38
-		t.Fatalf("Acquiring a port already in use should return an error")
39
-	}
40
-	if _, err := allocator.Acquire(ip2, 80); err != nil {
41
-		t.Fatalf("It should be possible to allocate the same port on a different interface")
42
-	}
43
-	if _, err := allocator.Acquire(ip2, 80); err == nil {
44
-		t.Fatalf("Acquiring a port already in use should return an error")
45
-	}
46
-	if err := allocator.Release(ip, 80); err != nil {
47
-		t.Fatal(err)
48
-	}
49
-	if _, err := allocator.Acquire(ip, 80); err != nil {
50
-		t.Fatal(err)
51
-	}
52
-}
53
-
54 10
 type StubProxy struct {
55 11
 	frontendAddr *net.Addr
56 12
 	backendAddr  *net.Addr
... ...
@@ -7,20 +7,16 @@ import (
7 7
 	"sync"
8 8
 )
9 9
 
10
-type portMappings map[string]*collections.OrderedIntSet
11
-
12
-type ipData struct {
13
-	allocatedPorts portMappings
14
-	availablePorts portMappings
15
-}
16
-
17
-type ipMapping map[net.IP]*ipData
18
-
19 10
 const (
20 11
 	BeginPortRange = 49153
21 12
 	EndPortRange   = 65535
22 13
 )
23 14
 
15
+type (
16
+	portMappings map[string]*collections.OrderedIntSet
17
+	ipMapping    map[string]portMappings
18
+)
19
+
24 20
 var (
25 21
 	ErrPortAlreadyAllocated = errors.New("port has already been allocated")
26 22
 	ErrPortExceedsRange     = errors.New("port exceeds upper range")
... ...
@@ -28,56 +24,19 @@ var (
28 28
 )
29 29
 
30 30
 var (
31
-	defaultIPData *ipData
32
-
33
-	lock      = sync.Mutex{}
34
-	ips       = ipMapping{}
35
-	defaultIP = net.ParseIP("0.0.0.0")
31
+	currentDynamicPort = map[string]int{
32
+		"tcp": BeginPortRange - 1,
33
+		"udp": BeginPortRange - 1,
34
+	}
35
+	defaultIP             = net.ParseIP("0.0.0.0")
36
+	defaultAllocatedPorts = portMappings{}
37
+	otherAllocatedPorts   = ipMapping{}
38
+	lock                  = sync.Mutex{}
36 39
 )
37 40
 
38 41
 func init() {
39
-	defaultIPData = newIpData()
40
-	ips[defaultIP] = defaultIP
41
-}
42
-
43
-func newIpData() {
44
-	data := &ipData{
45
-		allocatedPorts: portMappings{},
46
-		availablePorts: portMappings{},
47
-	}
48
-
49
-	data.allocatedPorts["udp"] = collections.NewOrderedIntSet()
50
-	data.availablePorts["udp"] = collections.NewOrderedIntSet()
51
-	data.allocatedPorts["tcp"] = collections.NewOrderedIntSet()
52
-	data.availablePorts["tcp"] = collections.NewOrderedIntSet()
53
-
54
-	return data
55
-}
56
-
57
-func getData(ip net.IP) *ipData {
58
-	data, exists := ips[ip]
59
-	if !exists {
60
-		data = newIpData()
61
-		ips[ip] = data
62
-	}
63
-	return data
64
-}
65
-
66
-func validateMapping(data *ipData, proto string, port int) error {
67
-	allocated := data.allocatedPorts[proto]
68
-	if allocated.Exists(proto) {
69
-		return ErrPortAlreadyAllocated
70
-	}
71
-	return nil
72
-}
73
-
74
-func usePort(data *ipData, proto string, port int) {
75
-	allocated, available := data.allocatedPorts[proto], data.availablePorts[proto]
76
-	for i := 0; i < 2; i++ {
77
-		allocated.Push(port)
78
-		available.Remove(port)
79
-		allocated, available = defaultIPData.allocatedPorts[proto], defaultIPData.availablePorts[proto]
80
-	}
42
+	defaultAllocatedPorts["tcp"] = collections.NewOrderedIntSet()
43
+	defaultAllocatedPorts["udp"] = collections.NewOrderedIntSet()
81 44
 }
82 45
 
83 46
 // RequestPort returns an available port if the port is 0
... ...
@@ -91,43 +50,14 @@ func RequestPort(ip net.IP, proto string, port int) (int, error) {
91 91
 		return 0, err
92 92
 	}
93 93
 
94
-	data := getData(ip)
95
-	allocated, available := data.allocatedPorts[proto], data.availablePorts[proto]
96
-
97 94
 	// If the user requested a specific port to be allocated
98 95
 	if port != 0 {
99
-		if err := validateMapping(defaultIP, proto, port); err != nil {
96
+		if err := registerSetPort(ip, proto, port); err != nil {
100 97
 			return 0, err
101 98
 		}
102
-
103
-		if !defaultIP.Equal(ip) {
104
-			if err := validateMapping(data, proto, port); err != nil {
105
-				return 0, err
106
-			}
107
-		}
108
-
109
-		available.Remove(port)
110
-		allocated.Push(port)
111
-
112 99
 		return port, nil
113 100
 	}
114
-
115
-	// Dynamic allocation
116
-	next := available.Pop()
117
-	if next == 0 {
118
-		next = allocated.PullBack()
119
-		if next == 0 {
120
-			next = BeginPortRange
121
-		} else {
122
-			next++
123
-		}
124
-		if next > EndPortRange {
125
-			return 0, ErrPortExceedsRange
126
-		}
127
-	}
128
-
129
-	allocated.Push(next)
130
-	return next, nil
101
+	return registerDynamicPort(ip, proto)
131 102
 }
132 103
 
133 104
 // ReleasePort will return the provided port back into the
... ...
@@ -140,16 +70,95 @@ func ReleasePort(ip net.IP, proto string, port int) error {
140 140
 		return err
141 141
 	}
142 142
 
143
-	allocated, available := getCollection(ip, proto)
144
-
143
+	allocated := defaultAllocatedPorts[proto]
145 144
 	allocated.Remove(port)
146
-	available.Push(port)
147 145
 
146
+	if !equalsDefault(ip) {
147
+		registerIP(ip)
148
+
149
+		// Remove the port for the specific ip address
150
+		allocated = otherAllocatedPorts[ip.String()][proto]
151
+		allocated.Remove(port)
152
+	}
148 153
 	return nil
149 154
 }
150 155
 
156
+func ReleaseAll() error {
157
+	lock.Lock()
158
+	defer lock.Unlock()
159
+
160
+	currentDynamicPort["tcp"] = BeginPortRange - 1
161
+	currentDynamicPort["udp"] = BeginPortRange - 1
162
+
163
+	defaultAllocatedPorts = portMappings{}
164
+	defaultAllocatedPorts["tcp"] = collections.NewOrderedIntSet()
165
+	defaultAllocatedPorts["udp"] = collections.NewOrderedIntSet()
166
+
167
+	otherAllocatedPorts = ipMapping{}
168
+
169
+	return nil
170
+}
171
+
172
+func registerDynamicPort(ip net.IP, proto string) (int, error) {
173
+	allocated := defaultAllocatedPorts[proto]
174
+
175
+	port := nextPort(proto)
176
+	if port > EndPortRange {
177
+		return 0, ErrPortExceedsRange
178
+	}
179
+
180
+	if !equalsDefault(ip) {
181
+		registerIP(ip)
182
+
183
+		ipAllocated := otherAllocatedPorts[ip.String()][proto]
184
+		ipAllocated.Push(port)
185
+	} else {
186
+		allocated.Push(port)
187
+	}
188
+	return port, nil
189
+}
190
+
191
+func registerSetPort(ip net.IP, proto string, port int) error {
192
+	allocated := defaultAllocatedPorts[proto]
193
+	if allocated.Exists(port) {
194
+		return ErrPortAlreadyAllocated
195
+	}
196
+
197
+	if !equalsDefault(ip) {
198
+		registerIP(ip)
199
+
200
+		ipAllocated := otherAllocatedPorts[ip.String()][proto]
201
+		if ipAllocated.Exists(port) {
202
+			return ErrPortAlreadyAllocated
203
+		}
204
+		ipAllocated.Push(port)
205
+	} else {
206
+		allocated.Push(port)
207
+	}
208
+	return nil
209
+}
210
+
211
+func equalsDefault(ip net.IP) bool {
212
+	return ip == nil || ip.Equal(defaultIP)
213
+}
214
+
215
+func nextPort(proto string) int {
216
+	c := currentDynamicPort[proto] + 1
217
+	currentDynamicPort[proto] = c
218
+	return c
219
+}
220
+
221
+func registerIP(ip net.IP) {
222
+	if _, exists := otherAllocatedPorts[ip.String()]; !exists {
223
+		otherAllocatedPorts[ip.String()] = portMappings{
224
+			"tcp": collections.NewOrderedIntSet(),
225
+			"udp": collections.NewOrderedIntSet(),
226
+		}
227
+	}
228
+}
229
+
151 230
 func validateProtocol(proto string) error {
152
-	if _, exists := allocatedPorts[proto]; !exists {
231
+	if _, exists := defaultAllocatedPorts[proto]; !exists {
153 232
 		return ErrUnknownProtocol
154 233
 	}
155 234
 	return nil
... ...
@@ -1,27 +1,18 @@
1 1
 package portallocator
2 2
 
3 3
 import (
4
-	"github.com/dotcloud/docker/pkg/collections"
4
+	"net"
5 5
 	"testing"
6 6
 )
7 7
 
8 8
 func reset() {
9
-	lock.Lock()
10
-	defer lock.Unlock()
11
-
12
-	allocatedPorts = portMappings{}
13
-	availablePorts = portMappings{}
14
-
15
-	allocatedPorts["udp"] = collections.NewOrderedIntSet()
16
-	availablePorts["udp"] = collections.NewOrderedIntSet()
17
-	allocatedPorts["tcp"] = collections.NewOrderedIntSet()
18
-	availablePorts["tcp"] = collections.NewOrderedIntSet()
9
+	ReleaseAll()
19 10
 }
20 11
 
21 12
 func TestRequestNewPort(t *testing.T) {
22 13
 	defer reset()
23 14
 
24
-	port, err := RequestPort("tcp", 0)
15
+	port, err := RequestPort(defaultIP, "tcp", 0)
25 16
 	if err != nil {
26 17
 		t.Fatal(err)
27 18
 	}
... ...
@@ -34,7 +25,7 @@ func TestRequestNewPort(t *testing.T) {
34 34
 func TestRequestSpecificPort(t *testing.T) {
35 35
 	defer reset()
36 36
 
37
-	port, err := RequestPort("tcp", 5000)
37
+	port, err := RequestPort(defaultIP, "tcp", 5000)
38 38
 	if err != nil {
39 39
 		t.Fatal(err)
40 40
 	}
... ...
@@ -46,7 +37,7 @@ func TestRequestSpecificPort(t *testing.T) {
46 46
 func TestReleasePort(t *testing.T) {
47 47
 	defer reset()
48 48
 
49
-	port, err := RequestPort("tcp", 5000)
49
+	port, err := RequestPort(defaultIP, "tcp", 5000)
50 50
 	if err != nil {
51 51
 		t.Fatal(err)
52 52
 	}
... ...
@@ -54,7 +45,7 @@ func TestReleasePort(t *testing.T) {
54 54
 		t.Fatalf("Expected port 5000 got %d", port)
55 55
 	}
56 56
 
57
-	if err := ReleasePort("tcp", 5000); err != nil {
57
+	if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
58 58
 		t.Fatal(err)
59 59
 	}
60 60
 }
... ...
@@ -62,7 +53,7 @@ func TestReleasePort(t *testing.T) {
62 62
 func TestReuseReleasedPort(t *testing.T) {
63 63
 	defer reset()
64 64
 
65
-	port, err := RequestPort("tcp", 5000)
65
+	port, err := RequestPort(defaultIP, "tcp", 5000)
66 66
 	if err != nil {
67 67
 		t.Fatal(err)
68 68
 	}
... ...
@@ -70,11 +61,11 @@ func TestReuseReleasedPort(t *testing.T) {
70 70
 		t.Fatalf("Expected port 5000 got %d", port)
71 71
 	}
72 72
 
73
-	if err := ReleasePort("tcp", 5000); err != nil {
73
+	if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
74 74
 		t.Fatal(err)
75 75
 	}
76 76
 
77
-	port, err = RequestPort("tcp", 5000)
77
+	port, err = RequestPort(defaultIP, "tcp", 5000)
78 78
 	if err != nil {
79 79
 		t.Fatal(err)
80 80
 	}
... ...
@@ -83,7 +74,7 @@ func TestReuseReleasedPort(t *testing.T) {
83 83
 func TestReleaseUnreadledPort(t *testing.T) {
84 84
 	defer reset()
85 85
 
86
-	port, err := RequestPort("tcp", 5000)
86
+	port, err := RequestPort(defaultIP, "tcp", 5000)
87 87
 	if err != nil {
88 88
 		t.Fatal(err)
89 89
 	}
... ...
@@ -91,7 +82,7 @@ func TestReleaseUnreadledPort(t *testing.T) {
91 91
 		t.Fatalf("Expected port 5000 got %d", port)
92 92
 	}
93 93
 
94
-	port, err = RequestPort("tcp", 5000)
94
+	port, err = RequestPort(defaultIP, "tcp", 5000)
95 95
 	if err != ErrPortAlreadyAllocated {
96 96
 		t.Fatalf("Expected error %s got %s", ErrPortAlreadyAllocated, err)
97 97
 	}
... ...
@@ -100,7 +91,7 @@ func TestReleaseUnreadledPort(t *testing.T) {
100 100
 func TestUnknowProtocol(t *testing.T) {
101 101
 	defer reset()
102 102
 
103
-	if _, err := RequestPort("tcpp", 0); err != ErrUnknownProtocol {
103
+	if _, err := RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol {
104 104
 		t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err)
105 105
 	}
106 106
 }
... ...
@@ -109,7 +100,7 @@ func TestAllocateAllPorts(t *testing.T) {
109 109
 	defer reset()
110 110
 
111 111
 	for i := 0; i <= EndPortRange-BeginPortRange; i++ {
112
-		port, err := RequestPort("tcp", 0)
112
+		port, err := RequestPort(defaultIP, "tcp", 0)
113 113
 		if err != nil {
114 114
 			t.Fatal(err)
115 115
 		}
... ...
@@ -119,11 +110,11 @@ func TestAllocateAllPorts(t *testing.T) {
119 119
 		}
120 120
 	}
121 121
 
122
-	if _, err := RequestPort("tcp", 0); err != ErrPortExceedsRange {
122
+	if _, err := RequestPort(defaultIP, "tcp", 0); err != ErrPortExceedsRange {
123 123
 		t.Fatalf("Expected error %s got %s", ErrPortExceedsRange, err)
124 124
 	}
125 125
 
126
-	_, err := RequestPort("udp", 0)
126
+	_, err := RequestPort(defaultIP, "udp", 0)
127 127
 	if err != nil {
128 128
 		t.Fatal(err)
129 129
 	}
... ...
@@ -132,10 +123,9 @@ func TestAllocateAllPorts(t *testing.T) {
132 132
 func BenchmarkAllocatePorts(b *testing.B) {
133 133
 	defer reset()
134 134
 
135
-	b.StartTimer()
136 135
 	for i := 0; i < b.N; i++ {
137 136
 		for i := 0; i <= EndPortRange-BeginPortRange; i++ {
138
-			port, err := RequestPort("tcp", 0)
137
+			port, err := RequestPort(defaultIP, "tcp", 0)
139 138
 			if err != nil {
140 139
 				b.Fatal(err)
141 140
 			}
... ...
@@ -146,5 +136,49 @@ func BenchmarkAllocatePorts(b *testing.B) {
146 146
 		}
147 147
 		reset()
148 148
 	}
149
-	b.StopTimer()
149
+}
150
+
151
+func TestPortAllocation(t *testing.T) {
152
+	defer reset()
153
+
154
+	ip := net.ParseIP("192.168.0.1")
155
+	ip2 := net.ParseIP("192.168.0.2")
156
+	if port, err := RequestPort(ip, "tcp", 80); err != nil {
157
+		t.Fatal(err)
158
+	} else if port != 80 {
159
+		t.Fatalf("Acquire(80) should return 80, not %d", port)
160
+	}
161
+	port, err := RequestPort(ip, "tcp", 0)
162
+	if err != nil {
163
+		t.Fatal(err)
164
+	}
165
+	if port <= 0 {
166
+		t.Fatalf("Acquire(0) should return a non-zero port")
167
+	}
168
+
169
+	if _, err := RequestPort(ip, "tcp", port); err == nil {
170
+		t.Fatalf("Acquiring a port already in use should return an error")
171
+	}
172
+
173
+	if newPort, err := RequestPort(ip, "tcp", 0); err != nil {
174
+		t.Fatal(err)
175
+	} else if newPort == port {
176
+		t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
177
+	}
178
+
179
+	if _, err := RequestPort(ip, "tcp", 80); err == nil {
180
+		t.Fatalf("Acquiring a port already in use should return an error")
181
+	}
182
+	if _, err := RequestPort(ip2, "tcp", 80); err != nil {
183
+		t.Fatalf("It should be possible to allocate the same port on a different interface")
184
+	}
185
+	if _, err := RequestPort(ip2, "tcp", 80); err == nil {
186
+		t.Fatalf("Acquiring a port already in use should return an error")
187
+	}
188
+	if err := ReleasePort(ip, "tcp", 80); err != nil {
189
+		t.Fatal(err)
190
+	}
191
+	if _, err := RequestPort(ip, "tcp", 80); err != nil {
192
+		t.Fatal(err)
193
+	}
150 194
 }
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"github.com/dotcloud/docker/graphdriver/aufs"
12 12
 	_ "github.com/dotcloud/docker/graphdriver/devmapper"
13 13
 	_ "github.com/dotcloud/docker/graphdriver/vfs"
14
+	"github.com/dotcloud/docker/networkdriver/portallocator"
14 15
 	"github.com/dotcloud/docker/pkg/graphdb"
15 16
 	"github.com/dotcloud/docker/pkg/sysinfo"
16 17
 	"github.com/dotcloud/docker/utils"
... ...
@@ -740,8 +741,8 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
740 740
 
741 741
 func (runtime *Runtime) Close() error {
742 742
 	errorsStrings := []string{}
743
-	if err := runtime.networkManager.Close(); err != nil {
744
-		utils.Errorf("runtime.networkManager.Close(): %s", err.Error())
743
+	if err := portallocator.ReleaseAll(); err != nil {
744
+		utils.Errorf("portallocator.ReleaseAll(): %s", err)
745 745
 		errorsStrings = append(errorsStrings, err.Error())
746 746
 	}
747 747
 	if err := runtime.driver.Cleanup(); err != nil {