Faster, more documented, less code.
Docker-DCO-1.1-Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com> (github: LK4D4)
| ... | ... |
@@ -12,10 +12,22 @@ type portMap struct {
|
| 12 | 12 |
last int |
| 13 | 13 |
} |
| 14 | 14 |
|
| 15 |
-type ( |
|
| 16 |
- protocolMap map[string]*portMap |
|
| 17 |
- ipMapping map[string]protocolMap |
|
| 18 |
-) |
|
| 15 |
+func newPortMap() *portMap {
|
|
| 16 |
+ return &portMap{
|
|
| 17 |
+ p: map[int]struct{}{},
|
|
| 18 |
+ } |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+type protoMap map[string]*portMap |
|
| 22 |
+ |
|
| 23 |
+func newProtoMap() protoMap {
|
|
| 24 |
+ return protoMap{
|
|
| 25 |
+ "tcp": newPortMap(), |
|
| 26 |
+ "udp": newPortMap(), |
|
| 27 |
+ } |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+type ipMapping map[string]protoMap |
|
| 19 | 31 |
|
| 20 | 32 |
const ( |
| 21 | 33 |
BeginPortRange = 49153 |
| ... | ... |
@@ -62,107 +74,83 @@ func (e ErrPortAlreadyAllocated) Error() string {
|
| 62 | 62 |
return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
|
| 63 | 63 |
} |
| 64 | 64 |
|
| 65 |
+// RequestPort requests new port from global ports pool for specified ip and proto. |
|
| 66 |
+// If port is 0 it returns first free port. Otherwise it cheks port availability |
|
| 67 |
+// in pool and return that port or error if port is already busy. |
|
| 65 | 68 |
func RequestPort(ip net.IP, proto string, port int) (int, error) {
|
| 66 | 69 |
mutex.Lock() |
| 67 | 70 |
defer mutex.Unlock() |
| 68 | 71 |
|
| 69 |
- if err := validateProto(proto); err != nil {
|
|
| 70 |
- return 0, err |
|
| 72 |
+ if proto != "tcp" && proto != "udp" {
|
|
| 73 |
+ return 0, ErrUnknownProtocol |
|
| 71 | 74 |
} |
| 72 | 75 |
|
| 73 |
- ip = getDefault(ip) |
|
| 74 |
- |
|
| 75 |
- mapping := getOrCreate(ip) |
|
| 76 |
- |
|
| 76 |
+ if ip == nil {
|
|
| 77 |
+ ip = defaultIP |
|
| 78 |
+ } |
|
| 79 |
+ ipstr := ip.String() |
|
| 80 |
+ protomap, ok := globalMap[ipstr] |
|
| 81 |
+ if !ok {
|
|
| 82 |
+ protomap = newProtoMap() |
|
| 83 |
+ globalMap[ipstr] = protomap |
|
| 84 |
+ } |
|
| 85 |
+ mapping := protomap[proto] |
|
| 77 | 86 |
if port > 0 {
|
| 78 |
- if _, ok := mapping[proto].p[port]; !ok {
|
|
| 79 |
- mapping[proto].p[port] = struct{}{}
|
|
| 87 |
+ if _, ok := mapping.p[port]; !ok {
|
|
| 88 |
+ mapping.p[port] = struct{}{}
|
|
| 80 | 89 |
return port, nil |
| 81 |
- } else {
|
|
| 82 |
- return 0, NewErrPortAlreadyAllocated(ip.String(), port) |
|
| 83 |
- } |
|
| 84 |
- } else {
|
|
| 85 |
- port, err := findPort(ip, proto) |
|
| 86 |
- |
|
| 87 |
- if err != nil {
|
|
| 88 |
- return 0, err |
|
| 89 | 90 |
} |
| 91 |
+ return 0, NewErrPortAlreadyAllocated(ipstr, port) |
|
| 92 |
+ } |
|
| 90 | 93 |
|
| 91 |
- return port, nil |
|
| 94 |
+ port, err := mapping.findPort() |
|
| 95 |
+ if err != nil {
|
|
| 96 |
+ return 0, err |
|
| 92 | 97 |
} |
| 98 |
+ return port, nil |
|
| 93 | 99 |
} |
| 94 | 100 |
|
| 101 |
+// ReleasePort releases port from global ports pool for specified ip and proto. |
|
| 95 | 102 |
func ReleasePort(ip net.IP, proto string, port int) error {
|
| 96 | 103 |
mutex.Lock() |
| 97 | 104 |
defer mutex.Unlock() |
| 98 | 105 |
|
| 99 |
- ip = getDefault(ip) |
|
| 100 |
- |
|
| 101 |
- mapping := getOrCreate(ip)[proto] |
|
| 102 |
- delete(mapping.p, port) |
|
| 103 |
- |
|
| 106 |
+ if ip == nil {
|
|
| 107 |
+ ip = defaultIP |
|
| 108 |
+ } |
|
| 109 |
+ protomap, ok := globalMap[ip.String()] |
|
| 110 |
+ if !ok {
|
|
| 111 |
+ return nil |
|
| 112 |
+ } |
|
| 113 |
+ delete(protomap[proto].p, port) |
|
| 104 | 114 |
return nil |
| 105 | 115 |
} |
| 106 | 116 |
|
| 117 |
+// ReleaseAll releases all ports for all ips. |
|
| 107 | 118 |
func ReleaseAll() error {
|
| 108 | 119 |
mutex.Lock() |
| 109 |
- defer mutex.Unlock() |
|
| 110 |
- |
|
| 111 | 120 |
globalMap = ipMapping{}
|
| 112 |
- |
|
| 121 |
+ mutex.Unlock() |
|
| 113 | 122 |
return nil |
| 114 | 123 |
} |
| 115 | 124 |
|
| 116 |
-func getOrCreate(ip net.IP) protocolMap {
|
|
| 117 |
- ipstr := ip.String() |
|
| 118 |
- |
|
| 119 |
- if _, ok := globalMap[ipstr]; !ok {
|
|
| 120 |
- globalMap[ipstr] = protocolMap{
|
|
| 121 |
- "tcp": &portMap{p: map[int]struct{}{}, last: 0},
|
|
| 122 |
- "udp": &portMap{p: map[int]struct{}{}, last: 0},
|
|
| 123 |
- } |
|
| 124 |
- } |
|
| 125 |
- |
|
| 126 |
- return globalMap[ipstr] |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-func findPort(ip net.IP, proto string) (int, error) {
|
|
| 130 |
- mapping := getOrCreate(ip)[proto] |
|
| 131 |
- |
|
| 132 |
- if mapping.last == 0 {
|
|
| 133 |
- mapping.p[BeginPortRange] = struct{}{}
|
|
| 134 |
- mapping.last = BeginPortRange |
|
| 125 |
+func (pm *portMap) findPort() (int, error) {
|
|
| 126 |
+ if pm.last == 0 {
|
|
| 127 |
+ pm.p[BeginPortRange] = struct{}{}
|
|
| 128 |
+ pm.last = BeginPortRange |
|
| 135 | 129 |
return BeginPortRange, nil |
| 136 | 130 |
} |
| 137 | 131 |
|
| 138 |
- for port := mapping.last + 1; port != mapping.last; port++ {
|
|
| 132 |
+ for port := pm.last + 1; port != pm.last; port++ {
|
|
| 139 | 133 |
if port > EndPortRange {
|
| 140 | 134 |
port = BeginPortRange |
| 141 | 135 |
} |
| 142 | 136 |
|
| 143 |
- if _, ok := mapping.p[port]; !ok {
|
|
| 144 |
- mapping.p[port] = struct{}{}
|
|
| 145 |
- mapping.last = port |
|
| 137 |
+ if _, ok := pm.p[port]; !ok {
|
|
| 138 |
+ pm.p[port] = struct{}{}
|
|
| 139 |
+ pm.last = port |
|
| 146 | 140 |
return port, nil |
| 147 | 141 |
} |
| 148 |
- |
|
| 149 | 142 |
} |
| 150 |
- |
|
| 151 | 143 |
return 0, ErrAllPortsAllocated |
| 152 | 144 |
} |
| 153 |
- |
|
| 154 |
-func getDefault(ip net.IP) net.IP {
|
|
| 155 |
- if ip == nil {
|
|
| 156 |
- return defaultIP |
|
| 157 |
- } |
|
| 158 |
- |
|
| 159 |
- return ip |
|
| 160 |
-} |
|
| 161 |
- |
|
| 162 |
-func validateProto(proto string) error {
|
|
| 163 |
- if proto != "tcp" && proto != "udp" {
|
|
| 164 |
- return ErrUnknownProtocol |
|
| 165 |
- } |
|
| 166 |
- |
|
| 167 |
- return nil |
|
| 168 |
-} |