Add ICMP stats

PiperOrigin-RevId: 240848882
Change-Id: I23dd4599f073263437aeab357c3f767e1a432b82
This commit is contained in:
Bert Muthalaly
2019-03-28 14:09:20 -07:00
committed by Shentubot
parent e373d3642e
commit f2e5dcf21c
6 changed files with 529 additions and 95 deletions
+66
View File
@@ -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."),
+9 -6
View File
@@ -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) {
+43 -2
View File
@@ -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.
}
+55 -9
View File
@@ -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)
}
+197 -68
View File
@@ -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
View File
@@ -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()))
}
}
}