mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
netstack: support source hints in routes
PiperOrigin-RevId: 587841841
This commit is contained in:
committed by
gVisor bot
parent
cdd3b866f5
commit
289757e903
@@ -521,7 +521,7 @@ func (igmp *igmpState) writePacketInner(buf *buffer.View, reportStat tcpip.Multi
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
addressEndpoint := igmp.ep.acquireOutgoingPrimaryAddressRLocked(destAddress, false /* allowExpired */)
|
||||
addressEndpoint := igmp.ep.acquireOutgoingPrimaryAddressRLocked(destAddress, tcpip.Address{} /* srcHint */, false /* allowExpired */)
|
||||
if addressEndpoint == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -1423,18 +1423,18 @@ func (e *endpoint) AcquireAssignedAddress(localAddr tcpip.Address, allowTemp boo
|
||||
}
|
||||
|
||||
// AcquireOutgoingPrimaryAddress implements stack.AddressableEndpoint.
|
||||
func (e *endpoint) AcquireOutgoingPrimaryAddress(remoteAddr tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
func (e *endpoint) AcquireOutgoingPrimaryAddress(remoteAddr, srcHint tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
return e.acquireOutgoingPrimaryAddressRLocked(remoteAddr, allowExpired)
|
||||
return e.acquireOutgoingPrimaryAddressRLocked(remoteAddr, srcHint, allowExpired)
|
||||
}
|
||||
|
||||
// acquireOutgoingPrimaryAddressRLocked is like AcquireOutgoingPrimaryAddress
|
||||
// but with locking requirements
|
||||
//
|
||||
// +checklocksread:e.mu
|
||||
func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
return e.addressableEndpointState.AcquireOutgoingPrimaryAddress(remoteAddr, allowExpired)
|
||||
func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr, srcHint tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
return e.addressableEndpointState.AcquireOutgoingPrimaryAddress(remoteAddr, srcHint, allowExpired)
|
||||
}
|
||||
|
||||
// PrimaryAddresses implements stack.AddressableEndpoint.
|
||||
|
||||
@@ -914,7 +914,7 @@ func (e *endpoint) LinkAddressRequest(targetAddr, localAddr tcpip.Address, remot
|
||||
|
||||
if localAddr.BitLen() == 0 {
|
||||
// Find an address that we can use as our source address.
|
||||
addressEndpoint := e.AcquireOutgoingPrimaryAddress(remoteAddr, false /* allowExpired */)
|
||||
addressEndpoint := e.AcquireOutgoingPrimaryAddress(remoteAddr, tcpip.Address{} /* srcHint */, false /* allowExpired */)
|
||||
if addressEndpoint == nil {
|
||||
return &tcpip.ErrNetworkUnreachable{}
|
||||
}
|
||||
|
||||
@@ -2051,10 +2051,10 @@ func (e *endpoint) acquireAddressOrCreateTempLocked(localAddr tcpip.Address, all
|
||||
}
|
||||
|
||||
// AcquireOutgoingPrimaryAddress implements stack.AddressableEndpoint.
|
||||
func (e *endpoint) AcquireOutgoingPrimaryAddress(remoteAddr tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
func (e *endpoint) AcquireOutgoingPrimaryAddress(remoteAddr, srcHint tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
return e.acquireOutgoingPrimaryAddressRLocked(remoteAddr, allowExpired)
|
||||
return e.acquireOutgoingPrimaryAddressRLocked(remoteAddr, srcHint, allowExpired)
|
||||
}
|
||||
|
||||
// getLinkLocalAddressRLocked returns a link-local address from the primary list
|
||||
@@ -2081,7 +2081,9 @@ func (e *endpoint) getLinkLocalAddressRLocked() tcpip.Address {
|
||||
// but with locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be read locked.
|
||||
func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr, srcHint tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
// TODO(b/309216156): Support IPv6 hints.
|
||||
|
||||
// addrCandidate is a candidate for Source Address Selection, as per
|
||||
// RFC 6724 section 5.
|
||||
type addrCandidate struct {
|
||||
@@ -2094,7 +2096,7 @@ func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr tcpip.Address
|
||||
}
|
||||
|
||||
if remoteAddr.BitLen() == 0 {
|
||||
return e.mu.addressableEndpointState.AcquireOutgoingPrimaryAddress(remoteAddr, allowExpired)
|
||||
return e.mu.addressableEndpointState.AcquireOutgoingPrimaryAddress(remoteAddr, srcHint, allowExpired)
|
||||
}
|
||||
|
||||
// Create a candidate set of available addresses we can potentially use as a
|
||||
|
||||
@@ -1837,7 +1837,7 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
// the unspecified address if no address is assigned
|
||||
// to the sending interface.
|
||||
localAddr := header.IPv6Any
|
||||
if addressEndpoint := ndp.ep.AcquireOutgoingPrimaryAddress(header.IPv6AllRoutersLinkLocalMulticastAddress, false); addressEndpoint != nil {
|
||||
if addressEndpoint := ndp.ep.AcquireOutgoingPrimaryAddress(header.IPv6AllRoutersLinkLocalMulticastAddress, tcpip.Address{} /* srcHint */, false); addressEndpoint != nil {
|
||||
localAddr = addressEndpoint.AddressWithPrefix().Address
|
||||
addressEndpoint.DecRef()
|
||||
}
|
||||
|
||||
@@ -434,7 +434,7 @@ func (a *AddressableEndpointState) MainAddress() tcpip.AddressWithPrefix {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
|
||||
ep := a.acquirePrimaryAddressRLocked(tcpip.Address{}, func(ep *addressState) bool {
|
||||
ep := a.acquirePrimaryAddressRLocked(tcpip.Address{}, tcpip.Address{} /* srcHint */, func(ep *addressState) bool {
|
||||
switch kind := ep.GetKind(); kind {
|
||||
case Permanent:
|
||||
return a.networkEndpoint.Enabled() || !a.options.HiddenWhileDisabled
|
||||
@@ -462,7 +462,7 @@ func (a *AddressableEndpointState) MainAddress() tcpip.AddressWithPrefix {
|
||||
// valid according to isValid.
|
||||
//
|
||||
// +checklocksread:a.mu
|
||||
func (a *AddressableEndpointState) acquirePrimaryAddressRLocked(remoteAddr tcpip.Address, isValid func(*addressState) bool) *addressState {
|
||||
func (a *AddressableEndpointState) acquirePrimaryAddressRLocked(remoteAddr, srcHint tcpip.Address, isValid func(*addressState) bool) *addressState {
|
||||
// TODO: Move this out into IPv4-specific code.
|
||||
// IPv6 handles source IP selection elsewhere. We have to do source
|
||||
// selection only for IPv4, in which case ep is never deprecated. Thus
|
||||
@@ -474,6 +474,11 @@ func (a *AddressableEndpointState) acquirePrimaryAddressRLocked(remoteAddr tcpip
|
||||
if !isValid(state) {
|
||||
continue
|
||||
}
|
||||
// Source hint takes precedent over prefix matching.
|
||||
if state.addr.Address == srcHint && srcHint != (tcpip.Address{}) {
|
||||
best = state
|
||||
break
|
||||
}
|
||||
stateLen := state.addr.Address.MatchingPrefix(remoteAddr)
|
||||
if best == nil || bestLen < stateLen {
|
||||
best = state
|
||||
@@ -618,11 +623,11 @@ func (a *AddressableEndpointState) AcquireAssignedAddress(localAddr tcpip.Addres
|
||||
}
|
||||
|
||||
// AcquireOutgoingPrimaryAddress implements AddressableEndpoint.
|
||||
func (a *AddressableEndpointState) AcquireOutgoingPrimaryAddress(remoteAddr tcpip.Address, allowExpired bool) AddressEndpoint {
|
||||
func (a *AddressableEndpointState) AcquireOutgoingPrimaryAddress(remoteAddr tcpip.Address, srcHint tcpip.Address, allowExpired bool) AddressEndpoint {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
ep := a.acquirePrimaryAddressRLocked(remoteAddr, func(ep *addressState) bool {
|
||||
ep := a.acquirePrimaryAddressRLocked(remoteAddr, srcHint, func(ep *addressState) bool {
|
||||
return ep.IsAssigned(allowExpired)
|
||||
})
|
||||
|
||||
|
||||
@@ -380,7 +380,7 @@ func (mt *MasqueradeTarget) Action(pkt PacketBufferPtr, hook Hook, r *Route, add
|
||||
}
|
||||
|
||||
// addressEP is expected to be set for the postrouting hook.
|
||||
ep := addressEP.AcquireOutgoingPrimaryAddress(pkt.Network().DestinationAddress(), false /* allowExpired */)
|
||||
ep := addressEP.AcquireOutgoingPrimaryAddress(pkt.Network().DestinationAddress(), tcpip.Address{} /* srcHint */, false /* allowExpired */)
|
||||
if ep == nil {
|
||||
// No address exists that we can use as a source address.
|
||||
return RuleDrop, 0
|
||||
|
||||
@@ -420,7 +420,7 @@ func (n *nic) Spoofing() bool {
|
||||
|
||||
// primaryAddress returns an address that can be used to communicate with
|
||||
// remoteAddr.
|
||||
func (n *nic) primaryEndpoint(protocol tcpip.NetworkProtocolNumber, remoteAddr tcpip.Address) AssignableAddressEndpoint {
|
||||
func (n *nic) primaryEndpoint(protocol tcpip.NetworkProtocolNumber, remoteAddr, srcHint tcpip.Address) AssignableAddressEndpoint {
|
||||
ep := n.getNetworkEndpoint(protocol)
|
||||
if ep == nil {
|
||||
return nil
|
||||
@@ -431,7 +431,7 @@ func (n *nic) primaryEndpoint(protocol tcpip.NetworkProtocolNumber, remoteAddr t
|
||||
return nil
|
||||
}
|
||||
|
||||
return addressableEndpoint.AcquireOutgoingPrimaryAddress(remoteAddr, n.Spoofing())
|
||||
return addressableEndpoint.AcquireOutgoingPrimaryAddress(remoteAddr, srcHint, n.Spoofing())
|
||||
}
|
||||
|
||||
type getAddressBehaviour int
|
||||
|
||||
@@ -681,7 +681,7 @@ type AddressableEndpoint interface {
|
||||
// The returned endpoint's reference count is incremented.
|
||||
//
|
||||
// Returns nil if a primary address is not available.
|
||||
AcquireOutgoingPrimaryAddress(remoteAddr tcpip.Address, allowExpired bool) AddressEndpoint
|
||||
AcquireOutgoingPrimaryAddress(remoteAddr, srcHint tcpip.Address, allowExpired bool) AddressEndpoint
|
||||
|
||||
// PrimaryAddresses returns the primary addresses.
|
||||
PrimaryAddresses() []tcpip.AddressWithPrefix
|
||||
|
||||
+10
-10
@@ -1168,9 +1168,9 @@ func (s *Stack) GetMainNICAddress(id tcpip.NICID, protocol tcpip.NetworkProtocol
|
||||
return nic.PrimaryAddress(protocol)
|
||||
}
|
||||
|
||||
func (s *Stack) getAddressEP(nic *nic, localAddr, remoteAddr tcpip.Address, netProto tcpip.NetworkProtocolNumber) AssignableAddressEndpoint {
|
||||
func (s *Stack) getAddressEP(nic *nic, localAddr, remoteAddr, srcHint tcpip.Address, netProto tcpip.NetworkProtocolNumber) AssignableAddressEndpoint {
|
||||
if localAddr.BitLen() == 0 {
|
||||
return nic.primaryEndpoint(netProto, remoteAddr)
|
||||
return nic.primaryEndpoint(netProto, remoteAddr, srcHint)
|
||||
}
|
||||
return nic.findEndpoint(netProto, localAddr, CanBePrimaryEndpoint)
|
||||
}
|
||||
@@ -1188,7 +1188,7 @@ func (s *Stack) NewRouteForMulticast(nicID tcpip.NICID, remoteAddr tcpip.Address
|
||||
return nil
|
||||
}
|
||||
|
||||
if addressEndpoint := s.getAddressEP(nic, tcpip.Address{} /* localAddr */, remoteAddr, netProto); addressEndpoint != nil {
|
||||
if addressEndpoint := s.getAddressEP(nic, tcpip.Address{} /* localAddr */, remoteAddr, tcpip.Address{} /* srcHint */, netProto); addressEndpoint != nil {
|
||||
return constructAndValidateRoute(netProto, addressEndpoint, nic, nic, tcpip.Address{} /* gateway */, tcpip.Address{} /* localAddr */, remoteAddr, s.handleLocal, false /* multicastLoop */)
|
||||
}
|
||||
return nil
|
||||
@@ -1300,9 +1300,9 @@ func isNICForwarding(nic *nic, proto tcpip.NetworkProtocolNumber) bool {
|
||||
// endpoint.
|
||||
//
|
||||
// +checklocksread:s.mu
|
||||
func (s *Stack) findRouteWithLocalAddrFromAnyInterfaceRLocked(outgoingNIC *nic, localAddr, remoteAddr, gateway tcpip.Address, netProto tcpip.NetworkProtocolNumber, multicastLoop bool) *Route {
|
||||
func (s *Stack) findRouteWithLocalAddrFromAnyInterfaceRLocked(outgoingNIC *nic, localAddr, remoteAddr, srcHint, gateway tcpip.Address, netProto tcpip.NetworkProtocolNumber, multicastLoop bool) *Route {
|
||||
for _, aNIC := range s.nics {
|
||||
addressEndpoint := s.getAddressEP(aNIC, localAddr, remoteAddr, netProto)
|
||||
addressEndpoint := s.getAddressEP(aNIC, localAddr, remoteAddr, srcHint, netProto)
|
||||
if addressEndpoint == nil {
|
||||
continue
|
||||
}
|
||||
@@ -1350,7 +1350,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
// through the interface if the interface is valid and enabled.
|
||||
if id != 0 && !needRoute {
|
||||
if nic, ok := s.nics[id]; ok && nic.Enabled() {
|
||||
if addressEndpoint := s.getAddressEP(nic, localAddr, remoteAddr, netProto); addressEndpoint != nil {
|
||||
if addressEndpoint := s.getAddressEP(nic, localAddr, remoteAddr, tcpip.Address{} /* srcHint */, netProto); addressEndpoint != nil {
|
||||
return makeRoute(
|
||||
netProto,
|
||||
tcpip.Address{}, /* gateway */
|
||||
@@ -1390,7 +1390,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
}
|
||||
|
||||
if id == 0 || id == route.NIC {
|
||||
if addressEndpoint := s.getAddressEP(nic, localAddr, remoteAddr, netProto); addressEndpoint != nil {
|
||||
if addressEndpoint := s.getAddressEP(nic, localAddr, remoteAddr, route.SourceHint, netProto); addressEndpoint != nil {
|
||||
var gateway tcpip.Address
|
||||
if needRoute {
|
||||
gateway = route.Gateway
|
||||
@@ -1421,7 +1421,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
chosenRoute = route
|
||||
continue
|
||||
}
|
||||
if r := s.findRouteWithLocalAddrFromAnyInterfaceRLocked(nic, localAddr, remoteAddr, route.Gateway, netProto, multicastLoop); r != nil {
|
||||
if r := s.findRouteWithLocalAddrFromAnyInterfaceRLocked(nic, localAddr, remoteAddr, route.SourceHint, route.Gateway, netProto, multicastLoop); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
@@ -1449,7 +1449,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
// Use the specified NIC to get the local address endpoint.
|
||||
if id != 0 {
|
||||
if aNIC, ok := s.nics[id]; ok {
|
||||
if addressEndpoint := s.getAddressEP(aNIC, localAddr, remoteAddr, netProto); addressEndpoint != nil {
|
||||
if addressEndpoint := s.getAddressEP(aNIC, localAddr, remoteAddr, chosenRoute.SourceHint, netProto); addressEndpoint != nil {
|
||||
if r := constructAndValidateRoute(netProto, addressEndpoint, aNIC /* localAddressNIC */, nic /* outgoingNIC */, gateway, localAddr, remoteAddr, s.handleLocal, multicastLoop); r != nil {
|
||||
return r, nil
|
||||
}
|
||||
@@ -1463,7 +1463,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
if id == 0 {
|
||||
// If an interface is not specified, try to find a NIC that holds the local
|
||||
// address endpoint to construct a route.
|
||||
if r := s.findRouteWithLocalAddrFromAnyInterfaceRLocked(nic, localAddr, remoteAddr, gateway, netProto, multicastLoop); r != nil {
|
||||
if r := s.findRouteWithLocalAddrFromAnyInterfaceRLocked(nic, localAddr, remoteAddr, chosenRoute.SourceHint, gateway, netProto, multicastLoop); r != nil {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3582,7 +3582,7 @@ func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
|
||||
t.Fatal("network endpoint is not addressable")
|
||||
}
|
||||
|
||||
addressEP := addressableEndpoint.AcquireOutgoingPrimaryAddress(test.remoteAddr, false /* allowExpired */)
|
||||
addressEP := addressableEndpoint.AcquireOutgoingPrimaryAddress(test.remoteAddr, tcpip.Address{} /* srcHint */, false /* allowExpired */)
|
||||
if addressEP == nil {
|
||||
t.Fatal("expected a non-nil address endpoint")
|
||||
}
|
||||
@@ -5565,6 +5565,7 @@ func TestFindRoute(t *testing.T) {
|
||||
gateway string
|
||||
subnet string
|
||||
nic tcpip.NICID
|
||||
srcHint string
|
||||
}
|
||||
type query struct {
|
||||
name string
|
||||
@@ -5681,6 +5682,38 @@ func TestFindRoute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one NIC, multiple addresses, no gateways",
|
||||
nics: []nic{{id: 1, addresses: []string{"169.254.169.1", "169.254.9.1"}}},
|
||||
routes: []route{
|
||||
{subnet: "10.0.0.0/8", nic: 1},
|
||||
},
|
||||
queries: []query{
|
||||
{
|
||||
name: "match first",
|
||||
remote: "10.132.0.4",
|
||||
wantID: 1,
|
||||
wantLocal: "169.254.169.1",
|
||||
wantNextHop: "", // No gateway, so no next hop.
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one NIC, multiple addresses, source hints, no gateways",
|
||||
nics: []nic{{id: 1, addresses: []string{"169.254.169.1", "169.254.9.1", "10.132.1.1"}}},
|
||||
routes: []route{
|
||||
{subnet: "10.0.0.0/8", nic: 1, srcHint: "169.254.9.1"},
|
||||
},
|
||||
queries: []query{
|
||||
{
|
||||
name: "match hint",
|
||||
remote: "10.132.0.4",
|
||||
wantID: 1,
|
||||
wantLocal: "169.254.9.1",
|
||||
wantNextHop: "", // No gateway, so no next hop.
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, stackConfig := range stacks {
|
||||
@@ -5713,11 +5746,17 @@ func TestFindRoute(t *testing.T) {
|
||||
// Setup the route table.
|
||||
var routeTable []tcpip.Route
|
||||
for _, route := range stackConfig.routes {
|
||||
routeTable = append(routeTable, tcpip.Route{
|
||||
rt := tcpip.Route{
|
||||
Destination: testutil.MustParseSubnet4(route.subnet),
|
||||
Gateway: testutil.MustParse4(route.gateway),
|
||||
NIC: route.nic,
|
||||
})
|
||||
}
|
||||
if len(route.gateway) > 0 {
|
||||
rt.Gateway = testutil.MustParse4(route.gateway)
|
||||
}
|
||||
if len(route.srcHint) > 0 {
|
||||
rt.SourceHint = testutil.MustParse4(route.srcHint)
|
||||
}
|
||||
routeTable = append(routeTable, rt)
|
||||
}
|
||||
stk.SetRouteTable(routeTable)
|
||||
|
||||
@@ -5742,7 +5781,11 @@ func TestFindRoute(t *testing.T) {
|
||||
if got, want := route.LocalAddress(), testutil.MustParse4(query.wantLocal); got != want {
|
||||
t.Errorf("got local address %s, but wanted %s", got, want)
|
||||
}
|
||||
if got, want := route.NextHop(), testutil.MustParse4(query.wantNextHop); got != want {
|
||||
var nextHop tcpip.Address
|
||||
if len(query.wantNextHop) > 0 {
|
||||
nextHop = testutil.MustParse4(query.wantNextHop)
|
||||
}
|
||||
if got, want := route.NextHop(), nextHop; got != want {
|
||||
t.Errorf("got next hop %s, but wanted %s", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1499,6 +1499,10 @@ type Route struct {
|
||||
|
||||
// NIC is the id of the nic to be used if this row is viable.
|
||||
NIC NICID
|
||||
|
||||
// SourceHint indicates a preferred source address to use when NICs
|
||||
// have multiple addresses.
|
||||
SourceHint Address
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
|
||||
Reference in New Issue
Block a user