Allow creation of Raw IP sockets in QUICHE PiperOrigin-RevId: 588217549
diff --git a/quiche/quic/core/io/event_loop_connecting_client_socket_test.cc b/quiche/quic/core/io/event_loop_connecting_client_socket_test.cc index c7e18ce..6cc6961 100644 --- a/quiche/quic/core/io/event_loop_connecting_client_socket_test.cc +++ b/quiche/quic/core/io/event_loop_connecting_client_socket_test.cc
@@ -4,16 +4,16 @@ #include "quiche/quic/core/io/event_loop_connecting_client_socket.h" -#include <functional> #include <memory> #include <optional> #include <string> +#include <tuple> #include <utility> -#include <vector> #include "absl/functional/bind_front.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "quiche/quic/core/connecting_client_socket.h" @@ -22,7 +22,7 @@ #include "quiche/quic/core/io/quic_event_loop.h" #include "quiche/quic/core/io/socket.h" #include "quiche/quic/core/quic_time.h" -#include "quiche/quic/platform/api/quic_ip_address_family.h" +#include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_socket_address.h" #include "quiche/quic/test_tools/mock_clock.h" #include "quiche/quic/test_tools/quic_test_utils.h" @@ -200,6 +200,9 @@ return socket_factory_->CreateTcpClientSocket( peer_address, /*receive_buffer_size=*/0, /*send_buffer_size=*/0, async_visitor); + default: + // Unexpected protocol. + QUICHE_NOTREACHED(); } } @@ -219,6 +222,9 @@ return socket_factory_->CreateTcpClientSocket( peer_address, /*receive_buffer_size=*/0, /*send_buffer_size=*/4, async_visitor); + default: + // Unexpected protocol. + QUICHE_NOTREACHED(); } } @@ -275,6 +281,9 @@ runner = std::make_unique<TestTcpServerSocketRunner>( server_socket_descriptor_, std::move(behavior)); break; + default: + // Unexpected protocol. + QUICHE_NOTREACHED(); } // Runner takes responsibility for closing server socket. @@ -375,6 +384,9 @@ EXPECT_TRUE(connect_result_.value().ok()); socket->Disconnect(); break; + default: + // Unexpected protocol. + FAIL(); } } @@ -411,6 +423,9 @@ // server. EXPECT_TRUE(connect_result_.value().ok()); break; + default: + // Unexpected protocol. + FAIL(); } } @@ -659,6 +674,9 @@ socket->SendAsync(data); expected = data; break; + default: + // Unexpected protocol. + FAIL(); } ASSERT_TRUE(send_result_.has_value()); EXPECT_TRUE(send_result_.value().ok());
diff --git a/quiche/quic/core/io/socket.cc b/quiche/quic/core/io/socket.cc index 0118003..d41106f 100644 --- a/quiche/quic/core/io/socket.cc +++ b/quiche/quic/core/io/socket.cc
@@ -50,10 +50,10 @@ } } -absl::Status SetSockOptInt(SocketFd fd, int option, int value) { +absl::Status SetSockOptInt(SocketFd fd, int level, int option, int value) { QUICHE_DCHECK_NE(fd, kInvalidSocketFd); - int result = SyscallSetsockopt(fd, SOL_SOCKET, option, &value, sizeof(value)); + int result = SyscallSetsockopt(fd, level, option, &value, sizeof(value)); if (result >= 0) { return absl::OkStatus(); @@ -71,14 +71,21 @@ QUICHE_DCHECK_NE(fd, kInvalidSocketFd); QUICHE_DCHECK_LE(size, QuicByteCount{INT_MAX}); - return SetSockOptInt(fd, SO_RCVBUF, static_cast<int>(size)); + return SetSockOptInt(fd, SOL_SOCKET, SO_RCVBUF, static_cast<int>(size)); } absl::Status SetSendBufferSize(SocketFd fd, QuicByteCount size) { QUICHE_DCHECK_NE(fd, kInvalidSocketFd); QUICHE_DCHECK_LE(size, QuicByteCount{INT_MAX}); - return SetSockOptInt(fd, SO_SNDBUF, static_cast<int>(size)); + return SetSockOptInt(fd, SOL_SOCKET, SO_SNDBUF, static_cast<int>(size)); +} + +absl::Status SetIpHeaderIncluded(SocketFd fd, bool ip_header_included) { + QUICHE_DCHECK_NE(fd, kInvalidSocketFd); + + return SetSockOptInt(fd, IPPROTO_IP, IP_HDRINCL, + static_cast<int>(ip_header_included)); } absl::Status Connect(SocketFd fd, const QuicSocketAddress& peer_address) {
diff --git a/quiche/quic/core/io/socket.h b/quiche/quic/core/io/socket.h index edff4ee..319dff4 100644 --- a/quiche/quic/core/io/socket.h +++ b/quiche/quic/core/io/socket.h
@@ -5,9 +5,6 @@ #ifndef QUICHE_QUIC_CORE_IO_SOCKET_H_ #define QUICHE_QUIC_CORE_IO_SOCKET_H_ -#include <functional> -#include <string> - #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -15,7 +12,6 @@ #include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_ip_address_family.h" #include "quiche/quic/platform/api/quic_socket_address.h" -#include "quiche/common/platform/api/quiche_export.h" #if defined(_WIN32) #include <winsock2.h> @@ -42,6 +38,7 @@ enum class SocketProtocol { kUdp, kTcp, + kRawIp, }; inline absl::string_view GetProtocolName(SocketProtocol protocol) { @@ -50,6 +47,8 @@ return "UDP"; case SocketProtocol::kTcp: return "TCP"; + case SocketProtocol::kRawIp: + return "RAW_IP"; } return "unknown"; @@ -76,6 +75,11 @@ absl::Status SetReceiveBufferSize(SocketFd fd, QuicByteCount size); absl::Status SetSendBufferSize(SocketFd fd, QuicByteCount size); +// Only allowed for raw IP sockets. If set, sent data buffers include the IP +// header. If not set, sent data buffers only contain the IP packet payload, and +// the header will be generated. +absl::Status SetIpHeaderIncluded(SocketFd fd, bool ip_header_included); + // Connects socket `fd` to `peer_address`. Returns a status with // `absl::StatusCode::kUnavailable` iff the socket is non-blocking and the // connection could not be immediately completed. The socket will then complete
diff --git a/quiche/quic/core/io/socket_internal.h b/quiche/quic/core/io/socket_internal.h index 3fc9f00..d11c0ef 100644 --- a/quiche/quic/core/io/socket_internal.h +++ b/quiche/quic/core/io/socket_internal.h
@@ -21,6 +21,8 @@ return SOCK_DGRAM; case SocketProtocol::kTcp: return SOCK_STREAM; + case SocketProtocol::kRawIp: + return SOCK_RAW; } QUICHE_NOTREACHED(); @@ -33,6 +35,8 @@ return IPPROTO_UDP; case SocketProtocol::kTcp: return IPPROTO_TCP; + case SocketProtocol::kRawIp: + return IPPROTO_RAW; } QUICHE_NOTREACHED();
diff --git a/quiche/quic/core/io/socket_test.cc b/quiche/quic/core/io/socket_test.cc index da1d24a..9e18cc1 100644 --- a/quiche/quic/core/io/socket_test.cc +++ b/quiche/quic/core/io/socket_test.cc
@@ -5,13 +5,12 @@ #include "quiche/quic/core/io/socket.h" #include <string> -#include <utility> #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" -#include "quiche/quic/platform/api/quic_ip_address_family.h" +#include "quiche/quic/platform/api/quic_ip_address.h" #include "quiche/quic/platform/api/quic_socket_address.h" #include "quiche/common/platform/api/quiche_logging.h" #include "quiche/common/platform/api/quiche_test.h" @@ -39,6 +38,20 @@ } } +SocketFd CreateTestRawSocket(bool blocking = true) { + absl::StatusOr<SocketFd> socket = + socket_api::CreateSocket(quiche::TestLoopback().address_family(), + socket_api::SocketProtocol::kRawIp, blocking); + + if (socket.ok()) { + return socket.value(); + } else { + // This is expected if test not run with relevant admin privileges. + QUICHE_CHECK(absl::IsPermissionDenied(socket.status())); + return kInvalidSocketFd; + } +} + TEST(SocketTest, CreateAndCloseSocket) { QuicIpAddress localhost_address = quiche::TestLoopback(); absl::StatusOr<SocketFd> created_socket = socket_api::CreateSocket( @@ -49,6 +62,22 @@ QUICHE_EXPECT_OK(socket_api::Close(created_socket.value())); } +TEST(SocketTest, CreateAndCloseRawSocket) { + QuicIpAddress localhost_address = quiche::TestLoopback(); + absl::StatusOr<SocketFd> created_socket = socket_api::CreateSocket( + localhost_address.address_family(), socket_api::SocketProtocol::kRawIp); + + // Raw IP socket creation will typically fail if not run with relevant admin + // privileges. + if (!created_socket.ok()) { + EXPECT_THAT(created_socket.status(), + StatusIs(absl::StatusCode::kPermissionDenied)); + return; + } + + QUICHE_EXPECT_OK(socket_api::Close(created_socket.value())); +} + TEST(SocketTest, SetSocketBlocking) { SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp, /*blocking=*/true); @@ -76,6 +105,30 @@ QUICHE_EXPECT_OK(socket_api::Close(socket)); } +TEST(SocketTest, SetIpHeaderIncludedForRaw) { + SocketFd socket = CreateTestRawSocket(/*blocking=*/true); + if (socket == kInvalidSocketFd) { + return; + } + + QUICHE_EXPECT_OK( + socket_api::SetIpHeaderIncluded(socket, /*ip_header_included=*/true)); + + QUICHE_EXPECT_OK(socket_api::Close(socket)); +} + +TEST(SocketTest, SetIpHeaderIncludedForUdp) { + SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp, + /*blocking=*/true); + + // Expect option only allowed for raw IP sockets. + EXPECT_THAT( + socket_api::SetIpHeaderIncluded(socket, /*ip_header_included=*/true), + StatusIs(absl::StatusCode::kInvalidArgument)); + + QUICHE_EXPECT_OK(socket_api::Close(socket)); +} + TEST(SocketTest, Connect) { SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp);