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 <albinker@gmail.com>
This commit is contained in:
Albin Kerouanton
2025-07-01 02:04:57 +02:00
parent af677b61a5
commit 9efc1cc264
5 changed files with 64 additions and 65 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))
}

View File

@@ -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()

View File

@@ -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
}