api: Convert libnet's errors into HTTP status code
**- What I did**

Both libnetwork's BadRequestError and NoServiceError interfaces used a different method name than their equivalent in `errdefs`. As the package httpstatus wasn't aware of these interfaces, any libnetwork's errors implementing those interfaces were returning a 500 and a "FIXME" mesage would be logged. This is now fixed by replacing libnetwork's interfaces with newer interfaces compatible with errdefs' ones.

**- How to verify it**

CI is green

**- A picture of a cute animal (not mandatory but encouraged)**

![](https://www.thesprucepets.com/thmb/wbacRYRNfOaMJ0aeDXtsQUxI0Rk=/5200x0/filters:no_upscale():strip_icc()/mandarinfish-swimming-in-coral-reef-166264759-5ac6c034303713003775cb26.jpg)

libnetwork/ipam/structures.go
package ipam

import (
	"fmt"
	"net/netip"
	"strings"
	"sync"

	"github.com/docker/docker/libnetwork/bitmap"
	"github.com/docker/docker/libnetwork/ipamapi"
	"github.com/docker/docker/libnetwork/types"
)

// PoolID is the pointer to the configured pools in each address space
type PoolID struct {
	AddressSpace string
	SubnetKey
}

// PoolData contains the configured pool data
type PoolData struct {
	addrs    *bitmap.Bitmap
	children map[netip.Prefix]struct{}

	// Whether to implicitly release the pool once it no longer has any children.
	autoRelease bool
}

// SubnetKey is the composite key to an address pool within an address space.
type SubnetKey struct {
	Subnet, ChildSubnet netip.Prefix
}

// addrSpace contains the pool configurations for the address space
type addrSpace struct {
	// Master subnet pools, indexed by the value's stringified PoolData.Pool field.
	subnets map[netip.Prefix]*PoolData

	// Predefined pool for the address space
	predefined           []netip.Prefix
	predefinedStartIndex int

	sync.Mutex
}

// PoolIDFromString creates a new PoolID and populates the SubnetKey object
// reading it from the given string.
func PoolIDFromString(str string) (pID PoolID, err error) {
	if str == "" {
		return pID, types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
	}

	p := strings.Split(str, "/")
	if len(p) != 3 && len(p) != 5 {
		return pID, types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
	}
	pID.AddressSpace = p[0]
	pID.Subnet, err = netip.ParsePrefix(p[1] + "/" + p[2])
	if err != nil {
		return pID, types.BadRequestErrorf("%v", err)
	}
	if len(p) == 5 {
		pID.ChildSubnet, err = netip.ParsePrefix(p[3] + "/" + p[4])
		if err != nil {
			return pID, types.BadRequestErrorf("%v", err)
		}
	}

	return pID, nil
}

// String returns the string form of the SubnetKey object
func (s *PoolID) String() string {
	if s.ChildSubnet == (netip.Prefix{}) {
		return s.AddressSpace + "/" + s.Subnet.String()
	} else {
		return s.AddressSpace + "/" + s.Subnet.String() + "/" + s.ChildSubnet.String()
	}
}

// String returns the string form of the PoolData object
func (p *PoolData) String() string {
	return fmt.Sprintf("PoolData[Children: %d]", len(p.children))
}

// allocateSubnet adds the subnet k to the address space.
func (aSpace *addrSpace) allocateSubnet(nw, sub netip.Prefix) error {
	aSpace.Lock()
	defer aSpace.Unlock()

	// Check if already allocated
	if pool, ok := aSpace.subnets[nw]; ok {
		var childExists bool
		if sub != (netip.Prefix{}) {
			_, childExists = pool.children[sub]
		}
		if sub == (netip.Prefix{}) || childExists {
			// This means the same pool is already allocated. allocateSubnet is called when there
			// is request for a pool/subpool. It should ensure there is no overlap with existing pools
			return ipamapi.ErrPoolOverlap
		}
	}

	return aSpace.allocateSubnetL(nw, sub)
}

func (aSpace *addrSpace) allocateSubnetL(nw, sub netip.Prefix) error {
	// If master pool, check for overlap
	if sub == (netip.Prefix{}) {
		if aSpace.contains(nw) {
			return ipamapi.ErrPoolOverlap
		}
		// This is a new master pool, add it along with corresponding bitmask
		aSpace.subnets[nw] = newPoolData(nw)
		return nil
	}

	// This is a new non-master pool (subPool)
	if nw.Addr().BitLen() != sub.Addr().BitLen() {
		return fmt.Errorf("pool and subpool are of incompatible address families")
	}

	// Look for parent pool
	pp, ok := aSpace.subnets[nw]
	if !ok {
		// Parent pool does not exist, add it along with corresponding bitmask
		pp = newPoolData(nw)
		pp.autoRelease = true
		aSpace.subnets[nw] = pp
	}
	pp.children[sub] = struct{}{}
	return nil
}

func (aSpace *addrSpace) releaseSubnet(nw, sub netip.Prefix) error {
	aSpace.Lock()
	defer aSpace.Unlock()

	p, ok := aSpace.subnets[nw]
	if !ok {
		return ipamapi.ErrBadPool
	}

	if sub != (netip.Prefix{}) {
		if _, ok := p.children[sub]; !ok {
			return ipamapi.ErrBadPool
		}
		delete(p.children, sub)
	} else {
		p.autoRelease = true
	}

	if len(p.children) == 0 && p.autoRelease {
		delete(aSpace.subnets, nw)
	}

	return nil
}

// contains checks whether nw is a superset or subset of any of the existing subnets in this address space.
func (aSpace *addrSpace) contains(nw netip.Prefix) bool {
	for pool := range aSpace.subnets {
		if nw.Contains(pool.Addr()) || pool.Contains(nw.Addr()) {
			return true
		}
	}
	return false
}

package ipam

import (
	"fmt"
	"net/netip"
	"strings"
	"sync"

	"github.com/docker/docker/libnetwork/bitmap"
	"github.com/docker/docker/libnetwork/ipamapi"
	"github.com/docker/docker/libnetwork/types"
)

// PoolID is the pointer to the configured pools in each address space
type PoolID struct {
	AddressSpace string
	SubnetKey
}

// PoolData contains the configured pool data
type PoolData struct {
	addrs    *bitmap.Bitmap
	children map[netip.Prefix]struct{}

	// Whether to implicitly release the pool once it no longer has any children.
	autoRelease bool
}

// SubnetKey is the composite key to an address pool within an address space.
type SubnetKey struct {
	Subnet, ChildSubnet netip.Prefix
}

// addrSpace contains the pool configurations for the address space
type addrSpace struct {
	// Master subnet pools, indexed by the value's stringified PoolData.Pool field.
	subnets map[netip.Prefix]*PoolData

	// Predefined pool for the address space
	predefined           []netip.Prefix
	predefinedStartIndex int

	sync.Mutex
}

// PoolIDFromString creates a new PoolID and populates the SubnetKey object
// reading it from the given string.
func PoolIDFromString(str string) (pID PoolID, err error) {
	if str == "" {
		return pID, types.InvalidParameterErrorf("invalid string form for subnetkey: %s", str)
	}

	p := strings.Split(str, "/")
	if len(p) != 3 && len(p) != 5 {
		return pID, types.InvalidParameterErrorf("invalid string form for subnetkey: %s", str)
	}
	pID.AddressSpace = p[0]
	pID.Subnet, err = netip.ParsePrefix(p[1] + "/" + p[2])
	if err != nil {
		return pID, types.InvalidParameterErrorf("invalid string form for subnetkey: %s", str)
	}
	if len(p) == 5 {
		pID.ChildSubnet, err = netip.ParsePrefix(p[3] + "/" + p[4])
		if err != nil {
			return pID, types.InvalidParameterErrorf("invalid string form for subnetkey: %s", str)
		}
	}

	return pID, nil
}

// String returns the string form of the SubnetKey object
func (s *PoolID) String() string {
	if s.ChildSubnet == (netip.Prefix{}) {
		return s.AddressSpace + "/" + s.Subnet.String()
	} else {
		return s.AddressSpace + "/" + s.Subnet.String() + "/" + s.ChildSubnet.String()
	}
}

// String returns the string form of the PoolData object
func (p *PoolData) String() string {
	return fmt.Sprintf("PoolData[Children: %d]", len(p.children))
}

// allocateSubnet adds the subnet k to the address space.
func (aSpace *addrSpace) allocateSubnet(nw, sub netip.Prefix) error {
	aSpace.Lock()
	defer aSpace.Unlock()

	// Check if already allocated
	if pool, ok := aSpace.subnets[nw]; ok {
		var childExists bool
		if sub != (netip.Prefix{}) {
			_, childExists = pool.children[sub]
		}
		if sub == (netip.Prefix{}) || childExists {
			// This means the same pool is already allocated. allocateSubnet is called when there
			// is request for a pool/subpool. It should ensure there is no overlap with existing pools
			return ipamapi.ErrPoolOverlap
		}
	}

	return aSpace.allocateSubnetL(nw, sub)
}

func (aSpace *addrSpace) allocateSubnetL(nw, sub netip.Prefix) error {
	// If master pool, check for overlap
	if sub == (netip.Prefix{}) {
		if aSpace.contains(nw) {
			return ipamapi.ErrPoolOverlap
		}
		// This is a new master pool, add it along with corresponding bitmask
		aSpace.subnets[nw] = newPoolData(nw)
		return nil
	}

	// This is a new non-master pool (subPool)
	if nw.Addr().BitLen() != sub.Addr().BitLen() {
		return fmt.Errorf("pool and subpool are of incompatible address families")
	}

	// Look for parent pool
	pp, ok := aSpace.subnets[nw]
	if !ok {
		// Parent pool does not exist, add it along with corresponding bitmask
		pp = newPoolData(nw)
		pp.autoRelease = true
		aSpace.subnets[nw] = pp
	}
	pp.children[sub] = struct{}{}
	return nil
}

func (aSpace *addrSpace) releaseSubnet(nw, sub netip.Prefix) error {
	aSpace.Lock()
	defer aSpace.Unlock()

	p, ok := aSpace.subnets[nw]
	if !ok {
		return ipamapi.ErrBadPool
	}

	if sub != (netip.Prefix{}) {
		if _, ok := p.children[sub]; !ok {
			return ipamapi.ErrBadPool
		}
		delete(p.children, sub)
	} else {
		p.autoRelease = true
	}

	if len(p.children) == 0 && p.autoRelease {
		delete(aSpace.subnets, nw)
	}

	return nil
}

// contains checks whether nw is a superset or subset of any of the existing subnets in this address space.
func (aSpace *addrSpace) contains(nw netip.Prefix) bool {
	for pool := range aSpace.subnets {
		if nw.Contains(pool.Addr()) || pool.Contains(nw.Addr()) {
			return true
		}
	}
	return false
}

If we want to keep the `types` helpers, we could still make the utilities a wrapper, e.g.

```go
// InvalidParameterErrorf creates an instance of InvalidParameterError
func InvalidParameterErrorf(format string, params ...interface{}) error {
	return errdefs.InvalidParameter(fmt.Errorf(format, params...))
}
```