mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
committed by
gVisor bot
parent
34152da7e5
commit
0f5c1f5eaf
@@ -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()
|
||||
},
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user