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