mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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."),
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user