github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/libnetwork/portallocator/portallocator.go (about) 1 package portallocator 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "sync" 8 ) 9 10 const ( 11 // DefaultPortRangeStart indicates the first port in port range 12 DefaultPortRangeStart = 49153 13 // DefaultPortRangeEnd indicates the last port in port range 14 DefaultPortRangeEnd = 65535 15 ) 16 17 type ipMapping map[string]protoMap 18 19 var ( 20 // ErrAllPortsAllocated is returned when no more ports are available 21 ErrAllPortsAllocated = errors.New("all ports are allocated") 22 // ErrUnknownProtocol is returned when an unknown protocol was specified 23 ErrUnknownProtocol = errors.New("unknown protocol") 24 defaultIP = net.ParseIP("0.0.0.0") 25 once sync.Once 26 instance *PortAllocator 27 createInstance = func() { instance = newInstance() } 28 ) 29 30 // ErrPortAlreadyAllocated is the returned error information when a requested port is already being used 31 type ErrPortAlreadyAllocated struct { 32 ip string 33 port int 34 } 35 36 func newErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated { 37 return ErrPortAlreadyAllocated{ 38 ip: ip, 39 port: port, 40 } 41 } 42 43 // IP returns the address to which the used port is associated 44 func (e ErrPortAlreadyAllocated) IP() string { 45 return e.ip 46 } 47 48 // Port returns the value of the already used port 49 func (e ErrPortAlreadyAllocated) Port() int { 50 return e.port 51 } 52 53 // IPPort returns the address and the port in the form ip:port 54 func (e ErrPortAlreadyAllocated) IPPort() string { 55 return fmt.Sprintf("%s:%d", e.ip, e.port) 56 } 57 58 // Error is the implementation of error.Error interface 59 func (e ErrPortAlreadyAllocated) Error() string { 60 return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port) 61 } 62 63 type ( 64 // PortAllocator manages the transport ports database 65 PortAllocator struct { 66 mutex sync.Mutex 67 ipMap ipMapping 68 Begin int 69 End int 70 } 71 portRange struct { 72 begin int 73 end int 74 last int 75 } 76 portMap struct { 77 p map[int]struct{} 78 defaultRange string 79 portRanges map[string]*portRange 80 } 81 protoMap map[string]*portMap 82 ) 83 84 // Get returns the default instance of PortAllocator 85 func Get() *PortAllocator { 86 // Port Allocator is a singleton 87 // Note: Long term solution will be each PortAllocator will have access to 88 // the OS so that it can have up to date view of the OS port allocation. 89 // When this happens singleton behavior will be removed. Clients do not 90 // need to worry about this, they will not see a change in behavior. 91 once.Do(createInstance) 92 return instance 93 } 94 95 func newInstance() *PortAllocator { 96 start, end, err := getDynamicPortRange() 97 if err != nil { 98 start, end = DefaultPortRangeStart, DefaultPortRangeEnd 99 } 100 return &PortAllocator{ 101 ipMap: ipMapping{}, 102 Begin: start, 103 End: end, 104 } 105 } 106 107 // RequestPort requests new port from global ports pool for specified ip and proto. 108 // If port is 0 it returns first free port. Otherwise it checks port availability 109 // in proto's pool and returns that port or error if port is already busy. 110 func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) { 111 return p.RequestPortInRange(ip, proto, port, port) 112 } 113 114 // RequestPortInRange requests new port from global ports pool for specified ip and proto. 115 // If portStart and portEnd are 0 it returns the first free port in the default ephemeral range. 116 // If portStart != portEnd it returns the first free port in the requested range. 117 // Otherwise (portStart == portEnd) it checks port availability in the requested proto's port-pool 118 // and returns that port or error if port is already busy. 119 func (p *PortAllocator) RequestPortInRange(ip net.IP, proto string, portStart, portEnd int) (int, error) { 120 p.mutex.Lock() 121 defer p.mutex.Unlock() 122 123 if proto != "tcp" && proto != "udp" { 124 return 0, ErrUnknownProtocol 125 } 126 127 if ip == nil { 128 ip = defaultIP 129 } 130 ipstr := ip.String() 131 protomap, ok := p.ipMap[ipstr] 132 if !ok { 133 protomap = protoMap{ 134 "tcp": p.newPortMap(), 135 "udp": p.newPortMap(), 136 } 137 138 p.ipMap[ipstr] = protomap 139 } 140 mapping := protomap[proto] 141 if portStart > 0 && portStart == portEnd { 142 if _, ok := mapping.p[portStart]; !ok { 143 mapping.p[portStart] = struct{}{} 144 return portStart, nil 145 } 146 return 0, newErrPortAlreadyAllocated(ipstr, portStart) 147 } 148 149 port, err := mapping.findPort(portStart, portEnd) 150 if err != nil { 151 return 0, err 152 } 153 return port, nil 154 } 155 156 // ReleasePort releases port from global ports pool for specified ip and proto. 157 func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error { 158 p.mutex.Lock() 159 defer p.mutex.Unlock() 160 161 if ip == nil { 162 ip = defaultIP 163 } 164 protomap, ok := p.ipMap[ip.String()] 165 if !ok { 166 return nil 167 } 168 delete(protomap[proto].p, port) 169 return nil 170 } 171 172 func (p *PortAllocator) newPortMap() *portMap { 173 defaultKey := getRangeKey(p.Begin, p.End) 174 pm := &portMap{ 175 p: map[int]struct{}{}, 176 defaultRange: defaultKey, 177 portRanges: map[string]*portRange{ 178 defaultKey: newPortRange(p.Begin, p.End), 179 }, 180 } 181 return pm 182 } 183 184 // ReleaseAll releases all ports for all ips. 185 func (p *PortAllocator) ReleaseAll() error { 186 p.mutex.Lock() 187 p.ipMap = ipMapping{} 188 p.mutex.Unlock() 189 return nil 190 } 191 192 func getRangeKey(portStart, portEnd int) string { 193 return fmt.Sprintf("%d-%d", portStart, portEnd) 194 } 195 196 func newPortRange(portStart, portEnd int) *portRange { 197 return &portRange{ 198 begin: portStart, 199 end: portEnd, 200 last: portEnd, 201 } 202 } 203 204 func (pm *portMap) getPortRange(portStart, portEnd int) (*portRange, error) { 205 var key string 206 if portStart == 0 && portEnd == 0 { 207 key = pm.defaultRange 208 } else { 209 key = getRangeKey(portStart, portEnd) 210 if portStart == portEnd || 211 portStart == 0 || portEnd == 0 || 212 portEnd < portStart { 213 return nil, fmt.Errorf("invalid port range: %s", key) 214 } 215 } 216 217 // Return existing port range, if already known. 218 if pr, exists := pm.portRanges[key]; exists { 219 return pr, nil 220 } 221 222 // Otherwise create a new port range. 223 pr := newPortRange(portStart, portEnd) 224 pm.portRanges[key] = pr 225 return pr, nil 226 } 227 228 func (pm *portMap) findPort(portStart, portEnd int) (int, error) { 229 pr, err := pm.getPortRange(portStart, portEnd) 230 if err != nil { 231 return 0, err 232 } 233 port := pr.last 234 235 for i := 0; i <= pr.end-pr.begin; i++ { 236 port++ 237 if port > pr.end { 238 port = pr.begin 239 } 240 241 if _, ok := pm.p[port]; !ok { 242 pm.p[port] = struct{}{} 243 pr.last = port 244 return port, nil 245 } 246 } 247 return 0, ErrAllPortsAllocated 248 }