Add fuzzing tests to ApfFilter RA processing

Test: added new unit tests

(cherry picked from commit 8acea76a2b7555b3bd5ca1170cca9d09e979fafc)

Change-Id: I847d7e4895766042043c0bba1c9b9a698a705d87
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 4c75452..957a8d3 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -289,8 +289,15 @@
         return System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS;
     }
 
+    public static class InvalidRaException extends Exception {
+        public InvalidRaException(String m) {
+            super(m);
+        }
+    }
+
     // A class to hold information about an RA.
-    private class Ra {
+    @VisibleForTesting
+    class Ra {
         // From RFC4861:
         private static final int ICMP6_RA_HEADER_LEN = 16;
         private static final int ICMP6_RA_CHECKSUM_OFFSET =
@@ -362,7 +369,7 @@
             } catch (UnsupportedOperationException e) {
                 // array() failed. Cannot happen, mPacket is array-backed and read-write.
                 return "???";
-            } catch (ClassCastException | UnknownHostException e) {
+            } catch (ClassCastException|UnknownHostException e) {
                 // Cannot happen.
                 return "???";
             }
@@ -403,7 +410,7 @@
                     rdnssOptionToString(sb, i);
                 }
                 return sb.toString();
-            } catch (BufferUnderflowException | IndexOutOfBoundsException e) {
+            } catch (BufferUnderflowException|IndexOutOfBoundsException e) {
                 return "<Malformed RA>";
             }
         }
@@ -436,7 +443,11 @@
         // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
         // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
         // specifications.
-        Ra(byte[] packet, int length) {
+        Ra(byte[] packet, int length) throws InvalidRaException {
+            if (length < ICMP6_RA_OPTION_OFFSET) {
+                throw new InvalidRaException("Not an ICMP6 router advertisement");
+            }
+
             mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
             mLastSeen = curTime();
 
@@ -445,7 +456,7 @@
             if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
                     uint8(mPacket.get(IPV6_NEXT_HEADER_OFFSET)) != IPPROTO_ICMPV6 ||
                     uint8(mPacket.get(ICMP6_TYPE_OFFSET)) != ICMP6_ROUTER_ADVERTISEMENT) {
-                throw new IllegalArgumentException("Not an ICMP6 router advertisement");
+                throw new InvalidRaException("Not an ICMP6 router advertisement");
             }
 
 
@@ -511,7 +522,7 @@
                         break;
                 }
                 if (optionLength <= 0) {
-                    throw new IllegalArgumentException(String.format(
+                    throw new InvalidRaException(String.format(
                         "Invalid option length opt=%d len=%d", optionType, optionLength));
                 }
                 mPacket.position(position + optionLength);
@@ -925,8 +936,8 @@
             // Execution will reach the end of the program if no filters match, which will pass the
             // packet to the AP.
             program = gen.generate();
-        } catch (IllegalInstructionException e) {
-            Log.e(TAG, "Program failed to generate: ", e);
+        } catch (IllegalInstructionException|IllegalStateException e) {
+            Log.e(TAG, "Failed to generate APF program.", e);
             return;
         }
         mLastTimeInstalledProgram = curTime();
@@ -972,7 +983,8 @@
      * if the current APF program should be updated.
      * @return a ProcessRaResult enum describing what action was performed.
      */
-    private synchronized ProcessRaResult processRa(byte[] packet, int length) {
+    @VisibleForTesting
+    synchronized ProcessRaResult processRa(byte[] packet, int length) {
         if (VDBG) hexDump("Read packet = ", packet, length);
 
         // Have we seen this RA before?
@@ -1011,7 +1023,7 @@
         try {
             ra = new Ra(packet, length);
         } catch (Exception e) {
-            Log.e(TAG, "Error parsing RA: " + e);
+            Log.e(TAG, "Error parsing RA", e);
             return ProcessRaResult.PARSE_ERROR;
         }
         // Ignore 0 lifetime RAs.
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index f7c61d1..37807b2 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -16,10 +16,6 @@
 
 package android.net.apf;
 
-import static android.system.OsConstants.*;
-
-import com.android.frameworks.servicestests.R;
-
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
@@ -37,6 +33,10 @@
 import android.system.Os;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import static android.system.OsConstants.*;
+
+import com.android.frameworks.servicestests.R;
+import com.android.internal.util.HexDump;
 
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -54,6 +54,7 @@
 import java.net.NetworkInterface;
 import java.nio.ByteBuffer;
 import java.util.List;
+import java.util.Random;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
@@ -1146,6 +1147,39 @@
         buffer.position(original);
     }
 
+    public void testRaParsing() throws Exception {
+        final int maxRandomPacketSize = 512;
+        final Random r = new Random();
+        MockIpManagerCallback cb = new MockIpManagerCallback();
+        TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST, mLog);
+        for (int i = 0; i < 1000; i++) {
+            byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
+            r.nextBytes(packet);
+            try {
+                apfFilter.new Ra(packet, packet.length);
+            } catch (ApfFilter.InvalidRaException e) {
+            } catch (Exception e) {
+                throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
+            }
+        }
+    }
+
+    public void testRaProcessing() throws Exception {
+        final int maxRandomPacketSize = 512;
+        final Random r = new Random();
+        MockIpManagerCallback cb = new MockIpManagerCallback();
+        TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST, mLog);
+        for (int i = 0; i < 1000; i++) {
+            byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
+            r.nextBytes(packet);
+            try {
+                apfFilter.processRa(packet, packet.length);
+            } catch (Exception e) {
+                throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
+            }
+        }
+    }
+
     /**
      * Call the APF interpreter the run {@code program} on {@code packet} pretending the
      * filter was installed {@code filter_age} seconds ago.