Fix network usage stats on 464xlat tethered.

Usage stats corrections for 464xlat in NetworkStatsFactory are not applied
to tethered traffic. Add adjustments in NetworkStatsService. After
migrating external callers off NetworkStatsFactory, we will be able to
only apply adjustments in NetworkStatsService and remove stacked
interface tracking from NetworkStatsFactory.
Bug: 72107146
Fixes: 72107146
Test: runtest frameworks-net & manual - checked corrected network usage

Change-Id: I5ce450e616b4fddf21f2a491fe5d0c9e9f969bda
This commit is contained in:
Remi NGUYEN VAN
2018-02-27 16:47:22 +09:00
parent 0721bd0ff4
commit 857952d498
3 changed files with 92 additions and 51 deletions

View File

@@ -31,6 +31,7 @@ import java.io.CharArrayWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -97,6 +98,11 @@ public class NetworkStats implements Parcelable {
/** Denotes a request for stats at the interface and UID level. */ /** Denotes a request for stats at the interface and UID level. */
public static final int STATS_PER_UID = 1; public static final int STATS_PER_UID = 1;
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;
// TODO: move fields to "mVariable" notation // TODO: move fields to "mVariable" notation
/** /**
@@ -751,6 +757,75 @@ public class NetworkStats implements Parcelable {
return result; return result;
} }
/**
* Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
*
* <p>This mutates both base and stacked traffic stats, to account respectively for
* double-counted traffic and IPv4/IPv6 header size difference.
*
* <p>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).
*
* <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
* {@code ConcurrentHashMap}
* @param baseTraffic Traffic on the base interfaces. Will be mutated.
* @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
* @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
*/
public static void apply464xlatAdjustments(NetworkStats baseTraffic,
NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
// Total 464xlat traffic to subtract from uid 0 on all base interfaces.
// stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
// For recycling
Entry entry = null;
Entry adjust = new NetworkStats.Entry(IFACE_ALL, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
for (int i = 0; i < stackedTraffic.size; i++) {
entry = stackedTraffic.getValues(i, entry);
if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
continue;
}
final String baseIface = stackedIfaces.get(entry.iface);
if (baseIface == null) {
continue;
}
// Subtract any 464lat traffic seen for the root UID on the current base interface.
adjust.iface = baseIface;
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;
adjustments.combineValues(adjust);
// For 464xlat traffic, xt_qtaguid only counts the bytes of the native 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).
entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA;
stackedTraffic.setValues(i, entry);
}
baseTraffic.combineAllValues(adjustments);
}
/**
* Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
*
* <p>This mutates the object this method is called on. Equivalent to calling
* {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as
* base and stacked traffic.
* @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
*/
public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
apply464xlatAdjustments(this, this, stackedIfaces);
}
/** /**
* Return total statistics grouped by {@link #iface}; doesn't mutate the * Return total statistics grouped by {@link #iface}; doesn't mutate the
* original structure. * original structure.

View File

@@ -54,11 +54,6 @@ public class NetworkStatsFactory {
private static final boolean USE_NATIVE_PARSING = true; private static final boolean USE_NATIVE_PARSING = true;
private static final boolean SANITY_CHECK_NATIVE = false; 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/dev}. */ /** Path to {@code /proc/net/dev}. */
private final File mStatsIfaceDev; private final File mStatsIfaceDev;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */ /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
@@ -94,7 +89,7 @@ public class NetworkStatsFactory {
* {@link #noteStackedIface(String, String)}, but only interfaces noted before this method * {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
* is called are guaranteed to be included. * is called are guaranteed to be included.
*/ */
public static String[] augmentWithStackedInterfacesLocked(@Nullable String[] requiredIfaces) { public static String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
if (requiredIfaces == NetworkStats.INTERFACES_ALL) { if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
return null; return null;
} }
@@ -116,6 +111,15 @@ public class NetworkStatsFactory {
return relatedIfaces.toArray(outArray); return relatedIfaces.toArray(outArray);
} }
/**
* Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
* @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
*/
public static void apply464xlatAdjustments(NetworkStats baseTraffic,
NetworkStats stackedTraffic) {
NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces);
}
@VisibleForTesting @VisibleForTesting
public static void clearStackedIfaces() { public static void clearStackedIfaces() {
sStackedIfaces.clear(); sStackedIfaces.clear();
@@ -287,48 +291,10 @@ public class NetworkStatsFactory {
NetworkStats lastStats) throws IOException { NetworkStats lastStats) throws IOException {
final NetworkStats stats = final NetworkStats stats =
readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats); readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats);
// Total 464xlat traffic to subtract from uid 0 on all base interfaces.
// sStackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
final NetworkStats adjustments = new NetworkStats(0, sStackedIfaces.size());
NetworkStats.Entry entry = null; // For recycling // No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
// TODO: remove this and only apply adjustments in NetworkStatsService.
// For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4 stats.apply464xlatAdjustments(sStackedIfaces);
// 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).
for (int i = 0; i < stats.size(); i++) {
entry = stats.getValues(i, entry);
if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
continue;
}
final String baseIface = sStackedIfaces.get(entry.iface);
if (baseIface == null) {
continue;
}
NetworkStats.Entry adjust =
new NetworkStats.Entry(baseIface, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
// Subtract any 464lat traffic seen for the root UID on the current base interface.
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;
adjustments.combineValues(adjust);
// For 464xlat traffic, xt_qtaguid only counts the bytes of the native 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).
entry.rxBytes = entry.rxPackets * IPV4V6_HEADER_DELTA;
entry.txBytes = entry.txPackets * IPV4V6_HEADER_DELTA;
entry.rxPackets = 0;
entry.txPackets = 0;
stats.combineValues(entry);
}
stats.combineAllValues(adjustments);
return stats; return stats;
} }

View File

@@ -146,7 +146,6 @@ import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Collect and persist detailed network statistics, and provide this data to * Collect and persist detailed network statistics, and provide this data to
@@ -756,7 +755,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public NetworkStats getDetailedUidStats(String[] requiredIfaces) { public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
try { try {
final String[] ifacesToQuery = final String[] ifacesToQuery =
NetworkStatsFactory.augmentWithStackedInterfacesLocked(requiredIfaces); NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
return getNetworkStatsUidDetail(ifacesToQuery); return getNetworkStatsUidDetail(ifacesToQuery);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.wtf(TAG, "Error compiling UID stats", e); Log.wtf(TAG, "Error compiling UID stats", e);
@@ -1490,12 +1489,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private NetworkStats getNetworkStatsUidDetail(String[] ifaces) private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
throws RemoteException { throws RemoteException {
// TODO: remove 464xlat adjustments from NetworkStatsFactory and apply all at once here.
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL, final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL,
ifaces); ifaces);
// fold tethering stats and operations into uid snapshot // fold tethering stats and operations into uid snapshot
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID); final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL); tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
uidSnapshot.combineAllValues(tetherSnapshot); uidSnapshot.combineAllValues(tetherSnapshot);
final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService( final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
@@ -1505,13 +1506,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID); final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
if (vtStats != null) { if (vtStats != null) {
vtStats.filter(UID_ALL, ifaces, TAG_ALL); vtStats.filter(UID_ALL, ifaces, TAG_ALL);
NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats);
uidSnapshot.combineAllValues(vtStats); uidSnapshot.combineAllValues(vtStats);
} }
uidSnapshot.combineAllValues(mUidOperations); uidSnapshot.combineAllValues(mUidOperations);
// TODO: apply tethering & VC 464xlat adjustments here
return uidSnapshot; return uidSnapshot;
} }