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

Commit 5142df56 authored by Hugo Benichi's avatar Hugo Benichi Committed by android-build-merger
Browse files

Merge \"IpConn metrics: correctly read RA lifetimes\" into nyc-mr1-dev

am: 54756fdf

Change-Id: Iabf3cfa4b6aaf5a1fbda6f169a65a1c7c117fd32
parents a2595b3b 54756fdf
Loading
Loading
Loading
Loading
+59 −0
Original line number Diff line number Diff line
@@ -27,6 +27,9 @@ import android.os.Parcelable;
@SystemApi
public final class RaEvent implements Parcelable {

    /** {@hide} */
    public static final long NO_LIFETIME = -1L;

    // Lifetime in seconds of options found in a single RA packet.
    // When an option is not set, the value of the associated field is -1;
    public final long routerLifetime;
@@ -92,4 +95,60 @@ public final class RaEvent implements Parcelable {
            return new RaEvent[size];
        }
    };

    /** {@hide} */
    public static class Builder {

        long routerLifetime          = NO_LIFETIME;
        long prefixValidLifetime     = NO_LIFETIME;
        long prefixPreferredLifetime = NO_LIFETIME;
        long routeInfoLifetime       = NO_LIFETIME;
        long rdnssLifetime           = NO_LIFETIME;
        long dnsslLifetime           = NO_LIFETIME;

        public Builder() {
        }

        public RaEvent build() {
            return new RaEvent(routerLifetime, prefixValidLifetime, prefixPreferredLifetime,
                    routeInfoLifetime, rdnssLifetime, dnsslLifetime);
        }

        public Builder updateRouterLifetime(long lifetime) {
            routerLifetime = updateLifetime(routerLifetime, lifetime);
            return this;
        }

        public Builder updatePrefixValidLifetime(long lifetime) {
            prefixValidLifetime = updateLifetime(prefixValidLifetime, lifetime);
            return this;
        }

        public Builder updatePrefixPreferredLifetime(long lifetime) {
            prefixPreferredLifetime = updateLifetime(prefixPreferredLifetime, lifetime);
            return this;
        }

        public Builder updateRouteInfoLifetime(long lifetime) {
            routeInfoLifetime = updateLifetime(routeInfoLifetime, lifetime);
            return this;
        }

        public Builder updateRdnssLifetime(long lifetime) {
            rdnssLifetime = updateLifetime(rdnssLifetime, lifetime);
            return this;
        }

        public Builder updateDnsslLifetime(long lifetime) {
            dnsslLifetime = updateLifetime(dnsslLifetime, lifetime);
            return this;
        }

        private long updateLifetime(long currentLifetime, long newLifetime) {
            if (currentLifetime == RaEvent.NO_LIFETIME) {
                return newLifetime;
            }
            return Math.min(currentLifetime, newLifetime);
        }
    }
}
+32 −28
Original line number Diff line number Diff line
@@ -213,7 +213,7 @@ public class ApfFilter {
    private final ApfCapabilities mApfCapabilities;
    private final IpManager.Callback mIpManagerCallback;
    private final NetworkInterface mNetworkInterface;
    private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
    private final IpConnectivityLog mMetricsLog;
    @VisibleForTesting
    byte[] mHardwareAddress;
    @VisibleForTesting
@@ -228,11 +228,12 @@ public class ApfFilter {

    @VisibleForTesting
    ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
            IpManager.Callback ipManagerCallback, boolean multicastFilter) {
            IpManager.Callback ipManagerCallback, boolean multicastFilter, IpConnectivityLog log) {
        mApfCapabilities = apfCapabilities;
        mIpManagerCallback = ipManagerCallback;
        mNetworkInterface = networkInterface;
        mMulticastFilter = multicastFilter;
        mMetricsLog = log;

        maybeStartFilter();
    }
@@ -371,6 +372,14 @@ public class ApfFilter {
            return i & 0xffffffffL;
        }

        private long getUint16(ByteBuffer buffer, int position) {
            return uint16(buffer.getShort(position));
        }

        private long getUint32(ByteBuffer buffer, int position) {
            return uint32(buffer.getInt(position));
        }

        private void prefixOptionToString(StringBuffer sb, int offset) {
            String prefix = IPv6AddresstoString(offset + 16);
            int length = uint8(mPacket.get(offset + 2));
@@ -438,9 +447,9 @@ public class ApfFilter {
        // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
        // specifications.
        Ra(byte[] packet, int length) {
            mPacket = ByteBuffer.allocate(length).put(ByteBuffer.wrap(packet, 0, length));
            mPacket.clear();
            mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
            mLastSeen = curTime();
            RaEvent.Builder builder = new RaEvent.Builder();

            // Ignore the checksum.
            int lastNonLifetimeStart = addNonLifetime(0,
@@ -451,14 +460,7 @@ public class ApfFilter {
            lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
                    ICMP6_RA_ROUTER_LIFETIME_OFFSET,
                    ICMP6_RA_ROUTER_LIFETIME_LEN);

            long routerLifetime = uint16(mPacket.getShort(
                    ICMP6_RA_ROUTER_LIFETIME_OFFSET + mPacket.position()));
            long prefixValidLifetime = -1L;
            long prefixPreferredLifetime = -1L;
            long routeInfoLifetime = -1L;
            long dnsslLifetime = - 1L;
            long rdnssLifetime = -1L;
            builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET));

            // Ensures that the RA is not truncated.
            mPacket.position(ICMP6_RA_OPTION_OFFSET);
@@ -466,39 +468,42 @@ public class ApfFilter {
                final int position = mPacket.position();
                final int optionType = uint8(mPacket.get(position));
                final int optionLength = uint8(mPacket.get(position + 1)) * 8;
                long lifetime;
                switch (optionType) {
                    case ICMP6_PREFIX_OPTION_TYPE:
                        // Parse valid lifetime
                        lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
                                ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
                                ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
                        lifetime = getUint32(mPacket,
                                position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET);
                        builder.updatePrefixValidLifetime(lifetime);
                        // Parse preferred lifetime
                        lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
                                ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
                                ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
                        lifetime = getUint32(mPacket,
                                position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET);
                        builder.updatePrefixPreferredLifetime(lifetime);
                        mPrefixOptionOffsets.add(position);
                        prefixValidLifetime = uint32(mPacket.getInt(
                                ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET + position));
                        prefixPreferredLifetime = uint32(mPacket.getInt(
                                ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET + position));
                        break;
                    // These three options have the same lifetime offset and size, and
                    // are processed with the same specialized addNonLifetime4B:
                    // are processed with the same specialized addNonLifetimeU32:
                    case ICMP6_RDNSS_OPTION_TYPE:
                        mRdnssOptionOffsets.add(position);
                        lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
                        rdnssLifetime =
                                uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position));
                        lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
                        builder.updateRdnssLifetime(lifetime);
                        break;
                    case ICMP6_ROUTE_INFO_OPTION_TYPE:
                        lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
                        routeInfoLifetime =
                                uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position));
                        lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
                        builder.updateRouteInfoLifetime(lifetime);
                        break;
                    case ICMP6_DNSSL_OPTION_TYPE:
                        lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
                        dnsslLifetime =
                                uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position));
                        lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
                        builder.updateDnsslLifetime(lifetime);
                        break;
                    default:
                        // RFC4861 section 4.2 dictates we ignore unknown options for fowards
@@ -514,9 +519,7 @@ public class ApfFilter {
            // Mark non-lifetime bytes since last lifetime.
            addNonLifetime(lastNonLifetimeStart, 0, 0);
            mMinLifetime = minLifetime(packet, length);
            // TODO: record per-option minimum lifetimes instead of last seen lifetimes
            mMetricsLog.log(new RaEvent(routerLifetime, prefixValidLifetime,
                    prefixPreferredLifetime, routeInfoLifetime, rdnssLifetime, dnsslLifetime));
            mMetricsLog.log(builder.build());
        }

        // Ignoring lifetimes (which may change) does {@code packet} match this RA?
@@ -1000,7 +1003,8 @@ public class ApfFilter {
            Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
            return null;
        }
        return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback, multicastFilter);
        return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback,
                multicastFilter, new IpConnectivityLog());
    }

    public synchronized void shutdown() {
+59 −9
Original line number Diff line number Diff line
@@ -26,14 +26,23 @@ import android.net.apf.ApfGenerator;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpManager;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.os.ConditionVariable;
import android.os.Parcelable;
import android.system.ErrnoException;
import android.system.Os;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;

import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -43,6 +52,7 @@ import java.io.OutputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.util.List;

import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -56,9 +66,12 @@ import libcore.io.Streams;
public class ApfTest extends AndroidTestCase {
    private static final int TIMEOUT_MS = 500;

    @Mock IpConnectivityLog mLog;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        MockitoAnnotations.initMocks(this);
        // Load up native shared library containing APF interpreter exposed via JNI.
        System.loadLibrary("servicestestsjni");
    }
@@ -70,6 +83,9 @@ public class ApfTest extends AndroidTestCase {
    // least the minimum packet size.
    private final static int MIN_PKT_SIZE = 15;

    private final static boolean DROP_MULTICAST = true;
    private final static boolean ALLOW_MULTICAST = false;

    private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
        assertEquals(expected, apfSimulate(program, packet, filterAge));
    }
@@ -562,10 +578,10 @@ public class ApfTest extends AndroidTestCase {
        public final static byte[] MOCK_MAC_ADDR = new byte[]{1,2,3,4,5,6};
        private FileDescriptor mWriteSocket;

        public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter) throws
                Exception {
        public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
                IpConnectivityLog log) throws Exception {
            super(new ApfCapabilities(2, 1000, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
                    ipManagerCallback, multicastFilter);
                    ipManagerCallback, multicastFilter, log);
        }

        // Pretend an RA packet has been received and show it to ApfFilter.
@@ -667,7 +683,7 @@ public class ApfTest extends AndroidTestCase {
    @LargeTest
    public void testApfFilterIPv4() throws Exception {
        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
        byte[] program = ipManagerCallback.getApfProgram();

        // Verify empty packet of 100 zero bytes is passed
@@ -699,7 +715,7 @@ public class ApfTest extends AndroidTestCase {
    @LargeTest
    public void testApfFilterIPv6() throws Exception {
        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
        byte[] program = ipManagerCallback.getApfProgram();

        // Verify empty IPv6 packet is passed
@@ -726,7 +742,7 @@ public class ApfTest extends AndroidTestCase {
    @LargeTest
    public void testApfFilterMulticast() throws Exception {
        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
        byte[] program = ipManagerCallback.getApfProgram();

        // Construct IPv4 and IPv6 multicast packets.
@@ -772,7 +788,7 @@ public class ApfTest extends AndroidTestCase {
        // Verify it can be initialized to on
        ipManagerCallback.resetApfProgramWait();
        apfFilter.shutdown();
        apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
        apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
        program = ipManagerCallback.getApfProgram();
        assertDrop(program, bcastv4packet.array(), 0);
        assertDrop(program, mcastv4packet.array(), 0);
@@ -804,7 +820,7 @@ public class ApfTest extends AndroidTestCase {
    @LargeTest
    public void testApfFilterArp() throws Exception {
        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
        byte[] program = ipManagerCallback.getApfProgram();

        // Verify initially ARP filter is off
@@ -867,6 +883,35 @@ public class ApfTest extends AndroidTestCase {
        verifyRaLifetime(ipManagerCallback, packet, lifetime);
    }

    private void verifyRaEvent(RaEvent expected) {
        ArgumentCaptor<Parcelable> captor = ArgumentCaptor.forClass(Parcelable.class);
        verify(mLog, atLeastOnce()).log(captor.capture());
        RaEvent got = lastRaEvent(captor.getAllValues());
        if (!raEventEquals(expected, got)) {
            assertEquals(expected, got);  // fail for printing an assertion error message.
        }
    }

    private RaEvent lastRaEvent(List<Parcelable> events) {
        RaEvent got = null;
        for (Parcelable ev : events) {
            if (ev instanceof RaEvent) {
                got = (RaEvent) ev;
            }
        }
        return got;
    }

    private boolean raEventEquals(RaEvent ev1, RaEvent ev2) {
        return (ev1 != null) && (ev2 != null)
                && (ev1.routerLifetime == ev2.routerLifetime)
                && (ev1.prefixValidLifetime == ev2.prefixValidLifetime)
                && (ev1.prefixPreferredLifetime == ev2.prefixPreferredLifetime)
                && (ev1.routeInfoLifetime == ev2.routeInfoLifetime)
                && (ev1.rdnssLifetime == ev2.rdnssLifetime)
                && (ev1.dnsslLifetime == ev2.dnsslLifetime);
    }

    private void assertInvalidRa(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
            ByteBuffer packet) throws IOException, ErrnoException {
        ipManagerCallback.resetApfProgramWait();
@@ -877,7 +922,7 @@ public class ApfTest extends AndroidTestCase {
    @LargeTest
    public void testApfFilterRa() throws Exception {
        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
        TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
        TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
        byte[] program = ipManagerCallback.getApfProgram();

        // Verify RA is passed the first time
@@ -891,6 +936,7 @@ public class ApfTest extends AndroidTestCase {
        assertPass(program, basePacket.array(), 0);

        testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
        verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, -1));

        // Ensure zero-length options cause the packet to be silently skipped.
        // Do this before we test other packets. http://b/29586253
@@ -916,6 +962,7 @@ public class ApfTest extends AndroidTestCase {
        prefixOptionPacket.putInt(
                ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, 200);
        testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, 100);
        verifyRaEvent(new RaEvent(1000, 200, 100, -1, -1, -1));

        ByteBuffer rdnssOptionPacket = ByteBuffer.wrap(
                new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
@@ -926,6 +973,7 @@ public class ApfTest extends AndroidTestCase {
        rdnssOptionPacket.putInt(
                ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 300);
        testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, 300);
        verifyRaEvent(new RaEvent(1000, -1, -1, -1, 300, -1));

        ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
                new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
@@ -936,6 +984,7 @@ public class ApfTest extends AndroidTestCase {
        routeInfoOptionPacket.putInt(
                ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 400);
        testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, 400);
        verifyRaEvent(new RaEvent(1000, -1, -1, 400, -1, -1));

        ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
                new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
@@ -948,6 +997,7 @@ public class ApfTest extends AndroidTestCase {
        // Note that lifetime of 2000 will be ignored in favor of shorter
        // route lifetime of 1000.
        testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, 1000);
        verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, 2000));

        // Verify that current program filters all five RAs:
        verifyRaLifetime(ipManagerCallback, basePacket, 1000);