raw sockets: don't overwrite destination address

Also makes the behavior of raw sockets WRT fragmentation clearer, and makes the
ICMPv4 header-length check explicit.

Fixes #3160.

PiperOrigin-RevId: 380033450
This commit is contained in:
Kevin Krakauer
2021-06-17 13:35:29 -07:00
committed by gVisor bot
parent 34152da7e5
commit 0f5c1f5eaf
10 changed files with 71 additions and 38 deletions
+27 -13
View File
@@ -88,6 +88,7 @@ type testObject struct {
dataCalls int
controlCalls int
rawCalls int
}
// checkValues verifies that the transport protocol, data contents, src & dst
@@ -148,6 +149,10 @@ func (t *testObject) DeliverTransportError(local, remote tcpip.Address, net tcpi
t.controlCalls++
}
func (t *testObject) DeliverRawPacket(tcpip.TransportProtocolNumber, *stack.PacketBuffer) {
t.rawCalls++
}
// Attach is only implemented to satisfy the LinkEndpoint interface.
func (*testObject) Attach(stack.NetworkDispatcher) {}
@@ -717,7 +722,10 @@ func TestReceive(t *testing.T) {
}
test.handlePacket(t, ep, &nic)
if nic.testObject.dataCalls != 1 {
t.Errorf("Bad number of data calls: got %x, want 1", nic.testObject.dataCalls)
t.Errorf("Bad number of data calls: got %d, want 1", nic.testObject.dataCalls)
}
if nic.testObject.rawCalls != 1 {
t.Errorf("Bad number of raw calls: got %d, want 1", nic.testObject.rawCalls)
}
if got := stat.Value(); got != 1 {
t.Errorf("got s.Stats().IP.PacketsReceived.Value() = %d, want = 1", got)
@@ -968,7 +976,10 @@ func TestIPv4FragmentationReceive(t *testing.T) {
ep.HandlePacket(pkt)
if nic.testObject.dataCalls != 0 {
t.Fatalf("Bad number of data calls: got %x, want 0", nic.testObject.dataCalls)
t.Fatalf("Bad number of data calls: got %d, want 0", nic.testObject.dataCalls)
}
if nic.testObject.rawCalls != 0 {
t.Errorf("Bad number of raw calls: got %d, want 0", nic.testObject.rawCalls)
}
// Send second segment.
@@ -977,7 +988,10 @@ func TestIPv4FragmentationReceive(t *testing.T) {
})
ep.HandlePacket(pkt)
if nic.testObject.dataCalls != 1 {
t.Fatalf("Bad number of data calls: got %x, want 1", nic.testObject.dataCalls)
t.Fatalf("Bad number of data calls: got %d, want 1", nic.testObject.dataCalls)
}
if nic.testObject.rawCalls != 1 {
t.Errorf("Bad number of raw calls: got %d, want 1", nic.testObject.rawCalls)
}
}
@@ -1310,7 +1324,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv4Addr,
})
return hdr.View().ToVectorisedView()
},
@@ -1351,7 +1365,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv4Addr,
})
ip.SetHeaderLength(header.IPv4MinimumSize - 1)
return hdr.View().ToVectorisedView()
@@ -1370,7 +1384,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv4Addr,
})
return buffer.View(ip[:len(ip)-1]).ToVectorisedView()
},
@@ -1388,7 +1402,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv4Addr,
})
return buffer.View(ip).ToVectorisedView()
},
@@ -1430,7 +1444,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv4Addr,
Options: ipv4Options,
})
return hdr.View().ToVectorisedView()
@@ -1469,7 +1483,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv4Addr,
Options: ipv4Options,
})
vv := buffer.View(ip).ToVectorisedView()
@@ -1515,7 +1529,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
TransportProtocol: transportProto,
HopLimit: ipv6.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv6Addr,
})
return hdr.View().ToVectorisedView()
},
@@ -1560,7 +1574,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
TransportProtocol: tcpip.TransportProtocolNumber(header.IPv6FragmentExtHdrIdentifier),
HopLimit: ipv6.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv6Addr,
})
return hdr.View().ToVectorisedView()
},
@@ -1595,7 +1609,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
TransportProtocol: transportProto,
HopLimit: ipv6.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv6Addr,
})
return buffer.View(ip).ToVectorisedView()
},
@@ -1630,7 +1644,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
TransportProtocol: transportProto,
HopLimit: ipv6.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
DstAddr: remoteIPv4Addr,
})
return buffer.View(ip[:len(ip)-1]).ToVectorisedView()
},
+10 -4
View File
@@ -614,10 +614,6 @@ func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, pkt *stack.PacketBu
ipH.SetSourceAddress(r.LocalAddress())
}
// Set the destination. If the packet already included a destination, it will
// be part of the route anyways.
ipH.SetDestinationAddress(r.RemoteAddress())
// Set the packet ID when zero.
if ipH.ID() == 0 {
// RFC 6864 section 4.3 mandates uniqueness of ID values for
@@ -860,6 +856,13 @@ func (e *endpoint) handleLocalPacket(pkt *stack.PacketBuffer, canSkipRXChecksum
}
func (e *endpoint) handleValidatedPacket(h header.IPv4, pkt *stack.PacketBuffer, inNICName string) {
// Raw socket packets are delivered based solely on the transport protocol
// number. We only require that the packet be valid IPv4, and that they not
// be fragmented.
if !h.More() && h.FragmentOffset() == 0 {
e.dispatcher.DeliverRawPacket(h.TransportProtocol(), pkt)
}
pkt.NICID = e.nic.ID()
stats := e.stats
stats.ip.ValidPacketsReceived.Increment()
@@ -995,6 +998,9 @@ func (e *endpoint) handleValidatedPacket(h header.IPv4, pkt *stack.PacketBuffer,
// to do it here.
h.SetTotalLength(uint16(pkt.Data().Size() + len(h)))
h.SetFlagsFragmentOffset(0, 0)
// Now that the packet is reassembled, it can be sent to raw sockets.
e.dispatcher.DeliverRawPacket(h.TransportProtocol(), pkt)
}
stats.ip.PacketsDelivered.Increment()
+4
View File
@@ -90,6 +90,10 @@ func (*stubDispatcher) DeliverTransportPacket(tcpip.TransportProtocolNumber, *st
return stack.TransportPacketHandled
}
func (*stubDispatcher) DeliverRawPacket(tcpip.TransportProtocolNumber, *stack.PacketBuffer) {
// No-op.
}
var _ stack.NetworkInterface = (*testInterface)(nil)
type testInterface struct {
+4 -4
View File
@@ -928,10 +928,6 @@ func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, pkt *stack.PacketBu
ipH.SetSourceAddress(r.LocalAddress())
}
// Set the destination. If the packet already included a destination, it will
// be part of the route anyways.
ipH.SetDestinationAddress(r.RemoteAddress())
// Populate the packet buffer's network header and don't allow an invalid
// packet to be sent.
//
@@ -1128,6 +1124,10 @@ func (e *endpoint) handleLocalPacket(pkt *stack.PacketBuffer, canSkipRXChecksum
}
func (e *endpoint) handleValidatedPacket(h header.IPv6, pkt *stack.PacketBuffer, inNICName string) {
// Raw socket packets are delivered based solely on the transport protocol
// number. We only require that the packet be valid IPv6.
e.dispatcher.DeliverRawPacket(h.TransportProtocol(), pkt)
pkt.NICID = e.nic.ID()
stats := e.stats.ip
stats.ValidPacketsReceived.Increment()
+11 -5
View File
@@ -779,11 +779,6 @@ func (n *nic) DeliverTransportPacket(protocol tcpip.TransportProtocolNumber, pkt
transProto := state.proto
// Raw socket packets are delivered based solely on the transport
// protocol number. We do not inspect the payload to ensure it's
// validly formed.
n.stack.demux.deliverRawPacket(protocol, pkt)
// TransportHeader is empty only when pkt is an ICMP packet or was reassembled
// from fragments.
if pkt.TransportHeader().View().IsEmpty() {
@@ -877,6 +872,17 @@ func (n *nic) DeliverTransportError(local, remote tcpip.Address, net tcpip.Netwo
}
}
// DeliverRawPacket implements TransportDispatcher.
func (n *nic) DeliverRawPacket(protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) {
// For ICMPv4 only we validate the header length for compatibility with
// raw(7) ICMP_FILTER. The same check is made in Linux here:
// https://github.com/torvalds/linux/blob/70585216/net/ipv4/raw.c#L189.
if protocol == header.ICMPv4ProtocolNumber && pkt.TransportHeader().View().Size()+pkt.Data().Size() < header.ICMPv4MinimumSize {
return
}
n.stack.demux.deliverRawPacket(protocol, pkt)
}
// ID implements NetworkInterface.
func (n *nic) ID() tcpip.NICID {
return n.id
+5
View File
@@ -265,6 +265,11 @@ type TransportDispatcher interface {
//
// DeliverTransportError takes ownership of the packet buffer.
DeliverTransportError(local, remote tcpip.Address, _ tcpip.NetworkProtocolNumber, _ tcpip.TransportProtocolNumber, _ TransportError, _ *PacketBuffer)
// DeliverRawPacket delivers a packet to any subscribed raw sockets.
//
// DeliverRawPacket does NOT take ownership of the packet buffer.
DeliverRawPacket(tcpip.TransportProtocolNumber, *PacketBuffer)
}
// PacketLooping specifies where an outbound packet should be sent.
+1
View File
@@ -16,6 +16,7 @@ package stack
import (
"fmt"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/hash/jenkins"
-1
View File
@@ -424,7 +424,6 @@ func (ep *endpoint) HandlePacket(nicID tcpip.NICID, localAddr tcpip.LinkAddress,
default:
panic(fmt.Sprintf("unexpected PktType in pkt: %+v", pkt))
}
} else {
// Raw packets need their ethernet headers prepended before
// queueing.
+7 -9
View File
@@ -281,9 +281,6 @@ TEST_F(RawHDRINCL, SendAndReceive) {
// Send and receive a packet where the sendto address is not the same as the
// provided destination.
TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
// FIXME(gvisor.dev/issue/3160): Test currently flaky.
SKIP_IF(true);
int port = 40000;
if (!IsRunningOnGvisor()) {
port = static_cast<short>(ASSERT_NO_ERRNO_AND_VALUE(
@@ -301,18 +298,20 @@ TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
ASSERT_TRUE(
FillPacket(packet, sizeof(packet), port, kPayload, sizeof(kPayload)));
// Overwrite the IP destination address with an IP we can't get to.
constexpr int32_t kUnreachable = 42;
struct iphdr iphdr = {};
memcpy(&iphdr, packet, sizeof(iphdr));
iphdr.daddr = 42;
iphdr.daddr = kUnreachable;
memcpy(packet, &iphdr, sizeof(iphdr));
// Send to localhost via loopback.
socklen_t addrlen = sizeof(addr_);
ASSERT_NO_FATAL_FAILURE(sendto(socket_, &packet, sizeof(packet), 0,
reinterpret_cast<struct sockaddr*>(&addr_),
addrlen));
// Receive the payload, since sendto should replace the bad destination with
// localhost.
// Receive the payload. Despite an unreachable destination address, sendto
// should have sent the packet through loopback.
char recv_buf[sizeof(packet)];
struct sockaddr_in src;
socklen_t src_size = sizeof(src);
@@ -330,9 +329,8 @@ TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
struct iphdr recv_iphdr = {};
memcpy(&recv_iphdr, recv_buf, sizeof(recv_iphdr));
EXPECT_NE(recv_iphdr.id, 0);
// The destination address should be localhost, not the bad IP we set
// initially.
EXPECT_EQ(absl::gbswap_32(recv_iphdr.daddr), INADDR_LOOPBACK);
// The destination address is kUnreachable despite arriving via loopback.
EXPECT_EQ(recv_iphdr.daddr, kUnreachable);
}
// Send and receive a packet w/ the IP_HDRINCL option set.
+2 -2
View File
@@ -262,8 +262,8 @@ TEST_F(RawSocketICMPTest, RawAndPingSockets) {
}
// A raw ICMP socket should be able to send a malformed short ICMP Echo Request,
// while ping socket should not.
// Neither should be able to receieve a short malformed packet.
// while a ping socket should not. Neither should be able to receieve a short
// malformed packet.
TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));