mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add stats for ARP
Fixes #4963 Startblock: has LGTM from sbalana and then add reviewer ghanan PiperOrigin-RevId: 351886320
This commit is contained in:
@@ -186,6 +186,21 @@ var Metrics = tcpip.Stats{
|
||||
IPTablesInputDropped: mustCreateMetric("/netstack/ip/iptables/input_dropped", "Total number of IP packets dropped in the Input chain."),
|
||||
IPTablesOutputDropped: mustCreateMetric("/netstack/ip/iptables/output_dropped", "Total number of IP packets dropped in the Output chain."),
|
||||
},
|
||||
ARP: tcpip.ARPStats{
|
||||
PacketsReceived: mustCreateMetric("/netstack/arp/packets_received", "Number of ARP packets received from the link layer."),
|
||||
DisabledPacketsReceived: mustCreateMetric("/netstack/arp/disabled_packets_received", "Number of ARP packets received from the link layer when the ARP layer is disabled."),
|
||||
MalformedPacketsReceived: mustCreateMetric("/netstack/arp/malformed_packets_received", "Number of ARP packets which failed ARP header validation checks."),
|
||||
RequestsReceived: mustCreateMetric("/netstack/arp/requests_received", "Number of ARP requests received."),
|
||||
RequestsReceivedUnknownTargetAddress: mustCreateMetric("/netstack/arp/requests_received_unknown_addr", "Number of ARP requests received with an unknown target address."),
|
||||
OutgoingRequestInterfaceHasNoLocalAddressErrors: mustCreateMetric("/netstack/arp/outgoing_requests_iface_has_no_addr", "Number of failed attempts to send an ARP request with an interface that has no network address."),
|
||||
OutgoingRequestBadLocalAddressErrors: mustCreateMetric("/netstack/arp/outgoing_requests_invalid_local_addr", "Number of failed attempts to send an ARP request with a provided local address that is invalid."),
|
||||
OutgoingRequestNetworkUnreachableErrors: mustCreateMetric("/netstack/arp/outgoing_requests_network_unreachable", "Number of failed attempts to send an ARP request with a network unreachable error."),
|
||||
OutgoingRequestsDropped: mustCreateMetric("/netstack/arp/outgoing_requests_dropped", "Number of ARP requests which failed to write to a link-layer endpoint."),
|
||||
OutgoingRequestsSent: mustCreateMetric("/netstack/arp/outgoing_requests_sent", "Number of ARP requests sent."),
|
||||
RepliesReceived: mustCreateMetric("/netstack/arp/replies_received", "Number of ARP replies received."),
|
||||
OutgoingRepliesDropped: mustCreateMetric("/netstack/arp/outgoing_replies_dropped", "Number of ARP replies which failed to write to a link-layer endpoint."),
|
||||
OutgoingRepliesSent: mustCreateMetric("/netstack/arp/outgoing_replies_sent", "Number of ARP replies sent."),
|
||||
},
|
||||
TCP: tcpip.TCPStats{
|
||||
ActiveConnectionOpenings: mustCreateMetric("/netstack/tcp/active_connection_openings", "Number of connections opened successfully via Connect."),
|
||||
PassiveConnectionOpenings: mustCreateMetric("/netstack/tcp/passive_connection_openings", "Number of connections opened successfully via Listen."),
|
||||
|
||||
@@ -119,21 +119,28 @@ func (*endpoint) WriteHeaderIncludedPacket(*stack.Route, *stack.PacketBuffer) *t
|
||||
}
|
||||
|
||||
func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
stats := e.protocol.stack.Stats().ARP
|
||||
stats.PacketsReceived.Increment()
|
||||
|
||||
if !e.isEnabled() {
|
||||
stats.DisabledPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
h := header.ARP(pkt.NetworkHeader().View())
|
||||
if !h.IsValid() {
|
||||
stats.MalformedPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
switch h.Op() {
|
||||
case header.ARPRequest:
|
||||
stats.RequestsReceived.Increment()
|
||||
localAddr := tcpip.Address(h.ProtocolAddressTarget())
|
||||
|
||||
if e.nud == nil {
|
||||
if e.linkAddrCache.CheckLocalAddress(e.nic.ID(), header.IPv4ProtocolNumber, localAddr) == 0 {
|
||||
stats.RequestsReceivedUnknownTargetAddress.Increment()
|
||||
return // we have no useful answer, ignore the request
|
||||
}
|
||||
|
||||
@@ -142,6 +149,7 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
e.linkAddrCache.AddLinkAddress(e.nic.ID(), addr, linkAddr)
|
||||
} else {
|
||||
if e.protocol.stack.CheckLocalAddress(e.nic.ID(), header.IPv4ProtocolNumber, localAddr) == 0 {
|
||||
stats.RequestsReceivedUnknownTargetAddress.Increment()
|
||||
return // we have no useful answer, ignore the request
|
||||
}
|
||||
|
||||
@@ -177,9 +185,14 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
//
|
||||
// Send the packet to the (new) target hardware address on the same
|
||||
// hardware on which the request was received.
|
||||
_ = e.nic.WritePacketToRemote(tcpip.LinkAddress(origSender), nil /* gso */, ProtocolNumber, respPkt)
|
||||
if err := e.nic.WritePacketToRemote(tcpip.LinkAddress(origSender), nil /* gso */, ProtocolNumber, respPkt); err != nil {
|
||||
stats.OutgoingRepliesDropped.Increment()
|
||||
} else {
|
||||
stats.OutgoingRepliesSent.Increment()
|
||||
}
|
||||
|
||||
case header.ARPReply:
|
||||
stats.RepliesReceived.Increment()
|
||||
addr := tcpip.Address(h.ProtocolAddressSender())
|
||||
linkAddr := tcpip.LinkAddress(h.HardwareAddressSender())
|
||||
|
||||
@@ -233,6 +246,8 @@ func (*protocol) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
|
||||
|
||||
// LinkAddressRequest implements stack.LinkAddressResolver.LinkAddressRequest.
|
||||
func (p *protocol) LinkAddressRequest(targetAddr, localAddr tcpip.Address, remoteLinkAddr tcpip.LinkAddress, nic stack.NetworkInterface) *tcpip.Error {
|
||||
stats := p.stack.Stats().ARP
|
||||
|
||||
if len(remoteLinkAddr) == 0 {
|
||||
remoteLinkAddr = header.EthernetBroadcastAddress
|
||||
}
|
||||
@@ -241,15 +256,18 @@ func (p *protocol) LinkAddressRequest(targetAddr, localAddr tcpip.Address, remot
|
||||
if len(localAddr) == 0 {
|
||||
addr, err := p.stack.GetMainNICAddress(nicID, header.IPv4ProtocolNumber)
|
||||
if err != nil {
|
||||
stats.OutgoingRequestInterfaceHasNoLocalAddressErrors.Increment()
|
||||
return err
|
||||
}
|
||||
|
||||
if len(addr.Address) == 0 {
|
||||
stats.OutgoingRequestNetworkUnreachableErrors.Increment()
|
||||
return tcpip.ErrNetworkUnreachable
|
||||
}
|
||||
|
||||
localAddr = addr.Address
|
||||
} else if p.stack.CheckLocalAddress(nicID, header.IPv4ProtocolNumber, localAddr) == 0 {
|
||||
stats.OutgoingRequestBadLocalAddressErrors.Increment()
|
||||
return tcpip.ErrBadLocalAddress
|
||||
}
|
||||
|
||||
@@ -269,7 +287,12 @@ func (p *protocol) LinkAddressRequest(targetAddr, localAddr tcpip.Address, remot
|
||||
if n := copy(h.ProtocolAddressTarget(), targetAddr); n != header.IPv4AddressSize {
|
||||
panic(fmt.Sprintf("copied %d bytes, expected %d bytes", n, header.IPv4AddressSize))
|
||||
}
|
||||
return nic.WritePacketToRemote(remoteLinkAddr, nil /* gso */, ProtocolNumber, pkt)
|
||||
if err := nic.WritePacketToRemote(remoteLinkAddr, nil /* gso */, ProtocolNumber, pkt); err != nil {
|
||||
stats.OutgoingRequestsDropped.Increment()
|
||||
return err
|
||||
}
|
||||
stats.OutgoingRequestsSent.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResolveStaticAddress implements stack.LinkAddressResolver.ResolveStaticAddress.
|
||||
|
||||
@@ -240,6 +240,10 @@ func TestDirectRequest(t *testing.T) {
|
||||
|
||||
for i, address := range []tcpip.Address{stackAddr, remoteAddr} {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
expectedPacketsReceived := c.s.Stats().ARP.PacketsReceived.Value() + 1
|
||||
expectedRequestsReceived := c.s.Stats().ARP.RequestsReceived.Value() + 1
|
||||
expectedRepliesSent := c.s.Stats().ARP.OutgoingRepliesSent.Value() + 1
|
||||
|
||||
inject(address)
|
||||
pi, _ := c.linkEP.ReadContext(context.Background())
|
||||
if pi.Proto != arp.ProtocolNumber {
|
||||
@@ -249,6 +253,9 @@ func TestDirectRequest(t *testing.T) {
|
||||
if !rep.IsValid() {
|
||||
t.Fatalf("invalid ARP response: len = %d; response = %x", len(rep), rep)
|
||||
}
|
||||
if got := rep.Op(); got != header.ARPReply {
|
||||
t.Fatalf("got Op = %d, want = %d", got, header.ARPReply)
|
||||
}
|
||||
if got, want := tcpip.LinkAddress(rep.HardwareAddressSender()), stackLinkAddr; got != want {
|
||||
t.Errorf("got HardwareAddressSender = %s, want = %s", got, want)
|
||||
}
|
||||
@@ -261,6 +268,16 @@ func TestDirectRequest(t *testing.T) {
|
||||
if got, want := tcpip.Address(rep.ProtocolAddressTarget()), tcpip.Address(h.ProtocolAddressSender()); got != want {
|
||||
t.Errorf("got ProtocolAddressTarget = %s, want = %s", got, want)
|
||||
}
|
||||
|
||||
if got := c.s.Stats().ARP.PacketsReceived.Value(); got != expectedPacketsReceived {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = %d", got, expectedPacketsReceived)
|
||||
}
|
||||
if got := c.s.Stats().ARP.RequestsReceived.Value(); got != expectedRequestsReceived {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = %d", got, expectedRequestsReceived)
|
||||
}
|
||||
if got := c.s.Stats().ARP.OutgoingRepliesSent.Value(); got != expectedRepliesSent {
|
||||
t.Errorf("got c.s.Stats().ARP.OutgoingRepliesSent.Value() = %d, want = %d", got, expectedRepliesSent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -273,6 +290,84 @@ func TestDirectRequest(t *testing.T) {
|
||||
if pkt, ok := c.linkEP.ReadContext(ctx); ok {
|
||||
t.Errorf("stackAddrBad: unexpected packet sent, Proto=%v", pkt.Proto)
|
||||
}
|
||||
if got := c.s.Stats().ARP.RequestsReceivedUnknownTargetAddress.Value(); got != 1 {
|
||||
t.Errorf("got c.s.Stats().ARP.RequestsReceivedUnKnownTargetAddress.Value() = %d, want = 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMalformedPacket(t *testing.T) {
|
||||
c := newTestContext(t, false)
|
||||
defer c.cleanup()
|
||||
|
||||
v := make(buffer.View, header.ARPSize)
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: v.ToVectorisedView(),
|
||||
})
|
||||
|
||||
c.linkEP.InjectInbound(arp.ProtocolNumber, pkt)
|
||||
|
||||
if got := c.s.Stats().ARP.PacketsReceived.Value(); got != 1 {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = 1", got)
|
||||
}
|
||||
if got := c.s.Stats().ARP.MalformedPacketsReceived.Value(); got != 1 {
|
||||
t.Errorf("got c.s.Stats().ARP.MalformedPacketsReceived.Value() = %d, want = 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisabledEndpoint(t *testing.T) {
|
||||
c := newTestContext(t, false)
|
||||
defer c.cleanup()
|
||||
|
||||
ep, err := c.s.GetNetworkEndpoint(nicID, header.ARPProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("GetNetworkEndpoint(%d, header.ARPProtocolNumber) failed: %s", nicID, err)
|
||||
}
|
||||
ep.Disable()
|
||||
|
||||
v := make(buffer.View, header.ARPSize)
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: v.ToVectorisedView(),
|
||||
})
|
||||
|
||||
c.linkEP.InjectInbound(arp.ProtocolNumber, pkt)
|
||||
|
||||
if got := c.s.Stats().ARP.PacketsReceived.Value(); got != 1 {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = 1", got)
|
||||
}
|
||||
if got := c.s.Stats().ARP.DisabledPacketsReceived.Value(); got != 1 {
|
||||
t.Errorf("got c.s.Stats().ARP.DisabledPacketsReceived.Value() = %d, want = 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectReply(t *testing.T) {
|
||||
c := newTestContext(t, false)
|
||||
defer c.cleanup()
|
||||
|
||||
const senderMAC = "\x01\x02\x03\x04\x05\x06"
|
||||
const senderIPv4 = "\x0a\x00\x00\x02"
|
||||
|
||||
v := make(buffer.View, header.ARPSize)
|
||||
h := header.ARP(v)
|
||||
h.SetIPv4OverEthernet()
|
||||
h.SetOp(header.ARPReply)
|
||||
|
||||
copy(h.HardwareAddressSender(), senderMAC)
|
||||
copy(h.ProtocolAddressSender(), senderIPv4)
|
||||
copy(h.HardwareAddressTarget(), stackLinkAddr)
|
||||
copy(h.ProtocolAddressTarget(), stackAddr)
|
||||
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: v.ToVectorisedView(),
|
||||
})
|
||||
|
||||
c.linkEP.InjectInbound(arp.ProtocolNumber, pkt)
|
||||
|
||||
if got := c.s.Stats().ARP.PacketsReceived.Value(); got != 1 {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = 1", got)
|
||||
}
|
||||
if got := c.s.Stats().ARP.RepliesReceived.Value(); got != 1 {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectRequestWithNeighborCache(t *testing.T) {
|
||||
@@ -311,6 +406,11 @@ func TestDirectRequestWithNeighborCache(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
packetsRecv := c.s.Stats().ARP.PacketsReceived.Value()
|
||||
requestsRecv := c.s.Stats().ARP.RequestsReceived.Value()
|
||||
requestsRecvUnknownAddr := c.s.Stats().ARP.RequestsReceivedUnknownTargetAddress.Value()
|
||||
outgoingReplies := c.s.Stats().ARP.OutgoingRepliesSent.Value()
|
||||
|
||||
// Inject an incoming ARP request.
|
||||
v := make(buffer.View, header.ARPSize)
|
||||
h := header.ARP(v)
|
||||
@@ -323,6 +423,13 @@ func TestDirectRequestWithNeighborCache(t *testing.T) {
|
||||
Data: v.ToVectorisedView(),
|
||||
}))
|
||||
|
||||
if got, want := c.s.Stats().ARP.PacketsReceived.Value(), packetsRecv+1; got != want {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = %d", got, want)
|
||||
}
|
||||
if got, want := c.s.Stats().ARP.RequestsReceived.Value(), requestsRecv+1; got != want {
|
||||
t.Errorf("got c.s.Stats().ARP.PacketsReceived.Value() = %d, want = %d", got, want)
|
||||
}
|
||||
|
||||
if !test.isValid {
|
||||
// No packets should be sent after receiving an invalid ARP request.
|
||||
// There is no need to perform a blocking read here, since packets are
|
||||
@@ -330,9 +437,20 @@ func TestDirectRequestWithNeighborCache(t *testing.T) {
|
||||
if pkt, ok := c.linkEP.Read(); ok {
|
||||
t.Errorf("unexpected packet sent with network protocol number %d", pkt.Proto)
|
||||
}
|
||||
if got, want := c.s.Stats().ARP.RequestsReceivedUnknownTargetAddress.Value(), requestsRecvUnknownAddr+1; got != want {
|
||||
t.Errorf("got c.s.Stats().ARP.RequestsReceivedUnknownTargetAddress.Value() = %d, want = %d", got, want)
|
||||
}
|
||||
if got, want := c.s.Stats().ARP.OutgoingRepliesSent.Value(), outgoingReplies; got != want {
|
||||
t.Errorf("got c.s.Stats().ARP.OutgoingRepliesSent.Value() = %d, want = %d", got, want)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if got, want := c.s.Stats().ARP.OutgoingRepliesSent.Value(), outgoingReplies+1; got != want {
|
||||
t.Errorf("got c.s.Stats().ARP.OutgoingRepliesSent.Value() = %d, want = %d", got, want)
|
||||
}
|
||||
|
||||
// Verify an ARP response was sent.
|
||||
pi, ok := c.linkEP.Read()
|
||||
if !ok {
|
||||
@@ -418,6 +536,8 @@ type testInterface struct {
|
||||
stack.LinkEndpoint
|
||||
|
||||
nicID tcpip.NICID
|
||||
|
||||
writeErr *tcpip.Error
|
||||
}
|
||||
|
||||
func (t *testInterface) ID() tcpip.NICID {
|
||||
@@ -441,6 +561,10 @@ func (*testInterface) Promiscuous() bool {
|
||||
}
|
||||
|
||||
func (t *testInterface) WritePacketToRemote(remoteLinkAddr tcpip.LinkAddress, gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) *tcpip.Error {
|
||||
if t.writeErr != nil {
|
||||
return t.writeErr
|
||||
}
|
||||
|
||||
var r stack.Route
|
||||
r.NetProto = protocol
|
||||
r.ResolveWith(remoteLinkAddr)
|
||||
@@ -458,61 +582,99 @@ func TestLinkAddressRequest(t *testing.T) {
|
||||
localAddr tcpip.Address
|
||||
remoteLinkAddr tcpip.LinkAddress
|
||||
|
||||
expectedErr *tcpip.Error
|
||||
expectedLocalAddr tcpip.Address
|
||||
expectedRemoteLinkAddr tcpip.LinkAddress
|
||||
linkErr *tcpip.Error
|
||||
expectedErr *tcpip.Error
|
||||
expectedLocalAddr tcpip.Address
|
||||
expectedRemoteLinkAddr tcpip.LinkAddress
|
||||
expectedRequestsSent uint64
|
||||
expectedRequestBadLocalAddressErrors uint64
|
||||
expectedRequestNetworkUnreachableErrors uint64
|
||||
expectedRequestDroppedErrors uint64
|
||||
}{
|
||||
{
|
||||
name: "Unicast",
|
||||
nicAddr: stackAddr,
|
||||
localAddr: stackAddr,
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedRemoteLinkAddr: remoteLinkAddr,
|
||||
name: "Unicast",
|
||||
nicAddr: stackAddr,
|
||||
localAddr: stackAddr,
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedRemoteLinkAddr: remoteLinkAddr,
|
||||
expectedRequestsSent: 1,
|
||||
expectedRequestBadLocalAddressErrors: 0,
|
||||
expectedRequestNetworkUnreachableErrors: 0,
|
||||
},
|
||||
{
|
||||
name: "Multicast",
|
||||
nicAddr: stackAddr,
|
||||
localAddr: stackAddr,
|
||||
remoteLinkAddr: "",
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedRemoteLinkAddr: header.EthernetBroadcastAddress,
|
||||
name: "Multicast",
|
||||
nicAddr: stackAddr,
|
||||
localAddr: stackAddr,
|
||||
remoteLinkAddr: "",
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedRemoteLinkAddr: header.EthernetBroadcastAddress,
|
||||
expectedRequestsSent: 1,
|
||||
expectedRequestBadLocalAddressErrors: 0,
|
||||
expectedRequestNetworkUnreachableErrors: 0,
|
||||
},
|
||||
{
|
||||
name: "Unicast with unspecified source",
|
||||
nicAddr: stackAddr,
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedRemoteLinkAddr: remoteLinkAddr,
|
||||
name: "Unicast with unspecified source",
|
||||
nicAddr: stackAddr,
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedRemoteLinkAddr: remoteLinkAddr,
|
||||
expectedRequestsSent: 1,
|
||||
expectedRequestBadLocalAddressErrors: 0,
|
||||
expectedRequestNetworkUnreachableErrors: 0,
|
||||
},
|
||||
{
|
||||
name: "Multicast with unspecified source",
|
||||
nicAddr: stackAddr,
|
||||
remoteLinkAddr: "",
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedRemoteLinkAddr: header.EthernetBroadcastAddress,
|
||||
name: "Multicast with unspecified source",
|
||||
nicAddr: stackAddr,
|
||||
remoteLinkAddr: "",
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedRemoteLinkAddr: header.EthernetBroadcastAddress,
|
||||
expectedRequestsSent: 1,
|
||||
expectedRequestBadLocalAddressErrors: 0,
|
||||
expectedRequestNetworkUnreachableErrors: 0,
|
||||
},
|
||||
{
|
||||
name: "Unicast with unassigned address",
|
||||
localAddr: testAddr,
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
expectedErr: tcpip.ErrBadLocalAddress,
|
||||
name: "Unicast with unassigned address",
|
||||
localAddr: testAddr,
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
expectedErr: tcpip.ErrBadLocalAddress,
|
||||
expectedRequestsSent: 0,
|
||||
expectedRequestBadLocalAddressErrors: 1,
|
||||
expectedRequestNetworkUnreachableErrors: 0,
|
||||
},
|
||||
{
|
||||
name: "Multicast with unassigned address",
|
||||
localAddr: testAddr,
|
||||
remoteLinkAddr: "",
|
||||
expectedErr: tcpip.ErrBadLocalAddress,
|
||||
name: "Multicast with unassigned address",
|
||||
localAddr: testAddr,
|
||||
remoteLinkAddr: "",
|
||||
expectedErr: tcpip.ErrBadLocalAddress,
|
||||
expectedRequestsSent: 0,
|
||||
expectedRequestBadLocalAddressErrors: 1,
|
||||
expectedRequestNetworkUnreachableErrors: 0,
|
||||
},
|
||||
{
|
||||
name: "Unicast with no local address available",
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
expectedErr: tcpip.ErrNetworkUnreachable,
|
||||
name: "Unicast with no local address available",
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
expectedErr: tcpip.ErrNetworkUnreachable,
|
||||
expectedRequestsSent: 0,
|
||||
expectedRequestBadLocalAddressErrors: 0,
|
||||
expectedRequestNetworkUnreachableErrors: 1,
|
||||
},
|
||||
{
|
||||
name: "Multicast with no local address available",
|
||||
remoteLinkAddr: "",
|
||||
expectedErr: tcpip.ErrNetworkUnreachable,
|
||||
name: "Multicast with no local address available",
|
||||
remoteLinkAddr: "",
|
||||
expectedErr: tcpip.ErrNetworkUnreachable,
|
||||
expectedRequestsSent: 0,
|
||||
expectedRequestBadLocalAddressErrors: 0,
|
||||
expectedRequestNetworkUnreachableErrors: 1,
|
||||
},
|
||||
{
|
||||
name: "Link error",
|
||||
nicAddr: stackAddr,
|
||||
localAddr: stackAddr,
|
||||
remoteLinkAddr: remoteLinkAddr,
|
||||
linkErr: tcpip.ErrInvalidEndpointState,
|
||||
expectedErr: tcpip.ErrInvalidEndpointState,
|
||||
expectedRequestDroppedErrors: 1,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -543,10 +705,24 @@ func TestLinkAddressRequest(t *testing.T) {
|
||||
// can mock a link address request and observe the packets sent to the
|
||||
// link endpoint even though the stack uses the real NIC to validate the
|
||||
// local address.
|
||||
if err := linkRes.LinkAddressRequest(remoteAddr, test.localAddr, test.remoteLinkAddr, &testInterface{LinkEndpoint: linkEP, nicID: nicID}); err != test.expectedErr {
|
||||
iface := testInterface{LinkEndpoint: linkEP, nicID: nicID, writeErr: test.linkErr}
|
||||
if err := linkRes.LinkAddressRequest(remoteAddr, test.localAddr, test.remoteLinkAddr, &iface); err != test.expectedErr {
|
||||
t.Fatalf("got p.LinkAddressRequest(%s, %s, %s, _) = %s, want = %s", remoteAddr, test.localAddr, test.remoteLinkAddr, err, test.expectedErr)
|
||||
}
|
||||
|
||||
if got := s.Stats().ARP.OutgoingRequestsSent.Value(); got != test.expectedRequestsSent {
|
||||
t.Errorf("got s.Stats().ARP.OutgoingRequestsSent.Value() = %d, want = %d", got, test.expectedRequestsSent)
|
||||
}
|
||||
if got := s.Stats().ARP.OutgoingRequestBadLocalAddressErrors.Value(); got != test.expectedRequestBadLocalAddressErrors {
|
||||
t.Errorf("got s.Stats().ARP.OutgoingRequestBadLocalAddressErrors.Value() = %d, want = %d", got, test.expectedRequestBadLocalAddressErrors)
|
||||
}
|
||||
if got := s.Stats().ARP.OutgoingRequestNetworkUnreachableErrors.Value(); got != test.expectedRequestNetworkUnreachableErrors {
|
||||
t.Errorf("got s.Stats().ARP.OutgoingRequestNetworkUnreachableErrors.Value() = %d, want = %d", got, test.expectedRequestNetworkUnreachableErrors)
|
||||
}
|
||||
if got := s.Stats().ARP.OutgoingRequestsDropped.Value(); got != test.expectedRequestDroppedErrors {
|
||||
t.Errorf("got s.Stats().ARP.OutgoingRequestsDropped.Value() = %d, want = %d", got, test.expectedRequestDroppedErrors)
|
||||
}
|
||||
|
||||
if test.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
@@ -561,6 +737,9 @@ func TestLinkAddressRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
rep := header.ARP(stack.PayloadSince(pkt.Pkt.NetworkHeader()))
|
||||
if got := rep.Op(); got != header.ARPRequest {
|
||||
t.Errorf("got Op = %d, want = %d", got, header.ARPRequest)
|
||||
}
|
||||
if got := tcpip.LinkAddress(rep.HardwareAddressSender()); got != stackLinkAddr {
|
||||
t.Errorf("got HardwareAddressSender = %s, want = %s", got, stackLinkAddr)
|
||||
}
|
||||
@@ -576,3 +755,22 @@ func TestLinkAddressRequest(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkAddressRequestWithoutNIC(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol},
|
||||
})
|
||||
p := s.NetworkProtocolInstance(arp.ProtocolNumber)
|
||||
linkRes, ok := p.(stack.LinkAddressResolver)
|
||||
if !ok {
|
||||
t.Fatal("expected ARP protocol to implement stack.LinkAddressResolver")
|
||||
}
|
||||
|
||||
if err := linkRes.LinkAddressRequest(remoteAddr, "", remoteLinkAddr, &testInterface{nicID: nicID}); err != tcpip.ErrUnknownNICID {
|
||||
t.Fatalf("got p.LinkAddressRequest(%s, %s, %s, _) = %s, want = %s", remoteAddr, "", remoteLinkAddr, err, tcpip.ErrUnknownNICID)
|
||||
}
|
||||
|
||||
if got := s.Stats().ARP.OutgoingRequestInterfaceHasNoLocalAddressErrors.Value(); got != 1 {
|
||||
t.Errorf("got s.Stats().ARP.OutgoingRequestInterfaceHasNoLocalAddressErrors.Value() = %d, want = 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1591,6 +1591,59 @@ type IPStats struct {
|
||||
OptionUnknownReceived *StatCounter
|
||||
}
|
||||
|
||||
// ARPStats collects ARP-specific stats.
|
||||
type ARPStats struct {
|
||||
// PacketsReceived is the number of ARP packets received from the link layer.
|
||||
PacketsReceived *StatCounter
|
||||
|
||||
// DisabledPacketsReceived is the number of ARP packets received from the link
|
||||
// layer when the ARP layer is disabled.
|
||||
DisabledPacketsReceived *StatCounter
|
||||
|
||||
// MalformedPacketsReceived is the number of ARP packets that were dropped due
|
||||
// to being malformed.
|
||||
MalformedPacketsReceived *StatCounter
|
||||
|
||||
// RequestsReceived is the number of ARP requests received.
|
||||
RequestsReceived *StatCounter
|
||||
|
||||
// RequestsReceivedUnknownTargetAddress is the number of ARP requests that
|
||||
// were targeted to an interface different from the one it was received on.
|
||||
RequestsReceivedUnknownTargetAddress *StatCounter
|
||||
|
||||
// OutgoingRequestInterfaceHasNoLocalAddressErrors is the number of failures
|
||||
// to send an ARP request because the interface has no network address
|
||||
// assigned to it.
|
||||
OutgoingRequestInterfaceHasNoLocalAddressErrors *StatCounter
|
||||
|
||||
// OutgoingRequestBadLocalAddressErrors is the number of failures to send an
|
||||
// ARP request with a bad local address.
|
||||
OutgoingRequestBadLocalAddressErrors *StatCounter
|
||||
|
||||
// OutgoingRequestNetworkUnreachableErrors is the number of failures to send
|
||||
// an ARP request with a network unreachable error.
|
||||
OutgoingRequestNetworkUnreachableErrors *StatCounter
|
||||
|
||||
// OutgoingRequestsDropped is the number of ARP requests which failed to write
|
||||
// to a link-layer endpoint.
|
||||
OutgoingRequestsDropped *StatCounter
|
||||
|
||||
// OutgoingRequestSent is the number of ARP requests successfully written to a
|
||||
// link-layer endpoint.
|
||||
OutgoingRequestsSent *StatCounter
|
||||
|
||||
// RepliesReceived is the number of ARP replies received.
|
||||
RepliesReceived *StatCounter
|
||||
|
||||
// OutgoingRepliesDropped is the number of ARP replies which failed to write
|
||||
// to a link-layer endpoint.
|
||||
OutgoingRepliesDropped *StatCounter
|
||||
|
||||
// OutgoingRepliesSent is the number of ARP replies successfully written to a
|
||||
// link-layer endpoint.
|
||||
OutgoingRepliesSent *StatCounter
|
||||
}
|
||||
|
||||
// TCPStats collects TCP-specific stats.
|
||||
type TCPStats struct {
|
||||
// ActiveConnectionOpenings is the number of connections opened
|
||||
@@ -1743,6 +1796,9 @@ type Stats struct {
|
||||
// IP breaks out IP-specific stats (both v4 and v6).
|
||||
IP IPStats
|
||||
|
||||
// ARP breaks out ARP-specific stats.
|
||||
ARP ARPStats
|
||||
|
||||
// TCP breaks out TCP-specific stats.
|
||||
TCP TCPStats
|
||||
|
||||
|
||||
Reference in New Issue
Block a user