Add various statistics

PiperOrigin-RevId: 210442599
Change-Id: I9498351f461dc69c77b7f815d526c5693bec8e4a
This commit is contained in:
Tamir Duberstein
2018-08-27 15:29:55 -07:00
committed by Shentubot
parent 0b3bfe2ea3
commit 0923bcf06b
16 changed files with 703 additions and 225 deletions
+15 -5
View File
@@ -118,7 +118,7 @@ func NewWithFile(lower tcpip.LinkEndpointID, file *os.File, snapLen uint32) (tcp
// logs the packet before forwarding to the actual dispatcher.
func (e *endpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remoteLinkAddr tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv *buffer.VectorisedView) {
if atomic.LoadUint32(&LogPackets) == 1 && e.file == nil {
LogPacket("recv", protocol, vv.First(), nil)
logPacket("recv", protocol, vv.First(), nil)
}
if e.file != nil && atomic.LoadUint32(&LogPacketsToFile) == 1 {
vs := vv.Views()
@@ -190,7 +190,7 @@ func (e *endpoint) LinkAddress() tcpip.LinkAddress {
// the request to the lower endpoint.
func (e *endpoint) WritePacket(r *stack.Route, hdr *buffer.Prependable, payload buffer.View, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
if atomic.LoadUint32(&LogPackets) == 1 && e.file == nil {
LogPacket("send", protocol, hdr.UsedBytes(), payload)
logPacket("send", protocol, hdr.UsedBytes(), payload)
}
if e.file != nil && atomic.LoadUint32(&LogPacketsToFile) == 1 {
hdrBuf := hdr.UsedBytes()
@@ -226,8 +226,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr *buffer.Prependable, payload
return e.lower.WritePacket(r, hdr, payload, protocol)
}
// LogPacket logs the given packet.
func LogPacket(prefix string, protocol tcpip.NetworkProtocolNumber, b, plb []byte) {
func logPacket(prefix string, protocol tcpip.NetworkProtocolNumber, b, plb []byte) {
// Figure out the network layer info.
var transProto uint8
src := tcpip.Address("unknown")
@@ -316,9 +315,19 @@ func LogPacket(prefix string, protocol tcpip.NetworkProtocolNumber, b, plb []byt
case header.TCPProtocolNumber:
transName = "tcp"
tcp := header.TCP(b)
offset := int(tcp.DataOffset())
if offset < header.TCPMinimumSize {
details += fmt.Sprintf("invalid packet: tcp data offset too small %d", offset)
break
}
if offset > len(tcp) {
details += fmt.Sprintf("invalid packet: tcp data offset %d larger than packet buffer length %d", offset, len(tcp))
break
}
srcPort = tcp.SourcePort()
dstPort = tcp.DestinationPort()
size -= uint16(tcp.DataOffset())
size -= uint16(offset)
// Initialize the TCP flags.
flags := tcp.Flags()
@@ -334,6 +343,7 @@ func LogPacket(prefix string, protocol tcpip.NetworkProtocolNumber, b, plb []byt
} else {
details += fmt.Sprintf(" options: %+v", tcp.ParsedOptions())
}
default:
log.Infof("%s %v -> %v unknown transport protocol: %d", prefix, src, dst, transProto)
return
+3
View File
@@ -12,8 +12,11 @@ go_test(
"//pkg/tcpip",
"//pkg/tcpip/buffer",
"//pkg/tcpip/header",
"//pkg/tcpip/link/loopback",
"//pkg/tcpip/network/ipv4",
"//pkg/tcpip/network/ipv6",
"//pkg/tcpip/stack",
"//pkg/tcpip/transport/tcp",
"//pkg/tcpip/transport/udp",
],
)
+97 -52
View File
@@ -20,9 +20,25 @@ import (
"gvisor.googlesource.com/gvisor/pkg/tcpip"
"gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
"gvisor.googlesource.com/gvisor/pkg/tcpip/header"
"gvisor.googlesource.com/gvisor/pkg/tcpip/link/loopback"
"gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4"
"gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv6"
"gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/tcp"
"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/udp"
)
const (
localIpv4Addr = "\x0a\x00\x00\x01"
remoteIpv4Addr = "\x0a\x00\x00\x02"
ipv4SubnetAddr = "\x0a\x00\x00\x00"
ipv4SubnetMask = "\xff\xff\xff\x00"
ipv4Gateway = "\x0a\x00\x00\x03"
localIpv6Addr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
remoteIpv6Addr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
ipv6SubnetAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
ipv6SubnetMask = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00"
ipv6Gateway = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03"
)
// testObject implements two interfaces: LinkEndpoint and TransportDispatcher.
@@ -152,10 +168,38 @@ func (t *testObject) WritePacket(_ *stack.Route, hdr *buffer.Prependable, payloa
return nil
}
func buildIPv4Route(local, remote tcpip.Address) (stack.Route, *tcpip.Error) {
s := stack.New([]string{ipv4.ProtocolName}, []string{udp.ProtocolName, tcp.ProtocolName}, stack.Options{})
s.CreateNIC(1, loopback.New())
s.AddAddress(1, ipv4.ProtocolNumber, local)
s.SetRouteTable([]tcpip.Route{{
Destination: ipv4SubnetAddr,
Mask: ipv4SubnetMask,
Gateway: ipv4Gateway,
NIC: 1,
}})
return s.FindRoute(1, local, remote, ipv4.ProtocolNumber)
}
func buildIPv6Route(local, remote tcpip.Address) (stack.Route, *tcpip.Error) {
s := stack.New([]string{ipv6.ProtocolName}, []string{udp.ProtocolName, tcp.ProtocolName}, stack.Options{})
s.CreateNIC(1, loopback.New())
s.AddAddress(1, ipv6.ProtocolNumber, local)
s.SetRouteTable([]tcpip.Route{{
Destination: ipv6SubnetAddr,
Mask: ipv6SubnetMask,
Gateway: ipv6Gateway,
NIC: 1,
}})
return s.FindRoute(1, local, remote, ipv6.ProtocolNumber)
}
func TestIPv4Send(t *testing.T) {
o := testObject{t: t, v4: true}
proto := ipv4.NewProtocol()
ep, err := proto.NewEndpoint(1, "\x0a\x00\x00\x01", nil, nil, &o)
ep, err := proto.NewEndpoint(1, localIpv4Addr, nil, nil, &o)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
@@ -171,13 +215,13 @@ func TestIPv4Send(t *testing.T) {
// Issue the write.
o.protocol = 123
o.srcAddr = "\x0a\x00\x00\x01"
o.dstAddr = "\x0a\x00\x00\x02"
o.srcAddr = localIpv4Addr
o.dstAddr = remoteIpv4Addr
o.contents = payload
r := stack.Route{
RemoteAddress: o.dstAddr,
LocalAddress: o.srcAddr,
r, err := buildIPv4Route(localIpv4Addr, remoteIpv4Addr)
if err != nil {
t.Fatalf("could not find route: %v", err)
}
if err := ep.WritePacket(&r, &hdr, payload, 123); err != nil {
t.Fatalf("WritePacket failed: %v", err)
@@ -187,7 +231,7 @@ func TestIPv4Send(t *testing.T) {
func TestIPv4Receive(t *testing.T) {
o := testObject{t: t, v4: true}
proto := ipv4.NewProtocol()
ep, err := proto.NewEndpoint(1, "\x0a\x00\x00\x01", nil, &o, nil)
ep, err := proto.NewEndpoint(1, localIpv4Addr, nil, &o, nil)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
@@ -200,8 +244,8 @@ func TestIPv4Receive(t *testing.T) {
TotalLength: uint16(totalLen),
TTL: 20,
Protocol: 10,
SrcAddr: "\x0a\x00\x00\x02",
DstAddr: "\x0a\x00\x00\x01",
SrcAddr: remoteIpv4Addr,
DstAddr: localIpv4Addr,
})
// Make payload be non-zero.
@@ -211,13 +255,13 @@ func TestIPv4Receive(t *testing.T) {
// Give packet to ipv4 endpoint, dispatcher will validate that it's ok.
o.protocol = 10
o.srcAddr = "\x0a\x00\x00\x02"
o.dstAddr = "\x0a\x00\x00\x01"
o.srcAddr = remoteIpv4Addr
o.dstAddr = localIpv4Addr
o.contents = view[header.IPv4MinimumSize:totalLen]
r := stack.Route{
LocalAddress: o.dstAddr,
RemoteAddress: o.srcAddr,
r, err := buildIPv4Route(localIpv4Addr, remoteIpv4Addr)
if err != nil {
t.Fatalf("could not find route: %v", err)
}
var views [1]buffer.View
vv := view.ToVectorisedView(views)
@@ -248,7 +292,7 @@ func TestIPv4ReceiveControl(t *testing.T) {
{"Zero-length packet", 0, 0, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 2*header.IPv4MinimumSize + header.ICMPv4DstUnreachableMinimumSize + 8},
}
r := stack.Route{
LocalAddress: "\x0a\x00\x00\x01",
LocalAddress: localIpv4Addr,
RemoteAddress: "\x0a\x00\x00\xbb",
}
for _, c := range cases {
@@ -256,7 +300,7 @@ func TestIPv4ReceiveControl(t *testing.T) {
var views [1]buffer.View
o := testObject{t: t}
proto := ipv4.NewProtocol()
ep, err := proto.NewEndpoint(1, "\x0a\x00\x00\x01", nil, &o, nil)
ep, err := proto.NewEndpoint(1, localIpv4Addr, nil, &o, nil)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
@@ -273,7 +317,7 @@ func TestIPv4ReceiveControl(t *testing.T) {
TTL: 20,
Protocol: uint8(header.ICMPv4ProtocolNumber),
SrcAddr: "\x0a\x00\x00\xbb",
DstAddr: "\x0a\x00\x00\x01",
DstAddr: localIpv4Addr,
})
// Create the ICMP header.
@@ -290,8 +334,8 @@ func TestIPv4ReceiveControl(t *testing.T) {
TTL: 20,
Protocol: 10,
FragmentOffset: c.fragmentOffset,
SrcAddr: "\x0a\x00\x00\x01",
DstAddr: "\x0a\x00\x00\x02",
SrcAddr: localIpv4Addr,
DstAddr: remoteIpv4Addr,
})
// Make payload be non-zero.
@@ -302,8 +346,8 @@ func TestIPv4ReceiveControl(t *testing.T) {
// Give packet to IPv4 endpoint, dispatcher will validate that
// it's ok.
o.protocol = 10
o.srcAddr = "\x0a\x00\x00\x02"
o.dstAddr = "\x0a\x00\x00\x01"
o.srcAddr = remoteIpv4Addr
o.dstAddr = localIpv4Addr
o.contents = view[dataOffset:]
o.typ = c.expectedTyp
o.extra = c.expectedExtra
@@ -321,7 +365,7 @@ func TestIPv4ReceiveControl(t *testing.T) {
func TestIPv4FragmentationReceive(t *testing.T) {
o := testObject{t: t, v4: true}
proto := ipv4.NewProtocol()
ep, err := proto.NewEndpoint(1, "\x0a\x00\x00\x01", nil, &o, nil)
ep, err := proto.NewEndpoint(1, localIpv4Addr, nil, &o, nil)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
@@ -337,8 +381,8 @@ func TestIPv4FragmentationReceive(t *testing.T) {
Protocol: 10,
FragmentOffset: 0,
Flags: header.IPv4FlagMoreFragments,
SrcAddr: "\x0a\x00\x00\x02",
DstAddr: "\x0a\x00\x00\x01",
SrcAddr: remoteIpv4Addr,
DstAddr: localIpv4Addr,
})
// Make payload be non-zero.
for i := header.IPv4MinimumSize; i < totalLen; i++ {
@@ -353,8 +397,8 @@ func TestIPv4FragmentationReceive(t *testing.T) {
TTL: 20,
Protocol: 10,
FragmentOffset: 24,
SrcAddr: "\x0a\x00\x00\x02",
DstAddr: "\x0a\x00\x00\x01",
SrcAddr: remoteIpv4Addr,
DstAddr: localIpv4Addr,
})
// Make payload be non-zero.
for i := header.IPv4MinimumSize; i < totalLen; i++ {
@@ -363,13 +407,13 @@ func TestIPv4FragmentationReceive(t *testing.T) {
// Give packet to ipv4 endpoint, dispatcher will validate that it's ok.
o.protocol = 10
o.srcAddr = "\x0a\x00\x00\x02"
o.dstAddr = "\x0a\x00\x00\x01"
o.srcAddr = remoteIpv4Addr
o.dstAddr = localIpv4Addr
o.contents = append(frag1[header.IPv4MinimumSize:totalLen], frag2[header.IPv4MinimumSize:totalLen]...)
r := stack.Route{
LocalAddress: o.dstAddr,
RemoteAddress: o.srcAddr,
r, err := buildIPv4Route(localIpv4Addr, remoteIpv4Addr)
if err != nil {
t.Fatalf("could not find route: %v", err)
}
// Send first segment.
@@ -393,7 +437,7 @@ func TestIPv4FragmentationReceive(t *testing.T) {
func TestIPv6Send(t *testing.T) {
o := testObject{t: t}
proto := ipv6.NewProtocol()
ep, err := proto.NewEndpoint(1, "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", nil, nil, &o)
ep, err := proto.NewEndpoint(1, localIpv6Addr, nil, nil, &o)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
@@ -409,13 +453,13 @@ func TestIPv6Send(t *testing.T) {
// Issue the write.
o.protocol = 123
o.srcAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
o.dstAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
o.srcAddr = localIpv6Addr
o.dstAddr = remoteIpv6Addr
o.contents = payload
r := stack.Route{
RemoteAddress: o.dstAddr,
LocalAddress: o.srcAddr,
r, err := buildIPv6Route(localIpv6Addr, remoteIpv6Addr)
if err != nil {
t.Fatalf("could not find route: %v", err)
}
if err := ep.WritePacket(&r, &hdr, payload, 123); err != nil {
t.Fatalf("WritePacket failed: %v", err)
@@ -425,7 +469,7 @@ func TestIPv6Send(t *testing.T) {
func TestIPv6Receive(t *testing.T) {
o := testObject{t: t}
proto := ipv6.NewProtocol()
ep, err := proto.NewEndpoint(1, "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", nil, &o, nil)
ep, err := proto.NewEndpoint(1, localIpv6Addr, nil, &o, nil)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
@@ -437,8 +481,8 @@ func TestIPv6Receive(t *testing.T) {
PayloadLength: uint16(totalLen - header.IPv6MinimumSize),
NextHeader: 10,
HopLimit: 20,
SrcAddr: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02",
DstAddr: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
SrcAddr: remoteIpv6Addr,
DstAddr: localIpv6Addr,
})
// Make payload be non-zero.
@@ -448,14 +492,15 @@ func TestIPv6Receive(t *testing.T) {
// Give packet to ipv6 endpoint, dispatcher will validate that it's ok.
o.protocol = 10
o.srcAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
o.dstAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
o.srcAddr = remoteIpv6Addr
o.dstAddr = localIpv6Addr
o.contents = view[header.IPv6MinimumSize:totalLen]
r := stack.Route{
LocalAddress: o.dstAddr,
RemoteAddress: o.srcAddr,
r, err := buildIPv6Route(localIpv6Addr, remoteIpv6Addr)
if err != nil {
t.Fatalf("could not find route: %v", err)
}
var views [1]buffer.View
vv := view.ToVectorisedView(views)
ep.HandlePacket(&r, &vv)
@@ -491,7 +536,7 @@ func TestIPv6ReceiveControl(t *testing.T) {
{"Zero-length packet", 0, nil, header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 2*header.IPv6MinimumSize + header.ICMPv6DstUnreachableMinimumSize + 8},
}
r := stack.Route{
LocalAddress: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
LocalAddress: localIpv6Addr,
RemoteAddress: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa",
}
for _, c := range cases {
@@ -499,7 +544,7 @@ func TestIPv6ReceiveControl(t *testing.T) {
var views [1]buffer.View
o := testObject{t: t}
proto := ipv6.NewProtocol()
ep, err := proto.NewEndpoint(1, "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", nil, &o, nil)
ep, err := proto.NewEndpoint(1, localIpv6Addr, nil, &o, nil)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
@@ -519,7 +564,7 @@ func TestIPv6ReceiveControl(t *testing.T) {
NextHeader: uint8(header.ICMPv6ProtocolNumber),
HopLimit: 20,
SrcAddr: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa",
DstAddr: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
DstAddr: localIpv6Addr,
})
// Create the ICMP header.
@@ -534,8 +579,8 @@ func TestIPv6ReceiveControl(t *testing.T) {
PayloadLength: 100,
NextHeader: 10,
HopLimit: 20,
SrcAddr: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
DstAddr: "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02",
SrcAddr: localIpv6Addr,
DstAddr: remoteIpv6Addr,
})
// Build the fragmentation header if needed.
@@ -558,8 +603,8 @@ func TestIPv6ReceiveControl(t *testing.T) {
// Give packet to IPv6 endpoint, dispatcher will validate that
// it's ok.
o.protocol = 10
o.srcAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
o.dstAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
o.srcAddr = remoteIpv6Addr
o.dstAddr = localIpv6Addr
o.contents = view[dataOffset:]
o.typ = c.expectedTyp
o.extra = c.expectedExtra
+2
View File
@@ -121,6 +121,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr *buffer.Prependable, payload
DstAddr: r.RemoteAddress,
})
ip.SetChecksum(^ip.CalculateChecksum())
r.Stats().IP.PacketsSent.Increment()
return e.linkEP.WritePacket(r, hdr, payload, ProtocolNumber)
}
@@ -153,6 +154,7 @@ func (e *endpoint) HandlePacket(r *stack.Route, vv *buffer.VectorisedView) {
e.handleICMP(r, vv)
return
}
r.Stats().IP.PacketsDelivered.Increment()
e.dispatcher.DeliverTransportPacket(r, p, vv)
}
+2
View File
@@ -97,6 +97,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr *buffer.Prependable, payload
SrcAddr: tcpip.Address(e.address[:]),
DstAddr: r.RemoteAddress,
})
r.Stats().IP.PacketsSent.Increment()
return e.linkEP.WritePacket(r, hdr, payload, ProtocolNumber)
}
@@ -118,6 +119,7 @@ func (e *endpoint) HandlePacket(r *stack.Route, vv *buffer.VectorisedView) {
return
}
r.Stats().IP.PacketsDelivered.Increment()
e.dispatcher.DeliverTransportPacket(r, p, vv)
}
+6 -1
View File
@@ -22,6 +22,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/ilist"
"gvisor.googlesource.com/gvisor/pkg/tcpip"
"gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
"gvisor.googlesource.com/gvisor/pkg/tcpip/header"
)
// NIC represents a "network interface card" to which the networking stack is
@@ -286,6 +287,10 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remoteLinkAddr tcpip.Lin
return
}
if netProto.Number() == header.IPv4ProtocolNumber || netProto.Number() == header.IPv6ProtocolNumber {
n.stack.stats.IP.PacketsReceived.Increment()
}
if len(vv.First()) < netProto.MinimumPacketSize() {
n.stack.stats.MalformedRcvdPackets.Increment()
return
@@ -330,7 +335,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remoteLinkAddr tcpip.Lin
}
if ref == nil {
n.stack.stats.UnknownNetworkEndpointRcvdPackets.Increment()
n.stack.stats.IP.InvalidAddressesReceived.Increment()
return
}
+10 -1
View File
@@ -70,6 +70,11 @@ func (r *Route) MaxHeaderLength() uint16 {
return r.ref.ep.MaxHeaderLength()
}
// Stats returns a mutable copy of current stats.
func (r *Route) Stats() tcpip.Stats {
return r.ref.nic.stack.Stats()
}
// PseudoHeaderChecksum forwards the call to the network endpoint's
// implementation.
func (r *Route) PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber) uint16 {
@@ -125,7 +130,11 @@ func (r *Route) IsResolutionRequired() bool {
// WritePacket writes the packet through the given route.
func (r *Route) WritePacket(hdr *buffer.Prependable, payload buffer.View, protocol tcpip.TransportProtocolNumber) *tcpip.Error {
return r.ref.ep.WritePacket(r, hdr, payload, protocol)
err := r.ref.ep.WritePacket(r, hdr, payload, protocol)
if err == tcpip.ErrNoRoute {
r.Stats().IP.OutgoingPacketErrors.Increment()
}
return err
}
// MTU returns the MTU of the underlying network endpoint.
+5
View File
@@ -19,6 +19,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/tcpip"
"gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
"gvisor.googlesource.com/gvisor/pkg/tcpip/header"
)
type protocolIDs struct {
@@ -111,6 +112,10 @@ func (d *transportDemuxer) deliverPacket(r *Route, protocol tcpip.TransportProto
// Fail if we didn't find one.
if ep == nil {
// UDP packet could not be delivered to an unknown destination port.
if protocol == header.UDPProtocolNumber {
r.Stats().UDP.UnknownPortErrors.Increment()
}
return false
}
+146 -52
View File
@@ -31,6 +31,7 @@ package tcpip
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"sync"
@@ -47,48 +48,56 @@ import (
// Note: to support save / restore, it is important that all tcpip errors have
// distinct error messages.
type Error struct {
string
msg string
ignoreStats bool
}
// String implements fmt.Stringer.String.
func (e *Error) String() string {
return e.string
return e.msg
}
// IgnoreStats indicates whether this error type should be included in failure
// counts in tcpip.Stats structs.
func (e *Error) IgnoreStats() bool {
return e.ignoreStats
}
// Errors that can be returned by the network stack.
var (
ErrUnknownProtocol = &Error{"unknown protocol"}
ErrUnknownNICID = &Error{"unknown nic id"}
ErrUnknownProtocolOption = &Error{"unknown option for protocol"}
ErrDuplicateNICID = &Error{"duplicate nic id"}
ErrDuplicateAddress = &Error{"duplicate address"}
ErrNoRoute = &Error{"no route"}
ErrBadLinkEndpoint = &Error{"bad link layer endpoint"}
ErrAlreadyBound = &Error{"endpoint already bound"}
ErrInvalidEndpointState = &Error{"endpoint is in invalid state"}
ErrAlreadyConnecting = &Error{"endpoint is already connecting"}
ErrAlreadyConnected = &Error{"endpoint is already connected"}
ErrNoPortAvailable = &Error{"no ports are available"}
ErrPortInUse = &Error{"port is in use"}
ErrBadLocalAddress = &Error{"bad local address"}
ErrClosedForSend = &Error{"endpoint is closed for send"}
ErrClosedForReceive = &Error{"endpoint is closed for receive"}
ErrWouldBlock = &Error{"operation would block"}
ErrConnectionRefused = &Error{"connection was refused"}
ErrTimeout = &Error{"operation timed out"}
ErrAborted = &Error{"operation aborted"}
ErrConnectStarted = &Error{"connection attempt started"}
ErrDestinationRequired = &Error{"destination address is required"}
ErrNotSupported = &Error{"operation not supported"}
ErrQueueSizeNotSupported = &Error{"queue size querying not supported"}
ErrNotConnected = &Error{"endpoint not connected"}
ErrConnectionReset = &Error{"connection reset by peer"}
ErrConnectionAborted = &Error{"connection aborted"}
ErrNoSuchFile = &Error{"no such file"}
ErrInvalidOptionValue = &Error{"invalid option value specified"}
ErrNoLinkAddress = &Error{"no remote link address"}
ErrBadAddress = &Error{"bad address"}
ErrNetworkUnreachable = &Error{"network is unreachable"}
ErrUnknownProtocol = &Error{msg: "unknown protocol"}
ErrUnknownNICID = &Error{msg: "unknown nic id"}
ErrUnknownProtocolOption = &Error{msg: "unknown option for protocol"}
ErrDuplicateNICID = &Error{msg: "duplicate nic id"}
ErrDuplicateAddress = &Error{msg: "duplicate address"}
ErrNoRoute = &Error{msg: "no route"}
ErrBadLinkEndpoint = &Error{msg: "bad link layer endpoint"}
ErrAlreadyBound = &Error{msg: "endpoint already bound", ignoreStats: true}
ErrInvalidEndpointState = &Error{msg: "endpoint is in invalid state"}
ErrAlreadyConnecting = &Error{msg: "endpoint is already connecting", ignoreStats: true}
ErrAlreadyConnected = &Error{msg: "endpoint is already connected", ignoreStats: true}
ErrNoPortAvailable = &Error{msg: "no ports are available"}
ErrPortInUse = &Error{msg: "port is in use"}
ErrBadLocalAddress = &Error{msg: "bad local address"}
ErrClosedForSend = &Error{msg: "endpoint is closed for send"}
ErrClosedForReceive = &Error{msg: "endpoint is closed for receive"}
ErrWouldBlock = &Error{msg: "operation would block", ignoreStats: true}
ErrConnectionRefused = &Error{msg: "connection was refused"}
ErrTimeout = &Error{msg: "operation timed out"}
ErrAborted = &Error{msg: "operation aborted"}
ErrConnectStarted = &Error{msg: "connection attempt started", ignoreStats: true}
ErrDestinationRequired = &Error{msg: "destination address is required"}
ErrNotSupported = &Error{msg: "operation not supported"}
ErrQueueSizeNotSupported = &Error{msg: "queue size querying not supported"}
ErrNotConnected = &Error{msg: "endpoint not connected"}
ErrConnectionReset = &Error{msg: "connection reset by peer"}
ErrConnectionAborted = &Error{msg: "connection aborted"}
ErrNoSuchFile = &Error{msg: "no such file"}
ErrInvalidOptionValue = &Error{msg: "invalid option value specified"}
ErrNoLinkAddress = &Error{msg: "no remote link address"}
ErrBadAddress = &Error{msg: "bad address"}
ErrNetworkUnreachable = &Error{msg: "network is unreachable"}
)
// Errors related to Subnet
@@ -473,7 +482,7 @@ type StatCounter struct {
// Increment adds one to the counter.
func (s *StatCounter) Increment() {
atomic.AddUint64(&s.count, 1)
s.IncrementBy(1)
}
// Value returns the current value of the counter.
@@ -486,6 +495,82 @@ func (s *StatCounter) IncrementBy(v uint64) {
atomic.AddUint64(&s.count, v)
}
// 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 *StatCounter
// InvalidAddressesReceived is the total number of IP packets received
// with an unknown or invalid destination address.
InvalidAddressesReceived *StatCounter
// PacketsDelivered is the total number of incoming IP packets that
// are successfully delivered to the transport layer via HandlePacket.
PacketsDelivered *StatCounter
// PacketsSent is the total number of IP packets sent via WritePacket.
PacketsSent *StatCounter
// OutgoingPacketErrors is the total number of IP packets which failed
// to write to a link-layer endpoint.
OutgoingPacketErrors *StatCounter
}
// TCPStats collects TCP-specific stats.
type TCPStats struct {
// ActiveConnectionOpenings is the number of connections opened successfully
// via Connect.
ActiveConnectionOpenings *StatCounter
// PassiveConnectionOpenings is the number of connections opened
// successfully via Listen.
PassiveConnectionOpenings *StatCounter
// FailedConnectionAttempts is the number of calls to Connect or Listen
// (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 *StatCounter
// InvalidSegmentsReceived is the number of TCP segments received that
// the transport layer could not parse.
InvalidSegmentsReceived *StatCounter
// SegmentsSent is the number of TCP segments sent.
SegmentsSent *StatCounter
// ResetsSent is the number of TCP resets sent.
ResetsSent *StatCounter
// ResetsReceived is the number of TCP resets received.
ResetsReceived *StatCounter
}
// UDPStats collects UDP-specific stats.
type UDPStats struct {
// PacketsReceived is the number of UDP datagrams received via
// HandlePacket.
PacketsReceived *StatCounter
// UnknownPortErrors is the number of incoming UDP datagrams dropped
// because they did not have a known destination port.
UnknownPortErrors *StatCounter
// ReceiveBufferErrors is the number of incoming UDP datagrams dropped
// due to the receiving buffer being in an invalid state.
ReceiveBufferErrors *StatCounter
// MalformedPacketsReceived is the number of incoming UDP datagrams
// dropped due to the UDP header being in a malformed state.
MalformedPacketsReceived *StatCounter
// PacketsSent is the number of UDP datagrams sent via sendUDP.
PacketsSent *StatCounter
}
// Stats holds statistics about the networking stack.
//
// All fields are optional.
@@ -494,33 +579,42 @@ type Stats struct {
// stack that were for an unknown or unsupported protocol.
UnknownProtocolRcvdPackets *StatCounter
// UnknownNetworkEndpointRcvdPackets is the number of packets received
// by the stack that were for a supported network protocol, but whose
// destination address didn't having a matching endpoint.
UnknownNetworkEndpointRcvdPackets *StatCounter
// MalformedRcvPackets is the number of packets received by the stack
// that were deemed malformed.
MalformedRcvdPackets *StatCounter
// DroppedPackets is the number of packets dropped due to full queues.
DroppedPackets *StatCounter
// IP breaks out IP-specific stats (both v4 and v6).
IP IPStats
// TCP breaks out TCP-specific stats.
TCP TCPStats
// UDP breaks out UDP-specific stats.
UDP UDPStats
}
func fillIn(v reflect.Value) {
for i := 0; i < v.NumField(); i++ {
v := v.Field(i)
switch v.Kind() {
case reflect.Ptr:
if s, ok := v.Addr().Interface().(**StatCounter); ok {
if *s == nil {
*s = &StatCounter{}
}
}
case reflect.Struct:
fillIn(v)
}
}
}
// FillIn returns a copy of s with nil fields initialized to new StatCounters.
func (s Stats) FillIn() Stats {
if s.UnknownProtocolRcvdPackets == nil {
s.UnknownProtocolRcvdPackets = &StatCounter{}
}
if s.UnknownNetworkEndpointRcvdPackets == nil {
s.UnknownNetworkEndpointRcvdPackets = &StatCounter{}
}
if s.MalformedRcvdPackets == nil {
s.MalformedRcvdPackets = &StatCounter{}
}
if s.DroppedPackets == nil {
s.DroppedPackets = &StatCounter{}
}
fillIn(reflect.ValueOf(&s).Elem())
return s
}
+10
View File
@@ -604,6 +604,11 @@ func sendTCPWithOptions(r *stack.Route, id stack.TransportEndpointID, data buffe
tcp.SetChecksum(^tcp.CalculateChecksum(xsum, length))
}
r.Stats().TCP.SegmentsSent.Increment()
if (flags & flagRst) != 0 {
r.Stats().TCP.ResetsSent.Increment()
}
return r.WritePacket(&hdr, data, ProtocolNumber)
}
@@ -641,6 +646,11 @@ func sendTCP(r *stack.Route, id stack.TransportEndpointID, data buffer.View, fla
tcp.SetChecksum(^tcp.CalculateChecksum(xsum, length))
}
r.Stats().TCP.SegmentsSent.Increment()
if (flags & flagRst) != 0 {
r.Stats().TCP.ResetsSent.Increment()
}
return r.WritePacket(&hdr, data, ProtocolNumber)
}
+20 -2
View File
@@ -829,9 +829,14 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
// created (so no new handshaking is done); for stack-accepted connections not
// yet accepted by the app, they are restored without running the main goroutine
// here.
func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tcpip.Error {
func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) (err *tcpip.Error) {
e.mu.Lock()
defer e.mu.Unlock()
defer func() {
if err != nil && !err.IgnoreStats() {
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
}
}()
connectingAddr := addr.Addr
@@ -960,6 +965,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
if run {
e.workerRunning = true
e.stack.Stats().TCP.ActiveConnectionOpenings.Increment()
go e.protocolMainLoop(handshake) // S/R-SAFE: will be drained before save.
}
@@ -1032,9 +1038,14 @@ func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
// Listen puts the endpoint in "listen" mode, which allows it to accept
// new connections.
func (e *endpoint) Listen(backlog int) *tcpip.Error {
func (e *endpoint) Listen(backlog int) (err *tcpip.Error) {
e.mu.Lock()
defer e.mu.Unlock()
defer func() {
if err != nil && !err.IgnoreStats() {
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
}
}()
// Allow the backlog to be adjusted if the endpoint is not shutting down.
// When the endpoint shuts down, it sets workerCleanup to true, and from
@@ -1075,6 +1086,7 @@ func (e *endpoint) Listen(backlog int) *tcpip.Error {
}
e.workerRunning = true
e.stack.Stats().TCP.PassiveConnectionOpenings.Increment()
go e.protocolListenLoop( // S/R-SAFE: drained on save.
seqnum.Size(e.receiveBufferAvailable()))
@@ -1226,10 +1238,16 @@ func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv
s := newSegment(r, id, vv)
if !s.parse() {
e.stack.Stats().MalformedRcvdPackets.Increment()
e.stack.Stats().TCP.InvalidSegmentsReceived.Increment()
s.decRef()
return
}
e.stack.Stats().TCP.ValidSegmentsReceived.Increment()
if (s.flags & flagRst) != 0 {
e.stack.Stats().TCP.ResetsReceived.Increment()
}
// Send packet to worker goroutine.
if e.segmentQueue.enqueue(s) {
e.newSegmentWaker.Assert()
File diff suppressed because it is too large Load Diff
@@ -267,8 +267,7 @@ func TestSegmentDropWhenTimestampMissing(t *testing.T) {
c.WQ.EventRegister(&we, waiter.EventIn)
defer c.WQ.EventUnregister(&we)
stk := c.Stack()
droppedPacketsStat := stk.Stats().DroppedPackets
droppedPacketsStat := c.Stack().Stats().DroppedPackets
droppedPackets := droppedPacketsStat.Value()
data := []byte{1, 2, 3}
// Save the sequence number as we will reset it later down
@@ -295,9 +295,8 @@ func (c *Context) SendICMPPacket(typ header.ICMPv4Type, code uint8, p1, p2 []byt
c.linkEP.Inject(ipv4.ProtocolNumber, &vv)
}
// SendPacket builds and sends a TCP segment(with the provided payload & TCP
// headers) in an IPv4 packet via the link layer endpoint.
func (c *Context) SendPacket(payload []byte, h *Headers) {
// BuildSegment builds a TCP segment based on the given Headers and payload.
func (c *Context) BuildSegment(payload []byte, h *Headers) buffer.VectorisedView {
// Allocate a buffer for data and headers.
buf := buffer.NewView(header.TCPMinimumSize + header.IPv4MinimumSize + len(h.TCPOpts) + len(payload))
copy(buf[len(buf)-len(payload):], payload)
@@ -340,6 +339,20 @@ func (c *Context) SendPacket(payload []byte, h *Headers) {
// Inject packet.
var views [1]buffer.View
vv := buf.ToVectorisedView(views)
return vv
}
// SendSegment sends a TCP segment that has already been built and written to a
// buffer.VectorisedView.
func (c *Context) SendSegment(s *buffer.VectorisedView) {
c.linkEP.Inject(ipv4.ProtocolNumber, s)
}
// SendPacket builds and sends a TCP segment(with the provided payload & TCP
// headers) in an IPv4 packet via the link layer endpoint.
func (c *Context) SendPacket(payload []byte, h *Headers) {
vv := c.BuildSegment(payload, h)
c.linkEP.Inject(ipv4.ProtocolNumber, &vv)
}
+10 -1
View File
@@ -327,7 +327,10 @@ func (e *endpoint) Write(p tcpip.Payload, opts tcpip.WriteOptions) (uintptr, *tc
if err != nil {
return 0, err
}
sendUDP(route, v, e.id.LocalPort, dstPort)
if err := sendUDP(route, v, e.id.LocalPort, dstPort); err != nil {
return 0, err
}
return uintptr(len(v)), nil
}
@@ -447,6 +450,9 @@ func sendUDP(r *stack.Route, data buffer.View, localPort, remotePort uint16) *tc
udp.SetChecksum(^udp.CalculateChecksum(xsum, length))
}
// Track count of packets sent.
r.Stats().UDP.PacketsSent.Increment()
return r.WritePacket(&hdr, data, ProtocolNumber)
}
@@ -758,15 +764,18 @@ func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv
hdr := header.UDP(vv.First())
if int(hdr.Length()) > vv.Size() {
// Malformed packet.
e.stack.Stats().UDP.MalformedPacketsReceived.Increment()
return
}
vv.TrimFront(header.UDPMinimumSize)
e.rcvMu.Lock()
e.stack.Stats().UDP.PacketsReceived.Increment()
// Drop the packet if our buffer is currently full.
if !e.rcvReady || e.rcvClosed || e.rcvBufSize >= e.rcvBufSizeMax {
e.stack.Stats().UDP.ReceiveBufferErrors.Increment()
e.rcvMu.Unlock()
return
}
+38
View File
@@ -661,3 +661,41 @@ func TestV4WriteOnConnected(t *testing.T) {
c.t.Fatalf("Bad payload: got %x, want %x", udp.Payload(), payload)
}
}
func TestReadIncrementsPacketsReceived(t *testing.T) {
c := newDualTestContext(t, defaultMTU)
defer c.cleanup()
// Create IPv4 UDP endpoint
var err *tcpip.Error
c.ep, err = c.s.NewEndpoint(udp.ProtocolNumber, ipv4.ProtocolNumber, &c.wq)
if err != nil {
c.t.Fatalf("NewEndpoint failed: %v", err)
}
// Bind to wildcard.
if err := c.ep.Bind(tcpip.FullAddress{Port: stackPort}, nil); err != nil {
c.t.Fatalf("Bind failed: %v", err)
}
testV4Read(c)
var want uint64 = 1
if got := c.s.Stats().UDP.PacketsReceived.Value(); got != want {
c.t.Fatalf("Read did not increment PacketsReceived: got %v, want %v", got, want)
}
}
func TestWriteIncrementsPacketsSent(t *testing.T) {
c := newDualTestContext(t, defaultMTU)
defer c.cleanup()
c.createV6Endpoint(false)
testDualWrite(c)
var want uint64 = 2
if got := c.s.Stats().UDP.PacketsSent.Value(); got != want {
c.t.Fatalf("Write did not increment PacketsSent: got %v, want %v", got, want)
}
}