mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add ICMP stats
PiperOrigin-RevId: 240848882 Change-Id: I23dd4599f073263437aeab357c3f767e1a432b82
This commit is contained in:
committed by
Shentubot
parent
e373d3642e
commit
f2e5dcf21c
@@ -66,6 +66,72 @@ var Metrics = tcpip.Stats{
|
||||
UnknownProtocolRcvdPackets: mustCreateMetric("/netstack/unknown_protocol_received_packets", "Number of packets received by netstack that were for an unknown or unsupported protocol."),
|
||||
MalformedRcvdPackets: mustCreateMetric("/netstack/malformed_received_packets", "Number of packets received by netstack that were deemed malformed."),
|
||||
DroppedPackets: mustCreateMetric("/netstack/dropped_packets", "Number of packets dropped by netstack due to full queues."),
|
||||
ICMP: tcpip.ICMPStats{
|
||||
V4PacketsSent: tcpip.ICMPv4SentPacketStats{
|
||||
ICMPv4PacketStats: tcpip.ICMPv4PacketStats{
|
||||
Echo: mustCreateMetric("/netstack/icmp/v4/packets_sent/echo", "Total number of ICMPv4 echo packets sent by netstack."),
|
||||
EchoReply: mustCreateMetric("/netstack/icmp/v4/packets_sent/echo_reply", "Total number of ICMPv4 echo reply packets sent by netstack."),
|
||||
DstUnreachable: mustCreateMetric("/netstack/icmp/v4/packets_sent/dst_unreachable", "Total number of ICMPv4 destination unreachable packets sent by netstack."),
|
||||
SrcQuench: mustCreateMetric("/netstack/icmp/v4/packets_sent/src_quench", "Total number of ICMPv4 source quench packets sent by netstack."),
|
||||
Redirect: mustCreateMetric("/netstack/icmp/v4/packets_sent/redirect", "Total number of ICMPv4 redirect packets sent by netstack."),
|
||||
TimeExceeded: mustCreateMetric("/netstack/icmp/v4/packets_sent/time_exceeded", "Total number of ICMPv4 time exceeded packets sent by netstack."),
|
||||
ParamProblem: mustCreateMetric("/netstack/icmp/v4/packets_sent/param_problem", "Total number of ICMPv4 parameter problem packets sent by netstack."),
|
||||
Timestamp: mustCreateMetric("/netstack/icmp/v4/packets_sent/timestamp", "Total number of ICMPv4 timestamp packets sent by netstack."),
|
||||
TimestampReply: mustCreateMetric("/netstack/icmp/v4/packets_sent/timestamp_reply", "Total number of ICMPv4 timestamp reply packets sent by netstack."),
|
||||
InfoRequest: mustCreateMetric("/netstack/icmp/v4/packets_sent/info_request", "Total number of ICMPv4 information request packets sent by netstack."),
|
||||
InfoReply: mustCreateMetric("/netstack/icmp/v4/packets_sent/info_reply", "Total number of ICMPv4 information reply packets sent by netstack."),
|
||||
},
|
||||
Dropped: mustCreateMetric("/netstack/icmp/v4/packets_sent/dropped", "Total number of ICMPv4 packets dropped by netstack due to link layer errors."),
|
||||
},
|
||||
V4PacketsReceived: tcpip.ICMPv4ReceivedPacketStats{
|
||||
ICMPv4PacketStats: tcpip.ICMPv4PacketStats{
|
||||
Echo: mustCreateMetric("/netstack/icmp/v4/packets_received/echo", "Total number of ICMPv4 echo packets received by netstack."),
|
||||
EchoReply: mustCreateMetric("/netstack/icmp/v4/packets_received/echo_reply", "Total number of ICMPv4 echo reply packets received by netstack."),
|
||||
DstUnreachable: mustCreateMetric("/netstack/icmp/v4/packets_received/dst_unreachable", "Total number of ICMPv4 destination unreachable packets received by netstack."),
|
||||
SrcQuench: mustCreateMetric("/netstack/icmp/v4/packets_received/src_quench", "Total number of ICMPv4 source quench packets received by netstack."),
|
||||
Redirect: mustCreateMetric("/netstack/icmp/v4/packets_received/redirect", "Total number of ICMPv4 redirect packets received by netstack."),
|
||||
TimeExceeded: mustCreateMetric("/netstack/icmp/v4/packets_received/time_exceeded", "Total number of ICMPv4 time exceeded packets received by netstack."),
|
||||
ParamProblem: mustCreateMetric("/netstack/icmp/v4/packets_received/param_problem", "Total number of ICMPv4 parameter problem packets received by netstack."),
|
||||
Timestamp: mustCreateMetric("/netstack/icmp/v4/packets_received/timestamp", "Total number of ICMPv4 timestamp packets received by netstack."),
|
||||
TimestampReply: mustCreateMetric("/netstack/icmp/v4/packets_received/timestamp_reply", "Total number of ICMPv4 timestamp reply packets received by netstack."),
|
||||
InfoRequest: mustCreateMetric("/netstack/icmp/v4/packets_received/info_request", "Total number of ICMPv4 information request packets received by netstack."),
|
||||
InfoReply: mustCreateMetric("/netstack/icmp/v4/packets_received/info_reply", "Total number of ICMPv4 information reply packets received by netstack."),
|
||||
},
|
||||
Invalid: mustCreateMetric("/netstack/icmp/v4/packets_received/invalid", "Total number of ICMPv4 packets received that the transport layer could not parse."),
|
||||
},
|
||||
V6PacketsSent: tcpip.ICMPv6SentPacketStats{
|
||||
ICMPv6PacketStats: tcpip.ICMPv6PacketStats{
|
||||
EchoRequest: mustCreateMetric("/netstack/icmp/v6/packets_sent/echo_request", "Total number of ICMPv6 echo request packets sent by netstack."),
|
||||
EchoReply: mustCreateMetric("/netstack/icmp/v6/packets_sent/echo_reply", "Total number of ICMPv6 echo reply packets sent by netstack."),
|
||||
DstUnreachable: mustCreateMetric("/netstack/icmp/v6/packets_sent/dst_unreachable", "Total number of ICMPv6 destination unreachable packets sent by netstack."),
|
||||
PacketTooBig: mustCreateMetric("/netstack/icmp/v6/packets_sent/packet_too_big", "Total number of ICMPv6 packet too big packets sent by netstack."),
|
||||
TimeExceeded: mustCreateMetric("/netstack/icmp/v6/packets_sent/time_exceeded", "Total number of ICMPv6 time exceeded packets sent by netstack."),
|
||||
ParamProblem: mustCreateMetric("/netstack/icmp/v6/packets_sent/param_problem", "Total number of ICMPv6 parameter problem packets sent by netstack."),
|
||||
RouterSolicit: mustCreateMetric("/netstack/icmp/v6/packets_sent/router_solicit", "Total number of ICMPv6 router solicit packets sent by netstack."),
|
||||
RouterAdvert: mustCreateMetric("/netstack/icmp/v6/packets_sent/router_advert", "Total number of ICMPv6 router advert packets sent by netstack."),
|
||||
NeighborSolicit: mustCreateMetric("/netstack/icmp/v6/packets_sent/neighbor_solicit", "Total number of ICMPv6 neighbor solicit packets sent by netstack."),
|
||||
NeighborAdvert: mustCreateMetric("/netstack/icmp/v6/packets_sent/neighbor_advert", "Total number of ICMPv6 neighbor advert packets sent by netstack."),
|
||||
RedirectMsg: mustCreateMetric("/netstack/icmp/v6/packets_sent/redirect_msg", "Total number of ICMPv6 redirect message packets sent by netstack."),
|
||||
},
|
||||
Dropped: mustCreateMetric("/netstack/icmp/v6/packets_sent/dropped", "Total number of ICMPv6 packets dropped by netstack due to link layer errors."),
|
||||
},
|
||||
V6PacketsReceived: tcpip.ICMPv6ReceivedPacketStats{
|
||||
ICMPv6PacketStats: tcpip.ICMPv6PacketStats{
|
||||
EchoRequest: mustCreateMetric("/netstack/icmp/v6/packets_received/echo_request", "Total number of ICMPv6 echo request packets received by netstack."),
|
||||
EchoReply: mustCreateMetric("/netstack/icmp/v6/packets_received/echo_reply", "Total number of ICMPv6 echo reply packets received by netstack."),
|
||||
DstUnreachable: mustCreateMetric("/netstack/icmp/v6/packets_received/dst_unreachable", "Total number of ICMPv6 destination unreachable packets received by netstack."),
|
||||
PacketTooBig: mustCreateMetric("/netstack/icmp/v6/packets_received/packet_too_big", "Total number of ICMPv6 packet too big packets received by netstack."),
|
||||
TimeExceeded: mustCreateMetric("/netstack/icmp/v6/packets_received/time_exceeded", "Total number of ICMPv6 time exceeded packets received by netstack."),
|
||||
ParamProblem: mustCreateMetric("/netstack/icmp/v6/packets_received/param_problem", "Total number of ICMPv6 parameter problem packets received by netstack."),
|
||||
RouterSolicit: mustCreateMetric("/netstack/icmp/v6/packets_received/router_solicit", "Total number of ICMPv6 router solicit packets received by netstack."),
|
||||
RouterAdvert: mustCreateMetric("/netstack/icmp/v6/packets_received/router_advert", "Total number of ICMPv6 router advert packets received by netstack."),
|
||||
NeighborSolicit: mustCreateMetric("/netstack/icmp/v6/packets_received/neighbor_solicit", "Total number of ICMPv6 neighbor solicit packets received by netstack."),
|
||||
NeighborAdvert: mustCreateMetric("/netstack/icmp/v6/packets_received/neighbor_advert", "Total number of ICMPv6 neighbor advert packets received by netstack."),
|
||||
RedirectMsg: mustCreateMetric("/netstack/icmp/v6/packets_received/redirect_msg", "Total number of ICMPv6 redirect message packets received by netstack."),
|
||||
},
|
||||
Invalid: mustCreateMetric("/netstack/icmp/v6/packets_received/invalid", "Total number of ICMPv6 packets received that the transport layer could not parse."),
|
||||
},
|
||||
},
|
||||
IP: tcpip.IPStats{
|
||||
PacketsReceived: mustCreateMetric("/netstack/ip/packets_received", "Total number of IP packets received from the link layer in nic.DeliverNetworkPacket."),
|
||||
InvalidAddressesReceived: mustCreateMetric("/netstack/ip/invalid_addresses_received", "Total number of IP packets received with an unknown or invalid destination address."),
|
||||
|
||||
@@ -287,9 +287,9 @@ func TestIPv4ReceiveControl(t *testing.T) {
|
||||
{"Non-zero fragment offset", 0, 100, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 0},
|
||||
{"Zero-length packet", 0, 0, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 2*header.IPv4MinimumSize + header.ICMPv4DstUnreachableMinimumSize + 8},
|
||||
}
|
||||
r := stack.Route{
|
||||
LocalAddress: localIpv4Addr,
|
||||
RemoteAddress: "\x0a\x00\x00\xbb",
|
||||
r, err := buildIPv4Route(localIpv4Addr, "\x0a\x00\x00\xbb")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
@@ -521,9 +521,12 @@ func TestIPv6ReceiveControl(t *testing.T) {
|
||||
{"Non-zero fragment offset", 0, newUint16(100), header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 0},
|
||||
{"Zero-length packet", 0, nil, header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 2*header.IPv6MinimumSize + header.ICMPv6DstUnreachableMinimumSize + 8},
|
||||
}
|
||||
r := stack.Route{
|
||||
LocalAddress: localIpv6Addr,
|
||||
RemoteAddress: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa",
|
||||
r, err := buildIPv6Route(
|
||||
localIpv6Addr,
|
||||
"\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
@@ -55,15 +55,21 @@ func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, vv buffer.
|
||||
}
|
||||
|
||||
func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.VectorisedView) {
|
||||
stats := r.Stats()
|
||||
received := stats.ICMP.V4PacketsReceived
|
||||
v := vv.First()
|
||||
if len(v) < header.ICMPv4MinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
h := header.ICMPv4(v)
|
||||
|
||||
// TODO: Meaningfully handle all ICMP types.
|
||||
switch h.Type() {
|
||||
case header.ICMPv4Echo:
|
||||
received.Echo.Increment()
|
||||
if len(v) < header.ICMPv4EchoMinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
// It's possible that a raw socket expects to receive this.
|
||||
@@ -76,16 +82,25 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
|
||||
copy(pkt, h)
|
||||
pkt.SetType(header.ICMPv4EchoReply)
|
||||
pkt.SetChecksum(^header.Checksum(pkt, header.ChecksumVV(vv, 0)))
|
||||
r.WritePacket(nil /* gso */, hdr, vv, header.ICMPv4ProtocolNumber, r.DefaultTTL())
|
||||
sent := stats.ICMP.V4PacketsSent
|
||||
if err := r.WritePacket(nil /* gso */, hdr, vv, header.ICMPv4ProtocolNumber, r.DefaultTTL()); err != nil {
|
||||
sent.Dropped.Increment()
|
||||
return
|
||||
}
|
||||
sent.EchoReply.Increment()
|
||||
|
||||
case header.ICMPv4EchoReply:
|
||||
received.EchoReply.Increment()
|
||||
if len(v) < header.ICMPv4EchoMinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, netHeader, vv)
|
||||
|
||||
case header.ICMPv4DstUnreachable:
|
||||
received.DstUnreachable.Increment()
|
||||
if len(v) < header.ICMPv4DstUnreachableMinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
vv.TrimFront(header.ICMPv4DstUnreachableMinimumSize)
|
||||
@@ -97,6 +112,32 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
|
||||
mtu := uint32(binary.BigEndian.Uint16(v[header.ICMPv4DstUnreachableMinimumSize-2:]))
|
||||
e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv)
|
||||
}
|
||||
|
||||
case header.ICMPv4SrcQuench:
|
||||
received.SrcQuench.Increment()
|
||||
|
||||
case header.ICMPv4Redirect:
|
||||
received.Redirect.Increment()
|
||||
|
||||
case header.ICMPv4TimeExceeded:
|
||||
received.TimeExceeded.Increment()
|
||||
|
||||
case header.ICMPv4ParamProblem:
|
||||
received.ParamProblem.Increment()
|
||||
|
||||
case header.ICMPv4Timestamp:
|
||||
received.Timestamp.Increment()
|
||||
|
||||
case header.ICMPv4TimestampReply:
|
||||
received.TimestampReply.Increment()
|
||||
|
||||
case header.ICMPv4InfoRequest:
|
||||
received.InfoRequest.Increment()
|
||||
|
||||
case header.ICMPv4InfoReply:
|
||||
received.InfoReply.Increment()
|
||||
|
||||
default:
|
||||
received.Invalid.Increment()
|
||||
}
|
||||
// TODO: Handle other ICMP types.
|
||||
}
|
||||
|
||||
@@ -63,15 +63,22 @@ func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, vv buffer.
|
||||
}
|
||||
|
||||
func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.VectorisedView) {
|
||||
stats := r.Stats().ICMP
|
||||
sent := stats.V6PacketsSent
|
||||
received := stats.V6PacketsReceived
|
||||
v := vv.First()
|
||||
if len(v) < header.ICMPv6MinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
h := header.ICMPv6(v)
|
||||
|
||||
// TODO: Meaningfully handle all ICMP types.
|
||||
switch h.Type() {
|
||||
case header.ICMPv6PacketTooBig:
|
||||
received.PacketTooBig.Increment()
|
||||
if len(v) < header.ICMPv6PacketTooBigMinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
vv.TrimFront(header.ICMPv6PacketTooBigMinimumSize)
|
||||
@@ -79,7 +86,9 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
|
||||
e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv)
|
||||
|
||||
case header.ICMPv6DstUnreachable:
|
||||
received.DstUnreachable.Increment()
|
||||
if len(v) < header.ICMPv6DstUnreachableMinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
vv.TrimFront(header.ICMPv6DstUnreachableMinimumSize)
|
||||
@@ -89,15 +98,21 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
|
||||
}
|
||||
|
||||
case header.ICMPv6NeighborSolicit:
|
||||
received.NeighborSolicit.Increment()
|
||||
|
||||
e.linkAddrCache.AddLinkAddress(e.nicid, r.RemoteAddress, r.RemoteLinkAddress)
|
||||
|
||||
if len(v) < header.ICMPv6NeighborSolicitMinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
targetAddr := tcpip.Address(v[8 : 8+16])
|
||||
targetAddr := tcpip.Address(v[8:][:16])
|
||||
if e.linkAddrCache.CheckLocalAddress(e.nicid, ProtocolNumber, targetAddr) == 0 {
|
||||
// We don't have a useful answer; the best we can do is ignore the request.
|
||||
return
|
||||
}
|
||||
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize)
|
||||
|
||||
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6NeighborAdvertSize)
|
||||
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize))
|
||||
pkt.SetType(header.ICMPv6NeighborAdvert)
|
||||
pkt[icmpV6FlagOffset] = ndpSolicitedFlag | ndpOverrideFlag
|
||||
@@ -118,22 +133,29 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
|
||||
defer r.Release()
|
||||
r.LocalAddress = targetAddr
|
||||
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
|
||||
r.WritePacket(nil /* gso */, hdr, buffer.VectorisedView{}, header.ICMPv6ProtocolNumber, r.DefaultTTL())
|
||||
|
||||
e.linkAddrCache.AddLinkAddress(e.nicid, r.RemoteAddress, r.RemoteLinkAddress)
|
||||
|
||||
case header.ICMPv6NeighborAdvert:
|
||||
if len(v) < header.ICMPv6NeighborAdvertSize {
|
||||
if err := r.WritePacket(nil /* gso */, hdr, buffer.VectorisedView{}, header.ICMPv6ProtocolNumber, r.DefaultTTL()); err != nil {
|
||||
sent.Dropped.Increment()
|
||||
return
|
||||
}
|
||||
targetAddr := tcpip.Address(v[8 : 8+16])
|
||||
sent.NeighborAdvert.Increment()
|
||||
|
||||
case header.ICMPv6NeighborAdvert:
|
||||
received.NeighborAdvert.Increment()
|
||||
if len(v) < header.ICMPv6NeighborAdvertSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
targetAddr := tcpip.Address(v[8:][:16])
|
||||
e.linkAddrCache.AddLinkAddress(e.nicid, targetAddr, r.RemoteLinkAddress)
|
||||
if targetAddr != r.RemoteAddress {
|
||||
e.linkAddrCache.AddLinkAddress(e.nicid, r.RemoteAddress, r.RemoteLinkAddress)
|
||||
}
|
||||
|
||||
case header.ICMPv6EchoRequest:
|
||||
received.EchoRequest.Increment()
|
||||
if len(v) < header.ICMPv6EchoMinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -143,14 +165,37 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
|
||||
copy(pkt, h)
|
||||
pkt.SetType(header.ICMPv6EchoReply)
|
||||
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, vv))
|
||||
r.WritePacket(nil /* gso */, hdr, vv, header.ICMPv6ProtocolNumber, r.DefaultTTL())
|
||||
if err := r.WritePacket(nil /* gso */, hdr, vv, header.ICMPv6ProtocolNumber, r.DefaultTTL()); err != nil {
|
||||
sent.Dropped.Increment()
|
||||
return
|
||||
}
|
||||
sent.EchoReply.Increment()
|
||||
|
||||
case header.ICMPv6EchoReply:
|
||||
received.EchoReply.Increment()
|
||||
if len(v) < header.ICMPv6EchoMinimumSize {
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
e.dispatcher.DeliverTransportPacket(r, header.ICMPv6ProtocolNumber, netHeader, vv)
|
||||
|
||||
case header.ICMPv6TimeExceeded:
|
||||
received.TimeExceeded.Increment()
|
||||
|
||||
case header.ICMPv6ParamProblem:
|
||||
received.ParamProblem.Increment()
|
||||
|
||||
case header.ICMPv6RouterSolicit:
|
||||
received.RouterSolicit.Increment()
|
||||
|
||||
case header.ICMPv6RouterAdvert:
|
||||
received.RouterAdvert.Increment()
|
||||
|
||||
case header.ICMPv6RedirectMsg:
|
||||
received.RedirectMsg.Increment()
|
||||
|
||||
default:
|
||||
received.Invalid.Increment()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +247,7 @@ func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, linkEP stack.
|
||||
DstAddr: r.RemoteAddress,
|
||||
})
|
||||
|
||||
// TODO: count this in ICMP stats.
|
||||
return linkEP.WritePacket(r, nil /* gso */, hdr, buffer.VectorisedView{}, ProtocolNumber)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip"
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
|
||||
@@ -39,20 +40,151 @@ var (
|
||||
lladdr1 = header.LinkLocalAddr(linkAddr1)
|
||||
)
|
||||
|
||||
type icmpInfo struct {
|
||||
typ header.ICMPv6Type
|
||||
src tcpip.Address
|
||||
type stubLinkEndpoint struct {
|
||||
stack.LinkEndpoint
|
||||
}
|
||||
|
||||
func (*stubLinkEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (*stubLinkEndpoint) MaxHeaderLength() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (*stubLinkEndpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (*stubLinkEndpoint) WritePacket(*stack.Route, *stack.GSO, buffer.Prependable, buffer.VectorisedView, tcpip.NetworkProtocolNumber) *tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*stubLinkEndpoint) Attach(stack.NetworkDispatcher) {}
|
||||
|
||||
type stubDispatcher struct {
|
||||
stack.TransportDispatcher
|
||||
}
|
||||
|
||||
func (*stubDispatcher) DeliverTransportPacket(*stack.Route, tcpip.TransportProtocolNumber, buffer.View, buffer.VectorisedView) {
|
||||
}
|
||||
|
||||
type stubLinkAddressCache struct {
|
||||
stack.LinkAddressCache
|
||||
}
|
||||
|
||||
func (*stubLinkAddressCache) CheckLocalAddress(tcpip.NICID, tcpip.NetworkProtocolNumber, tcpip.Address) tcpip.NICID {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (*stubLinkAddressCache) AddLinkAddress(tcpip.NICID, tcpip.Address, tcpip.LinkAddress) {
|
||||
}
|
||||
|
||||
func TestICMPCounts(t *testing.T) {
|
||||
s := stack.New([]string{ProtocolName}, []string{icmp.ProtocolName6}, stack.Options{})
|
||||
{
|
||||
id := stack.RegisterLinkEndpoint(&stubLinkEndpoint{})
|
||||
if err := s.CreateNIC(1, id); err != nil {
|
||||
t.Fatalf("CreateNIC(_) = %s", err)
|
||||
}
|
||||
if err := s.AddAddress(1, ProtocolNumber, lladdr0); err != nil {
|
||||
t.Fatalf("AddAddress(_, %d, %s) = %s", ProtocolNumber, lladdr0, err)
|
||||
}
|
||||
}
|
||||
s.SetRouteTable(
|
||||
[]tcpip.Route{{
|
||||
Destination: lladdr1,
|
||||
Mask: tcpip.AddressMask(strings.Repeat("\xff", 16)),
|
||||
NIC: 1,
|
||||
}},
|
||||
)
|
||||
|
||||
ep, err := s.NetworkProtocolInstance(ProtocolNumber).NewEndpoint(0, lladdr1, &stubLinkAddressCache{}, &stubDispatcher{}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewEndpoint(_) = _, %s, want = _, nil", err)
|
||||
}
|
||||
|
||||
r, err := s.FindRoute(1, lladdr0, lladdr1, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Fatalf("FindRoute(_) = _, %s, want = _, nil", err)
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
types := []struct {
|
||||
typ header.ICMPv6Type
|
||||
size int
|
||||
}{
|
||||
{header.ICMPv6DstUnreachable, header.ICMPv6DstUnreachableMinimumSize},
|
||||
{header.ICMPv6PacketTooBig, header.ICMPv6PacketTooBigMinimumSize},
|
||||
{header.ICMPv6TimeExceeded, header.ICMPv6MinimumSize},
|
||||
{header.ICMPv6ParamProblem, header.ICMPv6MinimumSize},
|
||||
{header.ICMPv6EchoRequest, header.ICMPv6EchoMinimumSize},
|
||||
{header.ICMPv6EchoReply, header.ICMPv6EchoMinimumSize},
|
||||
{header.ICMPv6RouterSolicit, header.ICMPv6MinimumSize},
|
||||
{header.ICMPv6RouterAdvert, header.ICMPv6MinimumSize},
|
||||
{header.ICMPv6NeighborSolicit, header.ICMPv6NeighborSolicitMinimumSize},
|
||||
{header.ICMPv6NeighborAdvert, header.ICMPv6NeighborAdvertSize},
|
||||
{header.ICMPv6RedirectMsg, header.ICMPv6MinimumSize},
|
||||
}
|
||||
|
||||
handleIPv6Payload := func(hdr buffer.Prependable) {
|
||||
payloadLength := hdr.UsedLength()
|
||||
ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
|
||||
ip.Encode(&header.IPv6Fields{
|
||||
PayloadLength: uint16(payloadLength),
|
||||
NextHeader: uint8(header.ICMPv6ProtocolNumber),
|
||||
HopLimit: r.DefaultTTL(),
|
||||
SrcAddr: r.LocalAddress,
|
||||
DstAddr: r.RemoteAddress,
|
||||
})
|
||||
ep.HandlePacket(&r, hdr.View().ToVectorisedView())
|
||||
}
|
||||
|
||||
for _, typ := range types {
|
||||
hdr := buffer.NewPrependable(header.IPv6MinimumSize + typ.size)
|
||||
pkt := header.ICMPv6(hdr.Prepend(typ.size))
|
||||
pkt.SetType(typ.typ)
|
||||
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
|
||||
|
||||
handleIPv6Payload(hdr)
|
||||
}
|
||||
|
||||
// Construct an empty ICMP packet so that
|
||||
// Stats().ICMP.ICMPv6ReceivedPacketStats.Invalid is incremented.
|
||||
handleIPv6Payload(buffer.NewPrependable(header.IPv6MinimumSize))
|
||||
|
||||
icmpv6Stats := s.Stats().ICMP.V6PacketsReceived
|
||||
visitStats(reflect.ValueOf(&icmpv6Stats).Elem(), func(name string, s *tcpip.StatCounter) {
|
||||
if got, want := s.Value(), uint64(1); got != want {
|
||||
t.Errorf("got %s = %d, want = %d", name, got, want)
|
||||
}
|
||||
})
|
||||
if t.Failed() {
|
||||
t.Logf("stats:\n%+v", s.Stats())
|
||||
}
|
||||
}
|
||||
|
||||
func visitStats(v reflect.Value, f func(string, *tcpip.StatCounter)) {
|
||||
t := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
v := v.Field(i)
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
f(t.Field(i).Name, v.Interface().(*tcpip.StatCounter))
|
||||
case reflect.Struct:
|
||||
visitStats(v, f)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected type %s", v.Type()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testContext struct {
|
||||
t *testing.T
|
||||
s0 *stack.Stack
|
||||
s1 *stack.Stack
|
||||
|
||||
linkEP0 *channel.Endpoint
|
||||
linkEP1 *channel.Endpoint
|
||||
|
||||
icmpCh chan icmpInfo
|
||||
}
|
||||
|
||||
type endpointWithResolutionCapability struct {
|
||||
@@ -65,10 +197,8 @@ func (e endpointWithResolutionCapability) Capabilities() stack.LinkEndpointCapab
|
||||
|
||||
func newTestContext(t *testing.T) *testContext {
|
||||
c := &testContext{
|
||||
t: t,
|
||||
s0: stack.New([]string{ProtocolName}, []string{icmp.ProtocolName6}, stack.Options{}),
|
||||
s1: stack.New([]string{ProtocolName}, []string{icmp.ProtocolName6}, stack.Options{}),
|
||||
icmpCh: make(chan icmpInfo, 10),
|
||||
s0: stack.New([]string{ProtocolName}, []string{icmp.ProtocolName6}, stack.Options{}),
|
||||
s1: stack.New([]string{ProtocolName}, []string{icmp.ProtocolName6}, stack.Options{}),
|
||||
}
|
||||
|
||||
const defaultMTU = 65536
|
||||
@@ -118,50 +248,58 @@ func newTestContext(t *testing.T) *testContext {
|
||||
}},
|
||||
)
|
||||
|
||||
go c.routePackets(linkEP0.C, linkEP1)
|
||||
go c.routePackets(linkEP1.C, linkEP0)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *testContext) countPacket(pkt channel.PacketInfo) {
|
||||
if pkt.Proto != ProtocolNumber {
|
||||
return
|
||||
}
|
||||
ipv6 := header.IPv6(pkt.Header)
|
||||
transProto := tcpip.TransportProtocolNumber(ipv6.NextHeader())
|
||||
if transProto != header.ICMPv6ProtocolNumber {
|
||||
return
|
||||
}
|
||||
b := pkt.Header[header.IPv6MinimumSize:]
|
||||
icmp := header.ICMPv6(b)
|
||||
c.icmpCh <- icmpInfo{
|
||||
typ: icmp.Type(),
|
||||
src: ipv6.SourceAddress(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *testContext) routePackets(ch <-chan channel.PacketInfo, ep *channel.Endpoint) {
|
||||
for pkt := range ch {
|
||||
c.countPacket(pkt)
|
||||
views := []buffer.View{pkt.Header, pkt.Payload}
|
||||
size := len(pkt.Header) + len(pkt.Payload)
|
||||
vv := buffer.NewVectorisedView(size, views)
|
||||
ep.InjectLinkAddr(pkt.Proto, ep.LinkAddress(), vv)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *testContext) cleanup() {
|
||||
close(c.linkEP0.C)
|
||||
close(c.linkEP1.C)
|
||||
}
|
||||
|
||||
type routeArgs struct {
|
||||
src, dst *channel.Endpoint
|
||||
typ header.ICMPv6Type
|
||||
}
|
||||
|
||||
func routeICMPv6Packet(t *testing.T, args routeArgs, fn func(*testing.T, header.ICMPv6)) {
|
||||
t.Helper()
|
||||
|
||||
pkt := <-args.src.C
|
||||
|
||||
{
|
||||
views := []buffer.View{pkt.Header, pkt.Payload}
|
||||
size := len(pkt.Header) + len(pkt.Payload)
|
||||
vv := buffer.NewVectorisedView(size, views)
|
||||
args.dst.InjectLinkAddr(pkt.Proto, args.dst.LinkAddress(), vv)
|
||||
}
|
||||
|
||||
if pkt.Proto != ProtocolNumber {
|
||||
t.Errorf("unexpected protocol number %d", pkt.Proto)
|
||||
return
|
||||
}
|
||||
ipv6 := header.IPv6(pkt.Header)
|
||||
transProto := tcpip.TransportProtocolNumber(ipv6.NextHeader())
|
||||
if transProto != header.ICMPv6ProtocolNumber {
|
||||
t.Errorf("unexpected transport protocol number %d", transProto)
|
||||
return
|
||||
}
|
||||
icmpv6 := header.ICMPv6(ipv6.Payload())
|
||||
if got, want := icmpv6.Type(), args.typ; got != want {
|
||||
t.Errorf("got ICMPv6 type = %d, want = %d", got, want)
|
||||
return
|
||||
}
|
||||
if fn != nil {
|
||||
fn(t, icmpv6)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkResolution(t *testing.T) {
|
||||
c := newTestContext(t)
|
||||
defer c.cleanup()
|
||||
|
||||
r, err := c.s0.FindRoute(1, lladdr0, lladdr1, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("FindRoute(_) = _, %s, want = _, nil", err)
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
@@ -176,14 +314,24 @@ func TestLinkResolution(t *testing.T) {
|
||||
var wq waiter.Queue
|
||||
ep, err := c.s0.NewEndpoint(header.ICMPv6ProtocolNumber, ProtocolNumber, &wq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("NewEndpoint(_) = _, %s, want = _, nil", err)
|
||||
}
|
||||
|
||||
for {
|
||||
_, resCh, err := ep.Write(payload, tcpip.WriteOptions{To: &tcpip.FullAddress{NIC: 1, Addr: lladdr1}})
|
||||
if resCh != nil {
|
||||
if err != tcpip.ErrNoLinkAddress {
|
||||
t.Fatalf("ep.Write(_) = _, <non-nil>, %s want _, <non-nil>, tcpip.ErrNoLinkAddress", err)
|
||||
t.Fatalf("ep.Write(_) = _, <non-nil>, %s, want = _, <non-nil>, tcpip.ErrNoLinkAddress", err)
|
||||
}
|
||||
for _, args := range []routeArgs{
|
||||
{src: c.linkEP0, dst: c.linkEP1, typ: header.ICMPv6NeighborSolicit},
|
||||
{src: c.linkEP1, dst: c.linkEP0, typ: header.ICMPv6NeighborAdvert},
|
||||
} {
|
||||
routeICMPv6Packet(t, args, func(t *testing.T, icmpv6 header.ICMPv6) {
|
||||
if got, want := tcpip.Address(icmpv6[8:][:16]), lladdr1; got != want {
|
||||
t.Errorf("%d: got target = %s, want = %s", icmpv6.Type(), got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
<-resCh
|
||||
continue
|
||||
@@ -194,29 +342,10 @@ func TestLinkResolution(t *testing.T) {
|
||||
break
|
||||
}
|
||||
|
||||
stats := make(map[header.ICMPv6Type]int)
|
||||
for {
|
||||
// This actually takes about 10 milliseconds, so no need to wait for
|
||||
// a multi-minute go test timeout if something is broken.
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Errorf("timeout waiting for ICMP, got: %#+v", stats)
|
||||
return
|
||||
case icmpInfo := <-c.icmpCh:
|
||||
switch icmpInfo.typ {
|
||||
case header.ICMPv6NeighborAdvert:
|
||||
if got, want := icmpInfo.src, lladdr1; got != want {
|
||||
t.Errorf("got ICMPv6NeighborAdvert.sourceAddress = %v, want = %v", got, want)
|
||||
}
|
||||
}
|
||||
stats[icmpInfo.typ]++
|
||||
|
||||
if stats[header.ICMPv6NeighborSolicit] > 0 &&
|
||||
stats[header.ICMPv6NeighborAdvert] > 0 &&
|
||||
stats[header.ICMPv6EchoRequest] > 0 &&
|
||||
stats[header.ICMPv6EchoReply] > 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, args := range []routeArgs{
|
||||
{src: c.linkEP0, dst: c.linkEP1, typ: header.ICMPv6EchoRequest},
|
||||
{src: c.linkEP1, dst: c.linkEP0, typ: header.ICMPv6EchoReply},
|
||||
} {
|
||||
routeICMPv6Packet(t, args, nil)
|
||||
}
|
||||
}
|
||||
|
||||
+159
-10
@@ -581,10 +581,156 @@ func (s *StatCounter) String() string {
|
||||
return strconv.FormatUint(s.Value(), 10)
|
||||
}
|
||||
|
||||
// ICMPv4PacketStats enumerates counts for all ICMPv4 packet types.
|
||||
type ICMPv4PacketStats struct {
|
||||
// Echo is the total number of ICMPv4 echo packets counted.
|
||||
Echo *StatCounter
|
||||
|
||||
// EchoReply is the total number of ICMPv4 echo reply packets counted.
|
||||
EchoReply *StatCounter
|
||||
|
||||
// DstUnreachable is the total number of ICMPv4 destination unreachable
|
||||
// packets counted.
|
||||
DstUnreachable *StatCounter
|
||||
|
||||
// SrcQuench is the total number of ICMPv4 source quench packets
|
||||
// counted.
|
||||
SrcQuench *StatCounter
|
||||
|
||||
// Redirect is the total number of ICMPv4 redirect packets counted.
|
||||
Redirect *StatCounter
|
||||
|
||||
// TimeExceeded is the total number of ICMPv4 time exceeded packets
|
||||
// counted.
|
||||
TimeExceeded *StatCounter
|
||||
|
||||
// ParamProblem is the total number of ICMPv4 parameter problem packets
|
||||
// counted.
|
||||
ParamProblem *StatCounter
|
||||
|
||||
// Timestamp is the total number of ICMPv4 timestamp packets counted.
|
||||
Timestamp *StatCounter
|
||||
|
||||
// TimestampReply is the total number of ICMPv4 timestamp reply packets
|
||||
// counted.
|
||||
TimestampReply *StatCounter
|
||||
|
||||
// InfoRequest is the total number of ICMPv4 information request
|
||||
// packets counted.
|
||||
InfoRequest *StatCounter
|
||||
|
||||
// InfoReply is the total number of ICMPv4 information reply packets
|
||||
// counted.
|
||||
InfoReply *StatCounter
|
||||
}
|
||||
|
||||
// ICMPv6PacketStats enumerates counts for all ICMPv6 packet types.
|
||||
type ICMPv6PacketStats struct {
|
||||
// EchoRequest is the total number of ICMPv6 echo request packets
|
||||
// counted.
|
||||
EchoRequest *StatCounter
|
||||
|
||||
// EchoReply is the total number of ICMPv6 echo reply packets counted.
|
||||
EchoReply *StatCounter
|
||||
|
||||
// DstUnreachable is the total number of ICMPv6 destination unreachable
|
||||
// packets counted.
|
||||
DstUnreachable *StatCounter
|
||||
|
||||
// PacketTooBig is the total number of ICMPv6 packet too big packets
|
||||
// counted.
|
||||
PacketTooBig *StatCounter
|
||||
|
||||
// TimeExceeded is the total number of ICMPv6 time exceeded packets
|
||||
// counted.
|
||||
TimeExceeded *StatCounter
|
||||
|
||||
// ParamProblem is the total number of ICMPv6 parameter problem packets
|
||||
// counted.
|
||||
ParamProblem *StatCounter
|
||||
|
||||
// RouterSolicit is the total number of ICMPv6 router solicit packets
|
||||
// counted.
|
||||
RouterSolicit *StatCounter
|
||||
|
||||
// RouterAdvert is the total number of ICMPv6 router advert packets
|
||||
// counted.
|
||||
RouterAdvert *StatCounter
|
||||
|
||||
// NeighborSolicit is the total number of ICMPv6 neighbor solicit
|
||||
// packets counted.
|
||||
NeighborSolicit *StatCounter
|
||||
|
||||
// NeighborAdvert is the total number of ICMPv6 neighbor advert packets
|
||||
// counted.
|
||||
NeighborAdvert *StatCounter
|
||||
|
||||
// RedirectMsg is the total number of ICMPv6 redirect message packets
|
||||
// counted.
|
||||
RedirectMsg *StatCounter
|
||||
}
|
||||
|
||||
// ICMPv4SentPacketStats collects outbound ICMPv4-specific stats.
|
||||
type ICMPv4SentPacketStats struct {
|
||||
ICMPv4PacketStats
|
||||
|
||||
// Dropped is the total number of ICMPv4 packets dropped due to link
|
||||
// layer errors.
|
||||
Dropped *StatCounter
|
||||
}
|
||||
|
||||
// ICMPv4ReceivedPacketStats collects inbound ICMPv4-specific stats.
|
||||
type ICMPv4ReceivedPacketStats struct {
|
||||
ICMPv4PacketStats
|
||||
|
||||
// Invalid is the total number of ICMPv4 packets received that the
|
||||
// transport layer could not parse.
|
||||
Invalid *StatCounter
|
||||
}
|
||||
|
||||
// ICMPv6SentPacketStats collects outbound ICMPv6-specific stats.
|
||||
type ICMPv6SentPacketStats struct {
|
||||
ICMPv6PacketStats
|
||||
|
||||
// Dropped is the total number of ICMPv6 packets dropped due to link
|
||||
// layer errors.
|
||||
Dropped *StatCounter
|
||||
}
|
||||
|
||||
// ICMPv6ReceivedPacketStats collects inbound ICMPv6-specific stats.
|
||||
type ICMPv6ReceivedPacketStats struct {
|
||||
ICMPv6PacketStats
|
||||
|
||||
// Invalid is the total number of ICMPv6 packets received that the
|
||||
// transport layer could not parse.
|
||||
Invalid *StatCounter
|
||||
}
|
||||
|
||||
// ICMPStats collects ICMP-specific stats (both v4 and v6).
|
||||
type ICMPStats struct {
|
||||
// ICMPv4SentPacketStats contains counts of sent packets by ICMPv4 packet type
|
||||
// and a single count of packets which failed to write to the link
|
||||
// layer.
|
||||
V4PacketsSent ICMPv4SentPacketStats
|
||||
|
||||
// ICMPv4ReceivedPacketStats contains counts of received packets by ICMPv4
|
||||
// packet type and a single count of invalid packets received.
|
||||
V4PacketsReceived ICMPv4ReceivedPacketStats
|
||||
|
||||
// ICMPv6SentPacketStats contains counts of sent packets by ICMPv6 packet type
|
||||
// and a single count of packets which failed to write to the link
|
||||
// layer.
|
||||
V6PacketsSent ICMPv6SentPacketStats
|
||||
|
||||
// ICMPv6ReceivedPacketStats contains counts of received packets by ICMPv6
|
||||
// packet type and a single count of invalid packets received.
|
||||
V6PacketsReceived ICMPv6ReceivedPacketStats
|
||||
}
|
||||
|
||||
// IPStats collects IP-specific stats (both v4 and v6).
|
||||
type IPStats struct {
|
||||
// PacketsReceived is the total number of IP packets received from the link
|
||||
// layer in nic.DeliverNetworkPacket.
|
||||
// PacketsReceived is the total number of IP packets received from the
|
||||
// link layer in nic.DeliverNetworkPacket.
|
||||
PacketsReceived *StatCounter
|
||||
|
||||
// InvalidAddressesReceived is the total number of IP packets received
|
||||
@@ -605,8 +751,8 @@ type IPStats struct {
|
||||
|
||||
// TCPStats collects TCP-specific stats.
|
||||
type TCPStats struct {
|
||||
// ActiveConnectionOpenings is the number of connections opened successfully
|
||||
// via Connect.
|
||||
// ActiveConnectionOpenings is the number of connections opened
|
||||
// successfully via Connect.
|
||||
ActiveConnectionOpenings *StatCounter
|
||||
|
||||
// PassiveConnectionOpenings is the number of connections opened
|
||||
@@ -617,8 +763,8 @@ type TCPStats struct {
|
||||
// (active and passive openings, respectively) that end in an error.
|
||||
FailedConnectionAttempts *StatCounter
|
||||
|
||||
// ValidSegmentsReceived is the number of TCP segments received that the
|
||||
// transport layer successfully parsed.
|
||||
// ValidSegmentsReceived is the number of TCP segments received that
|
||||
// the transport layer successfully parsed.
|
||||
ValidSegmentsReceived *StatCounter
|
||||
|
||||
// InvalidSegmentsReceived is the number of TCP segments received that
|
||||
@@ -694,6 +840,9 @@ type Stats struct {
|
||||
// DroppedPackets is the number of packets dropped due to full queues.
|
||||
DroppedPackets *StatCounter
|
||||
|
||||
// ICMP breaks out ICMP-specific stats (both v4 and v6).
|
||||
ICMP ICMPStats
|
||||
|
||||
// IP breaks out IP-specific stats (both v4 and v6).
|
||||
IP IPStats
|
||||
|
||||
@@ -709,13 +858,13 @@ func fillIn(v reflect.Value) {
|
||||
v := v.Field(i)
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
if s, ok := v.Addr().Interface().(**StatCounter); ok {
|
||||
if *s == nil {
|
||||
*s = &StatCounter{}
|
||||
}
|
||||
if s := v.Addr().Interface().(**StatCounter); *s == nil {
|
||||
*s = &StatCounter{}
|
||||
}
|
||||
case reflect.Struct:
|
||||
fillIn(v)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected type %s", v.Type()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user