Prefer matching labels and longest matching prefix

...when performing source address selection for IPv6.

These are defined in RFC 6724 section 5 rule 6 (prefer matching label)
and rule 8 (use longest matching prefix).

This change also considers ULA of global scope instead of its own scope,
as per RFC 6724 section 3.1:

   Also, note that ULAs are considered as global, not
   site-local, scope but are handled via the prefix policy table as
   discussed in Section 10.6.

Test: stack_test.TestIPv6SourceAddressSelectionScope

Startblock:
  has LGTM from peterjohnston
  and then
  add reviewer brunodalbo
PiperOrigin-RevId: 348580996
This commit is contained in:
Ghanan Gowripalan 2020-12-21 22:23:18 -08:00 committed by gVisor bot
parent 946cb909e6
commit 620de250a4
6 changed files with 340 additions and 119 deletions

View File

@ -18,7 +18,6 @@ import (
"crypto/sha256"
"encoding/binary"
"fmt"
"strings"
"gvisor.dev/gvisor/pkg/tcpip"
)
@ -153,13 +152,17 @@ const (
// IPv6EmptySubnet is the empty IPv6 subnet. It may also be known as the
// catch-all or wildcard subnet. That is, all IPv6 addresses are considered to
// be contained within this subnet.
var IPv6EmptySubnet = func() tcpip.Subnet {
subnet, err := tcpip.NewSubnet(IPv6Any, tcpip.AddressMask(IPv6Any))
if err != nil {
panic(err)
}
return subnet
}()
var IPv6EmptySubnet = tcpip.AddressWithPrefix{
Address: IPv6Any,
PrefixLen: 0,
}.Subnet()
// IPv4MappedIPv6Subnet is the prefix for an IPv4 mapped IPv6 address as defined
// by RFC 4291 section 2.5.5.
var IPv4MappedIPv6Subnet = tcpip.AddressWithPrefix{
Address: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00",
PrefixLen: 96,
}.Subnet()
// IPv6LinkLocalPrefix is the prefix for IPv6 link-local addresses, as defined
// by RFC 4291 section 2.5.6.
@ -293,7 +296,7 @@ func IsV4MappedAddress(addr tcpip.Address) bool {
return false
}
return strings.HasPrefix(string(addr), "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff")
return IPv4MappedIPv6Subnet.Contains(addr)
}
// IsV6MulticastAddress determines if the provided address is an IPv6
@ -399,17 +402,6 @@ func IsV6LinkLocalMulticastAddress(addr tcpip.Address) bool {
return IsV6MulticastAddress(addr) && addr[ipv6MulticastAddressScopeByteIdx]&ipv6MulticastAddressScopeMask == ipv6LinkLocalMulticastScope
}
// IsV6UniqueLocalAddress determines if the provided address is an IPv6
// unique-local address (within the prefix FC00::/7).
func IsV6UniqueLocalAddress(addr tcpip.Address) bool {
if len(addr) != IPv6AddressSize {
return false
}
// According to RFC 4193 section 3.1, a unique local address has the prefix
// FC00::/7.
return (addr[0] & 0xfe) == 0xfc
}
// AppendOpaqueInterfaceIdentifier appends a 64 bit opaque interface identifier
// (IID) to buf as outlined by RFC 7217 and returns the extended buffer.
//
@ -456,9 +448,6 @@ const (
// LinkLocalScope indicates a link-local address.
LinkLocalScope IPv6AddressScope = iota
// UniqueLocalScope indicates a unique-local address.
UniqueLocalScope
// GlobalScope indicates a global address.
GlobalScope
)
@ -476,9 +465,6 @@ func ScopeForIPv6Address(addr tcpip.Address) (IPv6AddressScope, *tcpip.Error) {
case IsV6LinkLocalAddress(addr):
return LinkLocalScope, nil
case IsV6UniqueLocalAddress(addr):
return UniqueLocalScope, nil
default:
return GlobalScope, nil
}

View File

@ -215,48 +215,6 @@ func TestLinkLocalAddrWithOpaqueIID(t *testing.T) {
}
}
func TestIsV6UniqueLocalAddress(t *testing.T) {
tests := []struct {
name string
addr tcpip.Address
expected bool
}{
{
name: "Valid Unique 1",
addr: uniqueLocalAddr1,
expected: true,
},
{
name: "Valid Unique 2",
addr: uniqueLocalAddr1,
expected: true,
},
{
name: "Link Local",
addr: linkLocalAddr,
expected: false,
},
{
name: "Global",
addr: globalAddr,
expected: false,
},
{
name: "IPv4",
addr: "\x01\x02\x03\x04",
expected: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if got := header.IsV6UniqueLocalAddress(test.addr); got != test.expected {
t.Errorf("got header.IsV6UniqueLocalAddress(%s) = %t, want = %t", test.addr, got, test.expected)
}
})
}
}
func TestIsV6LinkLocalMulticastAddress(t *testing.T) {
tests := []struct {
name string
@ -346,7 +304,7 @@ func TestScopeForIPv6Address(t *testing.T) {
{
name: "Unique Local",
addr: uniqueLocalAddr1,
scope: header.UniqueLocalScope,
scope: header.GlobalScope,
err: nil,
},
{

View File

@ -61,6 +61,108 @@ const (
buckets = 2048
)
// policyTable is the default policy table defined in RFC 6724 section 2.1.
//
// A more human-readable version:
//
// Prefix Precedence Label
// ::1/128 50 0
// ::/0 40 1
// ::ffff:0:0/96 35 4
// 2002::/16 30 2
// 2001::/32 5 5
// fc00::/7 3 13
// ::/96 1 3
// fec0::/10 1 11
// 3ffe::/16 1 12
//
// The table is sorted by prefix length so longest-prefix match can be easily
// achieved.
//
// We willingly left out ::/96, fec0::/10 and 3ffe::/16 since those prefix
// assignments are deprecated.
//
// As per RFC 4291 section 2.5.5.1 (for ::/96),
//
// The "IPv4-Compatible IPv6 address" is now deprecated because the
// current IPv6 transition mechanisms no longer use these addresses.
// New or updated implementations are not required to support this
// address type.
//
// As per RFC 3879 section 4 (for fec0::/10),
//
// This document formally deprecates the IPv6 site-local unicast prefix
// defined in [RFC3513], i.e., 1111111011 binary or FEC0::/10.
//
// As per RFC 3701 section 1 (for 3ffe::/16),
//
// As clearly stated in [TEST-NEW], the addresses for the 6bone are
// temporary and will be reclaimed in the future. It further states
// that all users of these addresses (within the 3FFE::/16 prefix) will
// be required to renumber at some time in the future.
//
// and section 2,
//
// Thus after the pTLA allocation cutoff date January 1, 2004, it is
// REQUIRED that no new 6bone 3FFE pTLAs be allocated.
//
// MUST NOT BE MODIFIED.
var policyTable = [...]struct {
subnet tcpip.Subnet
label uint8
}{
// ::1/128
{
subnet: header.IPv6Loopback.WithPrefix().Subnet(),
label: 0,
},
// ::ffff:0:0/96
{
subnet: header.IPv4MappedIPv6Subnet,
label: 4,
},
// 2001::/32 (Teredo prefix as per RFC 4380 section 2.6).
{
subnet: tcpip.AddressWithPrefix{
Address: "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
PrefixLen: 32,
}.Subnet(),
label: 5,
},
// 2002::/16 (6to4 prefix as per RFC 3056 section 2).
{
subnet: tcpip.AddressWithPrefix{
Address: "\x20\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
PrefixLen: 16,
}.Subnet(),
label: 2,
},
// fc00::/7 (Unique local addresses as per RFC 4193 section 3.1).
{
subnet: tcpip.AddressWithPrefix{
Address: "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
PrefixLen: 7,
}.Subnet(),
label: 13,
},
// ::/0
{
subnet: header.IPv6EmptySubnet,
label: 1,
},
}
func getLabel(addr tcpip.Address) uint8 {
for _, p := range policyTable {
if p.subnet.Contains(addr) {
return p.label
}
}
panic(fmt.Sprintf("should have a label for address = %s", addr))
}
var _ stack.GroupAddressableEndpoint = (*endpoint)(nil)
var _ stack.AddressableEndpoint = (*endpoint)(nil)
var _ stack.NetworkEndpoint = (*endpoint)(nil)
@ -1373,7 +1475,11 @@ func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr tcpip.Address
// RFC 6724 section 5.
type addrCandidate struct {
addressEndpoint stack.AddressEndpoint
addr tcpip.Address
scope header.IPv6AddressScope
label uint8
matchingPrefix uint8
}
if len(remoteAddr) == 0 {
@ -1400,7 +1506,10 @@ func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr tcpip.Address
cs = append(cs, addrCandidate{
addressEndpoint: addressEndpoint,
addr: addr,
scope: scope,
label: getLabel(addr),
matchingPrefix: remoteAddr.MatchingPrefix(addr),
})
return true
@ -1412,18 +1521,20 @@ func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr tcpip.Address
panic(fmt.Sprintf("header.ScopeForIPv6Address(%s): %s", remoteAddr, err))
}
remoteLabel := getLabel(remoteAddr)
// Sort the addresses as per RFC 6724 section 5 rules 1-3.
//
// TODO(b/146021396): Implement rules 4-8 of RFC 6724 section 5.
// TODO(b/146021396): Implement rules 4, 5 of RFC 6724 section 5.
sort.Slice(cs, func(i, j int) bool {
sa := cs[i]
sb := cs[j]
// Prefer same address as per RFC 6724 section 5 rule 1.
if sa.addressEndpoint.AddressWithPrefix().Address == remoteAddr {
if sa.addr == remoteAddr {
return true
}
if sb.addressEndpoint.AddressWithPrefix().Address == remoteAddr {
if sb.addr == remoteAddr {
return false
}
@ -1440,11 +1551,29 @@ func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr tcpip.Address
return sbDep
}
// Prefer matching label as per RFC 6724 section 5 rule 6.
if sa, sb := sa.label == remoteLabel, sb.label == remoteLabel; sa != sb {
if sa {
return true
}
if sb {
return false
}
}
// Prefer temporary addresses as per RFC 6724 section 5 rule 7.
if saTemp, sbTemp := sa.addressEndpoint.ConfigType() == stack.AddressConfigSlaacTemp, sb.addressEndpoint.ConfigType() == stack.AddressConfigSlaacTemp; saTemp != sbTemp {
return saTemp
}
// Use longest matching prefix as per RFC 6724 section 5 rule 8.
if sa.matchingPrefix > sb.matchingPrefix {
return true
}
if sb.matchingPrefix > sa.matchingPrefix {
return false
}
// sa and sb are equal, return the endpoint that is closest to the front of
// the primary endpoint list.
return i < j

View File

@ -2726,8 +2726,16 @@ func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
uniqueLocalAddr2 = tcpip.Address("\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
globalAddr1 = tcpip.Address("\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
globalAddr2 = tcpip.Address("\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
nicID = 1
lifetimeSeconds = 9999
globalAddr3 = tcpip.Address("\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03")
ipv4MappedIPv6Addr1 = tcpip.Address("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x01")
ipv4MappedIPv6Addr2 = tcpip.Address("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x02")
toredoAddr1 = tcpip.Address("\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
toredoAddr2 = tcpip.Address("\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
ipv6ToIPv4Addr1 = tcpip.Address("\x20\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
ipv6ToIPv4Addr2 = tcpip.Address("\x20\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
nicID = 1
lifetimeSeconds = 9999
)
prefix1, _, stableGlobalAddr1 := prefixSubnetAddr(0, linkAddr1)
@ -2744,139 +2752,191 @@ func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
slaacPrefixForTempAddrBeforeNICAddrAdd tcpip.AddressWithPrefix
nicAddrs []tcpip.Address
slaacPrefixForTempAddrAfterNICAddrAdd tcpip.AddressWithPrefix
connectAddr tcpip.Address
remoteAddr tcpip.Address
expectedLocalAddr tcpip.Address
}{
// Test Rule 1 of RFC 6724 section 5.
// Test Rule 1 of RFC 6724 section 5 (prefer same address).
{
name: "Same Global most preferred (last address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: globalAddr1,
nicAddrs: []tcpip.Address{linkLocalAddr1, globalAddr1},
remoteAddr: globalAddr1,
expectedLocalAddr: globalAddr1,
},
{
name: "Same Global most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
connectAddr: globalAddr1,
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1},
remoteAddr: globalAddr1,
expectedLocalAddr: globalAddr1,
},
{
name: "Same Link Local most preferred (last address)",
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1, linkLocalAddr1},
connectAddr: linkLocalAddr1,
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1},
remoteAddr: linkLocalAddr1,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Same Link Local most preferred (first address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: linkLocalAddr1,
nicAddrs: []tcpip.Address{linkLocalAddr1, globalAddr1},
remoteAddr: linkLocalAddr1,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Same Unique Local most preferred (last address)",
nicAddrs: []tcpip.Address{uniqueLocalAddr1, globalAddr1, linkLocalAddr1},
connectAddr: uniqueLocalAddr1,
nicAddrs: []tcpip.Address{uniqueLocalAddr1, globalAddr1},
remoteAddr: uniqueLocalAddr1,
expectedLocalAddr: uniqueLocalAddr1,
},
{
name: "Same Unique Local most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
connectAddr: uniqueLocalAddr1,
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1},
remoteAddr: uniqueLocalAddr1,
expectedLocalAddr: uniqueLocalAddr1,
},
// Test Rule 2 of RFC 6724 section 5.
// Test Rule 2 of RFC 6724 section 5 (prefer appropriate scope).
{
name: "Global most preferred (last address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: globalAddr2,
nicAddrs: []tcpip.Address{linkLocalAddr1, globalAddr1},
remoteAddr: globalAddr2,
expectedLocalAddr: globalAddr1,
},
{
name: "Global most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
connectAddr: globalAddr2,
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1},
remoteAddr: globalAddr2,
expectedLocalAddr: globalAddr1,
},
{
name: "Link Local most preferred (last address)",
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1, linkLocalAddr1},
connectAddr: linkLocalAddr2,
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1},
remoteAddr: linkLocalAddr2,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Link Local most preferred (first address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: linkLocalAddr2,
nicAddrs: []tcpip.Address{linkLocalAddr1, globalAddr1},
remoteAddr: linkLocalAddr2,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Link Local most preferred for link local multicast (last address)",
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1, linkLocalAddr1},
connectAddr: linkLocalMulticastAddr,
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1},
remoteAddr: linkLocalMulticastAddr,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Link Local most preferred for link local multicast (first address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: linkLocalMulticastAddr,
nicAddrs: []tcpip.Address{linkLocalAddr1, globalAddr1},
remoteAddr: linkLocalMulticastAddr,
expectedLocalAddr: linkLocalAddr1,
},
// Test Rule 6 of 6724 section 5 (prefer matching label).
{
name: "Unique Local most preferred (last address)",
nicAddrs: []tcpip.Address{uniqueLocalAddr1, globalAddr1, linkLocalAddr1},
connectAddr: uniqueLocalAddr2,
nicAddrs: []tcpip.Address{uniqueLocalAddr1, globalAddr1, ipv4MappedIPv6Addr1, toredoAddr1, ipv6ToIPv4Addr1},
remoteAddr: uniqueLocalAddr2,
expectedLocalAddr: uniqueLocalAddr1,
},
{
name: "Unique Local most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
connectAddr: uniqueLocalAddr2,
nicAddrs: []tcpip.Address{globalAddr1, ipv4MappedIPv6Addr1, toredoAddr1, ipv6ToIPv4Addr1, uniqueLocalAddr1},
remoteAddr: uniqueLocalAddr2,
expectedLocalAddr: uniqueLocalAddr1,
},
{
name: "Toredo most preferred (first address)",
nicAddrs: []tcpip.Address{toredoAddr1, uniqueLocalAddr1, globalAddr1, ipv4MappedIPv6Addr1, ipv6ToIPv4Addr1},
remoteAddr: toredoAddr2,
expectedLocalAddr: toredoAddr1,
},
{
name: "Toredo most preferred (last address)",
nicAddrs: []tcpip.Address{globalAddr1, ipv4MappedIPv6Addr1, ipv6ToIPv4Addr1, uniqueLocalAddr1, toredoAddr1},
remoteAddr: toredoAddr2,
expectedLocalAddr: toredoAddr1,
},
{
name: "6To4 most preferred (first address)",
nicAddrs: []tcpip.Address{ipv6ToIPv4Addr1, toredoAddr1, uniqueLocalAddr1, globalAddr1, ipv4MappedIPv6Addr1},
remoteAddr: ipv6ToIPv4Addr2,
expectedLocalAddr: ipv6ToIPv4Addr1,
},
{
name: "6To4 most preferred (last address)",
nicAddrs: []tcpip.Address{globalAddr1, ipv4MappedIPv6Addr1, uniqueLocalAddr1, toredoAddr1, ipv6ToIPv4Addr1},
remoteAddr: ipv6ToIPv4Addr2,
expectedLocalAddr: ipv6ToIPv4Addr1,
},
{
name: "IPv4 mapped IPv6 most preferred (first address)",
nicAddrs: []tcpip.Address{ipv4MappedIPv6Addr1, ipv6ToIPv4Addr1, toredoAddr1, uniqueLocalAddr1, globalAddr1},
remoteAddr: ipv4MappedIPv6Addr2,
expectedLocalAddr: ipv4MappedIPv6Addr1,
},
{
name: "IPv4 mapped IPv6 most preferred (last address)",
nicAddrs: []tcpip.Address{globalAddr1, ipv6ToIPv4Addr1, uniqueLocalAddr1, toredoAddr1, ipv4MappedIPv6Addr1},
remoteAddr: ipv4MappedIPv6Addr2,
expectedLocalAddr: ipv4MappedIPv6Addr1,
},
// Test Rule 7 of RFC 6724 section 5.
// Test Rule 7 of RFC 6724 section 5 (prefer temporary addresses).
{
name: "Temp Global most preferred (last address)",
slaacPrefixForTempAddrBeforeNICAddrAdd: prefix1,
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
connectAddr: globalAddr2,
remoteAddr: globalAddr2,
expectedLocalAddr: tempGlobalAddr1,
},
{
name: "Temp Global most preferred (first address)",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
slaacPrefixForTempAddrAfterNICAddrAdd: prefix1,
connectAddr: globalAddr2,
remoteAddr: globalAddr2,
expectedLocalAddr: tempGlobalAddr1,
},
// Test Rule 8 of RFC 6724 section 5 (use longest matching prefix).
{
name: "Longest prefix matched most preferred (first address)",
nicAddrs: []tcpip.Address{globalAddr2, globalAddr1},
remoteAddr: globalAddr3,
expectedLocalAddr: globalAddr2,
},
{
name: "Longest prefix matched most preferred (last address)",
nicAddrs: []tcpip.Address{globalAddr1, globalAddr2},
remoteAddr: globalAddr3,
expectedLocalAddr: globalAddr2,
},
// Test returning the endpoint that is closest to the front when
// candidate addresses are "equal" from the perspective of RFC 6724
// section 5.
{
name: "Unique Local for Global",
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, uniqueLocalAddr2},
connectAddr: globalAddr2,
remoteAddr: globalAddr2,
expectedLocalAddr: uniqueLocalAddr1,
},
{
name: "Link Local for Global",
nicAddrs: []tcpip.Address{linkLocalAddr1, linkLocalAddr2},
connectAddr: globalAddr2,
remoteAddr: globalAddr2,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Link Local for Unique Local",
nicAddrs: []tcpip.Address{linkLocalAddr1, linkLocalAddr2},
connectAddr: uniqueLocalAddr2,
remoteAddr: uniqueLocalAddr2,
expectedLocalAddr: linkLocalAddr1,
},
{
name: "Temp Global for Global",
slaacPrefixForTempAddrBeforeNICAddrAdd: prefix1,
slaacPrefixForTempAddrAfterNICAddrAdd: prefix2,
connectAddr: globalAddr1,
remoteAddr: globalAddr1,
expectedLocalAddr: tempGlobalAddr2,
},
}
@ -2898,12 +2958,6 @@ func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
if err := s.CreateNIC(nicID, e); err != nil {
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
}
s.SetRouteTable([]tcpip.Route{{
Destination: header.IPv6EmptySubnet,
Gateway: llAddr3,
NIC: nicID,
}})
s.AddLinkAddress(nicID, llAddr3, linkAddr3)
if test.slaacPrefixForTempAddrBeforeNICAddrAdd != (tcpip.AddressWithPrefix{}) {
e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr3, 0, test.slaacPrefixForTempAddrBeforeNICAddrAdd, true, true, lifetimeSeconds, lifetimeSeconds))
@ -2923,7 +2977,23 @@ func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
t.FailNow()
}
if got := addrForNewConnectionTo(t, s, tcpip.FullAddress{Addr: test.connectAddr, NIC: nicID, Port: 1234}); got != test.expectedLocalAddr {
netEP, err := s.GetNetworkEndpoint(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("s.GetNetworkEndpoint(%d, %d): %s", nicID, header.IPv6ProtocolNumber, err)
}
addressableEndpoint, ok := netEP.(stack.AddressableEndpoint)
if !ok {
t.Fatal("network endpoint is not addressable")
}
addressEP := addressableEndpoint.AcquireOutgoingPrimaryAddress(test.remoteAddr, false /* allowExpired */)
if addressEP == nil {
t.Fatal("expected a non-nil address endpoint")
}
defer addressEP.DecRef()
if got := addressEP.AddressWithPrefix().Address; got != test.expectedLocalAddr {
t.Errorf("got local address = %s, want = %s", got, test.expectedLocalAddr)
}
})

View File

@ -258,6 +258,44 @@ func (a Address) Unspecified() bool {
return true
}
// MatchingPrefix returns the matching prefix length in bits.
//
// Panics if b and a have different lengths.
func (a Address) MatchingPrefix(b Address) uint8 {
const bitsInAByte = 8
if len(a) != len(b) {
panic(fmt.Sprintf("addresses %s and %s do not have the same length", a, b))
}
var prefix uint8
for i := range a {
aByte := a[i]
bByte := b[i]
if aByte == bByte {
prefix += bitsInAByte
continue
}
// Count the remaining matching bits in the byte from MSbit to LSBbit.
mask := uint8(1) << (bitsInAByte - 1)
for {
if aByte&mask == bByte&mask {
prefix++
mask >>= 1
continue
}
break
}
break
}
return prefix
}
// AddressMask is a bitmask for an address.
type AddressMask string

View File

@ -270,3 +270,43 @@ func TestAddressUnspecified(t *testing.T) {
})
}
}
func TestAddressMatchingPrefix(t *testing.T) {
tests := []struct {
addrA Address
addrB Address
prefix uint8
}{
{
addrA: "\x01\x01",
addrB: "\x01\x01",
prefix: 16,
},
{
addrA: "\x01\x01",
addrB: "\x01\x00",
prefix: 15,
},
{
addrA: "\x01\x01",
addrB: "\x81\x00",
prefix: 0,
},
{
addrA: "\x01\x01",
addrB: "\x01\x80",
prefix: 8,
},
{
addrA: "\x01\x01",
addrB: "\x02\x80",
prefix: 6,
},
}
for _, test := range tests {
if got := test.addrA.MatchingPrefix(test.addrB); got != test.prefix {
t.Errorf("got (%s).MatchingPrefix(%s) = %d, want = %d", test.addrA, test.addrB, got, test.prefix)
}
}
}