Create a socket relay for connecting a socket on the qbone interface to
another interface.

Protected by FLAGS_qbone_client_config_file.

PiperOrigin-RevId: 380879625
diff --git a/quic/qbone/bonnet/tun_device_controller.cc b/quic/qbone/bonnet/tun_device_controller.cc
index 4a5c9e7..dd996c5 100644
--- a/quic/qbone/bonnet/tun_device_controller.cc
+++ b/quic/qbone/bonnet/tun_device_controller.cc
@@ -49,6 +49,10 @@
 
   if (address_updated) {
     current_address_ = desired_address;
+
+    for (const auto& cb : address_update_cbs_) {
+      cb(current_address_);
+    }
   }
 
   return address_updated;
@@ -161,4 +165,9 @@
   return current_address_;
 }
 
+void TunDeviceController::RegisterAddressUpdateCallback(
+    const std::function<void(QuicIpAddress)>& cb) {
+  address_update_cbs_.push_back(cb);
+}
+
 }  // namespace quic
diff --git a/quic/qbone/bonnet/tun_device_controller.h b/quic/qbone/bonnet/tun_device_controller.h
index 89464c3..612e98f 100644
--- a/quic/qbone/bonnet/tun_device_controller.h
+++ b/quic/qbone/bonnet/tun_device_controller.h
@@ -50,6 +50,9 @@
       const std::vector<IpRange>& desired_routes,
       int retries);
 
+  virtual void RegisterAddressUpdateCallback(
+      const std::function<void(QuicIpAddress)>& cb);
+
   virtual QuicIpAddress current_address();
 
  private:
@@ -62,6 +65,8 @@
   NetlinkInterface* netlink_;
 
   QuicIpAddress current_address_;
+
+  std::vector<std::function<void(QuicIpAddress)>> address_update_cbs_;
 };
 
 }  // namespace quic
diff --git a/quic/qbone/bonnet/tun_device_controller_test.cc b/quic/qbone/bonnet/tun_device_controller_test.cc
index 73a7abc..53e5b3c 100644
--- a/quic/qbone/bonnet/tun_device_controller_test.cc
+++ b/quic/qbone/bonnet/tun_device_controller_test.cc
@@ -44,8 +44,10 @@
  public:
   TunDeviceControllerTest()
       : controller_(kIfname, true, &netlink_),
-        link_local_range_(
-            *QboneConstants::TerminatorLocalAddressRange()) {}
+        link_local_range_(*QboneConstants::TerminatorLocalAddressRange()) {
+    controller_.RegisterAddressUpdateCallback(
+        [this](QuicIpAddress address) { notified_address_ = address; });
+  }
 
  protected:
   void ExpectLinkInfo(const std::string& interface_name, int ifindex) {
@@ -60,6 +62,7 @@
 
   MockNetlink netlink_;
   TunDeviceController controller_;
+  QuicIpAddress notified_address_;
 
   IpRange link_local_range_;
 };
@@ -77,6 +80,7 @@
       .WillOnce(Return(true));
 
   EXPECT_TRUE(controller_.UpdateAddress(kIpRange));
+  EXPECT_THAT(notified_address_, Eq(kIpRange.FirstAddressInRange()));
 }
 
 TEST_F(TunDeviceControllerTest, OldAddressesAreRemoved) {
@@ -110,6 +114,7 @@
       .WillOnce(Return(true));
 
   EXPECT_TRUE(controller_.UpdateAddress(kIpRange));
+  EXPECT_THAT(notified_address_, Eq(kIpRange.FirstAddressInRange()));
 }
 
 TEST_F(TunDeviceControllerTest, UpdateRoutesRemovedOldRoutes) {