Add support for setting SO_MARK on ICMP sockets. When set it will be used to manually steer the socket traffic to a specific device queue at the kernel routing. PiperOrigin-RevId: 975276589
diff --git a/quiche/quic/qbone/bonnet/icmp_reachable.cc b/quiche/quic/qbone/bonnet/icmp_reachable.cc index 7bee28c..42f1d1f 100644 --- a/quiche/quic/qbone/bonnet/icmp_reachable.cc +++ b/quiche/quic/qbone/bonnet/icmp_reachable.cc
@@ -5,14 +5,21 @@ #include "quiche/quic/qbone/bonnet/icmp_reachable.h" #include <netinet/ip6.h> +#include <sys/socket.h> +#include <cerrno> +#include <cstddef> +#include <cstdint> +#include <cstring> #include <string> #include "absl/strings/string_view.h" -#include "quiche/quic/core/crypto/quic_random.h" +#include "absl/synchronization/mutex.h" #include "quiche/quic/core/io/quic_event_loop.h" +#include "quiche/quic/core/quic_time.h" +#include "quiche/quic/platform/api/quic_ip_address.h" #include "quiche/quic/platform/api/quic_logging.h" -#include "quiche/quic/qbone/platform/icmp_packet.h" +#include "quiche/quic/qbone/platform/kernel_interface.h" #include "quiche/common/platform/api/quiche_logging.h" #include "quiche/common/quiche_text_utils.h" @@ -33,7 +40,8 @@ IcmpReachable::IcmpReachable(absl::string_view interface_name, QuicIpAddress source, QuicIpAddress destination, QuicTime::Delta timeout, KernelInterface* kernel, - QuicEventLoop* event_loop, StatsInterface* stats) + QuicEventLoop* event_loop, StatsInterface* stats, + uint32_t socket_mark) : timeout_(timeout), event_loop_(event_loop), clock_(event_loop->GetClock()), @@ -42,7 +50,8 @@ alarm_(alarm_factory_->CreateAlarm(new AlarmCallback(this))), kernel_(kernel), stats_(stats), - sock_fd_(0) { + sock_fd_(0), + socket_mark_(socket_mark) { src_.sin6_family = AF_INET6; dst_.sin6_family = AF_INET6; // Ensure the destination has its scope set to the QBONE TUN/TAP device. @@ -71,6 +80,14 @@ return false; } + if (socket_mark_ > 0) { + if (kernel_->setsockopt(sock_fd_, SOL_SOCKET, SO_MARK, &socket_mark_, + sizeof(socket_mark_)) < 0) { + QUIC_PLOG(ERROR) << "Unable to set SO_MARK on ICMP socket."; + return false; + } + } + if (kernel_->bind(sock_fd_, reinterpret_cast<struct sockaddr*>(&src_), sizeof(sockaddr_in6)) < 0) { QUIC_PLOG(ERROR) << "Unable to bind ICMP socket.";
diff --git a/quiche/quic/qbone/bonnet/icmp_reachable.h b/quiche/quic/qbone/bonnet/icmp_reachable.h index d88632a..b9aae77 100644 --- a/quiche/quic/qbone/bonnet/icmp_reachable.h +++ b/quiche/quic/qbone/bonnet/icmp_reachable.h
@@ -7,7 +7,9 @@ #include <netinet/icmp6.h> +#include <cstdint> #include <memory> +#include <string> #include "absl/base/thread_annotations.h" #include "absl/strings/string_view.h" @@ -73,10 +75,12 @@ // Server's thread. // |stats| is not owned, but should outlive this instance. It will be called // back on Echo Replies, timeouts, and I/O errors. + // |socket_mark| is the SO_MARK value to apply to the ICMP socket (for + // multiqueue queue steering). Defaults to 0 (no mark). IcmpReachable(absl::string_view interface_name, QuicIpAddress source, QuicIpAddress destination, QuicTime::Delta timeout, KernelInterface* kernel, QuicEventLoop* event_loop, - StatsInterface* stats); + StatsInterface* stats, uint32_t socket_mark = 0); ~IcmpReachable() override; @@ -142,6 +146,8 @@ QuicTime start_ = QuicTime::Zero(); QuicTime end_ = QuicTime::Zero(); + + uint32_t socket_mark_ = 0; }; } // namespace quic
diff --git a/quiche/quic/qbone/bonnet/icmp_reachable_test.cc b/quiche/quic/qbone/bonnet/icmp_reachable_test.cc index 492d578..b7e8f0f 100644 --- a/quiche/quic/qbone/bonnet/icmp_reachable_test.cc +++ b/quiche/quic/qbone/bonnet/icmp_reachable_test.cc
@@ -6,16 +6,23 @@ #include <netinet/ip6.h> +#include <cerrno> +#include <cstddef> +#include <cstdint> +#include <cstring> #include <memory> #include <string> #include "absl/container/node_hash_map.h" +#include "absl/strings/string_view.h" #include "quiche/quic/core/io/quic_default_event_loop.h" #include "quiche/quic/core/io/quic_event_loop.h" #include "quiche/quic/core/quic_default_clock.h" +#include "quiche/quic/core/quic_time.h" #include "quiche/quic/platform/api/quic_ip_address.h" #include "quiche/quic/platform/api/quic_test.h" #include "quiche/quic/qbone/platform/mock_kernel.h" +#include "quiche/common/platform/api/quiche_logging.h" namespace quic::test { namespace { @@ -266,5 +273,33 @@ EXPECT_EQ(stats_.ReadErrorCount(EIO), 1); } +TEST_F(IcmpReachableTest, SetsSocketMarkWhenConfigured) { + uint32_t socket_mark = 1; + InSequence seq; + EXPECT_CALL(kernel_, if_nametoindex(_)); + EXPECT_CALL(kernel_, socket(_, _, _)).WillOnce(Return(simulated_sock_fd_)); + EXPECT_CALL(kernel_, setsockopt(simulated_sock_fd_, SOL_SOCKET, SO_MARK, _, + sizeof(socket_mark))) + .WillOnce([socket_mark](int fd, int level, int optname, + const void* optval, socklen_t optlen) { + EXPECT_EQ(*reinterpret_cast<const uint32_t*>(optval), socket_mark); + return 0; + }); + EXPECT_CALL(kernel_, bind(simulated_sock_fd_, _, _)).WillOnce(Return(0)); + EXPECT_CALL(kernel_, getsockname(_, _, _)) + .WillOnce(DoAll( + SetArgPointee<1>(*reinterpret_cast<struct sockaddr*>(&send_socket_)), + Return(0))); + EXPECT_CALL(kernel_, close(simulated_sock_fd_)).WillOnce([](int fd) { + return close(fd); + }); + + IcmpReachable reachable(kInterfaceName, source_, destination_, + QuicTime::Delta::Zero(), &kernel_, event_loop_.get(), + &stats_, socket_mark); + + ASSERT_TRUE(reachable.Init()); +} + } // namespace } // namespace quic::test