github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/libnetwork/drivers/bridge/port_mapping.go (about)

     1  package bridge
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  
     9  	"github.com/docker/libnetwork/types"
    10  	"github.com/ishidawataru/sctp"
    11  	"github.com/sirupsen/logrus"
    12  )
    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  
    19  	defHostIP := net.IPv4zero // 0.0.0.0
    20  	if reqDefBindIP != nil {
    21  		defHostIP = reqDefBindIP
    22  	}
    23  
    24  	var containerIPv6 net.IP
    25  	if ep.addrv6 != nil {
    26  		containerIPv6 = ep.addrv6.IP
    27  	}
    28  
    29  	pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	return pb, nil
    34  }
    35  
    36  func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
    37  	bs := make([]types.PortBinding, 0, len(bindings))
    38  	for _, c := range bindings {
    39  		bIPv4 := c.GetCopy()
    40  		bIPv6 := c.GetCopy()
    41  		// Allocate IPv4 Port mappings
    42  		if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok {
    43  			if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil {
    44  				// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
    45  				if cuErr := n.releasePortsInternal(bs); cuErr != nil {
    46  					logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr)
    47  				}
    48  				return nil, err
    49  			}
    50  			bs = append(bs, bIPv4)
    51  		}
    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  					logrus.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  
    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  }
    94  
    95  // validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true
    96  // if this is a valid IPv6 binding, else returns false
    97  func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIP, defHostIP net.IP) bool {
    98  	// Return early if there is no container endpoint
    99  	if containerIP == nil {
   100  		return false
   101  	}
   102  	// Return early if there is a valid Host IP, which is a IPv4 address
   103  	if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil {
   104  		return false
   105  	}
   106  
   107  	// Setup a binding to  "::" if Host IP is empty and the default binding IP is 0.0.0.0
   108  	if len(bnd.HostIP) == 0 {
   109  		if defHostIP.Equal(net.IPv4zero) {
   110  			bnd.HostIP = net.IPv6zero
   111  			// If the default binding IP is an IPv6 address, use it
   112  		} else if defHostIP.To4() == nil {
   113  			bnd.HostIP = defHostIP
   114  			// Return false if default binding ip is an IPv4 address
   115  		} else {
   116  			return false
   117  		}
   118  	}
   119  	bnd.IP = containerIP
   120  	return true
   121  }
   122  
   123  func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error {
   124  	var (
   125  		host net.Addr
   126  		err  error
   127  	)
   128  
   129  	// Adjust HostPortEnd if this is not a range.
   130  	if bnd.HostPortEnd == 0 {
   131  		bnd.HostPortEnd = bnd.HostPort
   132  	}
   133  
   134  	// Construct the container side transport address
   135  	container, err := bnd.ContainerAddr()
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	portmapper := n.portMapper
   141  
   142  	if bnd.IP.To4() == nil {
   143  		portmapper = n.portMapperV6
   144  	}
   145  
   146  	// Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
   147  	for i := 0; i < maxAllocatePortAttempts; i++ {
   148  		if host, err = portmapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil {
   149  			break
   150  		}
   151  		// There is no point in immediately retrying to map an explicitly chosen port.
   152  		if bnd.HostPort != 0 {
   153  			logrus.Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
   154  			break
   155  		}
   156  		logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
   157  	}
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	// Save the host port (regardless it was or not specified in the binding)
   163  	switch netAddr := host.(type) {
   164  	case *net.TCPAddr:
   165  		bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
   166  		return nil
   167  	case *net.UDPAddr:
   168  		bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
   169  		return nil
   170  	case *sctp.SCTPAddr:
   171  		bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port)
   172  		return nil
   173  	default:
   174  		// For completeness
   175  		return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
   176  	}
   177  }
   178  
   179  func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error {
   180  	return n.releasePortsInternal(ep.portMapping)
   181  }
   182  
   183  func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error {
   184  	var errorBuf bytes.Buffer
   185  
   186  	// Attempt to release all port bindings, do not stop on failure
   187  	for _, m := range bindings {
   188  		if err := n.releasePort(m); err != nil {
   189  			errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
   190  		}
   191  	}
   192  
   193  	if errorBuf.Len() != 0 {
   194  		return errors.New(errorBuf.String())
   195  	}
   196  	return nil
   197  }
   198  
   199  func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error {
   200  	// Construct the host side transport address
   201  	host, err := bnd.HostAddr()
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	portmapper := n.portMapper
   207  
   208  	if bnd.HostIP.To4() == nil {
   209  		portmapper = n.portMapperV6
   210  	}
   211  
   212  	return portmapper.Unmap(host)
   213  }