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

Commit ec07fbc9 authored by Hugo Benichi's avatar Hugo Benichi
Browse files

NetworkStatsFactory: fix double accounting on IPv6 only networks

For 464xlat scenarios on IPv6 networks, the clatd interface setup
introduces double counting of apps ipv4 traffic. NetworkStatsFactory was
accounting for this on the tx path, but not on the rx path. Also it did
not accounted for the 20 bytes added by the IPv6 header.

This patch subtract correctly the rx and tx traffic from the root uid on
the underlying interface, and also adds correctly the 20 bytes cost per
packet on the stacked interface for 464xlat traffic.

Test: added several new unit tests, based on synthetic data and real
      data also.
Bug: 33681750
Change-Id: I4867fe181938d94b5594b3d88896a3c4e01d895c
Merged-In: I2675643b220acbc6110179fa937d4c313b6f5e32

(cherry picked from commit e1bb3a14)
parent bbc764e8
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