mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Do Source Address Selection when choosing an IPv6 source address
Do Source Address Selection when choosing an IPv6 source address as per RFC 6724
section 5 rules 1-3:
1) Prefer same address
2) Prefer appropriate scope
3) Avoid deprecated addresses.
A later change will update Source Address Selection to follow rules 4-8.
Tests:
Rule 1 & 2: stack.TestIPv6SourceAddressSelectionScopeAndSameAddress,
Rule 3: stack.TestAutoGenAddrTimerDeprecation,
stack.TestAutoGenAddrDeprecateFromPI
PiperOrigin-RevId: 289559373
This commit is contained in:
committed by
gVisor bot
parent
debd213da6
commit
1ad8381eac
@@ -333,6 +333,17 @@ func IsV6LinkLocalAddress(addr tcpip.Address) bool {
|
||||
return addr[0] == 0xfe && (addr[1]&0xc0) == 0x80
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
@@ -371,3 +382,35 @@ func LinkLocalAddrWithOpaqueIID(nicName string, dadCounter uint8, secretKey []by
|
||||
|
||||
return tcpip.Address(AppendOpaqueInterfaceIdentifier(lladdrb[:IIDOffsetInIPv6Address], IPv6LinkLocalPrefix.Subnet(), nicName, dadCounter, secretKey))
|
||||
}
|
||||
|
||||
// IPv6AddressScope is the scope of an IPv6 address.
|
||||
type IPv6AddressScope int
|
||||
|
||||
const (
|
||||
// LinkLocalScope indicates a link-local address.
|
||||
LinkLocalScope IPv6AddressScope = iota
|
||||
|
||||
// UniqueLocalScope indicates a unique-local address.
|
||||
UniqueLocalScope
|
||||
|
||||
// GlobalScope indicates a global address.
|
||||
GlobalScope
|
||||
)
|
||||
|
||||
// ScopeForIPv6Address returns the scope for an IPv6 address.
|
||||
func ScopeForIPv6Address(addr tcpip.Address) (IPv6AddressScope, *tcpip.Error) {
|
||||
if len(addr) != IPv6AddressSize {
|
||||
return GlobalScope, tcpip.ErrBadAddress
|
||||
}
|
||||
|
||||
switch {
|
||||
case IsV6LinkLocalAddress(addr):
|
||||
return LinkLocalScope, nil
|
||||
|
||||
case IsV6UniqueLocalAddress(addr):
|
||||
return UniqueLocalScope, nil
|
||||
|
||||
default:
|
||||
return GlobalScope, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,13 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
const linkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06")
|
||||
const (
|
||||
linkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06")
|
||||
linkLocalAddr = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
|
||||
uniqueLocalAddr1 = tcpip.Address("\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
|
||||
uniqueLocalAddr2 = tcpip.Address("\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
|
||||
globalAddr = tcpip.Address("\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
|
||||
)
|
||||
|
||||
func TestEthernetAdddressToModifiedEUI64(t *testing.T) {
|
||||
expectedIID := [header.IIDSize]byte{0, 2, 3, 255, 254, 4, 5, 6}
|
||||
@@ -206,3 +212,91 @@ 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 TestScopeForIPv6Address(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
addr tcpip.Address
|
||||
scope header.IPv6AddressScope
|
||||
err *tcpip.Error
|
||||
}{
|
||||
{
|
||||
name: "Unique Local",
|
||||
addr: uniqueLocalAddr1,
|
||||
scope: header.UniqueLocalScope,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Link Local",
|
||||
addr: linkLocalAddr,
|
||||
scope: header.LinkLocalScope,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Global",
|
||||
addr: globalAddr,
|
||||
scope: header.GlobalScope,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "IPv4",
|
||||
addr: "\x01\x02\x03\x04",
|
||||
scope: header.GlobalScope,
|
||||
err: tcpip.ErrBadAddress,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, err := header.ScopeForIPv6Address(test.addr)
|
||||
if err != test.err {
|
||||
t.Errorf("got header.IsV6UniqueLocalAddress(%s) = (_, %v), want = (_, %v)", test.addr, err, test.err)
|
||||
}
|
||||
if got != test.scope {
|
||||
t.Errorf("got header.IsV6UniqueLocalAddress(%s) = (%d, _), want = (%d, _)", test.addr, got, test.scope)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1732,9 +1732,11 @@ func stackAndNdpDispatcherWithDefaultRoute(t *testing.T, nicID tcpip.NICID) (*nd
|
||||
return ndpDisp, e, s
|
||||
}
|
||||
|
||||
// addrForNewConnection returns the local address used when creating a new
|
||||
// connection.
|
||||
func addrForNewConnection(t *testing.T, s *stack.Stack) tcpip.Address {
|
||||
// addrForNewConnectionTo returns the local address used when creating a new
|
||||
// connection to addr.
|
||||
func addrForNewConnectionTo(t *testing.T, s *stack.Stack, addr tcpip.FullAddress) tcpip.Address {
|
||||
t.Helper()
|
||||
|
||||
wq := waiter.Queue{}
|
||||
we, ch := waiter.NewChannelEntry(nil)
|
||||
wq.EventRegister(&we, waiter.EventIn)
|
||||
@@ -1748,8 +1750,8 @@ func addrForNewConnection(t *testing.T, s *stack.Stack) tcpip.Address {
|
||||
if err := ep.SetSockOptBool(tcpip.V6OnlyOption, true); err != nil {
|
||||
t.Fatalf("SetSockOpt(tcpip.V6OnlyOption, true): %s", err)
|
||||
}
|
||||
if err := ep.Connect(dstAddr); err != nil {
|
||||
t.Fatalf("ep.Connect(%+v): %s", dstAddr, err)
|
||||
if err := ep.Connect(addr); err != nil {
|
||||
t.Fatalf("ep.Connect(%+v): %s", addr, err)
|
||||
}
|
||||
got, err := ep.GetLocalAddress()
|
||||
if err != nil {
|
||||
@@ -1758,9 +1760,19 @@ func addrForNewConnection(t *testing.T, s *stack.Stack) tcpip.Address {
|
||||
return got.Addr
|
||||
}
|
||||
|
||||
// addrForNewConnection returns the local address used when creating a new
|
||||
// connection.
|
||||
func addrForNewConnection(t *testing.T, s *stack.Stack) tcpip.Address {
|
||||
t.Helper()
|
||||
|
||||
return addrForNewConnectionTo(t, s, dstAddr)
|
||||
}
|
||||
|
||||
// addrForNewConnectionWithAddr returns the local address used when creating a
|
||||
// new connection with a specific local address.
|
||||
func addrForNewConnectionWithAddr(t *testing.T, s *stack.Stack, addr tcpip.FullAddress) tcpip.Address {
|
||||
t.Helper()
|
||||
|
||||
wq := waiter.Queue{}
|
||||
we, ch := waiter.NewChannelEntry(nil)
|
||||
wq.EventRegister(&we, waiter.EventIn)
|
||||
|
||||
+109
-6
@@ -15,6 +15,8 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -251,13 +253,17 @@ func (n *NIC) setSpoofing(enable bool) {
|
||||
n.mu.Unlock()
|
||||
}
|
||||
|
||||
// primaryEndpoint returns the primary endpoint of n for the given network
|
||||
// protocol.
|
||||
//
|
||||
// primaryEndpoint will return the first non-deprecated endpoint if such an
|
||||
// endpoint exists. If no non-deprecated endpoint exists, the first deprecated
|
||||
// endpoint will be returned.
|
||||
func (n *NIC) primaryEndpoint(protocol tcpip.NetworkProtocolNumber) *referencedNetworkEndpoint {
|
||||
// endpoint exists for the given protocol and remoteAddr. If no non-deprecated
|
||||
// endpoint exists, the first deprecated endpoint will be returned.
|
||||
//
|
||||
// If an IPv6 primary endpoint is requested, Source Address Selection (as
|
||||
// defined by RFC 6724 section 5) will be performed.
|
||||
func (n *NIC) primaryEndpoint(protocol tcpip.NetworkProtocolNumber, remoteAddr tcpip.Address) *referencedNetworkEndpoint {
|
||||
if protocol == header.IPv6ProtocolNumber && remoteAddr != "" {
|
||||
return n.primaryIPv6Endpoint(remoteAddr)
|
||||
}
|
||||
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
@@ -296,6 +302,103 @@ func (n *NIC) primaryEndpoint(protocol tcpip.NetworkProtocolNumber) *referencedN
|
||||
return deprecatedEndpoint
|
||||
}
|
||||
|
||||
// ipv6AddrCandidate is an IPv6 candidate for Source Address Selection (RFC
|
||||
// 6724 section 5).
|
||||
type ipv6AddrCandidate struct {
|
||||
ref *referencedNetworkEndpoint
|
||||
scope header.IPv6AddressScope
|
||||
}
|
||||
|
||||
// primaryIPv6Endpoint returns an IPv6 endpoint following Source Address
|
||||
// Selection (RFC 6724 section 5).
|
||||
//
|
||||
// Note, only rules 1-3 are followed.
|
||||
//
|
||||
// remoteAddr must be a valid IPv6 address.
|
||||
func (n *NIC) primaryIPv6Endpoint(remoteAddr tcpip.Address) *referencedNetworkEndpoint {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
primaryAddrs := n.primary[header.IPv6ProtocolNumber]
|
||||
|
||||
if len(primaryAddrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a candidate set of available addresses we can potentially use as a
|
||||
// source address.
|
||||
cs := make([]ipv6AddrCandidate, 0, len(primaryAddrs))
|
||||
for _, r := range primaryAddrs {
|
||||
// If r is not valid for outgoing connections, it is not a valid endpoint.
|
||||
if !r.isValidForOutgoing() {
|
||||
continue
|
||||
}
|
||||
|
||||
addr := r.ep.ID().LocalAddress
|
||||
scope, err := header.ScopeForIPv6Address(addr)
|
||||
if err != nil {
|
||||
// Should never happen as we got r from the primary IPv6 endpoint list and
|
||||
// ScopeForIPv6Address only returns an error if addr is not an IPv6
|
||||
// address.
|
||||
log.Fatalf("header.ScopeForIPv6Address(%s): %s", addr, err)
|
||||
}
|
||||
|
||||
cs = append(cs, ipv6AddrCandidate{
|
||||
ref: r,
|
||||
scope: scope,
|
||||
})
|
||||
}
|
||||
|
||||
remoteScope, err := header.ScopeForIPv6Address(remoteAddr)
|
||||
if err != nil {
|
||||
// primaryIPv6Endpoint should never be called with an invalid IPv6 address.
|
||||
log.Fatalf("header.ScopeForIPv6Address(%s): %s", remoteAddr, err)
|
||||
}
|
||||
|
||||
// Sort the addresses as per RFC 6724 section 5 rules 1-3.
|
||||
//
|
||||
// TODO(b/146021396): Implement rules 4-8 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.ref.ep.ID().LocalAddress == remoteAddr {
|
||||
return true
|
||||
}
|
||||
if sb.ref.ep.ID().LocalAddress == remoteAddr {
|
||||
return false
|
||||
}
|
||||
|
||||
// Prefer appropriate scope as per RFC 6724 section 5 rule 2.
|
||||
if sa.scope < sb.scope {
|
||||
return sa.scope >= remoteScope
|
||||
} else if sb.scope < sa.scope {
|
||||
return sb.scope < remoteScope
|
||||
}
|
||||
|
||||
// Avoid deprecated addresses as per RFC 6724 section 5 rule 3.
|
||||
if saDep, sbDep := sa.ref.deprecated, sb.ref.deprecated; saDep != sbDep {
|
||||
// If sa is not deprecated, it is preferred over sb.
|
||||
return sbDep
|
||||
}
|
||||
|
||||
// sa and sb are equal, return the endpoint that is closest to the front of
|
||||
// the primary endpoint list.
|
||||
return i < j
|
||||
})
|
||||
|
||||
// Return the most preferred address that can have its reference count
|
||||
// incremented.
|
||||
for _, c := range cs {
|
||||
if r := c.ref; r.tryIncRef() {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasPermanentAddrLocked returns true if n has a permanent (including currently
|
||||
// tentative) address, addr.
|
||||
func (n *NIC) hasPermanentAddrLocked(addr tcpip.Address) bool {
|
||||
|
||||
@@ -1106,9 +1106,9 @@ func (s *Stack) GetMainNICAddress(id tcpip.NICID, protocol tcpip.NetworkProtocol
|
||||
return nic.primaryAddress(protocol), nil
|
||||
}
|
||||
|
||||
func (s *Stack) getRefEP(nic *NIC, localAddr tcpip.Address, netProto tcpip.NetworkProtocolNumber) (ref *referencedNetworkEndpoint) {
|
||||
func (s *Stack) getRefEP(nic *NIC, localAddr, remoteAddr tcpip.Address, netProto tcpip.NetworkProtocolNumber) (ref *referencedNetworkEndpoint) {
|
||||
if len(localAddr) == 0 {
|
||||
return nic.primaryEndpoint(netProto)
|
||||
return nic.primaryEndpoint(netProto, remoteAddr)
|
||||
}
|
||||
return nic.findEndpoint(netProto, localAddr, CanBePrimaryEndpoint)
|
||||
}
|
||||
@@ -1124,7 +1124,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
needRoute := !(isBroadcast || isMulticast || header.IsV6LinkLocalAddress(remoteAddr))
|
||||
if id != 0 && !needRoute {
|
||||
if nic, ok := s.nics[id]; ok {
|
||||
if ref := s.getRefEP(nic, localAddr, netProto); ref != nil {
|
||||
if ref := s.getRefEP(nic, localAddr, remoteAddr, netProto); ref != nil {
|
||||
return makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, nic.linkEP.LinkAddress(), ref, s.handleLocal && !nic.isLoopback(), multicastLoop && !nic.isLoopback()), nil
|
||||
}
|
||||
}
|
||||
@@ -1134,7 +1134,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
continue
|
||||
}
|
||||
if nic, ok := s.nics[route.NIC]; ok {
|
||||
if ref := s.getRefEP(nic, localAddr, netProto); ref != nil {
|
||||
if ref := s.getRefEP(nic, localAddr, remoteAddr, netProto); ref != nil {
|
||||
if len(remoteAddr) == 0 {
|
||||
// If no remote address was provided, then the route
|
||||
// provided will refer to the link local address.
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -2411,3 +2412,154 @@ func TestNewPEBOnPromotionToPermanent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
|
||||
const (
|
||||
linkLocalAddr1 = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
|
||||
linkLocalAddr2 = tcpip.Address("\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
|
||||
uniqueLocalAddr1 = tcpip.Address("\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
|
||||
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
|
||||
)
|
||||
|
||||
// Rule 3 is not tested here, and is instead tested by NDP's AutoGenAddr test.
|
||||
tests := []struct {
|
||||
name string
|
||||
nicAddrs []tcpip.Address
|
||||
connectAddr tcpip.Address
|
||||
expectedLocalAddr tcpip.Address
|
||||
}{
|
||||
// Test Rule 1 of RFC 6724 section 5.
|
||||
{
|
||||
name: "Same Global most preferred (last address)",
|
||||
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
|
||||
connectAddr: globalAddr1,
|
||||
expectedLocalAddr: globalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Same Global most preferred (first address)",
|
||||
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
|
||||
connectAddr: globalAddr1,
|
||||
expectedLocalAddr: globalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Same Link Local most preferred (last address)",
|
||||
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1, linkLocalAddr1},
|
||||
connectAddr: linkLocalAddr1,
|
||||
expectedLocalAddr: linkLocalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Same Link Local most preferred (first address)",
|
||||
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
|
||||
connectAddr: linkLocalAddr1,
|
||||
expectedLocalAddr: linkLocalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Same Unique Local most preferred (last address)",
|
||||
nicAddrs: []tcpip.Address{uniqueLocalAddr1, globalAddr1, linkLocalAddr1},
|
||||
connectAddr: uniqueLocalAddr1,
|
||||
expectedLocalAddr: uniqueLocalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Same Unique Local most preferred (first address)",
|
||||
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
|
||||
connectAddr: uniqueLocalAddr1,
|
||||
expectedLocalAddr: uniqueLocalAddr1,
|
||||
},
|
||||
|
||||
// Test Rule 2 of RFC 6724 section 5.
|
||||
{
|
||||
name: "Global most preferred (last address)",
|
||||
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
|
||||
connectAddr: globalAddr2,
|
||||
expectedLocalAddr: globalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Global most preferred (first address)",
|
||||
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
|
||||
connectAddr: globalAddr2,
|
||||
expectedLocalAddr: globalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Link Local most preferred (last address)",
|
||||
nicAddrs: []tcpip.Address{globalAddr1, uniqueLocalAddr1, linkLocalAddr1},
|
||||
connectAddr: linkLocalAddr2,
|
||||
expectedLocalAddr: linkLocalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Link Local most preferred (first address)",
|
||||
nicAddrs: []tcpip.Address{linkLocalAddr1, uniqueLocalAddr1, globalAddr1},
|
||||
connectAddr: linkLocalAddr2,
|
||||
expectedLocalAddr: linkLocalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Unique Local most preferred (last address)",
|
||||
nicAddrs: []tcpip.Address{uniqueLocalAddr1, globalAddr1, linkLocalAddr1},
|
||||
connectAddr: uniqueLocalAddr2,
|
||||
expectedLocalAddr: uniqueLocalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Unique Local most preferred (first address)",
|
||||
nicAddrs: []tcpip.Address{globalAddr1, linkLocalAddr1, uniqueLocalAddr1},
|
||||
connectAddr: uniqueLocalAddr2,
|
||||
expectedLocalAddr: uniqueLocalAddr1,
|
||||
},
|
||||
|
||||
// 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,
|
||||
expectedLocalAddr: uniqueLocalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Link Local for Global",
|
||||
nicAddrs: []tcpip.Address{linkLocalAddr1, linkLocalAddr2},
|
||||
connectAddr: globalAddr2,
|
||||
expectedLocalAddr: linkLocalAddr1,
|
||||
},
|
||||
{
|
||||
name: "Link Local for Unique Local",
|
||||
nicAddrs: []tcpip.Address{linkLocalAddr1, linkLocalAddr2},
|
||||
connectAddr: uniqueLocalAddr2,
|
||||
expectedLocalAddr: linkLocalAddr1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
e := channel.New(0, 1280, linkAddr1)
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
|
||||
TransportProtocols: []stack.TransportProtocol{udp.NewProtocol()},
|
||||
})
|
||||
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)
|
||||
|
||||
for _, a := range test.nicAddrs {
|
||||
if err := s.AddAddress(nicID, ipv6.ProtocolNumber, a); err != nil {
|
||||
t.Errorf("s.AddAddress(%d, %d, %s): %s", nicID, ipv6.ProtocolNumber, a, err)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if got := addrForNewConnectionTo(t, s, tcpip.FullAddress{Addr: test.connectAddr, NIC: nicID, Port: 1234}); got != test.expectedLocalAddr {
|
||||
t.Errorf("got local address = %s, want = %s", got, test.expectedLocalAddr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user