[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 {
|
||||
@Override
|
||||
public void enter() {
|
||||
startConntrackMonitoring();
|
||||
|
||||
if (!startIPv4()) {
|
||||
mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
||||
return;
|
||||
@@ -1149,6 +1159,7 @@ public class IpServer extends StateMachine {
|
||||
}
|
||||
|
||||
stopIPv4();
|
||||
stopConntrackMonitoring();
|
||||
|
||||
resetLinkProperties();
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ import android.net.MacAddress;
|
||||
import android.net.NetworkStats;
|
||||
import android.net.NetworkStats.Entry;
|
||||
import android.net.TetherOffloadRuleParcel;
|
||||
import android.net.ip.ConntrackMonitor;
|
||||
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
|
||||
import android.net.ip.IpServer;
|
||||
import android.net.netstats.provider.NetworkStatsProvider;
|
||||
import android.net.util.SharedLog;
|
||||
@@ -57,9 +59,11 @@ import com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim;
|
||||
import java.net.Inet6Address;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This coordinator is responsible for providing BPF offload relevant functionality.
|
||||
@@ -94,6 +98,8 @@ public class BpfCoordinator {
|
||||
private final SharedLog mLog;
|
||||
@NonNull
|
||||
private final Dependencies mDeps;
|
||||
@NonNull
|
||||
private final ConntrackMonitor mConntrackMonitor;
|
||||
@Nullable
|
||||
private final BpfTetherStatsProvider mStatsProvider;
|
||||
@NonNull
|
||||
@@ -156,6 +162,9 @@ public class BpfCoordinator {
|
||||
private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
|
||||
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.
|
||||
private final Runnable mScheduledPollingTask = () -> {
|
||||
updateForwardedStats();
|
||||
@@ -179,6 +188,11 @@ public class BpfCoordinator {
|
||||
/** Get tethering configuration. */
|
||||
@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.
|
||||
*
|
||||
@@ -232,6 +246,7 @@ public class BpfCoordinator {
|
||||
mNetd = mDeps.getNetd();
|
||||
mLog = mDeps.getSharedLog().forSubComponent(TAG);
|
||||
mIsBpfEnabled = isBpfEnabled();
|
||||
mConntrackMonitor = mDeps.getConntrackMonitor(new BpfConntrackEventConsumer());
|
||||
BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
|
||||
try {
|
||||
mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
|
||||
@@ -295,6 +310,58 @@ public class BpfCoordinator {
|
||||
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
|
||||
* 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() {
|
||||
final TetheringConfiguration config = mDeps.getTetherConfig();
|
||||
return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */;
|
||||
|
||||
@@ -172,6 +172,7 @@ public class IpServerTest {
|
||||
@Mock private PrivateAddressCoordinator mAddressCoordinator;
|
||||
@Mock private NetworkStatsManager mStatsManager;
|
||||
@Mock private TetheringConfiguration mTetherConfig;
|
||||
@Mock private ConntrackMonitor mConntrackMonitor;
|
||||
@Mock private BpfMap<TetherDownstream6Key, TetherDownstream6Value> mBpfDownstream6Map;
|
||||
@Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
|
||||
@Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
|
||||
@@ -295,6 +296,12 @@ public class IpServerTest {
|
||||
return mTetherConfig;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ConntrackMonitor getConntrackMonitor(
|
||||
ConntrackMonitor.ConntrackEventConsumer consumer) {
|
||||
return mConntrackMonitor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
|
||||
getBpfDownstream6Map() {
|
||||
|
||||
@@ -56,6 +56,8 @@ import android.net.MacAddress;
|
||||
import android.net.NetworkStats;
|
||||
import android.net.TetherOffloadRuleParcel;
|
||||
import android.net.TetherStatsParcel;
|
||||
import android.net.ip.ConntrackMonitor;
|
||||
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
|
||||
import android.net.ip.IpServer;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.Build;
|
||||
@@ -153,7 +155,9 @@ public class BpfCoordinatorTest {
|
||||
@Mock private NetworkStatsManager mStatsManager;
|
||||
@Mock private INetd mNetd;
|
||||
@Mock private IpServer mIpServer;
|
||||
@Mock private IpServer mIpServer2;
|
||||
@Mock private TetheringConfiguration mTetherConfig;
|
||||
@Mock private ConntrackMonitor mConntrackMonitor;
|
||||
@Mock private BpfMap<TetherDownstream6Key, TetherDownstream6Value> mBpfDownstream6Map;
|
||||
|
||||
// Late init since methods must be called by the thread that created this object.
|
||||
@@ -193,6 +197,11 @@ public class BpfCoordinatorTest {
|
||||
return mTetherConfig;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ConntrackMonitor getConntrackMonitor(ConntrackEventConsumer consumer) {
|
||||
return mConntrackMonitor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
|
||||
getBpfDownstream6Map() {
|
||||
@@ -983,4 +992,48 @@ public class BpfCoordinatorTest {
|
||||
waitForIdle();
|
||||
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