diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 8d672214f7..c78893a8fe 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -66,6 +66,7 @@ import android.util.SparseArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.modules.utils.build.SdkLevel; @@ -92,6 +93,7 @@ import java.net.Inet6Address; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -298,6 +300,8 @@ public class IpServer extends StateMachineShim { private int mLastIPv6UpstreamIfindex = 0; private boolean mUpstreamSupportsBpf = false; + @NonNull + private Set mLastIPv6UpstreamPrefixes = Collections.emptySet(); private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer { public void accept(NeighborEvent e) { @@ -782,13 +786,8 @@ public class IpServer extends StateMachineShim { if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment); - for (LinkAddress linkAddr : v6only.getLinkAddresses()) { - if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; - - final IpPrefix prefix = new IpPrefix( - linkAddr.getAddress(), linkAddr.getPrefixLength()); - params.prefixes.add(prefix); - + params.prefixes = getTetherableIpv6Prefixes(v6only); + for (IpPrefix prefix : params.prefixes) { final Inet6Address dnsServer = getLocalDnsIpFor(prefix); if (dnsServer != null) { params.dnses.add(dnsServer); @@ -808,9 +807,12 @@ public class IpServer extends StateMachineShim { // Not support BPF on virtual upstream interface final boolean upstreamSupportsBpf = upstreamIface != null && !isVcnInterface(upstreamIface); - updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, upstreamSupportsBpf); + final Set upstreamPrefixes = params != null ? params.prefixes : Set.of(); + updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamPrefixes, + upstreamIfIndex, upstreamPrefixes, upstreamSupportsBpf); mLastIPv6LinkProperties = v6only; mLastIPv6UpstreamIfindex = upstreamIfIndex; + mLastIPv6UpstreamPrefixes = upstreamPrefixes; mUpstreamSupportsBpf = upstreamSupportsBpf; if (mDadProxy != null) { mDadProxy.setUpstreamIface(upstreamIfaceParams); @@ -959,14 +961,17 @@ public class IpServer extends StateMachineShim { return supportsBpf ? ifindex : NO_UPSTREAM; } - // Handles updates to IPv6 forwarding rules if the upstream changes. - private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex, - boolean upstreamSupportsBpf) { + // Handles updates to IPv6 forwarding rules if the upstream or its prefixes change. + private void updateIpv6ForwardingRules(int prevUpstreamIfindex, + @NonNull Set prevUpstreamPrefixes, int upstreamIfindex, + @NonNull Set upstreamPrefixes, boolean upstreamSupportsBpf) { // If the upstream interface has changed, remove all rules and re-add them with the new // upstream interface. If upstream is a virtual network, treated as no upstream. - if (prevUpstreamIfindex != upstreamIfindex) { + if (prevUpstreamIfindex != upstreamIfindex + || !prevUpstreamPrefixes.equals(upstreamPrefixes)) { mBpfCoordinator.updateAllIpv6Rules(this, this.mInterfaceParams, - getInterfaceIndexForRule(upstreamIfindex, upstreamSupportsBpf)); + getInterfaceIndexForRule(upstreamIfindex, upstreamSupportsBpf), + upstreamPrefixes); } } @@ -1371,7 +1376,7 @@ public class IpServer extends StateMachineShim { for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname); mUpstreamIfaceSet = null; mBpfCoordinator.updateAllIpv6Rules( - IpServer.this, IpServer.this.mInterfaceParams, NO_UPSTREAM); + IpServer.this, IpServer.this.mInterfaceParams, NO_UPSTREAM, Set.of()); } private void cleanupUpstreamInterface(String upstreamIface) { @@ -1548,4 +1553,21 @@ public class IpServer extends StateMachineShim { } return random; } + + /** Get IPv6 prefixes from LinkProperties */ + @NonNull + @VisibleForTesting + static HashSet getTetherableIpv6Prefixes(@NonNull Collection addrs) { + final HashSet prefixes = new HashSet<>(); + for (LinkAddress linkAddr : addrs) { + if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; + prefixes.add(new IpPrefix(linkAddr.getAddress(), RFC7421_PREFIX_LENGTH)); + } + return prefixes; + } + + @NonNull + private HashSet getTetherableIpv6Prefixes(@NonNull LinkProperties lp) { + return getTetherableIpv6Prefixes(lp.getLinkAddresses()); + } } diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index 46c815f2f8..2b14a425fc 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -28,6 +28,7 @@ import static android.system.OsConstants.ETH_P_IP; import static android.system.OsConstants.ETH_P_IPV6; import static com.android.net.module.util.NetworkStackConstants.IPV4_MIN_MTU; +import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN; import static com.android.net.module.util.ip.ConntrackMonitor.ConntrackEvent; import static com.android.networkstack.tethering.BpfUtils.DOWNSTREAM; import static com.android.networkstack.tethering.BpfUtils.UPSTREAM; @@ -90,7 +91,6 @@ import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -123,7 +123,6 @@ public class BpfCoordinator { private static final int DUMP_TIMEOUT_MS = 10_000; private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString( "00:00:00:00:00:00"); - private static final IpPrefix IPV6_ZERO_PREFIX64 = new IpPrefix("::/64"); private static final String TETHER_DOWNSTREAM4_MAP_PATH = makeMapPath(DOWNSTREAM, 4); private static final String TETHER_UPSTREAM4_MAP_PATH = makeMapPath(UPSTREAM, 4); private static final String TETHER_DOWNSTREAM6_FS_PATH = makeMapPath(DOWNSTREAM, 6); @@ -768,7 +767,8 @@ public class BpfCoordinator { * Note that this can be only called on handler thread. */ public void updateAllIpv6Rules(@NonNull final IpServer ipServer, - final InterfaceParams interfaceParams, int newUpstreamIfindex) { + final InterfaceParams interfaceParams, int newUpstreamIfindex, + @NonNull final Set newUpstreamPrefixes) { if (!isUsingBpf()) return; // Remove IPv6 downstream rules. Remove the old ones before adding the new rules, otherwise @@ -791,9 +791,11 @@ public class BpfCoordinator { // Add new upstream rules. if (newUpstreamIfindex != 0 && interfaceParams != null && interfaceParams.macAddr != null) { - addIpv6UpstreamRule(ipServer, new Ipv6UpstreamRule( - newUpstreamIfindex, interfaceParams.index, IPV6_ZERO_PREFIX64, - interfaceParams.macAddr, NULL_MAC_ADDRESS, NULL_MAC_ADDRESS)); + for (final IpPrefix ipPrefix : newUpstreamPrefixes) { + addIpv6UpstreamRule(ipServer, new Ipv6UpstreamRule( + newUpstreamIfindex, interfaceParams.index, ipPrefix, + interfaceParams.macAddr, NULL_MAC_ADDRESS, NULL_MAC_ADDRESS)); + } } // Add updated downstream rules. @@ -1256,10 +1258,24 @@ public class BpfCoordinator { pw.decreaseIndent(); } + private IpPrefix longToPrefix(long ip64) { + final ByteBuffer prefixBuffer = ByteBuffer.allocate(IPV6_ADDR_LEN); + prefixBuffer.putLong(ip64); + IpPrefix sourcePrefix; + try { + sourcePrefix = new IpPrefix(InetAddress.getByAddress(prefixBuffer.array()), 64); + } catch (UnknownHostException e) { + // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte array + // is the wrong length, but we allocate it with fixed length IPV6_ADDR_LEN. + throw new IllegalArgumentException("Invalid IPv6 address"); + } + return sourcePrefix; + } + private String ipv6UpstreamRuleToString(TetherUpstream6Key key, Tether6Value value) { - return String.format("%d(%s) [%s] -> %d(%s) %04x [%s] [%s]", - key.iif, getIfName(key.iif), key.dstMac, value.oif, getIfName(value.oif), - value.ethProto, value.ethSrcMac, value.ethDstMac); + return String.format("%d(%s) [%s] [%s] -> %d(%s) %04x [%s] [%s]", + key.iif, getIfName(key.iif), key.dstMac, longToPrefix(key.src64), value.oif, + getIfName(value.oif), value.ethProto, value.ethSrcMac, value.ethDstMac); } private void dumpIpv6UpstreamRules(IndentingPrintWriter pw) { @@ -1309,8 +1325,8 @@ public class BpfCoordinator { // TODO: use dump utils with headerline and lambda which prints key and value to reduce // duplicate bpf map dump code. private void dumpBpfForwardingRulesIpv6(IndentingPrintWriter pw) { - pw.println("IPv6 Upstream: iif(iface) [inDstMac] -> oif(iface) etherType [outSrcMac] " - + "[outDstMac]"); + pw.println("IPv6 Upstream: iif(iface) [inDstMac] [sourcePrefix] -> oif(iface) etherType " + + "[outSrcMac] [outDstMac]"); pw.increaseIndent(); dumpIpv6UpstreamRules(pw); pw.decreaseIndent(); @@ -1554,8 +1570,7 @@ public class BpfCoordinator { */ @NonNull public TetherUpstream6Key makeTetherUpstream6Key() { - byte[] prefixBytes = Arrays.copyOf(sourcePrefix.getRawAddress(), 8); - long prefix64 = ByteBuffer.wrap(prefixBytes).order(ByteOrder.BIG_ENDIAN).getLong(); + long prefix64 = ByteBuffer.wrap(sourcePrefix.getRawAddress()).getLong(); return new TetherUpstream6Key(downstreamIfindex, inDstMac, prefix64); } diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 9724bd451d..bc970e4ac6 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -34,6 +34,7 @@ import static android.net.ip.IpServer.STATE_AVAILABLE; import static android.net.ip.IpServer.STATE_LOCAL_ONLY; 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; @@ -95,6 +96,8 @@ import android.os.Handler; 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; @@ -145,12 +148,15 @@ 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; @RunWith(AndroidJUnit4.class) @SmallTest @@ -188,6 +194,18 @@ public class IpServerTest { private final LinkAddress mTestAddress = new LinkAddress("192.168.42.5/24"); private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); + private static final Set NO_ADDRESSES = Set.of(); + private static final Set NO_PREFIXES = Set.of(); + private static final Set UPSTREAM_ADDRESSES = + Set.of(new LinkAddress("2001:db8:0:1234::168/64")); + private static final Set UPSTREAM_PREFIXES = + Set.of(new IpPrefix("2001:db8:0:1234::/64")); + private static final Set UPSTREAM_ADDRESSES2 = Set.of( + new LinkAddress("2001:db8:0:1234::168/64"), + new LinkAddress("2001:db8:0:abcd::168/64")); + private static final Set UPSTREAM_PREFIXES2 = Set.of( + new IpPrefix("2001:db8:0:1234::/64"), new IpPrefix("2001:db8:0:abcd::/64")); + @Mock private INetd mNetd; @Mock private IpServer.Callback mCallback; @Mock private SharedLog mSharedLog; @@ -271,24 +289,27 @@ public class IpServerTest { private void initTetheredStateMachine(int interfaceType, String upstreamIface) throws Exception { - initTetheredStateMachine(interfaceType, upstreamIface, false, + initTetheredStateMachine(interfaceType, upstreamIface, NO_ADDRESSES, false, DEFAULT_USING_BPF_OFFLOAD); } private void initTetheredStateMachine(int interfaceType, String upstreamIface, - boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception { + Set upstreamAddresses, boolean usingLegacyDhcp, boolean usingBpfOffload) + throws Exception { initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); if (upstreamIface != null) { LinkProperties lp = new LinkProperties(); lp.setInterfaceName(upstreamIface); + lp.setLinkAddresses(upstreamAddresses); dispatchTetherConnectionChanged(upstreamIface, lp, 0); - if (usingBpfOffload) { + if (usingBpfOffload && !lp.getLinkAddresses().isEmpty()) { + Set upstreamPrefixes = getTetherableIpv6Prefixes(lp.getLinkAddresses()); InterfaceParams interfaceParams = mDependencies.getInterfaceParams(upstreamIface); assertNotNull("missing upstream interface: " + upstreamIface, interfaceParams); verify(mBpfCoordinator).updateAllIpv6Rules( - mIpServer, TEST_IFACE_PARAMS, interfaceParams.index); - verifyStartUpstreamIpv6Forwarding(null, interfaceParams.index); + mIpServer, TEST_IFACE_PARAMS, interfaceParams.index, upstreamPrefixes); + verifyStartUpstreamIpv6Forwarding(null, interfaceParams.index, upstreamPrefixes); } else { verifyNoUpstreamIpv6ForwardingChange(null); } @@ -664,10 +685,7 @@ public class IpServerTest { inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mBpfCoordinator).updateAllIpv6Rules( - mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM); - if (!mBpfDeps.isAtLeastS()) { - inOrder.verify(mNetd).tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX); - } + mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES); // When tethering stops, upstream interface is set to zero and thus clearing all upstream // rules. Downstream rules are needed to be cleared explicitly by calling // BpfCoordinator#clearAllIpv6Rules in TetheredState#exit. @@ -857,8 +875,8 @@ public class IpServerTest { @Test public void doesNotStartDhcpServerIfDisabled() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, NO_ADDRESSES, + true /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD); dispatchTetherConnectionChanged(UPSTREAM_IFACE); verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); @@ -963,11 +981,19 @@ public class IpServerTest { TEST_IFACE_PARAMS.macAddr, ETH_P_IPV6, NetworkStackConstants.ETHER_MTU); } + private static long prefixToLong(IpPrefix prefix) { + return ByteBuffer.wrap(prefix.getRawAddress()).getLong(); + } + private T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) { + return verifyWithOrder(inOrder, t, times(1)); + } + + private T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t, VerificationMode mode) { if (inOrder != null) { - return inOrder.verify(t); + return inOrder.verify(t, mode); } else { - return verify(t); + return verify(t, mode); } } @@ -1027,23 +1053,49 @@ public class IpServerTest { } } - private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int upstreamIfindex) - throws Exception { + private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int upstreamIfindex, + @NonNull Set upstreamPrefixes) throws Exception { if (!mBpfDeps.isAtLeastS()) return; - final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index, - TEST_IFACE_PARAMS.macAddr, 0); - 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); + ArrayMap 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 keyCaptor = + ArgumentCaptor.forClass(TetherUpstream6Key.class); + ArgumentCaptor valueCaptor = + ArgumentCaptor.forClass(Tether6Value.class); + verifyWithOrder(inOrder, mBpfUpstream6Map, times(expected.size())).insertEntry( + keyCaptor.capture(), valueCaptor.capture()); + List keys = keyCaptor.getAllValues(); + List values = valueCaptor.getAllValues(); + ArrayMap 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) - throws Exception { + private void verifyStopUpstreamIpv6Forwarding(@Nullable InOrder inOrder, + @NonNull Set upstreamPrefixes) throws Exception { if (!mBpfDeps.isAtLeastS()) return; - final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index, - TEST_IFACE_PARAMS.macAddr, 0); - verifyWithOrder(inOrder, mBpfUpstream6Map).deleteEntry(key); + Set 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 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 { @@ -1084,8 +1136,8 @@ public class IpServerTest { @Test public void addRemoveipv6ForwardingRules() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD); + 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; @@ -1146,7 +1198,7 @@ public class IpServerTest { UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macNull); resetNetdBpfMapAndCoordinator(); - // Upstream changes result in updating the rules. + // Upstream interface changes result in updating the rules. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); resetNetdBpfMapAndCoordinator(); @@ -1154,14 +1206,36 @@ public class IpServerTest { 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); + 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); - verifyStartUpstreamIpv6Forwarding(inOrder, UPSTREAM_IFINDEX2); + 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(); + + // Upstream link addresses change result in updating the rules. + LinkProperties lp2 = new LinkProperties(); + lp2.setInterfaceName(UPSTREAM_IFACE2); + lp2.setLinkAddresses(UPSTREAM_ADDRESSES2); + 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, @@ -1175,8 +1249,8 @@ public class IpServerTest { // - processMessage CMD_IPV6_TETHER_UPDATE for the IPv6 upstream is lost. // See dispatchTetherConnectionChanged. verify(mBpfCoordinator, times(2)).updateAllIpv6Rules( - mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM); - verifyStopUpstreamIpv6Forwarding(inOrder); + mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES); + verifyStopUpstreamIpv6Forwarding(inOrder, UPSTREAM_PREFIXES2); verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX2, UPSTREAM_IFACE_PARAMS2.macAddr, neighA, macA); verifyTetherOffloadRuleRemove(null, @@ -1206,7 +1280,7 @@ public class IpServerTest { // with an upstream of NO_UPSTREAM are reapplied. lp.setInterfaceName(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); - verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX); + verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES); verify(mBpfCoordinator).addIpv6DownstreamRule( mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macA)); verifyTetherOffloadRuleAdd(null, @@ -1220,16 +1294,17 @@ public class IpServerTest { // If upstream IPv6 connectivity is lost, rules are removed. resetNetdBpfMapAndCoordinator(); dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); - verify(mBpfCoordinator).updateAllIpv6Rules(mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM); + verify(mBpfCoordinator).updateAllIpv6Rules( + mIpServer, TEST_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES); verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB); - verifyStopUpstreamIpv6Forwarding(null); + verifyStopUpstreamIpv6Forwarding(null, UPSTREAM_PREFIXES); // When upstream IPv6 connectivity comes back, upstream rules are added and downstream rules // are reapplied. lp.setInterfaceName(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); - verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX); + verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES); verify(mBpfCoordinator).addIpv6DownstreamRule( mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macA)); verifyTetherOffloadRuleAdd(null, @@ -1244,7 +1319,7 @@ public class IpServerTest { mIpServer.stop(); mLooper.dispatchAll(); verify(mBpfCoordinator).clearAllIpv6Rules(mIpServer); - verifyStopUpstreamIpv6Forwarding(null); + verifyStopUpstreamIpv6Forwarding(null, UPSTREAM_PREFIXES); verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA); verifyTetherOffloadRuleRemove(null, @@ -1269,8 +1344,8 @@ public class IpServerTest { // [1] Enable BPF offload. // A neighbor that is added or deleted causes the rule to be added or removed. - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - true /* usingBpfOffload */); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, UPSTREAM_ADDRESSES, + false /* usingLegacyDhcp */, true /* usingBpfOffload */); resetNetdBpfMapAndCoordinator(); recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); @@ -1290,15 +1365,17 @@ public class IpServerTest { // 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); - verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX2); + 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, false /* usingLegacyDhcp */, - false /* usingBpfOffload */); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, UPSTREAM_ADDRESSES, + false /* usingLegacyDhcp */, false /* usingBpfOffload */); resetNetdBpfMapAndCoordinator(); recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); @@ -1318,8 +1395,8 @@ public class IpServerTest { @Test public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - false /* usingBpfOffload */); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, UPSTREAM_ADDRESSES, + false /* usingLegacyDhcp */, false /* usingBpfOffload */); // IP neighbor monitor doesn't start if BPF offload is disabled. verify(mIpNeighborMonitor, never()).start(); @@ -1601,8 +1678,8 @@ public class IpServerTest { // TODO: move to BpfCoordinatorTest once IpNeighborMonitor is migrated to BpfCoordinator. @Test public void addRemoveTetherClient() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD); + 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; 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 601f587f76..7fbb670d20 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -55,6 +55,7 @@ import static com.android.networkstack.tethering.BpfCoordinator.toIpv4MappedAddr import static com.android.networkstack.tethering.BpfUtils.DOWNSTREAM; import static com.android.networkstack.tethering.BpfUtils.UPSTREAM; import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; +import static com.android.testutils.MiscAsserts.assertSameElements; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -73,6 +74,7 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -92,6 +94,7 @@ import android.net.ip.IpServer; import android.os.Build; import android.os.Handler; import android.os.test.TestLooper; +import android.util.ArrayMap; import android.util.SparseArray; import androidx.annotation.NonNull; @@ -136,16 +139,20 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; +import org.mockito.verification.VerificationMode; import java.io.StringWriter; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Set; @RunWith(AndroidJUnit4.class) @SmallTest @@ -159,7 +166,7 @@ public class BpfCoordinatorTest { private static final int TEST_NET_ID = 24; private static final int TEST_NET_ID2 = 25; - private static final int INVALID_IFINDEX = 0; + private static final int NO_UPSTREAM = 0; private static final int UPSTREAM_IFINDEX = 1001; private static final int UPSTREAM_XLAT_IFINDEX = 1002; private static final int UPSTREAM_IFINDEX2 = 1003; @@ -178,8 +185,17 @@ public class BpfCoordinatorTest { 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 InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1"); - private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2"); + 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 UPSTREAM_PREFIXES = Set.of(UPSTREAM_PREFIX); + private static final Set UPSTREAM_PREFIXES2 = + Set.of(UPSTREAM_PREFIX, UPSTREAM_PREFIX2); + private static final Set NO_PREFIXES = Set.of(); + + private static final InetAddress NEIGH_A = + InetAddresses.parseNumericAddress("2001:db8:0:1234::1"); + private static final InetAddress NEIGH_B = + InetAddresses.parseNumericAddress("2001:db8:0:1234::2"); private static final Inet4Address REMOTE_ADDR = (Inet4Address) InetAddresses.parseNumericAddress("140.112.8.116"); @@ -195,7 +211,6 @@ public class BpfCoordinatorTest { private static final Inet4Address XLAT_LOCAL_IPV4ADDR = (Inet4Address) InetAddresses.parseNumericAddress("192.0.0.46"); private static final IpPrefix NAT64_IP_PREFIX = new IpPrefix("64:ff9b::/96"); - private static final IpPrefix IPV6_ZERO_PREFIX = new IpPrefix("::/64"); // Generally, public port and private port are the same in the NAT conntrack message. // TODO: consider using different private port and public port for testing. @@ -624,10 +639,14 @@ public class BpfCoordinatorTest { } private T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) { + return verifyWithOrder(inOrder, t, times(1)); + } + + private T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t, VerificationMode mode) { if (inOrder != null) { - return inOrder.verify(t); + return inOrder.verify(t, mode); } else { - return verify(t); + return verify(t, mode); } } @@ -667,6 +686,28 @@ public class BpfCoordinatorTest { rule.makeTetherUpstream6Key(), rule.makeTether6Value()); } + private void verifyAddUpstreamRules(@Nullable InOrder inOrder, + @NonNull Set rules) throws Exception { + if (!mDeps.isAtLeastS()) return; + ArrayMap expected = new ArrayMap<>(); + for (Ipv6UpstreamRule rule : rules) { + expected.put(rule.makeTetherUpstream6Key(), rule.makeTether6Value()); + } + ArgumentCaptor keyCaptor = + ArgumentCaptor.forClass(TetherUpstream6Key.class); + ArgumentCaptor valueCaptor = + ArgumentCaptor.forClass(Tether6Value.class); + verifyWithOrder(inOrder, mBpfUpstream6Map, times(expected.size())).insertEntry( + keyCaptor.capture(), valueCaptor.capture()); + List keys = keyCaptor.getAllValues(); + List values = valueCaptor.getAllValues(); + ArrayMap captured = new ArrayMap<>(); + for (int i = 0; i < keys.size(); i++) { + captured.put(keys.get(i), values.get(i)); + } + assertEquals(expected, captured); + } + private void verifyAddDownstreamRule(@Nullable InOrder inOrder, @NonNull Ipv6DownstreamRule rule) throws Exception { if (mDeps.isAtLeastS()) { @@ -697,6 +738,20 @@ public class BpfCoordinatorTest { rule.makeTetherUpstream6Key()); } + private void verifyRemoveUpstreamRules(@Nullable InOrder inOrder, + @NonNull Set rules) throws Exception { + if (!mDeps.isAtLeastS()) return; + List expected = new ArrayList<>(); + for (Ipv6UpstreamRule rule : rules) { + expected.add(rule.makeTetherUpstream6Key()); + } + ArgumentCaptor keyCaptor = + ArgumentCaptor.forClass(TetherUpstream6Key.class); + verifyWithOrder(inOrder, mBpfUpstream6Map, times(expected.size())).deleteEntry( + keyCaptor.capture()); + assertSameElements(expected, keyCaptor.getAllValues()); + } + private void verifyRemoveDownstreamRule(@Nullable InOrder inOrder, @NonNull final Ipv6DownstreamRule rule) throws Exception { if (mDeps.isAtLeastS()) { @@ -785,10 +840,11 @@ public class BpfCoordinatorTest { final InOrder inOrder = inOrder(mNetd, mBpfUpstream6Map, mBpfDownstream6Map, mBpfLimitMap, mBpfStatsMap); final Ipv6UpstreamRule upstreamRule = buildTestUpstreamRule( - mobileIfIndex, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); + mobileIfIndex, DOWNSTREAM_IFINDEX, UPSTREAM_PREFIX, DOWNSTREAM_MAC); final Ipv6DownstreamRule downstreamRule = buildTestDownstreamRule( mobileIfIndex, NEIGH_A, MAC_A); - coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, mobileIfIndex); + coordinator.updateAllIpv6Rules( + mIpServer, DOWNSTREAM_IFACE_PARAMS, mobileIfIndex, UPSTREAM_PREFIXES); verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, true /* isInit */); verifyAddUpstreamRule(inOrder, upstreamRule); @@ -798,7 +854,8 @@ public class BpfCoordinatorTest { // Removing the last rule on current upstream immediately sends the cleanup stuff to BPF. updateStatsEntryForTetherOffloadGetAndClearStats( buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); - coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, 0); + coordinator.updateAllIpv6Rules( + mIpServer, DOWNSTREAM_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES); verifyRemoveDownstreamRule(inOrder, downstreamRule); verifyRemoveUpstreamRule(inOrder, upstreamRule); verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); @@ -998,11 +1055,10 @@ public class BpfCoordinatorTest { } @NonNull - private static Ipv6UpstreamRule buildTestUpstreamRule( - int upstreamIfindex, int downstreamIfindex, @NonNull MacAddress inDstMac) { - return new Ipv6UpstreamRule(upstreamIfindex, downstreamIfindex, - IPV6_ZERO_PREFIX, inDstMac, MacAddress.ALL_ZEROS_ADDRESS, - MacAddress.ALL_ZEROS_ADDRESS); + private static Ipv6UpstreamRule buildTestUpstreamRule(int upstreamIfindex, + int downstreamIfindex, @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac) { + return new Ipv6UpstreamRule(upstreamIfindex, downstreamIfindex, sourcePrefix, inDstMac, + MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); } @NonNull @@ -1054,9 +1110,10 @@ public class BpfCoordinatorTest { // Set the unlimited quota as default if the service has never applied a data limit for a // given upstream. Note that the data limit only be applied on an upstream which has rules. final Ipv6UpstreamRule rule = buildTestUpstreamRule( - mobileIfIndex, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); + mobileIfIndex, DOWNSTREAM_IFINDEX, UPSTREAM_PREFIX, DOWNSTREAM_MAC); final InOrder inOrder = inOrder(mNetd, mBpfUpstream6Map, mBpfLimitMap, mBpfStatsMap); - coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, mobileIfIndex); + coordinator.updateAllIpv6Rules( + mIpServer, DOWNSTREAM_IFACE_PARAMS, mobileIfIndex, UPSTREAM_PREFIXES); verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, true /* isInit */); verifyAddUpstreamRule(inOrder, rule); @@ -1104,28 +1161,32 @@ public class BpfCoordinatorTest { // Adding the first rule on current upstream immediately sends the quota to BPF. final Ipv6UpstreamRule ruleA = buildTestUpstreamRule( - mobileIfIndex, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); - coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, mobileIfIndex); + mobileIfIndex, DOWNSTREAM_IFINDEX, UPSTREAM_PREFIX, DOWNSTREAM_MAC); + coordinator.updateAllIpv6Rules( + mIpServer, DOWNSTREAM_IFACE_PARAMS, mobileIfIndex, UPSTREAM_PREFIXES); verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, limit, true /* isInit */); verifyAddUpstreamRule(inOrder, ruleA); inOrder.verifyNoMoreInteractions(); // Adding the second rule on current upstream does not send the quota to BPF. final Ipv6UpstreamRule ruleB = buildTestUpstreamRule( - mobileIfIndex, DOWNSTREAM_IFINDEX2, DOWNSTREAM_MAC2); - coordinator.updateAllIpv6Rules(mIpServer2, DOWNSTREAM_IFACE_PARAMS2, mobileIfIndex); + mobileIfIndex, DOWNSTREAM_IFINDEX2, UPSTREAM_PREFIX, DOWNSTREAM_MAC2); + coordinator.updateAllIpv6Rules( + mIpServer2, DOWNSTREAM_IFACE_PARAMS2, mobileIfIndex, UPSTREAM_PREFIXES); verifyAddUpstreamRule(inOrder, ruleB); verifyNeverTetherOffloadSetInterfaceQuota(inOrder); // Removing the second rule on current upstream does not send the quota to BPF. - coordinator.updateAllIpv6Rules(mIpServer2, DOWNSTREAM_IFACE_PARAMS2, 0); + coordinator.updateAllIpv6Rules( + mIpServer2, DOWNSTREAM_IFACE_PARAMS2, NO_UPSTREAM, NO_PREFIXES); verifyRemoveUpstreamRule(inOrder, ruleB); verifyNeverTetherOffloadSetInterfaceQuota(inOrder); // Removing the last rule on current upstream immediately sends the cleanup stuff to BPF. updateStatsEntryForTetherOffloadGetAndClearStats( buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); - coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, 0); + coordinator.updateAllIpv6Rules( + mIpServer, DOWNSTREAM_IFACE_PARAMS, NO_UPSTREAM, NO_PREFIXES); verifyRemoveUpstreamRule(inOrder, ruleA); verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); inOrder.verifyNoMoreInteractions(); @@ -1157,13 +1218,14 @@ public class BpfCoordinatorTest { // [1] Adding rules on the upstream Ethernet. // Note that the default data limit is applied after the first rule is added. final Ipv6UpstreamRule ethernetUpstreamRule = buildTestUpstreamRule( - ethIfIndex, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); + ethIfIndex, DOWNSTREAM_IFINDEX, UPSTREAM_PREFIX, DOWNSTREAM_MAC); final Ipv6DownstreamRule ethernetRuleA = buildTestDownstreamRule( ethIfIndex, NEIGH_A, MAC_A); final Ipv6DownstreamRule ethernetRuleB = buildTestDownstreamRule( ethIfIndex, NEIGH_B, MAC_B); - coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, ethIfIndex); + coordinator.updateAllIpv6Rules( + mIpServer, DOWNSTREAM_IFACE_PARAMS, ethIfIndex, UPSTREAM_PREFIXES); verifyTetherOffloadSetInterfaceQuota(inOrder, ethIfIndex, QUOTA_UNLIMITED, true /* isInit */); verifyAddUpstreamRule(inOrder, ethernetUpstreamRule); @@ -1174,7 +1236,9 @@ public class BpfCoordinatorTest { // [2] Update the existing rules from Ethernet to cellular. final Ipv6UpstreamRule mobileUpstreamRule = buildTestUpstreamRule( - mobileIfIndex, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); + mobileIfIndex, DOWNSTREAM_IFINDEX, UPSTREAM_PREFIX, DOWNSTREAM_MAC); + final Ipv6UpstreamRule mobileUpstreamRule2 = buildTestUpstreamRule( + mobileIfIndex, DOWNSTREAM_IFINDEX, UPSTREAM_PREFIX2, DOWNSTREAM_MAC); final Ipv6DownstreamRule mobileRuleA = buildTestDownstreamRule( mobileIfIndex, NEIGH_A, MAC_A); final Ipv6DownstreamRule mobileRuleB = buildTestDownstreamRule( @@ -1183,15 +1247,16 @@ public class BpfCoordinatorTest { buildTestTetherStatsParcel(ethIfIndex, 10, 20, 30, 40)); // Update the existing rules for upstream changes. The rules are removed and re-added one - // by one for updating upstream interface index by #tetherOffloadRuleUpdate. - coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, mobileIfIndex); + // by one for updating upstream interface index and prefixes by #tetherOffloadRuleUpdate. + coordinator.updateAllIpv6Rules( + mIpServer, DOWNSTREAM_IFACE_PARAMS, mobileIfIndex, UPSTREAM_PREFIXES2); verifyRemoveDownstreamRule(inOrder, ethernetRuleA); verifyRemoveDownstreamRule(inOrder, ethernetRuleB); verifyRemoveUpstreamRule(inOrder, ethernetUpstreamRule); verifyTetherOffloadGetAndClearStats(inOrder, ethIfIndex); verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, true /* isInit */); - verifyAddUpstreamRule(inOrder, mobileUpstreamRule); + verifyAddUpstreamRules(inOrder, Set.of(mobileUpstreamRule, mobileUpstreamRule2)); verifyAddDownstreamRule(inOrder, mobileRuleA); verifyAddDownstreamRule(inOrder, mobileRuleB); @@ -1201,7 +1266,7 @@ public class BpfCoordinatorTest { coordinator.clearAllIpv6Rules(mIpServer); verifyRemoveDownstreamRule(inOrder, mobileRuleA); verifyRemoveDownstreamRule(inOrder, mobileRuleB); - verifyRemoveUpstreamRule(inOrder, mobileUpstreamRule); + verifyRemoveUpstreamRules(inOrder, Set.of(mobileUpstreamRule, mobileUpstreamRule2)); verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); // [4] Force pushing stats update to verify that the last diff of stats is reported on all @@ -1264,8 +1329,8 @@ public class BpfCoordinatorTest { assertEquals(1, rules.size()); // The rule can't be updated. - coordinator.updateAllIpv6Rules( - mIpServer, DOWNSTREAM_IFACE_PARAMS, rule.upstreamIfindex + 1 /* new */); + coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, + rule.upstreamIfindex + 1 /* new */, UPSTREAM_PREFIXES); verifyNeverRemoveDownstreamRule(); verifyNeverAddDownstreamRule(); rules = coordinator.getIpv6DownstreamRulesForTesting().get(mIpServer); @@ -1561,12 +1626,12 @@ public class BpfCoordinatorTest { // // @param coordinator BpfCoordinator instance. // @param upstreamIfindex upstream interface index. can be the following values. - // INVALID_IFINDEX: no upstream interface + // NO_UPSTREAM: no upstream interface // UPSTREAM_IFINDEX: CELLULAR (raw ip interface) // UPSTREAM_IFINDEX2: WIFI (ethernet interface) private void setUpstreamInformationTo(final BpfCoordinator coordinator, @Nullable Integer upstreamIfindex) { - if (upstreamIfindex == INVALID_IFINDEX) { + if (upstreamIfindex == NO_UPSTREAM) { coordinator.updateUpstreamNetworkState(null); return; } @@ -1706,7 +1771,8 @@ public class BpfCoordinatorTest { final BpfCoordinator coordinator = makeBpfCoordinator(); coordinator.maybeAddUpstreamToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE); - coordinator.updateAllIpv6Rules(mIpServer, DOWNSTREAM_IFACE_PARAMS, UPSTREAM_IFINDEX); + coordinator.updateAllIpv6Rules( + mIpServer, DOWNSTREAM_IFACE_PARAMS, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES); verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)), eq(new TetherDevValue(UPSTREAM_IFINDEX))); verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX)), @@ -1715,7 +1781,8 @@ public class BpfCoordinatorTest { // Adding the second downstream, only the second downstream ifindex is added to DevMap, // the existing upstream ifindex won't be added again. - coordinator.updateAllIpv6Rules(mIpServer2, DOWNSTREAM_IFACE_PARAMS2, UPSTREAM_IFINDEX); + coordinator.updateAllIpv6Rules( + mIpServer2, DOWNSTREAM_IFACE_PARAMS2, UPSTREAM_IFINDEX, UPSTREAM_PREFIXES); verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX2)), eq(new TetherDevValue(DOWNSTREAM_IFINDEX2))); verify(mBpfDevMap, never()).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)), @@ -1996,6 +2063,11 @@ public class BpfCoordinatorTest { 100 /* nonzero, CT_NEW */); } + private static long prefixToLong(IpPrefix prefix) { + byte[] prefixBytes = Arrays.copyOf(prefix.getRawAddress(), 8); + return ByteBuffer.wrap(prefixBytes).getLong(); + } + void checkRule4ExistInUpstreamDownstreamMap() throws Exception { assertEquals(UPSTREAM4_RULE_VALUE_A, mBpfUpstream4Map.getValue(UPSTREAM4_RULE_KEY_A)); assertEquals(DOWNSTREAM4_RULE_VALUE_A, mBpfDownstream4Map.getValue( @@ -2113,7 +2185,7 @@ public class BpfCoordinatorTest { // [3] Switch upstream from the first upstream (rawip, bpf supported) to no upstream. Clear // all rules. - setUpstreamInformationTo(coordinator, INVALID_IFINDEX); + setUpstreamInformationTo(coordinator, NO_UPSTREAM); checkRule4NotExistInUpstreamDownstreamMap(); // Client information should be not deleted. @@ -2180,14 +2252,15 @@ public class BpfCoordinatorTest { public void testIpv6ForwardingRuleToString() throws Exception { final Ipv6DownstreamRule downstreamRule = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A); - assertEquals("upstreamIfindex: 1001, downstreamIfindex: 2001, address: 2001:db8::1, " + assertEquals("upstreamIfindex: 1001, downstreamIfindex: 2001, address: 2001:db8:0:1234::1, " + "srcMac: 12:34:56:78:90:ab, dstMac: 00:00:00:00:00:0a", downstreamRule.toString()); final Ipv6UpstreamRule upstreamRule = buildTestUpstreamRule( - UPSTREAM_IFINDEX, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); - assertEquals("upstreamIfindex: 1001, downstreamIfindex: 2001, sourcePrefix: ::/64, " - + "inDstMac: 12:34:56:78:90:ab, outSrcMac: 00:00:00:00:00:00, " - + "outDstMac: 00:00:00:00:00:00", upstreamRule.toString()); + UPSTREAM_IFINDEX, DOWNSTREAM_IFINDEX, UPSTREAM_PREFIX, DOWNSTREAM_MAC); + assertEquals("upstreamIfindex: 1001, downstreamIfindex: 2001, " + + "sourcePrefix: 2001:db8:0:1234::/64, inDstMac: 12:34:56:78:90:ab, " + + "outSrcMac: 00:00:00:00:00:00, outDstMac: 00:00:00:00:00:00", + upstreamRule.toString()); } private void verifyDump(@NonNull final BpfCoordinator coordinator) { @@ -2237,8 +2310,9 @@ public class BpfCoordinatorTest { final Ipv6DownstreamRule rule = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A); mBpfDownstream6Map.insertEntry(rule.makeTetherDownstream6Key(), rule.makeTether6Value()); + final long prefix64 = prefixToLong(UPSTREAM_PREFIX); final TetherUpstream6Key upstream6Key = new TetherUpstream6Key(DOWNSTREAM_IFINDEX, - DOWNSTREAM_MAC, 0); + DOWNSTREAM_MAC, prefix64); final Tether6Value upstream6Value = new Tether6Value(UPSTREAM_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS, ETH_P_IPV6, NetworkStackConstants.ETHER_MTU); diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c index 35b8eea538..90f96a14c8 100644 --- a/bpf_progs/offload.c +++ b/bpf_progs/offload.c @@ -198,7 +198,8 @@ static inline __always_inline int do_forward6(struct __sk_buff* skb, TetherUpstream6Key ku = { .iif = skb->ifindex, - .src64 = 0, + // Retrieve the first 64 bits of the source IPv6 address in network order + .src64 = *(uint64_t*)&(ip6->saddr.s6_addr32[0]), }; if (is_ethernet) __builtin_memcpy(stream.down ? kd.dstMac : ku.dstMac, eth->h_dest, ETH_ALEN);