Track join count in multicast group protocol state

Before this change, the join count and the state for IGMP/MLD was held
across different types which required multiple locks to be held when
accessing a multicast group's state.

Bug #4682, #4861
Fixes #4916

PiperOrigin-RevId: 345019091
This commit is contained in:
Ghanan Gowripalan
2020-12-01 07:52:40 -08:00
committed by gVisor bot
parent 6b1dbbbdc8
commit 25570ac4f3
10 changed files with 791 additions and 493 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+41 -45
View File
@@ -124,7 +124,14 @@ func (igmp *igmpState) init(ep *endpoint, opts IGMPOptions) {
defer igmp.mu.Unlock()
igmp.ep = ep
igmp.opts = opts
igmp.mu.genericMulticastProtocol.Init(ep.protocol.stack.Rand(), ep.protocol.stack.Clock(), igmp, UnsolicitedReportIntervalMax)
igmp.mu.genericMulticastProtocol.Init(ip.GenericMulticastProtocolOptions{
Enabled: opts.Enabled,
Rand: ep.protocol.stack.Rand(),
Clock: ep.protocol.stack.Clock(),
Protocol: igmp,
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
AllNodesAddress: header.IPv4AllSystems,
})
igmp.igmpV1Present = igmpV1PresentDefault
igmp.mu.igmpV1Job = igmp.ep.protocol.stack.NewJob(&igmp.mu, func() {
igmp.setV1Present(false)
@@ -201,17 +208,13 @@ func (igmp *igmpState) setV1Present(v bool) {
}
func (igmp *igmpState) handleMembershipQuery(groupAddress tcpip.Address, maxRespTime time.Duration) {
if !igmp.opts.Enabled {
return
}
igmp.mu.Lock()
defer igmp.mu.Unlock()
// As per RFC 2236 Section 6, Page 10: If the maximum response time is zero
// then change the state to note that an IGMPv1 router is present and
// schedule the query received Job.
if maxRespTime == 0 {
if maxRespTime == 0 && igmp.opts.Enabled {
igmp.mu.igmpV1Job.Cancel()
igmp.mu.igmpV1Job.Schedule(v1RouterPresentTimeout)
igmp.setV1Present(true)
@@ -222,10 +225,6 @@ func (igmp *igmpState) handleMembershipQuery(groupAddress tcpip.Address, maxResp
}
func (igmp *igmpState) handleMembershipReport(groupAddress tcpip.Address) {
if !igmp.opts.Enabled {
return
}
igmp.mu.Lock()
defer igmp.mu.Unlock()
igmp.mu.genericMulticastProtocol.HandleReport(groupAddress)
@@ -279,49 +278,46 @@ func (igmp *igmpState) writePacket(destAddress tcpip.Address, groupAddress tcpip
//
// If the group already exists in the membership map, returns
// tcpip.ErrDuplicateAddress.
func (igmp *igmpState) joinGroup(groupAddress tcpip.Address) *tcpip.Error {
if !igmp.opts.Enabled {
return nil
}
// As per RFC 2236 section 6 page 10,
//
// The all-systems group (address 224.0.0.1) is handled as a special
// case. The host starts in Idle Member state for that group on every
// interface, never transitions to another state, and never sends a
// report for that group.
//
// This is equivalent to not performing IGMP for the all-systems multicast
// address. Simply not performing IGMP when the group is added will prevent
// any work from being done on the all-systems multicast group when leaving
// the group or when query or report messages are received for it since the
// MGP state will not know about it.
if groupAddress == header.IPv4AllSystems {
return nil
}
func (igmp *igmpState) joinGroup(groupAddress tcpip.Address) {
igmp.mu.Lock()
defer igmp.mu.Unlock()
igmp.mu.genericMulticastProtocol.JoinGroup(groupAddress, !igmp.ep.Enabled() /* dontInitialize */)
}
// JoinGroup returns false if we have already joined the group.
if !igmp.mu.genericMulticastProtocol.JoinGroup(groupAddress) {
return tcpip.ErrDuplicateAddress
}
return nil
// isInGroup returns true if the specified group has been joined locally.
func (igmp *igmpState) isInGroup(groupAddress tcpip.Address) bool {
igmp.mu.Lock()
defer igmp.mu.Unlock()
return igmp.mu.genericMulticastProtocol.IsLocallyJoined(groupAddress)
}
// leaveGroup handles removing the group from the membership map, cancels any
// delay timers associated with that group, and sends the Leave Group message
// if required.
//
// If the group does not exist in the membership map, this function will
// silently return.
func (igmp *igmpState) leaveGroup(groupAddress tcpip.Address) {
if !igmp.opts.Enabled {
return
}
func (igmp *igmpState) leaveGroup(groupAddress tcpip.Address) *tcpip.Error {
igmp.mu.Lock()
defer igmp.mu.Unlock()
igmp.mu.genericMulticastProtocol.LeaveGroup(groupAddress)
// LeaveGroup returns false only if the group was not joined.
if igmp.mu.genericMulticastProtocol.LeaveGroup(groupAddress) {
return nil
}
return tcpip.ErrBadLocalAddress
}
// softLeaveAll leaves all groups from the perspective of IGMP, but remains
// joined locally.
func (igmp *igmpState) softLeaveAll() {
igmp.mu.Lock()
defer igmp.mu.Unlock()
igmp.mu.genericMulticastProtocol.MakeAllNonMember()
}
// initializeAll attemps to initialize the IGMP state for each group that has
// been joined locally.
func (igmp *igmpState) initializeAll() {
igmp.mu.Lock()
defer igmp.mu.Unlock()
igmp.mu.genericMulticastProtocol.InitializeGroups()
}
+21 -48
View File
@@ -127,16 +127,18 @@ func (e *endpoint) Enable() *tcpip.Error {
// endpoint may have left groups from the perspective of IGMP when the
// endpoint was disabled. Either way, we need to let routers know to
// send us multicast traffic.
joinedGroups := e.mu.addressableEndpointState.JoinedGroups()
for _, group := range joinedGroups {
e.igmp.joinGroup(group)
}
e.igmp.initializeAll()
// As per RFC 1122 section 3.3.7, all hosts should join the all-hosts
// multicast group. Note, the IANA calls the all-hosts multicast group the
// all-systems multicast group.
_, err = e.joinGroupLocked(header.IPv4AllSystems)
return err
if err := e.joinGroupLocked(header.IPv4AllSystems); err != nil {
// joinGroupLocked only returns an error if the group address is not a valid
// IPv4 multicast address.
panic(fmt.Sprintf("e.joinGroupLocked(%s): %s", header.IPv4AllSystems, err))
}
return nil
}
// Enabled implements stack.NetworkEndpoint.
@@ -173,16 +175,13 @@ func (e *endpoint) disableLocked() {
}
// The endpoint may have already left the multicast group.
if _, err := e.leaveGroupLocked(header.IPv4AllSystems); err != nil && err != tcpip.ErrBadLocalAddress {
if err := e.leaveGroupLocked(header.IPv4AllSystems); err != nil && err != tcpip.ErrBadLocalAddress {
panic(fmt.Sprintf("unexpected error when leaving group = %s: %s", header.IPv4AllSystems, err))
}
// Leave groups from the perspective of IGMP so that routers know that
// we are no longer interested in the group.
joinedGroups := e.mu.addressableEndpointState.JoinedGroups()
for _, group := range joinedGroups {
e.igmp.leaveGroup(group)
}
e.igmp.softLeaveAll()
// The address may have already been removed.
if err := e.mu.addressableEndpointState.RemovePermanentAddress(ipv4BroadcastAddr.Address); err != nil && err != tcpip.ErrBadLocalAddress {
@@ -849,69 +848,43 @@ func (e *endpoint) PermanentAddresses() []tcpip.AddressWithPrefix {
}
// JoinGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) JoinGroup(addr tcpip.Address) (bool, *tcpip.Error) {
func (e *endpoint) JoinGroup(addr tcpip.Address) *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
return e.joinGroupLocked(addr)
}
// joinGroupLocked is like JoinGroup, but with locking requirements.
// joinGroupLocked is like JoinGroup but with locking requirements.
//
// Precondition: e.mu must be locked.
func (e *endpoint) joinGroupLocked(addr tcpip.Address) (bool, *tcpip.Error) {
func (e *endpoint) joinGroupLocked(addr tcpip.Address) *tcpip.Error {
if !header.IsV4MulticastAddress(addr) {
return false, tcpip.ErrBadAddress
}
// TODO(gvisor.dev/issue/4916): Keep track of join count and IGMP state in a
// single type.
joined, err := e.mu.addressableEndpointState.JoinGroup(addr)
if err != nil || !joined {
return joined, err
return tcpip.ErrBadAddress
}
// Only join the group from the perspective of IGMP when the endpoint is
// enabled.
//
// If we are not enabled right now, we will join the group from the
// perspective of IGMP when the endpoint is enabled.
if !e.Enabled() {
return true, nil
}
// joinGroup only returns an error if we try to join a group twice, but we
// checked above to make sure that the group was newly joined.
if err := e.igmp.joinGroup(addr); err != nil {
panic(fmt.Sprintf("e.igmp.joinGroup(%s): %s", addr, err))
}
return true, nil
e.igmp.joinGroup(addr)
return nil
}
// LeaveGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) LeaveGroup(addr tcpip.Address) (bool, *tcpip.Error) {
func (e *endpoint) LeaveGroup(addr tcpip.Address) *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
return e.leaveGroupLocked(addr)
}
// leaveGroupLocked is like LeaveGroup, but with locking requirements.
// leaveGroupLocked is like LeaveGroup but with locking requirements.
//
// Precondition: e.mu must be locked.
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) (bool, *tcpip.Error) {
left, err := e.mu.addressableEndpointState.LeaveGroup(addr)
if err != nil || !left {
return left, err
}
e.igmp.leaveGroup(addr)
return true, nil
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) *tcpip.Error {
return e.igmp.leaveGroup(addr)
}
// IsInGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) IsInGroup(addr tcpip.Address) bool {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.addressableEndpointState.IsInGroup(addr)
return e.igmp.isInGroup(addr)
}
var _ stack.ForwardingNetworkProtocol = (*protocol)(nil)
+24 -52
View File
@@ -232,10 +232,7 @@ func (e *endpoint) Enable() *tcpip.Error {
// endpoint may have left groups from the perspective of MLD when the
// endpoint was disabled. Either way, we need to let routers know to
// send us multicast traffic.
joinedGroups := e.mu.addressableEndpointState.JoinedGroups()
for _, group := range joinedGroups {
e.mld.joinGroup(group)
}
e.mld.initializeAll()
// Join the IPv6 All-Nodes Multicast group if the stack is configured to
// use IPv6. This is required to ensure that this node properly receives
@@ -254,8 +251,10 @@ func (e *endpoint) Enable() *tcpip.Error {
// (NDP NS) messages may be sent to the All-Nodes multicast group if the
// source address of the NDP NS is the unspecified address, as per RFC 4861
// section 7.2.4.
if _, err := e.joinGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil {
return err
if err := e.joinGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil {
// joinGroupLocked only returns an error if the group address is not a valid
// IPv6 multicast address.
panic(fmt.Sprintf("e.joinGroupLocked(%s): %s", header.IPv6AllNodesMulticastAddress, err))
}
// Perform DAD on the all the unicast IPv6 endpoints that are in the permanent
@@ -344,16 +343,13 @@ func (e *endpoint) disableLocked() {
e.stopDADForPermanentAddressesLocked()
// The endpoint may have already left the multicast group.
if _, err := e.leaveGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil && err != tcpip.ErrBadLocalAddress {
if err := e.leaveGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil && err != tcpip.ErrBadLocalAddress {
panic(fmt.Sprintf("unexpected error when leaving group = %s: %s", header.IPv6AllNodesMulticastAddress, err))
}
// Leave groups from the perspective of MLD so that routers know that
// we are no longer interested in the group.
joinedGroups := e.mu.addressableEndpointState.JoinedGroups()
for _, group := range joinedGroups {
e.mld.leaveGroup(group)
}
e.mld.softLeaveAll()
}
// stopDADForPermanentAddressesLocked stops DAD for all permaneent addresses.
@@ -1182,8 +1178,10 @@ func (e *endpoint) addAndAcquirePermanentAddressLocked(addr tcpip.AddressWithPre
}
snmc := header.SolicitedNodeAddr(addr.Address)
if _, err := e.joinGroupLocked(snmc); err != nil {
return nil, err
if err := e.joinGroupLocked(snmc); err != nil {
// joinGroupLocked only returns an error if the group address is not a valid
// IPv6 multicast address.
panic(fmt.Sprintf("e.joinGroupLocked(%s): %s", snmc, err))
}
addressEndpoint.SetKind(stack.PermanentTentative)
@@ -1239,7 +1237,8 @@ func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEn
}
snmc := header.SolicitedNodeAddr(addr.Address)
if _, err := e.leaveGroupLocked(snmc); err != nil && err != tcpip.ErrBadLocalAddress {
// The endpoint may have already left the multicast group.
if err := e.leaveGroupLocked(snmc); err != nil && err != tcpip.ErrBadLocalAddress {
return err
}
@@ -1404,70 +1403,43 @@ func (e *endpoint) PermanentAddresses() []tcpip.AddressWithPrefix {
}
// JoinGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) JoinGroup(addr tcpip.Address) (bool, *tcpip.Error) {
func (e *endpoint) JoinGroup(addr tcpip.Address) *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
return e.joinGroupLocked(addr)
}
// joinGroupLocked is like JoinGroup, but with locking requirements.
// joinGroupLocked is like JoinGroup but with locking requirements.
//
// Precondition: e.mu must be locked.
func (e *endpoint) joinGroupLocked(addr tcpip.Address) (bool, *tcpip.Error) {
func (e *endpoint) joinGroupLocked(addr tcpip.Address) *tcpip.Error {
if !header.IsV6MulticastAddress(addr) {
return false, tcpip.ErrBadAddress
return tcpip.ErrBadAddress
}
// TODO(gvisor.dev/issue/4916): Keep track of join count and MLD state in a
// single type.
joined, err := e.mu.addressableEndpointState.JoinGroup(addr)
if err != nil || !joined {
return joined, err
}
// Only join the group from the perspective of IGMP when the endpoint is
// enabled.
//
// If we are not enabled right now, we will join the group from the
// perspective of MLD when the endpoint is enabled.
if !e.Enabled() {
return true, nil
}
// joinGroup only returns an error if we try to join a group twice, but we
// checked above to make sure that the group was newly joined.
if err := e.mld.joinGroup(addr); err != nil {
panic(fmt.Sprintf("e.mld.joinGroup(%s): %s", addr, err))
}
return true, nil
e.mld.joinGroup(addr)
return nil
}
// LeaveGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) LeaveGroup(addr tcpip.Address) (bool, *tcpip.Error) {
func (e *endpoint) LeaveGroup(addr tcpip.Address) *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
return e.leaveGroupLocked(addr)
}
// leaveGroupLocked is like LeaveGroup, but with locking requirements.
// leaveGroupLocked is like LeaveGroup but with locking requirements.
//
// Precondition: e.mu must be locked.
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) (bool, *tcpip.Error) {
left, err := e.mu.addressableEndpointState.LeaveGroup(addr)
if err != nil || !left {
return left, err
}
e.mld.leaveGroup(addr)
return true, nil
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) *tcpip.Error {
return e.mld.leaveGroup(addr)
}
// IsInGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) IsInGroup(addr tcpip.Address) bool {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.addressableEndpointState.IsInGroup(addr)
return e.mld.isInGroup(addr)
}
var _ stack.ForwardingNetworkProtocol = (*protocol)(nil)
+32 -43
View File
@@ -50,8 +50,7 @@ var _ ip.MulticastGroupProtocol = (*mldState)(nil)
// mldState.init MUST be called to initialize the MLD state.
type mldState struct {
// The IPv6 endpoint this mldState is for.
ep *endpoint
opts MLDOptions
ep *endpoint
genericMulticastProtocol ip.GenericMulticastProtocolState
}
@@ -70,23 +69,21 @@ func (mld *mldState) SendLeave(groupAddress tcpip.Address) *tcpip.Error {
// a new mldState.
func (mld *mldState) init(ep *endpoint, opts MLDOptions) {
mld.ep = ep
mld.opts = opts
mld.genericMulticastProtocol.Init(ep.protocol.stack.Rand(), ep.protocol.stack.Clock(), mld, UnsolicitedReportIntervalMax)
mld.genericMulticastProtocol.Init(ip.GenericMulticastProtocolOptions{
Enabled: opts.Enabled,
Rand: ep.protocol.stack.Rand(),
Clock: ep.protocol.stack.Clock(),
Protocol: mld,
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
AllNodesAddress: header.IPv6AllNodesMulticastAddress,
})
}
func (mld *mldState) handleMulticastListenerQuery(mldHdr header.MLD) {
if !mld.opts.Enabled {
return
}
mld.genericMulticastProtocol.HandleQuery(mldHdr.MulticastAddress(), mldHdr.MaximumResponseDelay())
}
func (mld *mldState) handleMulticastListenerReport(mldHdr header.MLD) {
if !mld.opts.Enabled {
return
}
mld.genericMulticastProtocol.HandleReport(mldHdr.MulticastAddress())
}
@@ -94,45 +91,37 @@ func (mld *mldState) handleMulticastListenerReport(mldHdr header.MLD) {
// messages.
//
// If the group is already joined, returns tcpip.ErrDuplicateAddress.
func (mld *mldState) joinGroup(groupAddress tcpip.Address) *tcpip.Error {
if !mld.opts.Enabled {
return nil
}
func (mld *mldState) joinGroup(groupAddress tcpip.Address) {
mld.genericMulticastProtocol.JoinGroup(groupAddress, !mld.ep.Enabled() /* dontInitialize */)
}
// As per RFC 2710 section 5 page 10,
//
// The link-scope all-nodes address (FF02::1) is handled as a special
// case. The node starts in Idle Listener state for that address on
// every interface, never transitions to another state, and never sends
// a Report or Done for that address.
//
// This is equivalent to not performing MLD for the all-nodes multicast
// address. Simply not performing MLD when the group is added will prevent
// any work from being done on the all-nodes multicast group when leaving the
// group or when query or report messages are received for it since the MGP
// state will not know about it.
if groupAddress == header.IPv6AllNodesMulticastAddress {
return nil
}
// JoinGroup returns false if we have already joined the group.
if !mld.genericMulticastProtocol.JoinGroup(groupAddress) {
return tcpip.ErrDuplicateAddress
}
return nil
// isInGroup returns true if the specified group has been joined locally.
func (mld *mldState) isInGroup(groupAddress tcpip.Address) bool {
return mld.genericMulticastProtocol.IsLocallyJoined(groupAddress)
}
// leaveGroup handles removing the group from the membership map, cancels any
// delay timers associated with that group, and sends the Done message, if
// required.
//
// If the group is not joined, this function will do nothing.
func (mld *mldState) leaveGroup(groupAddress tcpip.Address) {
if !mld.opts.Enabled {
return
func (mld *mldState) leaveGroup(groupAddress tcpip.Address) *tcpip.Error {
// LeaveGroup returns false only if the group was not joined.
if mld.genericMulticastProtocol.LeaveGroup(groupAddress) {
return nil
}
mld.genericMulticastProtocol.LeaveGroup(groupAddress)
return tcpip.ErrBadLocalAddress
}
// softLeaveAll leaves all groups from the perspective of MLD, but remains
// joined locally.
func (mld *mldState) softLeaveAll() {
mld.genericMulticastProtocol.MakeAllNonMember()
}
// initializeAll attemps to initialize the MLD state for each group that has
// been joined locally.
func (mld *mldState) initializeAll() {
mld.genericMulticastProtocol.InitializeGroups()
}
func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldType header.ICMPv6Type) *tcpip.Error {
@@ -21,7 +21,6 @@ import (
"gvisor.dev/gvisor/pkg/tcpip"
)
var _ GroupAddressableEndpoint = (*AddressableEndpointState)(nil)
var _ AddressableEndpoint = (*AddressableEndpointState)(nil)
// AddressableEndpointState is an implementation of an AddressableEndpoint.
@@ -37,10 +36,6 @@ type AddressableEndpointState struct {
endpoints map[tcpip.Address]*addressState
primary []*addressState
// groups holds the mapping between group addresses and the number of times
// they have been joined.
groups map[tcpip.Address]uint32
}
}
@@ -53,7 +48,6 @@ func (a *AddressableEndpointState) Init(networkEndpoint NetworkEndpoint) {
a.mu.Lock()
defer a.mu.Unlock()
a.mu.endpoints = make(map[tcpip.Address]*addressState)
a.mu.groups = make(map[tcpip.Address]uint32)
}
// ReadOnlyAddressableEndpointState provides read-only access to an
@@ -335,11 +329,6 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
func (a *AddressableEndpointState) RemovePermanentAddress(addr tcpip.Address) *tcpip.Error {
a.mu.Lock()
defer a.mu.Unlock()
if _, ok := a.mu.groups[addr]; ok {
panic(fmt.Sprintf("group address = %s must be removed with LeaveGroup", addr))
}
return a.removePermanentAddressLocked(addr)
}
@@ -588,61 +577,11 @@ func (a *AddressableEndpointState) PermanentAddresses() []tcpip.AddressWithPrefi
return addrs
}
// JoinGroup implements GroupAddressableEndpoint.
func (a *AddressableEndpointState) JoinGroup(group tcpip.Address) (bool, *tcpip.Error) {
a.mu.Lock()
defer a.mu.Unlock()
joins, ok := a.mu.groups[group]
a.mu.groups[group] = joins + 1
return !ok, nil
}
// LeaveGroup implements GroupAddressableEndpoint.
func (a *AddressableEndpointState) LeaveGroup(group tcpip.Address) (bool, *tcpip.Error) {
a.mu.Lock()
defer a.mu.Unlock()
joins, ok := a.mu.groups[group]
if !ok {
return false, tcpip.ErrBadLocalAddress
}
if joins == 1 {
delete(a.mu.groups, group)
return true, nil
}
a.mu.groups[group] = joins - 1
return false, nil
}
// IsInGroup implements GroupAddressableEndpoint.
func (a *AddressableEndpointState) IsInGroup(group tcpip.Address) bool {
a.mu.RLock()
defer a.mu.RUnlock()
_, ok := a.mu.groups[group]
return ok
}
// JoinedGroups returns a list of groups the endpoint is a member of.
func (a *AddressableEndpointState) JoinedGroups() []tcpip.Address {
a.mu.RLock()
defer a.mu.RUnlock()
groups := make([]tcpip.Address, 0, len(a.mu.groups))
for g := range a.mu.groups {
groups = append(groups, g)
}
return groups
}
// Cleanup forcefully leaves all groups and removes all permanent addresses.
func (a *AddressableEndpointState) Cleanup() {
a.mu.Lock()
defer a.mu.Unlock()
a.mu.groups = make(map[tcpip.Address]uint32)
for _, ep := range a.mu.endpoints {
// removePermanentEndpointLocked returns tcpip.ErrBadLocalAddress if ep is
// not a permanent address.
@@ -15,40 +15,12 @@
package stack_test
import (
"sort"
"testing"
"github.com/google/go-cmp/cmp"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
func TestJoinedGroups(t *testing.T) {
const addr1 = tcpip.Address("\x01")
const addr2 = tcpip.Address("\x02")
var ep fakeNetworkEndpoint
var s stack.AddressableEndpointState
s.Init(&ep)
if joined, err := s.JoinGroup(addr1); err != nil {
t.Fatalf("JoinGroup(%s): %s", addr1, err)
} else if !joined {
t.Errorf("got JoinGroup(%s) = false, want = true", addr1)
}
if joined, err := s.JoinGroup(addr2); err != nil {
t.Fatalf("JoinGroup(%s): %s", addr2, err)
} else if !joined {
t.Errorf("got JoinGroup(%s) = false, want = true", addr2)
}
joinedGroups := s.JoinedGroups()
sort.Slice(joinedGroups, func(i, j int) bool { return joinedGroups[i][0] < joinedGroups[j][0] })
if diff := cmp.Diff([]tcpip.Address{addr1, addr2}, joinedGroups); diff != "" {
t.Errorf("joined groups mismatch (-want +got):\n%s", diff)
}
}
// TestAddressableEndpointStateCleanup tests that cleaning up an addressable
// endpoint state removes permanent addresses and leaves groups.
func TestAddressableEndpointStateCleanup(t *testing.T) {
@@ -81,25 +53,9 @@ func TestAddressableEndpointStateCleanup(t *testing.T) {
ep.DecRef()
}
group := tcpip.Address("\x02")
if added, err := s.JoinGroup(group); err != nil {
t.Fatalf("s.JoinGroup(%s): %s", group, err)
} else if !added {
t.Fatalf("got s.JoinGroup(%s) = false, want = true", group)
}
if !s.IsInGroup(group) {
t.Fatalf("got s.IsInGroup(%s) = false, want = true", group)
}
s.Cleanup()
{
ep := s.AcquireAssignedAddress(addr.Address, false /* allowTemp */, stack.NeverPrimaryEndpoint)
if ep != nil {
ep.DecRef()
t.Fatalf("got s.AcquireAssignedAddress(%s, false, NeverPrimaryEndpoint) = %s, want = nil", addr.Address, ep.AddressWithPrefix())
}
}
if s.IsInGroup(group) {
t.Fatalf("got s.IsInGroup(%s) = true, want = false", group)
if ep := s.AcquireAssignedAddress(addr.Address, false /* allowTemp */, stack.NeverPrimaryEndpoint); ep != nil {
ep.DecRef()
t.Fatalf("got s.AcquireAssignedAddress(%s, false, NeverPrimaryEndpoint) = %s, want = nil", addr.Address, ep.AddressWithPrefix())
}
}
+2 -7
View File
@@ -563,8 +563,7 @@ func (n *NIC) joinGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address
return tcpip.ErrNotSupported
}
_, err := gep.JoinGroup(addr)
return err
return gep.JoinGroup(addr)
}
// leaveGroup decrements the count for the given multicast address, and when it
@@ -580,11 +579,7 @@ func (n *NIC) leaveGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Addres
return tcpip.ErrNotSupported
}
if _, err := gep.LeaveGroup(addr); err != nil {
return err
}
return nil
return gep.LeaveGroup(addr)
}
// isInGroup returns true if n has joined the multicast group addr.
+2 -6
View File
@@ -291,14 +291,10 @@ type NetworkHeaderParams struct {
// endpoints may associate themselves with the same identifier (group address).
type GroupAddressableEndpoint interface {
// JoinGroup joins the specified group.
//
// Returns true if the group was newly joined.
JoinGroup(group tcpip.Address) (bool, *tcpip.Error)
JoinGroup(group tcpip.Address) *tcpip.Error
// LeaveGroup attempts to leave the specified group.
//
// Returns tcpip.ErrBadLocalAddress if the endpoint has not joined the group.
LeaveGroup(group tcpip.Address) (bool, *tcpip.Error)
LeaveGroup(group tcpip.Address) *tcpip.Error
// IsInGroup returns true if the endpoint is a member of the specified group.
IsInGroup(group tcpip.Address) bool