[Refactor] move BPF tests to BpfCoordinatorTest
Preparing for refactor of IpServer and BpfCoordinator. All BPF related
code are planned to migrate from IpServer to BpfCoordinator. This CL
moves BPF related tests from IpServerTest to BpfCoordinatorTest first.
Test: atest TetheringTests
Bug: 294025403
Change-Id: I000f27c463368202dbbe2ace188c8c5146669096
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 98b624b..a7064e8 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -35,15 +35,9 @@
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import static android.net.ip.IpServer.getTetherableIpv6Prefixes;
-import static android.system.OsConstants.ETH_P_IPV6;
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
-import static com.android.net.module.util.netlink.NetlinkConstants.RTM_DELNEIGH;
-import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNEIGH;
-import static com.android.net.module.util.netlink.StructNdMsg.NUD_FAILED;
-import static com.android.net.module.util.netlink.StructNdMsg.NUD_REACHABLE;
-import static com.android.net.module.util.netlink.StructNdMsg.NUD_STALE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -57,7 +51,6 @@
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -66,7 +59,6 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -83,8 +75,6 @@
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.RoutingCoordinatorManager;
-import android.net.TetherOffloadRuleParcel;
-import android.net.TetherStatsParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpEventCallbacks;
@@ -96,40 +86,17 @@
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.build.SdkLevel;
-import com.android.net.module.util.BpfMap;
import com.android.net.module.util.InterfaceParams;
-import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
-import com.android.net.module.util.Struct.S32;
-import com.android.net.module.util.bpf.Tether4Key;
-import com.android.net.module.util.bpf.Tether4Value;
-import com.android.net.module.util.bpf.TetherStatsKey;
-import com.android.net.module.util.bpf.TetherStatsValue;
-import com.android.net.module.util.ip.ConntrackMonitor;
import com.android.net.module.util.ip.IpNeighborMonitor;
-import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent;
-import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEventConsumer;
import com.android.networkstack.tethering.BpfCoordinator;
-import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
-import com.android.networkstack.tethering.BpfCoordinator.Ipv6DownstreamRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
-import com.android.networkstack.tethering.Tether6Value;
-import com.android.networkstack.tethering.TetherDevKey;
-import com.android.networkstack.tethering.TetherDevValue;
-import com.android.networkstack.tethering.TetherDownstream6Key;
-import com.android.networkstack.tethering.TetherLimitKey;
-import com.android.networkstack.tethering.TetherLimitValue;
-import com.android.networkstack.tethering.TetherUpstream6Key;
import com.android.networkstack.tethering.TetheringConfiguration;
import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
@@ -143,18 +110,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.verification.VerificationMode;
import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -219,16 +181,8 @@
new LateSdk<>(SdkLevel.isAtLeastS() ? mock(RoutingCoordinatorManager.class) : null);
@Mock private NetworkStatsManager mStatsManager;
@Mock private TetheringConfiguration mTetherConfig;
- @Mock private ConntrackMonitor mConntrackMonitor;
@Mock private TetheringMetrics mTetheringMetrics;
- @Mock private BpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map;
- @Mock private BpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map;
- @Mock private BpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
- @Mock private BpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map;
- @Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
- @Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
- @Mock private BpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
- @Mock private BpfMap<S32, S32> mBpfErrorMap;
+ @Mock private BpfCoordinator mBpfCoordinator;
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
@@ -238,9 +192,6 @@
ArgumentCaptor.forClass(LinkProperties.class);
private IpServer mIpServer;
private InterfaceConfigurationParcel mInterfaceConfiguration;
- private NeighborEventConsumer mNeighborEventConsumer;
- private BpfCoordinator mBpfCoordinator;
- private BpfCoordinator.Dependencies mBpfDeps;
private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
@@ -262,22 +213,20 @@
mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
}
- ArgumentCaptor<NeighborEventConsumer> neighborCaptor =
- ArgumentCaptor.forClass(NeighborEventConsumer.class);
- doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(),
- neighborCaptor.capture());
+ doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(), any());
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(usingBpfOffload);
when(mTetherConfig.useLegacyDhcpServer()).thenReturn(usingLegacyDhcp);
when(mTetherConfig.getP2pLeasesSubnetPrefixLength()).thenReturn(P2P_SUBNET_PREFIX_LENGTH);
+ when(mBpfCoordinator.isUsingBpfOffload()).thenReturn(usingBpfOffload);
mIpServer = createIpServer(interfaceType);
+ verify(mIpNeighborMonitor).start();
mIpServer.start();
- mNeighborEventConsumer = neighborCaptor.getValue();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
- reset(mNetd, mCallback);
+ reset(mNetd, mCallback, mIpNeighborMonitor);
when(mRaDaemon.start()).thenReturn(true);
}
@@ -294,23 +243,17 @@
initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
if (upstreamIface != null) {
+ InterfaceParams interfaceParams = mDependencies.getInterfaceParams(upstreamIface);
+ assertNotNull("missing upstream interface: " + upstreamIface, interfaceParams);
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(upstreamIface);
lp.setLinkAddresses(upstreamAddresses);
dispatchTetherConnectionChanged(upstreamIface, lp, 0);
- if (usingBpfOffload && !lp.getLinkAddresses().isEmpty()) {
- Set<IpPrefix> upstreamPrefixes = getTetherableIpv6Prefixes(lp.getLinkAddresses());
- InterfaceParams interfaceParams = mDependencies.getInterfaceParams(upstreamIface);
- assertNotNull("missing upstream interface: " + upstreamIface, interfaceParams);
- verify(mBpfCoordinator).updateAllIpv6Rules(
- mIpServer, TEST_IFACE_PARAMS, interfaceParams.index, upstreamPrefixes);
- verifyStartUpstreamIpv6Forwarding(null, interfaceParams.index, upstreamPrefixes);
- } else {
- verifyNoUpstreamIpv6ForwardingChange(null);
- }
+ Set<IpPrefix> upstreamPrefixes = getTetherableIpv6Prefixes(lp.getLinkAddresses());
+ verify(mBpfCoordinator).updateAllIpv6Rules(
+ mIpServer, TEST_IFACE_PARAMS, interfaceParams.index, upstreamPrefixes);
}
- reset(mCallback, mAddressCoordinator);
- resetNetdBpfMapAndCoordinator();
+ reset(mNetd, mBpfCoordinator, mCallback, mAddressCoordinator);
when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(),
anyBoolean())).thenReturn(mTestAddress);
}
@@ -363,80 +306,6 @@
private IpServer createIpServer(final int interfaceType) {
mLooper = new TestLooper();
mHandler = new Handler(mLooper.getLooper());
- mBpfDeps = new BpfCoordinator.Dependencies() {
- @NonNull
- public Handler getHandler() {
- return mHandler;
- }
-
- @NonNull
- public INetd getNetd() {
- return mNetd;
- }
-
- @NonNull
- public NetworkStatsManager getNetworkStatsManager() {
- return mStatsManager;
- }
-
- @NonNull
- public SharedLog getSharedLog() {
- return mSharedLog;
- }
-
- @Nullable
- public TetheringConfiguration getTetherConfig() {
- return mTetherConfig;
- }
-
- @NonNull
- public ConntrackMonitor getConntrackMonitor(
- ConntrackMonitor.ConntrackEventConsumer consumer) {
- return mConntrackMonitor;
- }
-
- @Nullable
- public BpfMap<Tether4Key, Tether4Value> getBpfDownstream4Map() {
- return mBpfDownstream4Map;
- }
-
- @Nullable
- public BpfMap<Tether4Key, Tether4Value> getBpfUpstream4Map() {
- return mBpfUpstream4Map;
- }
-
- @Nullable
- public BpfMap<TetherDownstream6Key, Tether6Value> getBpfDownstream6Map() {
- return mBpfDownstream6Map;
- }
-
- @Nullable
- public BpfMap<TetherUpstream6Key, Tether6Value> getBpfUpstream6Map() {
- return mBpfUpstream6Map;
- }
-
- @Nullable
- public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
- return mBpfStatsMap;
- }
-
- @Nullable
- public BpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() {
- return mBpfLimitMap;
- }
-
- @Nullable
- public BpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
- return mBpfDevMap;
- }
-
- @Nullable
- public BpfMap<S32, S32> getBpfErrorMap() {
- return mBpfErrorMap;
- }
- };
-
- mBpfCoordinator = spy(new BpfCoordinator(mBpfDeps));
return new IpServer(IFACE_NAME, mHandler, interfaceType, mSharedLog, mNetd, mBpfCoordinator,
mRoutingCoordinatorManager, mCallback, mTetherConfig, mAddressCoordinator,
mTetheringMetrics, mDependencies);
@@ -886,346 +755,19 @@
verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
}
- private InetAddress addr(String addr) throws Exception {
- return InetAddresses.parseNumericAddress(addr);
- }
-
- private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
- mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr,
- nudState, mac));
- mLooper.dispatchAll();
- }
-
- private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
- mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr,
- nudState, mac));
- mLooper.dispatchAll();
- }
-
- /**
- * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable
- * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as:
- *
- * private void checkFooCalled(StableParcelable p, ...) {
- * ArgumentCaptor<FooParam> captor = ArgumentCaptor.forClass(FooParam.class);
- * verify(mMock).foo(captor.capture());
- * Foo foo = captor.getValue();
- * assertFooMatchesExpectations(foo);
- * }
- *
- * almost works, but not quite. This is because if the code under test calls foo() twice, the
- * first call to checkFooCalled() matches both the calls, putting both calls into the captor,
- * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito
- * features such as never(), inOrder(), etc.
- *
- * This approach isn't great because if the match fails, the error message is unhelpful
- * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does
- * work.
- *
- * TODO: consider making the error message more readable by adding a method that catching the
- * AssertionFailedError and throwing a new assertion with more details. See
- * NetworkMonitorTest#verifyNetworkTested.
- *
- * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the
- * TooManyActualInvocations problem described above by forcing the caller of the custom assert
- * method to specify all expected invocations in one call. This is useful when the stable
- * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and
- * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here
- * because there is no such object.
- */
- private static class TetherOffloadRuleParcelMatcher implements
- ArgumentMatcher<TetherOffloadRuleParcel> {
- public final int upstreamIfindex;
- public final InetAddress dst;
- public final MacAddress dstMac;
-
- TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
- this.upstreamIfindex = upstreamIfindex;
- this.dst = dst;
- this.dstMac = dstMac;
- }
-
- public boolean matches(TetherOffloadRuleParcel parcel) {
- return upstreamIfindex == parcel.inputInterfaceIndex
- && (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex)
- && Arrays.equals(dst.getAddress(), parcel.destination)
- && (128 == parcel.prefixLength)
- && Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address)
- && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address);
- }
-
- public String toString() {
- return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s",
- upstreamIfindex, dst.getHostAddress(), dstMac);
- }
- }
-
- @NonNull
- private static TetherOffloadRuleParcel matches(
- int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
- return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac));
- }
-
- @NonNull
- private static Ipv6DownstreamRule makeDownstreamRule(int upstreamIfindex,
- @NonNull InetAddress dst, @NonNull MacAddress dstMac) {
- return new Ipv6DownstreamRule(upstreamIfindex, TEST_IFACE_PARAMS.index,
- (Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac);
- }
-
- @NonNull
- private static TetherDownstream6Key makeDownstream6Key(int upstreamIfindex,
- @NonNull MacAddress upstreamMac, @NonNull final InetAddress dst) {
- return new TetherDownstream6Key(upstreamIfindex, upstreamMac, dst.getAddress());
- }
-
- @NonNull
- private static Tether6Value makeDownstream6Value(@NonNull final MacAddress dstMac) {
- return new Tether6Value(TEST_IFACE_PARAMS.index, dstMac,
- TEST_IFACE_PARAMS.macAddr, ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
- }
-
- private static long prefixToLong(IpPrefix prefix) {
- return ByteBuffer.wrap(prefix.getRawAddress()).getLong();
- }
-
- private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
- return verifyWithOrder(inOrder, t, times(1));
- }
-
- private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t, VerificationMode mode) {
- if (inOrder != null) {
- return inOrder.verify(t, mode);
- } else {
- return verify(t, mode);
- }
- }
-
- private void verifyTetherOffloadRuleAdd(@Nullable InOrder inOrder, int upstreamIfindex,
- @NonNull MacAddress upstreamMac, @NonNull final InetAddress dst,
- @NonNull final MacAddress dstMac) throws Exception {
- if (mBpfDeps.isAtLeastS()) {
- verifyWithOrder(inOrder, mBpfDownstream6Map).updateEntry(
- makeDownstream6Key(upstreamIfindex, upstreamMac, dst),
- makeDownstream6Value(dstMac));
- } else {
- verifyWithOrder(inOrder, mNetd).tetherOffloadRuleAdd(matches(upstreamIfindex, dst,
- dstMac));
- }
- }
-
- private void verifyNeverTetherOffloadRuleAdd(int upstreamIfindex,
- @NonNull MacAddress upstreamMac, @NonNull final InetAddress dst,
- @NonNull final MacAddress dstMac) throws Exception {
- if (mBpfDeps.isAtLeastS()) {
- verify(mBpfDownstream6Map, never()).updateEntry(
- makeDownstream6Key(upstreamIfindex, upstreamMac, dst),
- makeDownstream6Value(dstMac));
- } else {
- verify(mNetd, never()).tetherOffloadRuleAdd(matches(upstreamIfindex, dst, dstMac));
- }
- }
-
- private void verifyNeverTetherOffloadRuleAdd() throws Exception {
- if (mBpfDeps.isAtLeastS()) {
- verify(mBpfDownstream6Map, never()).updateEntry(any(), any());
- } else {
- verify(mNetd, never()).tetherOffloadRuleAdd(any());
- }
- }
-
- private void verifyTetherOffloadRuleRemove(@Nullable InOrder inOrder, int upstreamIfindex,
- @NonNull MacAddress upstreamMac, @NonNull final InetAddress dst,
- @NonNull final MacAddress dstMac) throws Exception {
- if (mBpfDeps.isAtLeastS()) {
- verifyWithOrder(inOrder, mBpfDownstream6Map).deleteEntry(makeDownstream6Key(
- upstreamIfindex, upstreamMac, dst));
- } else {
- // |dstMac| is not required for deleting rules. Used bacause tetherOffloadRuleRemove
- // uses a whole rule to be a argument.
- // See system/netd/server/TetherController.cpp/TetherController#removeOffloadRule.
- verifyWithOrder(inOrder, mNetd).tetherOffloadRuleRemove(matches(upstreamIfindex, dst,
- dstMac));
- }
- }
-
- private void verifyNeverTetherOffloadRuleRemove() throws Exception {
- if (mBpfDeps.isAtLeastS()) {
- verify(mBpfDownstream6Map, never()).deleteEntry(any());
- } else {
- verify(mNetd, never()).tetherOffloadRuleRemove(any());
- }
- }
-
- private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int upstreamIfindex,
- @NonNull Set<IpPrefix> upstreamPrefixes) throws Exception {
- if (!mBpfDeps.isAtLeastS()) return;
- ArrayMap<TetherUpstream6Key, Tether6Value> expected = new ArrayMap<>();
- for (IpPrefix upstreamPrefix : upstreamPrefixes) {
- long prefix64 = prefixToLong(upstreamPrefix);
- final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index,
- TEST_IFACE_PARAMS.macAddr, prefix64);
- final Tether6Value value = new Tether6Value(upstreamIfindex,
- MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
- ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
- expected.put(key, value);
- }
- ArgumentCaptor<TetherUpstream6Key> keyCaptor =
- ArgumentCaptor.forClass(TetherUpstream6Key.class);
- ArgumentCaptor<Tether6Value> valueCaptor =
- ArgumentCaptor.forClass(Tether6Value.class);
- verifyWithOrder(inOrder, mBpfUpstream6Map, times(expected.size())).insertEntry(
- keyCaptor.capture(), valueCaptor.capture());
- List<TetherUpstream6Key> keys = keyCaptor.getAllValues();
- List<Tether6Value> values = valueCaptor.getAllValues();
- ArrayMap<TetherUpstream6Key, Tether6Value> captured = new ArrayMap<>();
- for (int i = 0; i < keys.size(); i++) {
- captured.put(keys.get(i), values.get(i));
- }
- assertEquals(expected, captured);
- }
-
- private void verifyStopUpstreamIpv6Forwarding(@Nullable InOrder inOrder,
- @NonNull Set<IpPrefix> upstreamPrefixes) throws Exception {
- if (!mBpfDeps.isAtLeastS()) return;
- Set<TetherUpstream6Key> expected = new ArraySet<>();
- for (IpPrefix upstreamPrefix : upstreamPrefixes) {
- long prefix64 = prefixToLong(upstreamPrefix);
- final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index,
- TEST_IFACE_PARAMS.macAddr, prefix64);
- expected.add(key);
- }
- ArgumentCaptor<TetherUpstream6Key> keyCaptor =
- ArgumentCaptor.forClass(TetherUpstream6Key.class);
- verifyWithOrder(inOrder, mBpfUpstream6Map, times(expected.size())).deleteEntry(
- keyCaptor.capture());
- assertEquals(expected, new ArraySet(keyCaptor.getAllValues()));
- }
-
- private void verifyNoUpstreamIpv6ForwardingChange(@Nullable InOrder inOrder) throws Exception {
- if (!mBpfDeps.isAtLeastS()) return;
- if (inOrder != null) {
- inOrder.verify(mBpfUpstream6Map, never()).deleteEntry(any());
- inOrder.verify(mBpfUpstream6Map, never()).insertEntry(any(), any());
- inOrder.verify(mBpfUpstream6Map, never()).updateEntry(any(), any());
- } else {
- verify(mBpfUpstream6Map, never()).deleteEntry(any());
- verify(mBpfUpstream6Map, never()).insertEntry(any(), any());
- verify(mBpfUpstream6Map, never()).updateEntry(any(), any());
- }
- }
-
- @NonNull
- private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) {
- TetherStatsParcel parcel = new TetherStatsParcel();
- parcel.ifIndex = ifIndex;
- return parcel;
- }
-
- private void resetNetdBpfMapAndCoordinator() throws Exception {
- reset(mNetd, mBpfDownstream6Map, mBpfUpstream6Map, mBpfCoordinator);
- // When the last rule is removed, tetherOffloadGetAndClearStats will log a WTF (and
- // potentially crash the test) if the stats map is empty.
- when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
- when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX))
- .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX));
- when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX2))
- .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX2));
- // When the last rule is removed, tetherOffloadGetAndClearStats will log a WTF (and
- // potentially crash the test) if the stats map is empty.
- final TetherStatsValue allZeros = new TetherStatsValue(0, 0, 0, 0, 0, 0);
- when(mBpfStatsMap.getValue(new TetherStatsKey(UPSTREAM_IFINDEX))).thenReturn(allZeros);
- when(mBpfStatsMap.getValue(new TetherStatsKey(UPSTREAM_IFINDEX2))).thenReturn(allZeros);
- }
-
@Test
- public void addRemoveipv6ForwardingRules() throws Exception {
+ public void ipv6UpstreamInterfaceChanges() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, UPSTREAM_ADDRESSES,
false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
- final int myIfindex = TEST_IFACE_PARAMS.index;
- final int notMyIfindex = myIfindex - 1;
-
- final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1");
- final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2");
- final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1");
- final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234");
- final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
- final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
- final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
-
- resetNetdBpfMapAndCoordinator();
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
-
- // TODO: Perhaps verify the interaction of tetherOffloadSetInterfaceQuota and
- // tetherOffloadGetAndClearStats in netd while the rules are changed.
-
- // Events on other interfaces are ignored.
- recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
-
- // Events on this interface are received and sent to BpfCoordinator.
- recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- verify(mBpfCoordinator).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macA));
- verifyTetherOffloadRuleAdd(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA);
- resetNetdBpfMapAndCoordinator();
-
- recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- verify(mBpfCoordinator).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighB, macB));
- verifyTetherOffloadRuleAdd(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB);
- resetNetdBpfMapAndCoordinator();
-
- // Link-local and multicast neighbors are ignored.
- recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
- recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
-
- // A neighbor that is no longer valid causes the rule to be removed.
- // NUD_FAILED events do not have a MAC address.
- recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
- verify(mBpfCoordinator).removeIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macNull));
- verifyTetherOffloadRuleRemove(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macNull);
- resetNetdBpfMapAndCoordinator();
-
- // A neighbor that is deleted causes the rule to be removed.
- recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
- verify(mBpfCoordinator).removeIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighB, macNull));
- verifyTetherOffloadRuleRemove(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macNull);
- resetNetdBpfMapAndCoordinator();
-
// Upstream interface changes result in updating the rules.
- recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- resetNetdBpfMapAndCoordinator();
-
- InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE2);
lp.setLinkAddresses(UPSTREAM_ADDRESSES);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
verify(mBpfCoordinator).updateAllIpv6Rules(
mIpServer, TEST_IFACE_PARAMS, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES);
- verifyTetherOffloadRuleRemove(inOrder,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA);
- verifyTetherOffloadRuleRemove(inOrder,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB);
- verifyStopUpstreamIpv6Forwarding(inOrder, UPSTREAM_PREFIXES);
- verifyStartUpstreamIpv6Forwarding(inOrder, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES);
- verifyTetherOffloadRuleAdd(inOrder,
- UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighA, macA);
- verifyTetherOffloadRuleAdd(inOrder,
- UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighB, macB);
- verifyNoUpstreamIpv6ForwardingChange(inOrder);
- resetNetdBpfMapAndCoordinator();
+ reset(mBpfCoordinator);
// Upstream link addresses change result in updating the rules.
LinkProperties lp2 = new LinkProperties();
@@ -1234,17 +776,7 @@
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, -1);
verify(mBpfCoordinator).updateAllIpv6Rules(
mIpServer, TEST_IFACE_PARAMS, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES2);
- verifyTetherOffloadRuleRemove(inOrder,
- UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighA, macA);
- verifyTetherOffloadRuleRemove(inOrder,
- UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighB, macB);
- verifyStopUpstreamIpv6Forwarding(inOrder, UPSTREAM_PREFIXES);
- verifyStartUpstreamIpv6Forwarding(inOrder, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES2);
- verifyTetherOffloadRuleAdd(inOrder,
- UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighA, macA);
- verifyTetherOffloadRuleAdd(inOrder,
- UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighB, macB);
- resetNetdBpfMapAndCoordinator();
+ reset(mBpfCoordinator);
// When the upstream is lost, rules are removed.
dispatchTetherConnectionChanged(null, null, 0);
@@ -1254,156 +786,51 @@
// See dispatchTetherConnectionChanged.
verify(mBpfCoordinator, times(2)).updateAllIpv6Rules(
mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES);
- verifyStopUpstreamIpv6Forwarding(inOrder, UPSTREAM_PREFIXES2);
- verifyTetherOffloadRuleRemove(null,
- UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighA, macA);
- verifyTetherOffloadRuleRemove(null,
- UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighB, macB);
- // Upstream lost doesn't clear the downstream rules from BpfCoordinator.
- // Do that here.
- recvDelNeigh(myIfindex, neighA, NUD_STALE, macA);
- recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
- verify(mBpfCoordinator).removeIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(NO_UPSTREAM, neighA, macNull));
- verify(mBpfCoordinator).removeIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(NO_UPSTREAM, neighB, macNull));
- resetNetdBpfMapAndCoordinator();
+ reset(mBpfCoordinator);
- // If the upstream is IPv4-only, no IPv6 rules are added to BPF map.
+ // If the upstream is IPv4-only, no rules are added.
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- resetNetdBpfMapAndCoordinator();
- recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- verifyNoUpstreamIpv6ForwardingChange(null);
- // Downstream rules are only added to BpfCoordinator but not BPF map.
- verify(mBpfCoordinator).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(NO_UPSTREAM, neighA, macA));
- verifyNeverTetherOffloadRuleAdd();
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
+ verify(mBpfCoordinator, never()).updateAllIpv6Rules(
+ mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES);
+ reset(mBpfCoordinator);
- // Rules can be added again once upstream IPv6 connectivity is available. The existing rules
- // with an upstream of NO_UPSTREAM are reapplied.
+ // Rules are added again once upstream IPv6 connectivity is available.
lp.setInterfaceName(UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
- verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES);
- verify(mBpfCoordinator).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macA));
- verifyTetherOffloadRuleAdd(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA);
- recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- verify(mBpfCoordinator).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighB, macB));
- verifyTetherOffloadRuleAdd(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB);
+ verify(mBpfCoordinator).updateAllIpv6Rules(
+ mIpServer, TEST_IFACE_PARAMS, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES);
+ reset(mBpfCoordinator);
// If upstream IPv6 connectivity is lost, rules are removed.
- resetNetdBpfMapAndCoordinator();
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
verify(mBpfCoordinator).updateAllIpv6Rules(
mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES);
- verifyTetherOffloadRuleRemove(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB);
- verifyStopUpstreamIpv6Forwarding(null, UPSTREAM_PREFIXES);
+ reset(mBpfCoordinator);
- // When upstream IPv6 connectivity comes back, upstream rules are added and downstream rules
- // are reapplied.
+ // When upstream IPv6 connectivity comes back, rules are added.
lp.setInterfaceName(UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
- verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES);
- verify(mBpfCoordinator).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macA));
- verifyTetherOffloadRuleAdd(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA);
- verify(mBpfCoordinator).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighB, macB));
- verifyTetherOffloadRuleAdd(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB);
- resetNetdBpfMapAndCoordinator();
+ verify(mBpfCoordinator).updateAllIpv6Rules(
+ mIpServer, TEST_IFACE_PARAMS, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES);
+ reset(mBpfCoordinator);
// When the downstream interface goes down, rules are removed.
mIpServer.stop();
mLooper.dispatchAll();
verify(mBpfCoordinator).clearAllIpv6Rules(mIpServer);
- verifyStopUpstreamIpv6Forwarding(null, UPSTREAM_PREFIXES);
- verifyTetherOffloadRuleRemove(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA);
- verifyTetherOffloadRuleRemove(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB);
- verify(mIpNeighborMonitor).stop();
- resetNetdBpfMapAndCoordinator();
- }
-
- @Test
- public void enableDisableUsingBpfOffload() throws Exception {
- final int myIfindex = TEST_IFACE_PARAMS.index;
- final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
- final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
- final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
-
- // Expect that rules can be only added/removed when the BPF offload config is enabled.
- // Note that the BPF offload disabled case is not a realistic test case. Because IP
- // neighbor monitor doesn't start if BPF offload is disabled, there should have no
- // neighbor event listening. This is used for testing the protection check just in case.
- // TODO: Perhaps remove the BPF offload disabled case test once this check isn't needed
- // anymore.
-
- // [1] Enable BPF offload.
- // A neighbor that is added or deleted causes the rule to be added or removed.
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, UPSTREAM_ADDRESSES,
- false /* usingLegacyDhcp */, true /* usingBpfOffload */);
- resetNetdBpfMapAndCoordinator();
-
- recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
- verify(mBpfCoordinator).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neigh, macA));
- verifyTetherOffloadRuleAdd(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neigh, macA);
- resetNetdBpfMapAndCoordinator();
-
- recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
- verify(mBpfCoordinator).removeIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neigh, macNull));
- verifyTetherOffloadRuleRemove(null,
- UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neigh, macNull);
- resetNetdBpfMapAndCoordinator();
-
- // Upstream IPv6 connectivity change causes upstream rules change.
- LinkProperties lp2 = new LinkProperties();
- lp2.setInterfaceName(UPSTREAM_IFACE2);
- lp2.setLinkAddresses(UPSTREAM_ADDRESSES2);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0);
verify(mBpfCoordinator).updateAllIpv6Rules(
- mIpServer, TEST_IFACE_PARAMS, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES2);
- verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES2);
- resetNetdBpfMapAndCoordinator();
-
- // [2] Disable BPF offload.
- // A neighbor that is added or deleted doesn’t cause the rule to be added or removed.
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, UPSTREAM_ADDRESSES,
- false /* usingLegacyDhcp */, false /* usingBpfOffload */);
- resetNetdBpfMapAndCoordinator();
-
- recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
- verifyNeverTetherOffloadRuleAdd();
- resetNetdBpfMapAndCoordinator();
-
- recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
- verifyNeverTetherOffloadRuleRemove();
- resetNetdBpfMapAndCoordinator();
-
- // Upstream IPv6 connectivity change doesn't cause the rule to be added or removed.
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0);
- verifyNoUpstreamIpv6ForwardingChange(null);
- verifyNeverTetherOffloadRuleRemove();
- resetNetdBpfMapAndCoordinator();
+ mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES);
+ reset(mBpfCoordinator);
}
@Test
- public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception {
+ public void stopNeighborMonitoringWhenInterfaceDown() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, UPSTREAM_ADDRESSES,
- false /* usingLegacyDhcp */, false /* usingBpfOffload */);
+ false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
- // IP neighbor monitor doesn't start if BPF offload is disabled.
- verify(mIpNeighborMonitor, never()).start();
+ mIpServer.stop();
+ mLooper.dispatchAll();
+ verify(mIpNeighborMonitor).stop();
}
private LinkProperties buildIpv6OnlyLinkProperties(final String iface) {
@@ -1659,75 +1086,4 @@
public void testDadProxyUpdates_EnabledAfterR() throws Exception {
checkDadProxyEnabled(true);
}
-
- @Test
- public void testSkipVirtualNetworkInBpf() throws Exception {
- initTetheredStateMachine(TETHERING_BLUETOOTH, null);
- final LinkProperties v6Only = new LinkProperties();
- v6Only.setInterfaceName(IPSEC_IFACE);
- dispatchTetherConnectionChanged(IPSEC_IFACE, v6Only, 0);
-
- verify(mBpfCoordinator).maybeAttachProgram(IFACE_NAME, IPSEC_IFACE);
- verify(mNetd).tetherAddForward(IFACE_NAME, IPSEC_IFACE);
- verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, IPSEC_IFACE);
-
- final int myIfindex = TEST_IFACE_PARAMS.index;
- final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
- final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a");
- recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, mac);
- verify(mBpfCoordinator, never()).addIpv6DownstreamRule(
- mIpServer, makeDownstreamRule(IPSEC_IFINDEX, neigh, mac));
- }
-
- // TODO: move to BpfCoordinatorTest once IpNeighborMonitor is migrated to BpfCoordinator.
- @Test
- public void addRemoveTetherClient() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, UPSTREAM_ADDRESSES,
- false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
-
- final int myIfindex = TEST_IFACE_PARAMS.index;
- final int notMyIfindex = myIfindex - 1;
-
- final InetAddress neighA = InetAddresses.parseNumericAddress("192.168.80.1");
- final InetAddress neighB = InetAddresses.parseNumericAddress("192.168.80.2");
- final InetAddress neighLL = InetAddresses.parseNumericAddress("169.254.0.1");
- final InetAddress neighMC = InetAddresses.parseNumericAddress("224.0.0.1");
- final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
- final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
- final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
-
- // Events on other interfaces are ignored.
- recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator);
-
- // Events on this interface are received and sent to BpfCoordinator.
- recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- verify(mBpfCoordinator).tetherOffloadClientAdd(mIpServer, new ClientInfo(myIfindex,
- TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighA, macA));
- clearInvocations(mBpfCoordinator);
-
- recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- verify(mBpfCoordinator).tetherOffloadClientAdd(mIpServer, new ClientInfo(myIfindex,
- TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighB, macB));
- clearInvocations(mBpfCoordinator);
-
- // Link-local and multicast neighbors are ignored.
- recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator);
- recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator);
- clearInvocations(mBpfCoordinator);
-
- // A neighbor that is no longer valid causes the client to be removed.
- // NUD_FAILED events do not have a MAC address.
- recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
- verify(mBpfCoordinator).tetherOffloadClientRemove(mIpServer, new ClientInfo(myIfindex,
- TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighA, macNull));
- clearInvocations(mBpfCoordinator);
-
- // A neighbor that is deleted causes the client to be removed.
- recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
- verify(mBpfCoordinator).tetherOffloadClientRemove(mIpServer, new ClientInfo(myIfindex,
- TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighB, macNull));
- }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 7fbb670..6c1721e 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -25,6 +25,8 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStats.UID_TETHERING;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.ip.IpServer.STATE_TETHERED;
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;
@@ -43,6 +45,11 @@
import static com.android.net.module.util.netlink.ConntrackMessage.TupleProto;
import static com.android.net.module.util.netlink.NetlinkConstants.IPCTNL_MSG_CT_DELETE;
import static com.android.net.module.util.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW;
+import static com.android.net.module.util.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNEIGH;
+import static com.android.net.module.util.netlink.StructNdMsg.NUD_FAILED;
+import static com.android.net.module.util.netlink.StructNdMsg.NUD_REACHABLE;
+import static com.android.net.module.util.netlink.StructNdMsg.NUD_STALE;
import static com.android.networkstack.tethering.BpfCoordinator.CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS;
import static com.android.networkstack.tethering.BpfCoordinator.INVALID_MTU;
import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED;
@@ -71,11 +78,15 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.usage.NetworkStatsManager;
@@ -88,13 +99,16 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
+import android.net.RoutingCoordinatorManager;
import android.net.TetherOffloadRuleParcel;
import android.net.TetherStatsParcel;
import android.net.ip.IpServer;
+import android.net.ip.RouterAdvertisementDaemon;
import android.os.Build;
import android.os.Handler;
import android.os.test.TestLooper;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -104,10 +118,12 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetworkStackConstants;
+import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.Struct.S32;
import com.android.net.module.util.bpf.Tether4Key;
@@ -116,6 +132,9 @@
import com.android.net.module.util.bpf.TetherStatsValue;
import com.android.net.module.util.ip.ConntrackMonitor;
import com.android.net.module.util.ip.ConntrackMonitor.ConntrackEventConsumer;
+import com.android.net.module.util.ip.IpNeighborMonitor;
+import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent;
+import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEventConsumer;
import com.android.net.module.util.netlink.ConntrackMessage;
import com.android.net.module.util.netlink.NetlinkConstants;
import com.android.net.module.util.netlink.NetlinkUtils;
@@ -123,6 +142,8 @@
import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6DownstreamRule;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6UpstreamRule;
+import com.android.networkstack.tethering.metrics.TetheringMetrics;
+import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -172,19 +193,27 @@
private static final int UPSTREAM_IFINDEX2 = 1003;
private static final int DOWNSTREAM_IFINDEX = 2001;
private static final int DOWNSTREAM_IFINDEX2 = 2002;
+ private static final int IPSEC_IFINDEX = 103;
private static final String UPSTREAM_IFACE = "rmnet0";
private static final String UPSTREAM_XLAT_IFACE = "v4-rmnet0";
private static final String UPSTREAM_IFACE2 = "wlan0";
private static final String DOWNSTREAM_IFACE = "downstream1";
private static final String DOWNSTREAM_IFACE2 = "downstream2";
+ private static final String IPSEC_IFACE = "ipsec0";
private static final MacAddress DOWNSTREAM_MAC = MacAddress.fromString("12:34:56:78:90:ab");
private static final MacAddress DOWNSTREAM_MAC2 = MacAddress.fromString("ab:90:78:56:34:12");
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_NULL = MacAddress.fromString("00:00:00:00:00:00");
+ private static final LinkAddress UPSTREAM_ADDRESS = new LinkAddress("2001:db8:0:1234::168/64");
+ private static final LinkAddress UPSTREAM_ADDRESS2 = new LinkAddress("2001:db8:0:abcd::168/64");
+ private static final Set<LinkAddress> UPSTREAM_ADDRESSES = Set.of(UPSTREAM_ADDRESS);
+ private static final Set<LinkAddress> UPSTREAM_ADDRESSES2 =
+ Set.of(UPSTREAM_ADDRESS, UPSTREAM_ADDRESS2);
private static final IpPrefix UPSTREAM_PREFIX = new IpPrefix("2001:db8:0:1234::/64");
private static final IpPrefix UPSTREAM_PREFIX2 = new IpPrefix("2001:db8:0:abcd::/64");
private static final Set<IpPrefix> UPSTREAM_PREFIXES = Set.of(UPSTREAM_PREFIX);
@@ -196,6 +225,8 @@
InetAddresses.parseNumericAddress("2001:db8:0:1234::1");
private static final InetAddress NEIGH_B =
InetAddresses.parseNumericAddress("2001:db8:0:1234::2");
+ private static final InetAddress NEIGH_LL = InetAddresses.parseNumericAddress("fe80::1");
+ private static final InetAddress NEIGH_MC = InetAddresses.parseNumericAddress("ff02::1234");
private static final Inet4Address REMOTE_ADDR =
(Inet4Address) InetAddresses.parseNumericAddress("140.112.8.116");
@@ -235,6 +266,9 @@
private static final InterfaceParams DOWNSTREAM_IFACE_PARAMS2 = new InterfaceParams(
DOWNSTREAM_IFACE2, DOWNSTREAM_IFINDEX2, DOWNSTREAM_MAC2,
NetworkStackConstants.ETHER_MTU);
+ private static final InterfaceParams IPSEC_IFACE_PARAMS = new InterfaceParams(
+ IPSEC_IFACE, IPSEC_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS,
+ NetworkStackConstants.ETHER_MTU);
private static final Map<Integer, UpstreamInformation> UPSTREAM_INFORMATIONS = Map.of(
UPSTREAM_IFINDEX, new UpstreamInformation(UPSTREAM_IFACE_PARAMS,
@@ -414,6 +448,14 @@
@Mock private IpServer mIpServer2;
@Mock private TetheringConfiguration mTetherConfig;
@Mock private ConntrackMonitor mConntrackMonitor;
+ @Mock private IpNeighborMonitor mIpNeighborMonitor;
+ @Mock private RouterAdvertisementDaemon mRaDaemon;
+ @Mock private IpServer.Dependencies mIpServerDeps;
+ @Mock private IpServer.Callback mIpServerCallback;
+ @Mock private PrivateAddressCoordinator mAddressCoordinator;
+ private final LateSdk<RoutingCoordinatorManager> mRoutingCoordinatorManager =
+ new LateSdk<>(SdkLevel.isAtLeastS() ? mock(RoutingCoordinatorManager.class) : null);
+ @Mock private TetheringMetrics mTetheringMetrics;
// Late init since methods must be called by the thread that created this object.
private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
@@ -422,6 +464,7 @@
// 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 NeighborEventConsumer mNeighborEventConsumer;
private HashMap<IpServer, HashMap<Inet4Address, ClientInfo>> mTetherClients;
private long mElapsedRealtimeNanos = 0;
@@ -429,6 +472,7 @@
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class);
private final TestLooper mTestLooper = new TestLooper();
+ private final Handler mHandler = new Handler(mTestLooper.getLooper());
private final IBpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map =
spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
private final IBpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map =
@@ -449,7 +493,7 @@
spy(new BpfCoordinator.Dependencies() {
@NonNull
public Handler getHandler() {
- return new Handler(mTestLooper.getLooper());
+ return mHandler;
}
@NonNull
@@ -529,6 +573,24 @@
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);
+
+ // Simulate the behavior of RoutingCoordinator
+ if (null != mRoutingCoordinatorManager.value) {
+ doAnswer(it -> {
+ final String fromIface = (String) it.getArguments()[0];
+ final String toIface = (String) it.getArguments()[1];
+ mNetd.tetherAddForward(fromIface, toIface);
+ mNetd.ipfwdAddInterfaceForward(fromIface, toIface);
+ return null;
+ }).when(mRoutingCoordinatorManager.value).addInterfaceForward(any(), any());
+ doAnswer(it -> {
+ final String fromIface = (String) it.getArguments()[0];
+ final String toIface = (String) it.getArguments()[1];
+ mNetd.ipfwdRemoveInterfaceForward(fromIface, toIface);
+ mNetd.tetherRemoveForward(fromIface, toIface);
+ return null;
+ }).when(mRoutingCoordinatorManager.value).removeInterfaceForward(any(), any());
+ }
}
private void waitForIdle() {
@@ -542,7 +604,68 @@
}
@NonNull
+ private IpServer makeAndStartIpServer(String interfaceName, BpfCoordinator bpfCoordinator)
+ throws Exception {
+ final LinkAddress testAddress = new LinkAddress("192.168.42.5/24");
+ when(mIpServerDeps.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
+ when(mIpServerDeps.getInterfaceParams(DOWNSTREAM_IFACE)).thenReturn(
+ DOWNSTREAM_IFACE_PARAMS);
+ when(mIpServerDeps.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS);
+ when(mIpServerDeps.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2);
+ when(mIpServerDeps.getInterfaceParams(IPSEC_IFACE)).thenReturn(IPSEC_IFACE_PARAMS);
+ when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(),
+ anyBoolean())).thenReturn(testAddress);
+ when(mRaDaemon.start()).thenReturn(true);
+ ArgumentCaptor<NeighborEventConsumer> neighborEventCaptor =
+ ArgumentCaptor.forClass(NeighborEventConsumer.class);
+ doReturn(mIpNeighborMonitor).when(mIpServerDeps).getIpNeighborMonitor(any(), any(),
+ neighborEventCaptor.capture());
+ final IpServer ipServer = new IpServer(
+ interfaceName, mHandler, TETHERING_WIFI, new SharedLog("test"), mNetd,
+ bpfCoordinator, mRoutingCoordinatorManager, mIpServerCallback, mTetherConfig,
+ mAddressCoordinator, mTetheringMetrics, mIpServerDeps);
+ ipServer.start();
+ ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ mTestLooper.dispatchAll();
+
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ lp.setLinkAddresses(UPSTREAM_ADDRESSES);
+ dispatchTetherConnectionChanged(ipServer, UPSTREAM_IFACE, lp, 0);
+
+ mNeighborEventConsumer = neighborEventCaptor.getValue();
+ return ipServer;
+ }
+
+ private void dispatchTetherConnectionChanged(IpServer ipServer, String upstreamIface,
+ LinkProperties v6lp, int ttlAdjustment) {
+ dispatchTetherConnectionChanged(ipServer, upstreamIface);
+ ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp);
+ mTestLooper.dispatchAll();
+ }
+
+ private void dispatchTetherConnectionChanged(IpServer ipServer, String upstreamIface) {
+ final InterfaceSet ifs = (upstreamIface != null) ? new InterfaceSet(upstreamIface) : null;
+ ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifs);
+ mTestLooper.dispatchAll();
+ }
+
+ private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
+ mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr,
+ nudState, mac));
+ mTestLooper.dispatchAll();
+ }
+
+ private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
+ mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr,
+ nudState, mac));
+ mTestLooper.dispatchAll();
+ }
+
+ @NonNull
private BpfCoordinator makeBpfCoordinator() throws Exception {
+ // mStatsManager will be invoked twice if BpfCoordinator is created the second time.
+ clearInvocations(mStatsManager);
final BpfCoordinator coordinator = new BpfCoordinator(mDeps);
mConsumer = coordinator.getBpfConntrackEventConsumerForTesting();
@@ -666,6 +789,51 @@
}
}
+ private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int upstreamIfindex,
+ @NonNull Set<IpPrefix> upstreamPrefixes) throws Exception {
+ if (!mDeps.isAtLeastS()) return;
+ ArrayMap<TetherUpstream6Key, Tether6Value> expected = new ArrayMap<>();
+ for (IpPrefix upstreamPrefix : upstreamPrefixes) {
+ long prefix64 = prefixToLong(upstreamPrefix);
+ final TetherUpstream6Key key = new TetherUpstream6Key(DOWNSTREAM_IFACE_PARAMS.index,
+ DOWNSTREAM_IFACE_PARAMS.macAddr, prefix64);
+ final Tether6Value value = new Tether6Value(upstreamIfindex,
+ MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS, ETH_P_IPV6,
+ NetworkStackConstants.ETHER_MTU);
+ expected.put(key, value);
+ }
+ ArgumentCaptor<TetherUpstream6Key> keyCaptor =
+ ArgumentCaptor.forClass(TetherUpstream6Key.class);
+ ArgumentCaptor<Tether6Value> valueCaptor =
+ ArgumentCaptor.forClass(Tether6Value.class);
+ verifyWithOrder(inOrder, mBpfUpstream6Map, times(expected.size())).insertEntry(
+ keyCaptor.capture(), valueCaptor.capture());
+ List<TetherUpstream6Key> keys = keyCaptor.getAllValues();
+ List<Tether6Value> values = valueCaptor.getAllValues();
+ ArrayMap<TetherUpstream6Key, Tether6Value> captured = new ArrayMap<>();
+ for (int i = 0; i < keys.size(); i++) {
+ captured.put(keys.get(i), values.get(i));
+ }
+ assertEquals(expected, captured);
+ }
+
+ private void verifyStopUpstreamIpv6Forwarding(@Nullable InOrder inOrder,
+ @NonNull Set<IpPrefix> upstreamPrefixes) throws Exception {
+ if (!mDeps.isAtLeastS()) return;
+ Set<TetherUpstream6Key> expected = new ArraySet<>();
+ for (IpPrefix upstreamPrefix : upstreamPrefixes) {
+ long prefix64 = prefixToLong(upstreamPrefix);
+ final TetherUpstream6Key key = new TetherUpstream6Key(DOWNSTREAM_IFACE_PARAMS.index,
+ DOWNSTREAM_IFACE_PARAMS.macAddr, prefix64);
+ expected.add(key);
+ }
+ ArgumentCaptor<TetherUpstream6Key> keyCaptor =
+ ArgumentCaptor.forClass(TetherUpstream6Key.class);
+ verifyWithOrder(inOrder, mBpfUpstream6Map, times(expected.size())).deleteEntry(
+ keyCaptor.capture());
+ assertEquals(expected, new ArraySet(keyCaptor.getAllValues()));
+ }
+
private void verifyNoUpstreamIpv6ForwardingChange(@Nullable InOrder inOrder) throws Exception {
if (!mDeps.isAtLeastS()) return;
if (inOrder != null) {
@@ -708,6 +876,10 @@
assertEquals(expected, captured);
}
+ private void verifyAddDownstreamRule(@NonNull Ipv6DownstreamRule rule) throws Exception {
+ verifyAddDownstreamRule(null, rule);
+ }
+
private void verifyAddDownstreamRule(@Nullable InOrder inOrder,
@NonNull Ipv6DownstreamRule rule) throws Exception {
if (mDeps.isAtLeastS()) {
@@ -752,6 +924,11 @@
assertSameElements(expected, keyCaptor.getAllValues());
}
+ private void verifyRemoveDownstreamRule(@NonNull final Ipv6DownstreamRule rule)
+ throws Exception {
+ verifyRemoveDownstreamRule(null, rule);
+ }
+
private void verifyRemoveDownstreamRule(@Nullable InOrder inOrder,
@NonNull final Ipv6DownstreamRule rule) throws Exception {
if (mDeps.isAtLeastS()) {
@@ -1016,8 +1193,37 @@
mTetherStatsProviderCb.assertNoCallback();
}
- // The custom ArgumentMatcher simply comes from IpServerTest.
- // TODO: move both of them into a common utility class for reusing the code.
+ /**
+ * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable
+ * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as:
+ *
+ * private void checkFooCalled(StableParcelable p, ...) {
+ * ArgumentCaptor<@FooParam> captor = ArgumentCaptor.forClass(FooParam.class);
+ * verify(mMock).foo(captor.capture());
+ * Foo foo = captor.getValue();
+ * assertFooMatchesExpectations(foo);
+ * }
+ *
+ * almost works, but not quite. This is because if the code under test calls foo() twice, the
+ * first call to checkFooCalled() matches both the calls, putting both calls into the captor,
+ * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito
+ * features such as never(), inOrder(), etc.
+ *
+ * This approach isn't great because if the match fails, the error message is unhelpful
+ * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does
+ * work.
+ *
+ * TODO: consider making the error message more readable by adding a method that catching the
+ * AssertionFailedError and throwing a new assertion with more details. See
+ * NetworkMonitorTest#verifyNetworkTested.
+ *
+ * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the
+ * TooManyActualInvocations problem described above by forcing the caller of the custom assert
+ * method to specify all expected invocations in one call. This is useful when the stable
+ * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and
+ * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here
+ * because there is no such object.
+ */
private static class TetherOffloadRuleParcelMatcher implements
ArgumentMatcher<TetherOffloadRuleParcel> {
public final int upstreamIfindex;
@@ -2064,8 +2270,7 @@
}
private static long prefixToLong(IpPrefix prefix) {
- byte[] prefixBytes = Arrays.copyOf(prefix.getRawAddress(), 8);
- return ByteBuffer.wrap(prefixBytes).getLong();
+ return ByteBuffer.wrap(prefix.getRawAddress()).getLong();
}
void checkRule4ExistInUpstreamDownstreamMap() throws Exception {
@@ -2135,7 +2340,7 @@
assertNull(mTetherClients.get(mIpServer2));
}
- private void asseertClientInfoExist(@NonNull IpServer ipServer,
+ private void assertClientInfoExists(@NonNull IpServer ipServer,
@NonNull ClientInfo clientInfo) {
HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
assertNotNull(clients);
@@ -2144,16 +2349,16 @@
// Although either ClientInfo for a given downstream (IpServer) is not found or a given
// client address is not found on a given downstream can be treated "ClientInfo not
- // exist", we still want to know the real reason exactly. For example, we don't the
+ // exist", we still want to know the real reason exactly. For example, we don't know the
// exact reason in the following:
- // assertNull(clients == null ? clients : clients.get(clientInfo.clientAddress));
+ // assertNull(clients == null ? clients : clients.get(clientAddress));
// This helper only verifies the case that the downstream still has at least one client.
// In other words, ClientInfo for a given IpServer has not been removed yet.
- private void asseertClientInfoNotExist(@NonNull IpServer ipServer,
- @NonNull ClientInfo clientInfo) {
+ private void assertClientInfoDoesNotExist(@NonNull IpServer ipServer,
+ @NonNull Inet4Address clientAddress) {
HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
assertNotNull(clients);
- assertNull(clients.get(clientInfo.clientAddress));
+ assertNull(clients.get(clientAddress));
}
@Test
@@ -2189,8 +2394,8 @@
checkRule4NotExistInUpstreamDownstreamMap();
// Client information should be not deleted.
- asseertClientInfoExist(mIpServer, CLIENT_INFO_A);
- asseertClientInfoExist(mIpServer2, CLIENT_INFO_B);
+ assertClientInfoExists(mIpServer, CLIENT_INFO_A);
+ assertClientInfoExists(mIpServer2, CLIENT_INFO_B);
}
@Test
@@ -2205,8 +2410,8 @@
PRIVATE_ADDR2, MAC_B);
coordinator.tetherOffloadClientAdd(mIpServer, clientA);
coordinator.tetherOffloadClientAdd(mIpServer, clientB);
- asseertClientInfoExist(mIpServer, clientA);
- asseertClientInfoExist(mIpServer, clientB);
+ assertClientInfoExists(mIpServer, clientA);
+ assertClientInfoExists(mIpServer, clientB);
// Add the rules for client A and client B.
final Tether4Key upstream4KeyA = makeUpstream4Key(
@@ -2230,8 +2435,8 @@
// [2] Remove client information A. Only the rules on client A should be removed and
// the rules on client B should exist.
coordinator.tetherOffloadClientRemove(mIpServer, clientA);
- asseertClientInfoNotExist(mIpServer, clientA);
- asseertClientInfoExist(mIpServer, clientB);
+ assertClientInfoDoesNotExist(mIpServer, clientA.clientAddress);
+ assertClientInfoExists(mIpServer, clientB);
assertNull(mBpfUpstream4Map.getValue(upstream4KeyA));
assertNull(mBpfDownstream4Map.getValue(downstream4KeyA));
assertEquals(upstream4ValueB, mBpfUpstream4Map.getValue(upstream4KeyB));
@@ -2239,9 +2444,9 @@
// [3] Remove client information B. The rules on client B should be removed.
// Exactly, ClientInfo for a given IpServer is removed because the last client B
- // has been removed from the downstream. Can't use the helper #asseertClientInfoExist
+ // has been removed from the downstream. Can't use the helper #assertClientInfoExists
// to check because the container ClientInfo for a given downstream has been removed.
- // See #asseertClientInfoExist.
+ // See #assertClientInfoExists.
coordinator.tetherOffloadClientRemove(mIpServer, clientB);
assertNull(mTetherClients.get(mIpServer));
assertNull(mBpfUpstream4Map.getValue(upstream4KeyB));
@@ -2581,4 +2786,296 @@
public void testUpdateUpstreamNetworkState() throws Exception {
verifyUpdateUpstreamNetworkState();
}
+
+ @NonNull
+ private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) {
+ TetherStatsParcel parcel = new TetherStatsParcel();
+ parcel.ifIndex = ifIndex;
+ return parcel;
+ }
+
+ private void resetNetdAndBpfMaps() throws Exception {
+ reset(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
+ // When the last rule is removed, tetherOffloadGetAndClearStats will log a WTF (and
+ // potentially crash the test) if the stats map is empty.
+ when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
+ when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX))
+ .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX));
+ when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX2))
+ .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX2));
+ // When the last rule is removed, tetherOffloadGetAndClearStats will log a WTF (and
+ // potentially crash the test) if the stats map is empty.
+ final TetherStatsValue allZeros = new TetherStatsValue(0, 0, 0, 0, 0, 0);
+ when(mBpfStatsMap.getValue(new TetherStatsKey(UPSTREAM_IFINDEX))).thenReturn(allZeros);
+ when(mBpfStatsMap.getValue(new TetherStatsKey(UPSTREAM_IFINDEX2))).thenReturn(allZeros);
+ }
+
+ @Test
+ public void addRemoveIpv6ForwardingRules() throws Exception {
+ final int myIfindex = DOWNSTREAM_IFINDEX;
+ final int notMyIfindex = myIfindex - 1;
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ final IpServer ipServer = makeAndStartIpServer(DOWNSTREAM_IFACE, coordinator);
+
+ resetNetdAndBpfMaps();
+ verifyNoMoreInteractions(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
+
+ // TODO: Perhaps verify the interaction of tetherOffloadSetInterfaceQuota and
+ // tetherOffloadGetAndClearStats in netd while the rules are changed.
+
+ // Events on other interfaces are ignored.
+ recvNewNeigh(notMyIfindex, NEIGH_A, NUD_REACHABLE, MAC_A);
+ verifyNoMoreInteractions(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
+
+ // Events on this interface are received and sent to BpfCoordinator.
+ recvNewNeigh(myIfindex, NEIGH_A, NUD_REACHABLE, MAC_A);
+ final Ipv6DownstreamRule ruleA = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A);
+ verifyAddDownstreamRule(ruleA);
+ resetNetdAndBpfMaps();
+
+ recvNewNeigh(myIfindex, NEIGH_B, NUD_REACHABLE, MAC_B);
+ final Ipv6DownstreamRule ruleB = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_B, MAC_B);
+ verifyAddDownstreamRule(ruleB);
+ resetNetdAndBpfMaps();
+
+ // Link-local and multicast neighbors are ignored.
+ recvNewNeigh(myIfindex, NEIGH_LL, NUD_REACHABLE, MAC_A);
+ verifyNoMoreInteractions(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
+ recvNewNeigh(myIfindex, NEIGH_MC, NUD_REACHABLE, MAC_A);
+ verifyNoMoreInteractions(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
+
+ // A neighbor that is no longer valid causes the rule to be removed.
+ // NUD_FAILED events do not have a MAC address.
+ recvNewNeigh(myIfindex, NEIGH_A, NUD_FAILED, null);
+ final Ipv6DownstreamRule ruleANull = buildTestDownstreamRule(
+ UPSTREAM_IFINDEX, NEIGH_A, MAC_NULL);
+ verifyRemoveDownstreamRule(ruleANull);
+ resetNetdAndBpfMaps();
+
+ // A neighbor that is deleted causes the rule to be removed.
+ recvDelNeigh(myIfindex, NEIGH_B, NUD_STALE, MAC_B);
+ final Ipv6DownstreamRule ruleBNull = buildTestDownstreamRule(
+ UPSTREAM_IFINDEX, NEIGH_B, MAC_NULL);
+ verifyRemoveDownstreamRule(ruleBNull);
+ resetNetdAndBpfMaps();
+
+ // Upstream interface changes result in updating the rules.
+ recvNewNeigh(myIfindex, NEIGH_A, NUD_REACHABLE, MAC_A);
+ recvNewNeigh(myIfindex, NEIGH_B, NUD_REACHABLE, MAC_B);
+ resetNetdAndBpfMaps();
+
+ InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(UPSTREAM_IFACE2);
+ lp.setLinkAddresses(UPSTREAM_ADDRESSES);
+ dispatchTetherConnectionChanged(ipServer, UPSTREAM_IFACE2, lp, -1);
+ final Ipv6DownstreamRule ruleA2 = buildTestDownstreamRule(
+ UPSTREAM_IFINDEX2, NEIGH_A, MAC_A);
+ final Ipv6DownstreamRule ruleB2 = buildTestDownstreamRule(
+ UPSTREAM_IFINDEX2, NEIGH_B, MAC_B);
+ verifyRemoveDownstreamRule(inOrder, ruleA);
+ verifyRemoveDownstreamRule(inOrder, ruleB);
+ verifyStopUpstreamIpv6Forwarding(inOrder, UPSTREAM_PREFIXES);
+ verifyStartUpstreamIpv6Forwarding(inOrder, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES);
+ verifyAddDownstreamRule(inOrder, ruleA2);
+ verifyAddDownstreamRule(inOrder, ruleB2);
+ verifyNoUpstreamIpv6ForwardingChange(inOrder);
+ resetNetdAndBpfMaps();
+
+ // Upstream link addresses change result in updating the rules.
+ LinkProperties lp2 = new LinkProperties();
+ lp2.setInterfaceName(UPSTREAM_IFACE2);
+ lp2.setLinkAddresses(UPSTREAM_ADDRESSES2);
+ dispatchTetherConnectionChanged(ipServer, UPSTREAM_IFACE2, lp2, -1);
+ verifyRemoveDownstreamRule(inOrder, ruleA2);
+ verifyRemoveDownstreamRule(inOrder, ruleB2);
+ verifyStopUpstreamIpv6Forwarding(inOrder, UPSTREAM_PREFIXES);
+ verifyStartUpstreamIpv6Forwarding(inOrder, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES2);
+ verifyAddDownstreamRule(inOrder, ruleA2);
+ verifyAddDownstreamRule(inOrder, ruleB2);
+ resetNetdAndBpfMaps();
+
+ // When the upstream is lost, rules are removed.
+ dispatchTetherConnectionChanged(ipServer, null, null, 0);
+ verifyStopUpstreamIpv6Forwarding(inOrder, UPSTREAM_PREFIXES2);
+ verifyRemoveDownstreamRule(ruleA2);
+ verifyRemoveDownstreamRule(ruleB2);
+ // Upstream lost doesn't clear the downstream rules from the maps.
+ // Do that here.
+ recvDelNeigh(myIfindex, NEIGH_A, NUD_STALE, MAC_A);
+ recvDelNeigh(myIfindex, NEIGH_B, NUD_STALE, MAC_B);
+ resetNetdAndBpfMaps();
+
+ // If the upstream is IPv4-only, no IPv6 rules are added to BPF map.
+ dispatchTetherConnectionChanged(ipServer, UPSTREAM_IFACE);
+ resetNetdAndBpfMaps();
+ recvNewNeigh(myIfindex, NEIGH_A, NUD_REACHABLE, MAC_A);
+ verifyNoUpstreamIpv6ForwardingChange(null);
+ // Downstream rules are only added to BpfCoordinator but not BPF map.
+ verifyNeverAddDownstreamRule();
+ verifyNoMoreInteractions(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
+
+ // Rules can be added again once upstream IPv6 connectivity is available. The existing rules
+ // with an upstream of NO_UPSTREAM are reapplied.
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(ipServer, UPSTREAM_IFACE, lp, -1);
+ verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES);
+ verifyAddDownstreamRule(ruleA);
+ recvNewNeigh(myIfindex, NEIGH_B, NUD_REACHABLE, MAC_B);
+ verifyAddDownstreamRule(ruleB);
+
+ // If upstream IPv6 connectivity is lost, rules are removed.
+ resetNetdAndBpfMaps();
+ dispatchTetherConnectionChanged(ipServer, UPSTREAM_IFACE, null, 0);
+ verifyRemoveDownstreamRule(ruleA);
+ verifyRemoveDownstreamRule(ruleB);
+ verifyStopUpstreamIpv6Forwarding(null, UPSTREAM_PREFIXES);
+
+ // When upstream IPv6 connectivity comes back, upstream rules are added and downstream rules
+ // are reapplied.
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(ipServer, UPSTREAM_IFACE, lp, -1);
+ verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES);
+ verifyAddDownstreamRule(ruleA);
+ verifyAddDownstreamRule(ruleB);
+ resetNetdAndBpfMaps();
+
+ // When the downstream interface goes down, rules are removed.
+ ipServer.stop();
+ mTestLooper.dispatchAll();
+ verifyStopUpstreamIpv6Forwarding(null, UPSTREAM_PREFIXES);
+ verifyRemoveDownstreamRule(ruleA);
+ verifyRemoveDownstreamRule(ruleB);
+ verify(mIpNeighborMonitor).stop();
+ resetNetdAndBpfMaps();
+ }
+
+ @Test
+ public void enableDisableUsingBpfOffload() throws Exception {
+ final int myIfindex = DOWNSTREAM_IFINDEX;
+
+ // Expect that rules can be only added/removed when the BPF offload config is enabled.
+ // Note that the BPF offload disabled case is not a realistic test case. Because IP
+ // neighbor monitor doesn't start if BPF offload is disabled, there should have no
+ // neighbor event listening. This is used for testing the protection check just in case.
+ // TODO: Perhaps remove the BPF offload disabled case test once this check isn't needed
+ // anymore.
+
+ // [1] Enable BPF offload.
+ // A neighbor that is added or deleted causes the rule to be added or removed.
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ final IpServer ipServer = makeAndStartIpServer(DOWNSTREAM_IFACE, coordinator);
+ resetNetdAndBpfMaps();
+
+ recvNewNeigh(myIfindex, NEIGH_A, NUD_REACHABLE, MAC_A);
+ final Ipv6DownstreamRule rule = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A);
+ verifyAddDownstreamRule(rule);
+ resetNetdAndBpfMaps();
+
+ recvDelNeigh(myIfindex, NEIGH_A, NUD_STALE, MAC_A);
+ final Ipv6DownstreamRule ruleNull = buildTestDownstreamRule(
+ UPSTREAM_IFINDEX, NEIGH_A, MAC_NULL);
+ verifyRemoveDownstreamRule(ruleNull);
+ resetNetdAndBpfMaps();
+
+ // Upstream IPv6 connectivity change causes upstream rules change.
+ LinkProperties lp2 = new LinkProperties();
+ lp2.setInterfaceName(UPSTREAM_IFACE2);
+ lp2.setLinkAddresses(UPSTREAM_ADDRESSES2);
+ dispatchTetherConnectionChanged(ipServer, UPSTREAM_IFACE2, lp2, 0);
+ verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX2, UPSTREAM_PREFIXES2);
+ resetNetdAndBpfMaps();
+
+ // [2] Disable BPF offload.
+ // A neighbor that is added or deleted doesn’t cause the rule to be added or removed.
+ when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(false);
+ final BpfCoordinator coordinator2 = makeBpfCoordinator();
+ final IpServer ipServer2 = makeAndStartIpServer(DOWNSTREAM_IFACE, coordinator2);
+ verifyNoUpstreamIpv6ForwardingChange(null);
+ resetNetdAndBpfMaps();
+
+ recvNewNeigh(myIfindex, NEIGH_A, NUD_REACHABLE, MAC_A);
+ verifyNeverAddDownstreamRule();
+ resetNetdAndBpfMaps();
+
+ recvDelNeigh(myIfindex, NEIGH_A, NUD_STALE, MAC_A);
+ verifyNeverRemoveDownstreamRule();
+ resetNetdAndBpfMaps();
+
+ // Upstream IPv6 connectivity change doesn't cause the rule to be added or removed.
+ dispatchTetherConnectionChanged(ipServer2, UPSTREAM_IFACE2, lp2, 0);
+ verifyNoUpstreamIpv6ForwardingChange(null);
+ verifyNeverRemoveDownstreamRule();
+ resetNetdAndBpfMaps();
+ }
+
+ @Test
+ public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception {
+ when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(false);
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ final IpServer ipServer = makeAndStartIpServer(DOWNSTREAM_IFACE, coordinator);
+
+ // IP neighbor monitor doesn't start if BPF offload is disabled.
+ verify(mIpNeighborMonitor, never()).start();
+ }
+
+ @Test
+ public void testSkipVirtualNetworkInBpf() throws Exception {
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ final IpServer ipServer = makeAndStartIpServer(DOWNSTREAM_IFACE, coordinator);
+ final LinkProperties v6Only = new LinkProperties();
+ v6Only.setInterfaceName(IPSEC_IFACE);
+ v6Only.setLinkAddresses(UPSTREAM_ADDRESSES);
+
+ resetNetdAndBpfMaps();
+ dispatchTetherConnectionChanged(ipServer, IPSEC_IFACE, v6Only, 0);
+ verify(mNetd).tetherAddForward(DOWNSTREAM_IFACE, IPSEC_IFACE);
+ verify(mNetd).ipfwdAddInterfaceForward(DOWNSTREAM_IFACE, IPSEC_IFACE);
+ verifyNeverAddUpstreamRule();
+
+ recvNewNeigh(DOWNSTREAM_IFINDEX, NEIGH_A, NUD_REACHABLE, MAC_A);
+ verifyNeverAddDownstreamRule();
+ }
+
+ @Test
+ public void addRemoveTetherClient() throws Exception {
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ final IpServer ipServer = makeAndStartIpServer(DOWNSTREAM_IFACE, coordinator);
+ final int myIfindex = DOWNSTREAM_IFINDEX;
+ final int notMyIfindex = myIfindex - 1;
+
+ final InetAddress neighA = InetAddresses.parseNumericAddress("192.168.80.1");
+ final InetAddress neighB = InetAddresses.parseNumericAddress("192.168.80.2");
+ final InetAddress neighLL = InetAddresses.parseNumericAddress("169.254.0.1");
+ final InetAddress neighMC = InetAddresses.parseNumericAddress("224.0.0.1");
+
+ // Events on other interfaces are ignored.
+ recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, MAC_A);
+ assertNull(mTetherClients.get(ipServer));
+
+ // Events on this interface are received and sent to BpfCoordinator.
+ recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, MAC_A);
+ assertClientInfoExists(ipServer,
+ new ClientInfo(myIfindex, DOWNSTREAM_MAC, (Inet4Address) neighA, MAC_A));
+
+ recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, MAC_B);
+ assertClientInfoExists(ipServer,
+ new ClientInfo(myIfindex, DOWNSTREAM_MAC, (Inet4Address) neighB, MAC_B));
+
+ // Link-local and multicast neighbors are ignored.
+ recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, MAC_A);
+ assertClientInfoDoesNotExist(ipServer, (Inet4Address) neighLL);
+ recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, MAC_A);
+ assertClientInfoDoesNotExist(ipServer, (Inet4Address) neighMC);
+
+ // A neighbor that is no longer valid causes the client to be removed.
+ // NUD_FAILED events do not have a MAC address.
+ recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
+ assertClientInfoDoesNotExist(ipServer, (Inet4Address) neighA);
+
+ // A neighbor that is deleted causes the client to be removed.
+ recvDelNeigh(myIfindex, neighB, NUD_STALE, MAC_B);
+ // When last client information is deleted, IpServer will be removed from mTetherClients
+ assertNull(mTetherClients.get(ipServer));
+ }
}