Add new metrics to measure throughput in QBONE.

Historically, QBONE relies on metrics exported by the GFE and QUIC to measure throughput in bytes and packets:

* `/gfe/gfe2/http/bytes_in` for throughput in bytes to prod.
* `/gfe/gfe2/http/bytes_out` for throughput in bytes from prod.
* `/gfe/gfe2/spdy4_over_quic/packets_quic_read` for throughput in packets to prod.
* `/gfe/gfe2/spdy4_over_quic/connection_packet_writer_total_packets_sent` for throughput in packets from prod.

The initial motivation for this CL was to add a `traffic_class` dimension to the above methods, but as that involved modified non-QBONE code I decided to implement new QBONE-specific metrics. Each of the metrics in this CL have two dimensions:

* `direction`: TO_PROD or FROM_PROD
* `traffic_class`: A string that represents the traffic class of the packets.

A follow up CL will change the dashboard to use these metrics instead of the old ones once this is fully rolled out.

PiperOrigin-RevId: 620055645
diff --git a/quiche/quic/qbone/qbone_packet_processor.cc b/quiche/quic/qbone/qbone_packet_processor.cc
index 7fdfd0f..a4464d4 100644
--- a/quiche/quic/qbone/qbone_packet_processor.cc
+++ b/quiche/quic/qbone/qbone_packet_processor.cc
@@ -77,6 +77,8 @@
     return;
   }
 
+  stats_->RecordThroughput(packet->size(), direction, traffic_class);
+
   uint8_t transport_protocol;
   char* transport_data;
   icmp6_hdr icmp_header;
diff --git a/quiche/quic/qbone/qbone_packet_processor.h b/quiche/quic/qbone/qbone_packet_processor.h
index b337b19..e69e09a 100644
--- a/quiche/quic/qbone/qbone_packet_processor.h
+++ b/quiche/quic/qbone/qbone_packet_processor.h
@@ -9,6 +9,7 @@
 #include <netinet/in.h>
 #include <netinet/ip6.h>
 
+#include <cstddef>
 #include <cstdint>
 
 #include "absl/strings/string_view.h"
@@ -61,6 +62,9 @@
     virtual void SendPacketToNetwork(absl::string_view packet) = 0;
   };
 
+  // A visitor interface that allows the packet processor to collect stats
+  // without relying on a specific backend or exposing the entire packet.
+  // |traffic_class| should be extracted directly from the IPv6 header.
   class StatsInterface {
    public:
     virtual ~StatsInterface();
@@ -75,6 +79,8 @@
                                              uint8_t traffic_class) = 0;
     virtual void OnPacketDeferred(Direction direction,
                                   uint8_t traffic_class) = 0;
+    virtual void RecordThroughput(size_t bytes, Direction direction,
+                                  uint8_t traffic_class) = 0;
   };
 
   // Allows to implement a custom packet filter on top of the filtering done by
diff --git a/quiche/quic/qbone/qbone_packet_processor_test.cc b/quiche/quic/qbone/qbone_packet_processor_test.cc
index f3361b7..cbb63ed 100644
--- a/quiche/quic/qbone/qbone_packet_processor_test.cc
+++ b/quiche/quic/qbone/qbone_packet_processor_test.cc
@@ -333,6 +333,9 @@
     processor_ = std::make_unique<QbonePacketProcessor>(
         self_ip_, client_ip_, /*client_ip_subnet_length=*/62, &output_,
         &stats_);
+
+    // Ignore calls to RecordThroughput
+    EXPECT_CALL(stats_, RecordThroughput(_, _, _)).WillRepeatedly(Return());
   }
 
   void SendPacketFromClient(absl::string_view packet) {
@@ -356,9 +359,11 @@
 
 TEST_F(QbonePacketProcessorTest, EmptyPacket) {
   EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_OFF_NETWORK, _));
+  EXPECT_CALL(stats_, RecordThroughput(0, Direction::FROM_OFF_NETWORK, _));
   SendPacketFromClient("");
 
   EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_NETWORK, _));
+  EXPECT_CALL(stats_, RecordThroughput(0, Direction::FROM_NETWORK, _));
   SendPacketFromNetwork("");
 }
 
@@ -476,21 +481,29 @@
 
 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());
+  EXPECT_CALL(stats_, RecordThroughput(kReferenceClientPacket.size(),
+                                       Direction::FROM_OFF_NETWORK, 0));
   SendPacketFromClient(kReferenceClientPacket);
-  ASSERT_EQ(0, filter->last_tos());
+
+  EXPECT_CALL(stats_, RecordThroughput(kReferenceClientPacketAF4.size(),
+                                       Direction::FROM_OFF_NETWORK, 0x80));
   SendPacketFromClient(kReferenceClientPacketAF4);
-  ASSERT_EQ(0x80, filter->last_tos());
+
+  EXPECT_CALL(stats_, RecordThroughput(kReferenceClientPacketAF3.size(),
+                                       Direction::FROM_OFF_NETWORK, 0x60));
   SendPacketFromClient(kReferenceClientPacketAF3);
-  ASSERT_EQ(0x60, filter->last_tos());
+
+  EXPECT_CALL(stats_, RecordThroughput(kReferenceClientPacketAF2.size(),
+                                       Direction::FROM_OFF_NETWORK, 0x40));
   SendPacketFromClient(kReferenceClientPacketAF2);
-  ASSERT_EQ(0x40, filter->last_tos());
+
+  EXPECT_CALL(stats_, RecordThroughput(kReferenceClientPacketAF1.size(),
+                                       Direction::FROM_OFF_NETWORK, 0x20));
   SendPacketFromClient(kReferenceClientPacketAF1);
-  ASSERT_EQ(0x20, filter->last_tos());
 }
 
 TEST_F(QbonePacketProcessorTest, Icmp6EchoResponseHasRightPayload) {
diff --git a/quiche/quic/qbone/qbone_packet_processor_test_tools.h b/quiche/quic/qbone/qbone_packet_processor_test_tools.h
index 9b0ff85..619d5ef 100644
--- a/quiche/quic/qbone/qbone_packet_processor_test_tools.h
+++ b/quiche/quic/qbone/qbone_packet_processor_test_tools.h
@@ -35,6 +35,8 @@
               (QbonePacketProcessor::Direction, uint8_t), (override));
   MOCK_METHOD(void, OnPacketDeferred,
               (QbonePacketProcessor::Direction, uint8_t), (override));
+  MOCK_METHOD(void, RecordThroughput,
+              (size_t, QbonePacketProcessor::Direction, uint8_t), (override));
 };
 
 std::string PrependIPv6HeaderForTest(const std::string& body, int hops);
diff --git a/quiche/quic/qbone/qbone_server_session.h b/quiche/quic/qbone/qbone_server_session.h
index 0f325a6..74abad4 100644
--- a/quiche/quic/qbone/qbone_server_session.h
+++ b/quiche/quic/qbone/qbone_server_session.h
@@ -71,6 +71,8 @@
                                    uint8_t traffic_class) override {}
   void OnPacketDeferred(QbonePacketProcessor::Direction direction,
                         uint8_t traffic_class) override {}
+  void RecordThroughput(size_t bytes, QbonePacketProcessor::Direction direction,
+                        uint8_t traffic_class) override {}
 
  protected:
   // QboneSessionBase interface implementation.