mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Emit multicast forwarding events.
Updates #7338. PiperOrigin-RevId: 452051657
This commit is contained in:
@@ -367,6 +367,18 @@ func (e *endpoint) disableLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
// multicastEventDispatcher returns the multicast forwarding event dispatcher.
|
||||
//
|
||||
// Panics if a multicast forwarding event dispatcher does not exist. This
|
||||
// indicates that multicast forwarding is enabled, but no dispatcher was
|
||||
// provided.
|
||||
func (e *endpoint) multicastEventDispatcher() stack.MulticastForwardingEventDispatcher {
|
||||
if mcastDisp := e.protocol.options.MulticastForwardingDisp; mcastDisp != nil {
|
||||
return mcastDisp
|
||||
}
|
||||
panic("e.procotol.options.MulticastForwardingDisp unexpectedly nil")
|
||||
}
|
||||
|
||||
// DefaultTTL is the default time-to-live value for this endpoint.
|
||||
func (e *endpoint) DefaultTTL() uint8 {
|
||||
return e.protocol.DefaultTTL()
|
||||
@@ -886,10 +898,18 @@ func (e *endpoint) forwardMulticastPacket(h header.IPv4, pkt *stack.PacketBuffer
|
||||
return &ip.ErrNoMulticastPendingQueueBufferSpace{}
|
||||
}
|
||||
|
||||
// TODO(https://gvisor.dev/issue/7338): Emit an event for a missing route.
|
||||
if result.GetRouteResultState == multicast.InstalledRouteFound {
|
||||
switch result.GetRouteResultState {
|
||||
case multicast.InstalledRouteFound:
|
||||
// Attempt to forward the pkt using an existing route.
|
||||
return e.forwardValidatedMulticastPacket(pkt, result.InstalledRoute)
|
||||
case multicast.NoRouteFoundAndPendingInserted:
|
||||
e.multicastEventDispatcher().OnMissingRoute(stack.MulticastPacketContext{
|
||||
stack.UnicastSourceAndMulticastDestination{h.SourceAddress(), h.DestinationAddress()},
|
||||
e.nic.ID(),
|
||||
})
|
||||
case multicast.PacketQueuedInPendingRoute:
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected GetRouteResultState: %s", result.GetRouteResultState))
|
||||
}
|
||||
return &ip.ErrNoRoute{}
|
||||
}
|
||||
@@ -937,8 +957,11 @@ func (e *endpoint) forwardValidatedMulticastPacket(pkt *stack.PacketBuffer, inst
|
||||
// on the proper interface for forwarding. If not, the datagram is
|
||||
// dropped silently.
|
||||
if e.nic.ID() != installedRoute.ExpectedInputInterface {
|
||||
// TODO(https://gvisor.dev/issue/7338): Emit an event for an unexpected
|
||||
// input interface.
|
||||
h := header.IPv4(pkt.NetworkHeader().View())
|
||||
e.multicastEventDispatcher().OnUnexpectedInputInterface(stack.MulticastPacketContext{
|
||||
stack.UnicastSourceAndMulticastDestination{h.SourceAddress(), h.DestinationAddress()},
|
||||
e.nic.ID(),
|
||||
}, installedRoute.ExpectedInputInterface)
|
||||
return &ip.ErrUnexpectedMulticastInputInterface{}
|
||||
}
|
||||
|
||||
@@ -1749,6 +1772,10 @@ type Options struct {
|
||||
// AllowExternalLoopbackTraffic indicates that inbound loopback packets (i.e.
|
||||
// martian loopback packets) should be accepted.
|
||||
AllowExternalLoopbackTraffic bool
|
||||
|
||||
// MulticastForwardingDisp is the multicast forwarding event dispatcher that
|
||||
// an integrator can provide to receive multicast forwarding events.
|
||||
MulticastForwardingDisp stack.MulticastForwardingEventDispatcher
|
||||
}
|
||||
|
||||
// NewProtocolWithOptions returns an IPv4 network protocol.
|
||||
|
||||
@@ -199,6 +199,15 @@ func handleICMPInIPv6(ep stack.NetworkEndpoint, src, dst tcpip.Address, icmp hea
|
||||
pkt.DecRef()
|
||||
}
|
||||
|
||||
var _ stack.MulticastForwardingEventDispatcher = (*fakeMulticastEventDispatcher)(nil)
|
||||
|
||||
type fakeMulticastEventDispatcher struct{}
|
||||
|
||||
func (m *fakeMulticastEventDispatcher) OnMissingRoute(context stack.MulticastPacketContext) {}
|
||||
|
||||
func (m *fakeMulticastEventDispatcher) OnUnexpectedInputInterface(context stack.MulticastPacketContext, expectedInputInterface tcpip.NICID) {
|
||||
}
|
||||
|
||||
type testContext struct {
|
||||
s *stack.Stack
|
||||
clock *faketime.ManualClock
|
||||
@@ -207,7 +216,7 @@ type testContext struct {
|
||||
func newTestContext() testContext {
|
||||
clock := faketime.NewManualClock()
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocolWithOptions(Options{MulticastForwardingDisp: &fakeMulticastEventDispatcher{}})},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6, udp.NewProtocol},
|
||||
Clock: clock,
|
||||
})
|
||||
|
||||
@@ -1123,15 +1123,18 @@ func (e *endpoint) forwardMulticastPacket(h header.IPv6, pkt *stack.PacketBuffer
|
||||
return &ip.ErrNoMulticastPendingQueueBufferSpace{}
|
||||
}
|
||||
|
||||
// TODO(https://gvisor.dev/issue/7338): Emit an event for a missing route.
|
||||
switch result.GetRouteResultState {
|
||||
case multicast.InstalledRouteFound:
|
||||
// Attempt to forward the pkt using an existing route.
|
||||
return e.forwardValidatedMulticastPacket(pkt, result.InstalledRoute)
|
||||
case multicast.NoRouteFoundAndPendingInserted:
|
||||
e.multicastEventDispatcher().OnMissingRoute(stack.MulticastPacketContext{
|
||||
stack.UnicastSourceAndMulticastDestination{h.SourceAddress(), h.DestinationAddress()},
|
||||
e.nic.ID(),
|
||||
})
|
||||
case multicast.PacketQueuedInPendingRoute:
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected result.GetRouteResultState: %s", result.GetRouteResultState))
|
||||
panic(fmt.Sprintf("unexpected GetRouteResultState: %s", result.GetRouteResultState))
|
||||
}
|
||||
return &ip.ErrNoRoute{}
|
||||
}
|
||||
@@ -1148,8 +1151,11 @@ func (e *endpoint) forwardValidatedMulticastPacket(pkt *stack.PacketBuffer, inst
|
||||
// on the proper interface for forwarding. If not, the datagram is
|
||||
// dropped silently.
|
||||
if e.nic.ID() != installedRoute.ExpectedInputInterface {
|
||||
// TODO(https://gvisor.dev/issue/7338): Emit an event for an unexpected
|
||||
// input interface.
|
||||
h := header.IPv6(pkt.NetworkHeader().View())
|
||||
e.multicastEventDispatcher().OnUnexpectedInputInterface(stack.MulticastPacketContext{
|
||||
stack.UnicastSourceAndMulticastDestination{h.SourceAddress(), h.DestinationAddress()},
|
||||
e.nic.ID(),
|
||||
}, installedRoute.ExpectedInputInterface)
|
||||
return &ip.ErrUnexpectedMulticastInputInterface{}
|
||||
}
|
||||
|
||||
@@ -2264,6 +2270,18 @@ func (p *protocol) DefaultTTL() uint8 {
|
||||
return uint8(p.defaultTTL.Load())
|
||||
}
|
||||
|
||||
// multicastEventDispatcher returns the multicast forwarding event dispatcher.
|
||||
//
|
||||
// Panics if a multicast forwarding event dispatcher does not exist. This
|
||||
// indicates that multicast forwarding is enabled, but no dispatcher was
|
||||
// provided.
|
||||
func (e *endpoint) multicastEventDispatcher() stack.MulticastForwardingEventDispatcher {
|
||||
if mcastDisp := e.protocol.options.MulticastForwardingDisp; mcastDisp != nil {
|
||||
return mcastDisp
|
||||
}
|
||||
panic("e.procotol.options.MulticastForwardingDisp unexpectedly nil")
|
||||
}
|
||||
|
||||
// Close implements stack.TransportProtocol.
|
||||
func (p *protocol) Close() {
|
||||
p.fragmentation.Release()
|
||||
@@ -2528,6 +2546,10 @@ type Options struct {
|
||||
// AllowExternalLoopbackTraffic indicates that inbound loopback packets (i.e.
|
||||
// martian loopback packets) should be accepted.
|
||||
AllowExternalLoopbackTraffic bool
|
||||
|
||||
// MulticastForwardingDisp is the multicast forwarding event dispatcher that
|
||||
// an integrator can provide to receive multicast forwarding events.
|
||||
MulticastForwardingDisp stack.MulticastForwardingEventDispatcher
|
||||
}
|
||||
|
||||
// NewProtocolWithOptions returns an IPv6 network protocol.
|
||||
|
||||
@@ -819,6 +819,37 @@ type MulticastForwardingNetworkProtocol interface {
|
||||
MulticastRouteLastUsedTime(UnicastSourceAndMulticastDestination) (tcpip.MonotonicTime, tcpip.Error)
|
||||
}
|
||||
|
||||
// MulticastPacketContext is the context in which a multicast packet triggered
|
||||
// a multicast forwarding event.
|
||||
type MulticastPacketContext struct {
|
||||
// SourceAndDestination contains the unicast source address and the multicast
|
||||
// destination address found in the relevant multicast packet.
|
||||
SourceAndDestination UnicastSourceAndMulticastDestination
|
||||
// InputInterface is the interface on which the relevant multicast packet
|
||||
// arrived.
|
||||
InputInterface tcpip.NICID
|
||||
}
|
||||
|
||||
// MulticastForwardingEventDispatcher is the interface that integrators should
|
||||
// implement to handle multicast routing events.
|
||||
type MulticastForwardingEventDispatcher interface {
|
||||
// OnMissingRoute is called when an incoming multicast packet does not match
|
||||
// any installed route.
|
||||
//
|
||||
// The packet that triggered this event may be queued so that it can be
|
||||
// transmitted once a route is installed. Even then, it may still be dropped
|
||||
// as per the routing table's GC/eviction policy.
|
||||
OnMissingRoute(MulticastPacketContext)
|
||||
|
||||
// OnUnexpectedInputInterface is called when a multicast packet arrives at an
|
||||
// interface that does not match the installed route's expected input
|
||||
// interface.
|
||||
//
|
||||
// This may be an indication of a routing loop. The packet that triggered
|
||||
// this event is dropped without being forwarded.
|
||||
OnUnexpectedInputInterface(context MulticastPacketContext, expectedInputInterface tcpip.NICID)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -68,6 +68,33 @@ const (
|
||||
otherOutgoingEndpointAddr
|
||||
)
|
||||
|
||||
type onMissingRouteData struct {
|
||||
context stack.MulticastPacketContext
|
||||
}
|
||||
|
||||
type onUnexpectedInputInterfaceData struct {
|
||||
context stack.MulticastPacketContext
|
||||
expectedInputInterface tcpip.NICID
|
||||
}
|
||||
|
||||
var _ stack.MulticastForwardingEventDispatcher = (*fakeMulticastEventDispatcher)(nil)
|
||||
|
||||
type fakeMulticastEventDispatcher struct {
|
||||
onMissingRouteData *onMissingRouteData
|
||||
onUnexpectedInputInterfaceData *onUnexpectedInputInterfaceData
|
||||
}
|
||||
|
||||
func (m *fakeMulticastEventDispatcher) OnMissingRoute(context stack.MulticastPacketContext) {
|
||||
m.onMissingRouteData = &onMissingRouteData{context}
|
||||
}
|
||||
|
||||
func (m *fakeMulticastEventDispatcher) OnUnexpectedInputInterface(context stack.MulticastPacketContext, expectedInputInterface tcpip.NICID) {
|
||||
m.onUnexpectedInputInterfaceData = &onUnexpectedInputInterfaceData{
|
||||
context,
|
||||
expectedInputInterface,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
v4Addrs = map[addrType]tcpip.Address{
|
||||
anyAddr: header.IPv4Any,
|
||||
@@ -335,9 +362,12 @@ func TestAddMulticastRoute(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
for _, protocol := range []tcpip.NetworkProtocolNumber{ipv4.ProtocolNumber, ipv6.ProtocolNumber} {
|
||||
t.Run(fmt.Sprintf("%s %d", test.name, protocol), func(t *testing.T) {
|
||||
eventDispatcher := &fakeMulticastEventDispatcher{}
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol},
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||
ipv4.NewProtocolWithOptions(ipv4.Options{MulticastForwardingDisp: eventDispatcher}),
|
||||
ipv6.NewProtocolWithOptions(ipv6.Options{MulticastForwardingDisp: eventDispatcher}),
|
||||
},
|
||||
})
|
||||
defer s.Close()
|
||||
|
||||
@@ -646,8 +676,12 @@ func TestRemoveMulticastRoute(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
for _, protocol := range []tcpip.NetworkProtocolNumber{ipv4.ProtocolNumber, ipv6.ProtocolNumber} {
|
||||
t.Run(fmt.Sprintf("%s %d", test.name, protocol), func(t *testing.T) {
|
||||
eventDispatcher := &fakeMulticastEventDispatcher{}
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||
ipv4.NewProtocolWithOptions(ipv4.Options{MulticastForwardingDisp: eventDispatcher}),
|
||||
ipv6.NewProtocolWithOptions(ipv6.Options{MulticastForwardingDisp: eventDispatcher}),
|
||||
},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol},
|
||||
})
|
||||
defer s.Close()
|
||||
@@ -757,14 +791,16 @@ func TestMulticastForwarding(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dstAddr addrType
|
||||
ttl uint8
|
||||
routeInputInterface tcpip.NICID
|
||||
disableMulticastForwarding bool
|
||||
removeOutputInterface tcpip.NICID
|
||||
joinMulticastGroup bool
|
||||
expectedForwardingInterfaces []tcpip.NICID
|
||||
name string
|
||||
dstAddr addrType
|
||||
ttl uint8
|
||||
routeInputInterface tcpip.NICID
|
||||
disableMulticastForwarding bool
|
||||
removeOutputInterface tcpip.NICID
|
||||
expectMissingRouteEvent bool
|
||||
expectUnexpectedInputInterfaceEvent bool
|
||||
joinMulticastGroup bool
|
||||
expectedForwardingInterfaces []tcpip.NICID
|
||||
}{
|
||||
{
|
||||
name: "forward only",
|
||||
@@ -798,11 +834,12 @@ func TestMulticastForwarding(t *testing.T) {
|
||||
expectedForwardingInterfaces: []tcpip.NICID{},
|
||||
},
|
||||
{
|
||||
name: "unexpected input interface",
|
||||
dstAddr: multicastAddr,
|
||||
ttl: packetTTL,
|
||||
routeInputInterface: otherNICID,
|
||||
expectedForwardingInterfaces: []tcpip.NICID{},
|
||||
name: "unexpected input interface",
|
||||
dstAddr: multicastAddr,
|
||||
ttl: packetTTL,
|
||||
routeInputInterface: otherNICID,
|
||||
expectUnexpectedInputInterfaceEvent: true,
|
||||
expectedForwardingInterfaces: []tcpip.NICID{},
|
||||
},
|
||||
{
|
||||
name: "output interface removed",
|
||||
@@ -838,15 +875,27 @@ func TestMulticastForwarding(t *testing.T) {
|
||||
dstAddr: otherMulticastAddr,
|
||||
ttl: packetTTL,
|
||||
routeInputInterface: incomingNICID,
|
||||
expectMissingRouteEvent: true,
|
||||
expectedForwardingInterfaces: []tcpip.NICID{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
for _, protocol := range []tcpip.NetworkProtocolNumber{ipv4.ProtocolNumber, ipv6.ProtocolNumber} {
|
||||
ipv4EventDispatcher := &fakeMulticastEventDispatcher{}
|
||||
ipv6EventDispatcher := &fakeMulticastEventDispatcher{}
|
||||
|
||||
eventDispatchers := map[tcpip.NetworkProtocolNumber]*fakeMulticastEventDispatcher{
|
||||
ipv4.ProtocolNumber: ipv4EventDispatcher,
|
||||
ipv6.ProtocolNumber: ipv6EventDispatcher,
|
||||
}
|
||||
|
||||
t.Run(fmt.Sprintf("%s %d", test.name, protocol), func(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||
ipv4.NewProtocolWithOptions(ipv4.Options{MulticastForwardingDisp: ipv4EventDispatcher}),
|
||||
ipv6.NewProtocolWithOptions(ipv6.Options{MulticastForwardingDisp: ipv6EventDispatcher}),
|
||||
},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol},
|
||||
})
|
||||
defer s.Close()
|
||||
@@ -974,6 +1023,33 @@ func TestMulticastForwarding(t *testing.T) {
|
||||
checkEchoReply(t, protocol, p, getEndpointAddr(protocol, incomingEpAddrType).Address, srcAddr)
|
||||
p.DecRef()
|
||||
}
|
||||
|
||||
eventDispatcher, ok := eventDispatchers[protocol]
|
||||
if !ok {
|
||||
t.Fatalf("eventDispatchers[%d] = (_, false), want (_, true)", protocol)
|
||||
}
|
||||
|
||||
wantUnexpectedInputInterfaceEvent := func() *onUnexpectedInputInterfaceData {
|
||||
if test.expectUnexpectedInputInterfaceEvent {
|
||||
return &onUnexpectedInputInterfaceData{stack.MulticastPacketContext{stack.UnicastSourceAndMulticastDestination{srcAddr, dstAddr}, incomingNICID}, test.routeInputInterface}
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
if diff := cmp.Diff(wantUnexpectedInputInterfaceEvent, eventDispatcher.onUnexpectedInputInterfaceData, cmp.AllowUnexported(onUnexpectedInputInterfaceData{})); diff != "" {
|
||||
t.Errorf("onUnexpectedInputInterfaceData mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
wantMissingRouteEvent := func() *onMissingRouteData {
|
||||
if test.expectMissingRouteEvent {
|
||||
return &onMissingRouteData{stack.MulticastPacketContext{stack.UnicastSourceAndMulticastDestination{srcAddr, dstAddr}, incomingNICID}}
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
if diff := cmp.Diff(wantMissingRouteEvent, eventDispatcher.onMissingRouteData, cmp.AllowUnexported(onMissingRouteData{})); diff != "" {
|
||||
t.Errorf("onMissingRouteData mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user