Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 312ccac3 authored by Lorenzo Colitti's avatar Lorenzo Colitti
Browse files

Add more RA tests to ApfTest.

Bug: 66928272
Test: new tests pass
Change-Id: I61212a8012ce2f4f2727d8531d116975352a45e2
parent fbb796b3
Loading
Loading
Loading
Loading
+31 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_
import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
import static com.android.server.util.NetworkStackConstants.IPV6_ADDR_LEN;

import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -548,6 +549,9 @@ public class ApfFilter {
        // For debugging only. Offsets into the packet where RDNSS options are.
        private final ArrayList<Integer> mRdnssOptionOffsets = new ArrayList<>();

        // For debugging only. Offsets into the packet where RIO options are.
        private final ArrayList<Integer> mRioOptionOffsets = new ArrayList<>();

        // For debugging only. How many times this RA was seen.
        int seenCount = 0;

@@ -598,6 +602,28 @@ public class ApfFilter {
            for (int server = 0; server < numServers; server++) {
                sb.append(" ").append(IPv6AddresstoString(offset + 8 + 16 * server));
            }
            sb.append(" ");
        }

        private void rioOptionToString(StringBuffer sb, int offset) {
            int optLen = getUint8(mPacket, offset + 1) * 8;
            if (optLen < 8 || optLen > 24) return;  // Malformed or empty.
            int prefixLen = getUint8(mPacket, offset + 2);
            long lifetime = getUint32(mPacket, offset + 4);

            // This read is variable length because the prefix can be 0, 8 or 16 bytes long.
            // We can't use any of the ByteBuffer#get methods here because they all start reading
            // from the buffer's current position.
            byte[] prefix = new byte[IPV6_ADDR_LEN];
            System.arraycopy(mPacket.array(), offset + 8, prefix, 0, optLen - 8);
            sb.append("RIO ").append(lifetime).append("s ");
            try {
                InetAddress address = (Inet6Address) InetAddress.getByAddress(prefix);
                sb.append(address.getHostAddress());
            } catch (UnknownHostException impossible) {
                sb.append("???");
            }
            sb.append("/").append(prefixLen).append(" ");
        }

        public String toString() {
@@ -613,6 +639,9 @@ public class ApfFilter {
                for (int i: mRdnssOptionOffsets) {
                    rdnssOptionToString(sb, i);
                }
                for (int i: mRioOptionOffsets) {
                    rioOptionToString(sb, i);
                }
                return sb.toString();
            } catch (BufferUnderflowException|IndexOutOfBoundsException e) {
                return "<Malformed RA>";
@@ -649,7 +678,7 @@ public class ApfFilter {
        // specifications.
        Ra(byte[] packet, int length) throws InvalidRaException {
            if (length < ICMP6_RA_OPTION_OFFSET) {
                throw new InvalidRaException("Not an ICMP6 router advertisement");
                throw new InvalidRaException("Not an ICMP6 router advertisement: too short");
            }

            mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
@@ -716,6 +745,7 @@ public class ApfFilter {
                        builder.updateRdnssLifetime(lifetime);
                        break;
                    case ICMP6_ROUTE_INFO_OPTION_TYPE:
                        mRioOptionOffsets.add(position);
                        lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
                        lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
                        builder.updateRouteInfoLifetime(lifetime);
+116 −3
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import android.content.Context;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NattKeepalivePacketDataParcelable;
@@ -85,6 +86,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

@@ -184,7 +186,7 @@ public class ApfTest {

    private void assertProgramEquals(byte[] expected, byte[] program) throws AssertionError {
        // assertArrayEquals() would only print one byte, making debugging difficult.
        if (!java.util.Arrays.equals(expected, program)) {
        if (!Arrays.equals(expected, program)) {
            throw new AssertionError(
                    "\nexpected: " + HexDump.toHexString(expected) +
                    "\nactual:   " + HexDump.toHexString(program));
@@ -197,7 +199,7 @@ public class ApfTest {
        assertReturnCodesEqual(expected, apfSimulate(program, packet, data, 0 /* filterAge */));

        // assertArrayEquals() would only print one byte, making debugging difficult.
        if (!java.util.Arrays.equals(expected_data, data)) {
        if (!Arrays.equals(expected_data, data)) {
            throw new Exception(
                    "\nprogram:     " + HexDump.toHexString(program) +
                    "\ndata memory: " + HexDump.toHexString(data) +
@@ -1030,6 +1032,7 @@ public class ApfTest {
            {(byte) 255, (byte) 255, (byte) 255, (byte) 255};

    private static final int IPV6_HEADER_LEN             = 40;
    private static final int IPV6_PAYLOAD_LENGTH_OFFSET  = ETH_HEADER_LEN + 4;
    private static final int IPV6_NEXT_HEADER_OFFSET     = ETH_HEADER_LEN + 6;
    private static final int IPV6_SRC_ADDR_OFFSET        = ETH_HEADER_LEN + 8;
    private static final int IPV6_DEST_ADDR_OFFSET       = ETH_HEADER_LEN + 24;
@@ -1799,6 +1802,111 @@ public class ApfTest {
        return packet.array();
    }

    private void addRdnssOption(ByteBuffer packet, int lifetime, String... servers)
            throws Exception {
        int optionLength = 1 + 2 * servers.length;   // In 8-byte units
        packet.put((byte) ICMP6_RDNSS_OPTION_TYPE);  // Type
        packet.put((byte) optionLength);             // Length
        packet.putShort((short) 0);                  // Reserved
        packet.putInt(lifetime);                     // Lifetime
        for (String server : servers) {
            packet.put(InetAddress.getByName(server).getAddress());
        }
    }

    private void addRioOption(ByteBuffer packet, int lifetime, String prefixString)
            throws Exception {
        IpPrefix prefix = new IpPrefix(prefixString);

        int optionLength;
        if (prefix.getPrefixLength() == 0) {
            optionLength = 1;
        } else if (prefix.getPrefixLength() <= 64) {
            optionLength = 2;
        } else {
            optionLength = 3;
        }

        packet.put((byte) ICMP6_ROUTE_INFO_OPTION_TYPE);  // Type
        packet.put((byte) optionLength);                  // Length in 8-byte units
        packet.put((byte) prefix.getPrefixLength());      // Prefix length
        packet.put((byte) 0b00011000);                    // Pref = high
        packet.putInt(lifetime);                          // Lifetime

        byte[] prefixBytes = prefix.getRawAddress();
        packet.put(prefixBytes, 0, (optionLength - 1) * 8);
    }

    private void addPioOption(ByteBuffer packet, int valid, int preferred, String prefixString) {
        IpPrefix prefix = new IpPrefix(prefixString);
        packet.put((byte) ICMP6_PREFIX_OPTION_TYPE);  // Type
        packet.put((byte) 4);                         // Length in 8-byte units
        packet.put((byte) prefix.getPrefixLength());  // Prefix length
        packet.put((byte) 0b11000000);                // L = 1, A = 1
        packet.putInt(valid);
        packet.putInt(preferred);
        packet.putInt(0);                             // Reserved
        packet.put(prefix.getRawAddress());
    }

    private byte[] buildLargeRa() throws Exception {
        InetAddress src = InetAddress.getByName("fe80::1234:abcd");

        ByteBuffer packet = ByteBuffer.wrap(new byte[1514]);
        packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6);
        packet.position(ETH_HEADER_LEN);

        packet.putInt(0x60012345);                                  // Version, tclass, flowlabel
        packet.putShort((short) 0);                                 // Payload length; updated later
        packet.put((byte) IPPROTO_ICMPV6);                          // Next header
        packet.put((byte) 0xff);                                    // Hop limit
        packet.put(src.getAddress());                               // Source address
        packet.put(IPV6_ALL_NODES_ADDRESS);                         // Destination address

        packet.put((byte) ICMP6_ROUTER_ADVERTISEMENT);              // Type
        packet.put((byte) 0);                                       // Code (0)
        packet.putShort((short) 0);                                 // Checksum (ignored)
        packet.put((byte) 64);                                      // Hop limit
        packet.put((byte) 0);                                       // M/O, reserved
        packet.putShort((short) 1800);                              // Router lifetime
        packet.putInt(30_000);                                      // Reachable time
        packet.putInt(1000);                                        // Retrans timer

        addRioOption(packet, 1200, "64:ff9b::/96");
        addRdnssOption(packet, 7200, "2001:db8:1::1", "2001:db8:1::2");
        addRioOption(packet, 2100, "2000::/3");
        addRioOption(packet, 2400, "::/0");
        addPioOption(packet, 600, 300, "2001:db8:a::/64");
        addRioOption(packet, 1500, "2001:db8:c:d::/64");
        addPioOption(packet, 86400, 43200, "fd95:d1e:12::/64");

        int length = packet.position();
        packet.putShort(IPV6_PAYLOAD_LENGTH_OFFSET, (short) length);

        // Don't pass the Ra constructor a packet that is longer than the actual RA.
        // This relies on the fact that all the relative writes to the byte buffer are at the end.
        byte[] packetArray = new byte[length];
        packet.rewind();
        packet.get(packetArray);
        return packetArray;
    }

    @Test
    public void testRaToString() throws Exception {
        MockIpClientCallback cb = new MockIpClientCallback();
        ApfConfiguration config = getDefaultConfig();
        TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);

        byte[] packet = buildLargeRa();
        ApfFilter.Ra ra = apfFilter.new Ra(packet, packet.length);
        String expected = "RA fe80::1234:abcd -> ff02::1 1800s "
                + "2001:db8:a::/64 600s/300s fd95:d1e:12::/64 86400s/43200s "
                + "DNS 7200s 2001:db8:1::1 2001:db8:1::2 "
                + "RIO 1200s 64:ff9b::/96 RIO 2100s 2000::/3 "
                + "RIO 2400s ::/0 RIO 1500s 2001:db8:c:d::/64 ";
        assertEquals(expected, ra.toString());
    }

    // Verify that the last program pushed to the IpClient.Callback properly filters the
    // given packet for the given lifetime.
    private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
@@ -1980,7 +2088,11 @@ public class ApfTest {
        verifyRaLifetime(apfFilter, ipClientCallback, dnsslOptionPacket, ROUTER_LIFETIME);
        verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME));

        // Verify that current program filters all five RAs:
        ByteBuffer largeRaPacket = ByteBuffer.wrap(buildLargeRa());
        verifyRaLifetime(apfFilter, ipClientCallback, largeRaPacket, 300);
        verifyRaEvent(new RaEvent(1800, 600, 300, 1200, 7200, -1));

        // Verify that current program filters all the RAs:
        program = ipClientCallback.getApfProgram();
        verifyRaLifetime(program, basePacket, ROUTER_LIFETIME);
        verifyRaLifetime(program, newFlowLabelPacket, ROUTER_LIFETIME);
@@ -1988,6 +2100,7 @@ public class ApfTest {
        verifyRaLifetime(program, rdnssOptionPacket, RDNSS_LIFETIME);
        verifyRaLifetime(program, routeInfoOptionPacket, ROUTE_LIFETIME);
        verifyRaLifetime(program, dnsslOptionPacket, ROUTER_LIFETIME);
        verifyRaLifetime(program, largeRaPacket, 300);

        apfFilter.shutdown();
    }