portallocator.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package portallocator
  2. import (
  3. "errors"
  4. "github.com/dotcloud/docker/pkg/collections"
  5. "net"
  6. "sync"
  7. )
  8. const (
  9. BeginPortRange = 49153
  10. EndPortRange = 65535
  11. )
  12. type (
  13. portMappings map[string]*collections.OrderedIntSet
  14. ipMapping map[string]portMappings
  15. )
  16. var (
  17. ErrAllPortsAllocated = errors.New("all ports are allocated")
  18. ErrPortAlreadyAllocated = errors.New("port has already been allocated")
  19. ErrUnknownProtocol = errors.New("unknown protocol")
  20. )
  21. var (
  22. currentDynamicPort = map[string]int{
  23. "tcp": BeginPortRange - 1,
  24. "udp": BeginPortRange - 1,
  25. }
  26. defaultIP = net.ParseIP("0.0.0.0")
  27. defaultAllocatedPorts = portMappings{}
  28. otherAllocatedPorts = ipMapping{}
  29. lock = sync.Mutex{}
  30. )
  31. func init() {
  32. defaultAllocatedPorts["tcp"] = collections.NewOrderedIntSet()
  33. defaultAllocatedPorts["udp"] = collections.NewOrderedIntSet()
  34. }
  35. // RequestPort returns an available port if the port is 0
  36. // If the provided port is not 0 then it will be checked if
  37. // it is available for allocation
  38. func RequestPort(ip net.IP, proto string, port int) (int, error) {
  39. lock.Lock()
  40. defer lock.Unlock()
  41. if err := validateProtocol(proto); err != nil {
  42. return 0, err
  43. }
  44. // If the user requested a specific port to be allocated
  45. if port > 0 {
  46. if err := registerSetPort(ip, proto, port); err != nil {
  47. return 0, err
  48. }
  49. return port, nil
  50. }
  51. return registerDynamicPort(ip, proto)
  52. }
  53. // ReleasePort will return the provided port back into the
  54. // pool for reuse
  55. func ReleasePort(ip net.IP, proto string, port int) error {
  56. lock.Lock()
  57. defer lock.Unlock()
  58. if err := validateProtocol(proto); err != nil {
  59. return err
  60. }
  61. allocated := defaultAllocatedPorts[proto]
  62. allocated.Remove(port)
  63. if !equalsDefault(ip) {
  64. registerIP(ip)
  65. // Remove the port for the specific ip address
  66. allocated = otherAllocatedPorts[ip.String()][proto]
  67. allocated.Remove(port)
  68. }
  69. return nil
  70. }
  71. func ReleaseAll() error {
  72. lock.Lock()
  73. defer lock.Unlock()
  74. currentDynamicPort["tcp"] = BeginPortRange - 1
  75. currentDynamicPort["udp"] = BeginPortRange - 1
  76. defaultAllocatedPorts = portMappings{}
  77. defaultAllocatedPorts["tcp"] = collections.NewOrderedIntSet()
  78. defaultAllocatedPorts["udp"] = collections.NewOrderedIntSet()
  79. otherAllocatedPorts = ipMapping{}
  80. return nil
  81. }
  82. func registerDynamicPort(ip net.IP, proto string) (int, error) {
  83. if !equalsDefault(ip) {
  84. registerIP(ip)
  85. ipAllocated := otherAllocatedPorts[ip.String()][proto]
  86. port, err := findNextPort(proto, ipAllocated)
  87. if err != nil {
  88. return 0, err
  89. }
  90. ipAllocated.Push(port)
  91. return port, nil
  92. } else {
  93. allocated := defaultAllocatedPorts[proto]
  94. port, err := findNextPort(proto, allocated)
  95. if err != nil {
  96. return 0, err
  97. }
  98. allocated.Push(port)
  99. return port, nil
  100. }
  101. }
  102. func registerSetPort(ip net.IP, proto string, port int) error {
  103. allocated := defaultAllocatedPorts[proto]
  104. if allocated.Exists(port) {
  105. return ErrPortAlreadyAllocated
  106. }
  107. if !equalsDefault(ip) {
  108. registerIP(ip)
  109. ipAllocated := otherAllocatedPorts[ip.String()][proto]
  110. if ipAllocated.Exists(port) {
  111. return ErrPortAlreadyAllocated
  112. }
  113. ipAllocated.Push(port)
  114. } else {
  115. allocated.Push(port)
  116. }
  117. return nil
  118. }
  119. func equalsDefault(ip net.IP) bool {
  120. return ip == nil || ip.Equal(defaultIP)
  121. }
  122. func findNextPort(proto string, allocated *collections.OrderedIntSet) (int, error) {
  123. port := nextPort(proto)
  124. startSearchPort := port
  125. for allocated.Exists(port) {
  126. port = nextPort(proto)
  127. if startSearchPort == port {
  128. return 0, ErrAllPortsAllocated
  129. }
  130. }
  131. return port, nil
  132. }
  133. func nextPort(proto string) int {
  134. c := currentDynamicPort[proto] + 1
  135. if c > EndPortRange {
  136. c = BeginPortRange
  137. }
  138. currentDynamicPort[proto] = c
  139. return c
  140. }
  141. func registerIP(ip net.IP) {
  142. if _, exists := otherAllocatedPorts[ip.String()]; !exists {
  143. otherAllocatedPorts[ip.String()] = portMappings{
  144. "tcp": collections.NewOrderedIntSet(),
  145. "udp": collections.NewOrderedIntSet(),
  146. }
  147. }
  148. }
  149. func validateProtocol(proto string) error {
  150. if _, exists := defaultAllocatedPorts[proto]; !exists {
  151. return ErrUnknownProtocol
  152. }
  153. return nil
  154. }