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 }