mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Pass RouteInfo to the route resolve callback
The route resolution callback will be called with a stack.ResolvedFieldsResult which will hold the route info so callers can avoid attempting resolution again to check if a previous resolution attempt succeeded or not. Test: integration_test.TestRouteResolvedFields PiperOrigin-RevId: 353319019
This commit is contained in:
committed by
gVisor bot
parent
f52f0101bb
commit
f190e13a74
@@ -145,7 +145,7 @@ func (f *packetsPendingLinkResolution) enqueue(r *Route, gso *GSO, proto tcpip.N
|
||||
//
|
||||
// To make sure B does not interleave with A and C, we make sure A and C are
|
||||
// done while holding the lock.
|
||||
routeInfo, ch, err := r.ResolvedFields(nil)
|
||||
routeInfo, ch, err := r.resolvedFields(nil)
|
||||
switch err {
|
||||
case nil:
|
||||
// The route resolved immediately, so we don't need to wait for link
|
||||
|
||||
+34
-10
@@ -315,23 +315,42 @@ func (r *Route) ResolveWith(addr tcpip.LinkAddress) {
|
||||
r.mu.remoteLinkAddress = addr
|
||||
}
|
||||
|
||||
// ResolvedFields is like Fields but also attempts to resolve the remote link
|
||||
// address if it is not yet known.
|
||||
// ResolvedFieldsResult is the result of a route resolution attempt.
|
||||
type ResolvedFieldsResult struct {
|
||||
RouteInfo RouteInfo
|
||||
Success bool
|
||||
}
|
||||
|
||||
// ResolvedFields attempts to resolve the remote link address if it is not
|
||||
// known.
|
||||
//
|
||||
// If address resolution is required, returns tcpip.ErrWouldBlock and a
|
||||
// notification channel for the caller to block on. The channel will be readable
|
||||
// once address resolution is complete (successful or not). If a callback is
|
||||
// provided, it will be called when address resolution is complete, regardless
|
||||
// of success or failure before the notification channel is readable.
|
||||
// If a callback is provided, it will be called before ResolvedFields returns
|
||||
// when address resolution is not required. If address resolution is required,
|
||||
// the callback will be called once address resolution is complete, regardless
|
||||
// of success or failure.
|
||||
//
|
||||
// Note, the route will not cache the remote link address when address
|
||||
// resolution completes.
|
||||
func (r *Route) ResolvedFields(afterResolve func()) (RouteInfo, <-chan struct{}, *tcpip.Error) {
|
||||
func (r *Route) ResolvedFields(afterResolve func(ResolvedFieldsResult)) *tcpip.Error {
|
||||
_, _, err := r.resolvedFields(afterResolve)
|
||||
return err
|
||||
}
|
||||
|
||||
// resolvedFields is like ResolvedFields but also returns a notification channel
|
||||
// when address resolution is required. This channel will become readable once
|
||||
// address resolution is complete.
|
||||
//
|
||||
// The route's fields will also be returned, regardless of whether address
|
||||
// resolution is required or not.
|
||||
func (r *Route) resolvedFields(afterResolve func(ResolvedFieldsResult)) (RouteInfo, <-chan struct{}, *tcpip.Error) {
|
||||
r.mu.RLock()
|
||||
fields := r.fieldsLocked()
|
||||
resolutionRequired := r.isResolutionRequiredRLocked()
|
||||
r.mu.RUnlock()
|
||||
if !resolutionRequired {
|
||||
if afterResolve != nil {
|
||||
afterResolve(ResolvedFieldsResult{RouteInfo: fields, Success: true})
|
||||
}
|
||||
return fields, nil, nil
|
||||
}
|
||||
|
||||
@@ -347,9 +366,14 @@ func (r *Route) ResolvedFields(afterResolve func()) (RouteInfo, <-chan struct{},
|
||||
linkAddressResolutionRequestLocalAddr = r.LocalAddress
|
||||
}
|
||||
|
||||
linkAddr, ch, err := r.outgoingNIC.getNeighborLinkAddress(nextAddr, linkAddressResolutionRequestLocalAddr, r.linkRes, func(LinkResolutionResult) {
|
||||
afterResolveFields := fields
|
||||
linkAddr, ch, err := r.outgoingNIC.getNeighborLinkAddress(nextAddr, linkAddressResolutionRequestLocalAddr, r.linkRes, func(r LinkResolutionResult) {
|
||||
if afterResolve != nil {
|
||||
afterResolve()
|
||||
if r.Success {
|
||||
afterResolveFields.RemoteLinkAddress = r.LinkAddress
|
||||
}
|
||||
|
||||
afterResolve(ResolvedFieldsResult{RouteInfo: afterResolveFields, Success: r.Success})
|
||||
}
|
||||
})
|
||||
if err == nil {
|
||||
|
||||
@@ -471,6 +471,141 @@ func TestGetLinkAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteResolvedFields(t *testing.T) {
|
||||
const (
|
||||
host1NICID = 1
|
||||
host2NICID = 4
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
localAddr tcpip.Address
|
||||
remoteAddr tcpip.Address
|
||||
immediatelyResolvable bool
|
||||
expectedSuccess bool
|
||||
expectedLinkAddr tcpip.LinkAddress
|
||||
}{
|
||||
{
|
||||
name: "IPv4 immediately resolvable",
|
||||
netProto: ipv4.ProtocolNumber,
|
||||
localAddr: ipv4Addr1.AddressWithPrefix.Address,
|
||||
remoteAddr: header.IPv4AllSystems,
|
||||
immediatelyResolvable: true,
|
||||
expectedSuccess: true,
|
||||
expectedLinkAddr: header.EthernetAddressFromMulticastIPv4Address(header.IPv4AllSystems),
|
||||
},
|
||||
{
|
||||
name: "IPv6 immediately resolvable",
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
localAddr: ipv6Addr1.AddressWithPrefix.Address,
|
||||
remoteAddr: header.IPv6AllNodesMulticastAddress,
|
||||
immediatelyResolvable: true,
|
||||
expectedSuccess: true,
|
||||
expectedLinkAddr: header.EthernetAddressFromMulticastIPv6Address(header.IPv6AllNodesMulticastAddress),
|
||||
},
|
||||
{
|
||||
name: "IPv4 resolvable",
|
||||
netProto: ipv4.ProtocolNumber,
|
||||
localAddr: ipv4Addr1.AddressWithPrefix.Address,
|
||||
remoteAddr: ipv4Addr2.AddressWithPrefix.Address,
|
||||
immediatelyResolvable: false,
|
||||
expectedSuccess: true,
|
||||
expectedLinkAddr: linkAddr2,
|
||||
},
|
||||
{
|
||||
name: "IPv6 resolvable",
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
localAddr: ipv6Addr1.AddressWithPrefix.Address,
|
||||
remoteAddr: ipv6Addr2.AddressWithPrefix.Address,
|
||||
immediatelyResolvable: false,
|
||||
expectedSuccess: true,
|
||||
expectedLinkAddr: linkAddr2,
|
||||
},
|
||||
{
|
||||
name: "IPv4 not resolvable",
|
||||
netProto: ipv4.ProtocolNumber,
|
||||
localAddr: ipv4Addr1.AddressWithPrefix.Address,
|
||||
remoteAddr: ipv4Addr3.AddressWithPrefix.Address,
|
||||
immediatelyResolvable: false,
|
||||
expectedSuccess: false,
|
||||
},
|
||||
{
|
||||
name: "IPv6 not resolvable",
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
localAddr: ipv6Addr1.AddressWithPrefix.Address,
|
||||
remoteAddr: ipv6Addr3.AddressWithPrefix.Address,
|
||||
immediatelyResolvable: false,
|
||||
expectedSuccess: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, useNeighborCache := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("UseNeighborCache=%t", useNeighborCache), func(t *testing.T) {
|
||||
stackOpts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
UseNeighborCache: useNeighborCache,
|
||||
}
|
||||
|
||||
host1Stack, _ := setupStack(t, stackOpts, host1NICID, host2NICID)
|
||||
r, err := host1Stack.FindRoute(host1NICID, "", test.remoteAddr, test.netProto, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Fatalf("host1Stack.FindRoute(%d, '', %s, %d, false): %s", host1NICID, test.remoteAddr, test.netProto, err)
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
var wantRouteInfo stack.RouteInfo
|
||||
wantRouteInfo.LocalLinkAddress = linkAddr1
|
||||
wantRouteInfo.LocalAddress = test.localAddr
|
||||
wantRouteInfo.RemoteAddress = test.remoteAddr
|
||||
wantRouteInfo.NetProto = test.netProto
|
||||
wantRouteInfo.Loop = stack.PacketOut
|
||||
wantRouteInfo.RemoteLinkAddress = test.expectedLinkAddr
|
||||
|
||||
ch := make(chan stack.ResolvedFieldsResult, 1)
|
||||
|
||||
if !test.immediatelyResolvable {
|
||||
wantUnresolvedRouteInfo := wantRouteInfo
|
||||
wantUnresolvedRouteInfo.RemoteLinkAddress = ""
|
||||
|
||||
if err := r.ResolvedFields(func(r stack.ResolvedFieldsResult) {
|
||||
ch <- r
|
||||
}); err != tcpip.ErrWouldBlock {
|
||||
t.Errorf("got r.ResolvedFields(_) = %s, want = %s", err, tcpip.ErrWouldBlock)
|
||||
}
|
||||
if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Success: test.expectedSuccess}, <-ch, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" {
|
||||
t.Errorf("route resolve result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
if !test.expectedSuccess {
|
||||
return
|
||||
}
|
||||
|
||||
// At this point the neighbor table should be populated so the route
|
||||
// should be immediately resolvable.
|
||||
}
|
||||
|
||||
if err := r.ResolvedFields(func(r stack.ResolvedFieldsResult) {
|
||||
ch <- r
|
||||
}); err != nil {
|
||||
t.Errorf("r.ResolvedFields(_): %s", err)
|
||||
}
|
||||
select {
|
||||
case routeResolveRes := <-ch:
|
||||
if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Success: true}, routeResolveRes, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" {
|
||||
t.Errorf("route resolve result from resolved route mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected route to be immediately resolvable")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWritePacketsLinkResolution(t *testing.T) {
|
||||
const (
|
||||
host1NICID = 1
|
||||
|
||||
Reference in New Issue
Block a user