From 9efc1cc264c28cbf5df71cd5b4ed341ebd51f089 Mon Sep 17 00:00:00 2001 From: Albin Kerouanton Date: Tue, 1 Jul 2025 02:04:57 +0200 Subject: [PATCH] libnet/portmapper: rename, move PortMapper to portallocator The only viable way to allocate a port is to bind and listen to it. So, the windows PortMapper was really a PortAllocator in disguise. Rename it to OSAllocator and move it to the portallocator package. Signed-off-by: Albin Kerouanton --- .../windows/overlay/ov_endpoint_windows.go | 6 +-- .../windows/overlay/ov_network_windows.go | 14 ++--- .../drivers/windows/port_mapping.go | 26 ++++----- daemon/libnetwork/drivers/windows/windows.go | 30 +++++------ .../osallocator_windows.go} | 53 +++++++++---------- 5 files changed, 64 insertions(+), 65 deletions(-) rename daemon/libnetwork/{portmapper/mapper_windows.go => portallocator/osallocator_windows.go} (69%) diff --git a/daemon/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go b/daemon/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go index 9973ae1bde..14d5520fa5 100644 --- a/daemon/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go +++ b/daemon/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go @@ -159,14 +159,14 @@ func (d *driver) CreateEndpoint(ctx context.Context, nid, eid string, ifInfo dri } ep.portMapping = epConnectivity.PortBindings - ep.portMapping, err = windows.AllocatePorts(n.portMapper, ep.portMapping) + ep.portMapping, err = windows.AllocatePorts(n.pa, ep.portMapping) if err != nil { return err } defer func() { if err != nil { - windows.ReleasePorts(n.portMapper, ep.portMapping) + windows.ReleasePorts(n.pa, ep.portMapping) } }() @@ -227,7 +227,7 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { return fmt.Errorf("endpoint id %q not found", eid) } - windows.ReleasePorts(n.portMapper, ep.portMapping) + windows.ReleasePorts(n.pa, ep.portMapping) n.deleteEndpoint(eid) diff --git a/daemon/libnetwork/drivers/windows/overlay/ov_network_windows.go b/daemon/libnetwork/drivers/windows/overlay/ov_network_windows.go index f0ac35732b..649b475e98 100644 --- a/daemon/libnetwork/drivers/windows/overlay/ov_network_windows.go +++ b/daemon/libnetwork/drivers/windows/overlay/ov_network_windows.go @@ -15,7 +15,7 @@ import ( "github.com/moby/moby/v2/daemon/libnetwork/driverapi" "github.com/moby/moby/v2/daemon/libnetwork/drivers/overlay" "github.com/moby/moby/v2/daemon/libnetwork/netlabel" - "github.com/moby/moby/v2/daemon/libnetwork/portmapper" + "github.com/moby/moby/v2/daemon/libnetwork/portallocator" "github.com/moby/moby/v2/daemon/libnetwork/types" ) @@ -50,7 +50,7 @@ type network struct { initErr error subnets []*subnet secure bool - portMapper *portmapper.PortMapper + pa *portallocator.OSAllocator sync.Mutex } @@ -86,11 +86,11 @@ func (d *driver) CreateNetwork(ctx context.Context, id string, option map[string } n := &network{ - id: id, - driver: d, - endpoints: endpointTable{}, - subnets: []*subnet{}, - portMapper: portmapper.New(), + id: id, + driver: d, + endpoints: endpointTable{}, + subnets: []*subnet{}, + pa: portallocator.New(), } genData, ok := option[netlabel.GenericData].(map[string]string) diff --git a/daemon/libnetwork/drivers/windows/port_mapping.go b/daemon/libnetwork/drivers/windows/port_mapping.go index 23ef4dd1d8..00cebd7bd4 100644 --- a/daemon/libnetwork/drivers/windows/port_mapping.go +++ b/daemon/libnetwork/drivers/windows/port_mapping.go @@ -10,20 +10,20 @@ import ( "net" "github.com/containerd/log" - "github.com/moby/moby/v2/daemon/libnetwork/portmapper" + "github.com/moby/moby/v2/daemon/libnetwork/portallocator" "github.com/moby/moby/v2/daemon/libnetwork/types" ) const maxAllocatePortAttempts = 10 -// AllocatePorts allocates ports specified in bindings from the portMapper -func AllocatePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding) ([]types.PortBinding, error) { +// AllocatePorts allocates ports specified in bindings from the port allocator. +func AllocatePorts(pa *portallocator.OSAllocator, bindings []types.PortBinding) ([]types.PortBinding, error) { bs := make([]types.PortBinding, 0, len(bindings)) for _, c := range bindings { - b, err := allocatePort(portMapper, c) + b, err := allocatePort(pa, c) if err != nil { // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message - if cuErr := ReleasePorts(portMapper, bs); cuErr != nil { + if cuErr := ReleasePorts(pa, 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 @@ -33,9 +33,9 @@ func AllocatePorts(portMapper *portmapper.PortMapper, bindings []types.PortBindi return bs, nil } -func allocatePort(portMapper *portmapper.PortMapper, bnd types.PortBinding) (types.PortBinding, error) { +func allocatePort(pa *portallocator.OSAllocator, bnd types.PortBinding) (types.PortBinding, 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 the HostIP is nil, force it to be 0.0.0.0 for use as the key in the port allocator. if bnd.HostIP == nil { bnd.HostIP = net.IPv4zero } @@ -49,7 +49,7 @@ func allocatePort(portMapper *portmapper.PortMapper, bnd types.PortBinding) (typ var allocatedPort int var err error for i := 0; i < maxAllocatePortAttempts; i++ { - allocatedPort, err = portMapper.MapRange(bnd.HostIP, bnd.Proto, int(bnd.HostPort), int(bnd.HostPortEnd)) + allocatedPort, err = pa.AllocateHostPort(bnd.HostIP, bnd.Proto, int(bnd.HostPort), int(bnd.HostPortEnd)) if err == nil { break } @@ -70,13 +70,13 @@ func allocatePort(portMapper *portmapper.PortMapper, bnd types.PortBinding) (typ return bnd, nil } -// ReleasePorts releases ports specified in bindings from the portMapper -func ReleasePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding) error { +// ReleasePorts releases ports specified in bindings from the portAlloc +func ReleasePorts(pa *portallocator.OSAllocator, 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 { + if err := releasePort(pa, m); err != nil { errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err)) } } @@ -87,6 +87,6 @@ func ReleasePorts(portMapper *portmapper.PortMapper, bindings []types.PortBindin return nil } -func releasePort(portMapper *portmapper.PortMapper, bnd types.PortBinding) error { - return portMapper.Unmap(bnd.HostIP, bnd.Proto, int(bnd.HostPort)) +func releasePort(pa *portallocator.OSAllocator, bnd types.PortBinding) error { + return pa.Deallocate(bnd.HostIP, bnd.Proto, int(bnd.HostPort)) } diff --git a/daemon/libnetwork/drivers/windows/windows.go b/daemon/libnetwork/drivers/windows/windows.go index 8e373ae599..c61a93a847 100644 --- a/daemon/libnetwork/drivers/windows/windows.go +++ b/daemon/libnetwork/drivers/windows/windows.go @@ -27,7 +27,7 @@ import ( "github.com/moby/moby/v2/daemon/libnetwork/datastore" "github.com/moby/moby/v2/daemon/libnetwork/driverapi" "github.com/moby/moby/v2/daemon/libnetwork/netlabel" - "github.com/moby/moby/v2/daemon/libnetwork/portmapper" + "github.com/moby/moby/v2/daemon/libnetwork/portallocator" "github.com/moby/moby/v2/daemon/libnetwork/scope" "github.com/moby/moby/v2/daemon/libnetwork/types" "go.opentelemetry.io/otel" @@ -94,12 +94,12 @@ type hnsEndpoint struct { } type hnsNetwork struct { - id string - created bool - config *networkConfiguration - endpoints map[string]*hnsEndpoint // key: endpoint id - driver *driver // The network's driver - portMapper *portmapper.PortMapper + id string + created bool + config *networkConfiguration + endpoints map[string]*hnsEndpoint // key: endpoint id + driver *driver // The network's driver + pa *portallocator.OSAllocator sync.Mutex } @@ -308,11 +308,11 @@ func (ncfg *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data func (d *driver) createNetwork(config *networkConfiguration) *hnsNetwork { network := &hnsNetwork{ - id: config.ID, - endpoints: make(map[string]*hnsEndpoint), - config: config, - driver: d, - portMapper: portmapper.New(), + id: config.ID, + endpoints: make(map[string]*hnsEndpoint), + config: config, + driver: d, + pa: portallocator.New(), } d.Lock() @@ -701,14 +701,14 @@ func (d *driver) CreateEndpoint(ctx context.Context, nid, eid string, ifInfo dri portMapping := epConnectivity.PortBindings if n.config.Type == "l2bridge" || n.config.Type == "l2tunnel" { - portMapping, err = AllocatePorts(n.portMapper, portMapping) + portMapping, err = AllocatePorts(n.pa, portMapping) if err != nil { return err } defer func() { if err != nil { - ReleasePorts(n.portMapper, portMapping) + ReleasePorts(n.pa, portMapping) } }() } @@ -823,7 +823,7 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } if n.config.Type == "l2bridge" || n.config.Type == "l2tunnel" { - ReleasePorts(n.portMapper, ep.portMapping) + ReleasePorts(n.pa, ep.portMapping) } n.Lock() diff --git a/daemon/libnetwork/portmapper/mapper_windows.go b/daemon/libnetwork/portallocator/osallocator_windows.go similarity index 69% rename from daemon/libnetwork/portmapper/mapper_windows.go rename to daemon/libnetwork/portallocator/osallocator_windows.go index 6487da2300..df99f7b429 100644 --- a/daemon/libnetwork/portmapper/mapper_windows.go +++ b/daemon/libnetwork/portallocator/osallocator_windows.go @@ -1,4 +1,4 @@ -package portmapper +package portallocator import ( "context" @@ -11,7 +11,6 @@ import ( "github.com/containerd/log" "github.com/ishidawataru/sctp" - "github.com/moby/moby/v2/daemon/libnetwork/portallocator" "github.com/moby/moby/v2/daemon/libnetwork/types" ) @@ -22,42 +21,42 @@ var ( ErrPortNotMapped = errors.New("port is not mapped") ) -// PortMapper manages the network address translation -type PortMapper struct { +// OSAllocator allocates ports from the OS by creating listening sockets. +type OSAllocator struct { // osListeners stores listening sockets used by active port mappings // to reserve ports from the OS. Outer map is keyed by protocol, and inner // map is keyed by host address and port. osListeners map[types.Protocol]map[netip.AddrPort]io.Closer lock sync.Mutex - allocator *portallocator.PortAllocator + allocator *PortAllocator } -// New returns a new instance of PortMapper -func New() *PortMapper { - return &PortMapper{ +// New returns a new instance of OSAllocator +func New() *OSAllocator { + return &OSAllocator{ osListeners: make(map[types.Protocol]map[netip.AddrPort]io.Closer), - allocator: portallocator.Get(), + allocator: Get(), } } -// MapRange maps the specified container transport address to the host's network address and transport port range -func (pm *PortMapper) MapRange(hostIP net.IP, proto types.Protocol, hostPortStart, hostPortEnd int) (_ int, retErr error) { - pm.lock.Lock() - defer pm.lock.Unlock() +// AllocateHostPort allocates a port from the OS (by creating a listening socket). +func (pa *OSAllocator) AllocateHostPort(hostIP net.IP, proto types.Protocol, hostPortStart, hostPortEnd int) (_ int, retErr error) { + pa.lock.Lock() + defer pa.lock.Unlock() - allocatedHostPort, err := pm.allocator.RequestPortInRange(hostIP, proto.String(), hostPortStart, hostPortEnd) + allocatedHostPort, err := pa.allocator.RequestPortInRange(hostIP, proto.String(), hostPortStart, hostPortEnd) if err != nil { return 0, err } defer func() { if retErr != nil { - pm.allocator.ReleasePort(hostIP, proto.String(), allocatedHostPort) + pa.allocator.ReleasePort(hostIP, proto.String(), allocatedHostPort) } }() - if pm.osListeners[proto] == nil { - pm.osListeners[proto] = make(map[netip.AddrPort]io.Closer) + if pa.osListeners[proto] == nil { + pa.osListeners[proto] = make(map[netip.AddrPort]io.Closer) } addr, ok := netip.AddrFromSlice(hostIP) @@ -66,7 +65,7 @@ func (pm *PortMapper) MapRange(hostIP net.IP, proto types.Protocol, hostPortStar } hAddrPort := netip.AddrPortFrom(addr, uint16(allocatedHostPort)) - if _, exists := pm.osListeners[proto][hAddrPort]; exists { + if _, exists := pa.osListeners[proto][hAddrPort]; exists { return 0, ErrPortMappedForIP } @@ -83,7 +82,7 @@ func (pm *PortMapper) MapRange(hostIP net.IP, proto types.Protocol, hostPortStar return 0, err } - pm.osListeners[proto][hAddrPort] = osListener + pa.osListeners[proto][hAddrPort] = osListener return allocatedHostPort, nil } @@ -119,22 +118,22 @@ func allocateHostPort(proto string, hostIP net.IP, hostPort int) (io.Closer, err } } -// Unmap removes stored mapping for the specified host transport address -func (pm *PortMapper) Unmap(hostIP net.IP, proto types.Protocol, hostPort int) error { - pm.lock.Lock() - defer pm.lock.Unlock() +// Deallocate removes stored mapping for the specified host transport address +func (pa *OSAllocator) Deallocate(hostIP net.IP, proto types.Protocol, hostPort int) error { + pa.lock.Lock() + defer pa.lock.Unlock() addr, ok := netip.AddrFromSlice(hostIP) if !ok { return fmt.Errorf("invalid HostIP: %s", hostIP) } - if pm.osListeners[proto] == nil { + if pa.osListeners[proto] == nil { return ErrPortNotMapped } hAddrPort := netip.AddrPortFrom(addr, uint16(hostPort)) - osListener, exists := pm.osListeners[proto][hAddrPort] + osListener, exists := pa.osListeners[proto][hAddrPort] if !exists { return ErrPortNotMapped } @@ -147,8 +146,8 @@ func (pm *PortMapper) Unmap(hostIP net.IP, proto types.Protocol, hostPort int) e } } - delete(pm.osListeners[proto], hAddrPort) + delete(pa.osListeners[proto], hAddrPort) - pm.allocator.ReleasePort(hostIP, proto.String(), int(hostPort)) + pa.allocator.ReleasePort(hostIP, proto.String(), int(hostPort)) return nil }