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

Commit f37b2718 authored by Hugo Benichi's avatar Hugo Benichi Committed by Android (Google) Code Review
Browse files

Merge "NetworkStatsFactory: fix double accounting on IPv6 only networks" into oc-dev

parents 90bcc1c8 ec07fbc9
Loading
Loading
Loading
Loading
+36 −29
Original line number Diff line number Diff line
@@ -50,6 +50,11 @@ public class NetworkStatsFactory {
    private static final boolean USE_NATIVE_PARSING = true;
    private static final boolean SANITY_CHECK_NATIVE = false;

    private static final String CLATD_INTERFACE_PREFIX = "v4-";
    // Delta between IPv4 header (20b) and IPv6 header (40b).
    // Used for correct stats accounting on clatd interfaces.
    private static final int IPV4V6_HEADER_DELTA = 20;

    /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
    private final File mStatsXtIfaceAll;
    /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
@@ -57,6 +62,7 @@ public class NetworkStatsFactory {
    /** Path to {@code /proc/net/xt_qtaguid/stats}. */
    private final File mStatsXtUid;

    // TODO: to improve testability and avoid global state, do not use a static variable.
    @GuardedBy("sStackedIfaces")
    private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>();

@@ -124,9 +130,7 @@ public class NetworkStatsFactory {
                stats.addValues(entry);
                reader.finishLine();
            }
        } catch (NullPointerException e) {
            throw new ProtocolException("problem parsing stats", e);
        } catch (NumberFormatException e) {
        } catch (NullPointerException|NumberFormatException e) {
            throw new ProtocolException("problem parsing stats", e);
        } finally {
            IoUtils.closeQuietly(reader);
@@ -171,9 +175,7 @@ public class NetworkStatsFactory {
                stats.addValues(entry);
                reader.finishLine();
            }
        } catch (NullPointerException e) {
            throw new ProtocolException("problem parsing stats", e);
        } catch (NumberFormatException e) {
        } catch (NullPointerException|NumberFormatException e) {
            throw new ProtocolException("problem parsing stats", e);
        } finally {
            IoUtils.closeQuietly(reader);
@@ -188,26 +190,32 @@ public class NetworkStatsFactory {

    public NetworkStats readNetworkStatsDetail(int limitUid, String[] limitIfaces, int limitTag,
            NetworkStats lastStats) throws IOException {
        final NetworkStats stats = readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag,
                lastStats);
        final NetworkStats stats =
              readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats);
        NetworkStats.Entry entry = null; // for recycling

        synchronized (sStackedIfaces) {
            // Sigh, xt_qtaguid ends up double-counting tx traffic going through
            // clatd interfaces, so we need to subtract it here.
            // For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
            // packet on the stacked interface, and once as translated to an IPv6 packet on the
            // base interface. For correct stats accounting on the base interface, every 464xlat
            // packet needs to be subtracted from the root UID on the base interface both for tx
            // and rx traffic (http://b/12249687, http:/b/33681750).
            final int size = sStackedIfaces.size();
            for (int i = 0; i < size; i++) {
                final String stackedIface = sStackedIfaces.keyAt(i);
                final String baseIface = sStackedIfaces.valueAt(i);
                if (!stackedIface.startsWith(CLATD_INTERFACE_PREFIX)) {
                    continue;
                }

                // Count up the tx traffic and subtract from root UID on the
                // base interface.
                NetworkStats.Entry adjust = new NetworkStats.Entry(baseIface, 0, 0, 0, 0L, 0L, 0L,
                        0L, 0L);
                NetworkStats.Entry entry = null;
                NetworkStats.Entry adjust =
                    new NetworkStats.Entry(baseIface, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
                for (int j = 0; j < stats.size(); j++) {
                    entry = stats.getValues(j, entry);
                    if (Objects.equals(entry.iface, stackedIface)) {
                        adjust.txBytes -= entry.txBytes;
                        adjust.rxBytes -= (entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
                        adjust.txBytes -= (entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
                        adjust.rxPackets -= entry.rxPackets;
                        adjust.txPackets -= entry.txPackets;
                    }
                }
@@ -215,20 +223,21 @@ public class NetworkStatsFactory {
            }
        }

        // Double sigh, all rx traffic on clat needs to be tweaked to
        // account for the dropped IPv6 header size post-unwrap.
        NetworkStats.Entry entry = null;
        // For 464xlat traffic, xt_qtaguid only counts the bytes of the inner IPv4 packet sent on
        // the stacked interface with prefix "v4-" and drops the IPv6 header size after unwrapping.
        // To account correctly for on-the-wire traffic, add the 20 additional bytes difference
        // for all packets (http://b/12249687, http:/b/33681750).
        for (int i = 0; i < stats.size(); i++) {
            entry = stats.getValues(i, entry);
            if (entry.iface != null && entry.iface.startsWith("clat")) {
                // Delta between IPv4 header (20b) and IPv6 header (40b)
                entry.rxBytes = entry.rxPackets * 20;
            if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
                continue;
            }
            entry.rxBytes = entry.rxPackets * IPV4V6_HEADER_DELTA;
            entry.txBytes = entry.txPackets * IPV4V6_HEADER_DELTA;
            entry.rxPackets = 0;
                entry.txBytes = 0;
            entry.txPackets = 0;
            stats.combineValues(entry);
        }
        }

        return stats;
    }
@@ -305,9 +314,7 @@ public class NetworkStatsFactory {

                reader.finishLine();
            }
        } catch (NullPointerException e) {
            throw new ProtocolException("problem parsing idx " + idx, e);
        } catch (NumberFormatException e) {
        } catch (NullPointerException|NumberFormatException e) {
            throw new ProtocolException("problem parsing idx " + idx, e);
        } finally {
            IoUtils.closeQuietly(reader);
+84 −9
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
import android.content.res.Resources;
import android.net.NetworkStats;
import android.net.TrafficStats;
import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;

import com.android.frameworks.tests.net.R;
@@ -44,6 +45,7 @@ import libcore.io.Streams;
/**
 * Tests for {@link NetworkStatsFactory}.
 */
@SmallTest
public class NetworkStatsFactoryTest extends AndroidTestCase {
    private File mTestProc;
    private NetworkStatsFactory mFactory;
@@ -72,9 +74,8 @@ public class NetworkStatsFactoryTest extends AndroidTestCase {
    }

    public void testNetworkStatsDetail() throws Exception {
        stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));
        final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical);

        final NetworkStats stats = mFactory.readNetworkStatsDetail();
        assertEquals(70, stats.size());
        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 18621L, 2898L);
        assertStatsEntry(stats, "wlan0", 10011, SET_DEFAULT, 0x0, 35777L, 5718L);
@@ -98,9 +99,7 @@ public class NetworkStatsFactoryTest extends AndroidTestCase {
    }

    public void testNetworkStatsWithSet() throws Exception {
        stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));

        final NetworkStats stats = mFactory.readNetworkStatsDetail();
        final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical);
        assertEquals(70, stats.size());
        assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 578L, 227423L,
                676L);
@@ -108,8 +107,7 @@ public class NetworkStatsFactoryTest extends AndroidTestCase {
    }

    public void testNetworkStatsSingle() throws Exception {
        stageFile(R.raw.xt_qtaguid_iface_typical,
                new File(mTestProc, "net/xt_qtaguid/iface_stat_all"));
        stageFile(R.raw.xt_qtaguid_iface_typical, file("net/xt_qtaguid/iface_stat_all"));

        final NetworkStats stats = mFactory.readNetworkStatsSummaryDev();
        assertEquals(6, stats.size());
@@ -119,8 +117,7 @@ public class NetworkStatsFactoryTest extends AndroidTestCase {
    }

    public void testNetworkStatsXt() throws Exception {
        stageFile(R.raw.xt_qtaguid_iface_fmt_typical,
                new File(mTestProc, "net/xt_qtaguid/iface_stat_fmt"));
        stageFile(R.raw.xt_qtaguid_iface_fmt_typical, file("net/xt_qtaguid/iface_stat_fmt"));

        final NetworkStats stats = mFactory.readNetworkStatsSummaryXt();
        assertEquals(3, stats.size());
@@ -130,6 +127,67 @@ public class NetworkStatsFactoryTest extends AndroidTestCase {
        assertStatsEntry(stats, "rmnet2", UID_ALL, SET_ALL, TAG_NONE, 4968L, 35L, 3081L, 39L);
    }

    public void testDoubleClatAccounting() throws Exception {
        NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");

        // xt_qtaguid_with_clat_simple is a synthetic file that simulates
        //  - 213 received 464xlat packets of size 200 bytes
        //  - 41 sent 464xlat packets of size 100 bytes
        //  - no other traffic on base interface for root uid.
        NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_simple);
        assertEquals(4, stats.size());

        assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 46860L, 4920L);
        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L);

        stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat);
        assertEquals(42, stats.size());

        assertStatsEntry(stats, "v4-wlan0", 0, SET_DEFAULT, 0x0, 356L, 276L);
        assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L);
        assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L);
        assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L);
        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 5766L);
        assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L);
        assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L);
        assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L);
        assertStatsEntry(stats, "wlan0", 10060, SET_DEFAULT, 0x0, 134356L, 8705L);
        assertStatsEntry(stats, "wlan0", 10079, SET_DEFAULT, 0x0, 10926L, 1507L);
        assertStatsEntry(stats, "wlan0", 10102, SET_DEFAULT, 0x0, 25038L, 8245L);
        assertStatsEntry(stats, "wlan0", 10103, SET_DEFAULT, 0x0, 0L, 192L);
        assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L);
        assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L);

        NetworkStatsFactory.noteStackedIface("v4-wlan0", null);
    }

    public void testDoubleClatAccounting100MBDownload() throws Exception {
        // Downloading 100mb from an ipv4 only destination in a foreground activity

        long appRxBytesBefore = 328684029L;
        long appRxBytesAfter = 439237478L;
        assertEquals("App traffic should be ~100MB", 110553449, appRxBytesAfter - appRxBytesBefore);

        long rootRxBytesBefore = 1394011L;
        long rootRxBytesAfter = 1398634L;
        assertEquals("UID 0 traffic should be ~0", 4623, rootRxBytesAfter - rootRxBytesBefore);

        NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
        NetworkStats stats;

        // Stats snapshot before the download
        stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before);
        assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L);
        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 647888L);

        // Stats snapshot after the download
        stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
        assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 647587L);

        NetworkStatsFactory.noteStackedIface("v4-wlan0", null);
    }

    /**
     * Copy a {@link Resources#openRawResource(int)} into {@link File} for
     * testing purposes.
@@ -159,9 +217,22 @@ public class NetworkStatsFactoryTest extends AndroidTestCase {
        }
    }

    private File file(String path) throws Exception {
        return new File(mTestProc, path);
    }

    private NetworkStats parseDetailedStats(int resourceId) throws Exception {
        stageFile(resourceId, file("net/xt_qtaguid/stats"));
        return mFactory.readNetworkStatsDetail();
    }

    private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
            int tag, long rxBytes, long txBytes) {
        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO);
        if (i < 0) {
            fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)",
                    iface, uid, set, tag));
        }
        final NetworkStats.Entry entry = stats.getValues(i, null);
        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
        assertEquals("unexpected txBytes", txBytes, entry.txBytes);
@@ -170,6 +241,10 @@ public class NetworkStatsFactoryTest extends AndroidTestCase {
    private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
            int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO);
        if (i < 0) {
            fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)",
                    iface, uid, set, tag));
        }
        final NetworkStats.Entry entry = stats.getValues(i, null);
        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
        assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
+43 −0
Original line number Diff line number Diff line
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 v4-wlan0 0x0 0 0 256 5 196 4 256 5 0 0 0 0 196 4 0 0 0 0
3 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4 v4-wlan0 0x0 1000 0 30312 25 1770 27 30236 24 76 1 0 0 1694 26 76 1 0 0
5 v4-wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
6 v4-wlan0 0x0 10060 0 9398432 6717 169412 4235 9398432 6717 0 0 0 0 169412 4235 0 0 0 0
7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0
8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0
9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
10 wlan0 0x0 0 0 11058671 7892 312046 5113 11043898 7811 13117 61 1656 20 306544 5046 3230 38 2272 29
11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0
13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
14 wlan0 0x0 10013 0 0 0 144 2 0 0 0 0 0 0 144 2 0 0 0 0
15 wlan0 0x0 10013 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
16 wlan0 0x0 10018 0 5980263 4715 167667 1922 5972583 4709 0 0 7680 6 167667 1922 0 0 0 0
17 wlan0 0x0 10018 1 43995 37 2766 27 43995 37 0 0 0 0 2766 27 0 0 0 0
18 wlan0 0x0 10060 0 134356 133 8705 74 134356 133 0 0 0 0 8705 74 0 0 0 0
19 wlan0 0x0 10060 1 294709 326 26448 256 294709 326 0 0 0 0 26448 256 0 0 0 0
20 wlan0 0x0 10079 0 10926 13 1507 13 10926 13 0 0 0 0 1507 13 0 0 0 0
21 wlan0 0x0 10079 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
22 wlan0 0x0 10102 0 25038 42 8245 57 25038 42 0 0 0 0 8245 57 0 0 0 0
23 wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
24 wlan0 0x0 10103 0 0 0 192 2 0 0 0 0 0 0 0 0 192 2 0 0
25 wlan0 0x0 10103 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
26 wlan0 0x1000040700000000 10018 0 831 6 655 5 831 6 0 0 0 0 655 5 0 0 0 0
27 wlan0 0x1000040700000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
28 wlan0 0x1000040b00000000 10018 0 1714 8 1561 7 1714 8 0 0 0 0 1561 7 0 0 0 0
29 wlan0 0x1000040b00000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
30 wlan0 0x1000120300000000 10018 0 8243 11 2234 12 8243 11 0 0 0 0 2234 12 0 0 0 0
31 wlan0 0x1000120300000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
32 wlan0 0x1000180300000000 10018 0 56368 49 4790 39 56368 49 0 0 0 0 4790 39 0 0 0 0
33 wlan0 0x1000180300000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
34 wlan0 0x1000300000000000 10018 0 9488 17 18813 25 1808 11 0 0 7680 6 18813 25 0 0 0 0
35 wlan0 0x1000300000000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
36 wlan0 0x3000180400000000 10018 0 131262 103 7416 103 131262 103 0 0 0 0 7416 103 0 0 0 0
37 wlan0 0x3000180400000000 10018 1 43995 37 2766 27 43995 37 0 0 0 0 2766 27 0 0 0 0
38 wlan0 0xffffff0100000000 10018 0 5771986 4518 131190 1725 5771986 4518 0 0 0 0 131190 1725 0 0 0 0
39 wlan0 0xffffff0100000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
40 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8
43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+187 −0

File added.

Preview size limit exceeded, changes collapsed.

+185 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading