summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/network/ip
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/network/ip')
-rw-r--r--pkg/tcpip/network/ip/BUILD29
-rw-r--r--pkg/tcpip/network/ip/generic_multicast_protocol.go696
-rw-r--r--pkg/tcpip/network/ip/generic_multicast_protocol_test.go812
-rw-r--r--pkg/tcpip/network/ip/stats.go100
4 files changed, 0 insertions, 1637 deletions
diff --git a/pkg/tcpip/network/ip/BUILD b/pkg/tcpip/network/ip/BUILD
deleted file mode 100644
index 411bca25d..000000000
--- a/pkg/tcpip/network/ip/BUILD
+++ /dev/null
@@ -1,29 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "ip",
- srcs = [
- "generic_multicast_protocol.go",
- "stats.go",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//pkg/sync",
- "//pkg/tcpip",
- ],
-)
-
-go_test(
- name = "ip_test",
- size = "small",
- srcs = ["generic_multicast_protocol_test.go"],
- deps = [
- ":ip",
- "//pkg/sync",
- "//pkg/tcpip",
- "//pkg/tcpip/faketime",
- "@com_github_google_go_cmp//cmp:go_default_library",
- ],
-)
diff --git a/pkg/tcpip/network/ip/generic_multicast_protocol.go b/pkg/tcpip/network/ip/generic_multicast_protocol.go
deleted file mode 100644
index b9f129728..000000000
--- a/pkg/tcpip/network/ip/generic_multicast_protocol.go
+++ /dev/null
@@ -1,696 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package ip holds IPv4/IPv6 common utilities.
-package ip
-
-import (
- "fmt"
- "math/rand"
- "time"
-
- "gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/tcpip"
-)
-
-// hostState is the state a host may be in for a multicast group.
-type hostState int
-
-// The states below are generic across IGMPv2 (RFC 2236 section 6) and MLDv1
-// (RFC 2710 section 5). Even though the states are generic across both IGMPv2
-// and MLDv1, IGMPv2 terminology will be used.
-//
-// ______________receive query______________
-// | |
-// | _____send or receive report_____ |
-// | | | |
-// V | V |
-// +-------+ +-----------+ +------------+ +-------------------+ +--------+ |
-// | Non-M | | Pending-M | | Delaying-M | | Queued Delaying-M | | Idle-M | -
-// +-------+ +-----------+ +------------+ +-------------------+ +--------+
-// | ^ | ^ | ^ | ^
-// | | | | | | | |
-// ---------- ------- ---------- -------------
-// initialize new send inital fail to send send or receive
-// group membership report delayed report report
-//
-// Not shown in the diagram above, but any state may transition into the non
-// member state when a group is left.
-const (
- // nonMember is the "'Non-Member' state, when the host does not belong to the
- // group on the interface. This is the initial state for all memberships on
- // all network interfaces; it requires no storage in the host."
- //
- // 'Non-Listener' is the MLDv1 term used to describe this state.
- //
- // This state is used to keep track of groups that have been joined locally,
- // but without advertising the membership to the network.
- nonMember hostState = iota
-
- // pendingMember is a newly joined member that is waiting to successfully send
- // the initial set of reports.
- //
- // This is not an RFC defined state; it is an implementation specific state to
- // track that the initial report needs to be sent.
- //
- // MAY NOT transition to the idle member state from this state.
- pendingMember
-
- // delayingMember is the "'Delaying Member' state, when the host belongs to
- // the group on the interface and has a report delay timer running for that
- // membership."
- //
- // 'Delaying Listener' is the MLDv1 term used to describe this state.
- delayingMember
-
- // queuedDelayingMember is a delayingMember that failed to send a report after
- // its delayed report timer fired. Hosts in this state are waiting to attempt
- // retransmission of the delayed report.
- //
- // This is not an RFC defined state; it is an implementation specific state to
- // track that the delayed report needs to be sent.
- //
- // May transition to idle member if a report is received for a group.
- queuedDelayingMember
-
- // idleMember is the "Idle Member" state, when the host belongs to the group
- // on the interface and does not have a report delay timer running for that
- // membership.
- //
- // 'Idle Listener' is the MLDv1 term used to describe this state.
- idleMember
-)
-
-func (s hostState) isDelayingMember() bool {
- switch s {
- case nonMember, pendingMember, idleMember:
- return false
- case delayingMember, queuedDelayingMember:
- return true
- default:
- panic(fmt.Sprintf("unrecognized host state = %d", s))
- }
-}
-
-// multicastGroupState holds the Generic Multicast Protocol state for a
-// multicast group.
-type multicastGroupState struct {
- // joins is the number of times the group has been joined.
- joins uint64
-
- // state holds the host's state for the group.
- state hostState
-
- // lastToSendReport is true if we sent the last report for the group. It is
- // used to track whether there are other hosts on the subnet that are also
- // members of the group.
- //
- // Defined in RFC 2236 section 6 page 9 for IGMPv2 and RFC 2710 section 5 page
- // 8 for MLDv1.
- lastToSendReport bool
-
- // delayedReportJob is used to delay sending responses to membership report
- // messages in order to reduce duplicate reports from multiple hosts on the
- // interface.
- //
- // Must not be nil.
- delayedReportJob *tcpip.Job
-
- // delyedReportJobFiresAt is the time when the delayed report job will fire.
- //
- // A zero value indicates that the job is not scheduled.
- delayedReportJobFiresAt time.Time
-}
-
-func (m *multicastGroupState) cancelDelayedReportJob() {
- m.delayedReportJob.Cancel()
- m.delayedReportJobFiresAt = time.Time{}
-}
-
-// GenericMulticastProtocolOptions holds options for the generic multicast
-// protocol.
-type GenericMulticastProtocolOptions struct {
- // Rand is the source of random numbers.
- Rand *rand.Rand
-
- // Clock is the clock used to create timers.
- Clock tcpip.Clock
-
- // Protocol is the implementation of the variant of multicast group protocol
- // in use.
- Protocol MulticastGroupProtocol
-
- // MaxUnsolicitedReportDelay is the maximum amount of time to wait between
- // transmitting unsolicited reports.
- //
- // Unsolicited reports are transmitted when a group is newly joined.
- MaxUnsolicitedReportDelay time.Duration
-
- // AllNodesAddress is a multicast address that all nodes on a network should
- // be a member of.
- //
- // This address will not have the generic multicast protocol performed on it;
- // it will be left in the non member/listener state, and packets will never
- // be sent for it.
- AllNodesAddress tcpip.Address
-}
-
-// MulticastGroupProtocol is a multicast group protocol whose core state machine
-// can be represented by GenericMulticastProtocolState.
-type MulticastGroupProtocol interface {
- // Enabled indicates whether the generic multicast protocol will be
- // performed.
- //
- // When enabled, the protocol may transmit report and leave messages when
- // joining and leaving multicast groups respectively, and handle incoming
- // packets.
- //
- // When disabled, the protocol will still keep track of locally joined groups,
- // it just won't transmit and handle packets, or update groups' state.
- Enabled() bool
-
- // SendReport sends a multicast report for the specified group address.
- //
- // Returns false if the caller should queue the report to be sent later. Note,
- // returning false does not mean that the receiver hit an error.
- SendReport(groupAddress tcpip.Address) (sent bool, err tcpip.Error)
-
- // SendLeave sends a multicast leave for the specified group address.
- SendLeave(groupAddress tcpip.Address) tcpip.Error
-}
-
-// GenericMulticastProtocolState is the per interface generic multicast protocol
-// state.
-//
-// There is actually no protocol named "Generic Multicast Protocol". Instead,
-// the term used to refer to a generic multicast protocol that applies to both
-// IPv4 and IPv6. Specifically, Generic Multicast Protocol is the core state
-// machine of IGMPv2 as defined by RFC 2236 and MLDv1 as defined by RFC 2710.
-//
-// Callers must synchronize accesses to the generic multicast protocol state;
-// GenericMulticastProtocolState obtains no locks in any of its methods. The
-// only exception to this is GenericMulticastProtocolState's timer/job callbacks
-// which will obtain the lock provided to the GenericMulticastProtocolState when
-// it is initialized.
-//
-// GenericMulticastProtocolState.Init MUST be called before calling any of
-// the methods on GenericMulticastProtocolState.
-//
-// GenericMulticastProtocolState.MakeAllNonMemberLocked MUST be called when the
-// multicast group protocol is disabled so that leave messages may be sent.
-type GenericMulticastProtocolState struct {
- // Do not allow overwriting this state.
- _ sync.NoCopy
-
- opts GenericMulticastProtocolOptions
-
- // memberships holds group addresses and their associated state.
- memberships map[tcpip.Address]multicastGroupState
-
- // protocolMU is the mutex used to protect the protocol.
- protocolMU *sync.RWMutex
-}
-
-// Init initializes the Generic Multicast Protocol state.
-//
-// Must only be called once for the lifetime of g; Init will panic if it is
-// called twice.
-//
-// The GenericMulticastProtocolState will only grab the lock when timers/jobs
-// fire.
-//
-// Note: the methods on opts.Protocol will always be called while protocolMU is
-// held.
-func (g *GenericMulticastProtocolState) Init(protocolMU *sync.RWMutex, opts GenericMulticastProtocolOptions) {
- if g.memberships != nil {
- panic("attempted to initialize generic membership protocol state twice")
- }
-
- *g = GenericMulticastProtocolState{
- opts: opts,
- memberships: make(map[tcpip.Address]multicastGroupState),
- protocolMU: protocolMU,
- }
-}
-
-// MakeAllNonMemberLocked transitions all groups to the non-member state.
-//
-// The groups will still be considered joined locally.
-//
-// MUST be called when the multicast group protocol is disabled.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) MakeAllNonMemberLocked() {
- if !g.opts.Protocol.Enabled() {
- return
- }
-
- for groupAddress, info := range g.memberships {
- g.transitionToNonMemberLocked(groupAddress, &info)
- g.memberships[groupAddress] = info
- }
-}
-
-// InitializeGroupsLocked initializes each group, as if they were newly joined
-// but without affecting the groups' join count.
-//
-// Must only be called after calling MakeAllNonMember as a group should not be
-// initialized while it is not in the non-member state.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) InitializeGroupsLocked() {
- if !g.opts.Protocol.Enabled() {
- return
- }
-
- for groupAddress, info := range g.memberships {
- g.initializeNewMemberLocked(groupAddress, &info)
- g.memberships[groupAddress] = info
- }
-}
-
-// SendQueuedReportsLocked attempts to send reports for groups that failed to
-// send reports during their last attempt.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) SendQueuedReportsLocked() {
- for groupAddress, info := range g.memberships {
- switch info.state {
- case nonMember, delayingMember, idleMember:
- case pendingMember:
- // pendingMembers failed to send their initial unsolicited report so try
- // to send the report and queue the extra unsolicited reports.
- g.maybeSendInitialReportLocked(groupAddress, &info)
- case queuedDelayingMember:
- // queuedDelayingMembers failed to send their delayed reports so try to
- // send the report and transition them to the idle state.
- g.maybeSendDelayedReportLocked(groupAddress, &info)
- default:
- panic(fmt.Sprintf("unrecognized host state = %d", info.state))
- }
- g.memberships[groupAddress] = info
- }
-}
-
-// JoinGroupLocked handles joining a new group.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) JoinGroupLocked(groupAddress tcpip.Address) {
- if info, ok := g.memberships[groupAddress]; ok {
- // The group has already been joined.
- info.joins++
- g.memberships[groupAddress] = info
- return
- }
-
- info := multicastGroupState{
- // Since we just joined the group, its count is 1.
- joins: 1,
- // The state will be updated below, if required.
- state: nonMember,
- lastToSendReport: false,
- delayedReportJob: tcpip.NewJob(g.opts.Clock, g.protocolMU, func() {
- if !g.opts.Protocol.Enabled() {
- panic(fmt.Sprintf("delayed report job fired for group %s while the multicast group protocol is disabled", groupAddress))
- }
-
- info, ok := g.memberships[groupAddress]
- if !ok {
- panic(fmt.Sprintf("expected to find group state for group = %s", groupAddress))
- }
-
- g.maybeSendDelayedReportLocked(groupAddress, &info)
- g.memberships[groupAddress] = info
- }),
- }
-
- if g.opts.Protocol.Enabled() {
- g.initializeNewMemberLocked(groupAddress, &info)
- }
-
- g.memberships[groupAddress] = info
-}
-
-// IsLocallyJoinedRLocked returns true if the group is locally joined.
-//
-// Precondition: g.protocolMU must be read locked.
-func (g *GenericMulticastProtocolState) IsLocallyJoinedRLocked(groupAddress tcpip.Address) bool {
- _, ok := g.memberships[groupAddress]
- return ok
-}
-
-// LeaveGroupLocked handles leaving the group.
-//
-// Returns false if the group is not currently joined.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) LeaveGroupLocked(groupAddress tcpip.Address) bool {
- info, ok := g.memberships[groupAddress]
- if !ok {
- return false
- }
-
- if info.joins == 0 {
- panic(fmt.Sprintf("tried to leave group %s with a join count of 0", groupAddress))
- }
- info.joins--
- if info.joins != 0 {
- // If we still have outstanding joins, then do nothing further.
- g.memberships[groupAddress] = info
- return true
- }
-
- g.transitionToNonMemberLocked(groupAddress, &info)
- delete(g.memberships, groupAddress)
- return true
-}
-
-// HandleQueryLocked handles a query message with the specified maximum response
-// time.
-//
-// If the group address is unspecified, then reports will be scheduled for all
-// joined groups.
-//
-// Report(s) will be scheduled to be sent after a random duration between 0 and
-// the maximum response time.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) HandleQueryLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) {
- if !g.opts.Protocol.Enabled() {
- return
- }
-
- // As per RFC 2236 section 2.4 (for IGMPv2),
- //
- // In a Membership Query message, the group address field is set to zero
- // when sending a General Query, and set to the group address being
- // queried when sending a Group-Specific Query.
- //
- // As per RFC 2710 section 3.6 (for MLDv1),
- //
- // In a Query message, the Multicast Address field is set to zero when
- // sending a General Query, and set to a specific IPv6 multicast address
- // when sending a Multicast-Address-Specific Query.
- if groupAddress.Unspecified() {
- // This is a general query as the group address is unspecified.
- for groupAddress, info := range g.memberships {
- g.setDelayTimerForAddressRLocked(groupAddress, &info, maxResponseTime)
- g.memberships[groupAddress] = info
- }
- } else if info, ok := g.memberships[groupAddress]; ok {
- g.setDelayTimerForAddressRLocked(groupAddress, &info, maxResponseTime)
- g.memberships[groupAddress] = info
- }
-}
-
-// HandleReportLocked handles a report message.
-//
-// If the report is for a joined group, any active delayed report will be
-// cancelled and the host state for the group transitions to idle.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) HandleReportLocked(groupAddress tcpip.Address) {
- if !g.opts.Protocol.Enabled() {
- return
- }
-
- // As per RFC 2236 section 3 pages 3-4 (for IGMPv2),
- //
- // If the host receives another host's Report (version 1 or 2) while it has
- // a timer running, it stops its timer for the specified group and does not
- // send a Report
- //
- // As per RFC 2710 section 4 page 6 (for MLDv1),
- //
- // If a node receives another node's Report from an interface for a
- // multicast address while it has a timer running for that same address
- // on that interface, it stops its timer and does not send a Report for
- // that address, thus suppressing duplicate reports on the link.
- if info, ok := g.memberships[groupAddress]; ok && info.state.isDelayingMember() {
- info.cancelDelayedReportJob()
- info.lastToSendReport = false
- info.state = idleMember
- g.memberships[groupAddress] = info
- }
-}
-
-// initializeNewMemberLocked initializes a new group membership.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) initializeNewMemberLocked(groupAddress tcpip.Address, info *multicastGroupState) {
- if info.state != nonMember {
- panic(fmt.Sprintf("host must be in non-member state to be initialized; group = %s, state = %d", groupAddress, info.state))
- }
-
- info.lastToSendReport = false
-
- if groupAddress == g.opts.AllNodesAddress {
- // As per RFC 2236 section 6 page 10 (for IGMPv2),
- //
- // 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.
- //
- // As per RFC 2710 section 5 page 10 (for MLDv1),
- //
- // 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.
- info.state = idleMember
- return
- }
-
- info.state = pendingMember
- g.maybeSendInitialReportLocked(groupAddress, info)
-}
-
-// maybeSendInitialReportLocked attempts to start transmission of the initial
-// set of reports after newly joining a group.
-//
-// Host must be in pending member state.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) maybeSendInitialReportLocked(groupAddress tcpip.Address, info *multicastGroupState) {
- if info.state != pendingMember {
- panic(fmt.Sprintf("host must be in pending member state to send initial reports; group = %s, state = %d", groupAddress, info.state))
- }
-
- // As per RFC 2236 section 3 page 5 (for IGMPv2),
- //
- // When a host joins a multicast group, it should immediately transmit an
- // unsolicited Version 2 Membership Report for that group" ... "it is
- // recommended that it be repeated".
- //
- // As per RFC 2710 section 4 page 6 (for MLDv1),
- //
- // When a node starts listening to a multicast address on an interface,
- // it should immediately transmit an unsolicited Report for that address
- // on that interface, in case it is the first listener on the link. To
- // cover the possibility of the initial Report being lost or damaged, it
- // is recommended that it be repeated once or twice after short delays
- // [Unsolicited Report Interval].
- //
- // TODO(gvisor.dev/issue/4901): Support a configurable number of initial
- // unsolicited reports.
- sent, err := g.opts.Protocol.SendReport(groupAddress)
- if err == nil && sent {
- info.lastToSendReport = true
- g.setDelayTimerForAddressRLocked(groupAddress, info, g.opts.MaxUnsolicitedReportDelay)
- }
-}
-
-// maybeSendDelayedReportLocked attempts to send the delayed report.
-//
-// Host must be in pending, delaying or queued delaying member state.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) maybeSendDelayedReportLocked(groupAddress tcpip.Address, info *multicastGroupState) {
- if !info.state.isDelayingMember() {
- panic(fmt.Sprintf("host must be in delaying or queued delaying member state to send delayed reports; group = %s, state = %d", groupAddress, info.state))
- }
-
- sent, err := g.opts.Protocol.SendReport(groupAddress)
- if err == nil && sent {
- info.lastToSendReport = true
- info.state = idleMember
- } else {
- info.state = queuedDelayingMember
- }
-}
-
-// maybeSendLeave attempts to send a leave message.
-func (g *GenericMulticastProtocolState) maybeSendLeave(groupAddress tcpip.Address, lastToSendReport bool) {
- if !g.opts.Protocol.Enabled() || !lastToSendReport {
- return
- }
-
- if groupAddress == g.opts.AllNodesAddress {
- // As per RFC 2236 section 6 page 10 (for IGMPv2),
- //
- // 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.
- //
- // As per RFC 2710 section 5 page 10 (for MLDv1),
- //
- // 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.
- return
- }
-
- // Okay to ignore the error here as if packet write failed, the multicast
- // routers will eventually drop our membership anyways. If the interface is
- // being disabled or removed, the generic multicast protocol's should be
- // cleared eventually.
- //
- // As per RFC 2236 section 3 page 5 (for IGMPv2),
- //
- // When a router receives a Report, it adds the group being reported to
- // the list of multicast group memberships on the network on which it
- // received the Report and sets the timer for the membership to the
- // [Group Membership Interval]. Repeated Reports refresh the timer. If
- // no Reports are received for a particular group before this timer has
- // expired, the router assumes that the group has no local members and
- // that it need not forward remotely-originated multicasts for that
- // group onto the attached network.
- //
- // As per RFC 2710 section 4 page 5 (for MLDv1),
- //
- // When a router receives a Report from a link, if the reported address
- // is not already present in the router's list of multicast address
- // having listeners on that link, the reported address is added to the
- // list, its timer is set to [Multicast Listener Interval], and its
- // appearance is made known to the router's multicast routing component.
- // If a Report is received for a multicast address that is already
- // present in the router's list, the timer for that address is reset to
- // [Multicast Listener Interval]. If an address's timer expires, it is
- // assumed that there are no longer any listeners for that address
- // present on the link, so it is deleted from the list and its
- // disappearance is made known to the multicast routing component.
- //
- // The requirement to send a leave message is also optional (it MAY be
- // skipped):
- //
- // As per RFC 2236 section 6 page 8 (for IGMPv2),
- //
- // "send leave" for the group on the interface. If the interface
- // state says the Querier is running IGMPv1, this action SHOULD be
- // skipped. If the flag saying we were the last host to report is
- // cleared, this action MAY be skipped. The Leave Message is sent to
- // the ALL-ROUTERS group (224.0.0.2).
- //
- // As per RFC 2710 section 5 page 8 (for MLDv1),
- //
- // "send done" for the address on the interface. If the flag saying
- // we were the last node to report is cleared, this action MAY be
- // skipped. The Done message is sent to the link-scope all-routers
- // address (FF02::2).
- _ = g.opts.Protocol.SendLeave(groupAddress)
-}
-
-// transitionToNonMemberLocked transitions the given multicast group the the
-// non-member/listener state.
-//
-// Precondition: g.protocolMU must be locked.
-func (g *GenericMulticastProtocolState) transitionToNonMemberLocked(groupAddress tcpip.Address, info *multicastGroupState) {
- if info.state == nonMember {
- return
- }
-
- info.cancelDelayedReportJob()
- g.maybeSendLeave(groupAddress, info.lastToSendReport)
- info.lastToSendReport = false
- info.state = nonMember
-}
-
-// setDelayTimerForAddressRLocked sets timer to send a delay report.
-//
-// Precondition: g.protocolMU MUST be read locked.
-func (g *GenericMulticastProtocolState) setDelayTimerForAddressRLocked(groupAddress tcpip.Address, info *multicastGroupState, maxResponseTime time.Duration) {
- if info.state == nonMember {
- return
- }
-
- if groupAddress == g.opts.AllNodesAddress {
- // As per RFC 2236 section 6 page 10 (for IGMPv2),
- //
- // 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.
- //
- // As per RFC 2710 section 5 page 10 (for MLDv1),
- //
- // 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.
- return
- }
-
- // As per RFC 2236 section 3 page 3 (for IGMPv2),
- //
- // If a timer for the group is already unning, it is reset to the random
- // value only if the requested Max Response Time is less than the remaining
- // value of the running timer.
- //
- // As per RFC 2710 section 4 page 5 (for MLDv1),
- //
- // If a timer for any address is already running, it is reset to the new
- // random value only if the requested Maximum Response Delay is less than
- // the remaining value of the running timer.
- now := time.Unix(0 /* seconds */, g.opts.Clock.NowNanoseconds())
- if info.state == delayingMember {
- if info.delayedReportJobFiresAt.IsZero() {
- panic(fmt.Sprintf("delayed report unscheduled while in the delaying member state; group = %s", groupAddress))
- }
-
- if info.delayedReportJobFiresAt.Sub(now) <= maxResponseTime {
- // The timer is scheduled to fire before the maximum response time so we
- // leave our timer as is.
- return
- }
- }
-
- info.state = delayingMember
- info.cancelDelayedReportJob()
- maxResponseTime = g.calculateDelayTimerDuration(maxResponseTime)
- info.delayedReportJob.Schedule(maxResponseTime)
- info.delayedReportJobFiresAt = now.Add(maxResponseTime)
-}
-
-// calculateDelayTimerDuration returns a random time between (0, maxRespTime].
-func (g *GenericMulticastProtocolState) calculateDelayTimerDuration(maxRespTime time.Duration) time.Duration {
- // As per RFC 2236 section 3 page 3 (for IGMPv2),
- //
- // When a host receives a Group-Specific Query, it sets a delay timer to a
- // random value selected from the range (0, Max Response Time]...
- //
- // As per RFC 2710 section 4 page 6 (for MLDv1),
- //
- // When a node receives a Multicast-Address-Specific Query, if it is
- // listening to the queried Multicast Address on the interface from
- // which the Query was received, it sets a delay timer for that address
- // to a random value selected from the range [0, Maximum Response Delay],
- // as above.
- if maxRespTime == 0 {
- return 0
- }
- return time.Duration(g.opts.Rand.Int63n(int64(maxRespTime)))
-}
diff --git a/pkg/tcpip/network/ip/generic_multicast_protocol_test.go b/pkg/tcpip/network/ip/generic_multicast_protocol_test.go
deleted file mode 100644
index 60eaea37e..000000000
--- a/pkg/tcpip/network/ip/generic_multicast_protocol_test.go
+++ /dev/null
@@ -1,812 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package ip_test
-
-import (
- "math/rand"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- "gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/faketime"
- "gvisor.dev/gvisor/pkg/tcpip/network/ip"
-)
-
-const (
- addr1 = tcpip.Address("\x01")
- addr2 = tcpip.Address("\x02")
- addr3 = tcpip.Address("\x03")
- addr4 = tcpip.Address("\x04")
-
- maxUnsolicitedReportDelay = time.Second
-)
-
-var _ ip.MulticastGroupProtocol = (*mockMulticastGroupProtocol)(nil)
-
-type mockMulticastGroupProtocolProtectedFields struct {
- sync.RWMutex
-
- genericMulticastGroup ip.GenericMulticastProtocolState
- sendReportGroupAddrCount map[tcpip.Address]int
- sendLeaveGroupAddrCount map[tcpip.Address]int
- makeQueuePackets bool
- disabled bool
-}
-
-type mockMulticastGroupProtocol struct {
- t *testing.T
-
- mu mockMulticastGroupProtocolProtectedFields
-}
-
-func (m *mockMulticastGroupProtocol) init(opts ip.GenericMulticastProtocolOptions) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.initLocked()
- opts.Protocol = m
- m.mu.genericMulticastGroup.Init(&m.mu.RWMutex, opts)
-}
-
-func (m *mockMulticastGroupProtocol) initLocked() {
- m.mu.sendReportGroupAddrCount = make(map[tcpip.Address]int)
- m.mu.sendLeaveGroupAddrCount = make(map[tcpip.Address]int)
-}
-
-func (m *mockMulticastGroupProtocol) setEnabled(v bool) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.mu.disabled = !v
-}
-
-func (m *mockMulticastGroupProtocol) setQueuePackets(v bool) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.mu.makeQueuePackets = v
-}
-
-func (m *mockMulticastGroupProtocol) joinGroup(addr tcpip.Address) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.mu.genericMulticastGroup.JoinGroupLocked(addr)
-}
-
-func (m *mockMulticastGroupProtocol) leaveGroup(addr tcpip.Address) bool {
- m.mu.Lock()
- defer m.mu.Unlock()
- return m.mu.genericMulticastGroup.LeaveGroupLocked(addr)
-}
-
-func (m *mockMulticastGroupProtocol) handleReport(addr tcpip.Address) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.mu.genericMulticastGroup.HandleReportLocked(addr)
-}
-
-func (m *mockMulticastGroupProtocol) handleQuery(addr tcpip.Address, maxRespTime time.Duration) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.mu.genericMulticastGroup.HandleQueryLocked(addr, maxRespTime)
-}
-
-func (m *mockMulticastGroupProtocol) isLocallyJoined(addr tcpip.Address) bool {
- m.mu.RLock()
- defer m.mu.RUnlock()
- return m.mu.genericMulticastGroup.IsLocallyJoinedRLocked(addr)
-}
-
-func (m *mockMulticastGroupProtocol) makeAllNonMember() {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.mu.genericMulticastGroup.MakeAllNonMemberLocked()
-}
-
-func (m *mockMulticastGroupProtocol) initializeGroups() {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.mu.genericMulticastGroup.InitializeGroupsLocked()
-}
-
-func (m *mockMulticastGroupProtocol) sendQueuedReports() {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.mu.genericMulticastGroup.SendQueuedReportsLocked()
-}
-
-// Enabled implements ip.MulticastGroupProtocol.
-//
-// Precondition: m.mu must be read locked.
-func (m *mockMulticastGroupProtocol) Enabled() bool {
- if m.mu.TryLock() {
- m.mu.Unlock()
- m.t.Fatal("got write lock, expected to not take the lock; generic multicast protocol must take the read or write lock before calling Enabled")
- }
-
- return !m.mu.disabled
-}
-
-// SendReport implements ip.MulticastGroupProtocol.
-//
-// Precondition: m.mu must be locked.
-func (m *mockMulticastGroupProtocol) SendReport(groupAddress tcpip.Address) (bool, tcpip.Error) {
- if m.mu.TryLock() {
- m.mu.Unlock()
- m.t.Fatalf("got write lock, expected to not take the lock; generic multicast protocol must take the write lock before sending report for %s", groupAddress)
- }
- if m.mu.TryRLock() {
- m.mu.RUnlock()
- m.t.Fatalf("got read lock, expected to not take the lock; generic multicast protocol must take the write lock before sending report for %s", groupAddress)
- }
-
- m.mu.sendReportGroupAddrCount[groupAddress]++
- return !m.mu.makeQueuePackets, nil
-}
-
-// SendLeave implements ip.MulticastGroupProtocol.
-//
-// Precondition: m.mu must be locked.
-func (m *mockMulticastGroupProtocol) SendLeave(groupAddress tcpip.Address) tcpip.Error {
- if m.mu.TryLock() {
- m.mu.Unlock()
- m.t.Fatalf("got write lock, expected to not take the lock; generic multicast protocol must take the write lock before sending leave for %s", groupAddress)
- }
- if m.mu.TryRLock() {
- m.mu.RUnlock()
- m.t.Fatalf("got read lock, expected to not take the lock; generic multicast protocol must take the write lock before sending leave for %s", groupAddress)
- }
-
- m.mu.sendLeaveGroupAddrCount[groupAddress]++
- return nil
-}
-
-func (m *mockMulticastGroupProtocol) check(sendReportGroupAddresses []tcpip.Address, sendLeaveGroupAddresses []tcpip.Address) string {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- sendReportGroupAddrCount := make(map[tcpip.Address]int)
- for _, a := range sendReportGroupAddresses {
- sendReportGroupAddrCount[a] = 1
- }
-
- sendLeaveGroupAddrCount := make(map[tcpip.Address]int)
- for _, a := range sendLeaveGroupAddresses {
- sendLeaveGroupAddrCount[a] = 1
- }
-
- diff := cmp.Diff(
- &mockMulticastGroupProtocol{
- mu: mockMulticastGroupProtocolProtectedFields{
- sendReportGroupAddrCount: sendReportGroupAddrCount,
- sendLeaveGroupAddrCount: sendLeaveGroupAddrCount,
- },
- },
- m,
- cmp.AllowUnexported(mockMulticastGroupProtocol{}),
- cmp.AllowUnexported(mockMulticastGroupProtocolProtectedFields{}),
- // ignore mockMulticastGroupProtocol.mu and mockMulticastGroupProtocol.t
- cmp.FilterPath(
- func(p cmp.Path) bool {
- switch p.Last().String() {
- case ".RWMutex", ".t", ".makeQueuePackets", ".disabled", ".genericMulticastGroup":
- return true
- }
- return false
- },
- cmp.Ignore(),
- ),
- )
- m.initLocked()
- return diff
-}
-
-func TestJoinGroup(t *testing.T) {
- tests := []struct {
- name string
- addr tcpip.Address
- shouldSendReports bool
- }{
- {
- name: "Normal group",
- addr: addr1,
- shouldSendReports: true,
- },
- {
- name: "All-nodes group",
- addr: addr2,
- shouldSendReports: false,
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- mgp := mockMulticastGroupProtocol{t: t}
- clock := faketime.NewManualClock()
-
- mgp.init(ip.GenericMulticastProtocolOptions{
- Rand: rand.New(rand.NewSource(0)),
- Clock: clock,
- MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
- AllNodesAddress: addr2,
- })
-
- // Joining a group should send a report immediately and another after
- // a random interval between 0 and the maximum unsolicited report delay.
- mgp.joinGroup(test.addr)
- if test.shouldSendReports {
- if diff := mgp.check([]tcpip.Address{test.addr} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Generic multicast protocol timers are expected to take the job mutex.
- clock.Advance(maxUnsolicitedReportDelay)
- if diff := mgp.check([]tcpip.Address{test.addr} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- }
-
- // Should have no more messages to send.
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- })
- }
-}
-
-func TestLeaveGroup(t *testing.T) {
- tests := []struct {
- name string
- addr tcpip.Address
- shouldSendMessages bool
- }{
- {
- name: "Normal group",
- addr: addr1,
- shouldSendMessages: true,
- },
- {
- name: "All-nodes group",
- addr: addr2,
- shouldSendMessages: false,
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- mgp := mockMulticastGroupProtocol{t: t}
- clock := faketime.NewManualClock()
-
- mgp.init(ip.GenericMulticastProtocolOptions{
- Rand: rand.New(rand.NewSource(1)),
- Clock: clock,
- MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
- AllNodesAddress: addr2,
- })
-
- mgp.joinGroup(test.addr)
- if test.shouldSendMessages {
- if diff := mgp.check([]tcpip.Address{test.addr} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- }
-
- // Leaving a group should send a leave report immediately and cancel any
- // delayed reports.
- {
-
- if !mgp.leaveGroup(test.addr) {
- t.Fatalf("got mgp.leaveGroup(%s) = false, want = true", test.addr)
- }
- }
- if test.shouldSendMessages {
- if diff := mgp.check(nil /* sendReportGroupAddresses */, []tcpip.Address{test.addr} /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- }
-
- // Should have no more messages to send.
- //
- // Generic multicast protocol timers are expected to take the job mutex.
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- })
- }
-}
-
-func TestHandleReport(t *testing.T) {
- tests := []struct {
- name string
- reportAddr tcpip.Address
- expectReportsFor []tcpip.Address
- }{
- {
- name: "Unpecified empty",
- reportAddr: "",
- expectReportsFor: []tcpip.Address{addr1, addr2},
- },
- {
- name: "Unpecified any",
- reportAddr: "\x00",
- expectReportsFor: []tcpip.Address{addr1, addr2},
- },
- {
- name: "Specified",
- reportAddr: addr1,
- expectReportsFor: []tcpip.Address{addr2},
- },
- {
- name: "Specified all-nodes",
- reportAddr: addr3,
- expectReportsFor: []tcpip.Address{addr1, addr2},
- },
- {
- name: "Specified other",
- reportAddr: addr4,
- expectReportsFor: []tcpip.Address{addr1, addr2},
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- mgp := mockMulticastGroupProtocol{t: t}
- clock := faketime.NewManualClock()
-
- mgp.init(ip.GenericMulticastProtocolOptions{
- Rand: rand.New(rand.NewSource(2)),
- Clock: clock,
- MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
- AllNodesAddress: addr3,
- })
-
- mgp.joinGroup(addr1)
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.joinGroup(addr2)
- if diff := mgp.check([]tcpip.Address{addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.joinGroup(addr3)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Receiving a report for a group we have a timer scheduled for should
- // cancel our delayed report timer for the group.
- mgp.handleReport(test.reportAddr)
- if len(test.expectReportsFor) != 0 {
- // Generic multicast protocol timers are expected to take the job mutex.
- clock.Advance(maxUnsolicitedReportDelay)
- if diff := mgp.check(test.expectReportsFor /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- }
-
- // Should have no more messages to send.
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- })
- }
-}
-
-func TestHandleQuery(t *testing.T) {
- tests := []struct {
- name string
- queryAddr tcpip.Address
- maxDelay time.Duration
- expectQueriedReportsFor []tcpip.Address
- expectDelayedReportsFor []tcpip.Address
- }{
- {
- name: "Unpecified empty",
- queryAddr: "",
- maxDelay: 0,
- expectQueriedReportsFor: []tcpip.Address{addr1, addr2},
- expectDelayedReportsFor: nil,
- },
- {
- name: "Unpecified any",
- queryAddr: "\x00",
- maxDelay: 1,
- expectQueriedReportsFor: []tcpip.Address{addr1, addr2},
- expectDelayedReportsFor: nil,
- },
- {
- name: "Specified",
- queryAddr: addr1,
- maxDelay: 2,
- expectQueriedReportsFor: []tcpip.Address{addr1},
- expectDelayedReportsFor: []tcpip.Address{addr2},
- },
- {
- name: "Specified all-nodes",
- queryAddr: addr3,
- maxDelay: 3,
- expectQueriedReportsFor: nil,
- expectDelayedReportsFor: []tcpip.Address{addr1, addr2},
- },
- {
- name: "Specified other",
- queryAddr: addr4,
- maxDelay: 4,
- expectQueriedReportsFor: nil,
- expectDelayedReportsFor: []tcpip.Address{addr1, addr2},
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- mgp := mockMulticastGroupProtocol{t: t}
- clock := faketime.NewManualClock()
-
- mgp.init(ip.GenericMulticastProtocolOptions{
- Rand: rand.New(rand.NewSource(3)),
- Clock: clock,
- MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
- AllNodesAddress: addr3,
- })
-
- mgp.joinGroup(addr1)
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.joinGroup(addr2)
- if diff := mgp.check([]tcpip.Address{addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.joinGroup(addr3)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Receiving a query should make us reschedule our delayed report timer
- // to some time within the new max response delay.
- mgp.handleQuery(test.queryAddr, test.maxDelay)
- clock.Advance(test.maxDelay)
- if diff := mgp.check(test.expectQueriedReportsFor /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // The groups that were not affected by the query should still send a
- // report after the max unsolicited report delay.
- clock.Advance(maxUnsolicitedReportDelay)
- if diff := mgp.check(test.expectDelayedReportsFor /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Should have no more messages to send.
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- })
- }
-}
-
-func TestJoinCount(t *testing.T) {
- mgp := mockMulticastGroupProtocol{t: t}
- clock := faketime.NewManualClock()
-
- mgp.init(ip.GenericMulticastProtocolOptions{
- Rand: rand.New(rand.NewSource(4)),
- Clock: clock,
- MaxUnsolicitedReportDelay: time.Second,
- })
-
- // Set the join count to 2 for a group.
- mgp.joinGroup(addr1)
- if !mgp.isLocallyJoined(addr1) {
- t.Fatalf("got mgp.isLocallyJoined(%s) = false, want = true", addr1)
- }
- // Only the first join should trigger a report to be sent.
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.joinGroup(addr1)
- if !mgp.isLocallyJoined(addr1) {
- t.Errorf("got mgp.isLocallyJoined(%s) = false, want = true", addr1)
- }
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- if t.Failed() {
- t.FailNow()
- }
-
- // Group should still be considered joined after leaving once.
- if !mgp.leaveGroup(addr1) {
- t.Errorf("got mgp.leaveGroup(%s) = false, want = true", addr1)
- }
- if !mgp.isLocallyJoined(addr1) {
- t.Errorf("got mgp.isLocallyJoined(%s) = false, want = true", addr1)
- }
- // A leave report should only be sent once the join count reaches 0.
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- if t.Failed() {
- t.FailNow()
- }
-
- // Leaving once more should actually remove us from the group.
- if !mgp.leaveGroup(addr1) {
- t.Errorf("got mgp.leaveGroup(%s) = false, want = true", addr1)
- }
- if mgp.isLocallyJoined(addr1) {
- t.Errorf("got mgp.isLocallyJoined(%s) = true, want = false", addr1)
- }
- if diff := mgp.check(nil /* sendReportGroupAddresses */, []tcpip.Address{addr1} /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- if t.Failed() {
- t.FailNow()
- }
-
- // Group should no longer be joined so we should not have anything to
- // leave.
- if mgp.leaveGroup(addr1) {
- t.Errorf("got mgp.leaveGroup(%s) = true, want = false", addr1)
- }
- if mgp.isLocallyJoined(addr1) {
- t.Errorf("got mgp.isLocallyJoined(%s) = true, want = false", addr1)
- }
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Should have no more messages to send.
- //
- // Generic multicast protocol timers are expected to take the job mutex.
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-}
-
-func TestMakeAllNonMemberAndInitialize(t *testing.T) {
- mgp := mockMulticastGroupProtocol{t: t}
- clock := faketime.NewManualClock()
-
- mgp.init(ip.GenericMulticastProtocolOptions{
- Rand: rand.New(rand.NewSource(3)),
- Clock: clock,
- MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
- AllNodesAddress: addr3,
- })
-
- mgp.joinGroup(addr1)
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.joinGroup(addr2)
- if diff := mgp.check([]tcpip.Address{addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.joinGroup(addr3)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Should send the leave reports for each but still consider them locally
- // joined.
- mgp.makeAllNonMember()
- if diff := mgp.check(nil /* sendReportGroupAddresses */, []tcpip.Address{addr1, addr2} /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- // Generic multicast protocol timers are expected to take the job mutex.
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- for _, group := range []tcpip.Address{addr1, addr2, addr3} {
- if !mgp.isLocallyJoined(group) {
- t.Fatalf("got mgp.isLocallyJoined(%s) = false, want = true", group)
- }
- }
-
- // Should send the initial set of unsolcited reports.
- mgp.initializeGroups()
- if diff := mgp.check([]tcpip.Address{addr1, addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- clock.Advance(maxUnsolicitedReportDelay)
- if diff := mgp.check([]tcpip.Address{addr1, addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Should have no more messages to send.
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-}
-
-// TestGroupStateNonMember tests that groups do not send packets when in the
-// non-member state, but are still considered locally joined.
-func TestGroupStateNonMember(t *testing.T) {
- mgp := mockMulticastGroupProtocol{t: t}
- clock := faketime.NewManualClock()
-
- mgp.init(ip.GenericMulticastProtocolOptions{
- Rand: rand.New(rand.NewSource(3)),
- Clock: clock,
- MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
- })
- mgp.setEnabled(false)
-
- // Joining groups should not send any reports.
- mgp.joinGroup(addr1)
- if !mgp.isLocallyJoined(addr1) {
- t.Fatalf("got mgp.isLocallyJoined(%s) = false, want = true", addr1)
- }
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.joinGroup(addr2)
- if !mgp.isLocallyJoined(addr1) {
- t.Fatalf("got mgp.isLocallyJoined(%s) = false, want = true", addr2)
- }
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Receiving a query should not send any reports.
- mgp.handleQuery(addr1, time.Nanosecond)
- // Generic multicast protocol timers are expected to take the job mutex.
- clock.Advance(time.Nanosecond)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Leaving groups should not send any leave messages.
- if !mgp.leaveGroup(addr1) {
- t.Errorf("got mgp.leaveGroup(%s) = false, want = true", addr2)
- }
- if mgp.isLocallyJoined(addr1) {
- t.Errorf("got mgp.isLocallyJoined(%s) = true, want = false", addr2)
- }
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-}
-
-func TestQueuedPackets(t *testing.T) {
- clock := faketime.NewManualClock()
- mgp := mockMulticastGroupProtocol{t: t}
- mgp.init(ip.GenericMulticastProtocolOptions{
- Rand: rand.New(rand.NewSource(4)),
- Clock: clock,
- MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay,
- })
-
- // Joining should trigger a SendReport, but mgp should report that we did not
- // send the packet.
- mgp.setQueuePackets(true)
- mgp.joinGroup(addr1)
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // The delayed report timer should have been cancelled since we did not send
- // the initial report earlier.
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Mock being able to successfully send the report.
- mgp.setQueuePackets(false)
- mgp.sendQueuedReports()
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // The delayed report (sent after the initial report) should now be sent.
- clock.Advance(maxUnsolicitedReportDelay)
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Should not have anything else to send (we should be idle).
- mgp.sendQueuedReports()
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Receive a query but mock being unable to send reports again.
- mgp.setQueuePackets(true)
- mgp.handleQuery(addr1, time.Nanosecond)
- clock.Advance(time.Nanosecond)
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Mock being able to send reports again - we should have a packet queued to
- // send.
- mgp.setQueuePackets(false)
- mgp.sendQueuedReports()
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Should not have anything else to send.
- mgp.sendQueuedReports()
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Receive a query again, but mock being unable to send reports.
- mgp.setQueuePackets(true)
- mgp.handleQuery(addr1, time.Nanosecond)
- clock.Advance(time.Nanosecond)
- if diff := mgp.check([]tcpip.Address{addr1} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Receiving a report should should transition us into the idle member state,
- // even if we had a packet queued. We should no longer have any packets to
- // send.
- mgp.handleReport(addr1)
- mgp.sendQueuedReports()
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // When we fail to send the initial set of reports, incoming reports should
- // not affect a newly joined group's reports from being sent.
- mgp.setQueuePackets(true)
- mgp.joinGroup(addr2)
- if diff := mgp.check([]tcpip.Address{addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- mgp.handleReport(addr2)
- // Attempting to send queued reports while still unable to send reports should
- // not change the host state.
- mgp.sendQueuedReports()
- if diff := mgp.check([]tcpip.Address{addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- // Mock being able to successfully send the report.
- mgp.setQueuePackets(false)
- mgp.sendQueuedReports()
- if diff := mgp.check([]tcpip.Address{addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
- // The delayed report (sent after the initial report) should now be sent.
- clock.Advance(maxUnsolicitedReportDelay)
- if diff := mgp.check([]tcpip.Address{addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-
- // Should not have anything else to send.
- mgp.sendQueuedReports()
- clock.Advance(time.Hour)
- if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
- t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
- }
-}
diff --git a/pkg/tcpip/network/ip/stats.go b/pkg/tcpip/network/ip/stats.go
deleted file mode 100644
index 898f8b356..000000000
--- a/pkg/tcpip/network/ip/stats.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package ip
-
-import "gvisor.dev/gvisor/pkg/tcpip"
-
-// LINT.IfChange(MultiCounterIPStats)
-
-// MultiCounterIPStats holds IP statistics, each counter may have several
-// versions.
-type MultiCounterIPStats struct {
- // PacketsReceived is the total number of IP packets received from the link
- // layer.
- PacketsReceived tcpip.MultiCounterStat
-
- // DisabledPacketsReceived is the total number of IP packets received from the
- // link layer when the IP layer is disabled.
- DisabledPacketsReceived tcpip.MultiCounterStat
-
- // InvalidDestinationAddressesReceived is the total number of IP packets
- // received with an unknown or invalid destination address.
- InvalidDestinationAddressesReceived tcpip.MultiCounterStat
-
- // InvalidSourceAddressesReceived is the total number of IP packets received
- // with a source address that should never have been received on the wire.
- InvalidSourceAddressesReceived tcpip.MultiCounterStat
-
- // PacketsDelivered is the total number of incoming IP packets that are
- // successfully delivered to the transport layer.
- PacketsDelivered tcpip.MultiCounterStat
-
- // PacketsSent is the total number of IP packets sent via WritePacket.
- PacketsSent tcpip.MultiCounterStat
-
- // OutgoingPacketErrors is the total number of IP packets which failed to
- // write to a link-layer endpoint.
- OutgoingPacketErrors tcpip.MultiCounterStat
-
- // MalformedPacketsReceived is the total number of IP Packets that were
- // dropped due to the IP packet header failing validation checks.
- MalformedPacketsReceived tcpip.MultiCounterStat
-
- // MalformedFragmentsReceived is the total number of IP Fragments that were
- // dropped due to the fragment failing validation checks.
- MalformedFragmentsReceived tcpip.MultiCounterStat
-
- // IPTablesPreroutingDropped is the total number of IP packets dropped in the
- // Prerouting chain.
- IPTablesPreroutingDropped tcpip.MultiCounterStat
-
- // IPTablesInputDropped is the total number of IP packets dropped in the Input
- // chain.
- IPTablesInputDropped tcpip.MultiCounterStat
-
- // IPTablesOutputDropped is the total number of IP packets dropped in the
- // Output chain.
- IPTablesOutputDropped tcpip.MultiCounterStat
-
- // OptionTSReceived is the number of Timestamp options seen.
- OptionTSReceived tcpip.MultiCounterStat
-
- // OptionRRReceived is the number of Record Route options seen.
- OptionRRReceived tcpip.MultiCounterStat
-
- // OptionUnknownReceived is the number of unknown IP options seen.
- OptionUnknownReceived tcpip.MultiCounterStat
-}
-
-// Init sets internal counters to track a and b counters.
-func (m *MultiCounterIPStats) Init(a, b *tcpip.IPStats) {
- m.PacketsReceived.Init(a.PacketsReceived, b.PacketsReceived)
- m.DisabledPacketsReceived.Init(a.DisabledPacketsReceived, b.DisabledPacketsReceived)
- m.InvalidDestinationAddressesReceived.Init(a.InvalidDestinationAddressesReceived, b.InvalidDestinationAddressesReceived)
- m.InvalidSourceAddressesReceived.Init(a.InvalidSourceAddressesReceived, b.InvalidSourceAddressesReceived)
- m.PacketsDelivered.Init(a.PacketsDelivered, b.PacketsDelivered)
- m.PacketsSent.Init(a.PacketsSent, b.PacketsSent)
- m.OutgoingPacketErrors.Init(a.OutgoingPacketErrors, b.OutgoingPacketErrors)
- m.MalformedPacketsReceived.Init(a.MalformedPacketsReceived, b.MalformedPacketsReceived)
- m.MalformedFragmentsReceived.Init(a.MalformedFragmentsReceived, b.MalformedFragmentsReceived)
- m.IPTablesPreroutingDropped.Init(a.IPTablesPreroutingDropped, b.IPTablesPreroutingDropped)
- m.IPTablesInputDropped.Init(a.IPTablesInputDropped, b.IPTablesInputDropped)
- m.IPTablesOutputDropped.Init(a.IPTablesOutputDropped, b.IPTablesOutputDropped)
- m.OptionTSReceived.Init(a.OptionTSReceived, b.OptionTSReceived)
- m.OptionRRReceived.Init(a.OptionRRReceived, b.OptionRRReceived)
- m.OptionUnknownReceived.Init(a.OptionUnknownReceived, b.OptionUnknownReceived)
-}
-
-// LINT.ThenChange(:MultiCounterIPStats, ../../tcpip.go:IPStats)