diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go index 474cec95a55..77c11abb237 100644 --- a/src/pkg/net/ip.go +++ b/src/pkg/net/ip.go @@ -21,8 +21,8 @@ const ( ) // An IP is a single IP address, an array of bytes. -// Functions in this package accept either 4-byte (IP v4) -// or 16-byte (IP v6) arrays as input. Unless otherwise +// Functions in this package accept either 4-byte (IPv4) +// or 16-byte (IPv6) arrays as input. Unless otherwise // specified, functions in this package always return // IP addresses in 16-byte form using the canonical // embedding. @@ -51,17 +51,14 @@ func IPv4(a, b, c, d byte) IP { var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff} -// IPv4Mask returns the IP mask (in 16-byte form) of the +// IPv4Mask returns the IP mask (in 4-byte form) of the // IPv4 mask a.b.c.d. func IPv4Mask(a, b, c, d byte) IPMask { - p := make(IPMask, IPv6len) - for i := 0; i < 12; i++ { - p[i] = 0xff - } - p[12] = a - p[13] = b - p[14] = c - p[15] = d + p := make(IPMask, IPv4len) + p[0] = a + p[1] = b + p[2] = c + p[3] = d return p } @@ -213,13 +210,13 @@ func allFF(b []byte) bool { // Mask returns the result of masking the IP address ip with mask. func (ip IP) Mask(mask IPMask) IP { - n := len(ip) if len(mask) == IPv6len && len(ip) == IPv4len && allFF(mask[:12]) { mask = mask[12:] } if len(mask) == IPv4len && len(ip) == IPv6len && bytesEqual(ip[:12], v4InV6Prefix) { ip = ip[12:] } + n := len(ip) if n != len(mask) { return nil } @@ -238,7 +235,7 @@ func (ip IP) String() string { p := ip if len(ip) == 0 { - return "" + return "" } // If IPv4, use dotted notation. @@ -345,25 +342,27 @@ func simpleMaskLength(mask IPMask) int { return n } -// String returns the string representation of mask. -// If the mask is in the canonical form--ones followed by zeros--the -// string representation is just the decimal number of ones. -// If the mask is in a non-canonical form, it is formatted -// as an IP address. -func (mask IPMask) String() string { - switch len(mask) { - case IPv4len: - n := simpleMaskLength(mask) - if n >= 0 { - return itod(uint(n + (IPv6len-IPv4len)*8)) - } - case IPv6len: - n := simpleMaskLength(mask) - if n >= 12*8 { - return itod(uint(n - 12*8)) - } +// Size returns the number of leading ones and total bits in the mask. +// If the mask is not in the canonical form--ones followed by zeros--then +// Size returns 0, 0. +func (m IPMask) Size() (ones, bits int) { + ones, bits = simpleMaskLength(m), len(m)*8 + if ones == -1 { + return 0, 0 } - return IP(mask).String() + return +} + +// String returns the hexadecimal form of m, with no punctuation. +func (m IPMask) String() string { + s := "" + for _, b := range m { + s += itox(uint(b), 2) + } + if len(s) == 0 { + return "" + } + return s } // Parse IPv4 address (d.d.d.d). @@ -535,7 +534,7 @@ func ParseIP(s string) IP { } // ParseCIDR parses s as a CIDR notation IP address and mask, -// like "192.168.100.1/24", "2001:DB8::/48", as defined in +// like "192.168.100.1/24" or "2001:DB8::/48", as defined in // RFC 4632 and RFC 4291. func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) { i := byteIndex(s, '/') @@ -571,8 +570,12 @@ func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) { } } // address must not have any bits not in mask - for i := range ip { - if ip[i]&^mask[i] != 0 { + mip := ip + if iplen == IPv4len { + mip = ip[12:] + } + for i := range mip { + if mip[i]&^mask[i] != 0 { return nil, nil, &ParseError{"CIDR address", s} } } diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go index b189b10c4fd..5064783d090 100644 --- a/src/pkg/net/ip_test.go +++ b/src/pkg/net/ip_test.go @@ -49,22 +49,15 @@ var ipstringtests = []struct { out string }{ // cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation) - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, - "2001:db8::123:12:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1}, - "2001:db8::1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1}, - "2001:db8:0:1:0:1:0:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0}, - "2001:db8:1:0:1:0:1:0"}, - {IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, - "2001::1:0:0:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, - "2001:db8:0:0:1::"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, - "2001:db8::1:0:0:1"}, - {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, - "2001:db8::a:b:c:d"}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, "2001:db8::123:12:1"}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1"}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1}, "2001:db8:0:1:0:1:0:1"}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0}, "2001:db8:1:0:1:0:1:0"}, + {IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001::1:0:0:1"}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, "2001:db8:0:0:1::"}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1:0:0:1"}, + {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, "2001:db8::a:b:c:d"}, + {nil, ""}, } func TestIPString(t *testing.T) { @@ -75,6 +68,46 @@ func TestIPString(t *testing.T) { } } +var ipmasktests = []struct { + in IP + mask IPMask + out IP +}{ + {IPv4(192, 168, 1, 127), IPv4Mask(255, 255, 255, 128), IPv4(192, 168, 1, 0)}, + {IPv4(192, 168, 1, 127), IPMask(ParseIP("255.255.255.192")), IPv4(192, 168, 1, 64)}, + {IPv4(192, 168, 1, 127), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0")), IPv4(192, 168, 1, 96)}, + {IPv4(192, 168, 1, 127), IPv4Mask(255, 0, 255, 0), IPv4(192, 0, 1, 0)}, + {ParseIP("2001:db8::1"), IPMask(ParseIP("ffff:ff80::")), ParseIP("2001:d80::")}, + {ParseIP("2001:db8::1"), IPMask(ParseIP("f0f0:0f0f::")), ParseIP("2000:d08::")}, +} + +func TestIPMask(t *testing.T) { + for _, tt := range ipmasktests { + if out := tt.in.Mask(tt.mask); out == nil || !tt.out.Equal(out) { + t.Errorf("IP(%v).Mask(%v) = %v, want %v", tt.in, tt.mask, out, tt.out) + } + } +} + +var ipmaskstringtests = []struct { + in IPMask + out string +}{ + {IPv4Mask(255, 255, 255, 240), "fffffff0"}, + {IPv4Mask(255, 0, 128, 0), "ff008000"}, + {IPMask(ParseIP("ffff:ff80::")), "ffffff80000000000000000000000000"}, + {IPMask(ParseIP("ef00:ff80::cafe:0")), "ef00ff800000000000000000cafe0000"}, + {nil, ""}, +} + +func TestIPMaskString(t *testing.T) { + for _, tt := range ipmaskstringtests { + if out := tt.in.String(); out != tt.out { + t.Errorf("IPMask.String(%v) = %q, want %q", tt.in, out, tt.out) + } + } +} + var parsecidrtests = []struct { in string ip IP @@ -101,7 +134,7 @@ var parsecidrtests = []struct { func TestParseCIDR(t *testing.T) { for _, tt := range parsecidrtests { - if ip, mask, err := ParseCIDR(tt.in); !isEqual(ip, tt.ip) || !isEqual(mask, tt.mask) || !reflect.DeepEqual(err, tt.err) { + if ip, mask, err := ParseCIDR(tt.in); !tt.ip.Equal(ip) || !isEqual(mask, tt.mask) || !reflect.DeepEqual(err, tt.err) { t.Errorf("ParseCIDR(%q) = %v, %v, %v; want %v, %v, %v", tt.in, ip, mask, err, tt.ip, tt.mask, tt.err) } }