Filter out received packets with a local source IP address.

CERT Advisory CA-96.21 III. Solution advises that devices drop packets which
could not have correctly arrived on the wire, such as receiving a packet where
the source IP address is owned by the device that sent it.

Fixes #1507

PiperOrigin-RevId: 290378240
This commit is contained in:
Eyal Soha
2020-01-17 18:26:20 -08:00
committed by gVisor bot
parent f1a5178c58
commit 47d85257d3
5 changed files with 95 additions and 34 deletions
+8 -7
View File
@@ -138,13 +138,14 @@ var Metrics = tcpip.Stats{
},
},
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."),
PacketsDelivered: mustCreateMetric("/netstack/ip/packets_delivered", "Total number of incoming IP packets that are successfully delivered to the transport layer via HandlePacket."),
PacketsSent: mustCreateMetric("/netstack/ip/packets_sent", "Total number of IP packets sent via WritePacket."),
OutgoingPacketErrors: mustCreateMetric("/netstack/ip/outgoing_packet_errors", "Total number of IP packets which failed to write to a link-layer endpoint."),
MalformedPacketsReceived: mustCreateMetric("/netstack/ip/malformed_packets_received", "Total number of IP packets which failed IP header validation checks."),
MalformedFragmentsReceived: mustCreateMetric("/netstack/ip/malformed_fragments_received", "Total number of IP fragments which failed IP fragment validation checks."),
PacketsReceived: mustCreateMetric("/netstack/ip/packets_received", "Total number of IP packets received from the link layer in nic.DeliverNetworkPacket."),
InvalidDestinationAddressesReceived: mustCreateMetric("/netstack/ip/invalid_addresses_received", "Total number of IP packets received with an unknown or invalid destination address."),
InvalidSourceAddressesReceived: mustCreateMetric("/netstack/ip/invalid_source_addresses_received", "Total number of IP packets received with an unknown or invalid source address."),
PacketsDelivered: mustCreateMetric("/netstack/ip/packets_delivered", "Total number of incoming IP packets that are successfully delivered to the transport layer via HandlePacket."),
PacketsSent: mustCreateMetric("/netstack/ip/packets_sent", "Total number of IP packets sent via WritePacket."),
OutgoingPacketErrors: mustCreateMetric("/netstack/ip/outgoing_packet_errors", "Total number of IP packets which failed to write to a link-layer endpoint."),
MalformedPacketsReceived: mustCreateMetric("/netstack/ip/malformed_packets_received", "Total number of IP packets which failed IP header validation checks."),
MalformedFragmentsReceived: mustCreateMetric("/netstack/ip/malformed_fragments_received", "Total number of IP fragments which failed IP fragment validation checks."),
},
TCP: tcpip.TCPStats{
ActiveConnectionOpenings: mustCreateMetric("/netstack/tcp/active_connection_openings", "Number of connections opened successfully via Connect."),
+19 -19
View File
@@ -148,25 +148,25 @@ func (s *Stack) Statistics(stat interface{}, arg string) error {
case *inet.StatSNMPIP:
ip := Metrics.IP
*stats = inet.StatSNMPIP{
0, // TODO(gvisor.dev/issue/969): Support Ip/Forwarding.
0, // TODO(gvisor.dev/issue/969): Support Ip/DefaultTTL.
ip.PacketsReceived.Value(), // InReceives.
0, // TODO(gvisor.dev/issue/969): Support Ip/InHdrErrors.
ip.InvalidAddressesReceived.Value(), // InAddrErrors.
0, // TODO(gvisor.dev/issue/969): Support Ip/ForwDatagrams.
0, // TODO(gvisor.dev/issue/969): Support Ip/InUnknownProtos.
0, // TODO(gvisor.dev/issue/969): Support Ip/InDiscards.
ip.PacketsDelivered.Value(), // InDelivers.
ip.PacketsSent.Value(), // OutRequests.
ip.OutgoingPacketErrors.Value(), // OutDiscards.
0, // TODO(gvisor.dev/issue/969): Support Ip/OutNoRoutes.
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmTimeout.
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmReqds.
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmOKs.
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmFails.
0, // TODO(gvisor.dev/issue/969): Support Ip/FragOKs.
0, // TODO(gvisor.dev/issue/969): Support Ip/FragFails.
0, // TODO(gvisor.dev/issue/969): Support Ip/FragCreates.
0, // TODO(gvisor.dev/issue/969): Support Ip/Forwarding.
0, // TODO(gvisor.dev/issue/969): Support Ip/DefaultTTL.
ip.PacketsReceived.Value(), // InReceives.
0, // TODO(gvisor.dev/issue/969): Support Ip/InHdrErrors.
ip.InvalidDestinationAddressesReceived.Value(), // InAddrErrors.
0, // TODO(gvisor.dev/issue/969): Support Ip/ForwDatagrams.
0, // TODO(gvisor.dev/issue/969): Support Ip/InUnknownProtos.
0, // TODO(gvisor.dev/issue/969): Support Ip/InDiscards.
ip.PacketsDelivered.Value(), // InDelivers.
ip.PacketsSent.Value(), // OutRequests.
ip.OutgoingPacketErrors.Value(), // OutDiscards.
0, // TODO(gvisor.dev/issue/969): Support Ip/OutNoRoutes.
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmTimeout.
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmReqds.
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmOKs.
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmFails.
0, // TODO(gvisor.dev/issue/969): Support Ip/FragOKs.
0, // TODO(gvisor.dev/issue/969): Support Ip/FragFails.
0, // TODO(gvisor.dev/issue/969): Support Ip/FragCreates.
}
case *inet.StatSNMPICMP:
in := Metrics.ICMP.V4PacketsReceived.ICMPv4PacketStats
+11 -3
View File
@@ -984,7 +984,7 @@ func handlePacket(protocol tcpip.NetworkProtocolNumber, dst, src tcpip.Address,
// DeliverNetworkPacket finds the appropriate network protocol endpoint and
// hands the packet over for further processing. This function is called when
// the NIC receives a packet from the physical interface.
// the NIC receives a packet from the link endpoint.
// Note that the ownership of the slice backing vv is retained by the caller.
// This rule applies only to the slice itself, not to the items of the slice;
// the ownership of the items is not retained by the caller.
@@ -1029,6 +1029,14 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, local tcpip.Link
src, dst := netProto.ParseAddresses(pkt.Data.First())
if n.stack.handleLocal && !n.isLoopback() && n.getRef(protocol, src) != nil {
// The source address is one of our own, so we never should have gotten a
// packet like this unless handleLocal is false. Loopback also calls this
// function even though the packets didn't come from the physical interface
// so don't drop those.
n.stack.stats.IP.InvalidSourceAddressesReceived.Increment()
return
}
if ref := n.getRef(protocol, dst); ref != nil {
handlePacket(protocol, dst, src, linkEP.LinkAddress(), remote, ref, pkt)
return
@@ -1041,7 +1049,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, local tcpip.Link
if n.stack.Forwarding() {
r, err := n.stack.FindRoute(0, "", dst, protocol, false /* multicastLoop */)
if err != nil {
n.stack.stats.IP.InvalidAddressesReceived.Increment()
n.stack.stats.IP.InvalidDestinationAddressesReceived.Increment()
return
}
defer r.Release()
@@ -1079,7 +1087,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, local tcpip.Link
// If a packet socket handled the packet, don't treat it as invalid.
if len(packetEPs) == 0 {
n.stack.stats.IP.InvalidAddressesReceived.Increment()
n.stack.stats.IP.InvalidDestinationAddressesReceived.Increment()
}
}
+7 -3
View File
@@ -903,9 +903,13 @@ type IPStats struct {
// 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
// InvalidDestinationAddressesReceived is the total number of IP packets
// received with an unknown or invalid destination address.
InvalidDestinationAddressesReceived *StatCounter
// InvalidSourceAddressesReceived is the total number of IP packets received
// with a source address that should never have been received on the wire.
InvalidSourceAddressesReceived *StatCounter
// PacketsDelivered is the total number of incoming IP packets that
// are successfully delivered to the transport layer via HandlePacket.
+50 -2
View File
@@ -274,11 +274,16 @@ type testContext struct {
func newDualTestContext(t *testing.T, mtu uint32) *testContext {
t.Helper()
s := stack.New(stack.Options{
return newDualTestContextWithOptions(t, mtu, stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol(), ipv6.NewProtocol()},
TransportProtocols: []stack.TransportProtocol{udp.NewProtocol()},
})
}
func newDualTestContextWithOptions(t *testing.T, mtu uint32, options stack.Options) *testContext {
t.Helper()
s := stack.New(options)
ep := channel.New(256, mtu, "")
wep := stack.LinkEndpoint(ep)
@@ -763,6 +768,49 @@ func TestV6ReadOnV6(t *testing.T) {
testRead(c, unicastV6)
}
// TestV4ReadSelfSource checks that packets coming from a local IP address are
// correctly dropped when handleLocal is true and not otherwise.
func TestV4ReadSelfSource(t *testing.T) {
for _, tt := range []struct {
name string
handleLocal bool
wantErr *tcpip.Error
wantInvalidSource uint64
}{
{"HandleLocal", false, nil, 0},
{"NoHandleLocal", true, tcpip.ErrWouldBlock, 1},
} {
t.Run(tt.name, func(t *testing.T) {
c := newDualTestContextWithOptions(t, defaultMTU, stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol(), ipv6.NewProtocol()},
TransportProtocols: []stack.TransportProtocol{udp.NewProtocol()},
HandleLocal: tt.handleLocal,
})
defer c.cleanup()
c.createEndpointForFlow(unicastV4)
if err := c.ep.Bind(tcpip.FullAddress{Port: stackPort}); err != nil {
t.Fatalf("Bind failed: %s", err)
}
payload := newPayload()
h := unicastV4.header4Tuple(incoming)
h.srcAddr = h.dstAddr
c.injectV4Packet(payload, &h, true /* valid */)
if got := c.s.Stats().IP.InvalidSourceAddressesReceived.Value(); got != tt.wantInvalidSource {
t.Errorf("c.s.Stats().IP.InvalidSourceAddressesReceived got %d, want %d", got, tt.wantInvalidSource)
}
if _, _, err := c.ep.Read(nil); err != tt.wantErr {
t.Errorf("c.ep.Read() got error %v, want %v", err, tt.wantErr)
}
})
}
}
func TestV4ReadOnV4(t *testing.T) {
c := newDualTestContext(t, defaultMTU)
defer c.cleanup()