Expose traffic class bits in `QbonePacketProcessor::Filter`

PiperOrigin-RevId: 595830895
diff --git a/quiche/quic/qbone/qbone_packet_processor.h b/quiche/quic/qbone/qbone_packet_processor.h
index 77eb13e..fed7a8b 100644
--- a/quiche/quic/qbone/qbone_packet_processor.h
+++ b/quiche/quic/qbone/qbone_packet_processor.h
@@ -110,6 +110,10 @@
     uint8_t TransportProtocolFromHeader(absl::string_view ipv6_header) {
       return ipv6_header[6];
     }
+    uint8_t TrafficClassFromHeader(absl::string_view ipv6_header) {
+      return ipv6_header[0] << 4 | ipv6_header[1] >> 4;
+    }
+
     QuicIpAddress SourceIpFromHeader(absl::string_view ipv6_header) {
       QuicIpAddress address;
       address.FromPackedString(&ipv6_header[8],
diff --git a/quiche/quic/qbone/qbone_packet_processor_test.cc b/quiche/quic/qbone/qbone_packet_processor_test.cc
index 519ef43..85cf0ca 100644
--- a/quiche/quic/qbone/qbone_packet_processor_test.cc
+++ b/quiche/quic/qbone/qbone_packet_processor_test.cc
@@ -50,6 +50,110 @@
     0x00, 0x00,
 };
 
+static const char kReferenceClientPacketDataAF4[] = {
+    // IPv6 with 0x80 TOS and zero flow label.
+    0x68, 0x00, 0x00, 0x00,
+    // Payload size is 8 bytes.
+    0x00, 0x08,
+    // Next header is UDP
+    17,
+    // TTL is 50.
+    50,
+    // IP address of the sender is fd00:0:0:1::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // IP address of the receiver is fd00:0:0:5::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // Source port 12345
+    0x30, 0x39,
+    // Destination port 443
+    0x01, 0xbb,
+    // UDP content length is zero
+    0x00, 0x00,
+    // Checksum is not actually checked in any of the tests, so we leave it as
+    // zero
+    0x00, 0x00,
+};
+
+static const char kReferenceClientPacketDataAF3[] = {
+    // IPv6 with 0x60 TOS and zero flow label.
+    0x66, 0x00, 0x00, 0x00,
+    // Payload size is 8 bytes.
+    0x00, 0x08,
+    // Next header is UDP
+    17,
+    // TTL is 50.
+    50,
+    // IP address of the sender is fd00:0:0:1::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // IP address of the receiver is fd00:0:0:5::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // Source port 12345
+    0x30, 0x39,
+    // Destination port 443
+    0x01, 0xbb,
+    // UDP content length is zero
+    0x00, 0x00,
+    // Checksum is not actually checked in any of the tests, so we leave it as
+    // zero
+    0x00, 0x00,
+};
+
+static const char kReferenceClientPacketDataAF2[] = {
+    // IPv6 with 0x40 TOS and zero flow label.
+    0x64, 0x00, 0x00, 0x00,
+    // Payload size is 8 bytes.
+    0x00, 0x08,
+    // Next header is UDP
+    17,
+    // TTL is 50.
+    50,
+    // IP address of the sender is fd00:0:0:1::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // IP address of the receiver is fd00:0:0:5::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // Source port 12345
+    0x30, 0x39,
+    // Destination port 443
+    0x01, 0xbb,
+    // UDP content length is zero
+    0x00, 0x00,
+    // Checksum is not actually checked in any of the tests, so we leave it as
+    // zero
+    0x00, 0x00,
+};
+
+static const char kReferenceClientPacketDataAF1[] = {
+    // IPv6 with 0x20 TOS and zero flow label.
+    0x62, 0x00, 0x00, 0x00,
+    // Payload size is 8 bytes.
+    0x00, 0x08,
+    // Next header is UDP
+    17,
+    // TTL is 50.
+    50,
+    // IP address of the sender is fd00:0:0:1::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // IP address of the receiver is fd00:0:0:5::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // Source port 12345
+    0x30, 0x39,
+    // Destination port 443
+    0x01, 0xbb,
+    // UDP content length is zero
+    0x00, 0x00,
+    // Checksum is not actually checked in any of the tests, so we leave it as
+    // zero
+    0x00, 0x00,
+};
+
 static const char kReferenceNetworkPacketData[] = {
     // IPv6 with zero TOS and flow label.
     0x60, 0x00, 0x00, 0x00,
@@ -179,6 +283,19 @@
 static const absl::string_view kReferenceClientPacket(
     kReferenceClientPacketData, ABSL_ARRAYSIZE(kReferenceClientPacketData));
 
+static const absl::string_view kReferenceClientPacketAF4(
+    kReferenceClientPacketDataAF4,
+    ABSL_ARRAYSIZE(kReferenceClientPacketDataAF4));
+static const absl::string_view kReferenceClientPacketAF3(
+    kReferenceClientPacketDataAF3,
+    ABSL_ARRAYSIZE(kReferenceClientPacketDataAF3));
+static const absl::string_view kReferenceClientPacketAF2(
+    kReferenceClientPacketDataAF2,
+    ABSL_ARRAYSIZE(kReferenceClientPacketDataAF2));
+static const absl::string_view kReferenceClientPacketAF1(
+    kReferenceClientPacketDataAF1,
+    ABSL_ARRAYSIZE(kReferenceClientPacketDataAF1));
+
 static const absl::string_view kReferenceNetworkPacket(
     kReferenceNetworkPacketData, ABSL_ARRAYSIZE(kReferenceNetworkPacketData));
 
@@ -329,14 +446,17 @@
     EXPECT_EQ(client_ip_, SourceIpFromHeader(full_packet));
     EXPECT_EQ(network_ip_, DestinationIpFromHeader(full_packet));
 
+    last_tos_ = TrafficClassFromHeader(full_packet);
     called_++;
     return ProcessingResult::SILENT_DROP;
   }
 
   int called() const { return called_; }
+  uint8_t last_tos() const { return last_tos_; }
 
  private:
   int called_ = 0;
+  uint8_t last_tos_ = 0;
 
   QuicIpAddress client_ip_;
   QuicIpAddress network_ip_;
@@ -354,6 +474,25 @@
   ASSERT_EQ(1, filter->called());
 }
 
+TEST_F(QbonePacketProcessorTest, FilterHelperFunctionsTOS) {
+  auto filter_owned = std::make_unique<TestFilter>(client_ip_, network_ip_);
+  TestFilter* filter = filter_owned.get();
+  processor_->set_filter(std::move(filter_owned));
+
+  EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_OFF_NETWORK))
+      .Times(testing::AnyNumber());
+  SendPacketFromClient(kReferenceClientPacket);
+  ASSERT_EQ(0, filter->last_tos());
+  SendPacketFromClient(kReferenceClientPacketAF4);
+  ASSERT_EQ(0x80, filter->last_tos());
+  SendPacketFromClient(kReferenceClientPacketAF3);
+  ASSERT_EQ(0x60, filter->last_tos());
+  SendPacketFromClient(kReferenceClientPacketAF2);
+  ASSERT_EQ(0x40, filter->last_tos());
+  SendPacketFromClient(kReferenceClientPacketAF1);
+  ASSERT_EQ(0x20, filter->last_tos());
+}
+
 TEST_F(QbonePacketProcessorTest, Icmp6EchoResponseHasRightPayload) {
   auto filter = std::make_unique<MockPacketFilter>();
   EXPECT_CALL(*filter, FilterPacket(_, _, _, _, _))