port_mapping.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. //go:build windows
  2. package windows
  3. import (
  4. "bytes"
  5. "context"
  6. "errors"
  7. "fmt"
  8. "net"
  9. "github.com/containerd/log"
  10. "github.com/docker/docker/libnetwork/portmapper"
  11. "github.com/docker/docker/libnetwork/types"
  12. "github.com/ishidawataru/sctp"
  13. )
  14. const (
  15. maxAllocatePortAttempts = 10
  16. )
  17. // ErrUnsupportedAddressType is returned when the specified address type is not supported.
  18. type ErrUnsupportedAddressType string
  19. func (uat ErrUnsupportedAddressType) Error() string {
  20. return fmt.Sprintf("unsupported address type: %s", string(uat))
  21. }
  22. // AllocatePorts allocates ports specified in bindings from the portMapper
  23. func AllocatePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding, containerIP net.IP) ([]types.PortBinding, error) {
  24. bs := make([]types.PortBinding, 0, len(bindings))
  25. for _, c := range bindings {
  26. b := c.GetCopy()
  27. if err := allocatePort(portMapper, &b, containerIP); err != nil {
  28. // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
  29. if cuErr := ReleasePorts(portMapper, bs); cuErr != nil {
  30. log.G(context.TODO()).Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr)
  31. }
  32. return nil, err
  33. }
  34. bs = append(bs, b)
  35. }
  36. return bs, nil
  37. }
  38. func allocatePort(portMapper *portmapper.PortMapper, bnd *types.PortBinding, containerIP net.IP) error {
  39. var (
  40. host net.Addr
  41. err error
  42. )
  43. // Windows does not support a host ip for port bindings (this is validated in ConvertPortBindings()).
  44. // If the HostIP is nil, force it to be 0.0.0.0 for use as the key in portMapper.
  45. if bnd.HostIP == nil {
  46. bnd.HostIP = net.IPv4zero
  47. }
  48. // Store the container interface address in the operational binding
  49. bnd.IP = containerIP
  50. // Adjust HostPortEnd if this is not a range.
  51. if bnd.HostPortEnd == 0 {
  52. bnd.HostPortEnd = bnd.HostPort
  53. }
  54. // Construct the container side transport address
  55. container, err := bnd.ContainerAddr()
  56. if err != nil {
  57. return err
  58. }
  59. // Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
  60. for i := 0; i < maxAllocatePortAttempts; i++ {
  61. if host, err = portMapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), false); err == nil {
  62. break
  63. }
  64. // There is no point in immediately retrying to map an explicitly chosen port.
  65. if bnd.HostPort != 0 {
  66. log.G(context.TODO()).Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
  67. break
  68. }
  69. log.G(context.TODO()).Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
  70. }
  71. if err != nil {
  72. return err
  73. }
  74. // Save the host port (regardless it was or not specified in the binding)
  75. switch netAddr := host.(type) {
  76. case *net.TCPAddr:
  77. bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
  78. break
  79. case *net.UDPAddr:
  80. bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
  81. break
  82. case *sctp.SCTPAddr:
  83. bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port)
  84. break
  85. default:
  86. // For completeness
  87. return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
  88. }
  89. // Windows does not support host port ranges.
  90. bnd.HostPortEnd = bnd.HostPort
  91. return nil
  92. }
  93. // ReleasePorts releases ports specified in bindings from the portMapper
  94. func ReleasePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding) error {
  95. var errorBuf bytes.Buffer
  96. // Attempt to release all port bindings, do not stop on failure
  97. for _, m := range bindings {
  98. if err := releasePort(portMapper, m); err != nil {
  99. errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
  100. }
  101. }
  102. if errorBuf.Len() != 0 {
  103. return errors.New(errorBuf.String())
  104. }
  105. return nil
  106. }
  107. func releasePort(portMapper *portmapper.PortMapper, bnd types.PortBinding) error {
  108. // Construct the host side transport address
  109. host, err := bnd.HostAddr()
  110. if err != nil {
  111. return err
  112. }
  113. return portMapper.Unmap(host)
  114. }