port_mapping.go 7.3 KB

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