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:
parent
946cb909e6
commit
620de250a4
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue