portallocator.go 6.2 KB

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