Fix preferred ip allocation in ipam

- also provided a new utility to compute the
  host part ip address which is resilient to
  input passed in different representations.

Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Alessandro Boch
2015-08-16 09:38:17 -07:00
parent fa75dc24c4
commit 4e48ff3aab
4 changed files with 195 additions and 27 deletions
+53
View File
@@ -197,6 +197,59 @@ func CompareIPNet(a, b *net.IPNet) bool {
return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
}
// GetMinimalIP returns the address in its shortest form
func GetMinimalIP(ip net.IP) net.IP {
if ip != nil && ip.To4() != nil {
return ip.To4()
}
return ip
}
// GetMinimalIPNet returns a copy of the passed IP Network with congruent ip and mask notation
func GetMinimalIPNet(nw *net.IPNet) *net.IPNet {
if nw == nil {
return nil
}
if len(nw.IP) == 16 && nw.IP.To4() != nil {
m := nw.Mask
if len(m) == 16 {
m = m[12:16]
}
return &net.IPNet{IP: nw.IP.To4(), Mask: m}
}
return nw
}
var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
// GetHostPartIP returns the host portion of the ip address identified by the mask.
// IP address representation is not modified. If address and mask are not compatible
// an error is returned.
func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) {
// Find the effective starting of address and mask
is := 0
ms := 0
if len(ip) == net.IPv6len && ip.To4() != nil {
is = 12
}
if len(ip[is:]) == net.IPv4len && len(mask) == net.IPv6len && bytes.Equal(mask[:12], v4inV6MaskPrefix) {
ms = 12
}
// Check if address and mask are semantically compatible
if len(ip[is:]) != len(mask[ms:]) {
return nil, fmt.Errorf("cannot compute host portion ip address as ip and mask are not compatible: (%#v, %#v)", ip, mask)
}
// Compute host portion
out := GetIPCopy(ip)
for i := 0; i < len(mask[ms:]); i++ {
out[is+i] &= ^mask[ms+i]
}
return out, nil
}
const (
// NEXTHOP indicates a StaticRoute with an IP next hop.
NEXTHOP = iota
+59
View File
@@ -2,6 +2,7 @@ package types
import (
"flag"
"net"
"testing"
)
@@ -109,3 +110,61 @@ func TestErrorConstructors(t *testing.T) {
t.Fatal(err)
}
}
func TestUtilGetHostPortionIP(t *testing.T) {
input := []struct {
ip net.IP
mask net.IPMask
host net.IP
err error
}{
{ // ip in v4Inv6 representation, mask in v4 representation
ip: net.IPv4(172, 28, 30, 1),
mask: []byte{0xff, 0xff, 0xff, 0},
host: net.IPv4(0, 0, 0, 1),
},
{ // ip and mask in v4Inv6 representation
ip: net.IPv4(172, 28, 30, 2),
mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0},
host: net.IPv4(0, 0, 0, 2),
},
{ // ip in v4 representation, mask in v4Inv6 representation
ip: net.IPv4(172, 28, 30, 3)[12:],
mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0},
host: net.IPv4(0, 0, 0, 3)[12:],
},
{ // ip and mask in v4 representation
ip: net.IPv4(172, 28, 30, 4)[12:],
mask: []byte{0xff, 0xff, 0xff, 0},
host: net.IPv4(0, 0, 0, 4)[12:],
},
{ // ip and mask as v6
ip: net.ParseIP("2005:2004:2002:2001:FFFF:ABCD:EEAB:00CD"),
mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0},
host: net.ParseIP("0::AB:00CD"),
},
}
for _, i := range input {
h, err := GetHostPartIP(i.ip, i.mask)
if err != nil {
t.Fatal(err)
}
if !i.host.Equal(h) {
t.Fatalf("Failed to return expected host ip. Expected: %s. Got: %s", i.host, h)
}
}
// ip as v6 and mask as v4 are not compatible
if _, err := GetHostPartIP(net.ParseIP("2005:2004:2002:2001:FFFF:ABCD:EEAB:00CD"), []byte{0xff, 0xff, 0xff, 0}); err == nil {
t.Fatalf("Unexpected success")
}
// ip as v4 and non conventional mask
if _, err := GetHostPartIP(net.ParseIP("173.32.4.5"), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0}); err == nil {
t.Fatalf("Unexpected success")
}
// ip as v4 and non conventional mask
if _, err := GetHostPartIP(net.ParseIP("173.32.4.5"), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0}); err == nil {
t.Fatalf("Unexpected success")
}
}