Add option to enable multicast forwarding

Setting this newly added option to "true" will currently be a no-op. Full
support for multicast routing will be added in subsequent CLs.

Updates #7338.

PiperOrigin-RevId: 439356271
This commit is contained in:
Nate Hurley
2022-04-04 10:48:26 -07:00
committed by gVisor bot
parent 37d5dc5877
commit 47fbf57a83
6 changed files with 290 additions and 59 deletions
+28
View File
@@ -72,6 +72,7 @@ var ipv4BroadcastAddr = header.IPv4Broadcast.WithPrefix()
var _ stack.LinkResolvableNetworkEndpoint = (*endpoint)(nil)
var _ stack.ForwardingNetworkEndpoint = (*endpoint)(nil)
var _ stack.MulticastForwardingNetworkEndpoint = (*endpoint)(nil)
var _ stack.GroupAddressableEndpoint = (*endpoint)(nil)
var _ stack.AddressableEndpoint = (*endpoint)(nil)
var _ stack.NetworkEndpoint = (*endpoint)(nil)
@@ -94,6 +95,15 @@ type endpoint struct {
// +checkatomic
forwarding uint32
// multicastForwarding is set to forwardingEnabled when the endpoint has
// forwarding enabled and forwardingDisabled when it is disabled.
//
// TODO(https://gvisor.dev/issue/7338): Implement support for multicast
//forwarding. Currently, setting this value to true is a no-op.
//
// +checkatomic
multicastForwarding uint32
// mu protects below.
mu sync.RWMutex
@@ -236,6 +246,24 @@ func (e *endpoint) SetForwarding(forwarding bool) bool {
return prevForwarding
}
// MulticastForwarding implements stack.MulticastForwardingNetworkEndpoint.
func (e *endpoint) MulticastForwarding() bool {
return atomic.LoadUint32(&e.multicastForwarding) == forwardingEnabled
}
// SetMulticastForwarding implements stack.MulticastForwardingNetworkEndpoint.
func (e *endpoint) SetMulticastForwarding(forwarding bool) bool {
e.mu.Lock()
defer e.mu.Unlock()
updatedForwarding := uint32(forwardingDisabled)
if forwarding {
updatedForwarding = forwardingEnabled
}
return atomic.SwapUint32(&e.multicastForwarding, updatedForwarding) != forwardingDisabled
}
// Enable implements stack.NetworkEndpoint.
func (e *endpoint) Enable() tcpip.Error {
e.mu.Lock()
+28
View File
@@ -174,6 +174,7 @@ var _ stack.DuplicateAddressDetector = (*endpoint)(nil)
var _ stack.LinkAddressResolver = (*endpoint)(nil)
var _ stack.LinkResolvableNetworkEndpoint = (*endpoint)(nil)
var _ stack.ForwardingNetworkEndpoint = (*endpoint)(nil)
var _ stack.MulticastForwardingNetworkEndpoint = (*endpoint)(nil)
var _ stack.GroupAddressableEndpoint = (*endpoint)(nil)
var _ stack.AddressableEndpoint = (*endpoint)(nil)
var _ stack.NetworkEndpoint = (*endpoint)(nil)
@@ -198,6 +199,15 @@ type endpoint struct {
// Must be accessed using atomic operations.
forwarding uint32
// multicastForwarding is set to forwardingEnabled when the endpoint has
// forwarding enabled and forwardingDisabled when it is disabled.
//
// TODO(https://gvisor.dev/issue/7338): Implement support for multicast
// forwarding. Currently, setting this value to true is a no-op.
//
// Must be accessed using atomic operations.
multicastForwarding uint32
mu struct {
sync.RWMutex
@@ -505,6 +515,24 @@ func (e *endpoint) SetForwarding(forwarding bool) bool {
return prevForwarding
}
// MulticastForwarding implements stack.MulticastForwardingNetworkEndpoint.
func (e *endpoint) MulticastForwarding() bool {
return atomic.LoadUint32(&e.multicastForwarding) == forwardingEnabled
}
// SetMulticastForwarding implements stack.MulticastForwardingNetworkEndpoint.
func (e *endpoint) SetMulticastForwarding(forwarding bool) bool {
e.mu.Lock()
defer e.mu.Unlock()
updatedForwarding := uint32(forwardingDisabled)
if forwarding {
updatedForwarding = forwardingEnabled
}
return atomic.SwapUint32(&e.multicastForwarding, updatedForwarding) != forwardingDisabled
}
// Enable implements stack.NetworkEndpoint.
func (e *endpoint) Enable() tcpip.Error {
e.mu.Lock()
+32
View File
@@ -1040,3 +1040,35 @@ func (n *nic) forwarding(protocol tcpip.NetworkProtocolNumber) (bool, tcpip.Erro
return forwardingEP.Forwarding(), nil
}
func (n *nic) multicastForwardingEndpoint(protocol tcpip.NetworkProtocolNumber) (MulticastForwardingNetworkEndpoint, tcpip.Error) {
ep := n.getNetworkEndpoint(protocol)
if ep == nil {
return nil, &tcpip.ErrUnknownProtocol{}
}
forwardingEP, ok := ep.(MulticastForwardingNetworkEndpoint)
if !ok {
return nil, &tcpip.ErrNotSupported{}
}
return forwardingEP, nil
}
func (n *nic) setMulticastForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) (bool, tcpip.Error) {
ep, err := n.multicastForwardingEndpoint(protocol)
if err != nil {
return false, err
}
return ep.SetMulticastForwarding(enable), nil
}
func (n *nic) multicastForwarding(protocol tcpip.NetworkProtocolNumber) (bool, tcpip.Error) {
ep, err := n.multicastForwardingEndpoint(protocol)
if err != nil {
return false, err
}
return ep.MulticastForwarding(), nil
}
+15
View File
@@ -691,6 +691,21 @@ type ForwardingNetworkEndpoint interface {
SetForwarding(bool) bool
}
// MulticastForwardingNetworkEndpoint is a network endpoint that may forward
// multicast packets.
type MulticastForwardingNetworkEndpoint interface {
ForwardingNetworkEndpoint
// MulticastForwarding returns true if multicast forwarding is enabled.
// Otherwise, returns false.
MulticastForwarding() bool
// SetMulticastForwarding sets the multicast forwarding configuration.
//
// Returns the previous forwarding configuration.
SetMulticastForwarding(bool) bool
}
// NetworkProtocol is the interface that needs to be implemented by network
// protocols (e.g., ipv4, ipv6) that want to be part of the networking stack.
type NetworkProtocol interface {
+69 -18
View File
@@ -563,6 +563,40 @@ func (s *Stack) SetForwardingDefaultAndAllNICs(protocol tcpip.NetworkProtocolNum
return nil
}
// SetNICMulticastForwarding enables or disables multicast packet forwarding on
// the specified NIC for the passed protocol.
//
// Returns the previous configuration on the NIC.
//
// TODO(https://gvisor.dev/issue/7338): Implement support for multicast
// forwarding. Currently, setting this value is a no-op and is not ready for
// use.
func (s *Stack) SetNICMulticastForwarding(id tcpip.NICID, protocol tcpip.NetworkProtocolNumber, enable bool) (bool, tcpip.Error) {
s.mu.RLock()
defer s.mu.RUnlock()
nic, ok := s.nics[id]
if !ok {
return false, &tcpip.ErrUnknownNICID{}
}
return nic.setMulticastForwarding(protocol, enable)
}
// NICMulticastForwarding returns the multicast forwarding configuration for
// the specified NIC.
func (s *Stack) NICMulticastForwarding(id tcpip.NICID, protocol tcpip.NetworkProtocolNumber) (bool, tcpip.Error) {
s.mu.RLock()
defer s.mu.RUnlock()
nic, ok := s.nics[id]
if !ok {
return false, &tcpip.ErrUnknownNICID{}
}
return nic.multicastForwarding(protocol)
}
// PortRange returns the UDP and TCP inclusive range of ephemeral ports used in
// both IPv4 and IPv6.
func (s *Stack) PortRange() (uint16, uint16) {
@@ -843,6 +877,10 @@ type NICInfo struct {
// Forwarding holds the forwarding status for each network endpoint that
// supports forwarding.
Forwarding map[tcpip.NetworkProtocolNumber]bool
// MulticastForwarding holds the forwarding status for each network endpoint
// that supports multicast forwarding.
MulticastForwarding map[tcpip.NetworkProtocolNumber]bool
}
// HasNIC returns true if the NICID is defined in the stack.
@@ -858,6 +896,21 @@ func (s *Stack) NICInfo() map[tcpip.NICID]NICInfo {
s.mu.RLock()
defer s.mu.RUnlock()
type forwardingFn func(tcpip.NetworkProtocolNumber) (bool, tcpip.Error)
forwardingValue := func(forwardingFn forwardingFn, proto tcpip.NetworkProtocolNumber, nicID tcpip.NICID, fnName string) (forward bool, ok bool) {
switch forwarding, err := forwardingFn(proto); err.(type) {
case nil:
return forwarding, true
case *tcpip.ErrUnknownProtocol:
panic(fmt.Sprintf("expected network protocol %d to be available on NIC %d", proto, nicID))
case *tcpip.ErrNotSupported:
// Not all network protocols support forwarding.
default:
panic(fmt.Sprintf("nic(id=%d).%s(%d): %s", nicID, fnName, proto, err))
}
return false, false
}
nics := make(map[tcpip.NICID]NICInfo)
for id, nic := range s.nics {
flags := NICStateFlags{
@@ -873,28 +926,26 @@ func (s *Stack) NICInfo() map[tcpip.NICID]NICInfo {
}
info := NICInfo{
Name: nic.name,
LinkAddress: nic.NetworkLinkEndpoint.LinkAddress(),
ProtocolAddresses: nic.primaryAddresses(),
Flags: flags,
MTU: nic.NetworkLinkEndpoint.MTU(),
Stats: nic.stats.local,
NetworkStats: netStats,
Context: nic.context,
ARPHardwareType: nic.NetworkLinkEndpoint.ARPHardwareType(),
Forwarding: make(map[tcpip.NetworkProtocolNumber]bool),
Name: nic.name,
LinkAddress: nic.NetworkLinkEndpoint.LinkAddress(),
ProtocolAddresses: nic.primaryAddresses(),
Flags: flags,
MTU: nic.NetworkLinkEndpoint.MTU(),
Stats: nic.stats.local,
NetworkStats: netStats,
Context: nic.context,
ARPHardwareType: nic.NetworkLinkEndpoint.ARPHardwareType(),
Forwarding: make(map[tcpip.NetworkProtocolNumber]bool),
MulticastForwarding: make(map[tcpip.NetworkProtocolNumber]bool),
}
for proto := range s.networkProtocols {
switch forwarding, err := nic.forwarding(proto); err.(type) {
case nil:
if forwarding, ok := forwardingValue(nic.forwarding, proto, id, "forwarding"); ok {
info.Forwarding[proto] = forwarding
case *tcpip.ErrUnknownProtocol:
panic(fmt.Sprintf("expected network protocol %d to be available on NIC %d", proto, nic.ID()))
case *tcpip.ErrNotSupported:
// Not all network protocols support forwarding.
default:
panic(fmt.Sprintf("nic(id=%d).forwarding(%d): %s", nic.ID(), proto, err))
}
if multicastForwarding, ok := forwardingValue(nic.multicastForwarding, proto, id, "multicastForwarding"); ok {
info.MulticastForwarding[proto] = multicastForwarding
}
}
+118 -41
View File
@@ -84,8 +84,9 @@ type fakeNetworkEndpoint struct {
mu struct {
sync.RWMutex
enabled bool
forwarding bool
enabled bool
forwarding bool
multicastForwarding bool
}
nic stack.NetworkInterface
@@ -313,6 +314,22 @@ func (f *fakeNetworkEndpoint) SetForwarding(v bool) bool {
return prev
}
// MulticastForwarding implements stack.MulticastForwardingNetworkEndpoint.
func (f *fakeNetworkEndpoint) MulticastForwarding() bool {
f.mu.RLock()
defer f.mu.RUnlock()
return f.mu.multicastForwarding
}
// SetMulticastForwarding implements stack.MulticastForwardingNetworkEndpoint.
func (f *fakeNetworkEndpoint) SetMulticastForwarding(v bool) bool {
f.mu.Lock()
defer f.mu.Unlock()
prev := f.mu.multicastForwarding
f.mu.multicastForwarding = v
return prev
}
func fakeNetFactory(s *stack.Stack) stack.NetworkProtocol {
return &fakeNetworkProtocol{stack: s}
}
@@ -4670,50 +4687,110 @@ func TestNICForwarding(t *testing.T) {
},
}
subTests := []struct {
name string
getForwardingFunc func(*stack.Stack, tcpip.NICID, tcpip.NetworkProtocolNumber) (bool, tcpip.Error)
getForwardingFuncName string
setForwardingFunc func(*stack.Stack, tcpip.NICID, tcpip.NetworkProtocolNumber, bool) (bool, tcpip.Error)
setForwardingFuncName string
getNicInfoForwardingMap func(stack.NICInfo) map[tcpip.NetworkProtocolNumber]bool
nicInfoForwardingMapName string
}{
{
name: "unicast",
getForwardingFunc: (*stack.Stack).NICForwarding,
getForwardingFuncName: "NICForwarding",
setForwardingFunc: (*stack.Stack).SetNICForwarding,
setForwardingFuncName: "SetNICForwarding",
getNicInfoForwardingMap: func(info stack.NICInfo) map[tcpip.NetworkProtocolNumber]bool { return info.Forwarding },
nicInfoForwardingMapName: "Forwarding",
},
{
name: "multicast",
getForwardingFunc: (*stack.Stack).NICMulticastForwarding,
getForwardingFuncName: "NICMulticastForwarding",
setForwardingFunc: (*stack.Stack).SetNICMulticastForwarding,
setForwardingFuncName: "SetNICMulticastForwarding",
getNicInfoForwardingMap: func(info stack.NICInfo) map[tcpip.NetworkProtocolNumber]bool { return info.MulticastForwarding },
nicInfoForwardingMapName: "MulticastForwarding",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{test.factory},
})
if err := s.CreateNIC(nicID, channel.New(0, defaultMTU, "")); err != nil {
t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
}
for _, subTest := range subTests {
t.Run(subTest.name, func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{test.factory},
})
if err := s.CreateNIC(nicID, channel.New(0, defaultMTU, "")); err != nil {
t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
}
// Forwarding should initially be disabled.
if forwarding, err := s.NICForwarding(nicID, test.netProto); err != nil {
t.Fatalf("s.NICForwarding(%d, %d): %s", nicID, test.netProto, err)
} else if forwarding {
t.Errorf("got s.NICForwarding(%d, %d) = true, want = false", nicID, test.netProto)
}
// Forwarding should initially be disabled.
if forwarding, err := subTest.getForwardingFunc(s, nicID, test.netProto); err != nil {
t.Fatalf("s.%s(%d, %d): %s", subTest.getForwardingFuncName, nicID, test.netProto, err)
} else if forwarding {
t.Errorf("got s.%s(%d, %d) = true, want = false", subTest.getForwardingFuncName, nicID, test.netProto)
}
// Setting forwarding to be enabled should return the previous
// configuration of false. Enabling it a second time should be a no-op.
for _, wantPrevForwarding := range [...]bool{false, true} {
if prevForwarding, err := s.SetNICForwarding(nicID, test.netProto, true); err != nil {
t.Fatalf("s.SetNICForwarding(%d, %d, true): %s", nicID, test.netProto, err)
} else if prevForwarding != wantPrevForwarding {
t.Errorf("got s.SetNICForwarding(%d, %d, true) = %t, want = %t", nicID, test.netProto, prevForwarding, wantPrevForwarding)
}
if forwarding, err := s.NICForwarding(nicID, test.netProto); err != nil {
t.Fatalf("s.NICForwarding(%d, %d): %s", nicID, test.netProto, err)
} else if !forwarding {
t.Errorf("got s.NICForwarding(%d, %d) = false, want = true", nicID, test.netProto)
}
}
// Setting forwarding to be enabled should return the previous
// configuration of false. Enabling it a second time should be a
// no-op.
for _, wantPrevForwarding := range [...]bool{false, true} {
if prevForwarding, err := subTest.setForwardingFunc(s, nicID, test.netProto, true); err != nil {
t.Fatalf("s.%s(%d, %d, true): %s", subTest.setForwardingFuncName, nicID, test.netProto, err)
} else if prevForwarding != wantPrevForwarding {
t.Errorf("got s.%s(%d, %d, true) = %t, want = %t", subTest.setForwardingFuncName, nicID, test.netProto, prevForwarding, wantPrevForwarding)
}
if forwarding, err := subTest.getForwardingFunc(s, nicID, test.netProto); err != nil {
t.Fatalf("s.%s(%d, %d): %s", subTest.getForwardingFuncName, nicID, test.netProto, err)
} else if !forwarding {
t.Errorf("got s.%s(%d, %d) = false, want = true", subTest.getForwardingFuncName, nicID, test.netProto)
}
// Verify that the NICInfo also contains the expected value.
allNICInfo := s.NICInfo()
if info, ok := allNICInfo[nicID]; !ok {
t.Fatalf("entry for %d missing from allNICInfo = %+v", nicID, allNICInfo)
} else {
forwardingMap := subTest.getNicInfoForwardingMap(info)
if forward, ok := forwardingMap[test.netProto]; !ok {
t.Fatalf("entry for %d missing from info.%s = %+v", test.netProto, subTest.nicInfoForwardingMapName, forwardingMap)
} else if !forward {
t.Errorf("got info.%s[%d] = %t, want = true", subTest.nicInfoForwardingMapName, test.netProto, forward)
}
}
}
// Setting forwarding to be disabled should return the previous
// configuration of true. Disabling it a second time should be a no-op.
for _, wantPrevForwarding := range [...]bool{true, false} {
if prevForwarding, err := s.SetNICForwarding(nicID, test.netProto, false); err != nil {
t.Fatalf("s.SetNICForwarding(%d, %d, false): %s", nicID, test.netProto, err)
} else if prevForwarding != wantPrevForwarding {
t.Errorf("got s.SetNICForwarding(%d, %d, false) = %t, want = %t", nicID, test.netProto, prevForwarding, wantPrevForwarding)
}
if forwarding, err := s.NICForwarding(nicID, test.netProto); err != nil {
t.Fatalf("s.NICForwarding(%d, %d): %s", nicID, test.netProto, err)
} else if forwarding {
t.Errorf("got s.NICForwarding(%d, %d) = true, want = false", nicID, test.netProto)
}
// Setting forwarding to be disabled should return the previous
// configuration of true. Disabling it a second time should be a
// no-op.
for _, wantPrevForwarding := range [...]bool{true, false} {
if prevForwarding, err := subTest.setForwardingFunc(s, nicID, test.netProto, false); err != nil {
t.Fatalf("s.%s(%d, %d, false): %s", subTest.setForwardingFuncName, nicID, test.netProto, err)
} else if prevForwarding != wantPrevForwarding {
t.Errorf("got s.%s(%d, %d, false) = %t, want = %t", subTest.setForwardingFuncName, nicID, test.netProto, prevForwarding, wantPrevForwarding)
}
if forwarding, err := subTest.getForwardingFunc(s, nicID, test.netProto); err != nil {
t.Fatalf("s.%s(%d, %d): %s", subTest.getForwardingFuncName, nicID, test.netProto, err)
} else if forwarding {
t.Errorf("got s.%s(%d, %d) = true, want = false", subTest.getForwardingFuncName, nicID, test.netProto)
}
// Verify that the NICInfo also contains the expected value.
allNICInfo := s.NICInfo()
if info, ok := allNICInfo[nicID]; !ok {
t.Fatalf("entry for %d missing from allNICInfo = %+v", nicID, allNICInfo)
} else {
forwardingMap := subTest.getNicInfoForwardingMap(info)
if forward, ok := forwardingMap[test.netProto]; !ok {
t.Fatalf("entry for %d missing from info.%s = %+v", test.netProto, subTest.nicInfoForwardingMapName, forwardingMap)
} else if forward {
t.Errorf("got info.%s[%d] = %t, want = false", subTest.nicInfoForwardingMapName, test.netProto, forward)
}
}
}
})
}
})
}