port_mapping_linux.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. package bridge
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "github.com/containerd/log"
  9. "github.com/docker/docker/libnetwork/netutils"
  10. "github.com/docker/docker/libnetwork/types"
  11. "github.com/ishidawataru/sctp"
  12. )
  13. func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
  14. if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil {
  15. return nil, nil
  16. }
  17. defHostIP := net.IPv4zero // 0.0.0.0
  18. if reqDefBindIP != nil {
  19. defHostIP = reqDefBindIP
  20. }
  21. var containerIPv6 net.IP
  22. if ep.addrv6 != nil {
  23. containerIPv6 = ep.addrv6.IP
  24. }
  25. pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled)
  26. if err != nil {
  27. return nil, err
  28. }
  29. return pb, nil
  30. }
  31. func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
  32. bs := make([]types.PortBinding, 0, len(bindings))
  33. for _, c := range bindings {
  34. bIPv4 := c.GetCopy()
  35. bIPv6 := c.GetCopy()
  36. // Allocate IPv4 Port mappings
  37. if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok {
  38. if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil {
  39. // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
  40. if cuErr := n.releasePortsInternal(bs); cuErr != nil {
  41. log.G(context.TODO()).Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr)
  42. }
  43. return nil, err
  44. }
  45. bs = append(bs, bIPv4)
  46. }
  47. // skip adding implicit v6 addr, when the kernel was booted with `ipv6.disable=1`
  48. // https://github.com/moby/moby/issues/42288
  49. isV6Binding := c.HostIP != nil && c.HostIP.To4() == nil
  50. if !isV6Binding && !netutils.IsV6Listenable() {
  51. continue
  52. }
  53. // Allocate IPv6 Port mappings
  54. // If the container has no IPv6 address, allow proxying host IPv6 traffic to it
  55. // by setting up the binding with the IPv4 interface if the userland proxy is enabled
  56. // This change was added to keep backward compatibility
  57. containerIP := containerIPv6
  58. if ulPxyEnabled && (containerIPv6 == nil) {
  59. containerIP = containerIPv4
  60. }
  61. if ok := n.validatePortBindingIPv6(&bIPv6, containerIP, defHostIP); ok {
  62. if err := n.allocatePort(&bIPv6, ulPxyEnabled); err != nil {
  63. // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
  64. if cuErr := n.releasePortsInternal(bs); cuErr != nil {
  65. log.G(context.TODO()).Warnf("allocation failure for %v, failed to clear previously allocated ipv6 port bindings: %v", bIPv6, cuErr)
  66. }
  67. return nil, err
  68. }
  69. bs = append(bs, bIPv6)
  70. }
  71. }
  72. return bs, nil
  73. }
  74. // validatePortBindingIPv4 validates the port binding, populates the missing Host IP field and returns true
  75. // if this is a valid IPv4 binding, else returns false
  76. func (n *bridgeNetwork) validatePortBindingIPv4(bnd *types.PortBinding, containerIPv4, defHostIP net.IP) bool {
  77. // Return early if there is a valid Host IP, but its not a IPv4 address
  78. if len(bnd.HostIP) > 0 && bnd.HostIP.To4() == nil {
  79. return false
  80. }
  81. // Adjust the host address in the operational binding
  82. if len(bnd.HostIP) == 0 {
  83. // Return early if the default binding address is an IPv6 address
  84. if defHostIP.To4() == nil {
  85. return false
  86. }
  87. bnd.HostIP = defHostIP
  88. }
  89. bnd.IP = containerIPv4
  90. return true
  91. }
  92. // validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true
  93. // if this is a valid IPv6 binding, else returns false
  94. func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIP, defHostIP net.IP) bool {
  95. // Return early if there is no container endpoint
  96. if containerIP == nil {
  97. return false
  98. }
  99. // Return early if there is a valid Host IP, which is a IPv4 address
  100. if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil {
  101. return false
  102. }
  103. // Setup a binding to "::" if Host IP is empty and the default binding IP is 0.0.0.0
  104. if len(bnd.HostIP) == 0 {
  105. if defHostIP.Equal(net.IPv4zero) {
  106. bnd.HostIP = net.IPv6zero
  107. // If the default binding IP is an IPv6 address, use it
  108. } else if defHostIP.To4() == nil {
  109. bnd.HostIP = defHostIP
  110. // Return false if default binding ip is an IPv4 address
  111. } else {
  112. return false
  113. }
  114. }
  115. bnd.IP = containerIP
  116. return true
  117. }
  118. func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error {
  119. var (
  120. host net.Addr
  121. err error
  122. )
  123. // Adjust HostPortEnd if this is not a range.
  124. if bnd.HostPortEnd == 0 {
  125. bnd.HostPortEnd = bnd.HostPort
  126. }
  127. // Construct the container side transport address
  128. container, err := bnd.ContainerAddr()
  129. if err != nil {
  130. return err
  131. }
  132. portmapper := n.portMapper
  133. if bnd.HostIP.To4() == nil {
  134. portmapper = n.portMapperV6
  135. }
  136. // Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
  137. for i := 0; i < maxAllocatePortAttempts; i++ {
  138. if host, err = portmapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil {
  139. break
  140. }
  141. // There is no point in immediately retrying to map an explicitly chosen port.
  142. if bnd.HostPort != 0 {
  143. log.G(context.TODO()).Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
  144. break
  145. }
  146. log.G(context.TODO()).Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
  147. }
  148. if err != nil {
  149. return err
  150. }
  151. // Save the host port (regardless it was or not specified in the binding)
  152. switch netAddr := host.(type) {
  153. case *net.TCPAddr:
  154. bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
  155. return nil
  156. case *net.UDPAddr:
  157. bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
  158. return nil
  159. case *sctp.SCTPAddr:
  160. bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port)
  161. return nil
  162. default:
  163. // For completeness
  164. return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
  165. }
  166. }
  167. func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error {
  168. return n.releasePortsInternal(ep.portMapping)
  169. }
  170. func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error {
  171. var errorBuf bytes.Buffer
  172. // Attempt to release all port bindings, do not stop on failure
  173. for _, m := range bindings {
  174. if err := n.releasePort(m); err != nil {
  175. errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
  176. }
  177. }
  178. if errorBuf.Len() != 0 {
  179. return errors.New(errorBuf.String())
  180. }
  181. return nil
  182. }
  183. func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error {
  184. // Construct the host side transport address
  185. host, err := bnd.HostAddr()
  186. if err != nil {
  187. return err
  188. }
  189. portmapper := n.portMapper
  190. if bnd.HostIP.To4() == nil {
  191. portmapper = n.portMapperV6
  192. }
  193. return portmapper.Unmap(host)
  194. }