Configure gateway for QBONE routes when adding/updating routes.

This simplifies neighbor discovery for TAP devices, now only needing to respond to solicitations for the device's link-local gateway address.

PiperOrigin-RevId: 398333869
diff --git a/quic/qbone/bonnet/tun_device_packet_exchanger.cc b/quic/qbone/bonnet/tun_device_packet_exchanger.cc
index ecb4f5d..3381cd2 100644
--- a/quic/qbone/bonnet/tun_device_packet_exchanger.cc
+++ b/quic/qbone/bonnet/tun_device_packet_exchanger.cc
@@ -12,6 +12,7 @@
 #include "absl/strings/str_cat.h"
 #include "quic/qbone/platform/icmp_packet.h"
 #include "quic/qbone/platform/netlink_interface.h"
+#include "quic/qbone/qbone_constants.h"
 
 namespace quic {
 
@@ -167,6 +168,13 @@
     // respond with and write it back to the local interface.
     auto* icmp6_payload = l2_packet.data() + kIcmp6PrefixLen;
 
+    QuicIpAddress target_address(
+        *reinterpret_cast<const in6_addr*>(icmp6_payload));
+    if (target_address != *QboneConstants::GatewayAddress()) {
+      // Only respond to solicitations for our gateway address
+      return nullptr;
+    }
+
     // Neighbor Advertisement crafted per:
     // https://datatracker.ietf.org/doc/html/rfc4861#section-4.4
     //
diff --git a/quic/qbone/platform/netlink.cc b/quic/qbone/platform/netlink.cc
index 985632a..2f4bbe2 100644
--- a/quic/qbone/platform/netlink.cc
+++ b/quic/qbone/platform/netlink.cc
@@ -5,6 +5,7 @@
 #include "quic/qbone/platform/netlink.h"
 
 #include <linux/fib_rules.h>
+
 #include <utility>
 
 #include "absl/base/attributes.h"
@@ -13,6 +14,7 @@
 #include "quic/platform/api/quic_ip_address.h"
 #include "quic/platform/api/quic_logging.h"
 #include "quic/qbone/platform/rtnetlink_message.h"
+#include "quic/qbone/qbone_constants.h"
 
 namespace quic {
 
@@ -570,10 +572,17 @@
   // This is the source address to use in the IP packet should this routing rule
   // is used.
   if (preferred_source.IsInitialized()) {
+    auto src_str = preferred_source.ToPackedString();
     message.AppendAttribute(RTA_PREFSRC,
-                            reinterpret_cast<const void*>(
-                                preferred_source.ToPackedString().c_str()),
-                            preferred_source.ToPackedString().size());
+                            reinterpret_cast<const void*>(src_str.c_str()),
+                            src_str.size());
+  }
+
+  if (verb != Verb::kRemove) {
+    auto gateway_str = QboneConstants::GatewayAddress()->ToPackedString();
+    message.AppendAttribute(RTA_GATEWAY,
+                            reinterpret_cast<const void*>(gateway_str.c_str()),
+                            gateway_str.size());
   }
 
   if (!Send(message.BuildIoVec().get(), message.IoVecSize())) {
diff --git a/quic/qbone/platform/netlink_test.cc b/quic/qbone/platform/netlink_test.cc
index 16d2a19..f6fe7cd 100644
--- a/quic/qbone/platform/netlink_test.cc
+++ b/quic/qbone/platform/netlink_test.cc
@@ -564,6 +564,15 @@
               EXPECT_EQ(preferred_ip, address);
               break;
             }
+            case RTA_GATEWAY: {
+              const auto* raw_address =
+                  reinterpret_cast<const char*>(RTA_DATA(rta));
+              ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta));
+              QuicIpAddress address;
+              address.FromPackedString(raw_address, RTA_PAYLOAD(rta));
+              EXPECT_EQ(*QboneConstants::GatewayAddress(), address);
+              break;
+            }
             case RTA_OIF: {
               ASSERT_EQ(sizeof(int), RTA_PAYLOAD(rta));
               const auto* interface_index =
@@ -591,7 +600,7 @@
           }
           ++num_rta;
         }
-        EXPECT_EQ(4, num_rta);
+        EXPECT_EQ(5, num_rta);
       });
   EXPECT_TRUE(netlink->ChangeRoute(
       Netlink::Verb::kAdd, QboneConstants::kQboneRouteTableId, subnet,
@@ -728,6 +737,15 @@
               EXPECT_EQ(preferred_ip, address);
               break;
             }
+            case RTA_GATEWAY: {
+              const auto* raw_address =
+                  reinterpret_cast<const char*>(RTA_DATA(rta));
+              ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta));
+              QuicIpAddress address;
+              address.FromPackedString(raw_address, RTA_PAYLOAD(rta));
+              EXPECT_EQ(*QboneConstants::GatewayAddress(), address);
+              break;
+            }
             case RTA_OIF: {
               ASSERT_EQ(sizeof(int), RTA_PAYLOAD(rta));
               const auto* interface_index =
@@ -755,7 +773,7 @@
           }
           ++num_rta;
         }
-        EXPECT_EQ(4, num_rta);
+        EXPECT_EQ(5, num_rta);
       });
   EXPECT_TRUE(netlink->ChangeRoute(
       Netlink::Verb::kReplace, QboneConstants::kQboneRouteTableId, subnet,
diff --git a/quic/qbone/qbone_constants.cc b/quic/qbone/qbone_constants.cc
index fb92af7..f54ebc3 100644
--- a/quic/qbone/qbone_constants.cc
+++ b/quic/qbone/qbone_constants.cc
@@ -19,7 +19,7 @@
 
 const QuicIpAddress* QboneConstants::TerminatorLocalAddress() {
   static auto* terminator_address = []() {
-    QuicIpAddress* address = new QuicIpAddress;
+    auto* address = new QuicIpAddress;
     // 0x71 0x62 0x6f 0x6e 0x65 is 'qbone' in ascii.
     address->FromString("fe80::71:626f:6e65");
     return address;
@@ -33,4 +33,13 @@
   return range;
 }
 
+const QuicIpAddress* QboneConstants::GatewayAddress() {
+  static auto* gateway_address = []() {
+    auto* address = new QuicIpAddress;
+    address->FromString("fe80::1");
+    return address;
+  }();
+  return gateway_address;
+}
+
 }  // namespace quic
diff --git a/quic/qbone/qbone_constants.h b/quic/qbone/qbone_constants.h
index 8053013..141bb6c 100644
--- a/quic/qbone/qbone_constants.h
+++ b/quic/qbone/qbone_constants.h
@@ -25,6 +25,9 @@
   static const QuicIpAddress* TerminatorLocalAddress();
   // The IPRange containing the TerminatorLocalAddress
   static const IpRange* TerminatorLocalAddressRange();
+  // The gateway address to provide when configuring routes to the QBONE
+  // interface
+  static const QuicIpAddress* GatewayAddress();
 };
 
 }  // namespace quic