Don't break IPv6 connectivity when in doze mode.

Working IPv6 connectivity relies on the kernel being able to
receive certain ICMPv6 packets (router advertisements, neighbour
solicitations, neighbour advertisements) at all times. Allow
these packets when in doze mode.

This is not necessary for IPv4 because in IPv4 these functions
use ARP, which is invisible to iptables.

Bug: 23158230
Change-Id: I29ed77561db9688486cf58cd14ac3bce7fce4b40
diff --git a/server/FirewallController.cpp b/server/FirewallController.cpp
index bcf7524..cf5a7de 100644
--- a/server/FirewallController.cpp
+++ b/server/FirewallController.cpp
@@ -37,6 +37,18 @@
 const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
 const char* FirewallController::LOCAL_STANDBY = "fw_standby";
 
+// ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the
+// fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need
+// to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA).
+const char* FirewallController::ICMPV6_TYPES[] = {
+    "packet-too-big",
+    "router-solicitation",
+    "router-advertisement",
+    "neighbour-solicitation",
+    "neighbour-advertisement",
+    "redirect",
+};
+
 FirewallController::FirewallController(void) {
     // If no rules are set, it's in BLACKLIST mode
     mFirewallType = BLACKLIST;
@@ -264,11 +276,18 @@
     int res = 0;
     res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL);
     if (type == WHITELIST) {
+        // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
+        for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
+            res |= execIptables(V6, "-A", childChain, "-p", "icmpv6", "--icmpv6-type",
+                    ICMPV6_TYPES[i], "-j", "RETURN", NULL);
+        }
+
         // create default white list for system uid range
         char uidStr[16];
         sprintf(uidStr, "0-%d", AID_APP - 1);
         res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner",
                 uidStr, "-j", "RETURN", NULL);
+
         // create default rule to drop all traffic
         res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL);
     }
diff --git a/server/FirewallController.h b/server/FirewallController.h
index b32072e..34a8b9c 100644
--- a/server/FirewallController.h
+++ b/server/FirewallController.h
@@ -64,6 +64,9 @@
 
     static const char* LOCAL_DOZABLE;
     static const char* LOCAL_STANDBY;
+
+    static const char* ICMPV6_TYPES[];
+
 private:
     FirewallType mFirewallType;
     int attachChain(const char*, const char*);