Support translating ICMP errors.

When receiving ICMPv6 messages from IPv6-only nodes, use
255.0.0.<ttl> as a fake IPv4 source address. It's better than
nothing.

Bug: 8276725
Change-Id: Iae93f75764cb9cd875af9bb5f1862a0dce2c2fa7
diff --git a/ipv6.c b/ipv6.c
index bb1dc24..ef1e62f 100644
--- a/ipv6.c
+++ b/ipv6.c
@@ -64,12 +64,14 @@
  * fmt     - printf-style format, use %s to place the address
  * badaddr - the bad address in question
  */
-void log_bad_address(const char *fmt, const struct in6_addr *badaddr) {
+void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) {
 #if CLAT_DEBUG
-  char badaddr_str[INET6_ADDRSTRLEN];
+  char srcstr[INET6_ADDRSTRLEN];
+  char dststr[INET6_ADDRSTRLEN];
 
-  inet_ntop(AF_INET6, badaddr, badaddr_str, sizeof(badaddr_str));
-  logmsg_dbg(ANDROID_LOG_ERROR,fmt,badaddr_str);
+  inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr));
+  inet_ntop(AF_INET6, dst, dststr, sizeof(dststr));
+  logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr);
 #endif
 }
 
@@ -91,22 +93,27 @@
   int i;
 
   if(len < sizeof(struct ip6_hdr)) {
-    logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header");
+    logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len);
     return 0;
   }
 
   if(IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
-    log_bad_address("ipv6_packet/multicast %s", &ip6->ip6_dst);
+    log_bad_address("ipv6_packet/multicast %s->%s", &ip6->ip6_src, &ip6->ip6_dst);
     return 0; // silently ignore
   }
 
-  if (!is_in_plat_subnet(&ip6->ip6_src) && ip6->ip6_nxt) {
-    log_bad_address("ipv6_packet/wrong source address: %s", &ip6->ip6_src);
-    return 0;
-  }
-
-  if(!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) {
-    log_bad_address("ipv6_packet/wrong destination address: %s", &ip6->ip6_dst);
+  // If the packet is not from the plat subnet to the local subnet, or vice versa, drop it, unless
+  // it's an ICMP packet (which can come from anywhere). We do not send IPv6 packets from the plat
+  // subnet to the local subnet, but these can appear as inner packets in ICMP errors, so we need
+  // to translate them. We accept third-party ICMPv6 errors, even though their source addresses
+  // cannot be translated, so that things like unreachables and traceroute will work. fill_ip_header
+  // takes care of faking a source address for them.
+  if (!(is_in_plat_subnet(&ip6->ip6_src) &&
+        IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) &&
+      !(is_in_plat_subnet(&ip6->ip6_dst) &&
+        IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet)) &&
+      ip6->ip6_nxt != IPPROTO_ICMPV6) {
+    log_bad_address("ipv6_packet/wrong source address: %s->%s", &ip6->ip6_src, &ip6->ip6_dst);
     return 0;
   }
 
@@ -141,7 +148,7 @@
                          len_left);
   } else {
 #if CLAT_DEBUG
-    logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x",ip6->ip6_nxt);
+    logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt);
     logcat_hexdump("ipv6/nxthdr", packet, len);
 #endif
     return 0;