123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- //go:build windows
- package windows
- import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "net"
- "github.com/containerd/log"
- "github.com/docker/docker/libnetwork/portmapper"
- "github.com/docker/docker/libnetwork/types"
- "github.com/ishidawataru/sctp"
- )
- const (
- maxAllocatePortAttempts = 10
- )
- // ErrUnsupportedAddressType is returned when the specified address type is not supported.
- type ErrUnsupportedAddressType string
- func (uat ErrUnsupportedAddressType) Error() string {
- return fmt.Sprintf("unsupported address type: %s", string(uat))
- }
- // AllocatePorts allocates ports specified in bindings from the portMapper
- func AllocatePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding, containerIP net.IP) ([]types.PortBinding, error) {
- bs := make([]types.PortBinding, 0, len(bindings))
- for _, c := range bindings {
- b := c.GetCopy()
- if err := allocatePort(portMapper, &b, containerIP); err != nil {
- // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
- if cuErr := ReleasePorts(portMapper, bs); cuErr != nil {
- log.G(context.TODO()).Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr)
- }
- return nil, err
- }
- bs = append(bs, b)
- }
- return bs, nil
- }
- func allocatePort(portMapper *portmapper.PortMapper, bnd *types.PortBinding, containerIP net.IP) error {
- var (
- host net.Addr
- err error
- )
- // Windows does not support a host ip for port bindings (this is validated in ConvertPortBindings()).
- // If the HostIP is nil, force it to be 0.0.0.0 for use as the key in portMapper.
- if bnd.HostIP == nil {
- bnd.HostIP = net.IPv4zero
- }
- // Store the container interface address in the operational binding
- bnd.IP = containerIP
- // Adjust HostPortEnd if this is not a range.
- if bnd.HostPortEnd == 0 {
- bnd.HostPortEnd = bnd.HostPort
- }
- // Construct the container side transport address
- container, err := bnd.ContainerAddr()
- if err != nil {
- return err
- }
- // Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
- for i := 0; i < maxAllocatePortAttempts; i++ {
- if host, err = portMapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), false); err == nil {
- break
- }
- // There is no point in immediately retrying to map an explicitly chosen port.
- if bnd.HostPort != 0 {
- log.G(context.TODO()).Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
- break
- }
- log.G(context.TODO()).Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
- }
- if err != nil {
- return err
- }
- // Save the host port (regardless it was or not specified in the binding)
- switch netAddr := host.(type) {
- case *net.TCPAddr:
- bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
- break
- case *net.UDPAddr:
- bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
- break
- case *sctp.SCTPAddr:
- bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port)
- break
- default:
- // For completeness
- return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
- }
- // Windows does not support host port ranges.
- bnd.HostPortEnd = bnd.HostPort
- return nil
- }
- // ReleasePorts releases ports specified in bindings from the portMapper
- func ReleasePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding) error {
- var errorBuf bytes.Buffer
- // Attempt to release all port bindings, do not stop on failure
- for _, m := range bindings {
- if err := releasePort(portMapper, m); err != nil {
- errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
- }
- }
- if errorBuf.Len() != 0 {
- return errors.New(errorBuf.String())
- }
- return nil
- }
- func releasePort(portMapper *portmapper.PortMapper, bnd types.PortBinding) error {
- // Construct the host side transport address
- host, err := bnd.HostAddr()
- if err != nil {
- return err
- }
- return portMapper.Unmap(host)
- }
|