Merge "[NFCT.TETHER.11] Test TetherOffloadRule{Add, Remove} and set limit for IPv4" am: 68f6f5fe79
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/1563332 Change-Id: Id5f0d10b07b8fde23d627eaa7058b38512591f98
This commit is contained in:
@@ -137,6 +137,8 @@ public class BpfCoordinator {
|
|||||||
private final BpfTetherStatsProvider mStatsProvider;
|
private final BpfTetherStatsProvider mStatsProvider;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final BpfCoordinatorShim mBpfCoordinatorShim;
|
private final BpfCoordinatorShim mBpfCoordinatorShim;
|
||||||
|
@NonNull
|
||||||
|
private final BpfConntrackEventConsumer mBpfConntrackEventConsumer;
|
||||||
|
|
||||||
// True if BPF offload is supported, false otherwise. The BPF offload could be disabled by
|
// True if BPF offload is supported, false otherwise. The BPF offload could be disabled by
|
||||||
// a runtime resource overlay package or device configuration. This flag is only initialized
|
// a runtime resource overlay package or device configuration. This flag is only initialized
|
||||||
@@ -248,6 +250,11 @@ public class BpfCoordinator {
|
|||||||
return new ConntrackMonitor(getHandler(), getSharedLog(), consumer);
|
return new ConntrackMonitor(getHandler(), getSharedLog(), consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get interface information for a given interface. */
|
||||||
|
@NonNull public InterfaceParams getInterfaceParams(String ifName) {
|
||||||
|
return InterfaceParams.getByName(ifName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check OS Build at least S.
|
* Check OS Build at least S.
|
||||||
*
|
*
|
||||||
@@ -339,7 +346,14 @@ 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());
|
|
||||||
|
// The conntrack consummer needs to be initialized in BpfCoordinator constructor because it
|
||||||
|
// have to access the data members of BpfCoordinator which is not a static class. The
|
||||||
|
// consumer object is also needed for initializing the conntrack monitor which may be
|
||||||
|
// mocked for testing.
|
||||||
|
mBpfConntrackEventConsumer = new BpfConntrackEventConsumer();
|
||||||
|
mConntrackMonitor = mDeps.getConntrackMonitor(mBpfConntrackEventConsumer);
|
||||||
|
|
||||||
BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
|
BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
|
||||||
try {
|
try {
|
||||||
mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
|
mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
|
||||||
@@ -662,7 +676,7 @@ public class BpfCoordinator {
|
|||||||
if (lp == null || !lp.hasIpv4Address()) return;
|
if (lp == null || !lp.hasIpv4Address()) return;
|
||||||
|
|
||||||
// Support raw ip upstream interface only.
|
// Support raw ip upstream interface only.
|
||||||
final InterfaceParams params = InterfaceParams.getByName(lp.getInterfaceName());
|
final InterfaceParams params = mDeps.getInterfaceParams(lp.getInterfaceName());
|
||||||
if (params == null || params.hasMacAddress) return;
|
if (params == null || params.hasMacAddress) return;
|
||||||
|
|
||||||
Collection<InetAddress> addresses = lp.getAddresses();
|
Collection<InetAddress> addresses = lp.getAddresses();
|
||||||
@@ -1148,7 +1162,8 @@ public class BpfCoordinator {
|
|||||||
// TODO: add ether ip support.
|
// TODO: add ether ip support.
|
||||||
// TODO: parse CTA_PROTOINFO of conntrack event in ConntrackMonitor. For TCP, only add rules
|
// TODO: parse CTA_PROTOINFO of conntrack event in ConntrackMonitor. For TCP, only add rules
|
||||||
// while TCP status is established.
|
// while TCP status is established.
|
||||||
private class BpfConntrackEventConsumer implements ConntrackEventConsumer {
|
@VisibleForTesting
|
||||||
|
class BpfConntrackEventConsumer implements ConntrackEventConsumer {
|
||||||
@NonNull
|
@NonNull
|
||||||
private Tether4Key makeTetherUpstream4Key(
|
private Tether4Key makeTetherUpstream4Key(
|
||||||
@NonNull ConntrackEvent e, @NonNull ClientInfo c) {
|
@NonNull ConntrackEvent e, @NonNull ClientInfo c) {
|
||||||
@@ -1497,5 +1512,13 @@ public class BpfCoordinator {
|
|||||||
return mInterfaceNames;
|
return mInterfaceNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return BPF conntrack event consumer. This is used for testing only.
|
||||||
|
// Note that this can be only called on handler thread.
|
||||||
|
@NonNull
|
||||||
|
@VisibleForTesting
|
||||||
|
final BpfConntrackEventConsumer getBpfConntrackEventConsumerForTesting() {
|
||||||
|
return mBpfConntrackEventConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
private static native String[] getBpfCounterNames();
|
private static native String[] getBpfCounterNames();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,21 @@ import static android.net.NetworkStats.SET_DEFAULT;
|
|||||||
import static android.net.NetworkStats.TAG_NONE;
|
import static android.net.NetworkStats.TAG_NONE;
|
||||||
import static android.net.NetworkStats.UID_ALL;
|
import static android.net.NetworkStats.UID_ALL;
|
||||||
import static android.net.NetworkStats.UID_TETHERING;
|
import static android.net.NetworkStats.UID_TETHERING;
|
||||||
|
import static android.net.ip.ConntrackMonitor.ConntrackEvent;
|
||||||
|
import static android.net.netlink.ConntrackMessage.DYING_MASK;
|
||||||
|
import static android.net.netlink.ConntrackMessage.ESTABLISHED_MASK;
|
||||||
|
import static android.net.netlink.ConntrackMessage.Tuple;
|
||||||
|
import static android.net.netlink.ConntrackMessage.TupleIpv4;
|
||||||
|
import static android.net.netlink.ConntrackMessage.TupleProto;
|
||||||
|
import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_DELETE;
|
||||||
|
import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW;
|
||||||
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
|
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
|
||||||
|
import static android.system.OsConstants.ETH_P_IP;
|
||||||
import static android.system.OsConstants.ETH_P_IPV6;
|
import static android.system.OsConstants.ETH_P_IPV6;
|
||||||
|
import static android.system.OsConstants.IPPROTO_TCP;
|
||||||
|
import static android.system.OsConstants.IPPROTO_UDP;
|
||||||
|
|
||||||
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
|
||||||
import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
|
import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
|
||||||
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
|
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
|
||||||
@@ -43,9 +55,9 @@ import static org.mockito.Matchers.any;
|
|||||||
import static org.mockito.Matchers.anyInt;
|
import static org.mockito.Matchers.anyInt;
|
||||||
import static org.mockito.Matchers.anyLong;
|
import static org.mockito.Matchers.anyLong;
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Mockito.argThat;
|
import static org.mockito.Mockito.argThat;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
@@ -55,6 +67,8 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.app.usage.NetworkStatsManager;
|
import android.app.usage.NetworkStatsManager;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
import android.net.InetAddresses;
|
import android.net.InetAddresses;
|
||||||
|
import android.net.LinkAddress;
|
||||||
|
import android.net.LinkProperties;
|
||||||
import android.net.MacAddress;
|
import android.net.MacAddress;
|
||||||
import android.net.NetworkStats;
|
import android.net.NetworkStats;
|
||||||
import android.net.TetherOffloadRuleParcel;
|
import android.net.TetherOffloadRuleParcel;
|
||||||
@@ -62,6 +76,8 @@ import android.net.TetherStatsParcel;
|
|||||||
import android.net.ip.ConntrackMonitor;
|
import android.net.ip.ConntrackMonitor;
|
||||||
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
|
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
|
||||||
import android.net.ip.IpServer;
|
import android.net.ip.IpServer;
|
||||||
|
import android.net.netlink.NetlinkConstants;
|
||||||
|
import android.net.util.InterfaceParams;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -76,6 +92,8 @@ import androidx.test.runner.AndroidJUnit4;
|
|||||||
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
||||||
import com.android.net.module.util.NetworkStackConstants;
|
import com.android.net.module.util.NetworkStackConstants;
|
||||||
import com.android.net.module.util.Struct;
|
import com.android.net.module.util.Struct;
|
||||||
|
import com.android.networkstack.tethering.BpfCoordinator.BpfConntrackEventConsumer;
|
||||||
|
import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
|
||||||
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRule;
|
import com.android.testutils.DevSdkIgnoreRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
||||||
@@ -93,6 +111,7 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.MockitoSession;
|
import org.mockito.MockitoSession;
|
||||||
|
|
||||||
|
import java.net.Inet4Address;
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -108,13 +127,22 @@ public class BpfCoordinatorTest {
|
|||||||
@Rule
|
@Rule
|
||||||
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||||
|
|
||||||
private static final int DOWNSTREAM_IFINDEX = 10;
|
private static final int UPSTREAM_IFINDEX = 1001;
|
||||||
|
private static final int DOWNSTREAM_IFINDEX = 1002;
|
||||||
|
|
||||||
|
private static final String UPSTREAM_IFACE = "rmnet0";
|
||||||
|
|
||||||
private static final MacAddress DOWNSTREAM_MAC = MacAddress.fromString("12:34:56:78:90:ab");
|
private static final MacAddress DOWNSTREAM_MAC = MacAddress.fromString("12:34:56:78:90:ab");
|
||||||
private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1");
|
|
||||||
private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2");
|
|
||||||
private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a");
|
private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a");
|
||||||
private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b");
|
private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b");
|
||||||
|
|
||||||
|
private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1");
|
||||||
|
private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2");
|
||||||
|
|
||||||
|
private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams(
|
||||||
|
UPSTREAM_IFACE, UPSTREAM_IFINDEX, null /* macAddr, rawip */,
|
||||||
|
NetworkStackConstants.ETHER_MTU);
|
||||||
|
|
||||||
// The test fake BPF map class is needed because the test has no privilege to access the BPF
|
// The test fake BPF map class is needed because the test has no privilege to access the BPF
|
||||||
// map. All member functions which eventually call JNI to access the real native BPF map need
|
// map. All member functions which eventually call JNI to access the real native BPF map need
|
||||||
// to be overridden.
|
// to be overridden.
|
||||||
@@ -183,6 +211,11 @@ public class BpfCoordinatorTest {
|
|||||||
// 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.
|
||||||
private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
|
private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
|
||||||
private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider;
|
private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider;
|
||||||
|
|
||||||
|
// Late init since the object must be initialized by the BPF coordinator instance because
|
||||||
|
// it has to access the non-static function of BPF coordinator.
|
||||||
|
private BpfConntrackEventConsumer mConsumer;
|
||||||
|
|
||||||
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
|
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
|
||||||
ArgumentCaptor.forClass(ArrayList.class);
|
ArgumentCaptor.forClass(ArrayList.class);
|
||||||
private final TestLooper mTestLooper = new TestLooper();
|
private final TestLooper mTestLooper = new TestLooper();
|
||||||
@@ -262,6 +295,8 @@ public class BpfCoordinatorTest {
|
|||||||
mTestLooper.dispatchAll();
|
mTestLooper.dispatchAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove unnecessary calling on R because the BPF map accessing has been moved into
|
||||||
|
// module.
|
||||||
private void setupFunctioningNetdInterface() throws Exception {
|
private void setupFunctioningNetdInterface() throws Exception {
|
||||||
when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
|
when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
|
||||||
}
|
}
|
||||||
@@ -269,6 +304,8 @@ public class BpfCoordinatorTest {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private BpfCoordinator makeBpfCoordinator() throws Exception {
|
private BpfCoordinator makeBpfCoordinator() throws Exception {
|
||||||
final BpfCoordinator coordinator = new BpfCoordinator(mDeps);
|
final BpfCoordinator coordinator = new BpfCoordinator(mDeps);
|
||||||
|
|
||||||
|
mConsumer = coordinator.getBpfConntrackEventConsumerForTesting();
|
||||||
final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider>
|
final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider>
|
||||||
tetherStatsProviderCaptor =
|
tetherStatsProviderCaptor =
|
||||||
ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class);
|
ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class);
|
||||||
@@ -278,6 +315,7 @@ public class BpfCoordinatorTest {
|
|||||||
assertNotNull(mTetherStatsProvider);
|
assertNotNull(mTetherStatsProvider);
|
||||||
mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
|
mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
|
||||||
mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
|
mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
|
||||||
|
|
||||||
return coordinator;
|
return coordinator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,7 +504,7 @@ public class BpfCoordinatorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyNeverTetherOffloadSetInterfaceQuota(@Nullable InOrder inOrder)
|
private void verifyNeverTetherOffloadSetInterfaceQuota(@NonNull InOrder inOrder)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
if (mDeps.isAtLeastS()) {
|
if (mDeps.isAtLeastS()) {
|
||||||
inOrder.verify(mBpfStatsMap, never()).getValue(any());
|
inOrder.verify(mBpfStatsMap, never()).getValue(any());
|
||||||
@@ -477,7 +515,7 @@ public class BpfCoordinatorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyTetherOffloadGetAndClearStats(@Nullable InOrder inOrder, int ifIndex)
|
private void verifyTetherOffloadGetAndClearStats(@NonNull InOrder inOrder, int ifIndex)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
if (mDeps.isAtLeastS()) {
|
if (mDeps.isAtLeastS()) {
|
||||||
inOrder.verify(mBpfStatsMap).getValue(new TetherStatsKey(ifIndex));
|
inOrder.verify(mBpfStatsMap).getValue(new TetherStatsKey(ifIndex));
|
||||||
@@ -799,7 +837,7 @@ public class BpfCoordinatorTest {
|
|||||||
|
|
||||||
// TODO: Test the case in which the rules are changed from different IpServer objects.
|
// TODO: Test the case in which the rules are changed from different IpServer objects.
|
||||||
@Test
|
@Test
|
||||||
public void testSetDataLimitOnRuleChange() throws Exception {
|
public void testSetDataLimitOnRule6Change() throws Exception {
|
||||||
setupFunctioningNetdInterface();
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
final BpfCoordinator coordinator = makeBpfCoordinator();
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
@@ -1219,4 +1257,192 @@ public class BpfCoordinatorTest {
|
|||||||
coordinator.stopMonitoring(mIpServer);
|
coordinator.stopMonitoring(mIpServer);
|
||||||
verify(mConntrackMonitor).stop();
|
verify(mConntrackMonitor).stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test network topology:
|
||||||
|
//
|
||||||
|
// public network (rawip) private network
|
||||||
|
// | UE |
|
||||||
|
// +------------+ V +------------+------------+ V +------------+
|
||||||
|
// | Sever +---------+ Upstream | Downstream +---------+ Client |
|
||||||
|
// +------------+ +------------+------------+ +------------+
|
||||||
|
// remote ip public ip private ip
|
||||||
|
// 140.112.8.116:443 100.81.179.1:62449 192.168.80.12:62449
|
||||||
|
//
|
||||||
|
private static final Inet4Address REMOTE_ADDR =
|
||||||
|
(Inet4Address) InetAddresses.parseNumericAddress("140.112.8.116");
|
||||||
|
private static final Inet4Address PUBLIC_ADDR =
|
||||||
|
(Inet4Address) InetAddresses.parseNumericAddress("100.81.179.1");
|
||||||
|
private static final Inet4Address PRIVATE_ADDR =
|
||||||
|
(Inet4Address) InetAddresses.parseNumericAddress("192.168.80.12");
|
||||||
|
|
||||||
|
// IPv4-mapped IPv6 addresses
|
||||||
|
// Remote addrress ::ffff:140.112.8.116
|
||||||
|
// Public addrress ::ffff:100.81.179.1
|
||||||
|
// Private addrress ::ffff:192.168.80.12
|
||||||
|
private static final byte[] REMOTE_ADDR_V4MAPPED_BYTES = new byte[] {
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff,
|
||||||
|
(byte) 0x8c, (byte) 0x70, (byte) 0x08, (byte) 0x74 };
|
||||||
|
private static final byte[] PUBLIC_ADDR_V4MAPPED_BYTES = new byte[] {
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff,
|
||||||
|
(byte) 0x64, (byte) 0x51, (byte) 0xb3, (byte) 0x01 };
|
||||||
|
private static final byte[] PRIVATE_ADDR_V4MAPPED_BYTES = new byte[] {
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff,
|
||||||
|
(byte) 0xc0, (byte) 0xa8, (byte) 0x50, (byte) 0x0c };
|
||||||
|
|
||||||
|
// Generally, public port and private port are the same in the NAT conntrack message.
|
||||||
|
// TODO: consider using different private port and public port for testing.
|
||||||
|
private static final short REMOTE_PORT = (short) 443;
|
||||||
|
private static final short PUBLIC_PORT = (short) 62449;
|
||||||
|
private static final short PRIVATE_PORT = (short) 62449;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Tether4Key makeUpstream4Key(int proto) {
|
||||||
|
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
|
||||||
|
fail("Not support protocol " + proto);
|
||||||
|
}
|
||||||
|
return new Tether4Key(DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC, (short) proto,
|
||||||
|
PRIVATE_ADDR.getAddress(), REMOTE_ADDR.getAddress(), PRIVATE_PORT, REMOTE_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Tether4Key makeDownstream4Key(int proto) {
|
||||||
|
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
|
||||||
|
fail("Not support protocol " + proto);
|
||||||
|
}
|
||||||
|
return new Tether4Key(UPSTREAM_IFINDEX,
|
||||||
|
MacAddress.ALL_ZEROS_ADDRESS /* dstMac (rawip) */, (short) proto,
|
||||||
|
REMOTE_ADDR.getAddress(), PUBLIC_ADDR.getAddress(), REMOTE_PORT, PUBLIC_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Tether4Value makeUpstream4Value() {
|
||||||
|
return new Tether4Value(UPSTREAM_IFINDEX,
|
||||||
|
MacAddress.ALL_ZEROS_ADDRESS /* ethDstMac (rawip) */,
|
||||||
|
MacAddress.ALL_ZEROS_ADDRESS /* ethSrcMac (rawip) */, ETH_P_IP,
|
||||||
|
NetworkStackConstants.ETHER_MTU, PUBLIC_ADDR_V4MAPPED_BYTES,
|
||||||
|
REMOTE_ADDR_V4MAPPED_BYTES, PUBLIC_PORT, REMOTE_PORT, 0 /* lastUsed */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Tether4Value makeDownstream4Value() {
|
||||||
|
return new Tether4Value(DOWNSTREAM_IFINDEX, MAC_A /* client mac */, DOWNSTREAM_MAC,
|
||||||
|
ETH_P_IP, NetworkStackConstants.ETHER_MTU, REMOTE_ADDR_V4MAPPED_BYTES,
|
||||||
|
PRIVATE_ADDR_V4MAPPED_BYTES, REMOTE_PORT, PRIVATE_PORT, 0 /* lastUsed */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private ConntrackEvent makeTestConntrackEvent(short msgType, int proto) {
|
||||||
|
if (msgType != IPCTNL_MSG_CT_NEW && msgType != IPCTNL_MSG_CT_DELETE) {
|
||||||
|
fail("Not support message type " + msgType);
|
||||||
|
}
|
||||||
|
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
|
||||||
|
fail("Not support protocol " + proto);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int status = (msgType == IPCTNL_MSG_CT_NEW) ? ESTABLISHED_MASK : DYING_MASK;
|
||||||
|
final int timeoutSec = (msgType == IPCTNL_MSG_CT_NEW) ? 100 /* nonzero, new */
|
||||||
|
: 0 /* unused, delete */;
|
||||||
|
return new ConntrackEvent(
|
||||||
|
(short) (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8 | msgType),
|
||||||
|
new Tuple(new TupleIpv4(PRIVATE_ADDR, REMOTE_ADDR),
|
||||||
|
new TupleProto((byte) proto, PRIVATE_PORT, REMOTE_PORT)),
|
||||||
|
new Tuple(new TupleIpv4(REMOTE_ADDR, PUBLIC_ADDR),
|
||||||
|
new TupleProto((byte) proto, REMOTE_PORT, PUBLIC_PORT)),
|
||||||
|
status,
|
||||||
|
timeoutSec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpstreamInformationTo(final BpfCoordinator coordinator) {
|
||||||
|
final LinkProperties lp = new LinkProperties();
|
||||||
|
lp.setInterfaceName(UPSTREAM_IFACE);
|
||||||
|
lp.addLinkAddress(new LinkAddress(PUBLIC_ADDR, 32 /* prefix length */));
|
||||||
|
coordinator.addUpstreamIfindexToMap(lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDownstreamAndClientInformationTo(final BpfCoordinator coordinator) {
|
||||||
|
final ClientInfo clientInfo = new ClientInfo(DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC,
|
||||||
|
PRIVATE_ADDR, MAC_A /* client mac */);
|
||||||
|
coordinator.tetherOffloadClientAdd(mIpServer, clientInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Test the IPv4 and IPv6 exist concurrently.
|
||||||
|
// TODO: Test the IPv4 rule delete failed.
|
||||||
|
@Test
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.R)
|
||||||
|
public void testSetDataLimitOnRule4Change() throws Exception {
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
coordinator.startPolling();
|
||||||
|
|
||||||
|
// Needed because tetherOffloadRuleRemove of api31.BpfCoordinatorShimImpl only decreases
|
||||||
|
// the count while the entry is deleted. In the other words, deleteEntry returns true.
|
||||||
|
doReturn(true).when(mBpfDownstream4Map).deleteEntry(any());
|
||||||
|
|
||||||
|
// Needed because BpfCoordinator#addUpstreamIfindexToMap queries interface parameter for
|
||||||
|
// interface index.
|
||||||
|
doReturn(UPSTREAM_IFACE_PARAMS).when(mDeps).getInterfaceParams(UPSTREAM_IFACE);
|
||||||
|
|
||||||
|
coordinator.addUpstreamNameToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE);
|
||||||
|
setUpstreamInformationTo(coordinator);
|
||||||
|
setDownstreamAndClientInformationTo(coordinator);
|
||||||
|
|
||||||
|
// Applying a data limit to the current upstream does not take any immediate action.
|
||||||
|
// The data limit could be only set on an upstream which has rules.
|
||||||
|
final long limit = 12345;
|
||||||
|
final InOrder inOrder = inOrder(mNetd, mBpfUpstream4Map, mBpfDownstream4Map, mBpfLimitMap,
|
||||||
|
mBpfStatsMap);
|
||||||
|
mTetherStatsProvider.onSetLimit(UPSTREAM_IFACE, limit);
|
||||||
|
waitForIdle();
|
||||||
|
verifyNeverTetherOffloadSetInterfaceQuota(inOrder);
|
||||||
|
|
||||||
|
// Build TCP and UDP rules for testing. Note that the values of {TCP, UDP} are the same
|
||||||
|
// because the protocol is not an element of the value. Consider using different address
|
||||||
|
// or port to make them different for better testing.
|
||||||
|
// TODO: Make the values of {TCP, UDP} rules different.
|
||||||
|
final Tether4Key expectedUpstream4KeyTcp = makeUpstream4Key(IPPROTO_TCP);
|
||||||
|
final Tether4Key expectedDownstream4KeyTcp = makeDownstream4Key(IPPROTO_TCP);
|
||||||
|
final Tether4Value expectedUpstream4ValueTcp = makeUpstream4Value();
|
||||||
|
final Tether4Value expectedDownstream4ValueTcp = makeDownstream4Value();
|
||||||
|
|
||||||
|
final Tether4Key expectedUpstream4KeyUdp = makeUpstream4Key(IPPROTO_UDP);
|
||||||
|
final Tether4Key expectedDownstream4KeyUdp = makeDownstream4Key(IPPROTO_UDP);
|
||||||
|
final Tether4Value expectedUpstream4ValueUdp = makeUpstream4Value();
|
||||||
|
final Tether4Value expectedDownstream4ValueUdp = makeDownstream4Value();
|
||||||
|
|
||||||
|
// [1] Adding the first rule on current upstream immediately sends the quota.
|
||||||
|
mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP));
|
||||||
|
verifyTetherOffloadSetInterfaceQuota(inOrder, UPSTREAM_IFINDEX, limit, true /* isInit */);
|
||||||
|
inOrder.verify(mBpfUpstream4Map)
|
||||||
|
.insertEntry(eq(expectedUpstream4KeyTcp), eq(expectedUpstream4ValueTcp));
|
||||||
|
inOrder.verify(mBpfDownstream4Map)
|
||||||
|
.insertEntry(eq(expectedDownstream4KeyTcp), eq(expectedDownstream4ValueTcp));
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
|
||||||
|
// [2] Adding the second rule on current upstream does not send the quota.
|
||||||
|
mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP));
|
||||||
|
verifyNeverTetherOffloadSetInterfaceQuota(inOrder);
|
||||||
|
inOrder.verify(mBpfUpstream4Map)
|
||||||
|
.insertEntry(eq(expectedUpstream4KeyUdp), eq(expectedUpstream4ValueUdp));
|
||||||
|
inOrder.verify(mBpfDownstream4Map)
|
||||||
|
.insertEntry(eq(expectedDownstream4KeyUdp), eq(expectedDownstream4ValueUdp));
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
|
||||||
|
// [3] Removing the second rule on current upstream does not send the quota.
|
||||||
|
mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_UDP));
|
||||||
|
verifyNeverTetherOffloadSetInterfaceQuota(inOrder);
|
||||||
|
inOrder.verify(mBpfUpstream4Map).deleteEntry(eq(expectedUpstream4KeyUdp));
|
||||||
|
inOrder.verify(mBpfDownstream4Map).deleteEntry(eq(expectedDownstream4KeyUdp));
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
|
||||||
|
// [4] Removing the last rule on current upstream immediately sends the cleanup stuff.
|
||||||
|
updateStatsEntryForTetherOffloadGetAndClearStats(
|
||||||
|
buildTestTetherStatsParcel(UPSTREAM_IFINDEX, 0, 0, 0, 0));
|
||||||
|
mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_TCP));
|
||||||
|
inOrder.verify(mBpfUpstream4Map).deleteEntry(eq(expectedUpstream4KeyTcp));
|
||||||
|
inOrder.verify(mBpfDownstream4Map).deleteEntry(eq(expectedDownstream4KeyTcp));
|
||||||
|
verifyTetherOffloadGetAndClearStats(inOrder, UPSTREAM_IFINDEX);
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user