Support IPv6 multicast filtering in APF.

For now this just drops all non-ICMPv6 packets to ff00::/8 when
mMulticastFilter is true. Multicast ICMPv6 is already mostly
dealt with by other filters - the L2 multicast filter, the RA
filter, the multicast NA filter, and ND offload.

Bug: 28393601
Change-Id: Ia7b0d4f00fac6710093befe6a726b46677a5f20b
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 7079b9e..538e8f8 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -648,14 +648,31 @@
         // Here's a basic summary of what the IPv6 filter program does:
         //
         // if it's not ICMPv6:
+        //   if it's multicast and we're dropping multicast:
+        //     drop
         //   pass
         // if it's ICMPv6 NA to ff02::1:
         //   drop
 
-        // If not ICMPv6, pass
         gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
-        // TODO: Drop multicast if the multicast filter is enabled.
-        gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+
+        // Drop multicast if the multicast filter is enabled.
+        if (mMulticastFilter) {
+            // Don't touch ICMPv6 multicast here, we deal with it in more detail later.
+            String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter";
+            gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel);
+
+            // Drop all other packets sent to ff00::/8.
+            gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
+            gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
+            // Not multicast and not ICMPv6. Pass.
+            gen.addJump(gen.PASS_LABEL);
+            gen.defineLabel(skipIpv6MulticastFilterLabel);
+        } else {
+            // If not ICMPv6, pass.
+            gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+        }
+
         // Add unsolicited multicast neighbor announcements filter
         String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
         // If not neighbor announcements, skip unsolicited multicast NA filter
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index e9c5bdd..9e04d23 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -720,36 +720,52 @@
     }
 
     @LargeTest
-    public void testApfFilterIPv4Multicast() throws Exception {
+    public void testApfFilterMulticast() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
         byte[] program = ipManagerCallback.getApfProgram();
 
+        // Construct IPv4 and IPv6 multicast packets.
+        ByteBuffer v4packet = ByteBuffer.wrap(new byte[100]);
+        v4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+        v4packet.position(IPV4_DEST_ADDR_OFFSET);
+        v4packet.put(new byte[]{(byte)224,0,0,1});
+
+        ByteBuffer v6packet = ByteBuffer.wrap(new byte[100]);
+        v6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+        v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
+        v6packet.position(IPV6_DEST_ADDR_OFFSET);
+        v6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+
         // Verify initially disabled multicast filter is off
-        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
-        packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
-        packet.position(IPV4_DEST_ADDR_OFFSET);
-        packet.put(new byte[]{(byte)224,0,0,1});
-        assertPass(program, packet.array(), 0);
+        assertPass(program, v4packet.array(), 0);
+        assertPass(program, v6packet.array(), 0);
 
         // Turn on multicast filter and verify it works
         ipManagerCallback.resetApfProgramWait();
         apfFilter.setMulticastFilter(true);
         program = ipManagerCallback.getApfProgram();
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, v4packet.array(), 0);
+        assertDrop(program, v6packet.array(), 0);
 
         // Turn off multicast filter and verify it's off
         ipManagerCallback.resetApfProgramWait();
         apfFilter.setMulticastFilter(false);
         program = ipManagerCallback.getApfProgram();
-        assertPass(program, packet.array(), 0);
+        assertPass(program, v4packet.array(), 0);
+        assertPass(program, v6packet.array(), 0);
 
         // Verify it can be initialized to on
         ipManagerCallback.resetApfProgramWait();
         apfFilter.shutdown();
         apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
         program = ipManagerCallback.getApfProgram();
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, v4packet.array(), 0);
+        assertDrop(program, v6packet.array(), 0);
+
+        // Verify that ICMPv6 multicast is not dropped.
+        v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+        assertPass(program, v6packet.array(), 0);
 
         apfFilter.shutdown();
     }
@@ -839,7 +855,7 @@
     @LargeTest
     public void testApfFilterRa() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
-        TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+        TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
         byte[] program = ipManagerCallback.getApfProgram();
 
         // Verify RA is passed the first time
@@ -848,6 +864,8 @@
         basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
         basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
         basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)1000);
+        basePacket.position(IPV6_DEST_ADDR_OFFSET);
+        basePacket.put(IPV6_ALL_NODES_ADDRESS);
         assertPass(program, basePacket.array(), 0);
 
         testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);