From 28eda96b005ea8130bd3cabd23db3640ec26c67b Mon Sep 17 00:00:00 2001 From: Nate Hurley Date: Thu, 19 May 2022 12:51:40 -0700 Subject: [PATCH] Cleanup the multicast routing table. This change does the following: - Uses shared types for the route key and multicast route. - Returns a bool instead of error from GetRouteOrInsertPending since only one error type is possible. - Improves the naming of PendingRouteState using the suggestions in cl/445157188. Updates #7338. PiperOrigin-RevId: 449812803 --- .../internal/multicast/example_test.go | 26 ++-- .../network/internal/multicast/route_table.go | 121 +++++++----------- .../internal/multicast/route_table_test.go | 77 ++++++----- pkg/tcpip/stack/registration.go | 35 +++++ 4 files changed, 133 insertions(+), 126 deletions(-) diff --git a/pkg/tcpip/network/internal/multicast/example_test.go b/pkg/tcpip/network/internal/multicast/example_test.go index 08a187d35..912676fbc 100644 --- a/pkg/tcpip/network/internal/multicast/example_test.go +++ b/pkg/tcpip/network/internal/multicast/example_test.go @@ -32,6 +32,7 @@ import ( const ( defaultMinTTL = 10 + defaultMTU = 1500 inputNICID tcpip.NICID = 1 outgoingNICID tcpip.NICID = 2 ) @@ -39,8 +40,9 @@ const ( // Example shows how to interact with a multicast RouteTable. func Example() { address := testutil.MustParse4("192.168.1.1") - defaultOutgoingInterfaces := []multicast.OutgoingInterface{{ID: outgoingNICID, MinTTL: defaultMinTTL}} - routeKey := multicast.RouteKey{UnicastSource: address, MulticastDestination: address} + defaultOutgoingInterfaces := []stack.MulticastRouteOutgoingInterface{{ID: outgoingNICID, MinTTL: defaultMinTTL}} + routeKey := stack.UnicastSourceAndMulticastDestination{Source: address, Destination: address} + multicastRoute := stack.MulticastRoute{inputNICID, defaultOutgoingInterfaces} pkt := newPacketBuffer("hello") defer pkt.DecRef() @@ -59,34 +61,30 @@ func Example() { // Each entry in the table represents either an installed route or a pending // route. To insert a pending route, call: - result, err := table.GetRouteOrInsertPending(routeKey, pkt) + result, hasBufferSpace := table.GetRouteOrInsertPending(routeKey, pkt) // Callers should handle a no buffer space error (e.g. only deliver the // packet locally). - if err == multicast.ErrNoBufferSpace { + if !hasBufferSpace { deliverPktLocally(pkt) } - if err != nil { - panic(err) - } - // Callers should handle the various pending route states. - switch result.PendingRouteState { - case multicast.PendingRouteStateNone: + switch result.GetRouteResultState { + case multicast.InstalledRouteFound: // The packet can be forwarded using the installed route. forwardPkt(pkt, result.InstalledRoute) - case multicast.PendingRouteStateInstalled: + case multicast.NoRouteFoundAndPendingInserted: // The route has just entered the pending state. emitMissingRouteEvent(routeKey) deliverPktLocally(pkt) - case multicast.PendingRouteStateAppended: + case multicast.PacketQueuedInPendingRoute: // The route was already in the pending state. deliverPktLocally(pkt) } // To transition a pending route to the installed state, call: - route := table.NewInstalledRoute(inputNICID, defaultOutgoingInterfaces) + route := table.NewInstalledRoute(multicastRoute) pendingPackets := table.AddInstalledRoute(routeKey, route) // If there was a pending route, then the caller is responsible for @@ -121,7 +119,7 @@ func forwardPkt(*stack.PacketBuffer, *multicast.InstalledRoute) { fmt.Println("forwardPkt") } -func emitMissingRouteEvent(multicast.RouteKey) { +func emitMissingRouteEvent(stack.UnicastSourceAndMulticastDestination) { fmt.Println("emitMissingRouteEvent") } diff --git a/pkg/tcpip/network/internal/multicast/route_table.go b/pkg/tcpip/network/internal/multicast/route_table.go index 125ca077b..3fe307664 100644 --- a/pkg/tcpip/network/internal/multicast/route_table.go +++ b/pkg/tcpip/network/internal/multicast/route_table.go @@ -47,11 +47,11 @@ type RouteTable struct { // Maintaining pointers ensures that the installed routes are exclusively // locked only when a route is being installed. // +checklocks:installedMu - installedRoutes map[RouteKey]*InstalledRoute + installedRoutes map[stack.UnicastSourceAndMulticastDestination]*InstalledRoute pendingMu sync.RWMutex // +checklocks:pendingMu - pendingRoutes map[RouteKey]PendingRoute + pendingRoutes map[stack.UnicastSourceAndMulticastDestination]PendingRoute // cleanupPendingRoutesTimer is a timer that triggers a routine to remove // pending routes that are expired. // +checklocks:pendingMu @@ -75,35 +75,18 @@ var ( ErrAlreadyInitialized = errors.New("table is already initialized") ) -// RouteKey represents an entry key in the RouteTable. -type RouteKey struct { - UnicastSource tcpip.Address - MulticastDestination tcpip.Address -} - // InstalledRoute represents a route that is in the installed state. // // If a route is in the installed state, then it may be used to forward // multicast packets. type InstalledRoute struct { - expectedInputInterface tcpip.NICID - outgoingInterfaces []OutgoingInterface + stack.MulticastRoute lastUsedTimestampMu sync.RWMutex // +checklocks:lastUsedTimestampMu lastUsedTimestamp tcpip.MonotonicTime } -// ExpectedInputInterface returns the expected input interface for the route. -func (r *InstalledRoute) ExpectedInputInterface() tcpip.NICID { - return r.expectedInputInterface -} - -// OutgoingInterfaces returns the outgoing interfaces for the route. -func (r *InstalledRoute) OutgoingInterfaces() []OutgoingInterface { - return r.outgoingInterfaces -} - // LastUsedTimestamp returns a monotonic timestamp that corresponds to the last // time the route was used or updated. func (r *InstalledRoute) LastUsedTimestamp() tcpip.MonotonicTime { @@ -127,17 +110,6 @@ func (r *InstalledRoute) SetLastUsedTimestamp(monotonicTime tcpip.MonotonicTime) } } -// OutgoingInterface represents an interface that packets should be forwarded -// out of. -type OutgoingInterface struct { - // ID corresponds to the outgoing NIC. - ID tcpip.NICID - - // MinTTL represents the minumum TTL/HopLimit a multicast packet must have to - // be sent through the outgoing interface. - MinTTL uint8 -} - // PendingRoute represents a route that is in the "pending" state. // // A route is in the pending state if an installed route does not yet exist @@ -230,8 +202,8 @@ func (r *RouteTable) Init(config Config) error { } r.config = config - r.installedRoutes = make(map[RouteKey]*InstalledRoute) - r.pendingRoutes = make(map[RouteKey]PendingRoute) + r.installedRoutes = make(map[stack.UnicastSourceAndMulticastDestination]*InstalledRoute) + r.pendingRoutes = make(map[stack.UnicastSourceAndMulticastDestination]PendingRoute) return nil } @@ -299,53 +271,47 @@ func (r *RouteTable) newPendingRoute() PendingRoute { } // NewInstalledRoute instantiates an installed route for the table. -func (r *RouteTable) NewInstalledRoute(inputInterface tcpip.NICID, outgoingInterfaces []OutgoingInterface) *InstalledRoute { +func (r *RouteTable) NewInstalledRoute(route stack.MulticastRoute) *InstalledRoute { return &InstalledRoute{ - expectedInputInterface: inputInterface, - outgoingInterfaces: outgoingInterfaces, - lastUsedTimestamp: r.config.Clock.NowMonotonic(), + MulticastRoute: route, + lastUsedTimestamp: r.config.Clock.NowMonotonic(), } } -// GetRouteResult represents the result of calling -// RouteTable.GetRouteOrInsertPending. +// GetRouteResult represents the result of calling GetRouteOrInsertPending. type GetRouteResult struct { - // PendingRouteState represents the observed state of any applicable - // PendingRoute. - PendingRouteState + // GetRouteResultState signals the result of calling GetRouteOrInsertPending. + GetRouteResultState GetRouteResultState // InstalledRoute represents the existing installed route. This field will - // only be populated if the PendingRouteState is PendingRouteStateNone. - *InstalledRoute + // only be populated if the GetRouteResultState is InstalledRouteFound. + InstalledRoute *InstalledRoute } -// PendingRouteState represents the state of a PendingRoute as observed by the -// RouteTable.GetRouteOrInsertPending method. -type PendingRouteState uint8 +// GetRouteResultState signals the result of calling GetRouteOrInsertPending. +type GetRouteResultState uint8 const ( - // PendingRouteStateNone indicates that no pending route exists. In such a - // case, the GetRouteResult will contain an InstalledRoute. - PendingRouteStateNone PendingRouteState = iota + // InstalledRouteFound indicates that an InstalledRoute was found. + InstalledRouteFound GetRouteResultState = iota - // PendingRouteStateAppended indicates that the packet was queued in an + // PacketQueuedInPendingRoute indicates that the packet was queued in an // existing pending route. - PendingRouteStateAppended + PacketQueuedInPendingRoute - // PendingRouteStateInstalled indicates that a pending route was newly - // inserted into the RouteTable. In such a case, callers should typically - // emit a missing route event. - PendingRouteStateInstalled + // NoRouteFoundAndPendingInserted indicates that no route was found and that + // a pending route was newly inserted into the RouteTable. + NoRouteFoundAndPendingInserted ) -func (e PendingRouteState) String() string { +func (e GetRouteResultState) String() string { switch e { - case PendingRouteStateNone: - return "PendingRouteStateNone" - case PendingRouteStateAppended: - return "PendingRouteStateAppended" - case PendingRouteStateInstalled: - return "PendingRouteStateInstalled" + case InstalledRouteFound: + return "InstalledRouteFound" + case PacketQueuedInPendingRoute: + return "PacketQueuedInPendingRoute" + case NoRouteFoundAndPendingInserted: + return "NoRouteFoundAndPendingInserted" default: return fmt.Sprintf("%d", uint8(e)) } @@ -355,29 +321,28 @@ func (e PendingRouteState) String() string { // the provided key. // // If no matching installed route is found, then the pkt is cloned and queued -// in a pending route. The GetRouteResult.PendingRouteState will indicate +// in a pending route. The GetRouteResult.GetRouteResultState will indicate // whether the pkt was queued in a new pending route or an existing one. // -// If the relevant pending route queue is at max capacity, then -// ErrNoBufferSpace is returned. In such a case, callers are typically expected -// to only deliver the pkt locally (if relevant). -func (r *RouteTable) GetRouteOrInsertPending(key RouteKey, pkt *stack.PacketBuffer) (GetRouteResult, error) { +// If the relevant pending route queue is at max capacity, then returns false. +// Otherwise, returns true. +func (r *RouteTable) GetRouteOrInsertPending(key stack.UnicastSourceAndMulticastDestination, pkt *stack.PacketBuffer) (GetRouteResult, bool) { r.installedMu.RLock() defer r.installedMu.RUnlock() if route, ok := r.installedRoutes[key]; ok { - return GetRouteResult{PendingRouteState: PendingRouteStateNone, InstalledRoute: route}, nil + return GetRouteResult{GetRouteResultState: InstalledRouteFound, InstalledRoute: route}, true } r.pendingMu.Lock() defer r.pendingMu.Unlock() - pendingRoute, pendingRouteState := r.getOrCreatePendingRouteRLocked(key) + pendingRoute, getRouteResultState := r.getOrCreatePendingRouteRLocked(key) if len(pendingRoute.packets) >= int(r.config.MaxPendingQueueSize) { // The incoming packet is rejected if the pending queue is already at max // capacity. This behavior matches the Linux implementation: // https://github.com/torvalds/linux/blob/ae085d7f936/net/ipv4/ipmr.c#L1147 - return GetRouteResult{}, ErrNoBufferSpace + return GetRouteResult{}, false } pendingRoute.packets = append(pendingRoute.packets, pkt.Clone()) r.pendingRoutes[key] = pendingRoute @@ -392,15 +357,15 @@ func (r *RouteTable) GetRouteOrInsertPending(key RouteKey, pkt *stack.PacketBuff r.isCleanupRoutineRunning = true } - return GetRouteResult{PendingRouteState: pendingRouteState, InstalledRoute: nil}, nil + return GetRouteResult{GetRouteResultState: getRouteResultState, InstalledRoute: nil}, true } // +checklocks:r.pendingMu -func (r *RouteTable) getOrCreatePendingRouteRLocked(key RouteKey) (PendingRoute, PendingRouteState) { +func (r *RouteTable) getOrCreatePendingRouteRLocked(key stack.UnicastSourceAndMulticastDestination) (PendingRoute, GetRouteResultState) { if pendingRoute, ok := r.pendingRoutes[key]; ok { - return pendingRoute, PendingRouteStateAppended + return pendingRoute, PacketQueuedInPendingRoute } - return r.newPendingRoute(), PendingRouteStateInstalled + return r.newPendingRoute(), NoRouteFoundAndPendingInserted } // AddInstalledRoute adds the provided route to the table. @@ -409,7 +374,7 @@ func (r *RouteTable) getOrCreatePendingRouteRLocked(key RouteKey) (PendingRoute, // returned. The caller assumes ownership of these packets and is responsible // for forwarding and releasing them. If an installed route already exists for // the provided key, then it is overwritten. -func (r *RouteTable) AddInstalledRoute(key RouteKey, route *InstalledRoute) []*stack.PacketBuffer { +func (r *RouteTable) AddInstalledRoute(key stack.UnicastSourceAndMulticastDestination, route *InstalledRoute) []*stack.PacketBuffer { r.installedMu.Lock() defer r.installedMu.Unlock() r.installedRoutes[key] = route @@ -436,7 +401,7 @@ func (r *RouteTable) AddInstalledRoute(key RouteKey, route *InstalledRoute) []*s // key. // // Returns true if a route was removed. Otherwise returns false. -func (r *RouteTable) RemoveInstalledRoute(key RouteKey) bool { +func (r *RouteTable) RemoveInstalledRoute(key stack.UnicastSourceAndMulticastDestination) bool { r.installedMu.Lock() defer r.installedMu.Unlock() @@ -452,7 +417,7 @@ func (r *RouteTable) RemoveInstalledRoute(key RouteKey) bool { // time the route that matches the provided key was used or updated. // // Returns true if a matching route was found. Otherwise returns false. -func (r *RouteTable) GetLastUsedTimestamp(key RouteKey) (tcpip.MonotonicTime, bool) { +func (r *RouteTable) GetLastUsedTimestamp(key stack.UnicastSourceAndMulticastDestination) (tcpip.MonotonicTime, bool) { r.installedMu.RLock() defer r.installedMu.RUnlock() diff --git a/pkg/tcpip/network/internal/multicast/route_table_test.go b/pkg/tcpip/network/internal/multicast/route_table_test.go index 8a799a1a2..d1f697fdd 100644 --- a/pkg/tcpip/network/internal/multicast/route_table_test.go +++ b/pkg/tcpip/network/internal/multicast/route_table_test.go @@ -32,6 +32,7 @@ import ( const ( defaultMinTTL = 10 + defaultMTU = 1500 inputNICID tcpip.NICID = 1 outgoingNICID tcpip.NICID = 2 defaultNICID tcpip.NICID = 3 @@ -39,8 +40,9 @@ const ( var ( defaultAddress = testutil.MustParse4("192.168.1.1") - defaultRouteKey = RouteKey{UnicastSource: defaultAddress, MulticastDestination: defaultAddress} - defaultOutgoingInterfaces = []OutgoingInterface{{ID: outgoingNICID, MinTTL: defaultMinTTL}} + defaultRouteKey = stack.UnicastSourceAndMulticastDestination{Source: defaultAddress, Destination: defaultAddress} + defaultOutgoingInterfaces = []stack.MulticastRouteOutgoingInterface{{ID: outgoingNICID, MinTTL: defaultMinTTL}} + defaultRoute = stack.MulticastRoute{inputNICID, defaultOutgoingInterfaces} ) func newPacketBuffer(body string) *stack.PacketBuffer { @@ -77,11 +79,11 @@ func defaultConfig(opts ...configOption) Config { } func installedRouteComparer(a *InstalledRoute, b *InstalledRoute) bool { - if !cmp.Equal(a.OutgoingInterfaces(), b.OutgoingInterfaces()) { + if !cmp.Equal(a.OutgoingInterfaces, b.OutgoingInterfaces) { return false } - if a.ExpectedInputInterface() != b.ExpectedInputInterface() { + if a.ExpectedInputInterface != b.ExpectedInputInterface { return false } @@ -143,15 +145,19 @@ func TestNewInstalledRoute(t *testing.T) { t.Fatalf("table.Init(%#v): %s", config, err) } - route := table.NewInstalledRoute(inputNICID, defaultOutgoingInterfaces) - expectedRoute := &InstalledRoute{expectedInputInterface: inputNICID, outgoingInterfaces: defaultOutgoingInterfaces, lastUsedTimestamp: clock.NowMonotonic()} + route := table.NewInstalledRoute(defaultRoute) + + expectedRoute := &InstalledRoute{ + MulticastRoute: defaultRoute, + lastUsedTimestamp: clock.NowMonotonic(), + } if diff := cmp.Diff(expectedRoute, route, cmp.Comparer(installedRouteComparer)); diff != "" { t.Errorf("Installed route mismatch (-want +got):\n%s", diff) } } -func TestPendingRouteStates(t *testing.T) { +func TestGetRouteResultStates(t *testing.T) { table := RouteTable{} defer table.Close() config := defaultConfig(withMaxPendingQueueSize(2)) @@ -161,16 +167,17 @@ func TestPendingRouteStates(t *testing.T) { pkt := newPacketBuffer("hello") defer pkt.DecRef() - // Queue two pending packets for the same route. The PendingRouteState should - // transition from PendingRouteStateInstalled to PendingRouteStateAppended. - for _, wantPendingRouteState := range []PendingRouteState{PendingRouteStateInstalled, PendingRouteStateAppended} { - routeResult, err := table.GetRouteOrInsertPending(defaultRouteKey, pkt) + // Queue two pending packets for the same route. The GetRouteResultState + // should transition from NoRouteFoundAndPendingInserted to + // PacketQueuedInPendingRoute. + for _, wantPendingRouteState := range []GetRouteResultState{NoRouteFoundAndPendingInserted, PacketQueuedInPendingRoute} { + routeResult, hasBufferSpace := table.GetRouteOrInsertPending(defaultRouteKey, pkt) - if err != nil { - t.Errorf("table.GetRouteOrInsertPending(%#v, %#v) = (_, %v), want = (_, nil)", defaultRouteKey, pkt, err) + if !hasBufferSpace { + t.Errorf("table.GetRouteOrInsertPending(%#v, %#v) = (_, false), want = (_, true)", defaultRouteKey, pkt) } - expectedResult := GetRouteResult{PendingRouteState: wantPendingRouteState} + expectedResult := GetRouteResult{GetRouteResultState: wantPendingRouteState} if diff := cmp.Diff(expectedResult, routeResult); diff != "" { t.Errorf("table.GetRouteOrInsertPending(%#v, %#v) GetRouteResult mismatch (-want +got):\n%s", defaultRouteKey, pkt, diff) } @@ -178,8 +185,8 @@ func TestPendingRouteStates(t *testing.T) { // Queuing a third packet should yield an error since the pending queue is // already at max capacity. - if _, err := table.GetRouteOrInsertPending(defaultRouteKey, pkt); err != ErrNoBufferSpace { - t.Errorf("table.GetRouteOrInsertPending(%#v, %#v) = (_, %v), want = (_, ErrNoBufferSpace)", defaultRouteKey, pkt, err) + if _, hasBufferSpace := table.GetRouteOrInsertPending(defaultRouteKey, pkt); hasBufferSpace { + t.Errorf("table.GetRouteOrInsertPending(%#v, %#v) = (_, true), want = (_, false)", defaultRouteKey, pkt) } } @@ -225,8 +232,8 @@ func TestPendingRouteExpiration(t *testing.T) { clock.Advance(test.advanceBeforeInsert) - if _, err := table.GetRouteOrInsertPending(defaultRouteKey, pkt); err != nil { - t.Fatalf("table.GetRouteOrInsertPending(%#v, %#v): %v", defaultRouteKey, pkt, err) + if _, hasBufferSpace := table.GetRouteOrInsertPending(defaultRouteKey, pkt); !hasBufferSpace { + t.Fatalf("table.GetRouteOrInsertPending(%#v, %#v): false", defaultRouteKey, pkt) } clock.Advance(test.advanceAfterInsert) @@ -288,8 +295,8 @@ func TestAddInstalledRouteWithPending(t *testing.T) { t.Fatalf("table.Init(%#v): %s", config, err) } - if _, err := table.GetRouteOrInsertPending(defaultRouteKey, pkt); err != nil { - t.Fatalf("table.GetRouteOrInsertPending(%#v, %#v): %v", defaultRouteKey, pkt, err) + if _, hasBufferSpace := table.GetRouteOrInsertPending(defaultRouteKey, pkt); !hasBufferSpace { + t.Fatalf("table.GetRouteOrInsertPending(%#v, %#v): false", defaultRouteKey, pkt) } // Disable the cleanup routine. @@ -297,7 +304,7 @@ func TestAddInstalledRouteWithPending(t *testing.T) { clock.Advance(test.advance) - route := table.NewInstalledRoute(inputNICID, defaultOutgoingInterfaces) + route := table.NewInstalledRoute(defaultRoute) pendingPackets := table.AddInstalledRoute(defaultRouteKey, route) if diff := cmp.Diff(test.want, pendingPackets, cmpOpts...); diff != "" { @@ -326,27 +333,29 @@ func TestAddInstalledRouteWithNoPending(t *testing.T) { t.Fatalf("table.Init(%#v): %s", config, err) } - firstRoute := table.NewInstalledRoute(inputNICID, defaultOutgoingInterfaces) - secondRoute := table.NewInstalledRoute(defaultNICID, defaultOutgoingInterfaces) + firstRoute := table.NewInstalledRoute(defaultRoute) + + secondMulticastRoute := stack.MulticastRoute{defaultNICID, defaultOutgoingInterfaces} + secondRoute := table.NewInstalledRoute(secondMulticastRoute) pkt := newPacketBuffer("hello") defer pkt.DecRef() for _, route := range [...]*InstalledRoute{firstRoute, secondRoute} { if pendingPackets := table.AddInstalledRoute(defaultRouteKey, route); pendingPackets != nil { - t.Errorf("got table.AddInstalledRoute(%#v, %#v) = %#v, want = false", defaultRouteKey, route, pendingPackets) + t.Errorf("table.AddInstalledRoute(%#v, %#v) = %#v, want = false", defaultRouteKey, route, pendingPackets) } // AddInstalledRoute is invoked for the same routeKey two times. Verify // that the fetched InstalledRoute reflects the most recent invocation of // AddInstalledRoute. - routeResult, err := table.GetRouteOrInsertPending(defaultRouteKey, pkt) + routeResult, hasBufferSpace := table.GetRouteOrInsertPending(defaultRouteKey, pkt) - if err != nil { - t.Fatalf("table.GetRouteOrInsertPending(%#v, %#v): %v", defaultRouteKey, pkt, err) + if !hasBufferSpace { + t.Fatalf("table.GetRouteOrInsertPending(%#v, %#v): false", defaultRouteKey, pkt) } - if routeResult.PendingRouteState != PendingRouteStateNone { - t.Errorf("routeResult.PendingRouteState = %s, want = PendingRouteStateNone", routeResult.PendingRouteState) + if routeResult.GetRouteResultState != InstalledRouteFound { + t.Errorf("routeResult.GetRouteResultState = %s, want = InstalledRouteFound", routeResult.GetRouteResultState) } if diff := cmp.Diff(route, routeResult.InstalledRoute, cmp.Comparer(installedRouteComparer)); diff != "" { @@ -363,7 +372,7 @@ func TestRemoveInstalledRoute(t *testing.T) { t.Fatalf("table.Init(%#v): %s", config, err) } - route := table.NewInstalledRoute(inputNICID, defaultOutgoingInterfaces) + route := table.NewInstalledRoute(defaultRoute) table.AddInstalledRoute(defaultRouteKey, route) @@ -374,10 +383,10 @@ func TestRemoveInstalledRoute(t *testing.T) { pkt := newPacketBuffer("hello") defer pkt.DecRef() - result, err := table.GetRouteOrInsertPending(defaultRouteKey, pkt) + result, hasBufferSpace := table.GetRouteOrInsertPending(defaultRouteKey, pkt) - if err != nil { - t.Fatalf("table.GetRouteOrInsertPending(%#v, %#v): %v", defaultRouteKey, pkt, err) + if !hasBufferSpace { + t.Fatalf("table.GetRouteOrInsertPending(%#v, %#v): false", defaultRouteKey, pkt) } if result.InstalledRoute != nil { @@ -444,7 +453,7 @@ func TestSetLastUsedTimestamp(t *testing.T) { t.Fatalf("table.Init(%#v): %s", config, err) } - route := table.NewInstalledRoute(inputNICID, defaultOutgoingInterfaces) + route := table.NewInstalledRoute(defaultRoute) table.AddInstalledRoute(defaultRouteKey, route) diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index 9d09c49bb..ce69972c2 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -757,6 +757,41 @@ type NetworkProtocol interface { Parse(pkt *PacketBuffer) (proto tcpip.TransportProtocolNumber, hasTransportHdr bool, ok bool) } +// UnicastSourceAndMulticastDestination is a tuple that represents a unicast +// source address and a multicast destination address. +type UnicastSourceAndMulticastDestination struct { + // Source represents a unicast source address. + Source tcpip.Address + // Destination represents a multicast destination address. + Destination tcpip.Address +} + +// MulticastRouteOutgoingInterface represents an outgoing interface in a +// multicast route. +type MulticastRouteOutgoingInterface struct { + // ID corresponds to the outgoing NIC. + ID tcpip.NICID + + // MinTTL represents the minumum TTL/HopLimit a multicast packet must have to + // be sent through the outgoing interface. + // + // Note: a value of 0 allows all packets to be forwarded. + MinTTL uint8 +} + +// MulticastRoute is a multicast route. +type MulticastRoute struct { + // ExpectedInputInterface is the interface on which packets using this route + // are expected to ingress. + ExpectedInputInterface tcpip.NICID + + // OutgoingInterfaces is the set of interfaces that a multicast packet should + // be forwarded out of. + // + // This field should not be empty. + OutgoingInterfaces []MulticastRouteOutgoingInterface +} + // NetworkDispatcher contains the methods used by the network stack to deliver // inbound/outbound packets to the appropriate network/packet(if any) endpoints. type NetworkDispatcher interface {