diff --git a/libnetwork/ipam/allocator.go b/libnetwork/ipam/allocator.go index d7ed917637..d2cb3dd485 100644 --- a/libnetwork/ipam/allocator.go +++ b/libnetwork/ipam/allocator.go @@ -348,12 +348,15 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error { return err } - // Do not let network identifier address be reserved - // Do the same for IPv6 so that bridge ip starts with XXXX...::1 - h.Set(0) + // Pre-reserve the network address on IPv4 networks large + // enough to have one (i.e., anything bigger than a /31. + if !(ipVer == v4 && numAddresses <= 2) { + h.Set(0) + } - // Do not let broadcast address be reserved - if ipVer == v4 { + // Pre-reserve the broadcast address on IPv4 networks large + // enough to have one (i.e., anything bigger than a /31). + if ipVer == v4 && numAddresses > 2 { h.Set(numAddresses - 1) } diff --git a/libnetwork/ipam/allocator_test.go b/libnetwork/ipam/allocator_test.go index 8b05d32371..2c3150a636 100644 --- a/libnetwork/ipam/allocator_test.go +++ b/libnetwork/ipam/allocator_test.go @@ -1055,6 +1055,72 @@ func TestOverlappingRequests(t *testing.T) { } } +func TestUnusualSubnets(t *testing.T) { + + subnet := "192.168.0.2/31" + + outsideTheRangeAddresses := []struct { + address string + }{ + {"192.168.0.1"}, + {"192.168.0.4"}, + {"192.168.0.100"}, + } + + expectedAddresses := []struct { + address string + }{ + {"192.168.0.2"}, + {"192.168.0.3"}, + } + + for _, store := range []bool{false, true} { + + allocator, err := getAllocator(store) + if err != nil { + t.Fatal(err) + } + + // + // IPv4 /31 blocks. See RFC 3021. + // + + pool, _, _, err := allocator.RequestPool(localAddressSpace, subnet, "", nil, false) + if err != nil { + t.Fatal(err) + } + + // Outside-the-range + + for _, outside := range outsideTheRangeAddresses { + _, _, errx := allocator.RequestAddress(pool, net.ParseIP(outside.address), nil) + if errx != ipamapi.ErrIPOutOfRange { + t.Fatalf("Address %s failed to throw expected error: %s", outside.address, errx.Error()) + } + } + + // Should get just these two IPs followed by exhaustion on the next request + + for _, expected := range expectedAddresses { + got, _, errx := allocator.RequestAddress(pool, nil, nil) + if errx != nil { + t.Fatalf("Failed to obtain the address: %s", errx.Error()) + } + expectedIP := net.ParseIP(expected.address) + gotIP := got.IP + if !gotIP.Equal(expectedIP) { + t.Fatalf("Failed to obtain sequentialaddress. Expected: %s, Got: %s", expectedIP, gotIP) + } + } + + _, _, err = allocator.RequestAddress(pool, nil, nil) + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal("Did not get expected error when pool is exhausted.") + } + + } +} + func TestRelease(t *testing.T) { var ( subnet = "192.168.0.0/23"