Skip to content

Commit 7fc65f0

Browse files
committed
Merge pull request #34 from aboch/portmapper
Issue #33: Move portmapper and portallocator into libnetwork
2 parents 9c01fc7 + c0474b6 commit 7fc65f0

File tree

7 files changed

+923
-3
lines changed

7 files changed

+923
-3
lines changed

drivers/bridge/setup_ip_tables.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import (
44
"fmt"
55
"net"
66

7-
"github.com/docker/docker/daemon/networkdriver"
8-
"github.com/docker/docker/daemon/networkdriver/portmapper"
97
"github.com/docker/docker/pkg/iptables"
8+
"github.com/docker/libnetwork"
9+
"github.com/docker/libnetwork/portmapper"
1010
)
1111

1212
// DockerChain: DOCKER iptable chain name
@@ -20,7 +20,7 @@ func setupIPTables(i *bridgeInterface) error {
2020
return fmt.Errorf("Unexpected request to set IP tables for interface: %s", i.Config.BridgeName)
2121
}
2222

23-
addrv4, _, err := networkdriver.GetIfaceAddr(i.Config.BridgeName)
23+
addrv4, _, err := libnetwork.GetIfaceAddr(i.Config.BridgeName)
2424
if err != nil {
2525
return fmt.Errorf("Failed to setup IP tables, cannot acquire Interface address: %s", err.Error())
2626
}

portallocator/portallocator.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package portallocator
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net"
7+
"sync"
8+
)
9+
10+
type portMap struct {
11+
p map[int]struct{}
12+
last int
13+
}
14+
15+
func newPortMap() *portMap {
16+
return &portMap{
17+
p: map[int]struct{}{},
18+
last: EndPortRange,
19+
}
20+
}
21+
22+
type protoMap map[string]*portMap
23+
24+
func newProtoMap() protoMap {
25+
return protoMap{
26+
"tcp": newPortMap(),
27+
"udp": newPortMap(),
28+
}
29+
}
30+
31+
type ipMapping map[string]protoMap
32+
33+
const (
34+
// BeginPortRange indicates the first port in port range
35+
BeginPortRange = 49153
36+
// EndPortRange indicates the last port in port range
37+
EndPortRange = 65535
38+
)
39+
40+
var (
41+
// ErrAllPortsAllocated is returned when no more ports are available
42+
ErrAllPortsAllocated = errors.New("all ports are allocated")
43+
// ErrUnknownProtocol is returned when an unknown protocol was specified
44+
ErrUnknownProtocol = errors.New("unknown protocol")
45+
)
46+
47+
var (
48+
mutex sync.Mutex
49+
50+
defaultIP = net.ParseIP("0.0.0.0")
51+
globalMap = ipMapping{}
52+
)
53+
54+
// ErrPortAlreadyAllocated is the returned error information when a requested port is already being used
55+
type ErrPortAlreadyAllocated struct {
56+
ip string
57+
port int
58+
}
59+
60+
func newErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated {
61+
return ErrPortAlreadyAllocated{
62+
ip: ip,
63+
port: port,
64+
}
65+
}
66+
67+
// IP returns the address to which the used port is associated
68+
func (e ErrPortAlreadyAllocated) IP() string {
69+
return e.ip
70+
}
71+
72+
// Port returns the value of the already used port
73+
func (e ErrPortAlreadyAllocated) Port() int {
74+
return e.port
75+
}
76+
77+
// IPPort returns the address and the port in the form ip:port
78+
func (e ErrPortAlreadyAllocated) IPPort() string {
79+
return fmt.Sprintf("%s:%d", e.ip, e.port)
80+
}
81+
82+
// Error is the implementation of error.Error interface
83+
func (e ErrPortAlreadyAllocated) Error() string {
84+
return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
85+
}
86+
87+
// RequestPort requests new port from global ports pool for specified ip and proto.
88+
// If port is 0 it returns first free port. Otherwise it cheks port availability
89+
// in pool and return that port or error if port is already busy.
90+
func RequestPort(ip net.IP, proto string, port int) (int, error) {
91+
mutex.Lock()
92+
defer mutex.Unlock()
93+
94+
if proto != "tcp" && proto != "udp" {
95+
return 0, ErrUnknownProtocol
96+
}
97+
98+
if ip == nil {
99+
ip = defaultIP
100+
}
101+
ipstr := ip.String()
102+
protomap, ok := globalMap[ipstr]
103+
if !ok {
104+
protomap = newProtoMap()
105+
globalMap[ipstr] = protomap
106+
}
107+
mapping := protomap[proto]
108+
if port > 0 {
109+
if _, ok := mapping.p[port]; !ok {
110+
mapping.p[port] = struct{}{}
111+
return port, nil
112+
}
113+
return 0, newErrPortAlreadyAllocated(ipstr, port)
114+
}
115+
116+
port, err := mapping.findPort()
117+
if err != nil {
118+
return 0, err
119+
}
120+
return port, nil
121+
}
122+
123+
// ReleasePort releases port from global ports pool for specified ip and proto.
124+
func ReleasePort(ip net.IP, proto string, port int) error {
125+
mutex.Lock()
126+
defer mutex.Unlock()
127+
128+
if ip == nil {
129+
ip = defaultIP
130+
}
131+
protomap, ok := globalMap[ip.String()]
132+
if !ok {
133+
return nil
134+
}
135+
delete(protomap[proto].p, port)
136+
return nil
137+
}
138+
139+
// ReleaseAll releases all ports for all ips.
140+
func ReleaseAll() error {
141+
mutex.Lock()
142+
globalMap = ipMapping{}
143+
mutex.Unlock()
144+
return nil
145+
}
146+
147+
func (pm *portMap) findPort() (int, error) {
148+
port := pm.last
149+
for i := 0; i <= EndPortRange-BeginPortRange; i++ {
150+
port++
151+
if port > EndPortRange {
152+
port = BeginPortRange
153+
}
154+
155+
if _, ok := pm.p[port]; !ok {
156+
pm.p[port] = struct{}{}
157+
pm.last = port
158+
return port, nil
159+
}
160+
}
161+
return 0, ErrAllPortsAllocated
162+
}

0 commit comments

Comments
 (0)