Commit 3e9264c9 authored by Mikio Hara's avatar Mikio Hara

net: add support for Zone of IPNet

This change adds Zone field to IPNet structure for making it possible to
determine which network interface is associated with IPv6 link-local
address. Also makes ParseCIDR and IPNet.String capable handling literal
IPv6 address prefixes with zone identifier.

Fixes #14518.

Change-Id: I8f8a40d3b4f500ffef25728d4995651379d8408a
Reviewed-on: https://go-review.googlesource.com/19946Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent 45bb8874
...@@ -166,6 +166,7 @@ func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (*IPNet, error) { ...@@ -166,6 +166,7 @@ func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (*IPNet, error) {
// link-local address as the kernel-internal form. // link-local address as the kernel-internal form.
if ifa.IP.IsLinkLocalUnicast() { if ifa.IP.IsLinkLocalUnicast() {
ifa.IP[2], ifa.IP[3] = 0, 0 ifa.IP[2], ifa.IP[3] = 0, 0
ifa.Zone = ifi.Name
} }
} }
if ifa.IP == nil || ifa.Mask == nil { if ifa.IP == nil || ifa.Mask == nil {
......
...@@ -193,6 +193,9 @@ func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRou ...@@ -193,6 +193,9 @@ func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRou
case syscall.AF_INET6: case syscall.AF_INET6:
ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)} ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)}
copy(ifa.IP, a.Value[:]) copy(ifa.IP, a.Value[:])
if ifa.IP.IsLinkLocalUnicast() {
ifa.Zone = ifi.Name
}
return ifa return ifa
} }
} }
......
...@@ -221,6 +221,10 @@ func testAddrs(t *testing.T, ifat []Addr) (naf4, naf6 int) { ...@@ -221,6 +221,10 @@ func testAddrs(t *testing.T, ifat []Addr) (naf4, naf6 int) {
t.Errorf("unexpected prefix length for IPv6 loopback: %d/%d", prefixLen, maxPrefixLen) t.Errorf("unexpected prefix length for IPv6 loopback: %d/%d", prefixLen, maxPrefixLen)
continue continue
} }
if ifa.IP.IsLinkLocalUnicast() && ifa.Zone == "" {
t.Errorf("no IPv6 zone identifier found: %#v", ifa)
continue
}
naf6++ naf6++
} }
t.Logf("interface address %q", ifa.String()) t.Logf("interface address %q", ifa.String())
...@@ -239,7 +243,7 @@ func testAddrs(t *testing.T, ifat []Addr) (naf4, naf6 int) { ...@@ -239,7 +243,7 @@ func testAddrs(t *testing.T, ifat []Addr) (naf4, naf6 int) {
if ifa.IP.To16() != nil && ifa.IP.To4() == nil { if ifa.IP.To16() != nil && ifa.IP.To4() == nil {
naf6++ naf6++
} }
t.Logf("interface address %s", ifa.String()) t.Logf("interface address %q", ifa.String())
default: default:
t.Errorf("unexpected type: %T", ifa) t.Errorf("unexpected type: %T", ifa)
} }
......
...@@ -158,6 +158,9 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) { ...@@ -158,6 +158,9 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
l = addrPrefixLen(pfx6, IP(sa.Addr[:])) l = addrPrefixLen(pfx6, IP(sa.Addr[:]))
} }
ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(l, 8*IPv6len)} ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(l, 8*IPv6len)}
if ifa.IP.IsLinkLocalUnicast() {
ifa.Zone = syscall.UTF16ToString((*(*[10000]uint16)(unsafe.Pointer(aa.FriendlyName)))[:])
}
copy(ifa.IP, sa.Addr[:]) copy(ifa.IP, sa.Addr[:])
ifat = append(ifat, ifa) ifat = append(ifat, ifa)
} }
......
...@@ -36,6 +36,7 @@ type IPMask []byte ...@@ -36,6 +36,7 @@ type IPMask []byte
type IPNet struct { type IPNet struct {
IP IP // network number IP IP // network number
Mask IPMask // network mask Mask IPMask // network mask
Zone string // IPv6 scoped addressing zone
} }
// IPv4 returns the IP address (in 16-byte form) of the // IPv4 returns the IP address (in 16-byte form) of the
...@@ -494,11 +495,15 @@ func (n *IPNet) String() string { ...@@ -494,11 +495,15 @@ func (n *IPNet) String() string {
if nn == nil || m == nil { if nn == nil || m == nil {
return "<nil>" return "<nil>"
} }
ip := nn.String()
if n.Zone != "" {
ip = ip + "%" + n.Zone
}
l := simpleMaskLength(m) l := simpleMaskLength(m)
if l == -1 { if l == -1 {
return nn.String() + "/" + m.String() return ip + "/" + m.String()
} }
return nn.String() + "/" + uitoa(uint(l)) return ip + "/" + uitoa(uint(l))
} }
// Parse IPv4 address (d.d.d.d). // Parse IPv4 address (d.d.d.d).
...@@ -670,17 +675,18 @@ func ParseCIDR(s string) (IP, *IPNet, error) { ...@@ -670,17 +675,18 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
if i < 0 { if i < 0 {
return nil, nil, &ParseError{Type: "CIDR address", Text: s} return nil, nil, &ParseError{Type: "CIDR address", Text: s}
} }
var zone string
addr, mask := s[:i], s[i+1:] addr, mask := s[:i], s[i+1:]
iplen := IPv4len iplen := IPv4len
ip := parseIPv4(addr) ip := parseIPv4(addr)
if ip == nil { if ip == nil {
iplen = IPv6len iplen = IPv6len
ip, _ = parseIPv6(addr, false) ip, zone = parseIPv6(addr, true)
} }
n, i, ok := dtoi(mask, 0) n, i, ok := dtoi(mask, 0)
if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen { if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
return nil, nil, &ParseError{Type: "CIDR address", Text: s} return nil, nil, &ParseError{Type: "CIDR address", Text: s}
} }
m := CIDRMask(n, 8*iplen) m := CIDRMask(n, 8*iplen)
return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil return ip, &IPNet{IP: ip.Mask(m), Mask: m, Zone: zone}, nil
} }
...@@ -327,6 +327,9 @@ var parseCIDRTests = []struct { ...@@ -327,6 +327,9 @@ var parseCIDRTests = []struct {
{"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil}, {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil},
{"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
{"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
{"fe80::%en0/64", ParseIP("fe80::"), &IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, nil},
{"fe80::1%en0/64", ParseIP("fe80::1"), &IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, nil},
{"192.168.1.1/255.255.255.0", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/255.255.255.0"}}, {"192.168.1.1/255.255.255.0", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/255.255.255.0"}},
{"192.168.1.1/35", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/35"}}, {"192.168.1.1/35", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/35"}},
{"2001:db8::1/-1", nil, nil, &ParseError{Type: "CIDR address", Text: "2001:db8::1/-1"}}, {"2001:db8::1/-1", nil, nil, &ParseError{Type: "CIDR address", Text: "2001:db8::1/-1"}},
...@@ -373,8 +376,13 @@ var ipNetStringTests = []struct { ...@@ -373,8 +376,13 @@ var ipNetStringTests = []struct {
out string out string
}{ }{
{&IPNet{IP: IPv4(192, 168, 1, 0), Mask: CIDRMask(26, 32)}, "192.168.1.0/26"}, {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: CIDRMask(26, 32)}, "192.168.1.0/26"},
{&IPNet{IP: IPv4(192, 168, 1, 1), Mask: CIDRMask(26, 32)}, "192.168.1.1/26"},
{&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"}, {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"},
{&IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, "fe80::%en0/64"},
{&IPNet{IP: ParseIP("fe80::1"), Mask: CIDRMask(64, 128), Zone: "en0"}, "fe80::1%en0/64"},
{&IPNet{IP: ParseIP("fe80::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::")), Zone: "en0"}, "fe80::%en0/8000f1230000cafe0000000000000000"},
{&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"}, {&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"},
{&IPNet{IP: ParseIP("2001:db8::1"), Mask: CIDRMask(55, 128)}, "2001:db8::1/55"},
{&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"}, {&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"},
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment