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 Merged-In: Ieb25c41c651499fdd01225ae5ac21d95e3d823f5 Merged-In: I016722f3a0ae2ae0a1d48bfacc4fe07ee3578ef7 (cherry-pick of aosp I5ce450e616b4fddf21f2a491fe5d0c9e9f969bda) Change-Id: Id41cf22a0f9a63cb1832e9375bfb045861f08e52
This commit is contained in:
@@ -31,6 +31,7 @@ import java.io.CharArrayWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -97,6 +98,11 @@ public class NetworkStats implements Parcelable {
|
||||
/** Denotes a request for stats at the interface and UID level. */
|
||||
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
|
||||
|
||||
/**
|
||||
@@ -758,6 +764,75 @@ public class NetworkStats implements Parcelable {
|
||||
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
|
||||
* original structure.
|
||||
|
||||
@@ -56,11 +56,6 @@ 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/dev}. */
|
||||
private final File mStatsIfaceDev;
|
||||
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
|
||||
@@ -96,7 +91,7 @@ public class NetworkStatsFactory {
|
||||
* {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
|
||||
* 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) {
|
||||
return null;
|
||||
}
|
||||
@@ -118,6 +113,15 @@ public class NetworkStatsFactory {
|
||||
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
|
||||
public static void clearStackedIfaces() {
|
||||
sStackedIfaces.clear();
|
||||
@@ -289,48 +293,10 @@ public class NetworkStatsFactory {
|
||||
NetworkStats lastStats) throws IOException {
|
||||
final NetworkStats stats =
|
||||
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
|
||||
|
||||
// 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).
|
||||
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);
|
||||
// No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
|
||||
// TODO: remove this and only apply adjustments in NetworkStatsService.
|
||||
stats.apply464xlatAdjustments(sStackedIfaces);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user