send out IPv6 DAD packets when starting up clatd for the clat v6 address am: f6ec94ea69 am: 93fc5ff60a am: b09c61bfb1 am: 70e590382e

Original change: https://android-review.googlesource.com/c/platform/external/android-clat/+/2153053

Change-Id: I9a26305a24390c1fe5dd447cf0893ec28f4248db
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/clatd.c b/clatd.c
index f72f431..4ce94f7 100644
--- a/clatd.c
+++ b/clatd.c
@@ -40,6 +40,7 @@
 #include <sys/uio.h>
 
 #include "clatd.h"
+#include "checksum.h"
 #include "config.h"
 #include "dump.h"
 #include "getaddr.h"
@@ -123,11 +124,101 @@
   translate_packet(write_fd, 1 /* to_ipv6 */, packet, readlen);
 }
 
+// IPv6 DAD packet format:
+//   Ethernet header (if needed) will be added by the kernel:
+//     u8[6] src_mac; u8[6] dst_mac '33:33:ff:XX:XX:XX'; be16 ethertype '0x86DD'
+//   IPv6 header:
+//     be32 0x60000000 - ipv6, tclass 0, flowlabel 0
+//     be16 payload_length '32'; u8 nxt_hdr ICMPv6 '58'; u8 hop limit '255'
+//     u128 src_ip6 '::'
+//     u128 dst_ip6 'ff02::1:ffXX:XXXX'
+//   ICMPv6 header:
+//     u8 type '135'; u8 code '0'; u16 icmp6 checksum; u32 reserved '0'
+//   ICMPv6 neighbour solicitation payload:
+//     u128 tgt_ip6
+//   ICMPv6 ND options:
+//     u8 opt nr '14'; u8 length '1'; u8[6] nonce '6 random bytes'
+void send_dad(int fd, const struct in6_addr* tgt, int count) {
+  struct {
+    struct ip6_hdr ip6h;
+    struct nd_neighbor_solicit ns;
+    uint8_t ns_opt_nr;
+    uint8_t ns_opt_len;
+    uint8_t ns_opt_nonce[6];
+  } dad_pkt = {
+    .ip6h = {
+      .ip6_flow = htonl(6 << 28),  // v6, 0 tclass, 0 flowlabel
+      .ip6_plen = htons(sizeof(dad_pkt) - sizeof(struct ip6_hdr)),  // payload length, ie. 32
+      .ip6_nxt = IPPROTO_ICMPV6,  // 58
+      .ip6_hlim = 255,
+      .ip6_src = {},  // ::
+      .ip6_dst.s6_addr = {
+        0xFF, 0x02, 0, 0,
+        0, 0, 0, 0,
+        0, 0, 0, 1,
+        0xFF, tgt->s6_addr[13], tgt->s6_addr[14], tgt->s6_addr[15],
+      },  // ff02::1:ffXX:XXXX - multicast group address derived from bottom 24-bits of tgt
+    },
+    .ns = {
+      .nd_ns_type = ND_NEIGHBOR_SOLICIT,  // 135
+      .nd_ns_code = 0,
+      .nd_ns_cksum = 0,  // will be calculated later
+      .nd_ns_reserved = 0,
+      .nd_ns_target = *tgt,
+    },
+    .ns_opt_nr = 14,  // icmp6 option 'nonce' from RFC3971
+    .ns_opt_len = 1,  // in units of 8 bytes, including option nr and len
+    .ns_opt_nonce = {  // 'opt_len' * 8 - [size of u8: opt nr] - [size of u8: opt len] = 6 bytes
+      0, 0, 'C', 'L', 'A', 'T',
+    },  // first 2 bytes filled in below with random bytes, CLAT left to ease id in tcpdump
+  };
+  arc4random_buf(&dad_pkt.ns_opt_nonce, 2);
+
+  // 40 byte IPv6 header + 8 byte ICMPv6 header + 16 byte ipv6 target address + 8 byte nonce option
+  _Static_assert(sizeof(dad_pkt) == 40 + 8 + 16 + 8, "sizeof dad packet != 72");
+
+  // IPv6 header checksum is standard negated 16-bit one's complement sum over the icmpv6 pseudo
+  // header (which includes payload length, nextheader, and src/dst ip) and the icmpv6 payload.
+  //
+  // Src/dst ip immediately prefix the icmpv6 header itself, so can be handled along
+  // with the payload.  We thus only need to manually account for payload len & next header.
+  //
+  // The magic '8' is simply the offset of the ip6_src field in the ipv6 header,
+  // ie. we're skipping over the ipv6 version, tclass, flowlabel, payload length, next header
+  // and hop limit fields, because they're not quite where we want them to be.
+  //
+  // ip6_plen is already in network order, while ip6_nxt is a single byte and thus needs htons().
+  uint32_t csum = dad_pkt.ip6h.ip6_plen + htons(dad_pkt.ip6h.ip6_nxt);
+  csum = ip_checksum_add(csum, &dad_pkt.ip6h.ip6_src, sizeof(dad_pkt) - 8);
+  dad_pkt.ns.nd_ns_cksum = ip_checksum_finish(csum);
+
+  const struct sockaddr_in6 dst = {
+    .sin6_family = AF_INET6,
+    .sin6_addr = dad_pkt.ip6h.ip6_dst,
+    .sin6_scope_id = if_nametoindex(Global_Clatd_Config.native_ipv6_interface),
+  };
+
+  while (count--) {
+    sendto(fd, &dad_pkt, sizeof(dad_pkt), 0 /*flags*/, (const struct sockaddr *)&dst, sizeof(dst));
+  }
+}
+
 /* function: event_loop
  * reads packets from the tun network interface and passes them down the stack
  *   tunnel - tun device data
  */
 void event_loop(struct tun_data *tunnel) {
+  // Apparently some network gear will refuse to perform NS for IPs that aren't DAD'ed,
+  // this would then result in an ipv6-only network with working native ipv6, working
+  // IPv4 via DNS64, but non-functioning IPv4 via CLAT (ie. IPv4 literals + IPv4 only apps).
+  // The kernel itself doesn't do DAD for anycast ips (but does handle IPV6 MLD and handle ND).
+  // So we'll spoof dad here, and yeah, we really should check for a response and in
+  // case of failure pick a different IP.  Seeing as 48-bits of the IP are utterly random
+  // (with the other 16 chosen to guarantee checksum neutrality) this seems like a remote
+  // concern...
+  // TODO: actually perform true DAD
+  send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, 2);
+
   time_t last_interface_poll;
   struct pollfd wait_fd[] = {
     { tunnel->read_fd6, POLLIN, 0 },