tethering: DAD Proxy Daemon
DAD proxy daemon responsible for forwarding NS/NA between tethered iface and upstream iface. Change-Id: I2e58e10e7fa7dba6a6f63ad03b000549f3afc37e
This commit is contained in:
@@ -14,8 +14,22 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
java_defaults {
|
||||
name: "TetheringPrivilegedTestsJniDefaults",
|
||||
jni_libs: [
|
||||
"libdexmakerjvmtiagent",
|
||||
"libstaticjvmtiagent",
|
||||
"libtetherutilsjni",
|
||||
],
|
||||
jni_uses_sdk_apis: true,
|
||||
visibility: ["//visibility:private"],
|
||||
}
|
||||
|
||||
android_test {
|
||||
name: "TetheringPrivilegedTests",
|
||||
defaults: [
|
||||
"TetheringPrivilegedTestsJniDefaults",
|
||||
],
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
"src/**/*.kt",
|
||||
@@ -23,8 +37,13 @@ android_test {
|
||||
certificate: "networkstack",
|
||||
platform_apis: true,
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"device-tests",
|
||||
"mts",
|
||||
],
|
||||
static_libs: [
|
||||
"androidx.test.rules",
|
||||
"net-tests-utils",
|
||||
"TetheringApiCurrentLib",
|
||||
],
|
||||
compile_multilib: "both",
|
||||
}
|
||||
|
||||
338
Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
Normal file
338
Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net.ip;
|
||||
|
||||
import static android.system.OsConstants.IPPROTO_ICMPV6;
|
||||
import static android.system.OsConstants.IPPROTO_TCP;
|
||||
|
||||
import static com.android.internal.util.BitUtils.uint16;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.net.INetd;
|
||||
import android.net.InetAddresses;
|
||||
import android.net.MacAddress;
|
||||
import android.net.TestNetworkInterface;
|
||||
import android.net.TestNetworkManager;
|
||||
import android.net.util.InterfaceParams;
|
||||
import android.net.util.IpUtils;
|
||||
import android.net.util.TetheringUtils;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.testutils.TapPacketReader;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class DadProxyTest {
|
||||
private static final int DATA_BUFFER_LEN = 4096;
|
||||
private static final int PACKET_TIMEOUT_MS = 5_000;
|
||||
|
||||
// TODO: make NetworkStackConstants accessible to this test and use the constant from there.
|
||||
private static final int ETHER_SRC_ADDR_OFFSET = 6;
|
||||
|
||||
private DadProxy mProxy;
|
||||
TestNetworkInterface mUpstreamTestIface, mTetheredTestIface;
|
||||
private InterfaceParams mUpstreamParams, mTetheredParams;
|
||||
private HandlerThread mHandlerThread;
|
||||
private Handler mHandler;
|
||||
private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader;
|
||||
private FileDescriptor mUpstreamTapFd, mTetheredTapFd;
|
||||
|
||||
private static INetd sNetd;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupOnce() {
|
||||
System.loadLibrary("tetherutilsjni");
|
||||
|
||||
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
|
||||
final IBinder netdIBinder =
|
||||
(IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
|
||||
sNetd = INetd.Stub.asInterface(netdIBinder);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mHandlerThread = new HandlerThread(getClass().getSimpleName());
|
||||
mHandlerThread.start();
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
|
||||
setupTapInterfaces();
|
||||
|
||||
// Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads.
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
|
||||
DadProxy mProxy = setupProxy();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (mHandlerThread != null) {
|
||||
mHandler.post(mUpstreamPacketReader::stop); // Also closes the socket
|
||||
mHandler.post(mTetheredPacketReader::stop); // Also closes the socket
|
||||
mUpstreamTapFd = null;
|
||||
mTetheredTapFd = null;
|
||||
mHandlerThread.quitSafely();
|
||||
}
|
||||
|
||||
if (mTetheredParams != null) {
|
||||
sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
|
||||
}
|
||||
if (mUpstreamParams != null) {
|
||||
sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
|
||||
}
|
||||
|
||||
if (mUpstreamTestIface != null) {
|
||||
try {
|
||||
Os.close(mUpstreamTestIface.getFileDescriptor().getFileDescriptor());
|
||||
} catch (ErrnoException e) { }
|
||||
}
|
||||
|
||||
if (mTetheredTestIface != null) {
|
||||
try {
|
||||
Os.close(mTetheredTestIface.getFileDescriptor().getFileDescriptor());
|
||||
} catch (ErrnoException e) { }
|
||||
}
|
||||
}
|
||||
|
||||
private TestNetworkInterface setupTapInterface() {
|
||||
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
|
||||
AtomicReference<TestNetworkInterface> iface = new AtomicReference<>();
|
||||
|
||||
inst.getUiAutomation().adoptShellPermissionIdentity();
|
||||
try {
|
||||
final TestNetworkManager tnm = (TestNetworkManager) inst.getContext().getSystemService(
|
||||
Context.TEST_NETWORK_SERVICE);
|
||||
iface.set(tnm.createTapInterface());
|
||||
} finally {
|
||||
inst.getUiAutomation().dropShellPermissionIdentity();
|
||||
}
|
||||
|
||||
return iface.get();
|
||||
}
|
||||
|
||||
private void setupTapInterfaces() {
|
||||
// Create upstream test iface.
|
||||
mUpstreamTestIface = setupTapInterface();
|
||||
mUpstreamParams = InterfaceParams.getByName(mUpstreamTestIface.getInterfaceName());
|
||||
assertNotNull(mUpstreamParams);
|
||||
mUpstreamTapFd = mUpstreamTestIface.getFileDescriptor().getFileDescriptor();
|
||||
mUpstreamPacketReader = new TapPacketReader(mHandler, mUpstreamTapFd,
|
||||
DATA_BUFFER_LEN);
|
||||
mHandler.post(mUpstreamPacketReader::start);
|
||||
|
||||
// Create tethered test iface.
|
||||
mTetheredTestIface = setupTapInterface();
|
||||
mTetheredParams = InterfaceParams.getByName(mTetheredTestIface.getInterfaceName());
|
||||
assertNotNull(mTetheredParams);
|
||||
mTetheredTapFd = mTetheredTestIface.getFileDescriptor().getFileDescriptor();
|
||||
mTetheredPacketReader = new TapPacketReader(mHandler, mTetheredTapFd,
|
||||
DATA_BUFFER_LEN);
|
||||
mHandler.post(mTetheredPacketReader::start);
|
||||
}
|
||||
|
||||
private static final int IPV6_HEADER_LEN = 40;
|
||||
private static final int ETH_HEADER_LEN = 14;
|
||||
private static final int ICMPV6_NA_NS_LEN = 24;
|
||||
private static final int LL_TARGET_OPTION_LEN = 8;
|
||||
private static final int ICMPV6_CHECKSUM_OFFSET = 2;
|
||||
private static final int ETHER_TYPE_IPV6 = 0x86dd;
|
||||
|
||||
// TODO: move the IpUtils code to frameworks/lib/net and link it statically.
|
||||
private static int checksumFold(int sum) {
|
||||
while (sum > 0xffff) {
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// TODO: move the IpUtils code to frameworks/lib/net and link it statically.
|
||||
private static short checksumAdjust(short checksum, short oldWord, short newWord) {
|
||||
checksum = (short) ~checksum;
|
||||
int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord));
|
||||
return (short) ~tempSum;
|
||||
}
|
||||
|
||||
// TODO: move the IpUtils code to frameworks/lib/net and link it statically.
|
||||
private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset,
|
||||
int transportLen) {
|
||||
// The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses
|
||||
// 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental
|
||||
// checksum adjustment for the change in the next header byte.
|
||||
short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen);
|
||||
return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6);
|
||||
}
|
||||
|
||||
private static ByteBuffer createDadPacket(int type) {
|
||||
// Refer to buildArpPacket()
|
||||
int icmpLen = ICMPV6_NA_NS_LEN
|
||||
+ (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT
|
||||
? LL_TARGET_OPTION_LEN : 0);
|
||||
final ByteBuffer buf = ByteBuffer.allocate(icmpLen + IPV6_HEADER_LEN + ETH_HEADER_LEN);
|
||||
|
||||
// Ethernet header.
|
||||
final MacAddress srcMac = MacAddress.fromString("33:33:ff:66:77:88");
|
||||
buf.put(srcMac.toByteArray());
|
||||
final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06");
|
||||
buf.put(dstMac.toByteArray());
|
||||
buf.putShort((short) ETHER_TYPE_IPV6);
|
||||
|
||||
// IPv6 header
|
||||
byte[] version = {(byte) 0x60, 0x00, 0x00, 0x00};
|
||||
buf.put(version); // Version
|
||||
buf.putShort((byte) icmpLen); // Length
|
||||
buf.put((byte) IPPROTO_ICMPV6); // Next header
|
||||
buf.put((byte) 0xff); // Hop limit
|
||||
|
||||
final byte[] target =
|
||||
InetAddresses.parseNumericAddress("fe80::1122:3344:5566:7788").getAddress();
|
||||
final byte[] src;
|
||||
final byte[] dst;
|
||||
if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION) {
|
||||
src = InetAddresses.parseNumericAddress("::").getAddress();
|
||||
dst = InetAddresses.parseNumericAddress("ff02::1:ff66:7788").getAddress();
|
||||
} else {
|
||||
src = target;
|
||||
dst = TetheringUtils.ALL_NODES;
|
||||
}
|
||||
buf.put(src);
|
||||
buf.put(dst);
|
||||
|
||||
// ICMPv6 Header
|
||||
buf.put((byte) type); // Type
|
||||
buf.put((byte) 0x00); // Code
|
||||
buf.putShort((short) 0); // Checksum
|
||||
buf.putInt(0); // Reserved
|
||||
buf.put(target);
|
||||
|
||||
if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT) {
|
||||
//NA packet has LL target address
|
||||
//ICMPv6 Option
|
||||
buf.put((byte) 0x02); // Type
|
||||
buf.put((byte) 0x01); // Length
|
||||
byte[] ll_target = MacAddress.fromString("01:02:03:04:05:06").toByteArray();
|
||||
buf.put(ll_target);
|
||||
}
|
||||
|
||||
// Populate checksum field
|
||||
final int transportOffset = ETH_HEADER_LEN + IPV6_HEADER_LEN;
|
||||
final short checksum = icmpv6Checksum(buf, ETH_HEADER_LEN, transportOffset, icmpLen);
|
||||
buf.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum);
|
||||
|
||||
buf.flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
private DadProxy setupProxy() throws Exception {
|
||||
DadProxy proxy = new DadProxy(mHandler, mTetheredParams);
|
||||
mHandler.post(() -> proxy.setUpstreamIface(mUpstreamParams));
|
||||
|
||||
// Upstream iface is added to local network to simplify test case.
|
||||
// Otherwise the test needs to create and destroy a network for the upstream iface.
|
||||
sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
|
||||
sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
// TODO: change to assert.
|
||||
private boolean waitForPacket(ByteBuffer packet, TapPacketReader reader) {
|
||||
byte[] p;
|
||||
|
||||
while ((p = reader.popPacket(PACKET_TIMEOUT_MS)) != null) {
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(p);
|
||||
|
||||
if (buffer.compareTo(packet) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateDstMac(ByteBuffer buf, MacAddress mac) {
|
||||
buf.put(mac.toByteArray());
|
||||
buf.rewind();
|
||||
}
|
||||
private void updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams) {
|
||||
buf.position(ETHER_SRC_ADDR_OFFSET);
|
||||
buf.put(ifaceParams.macAddr.toByteArray());
|
||||
buf.rewind();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaForwardingFromUpstreamToTether() throws Exception {
|
||||
ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
|
||||
|
||||
mUpstreamPacketReader.sendResponse(na);
|
||||
updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
|
||||
updateSrcMac(na, mTetheredParams);
|
||||
assertTrue(waitForPacket(na, mTetheredPacketReader));
|
||||
}
|
||||
|
||||
@Test
|
||||
// TODO: remove test once DAD works in both directions.
|
||||
public void testNaForwardingFromTetherToUpstream() throws Exception {
|
||||
ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
|
||||
|
||||
mTetheredPacketReader.sendResponse(na);
|
||||
updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
|
||||
updateSrcMac(na, mTetheredParams);
|
||||
assertFalse(waitForPacket(na, mUpstreamPacketReader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNsForwardingFromTetherToUpstream() throws Exception {
|
||||
ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
|
||||
|
||||
mTetheredPacketReader.sendResponse(ns);
|
||||
updateSrcMac(ns, mUpstreamParams);
|
||||
assertTrue(waitForPacket(ns, mUpstreamPacketReader));
|
||||
}
|
||||
|
||||
@Test
|
||||
// TODO: remove test once DAD works in both directions.
|
||||
public void testNsForwardingFromUpstreamToTether() throws Exception {
|
||||
ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
|
||||
|
||||
mUpstreamPacketReader.sendResponse(ns);
|
||||
updateSrcMac(ns, mUpstreamParams);
|
||||
assertFalse(waitForPacket(ns, mTetheredPacketReader));
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,7 @@ import android.net.util.InterfaceParams;
|
||||
import android.net.util.InterfaceSet;
|
||||
import android.net.util.PrefixUtils;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.test.TestLooper;
|
||||
@@ -100,8 +101,12 @@ import com.android.networkstack.tethering.BpfCoordinator;
|
||||
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
||||
import com.android.networkstack.tethering.PrivateAddressCoordinator;
|
||||
import com.android.networkstack.tethering.TetheringConfiguration;
|
||||
import com.android.testutils.DevSdkIgnoreRule;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -120,6 +125,9 @@ import java.util.List;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class IpServerTest {
|
||||
@Rule
|
||||
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||
|
||||
private static final String IFACE_NAME = "testnet1";
|
||||
private static final String UPSTREAM_IFACE = "upstream0";
|
||||
private static final String UPSTREAM_IFACE2 = "upstream1";
|
||||
@@ -132,6 +140,11 @@ public class IpServerTest {
|
||||
|
||||
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
|
||||
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
|
||||
private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams(
|
||||
UPSTREAM_IFACE, UPSTREAM_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
|
||||
private static final InterfaceParams UPSTREAM_IFACE_PARAMS2 = new InterfaceParams(
|
||||
UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.ALL_ZEROS_ADDRESS,
|
||||
1500 /* defaultMtu */);
|
||||
|
||||
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
|
||||
|
||||
@@ -142,6 +155,7 @@ public class IpServerTest {
|
||||
@Mock private IpServer.Callback mCallback;
|
||||
@Mock private SharedLog mSharedLog;
|
||||
@Mock private IDhcpServer mDhcpServer;
|
||||
@Mock private DadProxy mDadProxy;
|
||||
@Mock private RouterAdvertisementDaemon mRaDaemon;
|
||||
@Mock private IpNeighborMonitor mIpNeighborMonitor;
|
||||
@Mock private IpServer.Dependencies mDependencies;
|
||||
@@ -165,8 +179,11 @@ public class IpServerTest {
|
||||
|
||||
private void initStateMachine(int interfaceType, boolean usingLegacyDhcp,
|
||||
boolean usingBpfOffload) throws Exception {
|
||||
when(mDependencies.getDadProxy(any(), any())).thenReturn(mDadProxy);
|
||||
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
|
||||
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
|
||||
when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS);
|
||||
when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2);
|
||||
|
||||
when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX);
|
||||
when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2);
|
||||
@@ -1103,4 +1120,78 @@ public class IpServerTest {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
|
||||
public void dadProxyUpdates() throws Exception {
|
||||
InOrder inOrder = inOrder(mDadProxy);
|
||||
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
|
||||
|
||||
// Add an upstream without IPv6.
|
||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(null);
|
||||
|
||||
// Add IPv6 to the upstream.
|
||||
LinkProperties lp = new LinkProperties();
|
||||
lp.setInterfaceName(UPSTREAM_IFACE);
|
||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
|
||||
|
||||
// Change upstream.
|
||||
// New linkproperties is needed, otherwise changing the iface has no impact.
|
||||
LinkProperties lp2 = new LinkProperties();
|
||||
lp2.setInterfaceName(UPSTREAM_IFACE2);
|
||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0);
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS2);
|
||||
|
||||
// Lose IPv6 on the upstream...
|
||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, null, 0);
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(null);
|
||||
|
||||
// ... and regain it on a different upstream.
|
||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
|
||||
|
||||
// Lose upstream.
|
||||
dispatchTetherConnectionChanged(null, null, 0);
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(null);
|
||||
|
||||
// Regain upstream.
|
||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
|
||||
|
||||
// Stop tethering.
|
||||
mIpServer.stop();
|
||||
mLooper.dispatchAll();
|
||||
}
|
||||
|
||||
private void checkDadProxyEnabled(boolean expectEnabled) throws Exception {
|
||||
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
|
||||
InOrder inOrder = inOrder(mDadProxy);
|
||||
// Add IPv6 to the upstream.
|
||||
LinkProperties lp = new LinkProperties();
|
||||
lp.setInterfaceName(UPSTREAM_IFACE);
|
||||
if (expectEnabled) {
|
||||
inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
|
||||
} else {
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
}
|
||||
// Stop tethering.
|
||||
mIpServer.stop();
|
||||
mLooper.dispatchAll();
|
||||
if (expectEnabled) {
|
||||
inOrder.verify(mDadProxy).stop();
|
||||
}
|
||||
else {
|
||||
verify(mDependencies, never()).getDadProxy(any(), any());
|
||||
}
|
||||
}
|
||||
@Test @IgnoreAfter(Build.VERSION_CODES.R)
|
||||
public void testDadProxyUpdates_DisabledUpToR() throws Exception {
|
||||
checkDadProxyEnabled(false);
|
||||
}
|
||||
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
|
||||
public void testDadProxyUpdates_EnabledAfterR() throws Exception {
|
||||
checkDadProxyEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ import android.net.TetheringRequestParcel;
|
||||
import android.net.dhcp.DhcpServerCallbacks;
|
||||
import android.net.dhcp.DhcpServingParamsParcel;
|
||||
import android.net.dhcp.IDhcpServer;
|
||||
import android.net.ip.DadProxy;
|
||||
import android.net.ip.IpNeighborMonitor;
|
||||
import android.net.ip.IpServer;
|
||||
import android.net.ip.RouterAdvertisementDaemon;
|
||||
@@ -196,6 +197,7 @@ public class TetheringTest {
|
||||
@Mock private CarrierConfigManager mCarrierConfigManager;
|
||||
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
|
||||
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
|
||||
@Mock private DadProxy mDadProxy;
|
||||
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
|
||||
@Mock private IpNeighborMonitor mIpNeighborMonitor;
|
||||
@Mock private IDhcpServer mDhcpServer;
|
||||
@@ -279,6 +281,12 @@ public class TetheringTest {
|
||||
}
|
||||
|
||||
public class MockIpServerDependencies extends IpServer.Dependencies {
|
||||
@Override
|
||||
public DadProxy getDadProxy(
|
||||
Handler handler, InterfaceParams ifParams) {
|
||||
return mDadProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
|
||||
InterfaceParams ifParams) {
|
||||
@@ -832,6 +840,7 @@ public class TetheringTest {
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
verify(mDadProxy, never()).setUpstreamIface(notNull());
|
||||
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||
any(), any());
|
||||
@@ -858,6 +867,8 @@ public class TetheringTest {
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
// TODO: add interfaceParams to compare in verify.
|
||||
verify(mDadProxy, times(1)).setUpstreamIface(notNull());
|
||||
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
|
||||
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
||||
}
|
||||
@@ -874,6 +885,7 @@ public class TetheringTest {
|
||||
any(), any());
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
verify(mDadProxy, times(1)).setUpstreamIface(notNull());
|
||||
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
|
||||
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
||||
}
|
||||
@@ -891,6 +903,7 @@ public class TetheringTest {
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
verify(mDadProxy, times(1)).setUpstreamIface(notNull());
|
||||
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
|
||||
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user