summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/socket/epsocket/epsocket.go66
-rw-r--r--pkg/tcpip/network/ip_test.go15
-rw-r--r--pkg/tcpip/network/ipv4/icmp.go45
-rw-r--r--pkg/tcpip/network/ipv6/icmp.go58
-rw-r--r--pkg/tcpip/network/ipv6/icmp_test.go251
-rw-r--r--pkg/tcpip/tcpip.go169
6 files changed, 519 insertions, 85 deletions
diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go
index e74bd1bdd..e170da169 100644
--- a/pkg/sentry/socket/epsocket/epsocket.go
+++ b/pkg/sentry/socket/epsocket/epsocket.go
@@ -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."),
diff --git a/pkg/tcpip/network/ip_test.go b/pkg/tcpip/network/ip_test.go
index d79eba4b0..522009fac 100644
--- a/pkg/tcpip/network/ip_test.go
+++ b/pkg/tcpip/network/ip_test.go
@@ -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) {
diff --git a/pkg/tcpip/network/ipv4/icmp.go b/pkg/tcpip/network/ipv4/icmp.go
index a9650de03..ed9a4eee5 100644
--- a/pkg/tcpip/network/ipv4/icmp.go
+++ b/pkg/tcpip/network/ipv4/icmp.go
@@ -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.
}
diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go
index 36d98caef..3210e6fc7 100644
--- a/pkg/tcpip/network/ipv6/icmp.go
+++ b/pkg/tcpip/network/ipv6/icmp.go
@@ -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)
+ if err := r.WritePacket(nil /* gso */, hdr, buffer.VectorisedView{}, header.ICMPv6ProtocolNumber, r.DefaultTTL()); err != nil {
+ sent.Dropped.Increment()
+ return
+ }
+ sent.NeighborAdvert.Increment()
case header.ICMPv6NeighborAdvert:
+ received.NeighborAdvert.Increment()
if len(v) < header.ICMPv6NeighborAdvertSize {
+ received.Invalid.Increment()
return
}
- targetAddr := tcpip.Address(v[8 : 8+16])
+ 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)
}
diff --git a/pkg/tcpip/network/ipv6/icmp_test.go b/pkg/tcpip/network/ipv6/icmp_test.go
index eee09f3af..8b57a0641 100644
--- a/pkg/tcpip/network/ipv6/icmp_test.go
+++ b/pkg/tcpip/network/ipv6/icmp_test.go
@@ -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) {
+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
}
- b := pkt.Header[header.IPv6MinimumSize:]
- icmp := header.ICMPv6(b)
- c.icmpCh <- icmpInfo{
- typ: icmp.Type(),
- src: ipv6.SourceAddress(),
+ 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
}
-}
-
-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)
+ if fn != nil {
+ fn(t, icmpv6)
}
}
-func (c *testContext) cleanup() {
- close(c.linkEP0.C)
- close(c.linkEP1.C)
-}
-
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)
}
}
diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go
index 825854148..e9f73635f 100644
--- a/pkg/tcpip/tcpip.go
+++ b/pkg/tcpip/tcpip.go
@@ -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()))
}
}
}