Implement support for reading and writing IPv6 flow labels in QuicUdpSocket.
Protected by FLAGS_quic_reloadable_flag_quic_support_flow_label.
PiperOrigin-RevId: 684868287
diff --git a/build/source_list.bzl b/build/source_list.bzl
index b663445..5aeac68 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -1311,6 +1311,7 @@
"quic/core/quic_time_test.cc",
"quic/core/quic_time_wait_list_manager_test.cc",
"quic/core/quic_trace_visitor_test.cc",
+ "quic/core/quic_udp_socket_test.cc",
"quic/core/quic_unacked_packet_map_test.cc",
"quic/core/quic_utils_test.cc",
"quic/core/quic_version_manager_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 35732c7..ce85b0e 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -1312,6 +1312,7 @@
"src/quiche/quic/core/quic_time_test.cc",
"src/quiche/quic/core/quic_time_wait_list_manager_test.cc",
"src/quiche/quic/core/quic_trace_visitor_test.cc",
+ "src/quiche/quic/core/quic_udp_socket_test.cc",
"src/quiche/quic/core/quic_unacked_packet_map_test.cc",
"src/quiche/quic/core/quic_utils_test.cc",
"src/quiche/quic/core/quic_version_manager_test.cc",
diff --git a/build/source_list.json b/build/source_list.json
index 1f6ca08..c10a0a9 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -1311,6 +1311,7 @@
"quiche/quic/core/quic_time_test.cc",
"quiche/quic/core/quic_time_wait_list_manager_test.cc",
"quiche/quic/core/quic_trace_visitor_test.cc",
+ "quiche/quic/core/quic_udp_socket_test.cc",
"quiche/quic/core/quic_unacked_packet_map_test.cc",
"quiche/quic/core/quic_utils_test.cc",
"quiche/quic/core/quic_version_manager_test.cc",
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index fd1fd04..2235d21 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -49,6 +49,7 @@
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_require_handshake_confirmation, true, true, "If true, require handshake confirmation for QUIC connections, functionally disabling 0-rtt handshakes.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_send_placeholder_ticket_when_encrypt_ticket_fails, false, true, "If true, when TicketCrypter fails to encrypt a session ticket, quic::TlsServerHandshaker will send a placeholder ticket, instead of an empty one, to the client.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_stop_reading_also_stops_header_decompression, true, true, "If true, QUIC stream will not continue decompressing buffer headers after StopReading() called.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_support_flow_label, false, false, "If true, QUIC will support reading and writing IPv6 flow labels.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_test_peer_addr_change_after_normalize, false, false, "If true, QuicConnection::ProcessValidatedPacket will use normalized address to test peer address changes.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_testonly_default_false, false, false, "A testonly reloadable flag that will always default to false.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_testonly_default_true, true, true, "A testonly reloadable flag that will always default to true.")
diff --git a/quiche/quic/core/quic_udp_socket.h b/quiche/quic/core/quic_udp_socket.h
index 404563f..001bc85 100644
--- a/quiche/quic/core/quic_udp_socket.h
+++ b/quiche/quic/core/quic_udp_socket.h
@@ -36,6 +36,7 @@
ECN, // Read
GOOGLE_PACKET_HEADER, // Read
IS_GRO, // Read
+ V6_FLOW_LABEL, // Read & Write
// Must be the last value.
NUM_BITS
@@ -154,13 +155,26 @@
bitmask_.Set(QuicUdpPacketInfoBit::GOOGLE_PACKET_HEADER);
}
- QuicEcnCodepoint ecn_codepoint() const { return ecn_codepoint_; }
+ QuicEcnCodepoint ecn_codepoint() const {
+ QUICHE_DCHECK(HasValue(QuicUdpPacketInfoBit::ECN));
+ return ecn_codepoint_;
+ }
void SetEcnCodepoint(const QuicEcnCodepoint ecn_codepoint) {
ecn_codepoint_ = ecn_codepoint;
bitmask_.Set(QuicUdpPacketInfoBit::ECN);
}
+ uint32_t flow_label() const {
+ QUICHE_DCHECK(HasValue(QuicUdpPacketInfoBit::V6_FLOW_LABEL));
+ return ipv6_flow_label_;
+ }
+
+ void SetFlowLabel(uint32_t ipv6_flow_label) {
+ ipv6_flow_label_ = ipv6_flow_label;
+ bitmask_.Set(QuicUdpPacketInfoBit::V6_FLOW_LABEL);
+ }
+
private:
QuicUdpPacketInfoBitMask bitmask_;
QuicPacketCount dropped_packets_;
@@ -172,6 +186,7 @@
BufferSpan google_packet_headers_;
size_t gso_size_ = 0;
QuicEcnCodepoint ecn_codepoint_ = ECN_NOT_ECT;
+ uint32_t ipv6_flow_label_ = 0;
};
// QuicUdpSocketApi provides a minimal set of apis for sending and receiving
diff --git a/quiche/quic/core/quic_udp_socket_posix.inc b/quiche/quic/core/quic_udp_socket_posix.inc
index 9293bf2..0b8d7b6 100644
--- a/quiche/quic/core/quic_udp_socket_posix.inc
+++ b/quiche/quic/core/quic_udp_socket_posix.inc
@@ -10,6 +10,15 @@
#include <sys/socket.h>
#include <sys/types.h>
+#if defined(__linux__)
+#include <linux/in6.h>
+#ifndef IPV6_FLOWLABEL
+#define IPV6_FLOWINFO 11
+#define IPV6_FLOWINFO_SEND 33
+#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff
+#endif
+#endif
+
#include "absl/base/optimization.h"
#include "quiche/quic/core/io/socket.h"
#include "quiche/quic/core/quic_udp_socket.h"
@@ -145,6 +154,17 @@
}
}
+#if defined(__linux__)
+ if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_FLOWINFO) {
+ if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V6_FLOW_LABEL)) {
+ uint32_t flow_label = 0;
+ memcpy(&flow_label, CMSG_DATA(cmsg), sizeof(flow_label));
+ flow_label = IPV6_FLOWINFO_FLOWLABEL & ntohl(flow_label);
+ packet_info->SetFlowLabel(flow_label);
+ }
+ }
+#endif
+
if (packet_info_interested.IsSet(
QuicUdpPacketInfoBit::GOOGLE_PACKET_HEADER)) {
BufferSpan google_packet_headers;
@@ -223,6 +243,23 @@
<< "IPv6 socket";
return false;
}
+#if defined(__linux__)
+ if (GetQuicReloadableFlag(quic_support_flow_label)) {
+ if (setsockopt(fd, SOL_IPV6, IPV6_FLOWINFO, &set, sizeof(set)) != 0) {
+ QUIC_LOG_FIRST_N(ERROR, 100)
+ << "Failed to request to receive flow label on "
+ << "IPv6 socket";
+ return false;
+ }
+ if (setsockopt(fd, SOL_IPV6, IPV6_FLOWINFO_SEND, &set, sizeof(set)) !=
+ 0) {
+ QUIC_LOG_FIRST_N(ERROR, 100)
+ << "Failed to request to send flow label on "
+ << "IPv6 socket";
+ return false;
+ }
+ }
+#endif
if (!EnableReceiveSelfIpAddressForV6(fd)) {
QUIC_LOG_FIRST_N(ERROR, 100)
<< "Failed to enable receiving of self v6 ip";
@@ -488,6 +525,20 @@
cmsghdr* cmsg = nullptr;
+ // Set IPv6 flow label.
+#if defined(__linux__)
+ if (packet_info.HasValue(QuicUdpPacketInfoBit::V6_FLOW_LABEL) &&
+ packet_info.flow_label() != 0) {
+ uint32_t flowlabel = packet_info.flow_label();
+ if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), SOL_IPV6,
+ IPV6_FLOWINFO, sizeof(flowlabel), &cmsg)) {
+ QUIC_LOG_FIRST_N(ERROR, 100) << "Not enough buffer to set flow label.";
+ return WriteResult(WRITE_STATUS_ERROR, EINVAL);
+ }
+ *reinterpret_cast<uint32_t*>(CMSG_DATA(cmsg)) =
+ htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL);
+ }
+#endif
// Set self IP.
if (packet_info.HasValue(QuicUdpPacketInfoBit::V4_SELF_IP) &&
packet_info.self_v4_ip().IsInitialized()) {
diff --git a/quiche/quic/core/quic_udp_socket_test.cc b/quiche/quic/core/quic_udp_socket_test.cc
new file mode 100644
index 0000000..3d46f87
--- /dev/null
+++ b/quiche/quic/core/quic_udp_socket_test.cc
@@ -0,0 +1,136 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/quic_udp_socket.h"
+
+#include <netinet/in.h>
+#include <stdint.h>
+
+#include <cstddef>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "quiche/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const int kReceiveBufferSize = 16000;
+const int kSendBufferSize = 16000;
+
+class QuicUdpSocketTest : public QuicTest {
+ protected:
+ ABSL_CACHELINE_ALIGNED char packet_buffer_[20];
+ ABSL_CACHELINE_ALIGNED char control_buffer_[512];
+};
+
+TEST_F(QuicUdpSocketTest, Basic) {
+ SetQuicReloadableFlag(quic_support_flow_label, true);
+ const QuicSocketAddress any_address(quiche::QuicheIpAddress::Any6(), 0);
+ QuicUdpSocketApi socket_api;
+
+ SocketFd server_socket =
+ socket_api.Create(AF_INET6, kSendBufferSize, kReceiveBufferSize);
+ ASSERT_NE(kQuicInvalidSocketFd, server_socket);
+ ASSERT_TRUE(socket_api.Bind(server_socket, any_address));
+ QuicSocketAddress server_address;
+ ASSERT_EQ(0, server_address.FromSocket(server_socket));
+
+ SocketFd client_socket =
+ socket_api.Create(AF_INET6, kSendBufferSize, kReceiveBufferSize);
+ ASSERT_NE(kQuicInvalidSocketFd, client_socket);
+ ASSERT_TRUE(socket_api.Bind(client_socket, any_address));
+ QuicSocketAddress client_address;
+ ASSERT_EQ(0, client_address.FromSocket(client_socket));
+
+ QuicUdpPacketInfo packet_info;
+ packet_info.SetPeerAddress(server_address);
+
+ WriteResult write_result;
+ const absl::string_view client_data = "acd";
+ write_result = socket_api.WritePacket(client_socket, client_data.data(),
+ client_data.length(), packet_info);
+ ASSERT_EQ(WRITE_STATUS_OK, write_result.status);
+
+ QuicUdpPacketInfoBitMask packet_info_interested;
+ QuicUdpSocketApi::ReadPacketResult read_result;
+ read_result.packet_buffer = {&packet_buffer_[0], sizeof(packet_buffer_)};
+ read_result.control_buffer = {&control_buffer_[0], sizeof(control_buffer_)};
+
+ socket_api.ReadPacket(server_socket, packet_info_interested, &read_result);
+ ASSERT_TRUE(read_result.ok);
+ ASSERT_EQ(client_data,
+ absl::string_view(read_result.packet_buffer.buffer,
+ read_result.packet_buffer.buffer_len));
+
+ const absl::string_view server_data = "acd";
+ packet_info.SetPeerAddress(client_address);
+ write_result = socket_api.WritePacket(server_socket, server_data.data(),
+ server_data.length(), packet_info);
+ ASSERT_EQ(WRITE_STATUS_OK, write_result.status);
+
+ read_result.Reset(sizeof(packet_buffer_));
+ socket_api.ReadPacket(client_socket, packet_info_interested, &read_result);
+ ASSERT_TRUE(read_result.ok);
+ ASSERT_EQ(server_data,
+ absl::string_view(read_result.packet_buffer.buffer,
+ read_result.packet_buffer.buffer_len));
+}
+
+TEST_F(QuicUdpSocketTest, FlowLabel) {
+ SetQuicReloadableFlag(quic_support_flow_label, true);
+ const QuicSocketAddress any_address(quiche::QuicheIpAddress::Any6(), 0);
+ QuicUdpSocketApi socket_api;
+
+ SocketFd server_socket =
+ socket_api.Create(AF_INET6, kSendBufferSize, kReceiveBufferSize);
+ ASSERT_NE(kQuicInvalidSocketFd, server_socket);
+ ASSERT_TRUE(socket_api.Bind(server_socket, any_address));
+ QuicSocketAddress server_address;
+ ASSERT_EQ(0, server_address.FromSocket(server_socket));
+
+ SocketFd client_socket =
+ socket_api.Create(AF_INET6, kSendBufferSize, kReceiveBufferSize);
+ ASSERT_NE(kQuicInvalidSocketFd, client_socket);
+ ASSERT_TRUE(socket_api.Bind(client_socket, any_address));
+ QuicSocketAddress client_address;
+ ASSERT_EQ(0, client_address.FromSocket(client_socket));
+
+ const absl::string_view data = "a";
+ const uint32_t client_flow_label = 1;
+ QuicUdpPacketInfo packet_info;
+ packet_info.SetFlowLabel(client_flow_label);
+ packet_info.SetPeerAddress(server_address);
+
+ WriteResult write_result;
+ write_result = socket_api.WritePacket(client_socket, data.data(),
+ data.length(), packet_info);
+ ASSERT_EQ(WRITE_STATUS_OK, write_result.status);
+
+ QuicUdpPacketInfoBitMask packet_info_interested(
+ {quic::QuicUdpPacketInfoBit::V6_FLOW_LABEL});
+ QuicUdpSocketApi::ReadPacketResult read_result;
+ read_result.packet_buffer = {&packet_buffer_[0], sizeof(packet_buffer_)};
+ read_result.control_buffer = {&control_buffer_[0], sizeof(control_buffer_)};
+
+ socket_api.ReadPacket(server_socket, packet_info_interested, &read_result);
+ EXPECT_EQ(client_flow_label, read_result.packet_info.flow_label());
+
+ const uint32_t server_flow_label = 3;
+ packet_info.SetPeerAddress(client_address);
+ packet_info.SetFlowLabel(server_flow_label);
+ write_result = socket_api.WritePacket(server_socket, data.data(),
+ data.length(), packet_info);
+ ASSERT_EQ(WRITE_STATUS_OK, write_result.status);
+
+ read_result.Reset(sizeof(packet_buffer_));
+ socket_api.ReadPacket(client_socket, packet_info_interested, &read_result);
+ EXPECT_EQ(server_flow_label, read_result.packet_info.flow_label());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic