summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/network/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/network/ipv6')
-rw-r--r--pkg/tcpip/network/ipv6/BUILD1
-rw-r--r--pkg/tcpip/network/ipv6/icmp.go135
-rw-r--r--pkg/tcpip/network/ipv6/ipv6.go111
-rw-r--r--pkg/tcpip/network/ipv6/ipv6_test.go46
-rw-r--r--pkg/tcpip/network/ipv6/mld.go10
-rw-r--r--pkg/tcpip/network/ipv6/ndp.go13
-rw-r--r--pkg/tcpip/network/ipv6/stats.go132
7 files changed, 324 insertions, 124 deletions
diff --git a/pkg/tcpip/network/ipv6/BUILD b/pkg/tcpip/network/ipv6/BUILD
index afa45aefe..0c5f8d683 100644
--- a/pkg/tcpip/network/ipv6/BUILD
+++ b/pkg/tcpip/network/ipv6/BUILD
@@ -10,6 +10,7 @@ go_library(
"ipv6.go",
"mld.go",
"ndp.go",
+ "stats.go",
],
visibility = ["//visibility:public"],
deps = [
diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go
index 6ee162713..47e8aa11a 100644
--- a/pkg/tcpip/network/ipv6/icmp.go
+++ b/pkg/tcpip/network/ipv6/icmp.go
@@ -125,15 +125,14 @@ func getTargetLinkAddr(it header.NDPOptionIterator) (tcpip.LinkAddress, bool) {
}
func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
- stats := e.protocol.stack.Stats().ICMP
- sent := stats.V6.PacketsSent
- received := stats.V6.PacketsReceived
+ sent := e.stats.icmp.packetsSent
+ received := e.stats.icmp.packetsReceived
// TODO(gvisor.dev/issue/170): ICMP packets don't have their
// TransportHeader fields set. See icmp/protocol.go:protocol.Parse for a
// full explanation.
v, ok := pkt.Data.PullUp(header.ICMPv6HeaderSize)
if !ok {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
h := header.ICMPv6(v)
@@ -147,7 +146,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
payload := pkt.Data.Clone(nil)
payload.TrimFront(len(h))
if got, want := h.Checksum(), header.ICMPv6Checksum(h, srcAddr, dstAddr, payload); got != want {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -165,10 +164,10 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// TODO(b/112892170): Meaningfully handle all ICMP types.
switch icmpType := h.Type(); icmpType {
case header.ICMPv6PacketTooBig:
- received.PacketTooBig.Increment()
+ received.packetTooBig.Increment()
hdr, ok := pkt.Data.PullUp(header.ICMPv6PacketTooBigMinimumSize)
if !ok {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
pkt.Data.TrimFront(header.ICMPv6PacketTooBigMinimumSize)
@@ -179,10 +178,10 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
e.handleControl(stack.ControlPacketTooBig, networkMTU, pkt)
case header.ICMPv6DstUnreachable:
- received.DstUnreachable.Increment()
+ received.dstUnreachable.Increment()
hdr, ok := pkt.Data.PullUp(header.ICMPv6DstUnreachableMinimumSize)
if !ok {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
pkt.Data.TrimFront(header.ICMPv6DstUnreachableMinimumSize)
@@ -194,9 +193,9 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
}
case header.ICMPv6NeighborSolicit:
- received.NeighborSolicit.Increment()
+ received.neighborSolicit.Increment()
if !isNDPValid() || pkt.Data.Size() < header.ICMPv6NeighborSolicitMinimumSize {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -210,7 +209,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// As per RFC 4861 section 4.3, the Target Address MUST NOT be a multicast
// address.
if header.IsV6MulticastAddress(targetAddr) {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -263,13 +262,13 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
if err != nil {
// Options are not valid as per the wire format, silently drop the
// packet.
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
sourceLinkAddr, ok = getSourceLinkAddr(it)
if !ok {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
}
@@ -282,11 +281,11 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
unspecifiedSource := srcAddr == header.IPv6Any
if len(sourceLinkAddr) == 0 {
if header.IsV6MulticastAddress(dstAddr) && !unspecifiedSource {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
} else if unspecifiedSource {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
} else if e.nud != nil {
e.nud.HandleProbe(srcAddr, header.IPv6ProtocolNumber, sourceLinkAddr, e.protocol)
@@ -301,7 +300,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// - If the IP source address is the unspecified address, the IP
// destination address is a solicited-node multicast address.
if unspecifiedSource && !header.IsSolicitedNodeAddr(dstAddr) {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -379,15 +378,15 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// The IP Hop Limit field has a value of 255, i.e., the packet
// could not possibly have been forwarded by a router.
if err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{Protocol: header.ICMPv6ProtocolNumber, TTL: header.NDPHopLimit, TOS: stack.DefaultTOS}, pkt); err != nil {
- sent.Dropped.Increment()
+ sent.dropped.Increment()
return
}
- sent.NeighborAdvert.Increment()
+ sent.neighborAdvert.Increment()
case header.ICMPv6NeighborAdvert:
- received.NeighborAdvert.Increment()
+ received.neighborAdvert.Increment()
if !isNDPValid() || pkt.Data.Size() < header.ICMPv6NeighborAdvertMinimumSize {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -423,7 +422,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
it, err := na.Options().Iter(false /* check */)
if err != nil {
// If we have a malformed NDP NA option, drop the packet.
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -438,7 +437,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// DAD.
targetLinkAddr, ok := getTargetLinkAddr(it)
if !ok {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -458,10 +457,10 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
})
case header.ICMPv6EchoRequest:
- received.EchoRequest.Increment()
+ received.echoRequest.Increment()
icmpHdr, ok := pkt.TransportHeader().Consume(header.ICMPv6EchoMinimumSize)
if !ok {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -493,27 +492,27 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
TTL: r.DefaultTTL(),
TOS: stack.DefaultTOS,
}, replyPkt); err != nil {
- sent.Dropped.Increment()
+ sent.dropped.Increment()
return
}
- sent.EchoReply.Increment()
+ sent.echoReply.Increment()
case header.ICMPv6EchoReply:
- received.EchoReply.Increment()
+ received.echoReply.Increment()
if pkt.Data.Size() < header.ICMPv6EchoMinimumSize {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
e.dispatcher.DeliverTransportPacket(header.ICMPv6ProtocolNumber, pkt)
case header.ICMPv6TimeExceeded:
- received.TimeExceeded.Increment()
+ received.timeExceeded.Increment()
case header.ICMPv6ParamProblem:
- received.ParamProblem.Increment()
+ received.paramProblem.Increment()
case header.ICMPv6RouterSolicit:
- received.RouterSolicit.Increment()
+ received.routerSolicit.Increment()
//
// Validate the RS as per RFC 4861 section 6.1.1.
@@ -521,7 +520,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// Is the NDP payload of sufficient size to hold a Router Solictation?
if !isNDPValid() || pkt.Data.Size()-header.ICMPv6HeaderSize < header.NDPRSMinimumSize {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -530,7 +529,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// Is the networking stack operating as a router?
if !stack.Forwarding(ProtocolNumber) {
// ... No, silently drop the packet.
- received.RouterOnlyPacketsDroppedByHost.Increment()
+ received.routerOnlyPacketsDroppedByHost.Increment()
return
}
@@ -540,13 +539,13 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
it, err := rs.Options().Iter(false /* check */)
if err != nil {
// Options are not valid as per the wire format, silently drop the packet.
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
sourceLinkAddr, ok := getSourceLinkAddr(it)
if !ok {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -557,7 +556,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// NOT be included when the source IP address is the unspecified address.
// Otherwise, it SHOULD be included on link layers that have addresses.
if srcAddr == header.IPv6Any {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -569,7 +568,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
}
case header.ICMPv6RouterAdvert:
- received.RouterAdvert.Increment()
+ received.routerAdvert.Increment()
//
// Validate the RA as per RFC 4861 section 6.1.2.
@@ -577,7 +576,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// Is the NDP payload of sufficient size to hold a Router Advertisement?
if !isNDPValid() || pkt.Data.Size()-header.ICMPv6HeaderSize < header.NDPRAMinimumSize {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -586,7 +585,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// Is the IP Source Address a link-local address?
if !header.IsV6LinkLocalAddress(routerAddr) {
// ...No, silently drop the packet.
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -596,13 +595,13 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
it, err := ra.Options().Iter(false /* check */)
if err != nil {
// Options are not valid as per the wire format, silently drop the packet.
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
sourceLinkAddr, ok := getSourceLinkAddr(it)
if !ok {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -638,26 +637,26 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// link-layer address be modified due to receiving one of the above
// messages, the state SHOULD also be set to STALE to provide prompt
// verification that the path to the new link-layer address is working."
- received.RedirectMsg.Increment()
+ received.redirectMsg.Increment()
if !isNDPValid() {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
case header.ICMPv6MulticastListenerQuery, header.ICMPv6MulticastListenerReport, header.ICMPv6MulticastListenerDone:
switch icmpType {
case header.ICMPv6MulticastListenerQuery:
- received.MulticastListenerQuery.Increment()
+ received.multicastListenerQuery.Increment()
case header.ICMPv6MulticastListenerReport:
- received.MulticastListenerReport.Increment()
+ received.multicastListenerReport.Increment()
case header.ICMPv6MulticastListenerDone:
- received.MulticastListenerDone.Increment()
+ received.multicastListenerDone.Increment()
default:
panic(fmt.Sprintf("unrecognized MLD message = %d", icmpType))
}
if pkt.Data.Size()-header.ICMPv6HeaderSize < header.MLDMinimumSize {
- received.Invalid.Increment()
+ received.invalid.Increment()
return
}
@@ -676,7 +675,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
}
default:
- received.Unrecognized.Increment()
+ received.unrecognized.Increment()
}
}
@@ -717,16 +716,23 @@ func (p *protocol) LinkAddressRequest(targetAddr, localAddr tcpip.Address, remot
ns.Options().Serialize(optsSerializer)
packet.SetChecksum(header.ICMPv6Checksum(packet, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
- stat := p.stack.Stats().ICMP.V6.PacketsSent
+ p.mu.Lock()
+ netEP, ok := p.mu.eps[nic.ID()]
+ p.mu.Unlock()
+ if !ok {
+ return tcpip.ErrNotConnected
+ }
+ stat := netEP.stats.icmp.packetsSent
+
if err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{
Protocol: header.ICMPv6ProtocolNumber,
TTL: header.NDPHopLimit,
}, pkt); err != nil {
- stat.Dropped.Increment()
+ stat.dropped.Increment()
return err
}
- stat.NeighborSolicit.Increment()
+ stat.neighborSolicit.Increment()
return nil
}
@@ -863,10 +869,17 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) *tcpi
}
defer route.Release()
- stats := p.stack.Stats().ICMP
- sent := stats.V6.PacketsSent
+ p.mu.Lock()
+ netEP, ok := p.mu.eps[pkt.NICID]
+ p.mu.Unlock()
+ if !ok {
+ return tcpip.ErrNotConnected
+ }
+
+ sent := netEP.stats.icmp.packetsSent
+
if !p.stack.AllowICMPMessage() {
- sent.RateLimited.Increment()
+ sent.rateLimited.Increment()
return nil
}
@@ -921,25 +934,25 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) *tcpi
newPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
icmpHdr := header.ICMPv6(newPkt.TransportHeader().Push(header.ICMPv6DstUnreachableMinimumSize))
- var counter *tcpip.StatCounter
+ var counter tcpip.MultiCounterStat
switch reason := reason.(type) {
case *icmpReasonParameterProblem:
icmpHdr.SetType(header.ICMPv6ParamProblem)
icmpHdr.SetCode(reason.code)
icmpHdr.SetTypeSpecific(reason.pointer)
- counter = sent.ParamProblem
+ counter = sent.paramProblem
case *icmpReasonPortUnreachable:
icmpHdr.SetType(header.ICMPv6DstUnreachable)
icmpHdr.SetCode(header.ICMPv6PortUnreachable)
- counter = sent.DstUnreachable
+ counter = sent.dstUnreachable
case *icmpReasonHopLimitExceeded:
icmpHdr.SetType(header.ICMPv6TimeExceeded)
icmpHdr.SetCode(header.ICMPv6HopLimitExceeded)
- counter = sent.TimeExceeded
+ counter = sent.timeExceeded
case *icmpReasonReassemblyTimeout:
icmpHdr.SetType(header.ICMPv6TimeExceeded)
icmpHdr.SetCode(header.ICMPv6ReassemblyTimeout)
- counter = sent.TimeExceeded
+ counter = sent.timeExceeded
default:
panic(fmt.Sprintf("unsupported ICMP type %T", reason))
}
@@ -953,7 +966,7 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) *tcpi
},
newPkt,
); err != nil {
- sent.Dropped.Increment()
+ sent.dropped.Increment()
return err
}
counter.Increment()
diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go
index ae4a8f508..37884505e 100644
--- a/pkg/tcpip/network/ipv6/ipv6.go
+++ b/pkg/tcpip/network/ipv6/ipv6.go
@@ -20,6 +20,7 @@ import (
"fmt"
"hash/fnv"
"math"
+ "reflect"
"sort"
"sync/atomic"
"time"
@@ -177,6 +178,7 @@ type endpoint struct {
dispatcher stack.TransportDispatcher
protocol *protocol
stack *stack.Stack
+ stats sharedStats
// enabled is set to 1 when the endpoint is enabled and 0 when it is
// disabled.
@@ -632,7 +634,7 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, params stack.Netw
nicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
if ok := e.protocol.stack.IPTables().Check(stack.Output, pkt, gso, r, "", nicName); !ok {
// iptables is telling us to drop the packet.
- e.protocol.stack.Stats().IP.IPTablesOutputDropped.Increment()
+ e.stats.ip.IPTablesOutputDropped.Increment()
return nil
}
@@ -675,9 +677,10 @@ func (e *endpoint) writePacket(r *stack.Route, gso *stack.GSO, pkt *stack.Packet
return nil
}
+ stats := e.stats.ip
networkMTU, err := calculateNetworkMTU(e.nic.MTU(), uint32(pkt.NetworkHeader().View().Size()))
if err != nil {
- r.Stats().IP.OutgoingPacketErrors.Increment()
+ stats.OutgoingPacketErrors.Increment()
return err
}
@@ -689,17 +692,17 @@ func (e *endpoint) writePacket(r *stack.Route, gso *stack.GSO, pkt *stack.Packet
// WritePackets(). It'll be faster but cost more memory.
return e.nic.WritePacket(r, gso, ProtocolNumber, fragPkt)
})
- r.Stats().IP.PacketsSent.IncrementBy(uint64(sent))
- r.Stats().IP.OutgoingPacketErrors.IncrementBy(uint64(remain))
+ stats.PacketsSent.IncrementBy(uint64(sent))
+ stats.OutgoingPacketErrors.IncrementBy(uint64(remain))
return err
}
if err := e.nic.WritePacket(r, gso, ProtocolNumber, pkt); err != nil {
- r.Stats().IP.OutgoingPacketErrors.Increment()
+ stats.OutgoingPacketErrors.Increment()
return err
}
- r.Stats().IP.PacketsSent.Increment()
+ stats.PacketsSent.Increment()
return nil
}
@@ -712,6 +715,7 @@ func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.Packe
return pkts.Len(), nil
}
+ stats := e.stats.ip
linkMTU := e.nic.MTU()
for pb := pkts.Front(); pb != nil; pb = pb.Next() {
if err := e.addIPHeader(r.LocalAddress, r.RemoteAddress, pb, params, nil /* extensionHeaders */); err != nil {
@@ -720,7 +724,7 @@ func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.Packe
networkMTU, err := calculateNetworkMTU(linkMTU, uint32(pb.NetworkHeader().View().Size()))
if err != nil {
- r.Stats().IP.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len()))
+ stats.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len()))
return 0, err
}
if packetMustBeFragmented(pb, networkMTU, gso) {
@@ -733,7 +737,7 @@ func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.Packe
pb = fragPkt
return nil
}); err != nil {
- r.Stats().IP.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len()))
+ stats.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len()))
return 0, err
}
// Remove the packet that was just fragmented and process the rest.
@@ -749,13 +753,13 @@ func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.Packe
// Fast path: If no packets are to be dropped then we can just invoke the
// faster WritePackets API directly.
n, err := e.nic.WritePackets(r, gso, pkts, ProtocolNumber)
- r.Stats().IP.PacketsSent.IncrementBy(uint64(n))
+ stats.PacketsSent.IncrementBy(uint64(n))
if err != nil {
- r.Stats().IP.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len() - n))
+ stats.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len() - n))
}
return n, err
}
- r.Stats().IP.IPTablesOutputDropped.IncrementBy(uint64(len(dropped)))
+ stats.IPTablesOutputDropped.IncrementBy(uint64(len(dropped)))
// Slow path as we are dropping some packets in the batch degrade to
// emitting one packet at a time.
@@ -779,8 +783,8 @@ func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.Packe
}
}
if err := e.nic.WritePacket(r, gso, ProtocolNumber, pkt); err != nil {
- r.Stats().IP.PacketsSent.IncrementBy(uint64(n))
- r.Stats().IP.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len() - n + len(dropped)))
+ stats.PacketsSent.IncrementBy(uint64(n))
+ stats.OutgoingPacketErrors.IncrementBy(uint64(pkts.Len() - n + len(dropped)))
// Dropped packets aren't errors, so include them in
// the return value.
return n + len(dropped), err
@@ -788,7 +792,7 @@ func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts stack.Packe
n++
}
- r.Stats().IP.PacketsSent.IncrementBy(uint64(n))
+ stats.PacketsSent.IncrementBy(uint64(n))
// Dropped packets aren't errors, so include them in the return value.
return n + len(dropped), nil
}
@@ -882,11 +886,12 @@ func (e *endpoint) forwardPacket(pkt *stack.PacketBuffer) *tcpip.Error {
// HandlePacket is called by the link layer when new ipv6 packets arrive for
// this endpoint.
func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
- stats := e.protocol.stack.Stats()
- stats.IP.PacketsReceived.Increment()
+ stats := e.stats.ip
+
+ stats.PacketsReceived.Increment()
if !e.isEnabled() {
- stats.IP.DisabledPacketsReceived.Increment()
+ stats.DisabledPacketsReceived.Increment()
return
}
@@ -894,7 +899,7 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
if !e.nic.IsLoopback() {
if ok := e.protocol.stack.IPTables().Check(stack.Prerouting, pkt, nil, nil, e.MainAddress().Address, ""); !ok {
// iptables is telling us to drop the packet.
- stats.IP.IPTablesPreroutingDropped.Increment()
+ stats.IPTablesPreroutingDropped.Increment()
return
}
}
@@ -906,11 +911,11 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
// iptables hook.
func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
pkt.NICID = e.nic.ID()
- stats := e.protocol.stack.Stats()
+ stats := e.stats.ip
h := header.IPv6(pkt.NetworkHeader().View())
if !h.IsValid(pkt.Data.Size() + pkt.NetworkHeader().View().Size() + pkt.TransportHeader().View().Size()) {
- stats.IP.MalformedPacketsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
return
}
srcAddr := h.SourceAddress()
@@ -920,7 +925,7 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
// Multicast addresses must not be used as source addresses in IPv6
// packets or appear in any Routing header.
if header.IsV6MulticastAddress(srcAddr) {
- stats.IP.InvalidSourceAddressesReceived.Increment()
+ stats.InvalidSourceAddressesReceived.Increment()
return
}
@@ -930,7 +935,7 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
addressEndpoint.DecRef()
} else if !e.IsInGroup(dstAddr) {
if !e.protocol.Forwarding() {
- stats.IP.InvalidDestinationAddressesReceived.Increment()
+ stats.InvalidDestinationAddressesReceived.Increment()
return
}
@@ -952,7 +957,7 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
// this machine and need not be forwarded.
if ok := e.protocol.stack.IPTables().Check(stack.Input, pkt, nil, nil, "", ""); !ok {
// iptables is telling us to drop the packet.
- stats.IP.IPTablesInputDropped.Increment()
+ stats.IPTablesInputDropped.Increment()
return
}
@@ -962,7 +967,7 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
previousHeaderStart := it.HeaderOffset()
extHdr, done, err := it.Next()
if err != nil {
- stats.IP.MalformedPacketsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
return
}
if done {
@@ -986,7 +991,7 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
for {
opt, done, err := optsIt.Next()
if err != nil {
- stats.IP.MalformedPacketsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
return
}
if done {
@@ -1075,8 +1080,8 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
for {
it, done, err := it.Next()
if err != nil {
- stats.IP.MalformedPacketsReceived.Increment()
- stats.IP.MalformedFragmentsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
+ stats.MalformedFragmentsReceived.Increment()
return
}
if done {
@@ -1103,8 +1108,8 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
switch lastHdr.(type) {
case header.IPv6RawPayloadHeader:
default:
- stats.IP.MalformedPacketsReceived.Increment()
- stats.IP.MalformedFragmentsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
+ stats.MalformedFragmentsReceived.Increment()
return
}
}
@@ -1112,8 +1117,8 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
fragmentPayloadLen := rawPayload.Buf.Size()
if fragmentPayloadLen == 0 {
// Drop the packet as it's marked as a fragment but has no payload.
- stats.IP.MalformedPacketsReceived.Increment()
- stats.IP.MalformedFragmentsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
+ stats.MalformedFragmentsReceived.Increment()
return
}
@@ -1126,8 +1131,8 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
// of the fragment, pointing to the Payload Length field of the
// fragment packet.
if extHdr.More() && fragmentPayloadLen%header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit != 0 {
- stats.IP.MalformedPacketsReceived.Increment()
- stats.IP.MalformedFragmentsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
+ stats.MalformedFragmentsReceived.Increment()
_ = e.protocol.returnError(&icmpReasonParameterProblem{
code: header.ICMPv6ErroneousHeader,
pointer: header.IPv6PayloadLenOffset,
@@ -1147,8 +1152,8 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
// the fragment, pointing to the Fragment Offset field of the fragment
// packet.
if int(start)+fragmentPayloadLen > header.IPv6MaximumPayloadSize {
- stats.IP.MalformedPacketsReceived.Increment()
- stats.IP.MalformedFragmentsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
+ stats.MalformedFragmentsReceived.Increment()
_ = e.protocol.returnError(&icmpReasonParameterProblem{
code: header.ICMPv6ErroneousHeader,
pointer: fragmentFieldOffset,
@@ -1173,8 +1178,8 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
pkt,
)
if err != nil {
- stats.IP.MalformedPacketsReceived.Increment()
- stats.IP.MalformedFragmentsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
+ stats.MalformedFragmentsReceived.Increment()
return
}
@@ -1194,7 +1199,7 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
for {
opt, done, err := optsIt.Next()
if err != nil {
- stats.IP.MalformedPacketsReceived.Increment()
+ stats.MalformedPacketsReceived.Increment()
return
}
if done {
@@ -1244,12 +1249,12 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
extHdr.Buf.TrimFront(pkt.TransportHeader().View().Size())
pkt.Data = extHdr.Buf
- stats.IP.PacketsDelivered.Increment()
+ stats.PacketsDelivered.Increment()
if p := tcpip.TransportProtocolNumber(extHdr.Identifier); p == header.ICMPv6ProtocolNumber {
pkt.TransportProtocolNumber = p
e.handleICMP(pkt, hasFragmentHeader)
} else {
- stats.IP.PacketsDelivered.Increment()
+ stats.PacketsDelivered.Increment()
switch res := e.dispatcher.DeliverTransportPacket(p, pkt); res {
case stack.TransportPacketHandled:
case stack.TransportPacketDestinationPortUnreachable:
@@ -1314,7 +1319,7 @@ func (e *endpoint) Close() {
e.mu.addressableEndpointState.Cleanup()
e.mu.Unlock()
- e.protocol.forgetEndpoint(e)
+ e.protocol.forgetEndpoint(e.nic.ID())
}
// NetworkProtocolNumber implements stack.NetworkEndpoint.NetworkProtocolNumber.
@@ -1662,6 +1667,11 @@ func (e *endpoint) IsInGroup(addr tcpip.Address) bool {
return e.mu.mld.isInGroup(addr)
}
+// Stats implements stack.NetworkEndpoint.
+func (e *endpoint) Stats() stack.NetworkEndpointStats {
+ return &e.stats.localStats
+}
+
var _ stack.ForwardingNetworkProtocol = (*protocol)(nil)
var _ stack.NetworkProtocol = (*protocol)(nil)
var _ fragmentation.TimeoutHandler = (*protocol)(nil)
@@ -1673,7 +1683,9 @@ type protocol struct {
mu struct {
sync.RWMutex
- eps map[*endpoint]struct{}
+ // eps is keyed by NICID to allow protocol methods to retrieve an endpoint
+ // when handling a packet, by looking at which NIC handled the packet.
+ eps map[tcpip.NICID]*endpoint
}
ids []uint32
@@ -1730,16 +1742,21 @@ func (p *protocol) NewEndpoint(nic stack.NetworkInterface, linkAddrCache stack.L
e.mu.mld.init(e)
e.mu.Unlock()
+ stackStats := p.stack.Stats()
+ tcpip.InitStatCounters(reflect.ValueOf(&e.stats.localStats).Elem())
+ e.stats.ip.Init(&e.stats.localStats.IP, &stackStats.IP)
+ e.stats.icmp.init(&e.stats.localStats.ICMP, &stackStats.ICMP.V6)
+
p.mu.Lock()
defer p.mu.Unlock()
- p.mu.eps[e] = struct{}{}
+ p.mu.eps[nic.ID()] = e
return e
}
-func (p *protocol) forgetEndpoint(e *endpoint) {
+func (p *protocol) forgetEndpoint(nicID tcpip.NICID) {
p.mu.Lock()
defer p.mu.Unlock()
- delete(p.mu.eps, e)
+ delete(p.mu.eps, nicID)
}
// SetOption implements NetworkProtocol.SetOption.
@@ -1814,7 +1831,7 @@ func (p *protocol) SetForwarding(v bool) {
return
}
- for ep := range p.mu.eps {
+ for _, ep := range p.mu.eps {
ep.transitionForwarding(v)
}
}
@@ -1906,7 +1923,7 @@ func NewProtocolWithOptions(opts Options) stack.NetworkProtocolFactory {
hashIV: hashIV,
}
p.fragmentation = fragmentation.NewFragmentation(header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit, fragmentation.HighFragThreshold, fragmentation.LowFragThreshold, ReassembleTimeout, s.Clock(), p)
- p.mu.eps = make(map[*endpoint]struct{})
+ p.mu.eps = make(map[tcpip.NICID]*endpoint)
p.SetDefaultTTL(DefaultTTL)
return p
}
diff --git a/pkg/tcpip/network/ipv6/ipv6_test.go b/pkg/tcpip/network/ipv6/ipv6_test.go
index b65c9d060..aa892d043 100644
--- a/pkg/tcpip/network/ipv6/ipv6_test.go
+++ b/pkg/tcpip/network/ipv6/ipv6_test.go
@@ -21,6 +21,7 @@ import (
"io/ioutil"
"math"
"net"
+ "reflect"
"testing"
"github.com/google/go-cmp/cmp"
@@ -2582,18 +2583,33 @@ func (lm *limitedMatcher) Match(stack.Hook, *stack.PacketBuffer, string) (bool,
return false, false
}
+func getKnownNICIDs(proto *protocol) []tcpip.NICID {
+ var nicIDs []tcpip.NICID
+
+ for k := range proto.mu.eps {
+ nicIDs = append(nicIDs, k)
+ }
+
+ return nicIDs
+}
+
func TestClearEndpointFromProtocolOnClose(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
})
proto := s.NetworkProtocolInstance(ProtocolNumber).(*protocol)
- ep := proto.NewEndpoint(&testInterface{}, nil, nil, nil).(*endpoint)
+ var nic testInterface
+ ep := proto.NewEndpoint(&nic, nil, nil, nil).(*endpoint)
{
proto.mu.Lock()
- _, hasEP := proto.mu.eps[ep]
+ foundEP, hasEP := proto.mu.eps[nic.ID()]
+ nicIDs := getKnownNICIDs(proto)
proto.mu.Unlock()
if !hasEP {
- t.Fatalf("expected protocol to have ep = %p in set of endpoints", ep)
+ t.Fatalf("expected to find the nic id %d in the protocol's known nic ids (%v)", nic.ID(), nicIDs)
+ }
+ if foundEP != ep {
+ t.Fatalf("expected protocol to map endpoint %p to nic id %d, but endpoint %p was found instead", ep, nic.ID(), foundEP)
}
}
@@ -2601,10 +2617,11 @@ func TestClearEndpointFromProtocolOnClose(t *testing.T) {
{
proto.mu.Lock()
- _, hasEP := proto.mu.eps[ep]
+ _, hasEP := proto.mu.eps[nic.ID()]
+ nicIDs := getKnownNICIDs(proto)
proto.mu.Unlock()
if hasEP {
- t.Fatalf("unexpectedly found ep = %p in set of protocol's endpoints", ep)
+ t.Fatalf("unexpectedly found an endpoint mapped to the nic id %d in the protocol's known nic ids (%v)", nic.ID(), nicIDs)
}
}
}
@@ -3053,3 +3070,22 @@ func TestForwarding(t *testing.T) {
})
}
}
+
+func TestMultiCounterStatsInitialization(t *testing.T) {
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
+ })
+ proto := s.NetworkProtocolInstance(ProtocolNumber).(*protocol)
+ var nic testInterface
+ ep := proto.NewEndpoint(&nic, nil, nil, nil).(*endpoint)
+ // At this point, the Stack's stats and the NetworkEndpoint's stats are
+ // supposed to be bound.
+ refStack := s.Stats()
+ refEP := ep.stats.localStats
+ if err := testutil.ValidateMultiCounterStats(reflect.ValueOf(&ep.stats.ip).Elem(), []reflect.Value{reflect.ValueOf(&refStack.IP).Elem(), reflect.ValueOf(&refEP.IP).Elem()}); err != nil {
+ t.Error(err)
+ }
+ if err := testutil.ValidateMultiCounterStats(reflect.ValueOf(&ep.stats.icmp).Elem(), []reflect.Value{reflect.ValueOf(&refStack.ICMP.V6).Elem(), reflect.ValueOf(&refEP.ICMP).Elem()}); err != nil {
+ t.Error(err)
+ }
+}
diff --git a/pkg/tcpip/network/ipv6/mld.go b/pkg/tcpip/network/ipv6/mld.go
index ec54d88cc..78d86e523 100644
--- a/pkg/tcpip/network/ipv6/mld.go
+++ b/pkg/tcpip/network/ipv6/mld.go
@@ -167,13 +167,13 @@ func (mld *mldState) sendQueuedReports() {
//
// Precondition: mld.ep.mu must be read locked.
func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldType header.ICMPv6Type) (bool, *tcpip.Error) {
- sentStats := mld.ep.protocol.stack.Stats().ICMP.V6.PacketsSent
- var mldStat *tcpip.StatCounter
+ sentStats := mld.ep.stats.icmp.packetsSent
+ var mldStat tcpip.MultiCounterStat
switch mldType {
case header.ICMPv6MulticastListenerReport:
- mldStat = sentStats.MulticastListenerReport
+ mldStat = sentStats.multicastListenerReport
case header.ICMPv6MulticastListenerDone:
- mldStat = sentStats.MulticastListenerDone
+ mldStat = sentStats.multicastListenerDone
default:
panic(fmt.Sprintf("unrecognized mld type = %d", mldType))
}
@@ -256,7 +256,7 @@ func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldTyp
panic(fmt.Sprintf("failed to add IP header: %s", err))
}
if err := mld.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv6Address(destAddress), nil /* gso */, ProtocolNumber, pkt); err != nil {
- sentStats.Dropped.Increment()
+ sentStats.dropped.Increment()
return false, err
}
mldStat.Increment()
diff --git a/pkg/tcpip/network/ipv6/ndp.go b/pkg/tcpip/network/ipv6/ndp.go
index 1d8fee50b..41112a0c4 100644
--- a/pkg/tcpip/network/ipv6/ndp.go
+++ b/pkg/tcpip/network/ipv6/ndp.go
@@ -731,7 +731,7 @@ func (ndp *ndpState) sendDADPacket(addr tcpip.Address, addressEndpoint stack.Add
Data: buffer.View(icmp).ToVectorisedView(),
})
- sent := ndp.ep.protocol.stack.Stats().ICMP.V6.PacketsSent
+ sent := ndp.ep.stats.icmp.packetsSent
if err := ndp.ep.addIPHeader(header.IPv6Any, snmc, pkt, stack.NetworkHeaderParams{
Protocol: header.ICMPv6ProtocolNumber,
TTL: header.NDPHopLimit,
@@ -740,10 +740,11 @@ func (ndp *ndpState) sendDADPacket(addr tcpip.Address, addressEndpoint stack.Add
}
if err := ndp.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv6Address(snmc), nil /* gso */, ProtocolNumber, pkt); err != nil {
- sent.Dropped.Increment()
+ sent.dropped.Increment()
return err
}
- sent.NeighborSolicit.Increment()
+ sent.neighborSolicit.Increment()
+
return nil
}
@@ -1855,7 +1856,7 @@ func (ndp *ndpState) startSolicitingRouters() {
Data: buffer.View(icmpData).ToVectorisedView(),
})
- sent := ndp.ep.protocol.stack.Stats().ICMP.V6.PacketsSent
+ sent := ndp.ep.stats.icmp.packetsSent
if err := ndp.ep.addIPHeader(localAddr, header.IPv6AllRoutersMulticastAddress, pkt, stack.NetworkHeaderParams{
Protocol: header.ICMPv6ProtocolNumber,
TTL: header.NDPHopLimit,
@@ -1863,12 +1864,12 @@ func (ndp *ndpState) startSolicitingRouters() {
panic(fmt.Sprintf("failed to add IP header: %s", err))
}
if err := ndp.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv6Address(header.IPv6AllRoutersMulticastAddress), nil /* gso */, ProtocolNumber, pkt); err != nil {
- sent.Dropped.Increment()
+ sent.dropped.Increment()
log.Printf("startSolicitingRouters: error writing NDP router solicit message on NIC(%d); err = %s", ndp.ep.nic.ID(), err)
// Don't send any more messages if we had an error.
remaining = 0
} else {
- sent.RouterSolicit.Increment()
+ sent.routerSolicit.Increment()
remaining--
}
diff --git a/pkg/tcpip/network/ipv6/stats.go b/pkg/tcpip/network/ipv6/stats.go
new file mode 100644
index 000000000..a2f2f4f78
--- /dev/null
+++ b/pkg/tcpip/network/ipv6/stats.go
@@ -0,0 +1,132 @@
+// 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 ipv6
+
+import (
+ "gvisor.dev/gvisor/pkg/tcpip"
+ "gvisor.dev/gvisor/pkg/tcpip/network/ip"
+ "gvisor.dev/gvisor/pkg/tcpip/stack"
+)
+
+var _ stack.IPNetworkEndpointStats = (*Stats)(nil)
+
+// Stats holds statistics related to the IPv6 protocol family.
+type Stats struct {
+ // IP holds IPv6 statistics.
+ IP tcpip.IPStats
+
+ // ICMP holds ICMPv6 statistics.
+ ICMP tcpip.ICMPv6Stats
+}
+
+// IsNetworkEndpointStats implements stack.NetworkEndpointStats.
+func (s *Stats) IsNetworkEndpointStats() {}
+
+// IPStats implements stack.IPNetworkEndointStats
+func (s *Stats) IPStats() *tcpip.IPStats {
+ return &s.IP
+}
+
+type sharedStats struct {
+ localStats Stats
+ ip ip.MultiCounterIPStats
+ icmp multiCounterICMPv6Stats
+}
+
+// LINT.IfChange(multiCounterICMPv6PacketStats)
+
+type multiCounterICMPv6PacketStats struct {
+ echoRequest tcpip.MultiCounterStat
+ echoReply tcpip.MultiCounterStat
+ dstUnreachable tcpip.MultiCounterStat
+ packetTooBig tcpip.MultiCounterStat
+ timeExceeded tcpip.MultiCounterStat
+ paramProblem tcpip.MultiCounterStat
+ routerSolicit tcpip.MultiCounterStat
+ routerAdvert tcpip.MultiCounterStat
+ neighborSolicit tcpip.MultiCounterStat
+ neighborAdvert tcpip.MultiCounterStat
+ redirectMsg tcpip.MultiCounterStat
+ multicastListenerQuery tcpip.MultiCounterStat
+ multicastListenerReport tcpip.MultiCounterStat
+ multicastListenerDone tcpip.MultiCounterStat
+}
+
+func (m *multiCounterICMPv6PacketStats) init(a, b *tcpip.ICMPv6PacketStats) {
+ m.echoRequest.Init(a.EchoRequest, b.EchoRequest)
+ m.echoReply.Init(a.EchoReply, b.EchoReply)
+ m.dstUnreachable.Init(a.DstUnreachable, b.DstUnreachable)
+ m.packetTooBig.Init(a.PacketTooBig, b.PacketTooBig)
+ m.timeExceeded.Init(a.TimeExceeded, b.TimeExceeded)
+ m.paramProblem.Init(a.ParamProblem, b.ParamProblem)
+ m.routerSolicit.Init(a.RouterSolicit, b.RouterSolicit)
+ m.routerAdvert.Init(a.RouterAdvert, b.RouterAdvert)
+ m.neighborSolicit.Init(a.NeighborSolicit, b.NeighborSolicit)
+ m.neighborAdvert.Init(a.NeighborAdvert, b.NeighborAdvert)
+ m.redirectMsg.Init(a.RedirectMsg, b.RedirectMsg)
+ m.multicastListenerQuery.Init(a.MulticastListenerQuery, b.MulticastListenerQuery)
+ m.multicastListenerReport.Init(a.MulticastListenerReport, b.MulticastListenerReport)
+ m.multicastListenerDone.Init(a.MulticastListenerDone, b.MulticastListenerDone)
+}
+
+// LINT.ThenChange(../../tcpip.go:ICMPv6PacketStats)
+
+// LINT.IfChange(multiCounterICMPv6SentPacketStats)
+
+type multiCounterICMPv6SentPacketStats struct {
+ multiCounterICMPv6PacketStats
+ dropped tcpip.MultiCounterStat
+ rateLimited tcpip.MultiCounterStat
+}
+
+func (m *multiCounterICMPv6SentPacketStats) init(a, b *tcpip.ICMPv6SentPacketStats) {
+ m.multiCounterICMPv6PacketStats.init(&a.ICMPv6PacketStats, &b.ICMPv6PacketStats)
+ m.dropped.Init(a.Dropped, b.Dropped)
+ m.rateLimited.Init(a.RateLimited, b.RateLimited)
+}
+
+// LINT.ThenChange(../../tcpip.go:ICMPv6SentPacketStats)
+
+// LINT.IfChange(multiCounterICMPv6ReceivedPacketStats)
+
+type multiCounterICMPv6ReceivedPacketStats struct {
+ multiCounterICMPv6PacketStats
+ unrecognized tcpip.MultiCounterStat
+ invalid tcpip.MultiCounterStat
+ routerOnlyPacketsDroppedByHost tcpip.MultiCounterStat
+}
+
+func (m *multiCounterICMPv6ReceivedPacketStats) init(a, b *tcpip.ICMPv6ReceivedPacketStats) {
+ m.multiCounterICMPv6PacketStats.init(&a.ICMPv6PacketStats, &b.ICMPv6PacketStats)
+ m.unrecognized.Init(a.Unrecognized, b.Unrecognized)
+ m.invalid.Init(a.Invalid, b.Invalid)
+ m.routerOnlyPacketsDroppedByHost.Init(a.RouterOnlyPacketsDroppedByHost, b.RouterOnlyPacketsDroppedByHost)
+}
+
+// LINT.ThenChange(../../tcpip.go:ICMPv6ReceivedPacketStats)
+
+// LINT.IfChange(multiCounterICMPv6Stats)
+
+type multiCounterICMPv6Stats struct {
+ packetsSent multiCounterICMPv6SentPacketStats
+ packetsReceived multiCounterICMPv6ReceivedPacketStats
+}
+
+func (m *multiCounterICMPv6Stats) init(a, b *tcpip.ICMPv6Stats) {
+ m.packetsSent.init(&a.PacketsSent, &b.PacketsSent)
+ m.packetsReceived.init(&a.PacketsReceived, &b.PacketsReceived)
+}
+
+// LINT.ThenChange(../../tcpip.go:ICMPv6Stats)