Program the upstream IPv6 map in BpfCoordinator.
- Add methods to start and stop IPv6 forwarding upstream
- Populate the upstream IPv6 map when the first rule for any
upstream/downstream pair is created.
- Clear the upstream IPv6 map when the last rule for any
upstream/downstream pair is deleted.
Test: Added coverage to IpServerTest and BpfCoordinatorTest
Change-Id: Ib041081e95f5f449489ab63138de034222ffac8f
diff --git a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
index 715e3a5..4e615a1 100644
--- a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
@@ -17,6 +17,7 @@
package com.android.networkstack.tethering.apishim.api30;
import android.net.INetd;
+import android.net.MacAddress;
import android.net.TetherStatsParcel;
import android.net.util.SharedLog;
import android.os.RemoteException;
@@ -78,6 +79,17 @@
}
@Override
+ public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
+ MacAddress srcMac, MacAddress dstMac, int mtu) {
+ return true;
+ }
+
+ @Override
+ public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex) {
+ return true;
+ }
+
+ @Override
@Nullable
public SparseArray<TetherStatsValue> tetherOffloadGetStats() {
final TetherStatsParcel[] tetherStatsList;
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 091c483..3e46f98 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -18,6 +18,7 @@
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
+import android.net.MacAddress;
import android.net.util.SharedLog;
import android.system.ErrnoException;
import android.system.Os;
@@ -38,6 +39,7 @@
import com.android.networkstack.tethering.TetherLimitValue;
import com.android.networkstack.tethering.TetherStatsKey;
import com.android.networkstack.tethering.TetherStatsValue;
+import com.android.networkstack.tethering.TetherUpstream6Key;
import java.io.FileDescriptor;
@@ -68,6 +70,10 @@
@Nullable
private final BpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
+ // BPF map for upstream IPv6 forwarding.
+ @Nullable
+ private final BpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map;
+
// BPF map of tethering statistics of the upstream interface since tethering startup.
@Nullable
private final BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
@@ -81,6 +87,7 @@
mBpfDownstream4Map = deps.getBpfDownstream4Map();
mBpfUpstream4Map = deps.getBpfUpstream4Map();
mBpfDownstream6Map = deps.getBpfDownstream6Map();
+ mBpfUpstream6Map = deps.getBpfUpstream6Map();
mBpfStatsMap = deps.getBpfStatsMap();
mBpfLimitMap = deps.getBpfLimitMap();
}
@@ -88,7 +95,7 @@
@Override
public boolean isInitialized() {
return mBpfDownstream4Map != null && mBpfUpstream4Map != null && mBpfDownstream6Map != null
- && mBpfStatsMap != null && mBpfLimitMap != null;
+ && mBpfUpstream6Map != null && mBpfStatsMap != null && mBpfLimitMap != null;
}
@Override
@@ -125,6 +132,37 @@
}
@Override
+ public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
+ MacAddress srcMac, MacAddress dstMac, int mtu) {
+ if (!isInitialized()) return false;
+
+ final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfindex);
+ final Tether6Value value = new Tether6Value(upstreamIfindex, srcMac,
+ dstMac, OsConstants.ETH_P_IPV6, mtu);
+ try {
+ mBpfUpstream6Map.insertEntry(key, value);
+ } catch (ErrnoException | IllegalStateException e) {
+ mLog.e("Could not insert upstream6 entry: " + e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex) {
+ if (!isInitialized()) return false;
+
+ final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfindex);
+ try {
+ mBpfUpstream6Map.deleteEntry(key);
+ } catch (ErrnoException e) {
+ mLog.e("Could not delete upstream IPv6 entry: " + e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
@Nullable
public SparseArray<TetherStatsValue> tetherOffloadGetStats() {
if (!isInitialized()) return null;
@@ -292,6 +330,8 @@
+ (mBpfDownstream4Map != null ? "initialized" : "not initialized") + "}, "
+ "mBpfUpstream4Map{"
+ (mBpfUpstream4Map != null ? "initialized" : "not initialized") + "}, "
+ + "mBpfUpstream6Map{"
+ + (mBpfUpstream6Map != null ? "initialized" : "not initialized") + "}, "
+ "mBpfDownstream6Map{"
+ (mBpfDownstream6Map != null ? "initialized" : "not initialized") + "}, "
+ "mBpfStatsMap{"
diff --git a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
index cbd843b..c61c449 100644
--- a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
+++ b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
@@ -16,6 +16,7 @@
package com.android.networkstack.tethering.apishim.common;
+import android.net.MacAddress;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -73,6 +74,27 @@
public abstract boolean tetherOffloadRuleRemove(@NonNull Ipv6ForwardingRule rule);
/**
+ * Starts IPv6 forwarding between the specified interfaces.
+
+ * @param downstreamIfindex the downstream interface index
+ * @param upstreamIfindex the upstream interface index
+ * @param srcMac the source MAC address to use for packets
+ * @oaram dstMac the destination MAC address to use for packets
+ * @return true if operation succeeded or was a no-op, false otherwise
+ */
+ public abstract boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
+ MacAddress srcMac, MacAddress dstMac, int mtu);
+
+ /**
+ * Stops IPv6 forwarding between the specified interfaces.
+
+ * @param downstreamIfindex the downstream interface index
+ * @param upstreamIfindex the upstream interface index
+ * @return true if operation succeeded or was a no-op, false otherwise
+ */
+ public abstract boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex);
+
+ /**
* Return BPF tethering offload statistics.
*
* @return an array of TetherStatsValue's, where each entry contains the upstream interface
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 911f83f..8c3f565 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -280,6 +280,17 @@
}
}
+ /** Get upstream6 BPF map. */
+ @Nullable public BpfMap<TetherUpstream6Key, Tether6Value> getBpfUpstream6Map() {
+ try {
+ return new BpfMap<>(TETHER_UPSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
+ TetherUpstream6Key.class, Tether6Value.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create upstream6 map: " + e);
+ return null;
+ }
+ }
+
/** Get stats BPF map. */
@Nullable public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
try {
@@ -444,7 +455,7 @@
}
LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
- // Setup the data limit on the given upstream if the first rule is added.
+ // When the first rule is added to an upstream, setup upstream forwarding and data limit.
final int upstreamIfindex = rule.upstreamIfindex;
if (!isAnyRuleOnUpstream(upstreamIfindex)) {
// If failed to set a data limit, probably should not use this upstream, because
@@ -455,6 +466,19 @@
final String iface = mInterfaceNames.get(upstreamIfindex);
mLog.e("Setting data limit for " + iface + " failed.");
}
+
+ }
+
+ if (!isAnyRuleFromDownstreamToUpstream(rule.downstreamIfindex, rule.upstreamIfindex)) {
+ final int downstream = rule.downstreamIfindex;
+ final int upstream = rule.upstreamIfindex;
+ // TODO: support upstream forwarding on non-point-to-point interfaces.
+ // TODO: get the MTU from LinkProperties and update the rules when it changes.
+ if (!mBpfCoordinatorShim.startUpstreamIpv6Forwarding(downstream, upstream,
+ NULL_MAC_ADDRESS, NULL_MAC_ADDRESS, NetworkStackConstants.ETHER_MTU)) {
+ mLog.e("Failed to enable upstream IPv6 forwarding from "
+ + mInterfaceNames.get(downstream) + " to " + mInterfaceNames.get(upstream));
+ }
}
// Must update the adding rule after calling #isAnyRuleOnUpstream because it needs to
@@ -487,6 +511,16 @@
mIpv6ForwardingRules.remove(ipServer);
}
+ // If no more rules between this upstream and downstream, stop upstream forwarding.
+ if (!isAnyRuleFromDownstreamToUpstream(rule.downstreamIfindex, rule.upstreamIfindex)) {
+ final int downstream = rule.downstreamIfindex;
+ final int upstream = rule.upstreamIfindex;
+ if (!mBpfCoordinatorShim.stopUpstreamIpv6Forwarding(downstream, upstream)) {
+ mLog.e("Failed to disable upstream IPv6 forwarding from "
+ + mInterfaceNames.get(downstream) + " to " + mInterfaceNames.get(upstream));
+ }
+ }
+
// Do cleanup functionality if there is no more rule on the given upstream.
final int upstreamIfindex = rule.upstreamIfindex;
if (!isAnyRuleOnUpstream(upstreamIfindex)) {
@@ -535,12 +569,22 @@
if (rules == null) return;
// Need to build a rule list because the rule map may be changed in the iteration.
- for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) {
+ // First remove all the old rules, then add all the new rules. This is because the upstream
+ // forwarding code in tetherOffloadRuleAdd cannot support rules on two upstreams at the
+ // same time. Deleting the rules first ensures that upstream forwarding is disabled on the
+ // old upstream when the last rule is removed from it, and re-enabled on the new upstream
+ // when the first rule is added to it.
+ // TODO: Once the IPv6 client processing code has moved from IpServer to BpfCoordinator, do
+ // something smarter.
+ final ArrayList<Ipv6ForwardingRule> rulesCopy = new ArrayList<>(rules.values());
+ for (final Ipv6ForwardingRule rule : rulesCopy) {
// Remove the old rule before adding the new one because the map uses the same key for
// both rules. Reversing the processing order causes that the new rule is removed as
// unexpected.
// TODO: Add new rule first to reduce the latency which has no rule.
tetherOffloadRuleRemove(ipServer, rule);
+ }
+ for (final Ipv6ForwardingRule rule : rulesCopy) {
tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex));
}
}
@@ -1059,6 +1103,19 @@
return false;
}
+ private boolean isAnyRuleFromDownstreamToUpstream(int downstreamIfindex, int upstreamIfindex) {
+ for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
+ .values()) {
+ for (Ipv6ForwardingRule rule : rules.values()) {
+ if (downstreamIfindex == rule.downstreamIfindex
+ && upstreamIfindex == rule.upstreamIfindex) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@NonNull
private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex,
@NonNull final ForwardedStats diff) {
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java b/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
new file mode 100644
index 0000000..c736f2a
--- /dev/null
+++ b/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering;
+
+import com.android.net.module.util.Struct;
+
+/** Key type for upstream IPv6 forwarding map. */
+public class TetherUpstream6Key extends Struct {
+ @Field(order = 0, type = Type.S32)
+ public final int iif; // The input interface index.
+
+ public TetherUpstream6Key(int iif) {
+ this.iif = iif;
+ }
+}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 24edf3b..c26458c 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -112,6 +112,7 @@
import com.android.networkstack.tethering.TetherLimitValue;
import com.android.networkstack.tethering.TetherStatsKey;
import com.android.networkstack.tethering.TetherStatsValue;
+import com.android.networkstack.tethering.TetherUpstream6Key;
import com.android.networkstack.tethering.TetheringConfiguration;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
@@ -178,6 +179,7 @@
@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;
@@ -325,6 +327,12 @@
}
@Nullable
+ public BpfMap<TetherUpstream6Key, Tether6Value>
+ getBpfUpstream6Map() {
+ return mBpfUpstream6Map;
+ }
+
+ @Nullable
public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
return mBpfStatsMap;
}
@@ -865,6 +873,36 @@
}
}
+ private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int upstreamIfindex)
+ throws Exception {
+ if (!mBpfDeps.isAtLeastS()) return;
+ final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index);
+ final Tether6Value value = new Tether6Value(upstreamIfindex,
+ MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
+ ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
+ verifyWithOrder(inOrder, mBpfUpstream6Map).insertEntry(key, value);
+ }
+
+ private void verifyStopUpstreamIpv6Forwarding(@Nullable InOrder inOrder)
+ throws Exception {
+ if (!mBpfDeps.isAtLeastS()) return;
+ final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index);
+ verifyWithOrder(inOrder, mBpfUpstream6Map).deleteEntry(key);
+ }
+
+ 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();
@@ -873,7 +911,9 @@
}
private void resetNetdBpfMapAndCoordinator() throws Exception {
- reset(mNetd, mBpfDownstream6Map, mBpfCoordinator);
+ 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));
@@ -894,7 +934,6 @@
final int myIfindex = TEST_IFACE_PARAMS.index;
final int notMyIfindex = myIfindex - 1;
- final MacAddress myMac = TEST_IFACE_PARAMS.macAddr;
final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1");
final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2");
final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1");
@@ -904,33 +943,35 @@
final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
resetNetdBpfMapAndCoordinator();
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map);
+ 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);
+ verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
// Events on this interface are received and sent to netd.
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
verify(mBpfCoordinator).tetherOffloadRuleAdd(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, neighA, macA);
+ verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX);
resetNetdBpfMapAndCoordinator();
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
verify(mBpfCoordinator).tetherOffloadRuleAdd(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, neighB, macB);
+ verifyNoUpstreamIpv6ForwardingChange(null);
resetNetdBpfMapAndCoordinator();
// Link-local and multicast neighbors are ignored.
recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map);
+ verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map);
+ 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.
@@ -938,6 +979,7 @@
verify(mBpfCoordinator).tetherOffloadRuleRemove(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull));
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighA, macNull);
+ verifyNoUpstreamIpv6ForwardingChange(null);
resetNetdBpfMapAndCoordinator();
// A neighbor that is deleted causes the rule to be removed.
@@ -945,22 +987,27 @@
verify(mBpfCoordinator).tetherOffloadRuleRemove(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull));
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighB, macNull);
+ verifyStopUpstreamIpv6Forwarding(null);
resetNetdBpfMapAndCoordinator();
// Upstream changes result in updating the rules.
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
resetNetdBpfMapAndCoordinator();
- InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map);
+ InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
verify(mBpfCoordinator).tetherOffloadRuleUpdate(mIpServer, UPSTREAM_IFINDEX2);
verifyTetherOffloadRuleRemove(inOrder, UPSTREAM_IFINDEX, neighA, macA);
- verifyTetherOffloadRuleAdd(inOrder, UPSTREAM_IFINDEX2, neighA, macA);
verifyTetherOffloadRuleRemove(inOrder, UPSTREAM_IFINDEX, neighB, macB);
+ verifyStopUpstreamIpv6Forwarding(inOrder);
+ verifyTetherOffloadRuleAdd(inOrder, UPSTREAM_IFINDEX2, neighA, macA);
+ verifyStartUpstreamIpv6Forwarding(inOrder, UPSTREAM_IFINDEX2);
verifyTetherOffloadRuleAdd(inOrder, UPSTREAM_IFINDEX2, neighB, macB);
+ verifyNoUpstreamIpv6ForwardingChange(inOrder);
resetNetdBpfMapAndCoordinator();
// When the upstream is lost, rules are removed.
@@ -972,6 +1019,7 @@
verify(mBpfCoordinator, times(2)).tetherOffloadRuleClear(mIpServer);
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX2, neighA, macA);
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX2, neighB, macB);
+ verifyStopUpstreamIpv6Forwarding(inOrder);
resetNetdBpfMapAndCoordinator();
// If the upstream is IPv4-only, no rules are added.
@@ -980,7 +1028,8 @@
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
// Clear function is called by #updateIpv6ForwardingRules for the IPv6 upstream is lost.
verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map);
+ verifyNoUpstreamIpv6ForwardingChange(null);
+ verifyNoMoreInteractions(mBpfCoordinator, mNetd, mBpfDownstream6Map, mBpfUpstream6Map);
// Rules can be added again once upstream IPv6 connectivity is available.
lp.setInterfaceName(UPSTREAM_IFACE);
@@ -989,6 +1038,7 @@
verify(mBpfCoordinator).tetherOffloadRuleAdd(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, neighB, macB);
+ verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX);
verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
verifyNeverTetherOffloadRuleAdd(UPSTREAM_IFINDEX, neighA, macA);
@@ -998,6 +1048,7 @@
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighB, macB);
+ verifyStopUpstreamIpv6Forwarding(null);
// When the interface goes down, rules are removed.
lp.setInterfaceName(UPSTREAM_IFACE);
@@ -1007,6 +1058,7 @@
verify(mBpfCoordinator).tetherOffloadRuleAdd(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, neighA, macA);
+ verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX);
verify(mBpfCoordinator).tetherOffloadRuleAdd(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, neighB, macB);
@@ -1017,6 +1069,7 @@
verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighA, macA);
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighB, macB);
+ verifyStopUpstreamIpv6Forwarding(null);
verify(mIpNeighborMonitor).stop();
resetNetdBpfMapAndCoordinator();
}
@@ -1045,12 +1098,14 @@
verify(mBpfCoordinator).tetherOffloadRuleAdd(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macA));
verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, neigh, macA);
+ verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX);
resetNetdBpfMapAndCoordinator();
recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
verify(mBpfCoordinator).tetherOffloadRuleRemove(
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull));
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neigh, macNull);
+ verifyStopUpstreamIpv6Forwarding(null);
resetNetdBpfMapAndCoordinator();
// [2] Disable BPF offload.
@@ -1062,11 +1117,13 @@
recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(any(), any());
verifyNeverTetherOffloadRuleAdd();
+ verifyNoUpstreamIpv6ForwardingChange(null);
resetNetdBpfMapAndCoordinator();
recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any());
verifyNeverTetherOffloadRuleRemove();
+ verifyNoUpstreamIpv6ForwardingChange(null);
resetNetdBpfMapAndCoordinator();
}
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 037f45e..0f9ca3d 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -161,6 +161,7 @@
@Mock private BpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map;
@Mock private BpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map;
@Mock private BpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
+ @Mock private BpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map;
// Late init since methods must be called by the thread that created this object.
private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
@@ -223,6 +224,12 @@
}
@Nullable
+ public BpfMap<TetherUpstream6Key, Tether6Value>
+ getBpfUpstream6Map() {
+ return mBpfUpstream6Map;
+ }
+
+ @Nullable
public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
return mBpfStatsMap;
}
@@ -362,6 +369,36 @@
}
}
+ private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int downstreamIfIndex,
+ int upstreamIfindex) throws Exception {
+ if (!mDeps.isAtLeastS()) return;
+ final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex);
+ final Tether6Value value = new Tether6Value(upstreamIfindex,
+ MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
+ ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
+ verifyWithOrder(inOrder, mBpfUpstream6Map).insertEntry(key, value);
+ }
+
+ private void verifyStopUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int downstreamIfIndex)
+ throws Exception {
+ if (!mDeps.isAtLeastS()) return;
+ final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex);
+ verifyWithOrder(inOrder, mBpfUpstream6Map).deleteEntry(key);
+ }
+
+ private void verifyNoUpstreamIpv6ForwardingChange(@Nullable InOrder inOrder) throws Exception {
+ if (!mDeps.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());
+ }
+ }
+
private void verifyTetherOffloadRuleAdd(@Nullable InOrder inOrder,
@NonNull Ipv6ForwardingRule rule) throws Exception {
if (mDeps.isAtLeastS()) {
@@ -804,7 +841,8 @@
coordinator.addUpstreamNameToLookupTable(ethIfIndex, ethIface);
coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
- final InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfLimitMap, mBpfStatsMap);
+ final InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfUpstream6Map, mBpfLimitMap,
+ mBpfStatsMap);
// Before the rule test, here are the additional actions while the rules are changed.
// - After adding the first rule on a given upstream, the coordinator adds a data limit.
@@ -824,7 +862,7 @@
verifyTetherOffloadRuleAdd(inOrder, ethernetRuleA);
verifyTetherOffloadSetInterfaceQuota(inOrder, ethIfIndex, QUOTA_UNLIMITED,
true /* isInit */);
-
+ verifyStartUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, ethIfIndex);
coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB);
verifyTetherOffloadRuleAdd(inOrder, ethernetRuleB);
@@ -840,11 +878,13 @@
// by one for updating upstream interface index by #tetherOffloadRuleUpdate.
coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex);
verifyTetherOffloadRuleRemove(inOrder, ethernetRuleA);
+ verifyTetherOffloadRuleRemove(inOrder, ethernetRuleB);
+ verifyStopUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX);
+ verifyTetherOffloadGetAndClearStats(inOrder, ethIfIndex);
verifyTetherOffloadRuleAdd(inOrder, mobileRuleA);
verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED,
true /* isInit */);
- verifyTetherOffloadRuleRemove(inOrder, ethernetRuleB);
- verifyTetherOffloadGetAndClearStats(inOrder, ethIfIndex);
+ verifyStartUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, mobileIfIndex);
verifyTetherOffloadRuleAdd(inOrder, mobileRuleB);
// [3] Clear all rules for a given IpServer.
@@ -853,6 +893,7 @@
coordinator.tetherOffloadRuleClear(mIpServer);
verifyTetherOffloadRuleRemove(inOrder, mobileRuleA);
verifyTetherOffloadRuleRemove(inOrder, mobileRuleB);
+ verifyStopUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX);
verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex);
// [4] Force pushing stats update to verify that the last diff of stats is reported on all
@@ -942,6 +983,15 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testBpfDisabledbyNoBpfUpstream6Map() throws Exception {
+ setupFunctioningNetdInterface();
+ doReturn(null).when(mDeps).getBpfUpstream6Map();
+
+ checkBpfDisabled();
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R)
public void testBpfDisabledbyNoBpfStatsMap() throws Exception {
setupFunctioningNetdInterface();
doReturn(null).when(mDeps).getBpfStatsMap();