[NFCT.TETHER.1] Add conntrack monitor to BpfCoordinator
A preparation for starting monitoring conntrack event which is required by IPv4 tethering offload. Test: atest TetheringCoverageTests Change-Id: Ied46aeca193554f52a90889dfdf92827e94845d6
This commit is contained in:
@@ -1111,9 +1111,19 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startConntrackMonitoring() {
|
||||||
|
mBpfCoordinator.startMonitoring(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopConntrackMonitoring() {
|
||||||
|
mBpfCoordinator.stopMonitoring(this);
|
||||||
|
}
|
||||||
|
|
||||||
class BaseServingState extends State {
|
class BaseServingState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
|
startConntrackMonitoring();
|
||||||
|
|
||||||
if (!startIPv4()) {
|
if (!startIPv4()) {
|
||||||
mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
||||||
return;
|
return;
|
||||||
@@ -1149,6 +1159,7 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stopIPv4();
|
stopIPv4();
|
||||||
|
stopConntrackMonitoring();
|
||||||
|
|
||||||
resetLinkProperties();
|
resetLinkProperties();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import android.net.MacAddress;
|
|||||||
import android.net.NetworkStats;
|
import android.net.NetworkStats;
|
||||||
import android.net.NetworkStats.Entry;
|
import android.net.NetworkStats.Entry;
|
||||||
import android.net.TetherOffloadRuleParcel;
|
import android.net.TetherOffloadRuleParcel;
|
||||||
|
import android.net.ip.ConntrackMonitor;
|
||||||
|
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
|
||||||
import android.net.ip.IpServer;
|
import android.net.ip.IpServer;
|
||||||
import android.net.netstats.provider.NetworkStatsProvider;
|
import android.net.netstats.provider.NetworkStatsProvider;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
@@ -57,9 +59,11 @@ import com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim;
|
|||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This coordinator is responsible for providing BPF offload relevant functionality.
|
* This coordinator is responsible for providing BPF offload relevant functionality.
|
||||||
@@ -94,6 +98,8 @@ public class BpfCoordinator {
|
|||||||
private final SharedLog mLog;
|
private final SharedLog mLog;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Dependencies mDeps;
|
private final Dependencies mDeps;
|
||||||
|
@NonNull
|
||||||
|
private final ConntrackMonitor mConntrackMonitor;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final BpfTetherStatsProvider mStatsProvider;
|
private final BpfTetherStatsProvider mStatsProvider;
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -156,6 +162,9 @@ public class BpfCoordinator {
|
|||||||
private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
|
private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
|
||||||
mIpv6ForwardingRules = new LinkedHashMap<>();
|
mIpv6ForwardingRules = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Set for which downstream is monitoring the conntrack netlink message.
|
||||||
|
private final Set<IpServer> mMonitoringIpServers = new HashSet<>();
|
||||||
|
|
||||||
// Runnable that used by scheduling next polling of stats.
|
// Runnable that used by scheduling next polling of stats.
|
||||||
private final Runnable mScheduledPollingTask = () -> {
|
private final Runnable mScheduledPollingTask = () -> {
|
||||||
updateForwardedStats();
|
updateForwardedStats();
|
||||||
@@ -179,6 +188,11 @@ public class BpfCoordinator {
|
|||||||
/** Get tethering configuration. */
|
/** Get tethering configuration. */
|
||||||
@Nullable public abstract TetheringConfiguration getTetherConfig();
|
@Nullable public abstract TetheringConfiguration getTetherConfig();
|
||||||
|
|
||||||
|
/** Get conntrack monitor. */
|
||||||
|
@NonNull public ConntrackMonitor getConntrackMonitor(ConntrackEventConsumer consumer) {
|
||||||
|
return new ConntrackMonitor(getHandler(), getSharedLog(), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check OS Build at least S.
|
* Check OS Build at least S.
|
||||||
*
|
*
|
||||||
@@ -232,6 +246,7 @@ public class BpfCoordinator {
|
|||||||
mNetd = mDeps.getNetd();
|
mNetd = mDeps.getNetd();
|
||||||
mLog = mDeps.getSharedLog().forSubComponent(TAG);
|
mLog = mDeps.getSharedLog().forSubComponent(TAG);
|
||||||
mIsBpfEnabled = isBpfEnabled();
|
mIsBpfEnabled = isBpfEnabled();
|
||||||
|
mConntrackMonitor = mDeps.getConntrackMonitor(new BpfConntrackEventConsumer());
|
||||||
BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
|
BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
|
||||||
try {
|
try {
|
||||||
mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
|
mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
|
||||||
@@ -295,6 +310,58 @@ public class BpfCoordinator {
|
|||||||
return mIsBpfEnabled && mBpfCoordinatorShim.isInitialized();
|
return mIsBpfEnabled && mBpfCoordinatorShim.isInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start conntrack message monitoring.
|
||||||
|
* Note that this can be only called on handler thread.
|
||||||
|
*
|
||||||
|
* TODO: figure out a better logging for non-interesting conntrack message.
|
||||||
|
* For example, the following logging is an IPCTNL_MSG_CT_GET message but looks scary.
|
||||||
|
* +---------------------------------------------------------------------------+
|
||||||
|
* | ERROR unparsable netlink msg: 1400000001010103000000000000000002000000 |
|
||||||
|
* +------------------+--------------------------------------------------------+
|
||||||
|
* | | struct nlmsghdr |
|
||||||
|
* | 14000000 | length = 20 |
|
||||||
|
* | 0101 | type = NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET |
|
||||||
|
* | 0103 | flags |
|
||||||
|
* | 00000000 | seqno = 0 |
|
||||||
|
* | 00000000 | pid = 0 |
|
||||||
|
* | | struct nfgenmsg |
|
||||||
|
* | 02 | nfgen_family = AF_INET |
|
||||||
|
* | 00 | version = NFNETLINK_V0 |
|
||||||
|
* | 0000 | res_id |
|
||||||
|
* +------------------+--------------------------------------------------------+
|
||||||
|
* See NetlinkMonitor#handlePacket, NetlinkMessage#parseNfMessage.
|
||||||
|
*/
|
||||||
|
public void startMonitoring(@NonNull final IpServer ipServer) {
|
||||||
|
if (!isUsingBpf()) return;
|
||||||
|
|
||||||
|
if (mMonitoringIpServers.contains(ipServer)) {
|
||||||
|
Log.wtf(TAG, "The same downstream " + ipServer.interfaceName()
|
||||||
|
+ " should not start monitoring twice.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMonitoringIpServers.isEmpty()) {
|
||||||
|
mConntrackMonitor.start();
|
||||||
|
mLog.i("Monitoring started");
|
||||||
|
}
|
||||||
|
|
||||||
|
mMonitoringIpServers.add(ipServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop conntrack event monitoring.
|
||||||
|
* Note that this can be only called on handler thread.
|
||||||
|
*/
|
||||||
|
public void stopMonitoring(@NonNull final IpServer ipServer) {
|
||||||
|
mMonitoringIpServers.remove(ipServer);
|
||||||
|
|
||||||
|
if (!mMonitoringIpServers.isEmpty()) return;
|
||||||
|
|
||||||
|
mConntrackMonitor.stop();
|
||||||
|
mLog.i("Monitoring stopped");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add forwarding rule. After adding the first rule on a given upstream, must add the data
|
* Add forwarding rule. After adding the first rule on a given upstream, must add the data
|
||||||
* limit on the given upstream.
|
* limit on the given upstream.
|
||||||
@@ -656,6 +723,10 @@ public class BpfCoordinator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class BpfConntrackEventConsumer implements ConntrackEventConsumer {
|
||||||
|
public void accept(ConntrackMonitor.ConntrackEvent e) { /* TODO */ }
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isBpfEnabled() {
|
private boolean isBpfEnabled() {
|
||||||
final TetheringConfiguration config = mDeps.getTetherConfig();
|
final TetheringConfiguration config = mDeps.getTetherConfig();
|
||||||
return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */;
|
return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */;
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ public class IpServerTest {
|
|||||||
@Mock private PrivateAddressCoordinator mAddressCoordinator;
|
@Mock private PrivateAddressCoordinator mAddressCoordinator;
|
||||||
@Mock private NetworkStatsManager mStatsManager;
|
@Mock private NetworkStatsManager mStatsManager;
|
||||||
@Mock private TetheringConfiguration mTetherConfig;
|
@Mock private TetheringConfiguration mTetherConfig;
|
||||||
|
@Mock private ConntrackMonitor mConntrackMonitor;
|
||||||
@Mock private BpfMap<TetherDownstream6Key, TetherDownstream6Value> mBpfDownstream6Map;
|
@Mock private BpfMap<TetherDownstream6Key, TetherDownstream6Value> mBpfDownstream6Map;
|
||||||
@Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
|
@Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
|
||||||
@Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
|
@Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
|
||||||
@@ -295,6 +296,12 @@ public class IpServerTest {
|
|||||||
return mTetherConfig;
|
return mTetherConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ConntrackMonitor getConntrackMonitor(
|
||||||
|
ConntrackMonitor.ConntrackEventConsumer consumer) {
|
||||||
|
return mConntrackMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
|
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
|
||||||
getBpfDownstream6Map() {
|
getBpfDownstream6Map() {
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ import android.net.MacAddress;
|
|||||||
import android.net.NetworkStats;
|
import android.net.NetworkStats;
|
||||||
import android.net.TetherOffloadRuleParcel;
|
import android.net.TetherOffloadRuleParcel;
|
||||||
import android.net.TetherStatsParcel;
|
import android.net.TetherStatsParcel;
|
||||||
|
import android.net.ip.ConntrackMonitor;
|
||||||
|
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
|
||||||
import android.net.ip.IpServer;
|
import android.net.ip.IpServer;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -153,7 +155,9 @@ public class BpfCoordinatorTest {
|
|||||||
@Mock private NetworkStatsManager mStatsManager;
|
@Mock private NetworkStatsManager mStatsManager;
|
||||||
@Mock private INetd mNetd;
|
@Mock private INetd mNetd;
|
||||||
@Mock private IpServer mIpServer;
|
@Mock private IpServer mIpServer;
|
||||||
|
@Mock private IpServer mIpServer2;
|
||||||
@Mock private TetheringConfiguration mTetherConfig;
|
@Mock private TetheringConfiguration mTetherConfig;
|
||||||
|
@Mock private ConntrackMonitor mConntrackMonitor;
|
||||||
@Mock private BpfMap<TetherDownstream6Key, TetherDownstream6Value> mBpfDownstream6Map;
|
@Mock private BpfMap<TetherDownstream6Key, TetherDownstream6Value> mBpfDownstream6Map;
|
||||||
|
|
||||||
// Late init since methods must be called by the thread that created this object.
|
// Late init since methods must be called by the thread that created this object.
|
||||||
@@ -193,6 +197,11 @@ public class BpfCoordinatorTest {
|
|||||||
return mTetherConfig;
|
return mTetherConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ConntrackMonitor getConntrackMonitor(ConntrackEventConsumer consumer) {
|
||||||
|
return mConntrackMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
|
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
|
||||||
getBpfDownstream6Map() {
|
getBpfDownstream6Map() {
|
||||||
@@ -983,4 +992,48 @@ public class BpfCoordinatorTest {
|
|||||||
waitForIdle();
|
waitForIdle();
|
||||||
verifyTetherOffloadGetStats();
|
verifyTetherOffloadGetStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartStopConntrackMonitoring() throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
|
||||||
|
// [1] Don't stop monitoring if it has never started.
|
||||||
|
coordinator.stopMonitoring(mIpServer);
|
||||||
|
verify(mConntrackMonitor, never()).start();
|
||||||
|
|
||||||
|
// [2] Start monitoring.
|
||||||
|
coordinator.startMonitoring(mIpServer);
|
||||||
|
verify(mConntrackMonitor).start();
|
||||||
|
clearInvocations(mConntrackMonitor);
|
||||||
|
|
||||||
|
// [3] Stop monitoring.
|
||||||
|
coordinator.stopMonitoring(mIpServer);
|
||||||
|
verify(mConntrackMonitor).stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartStopConntrackMonitoringWithTwoDownstreamIfaces() throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
|
||||||
|
// [1] Start monitoring at the first IpServer adding.
|
||||||
|
coordinator.startMonitoring(mIpServer);
|
||||||
|
verify(mConntrackMonitor).start();
|
||||||
|
clearInvocations(mConntrackMonitor);
|
||||||
|
|
||||||
|
// [2] Don't start monitoring at the second IpServer adding.
|
||||||
|
coordinator.startMonitoring(mIpServer2);
|
||||||
|
verify(mConntrackMonitor, never()).start();
|
||||||
|
|
||||||
|
// [3] Don't stop monitoring if any downstream interface exists.
|
||||||
|
coordinator.stopMonitoring(mIpServer2);
|
||||||
|
verify(mConntrackMonitor, never()).stop();
|
||||||
|
|
||||||
|
// [4] Stop monitoring if no downstream exists.
|
||||||
|
coordinator.stopMonitoring(mIpServer);
|
||||||
|
verify(mConntrackMonitor).stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user