Move runDhcp to TetheringTester
This is a no-op CL which add TetheringTester and move runDhcp related logic to it. Test: atest EthernetTetheringTest Change-Id: Ib1c5647b2bd5a1b27c976450d3aa265aff8f5b70
This commit is contained in:
@@ -47,14 +47,10 @@ import android.net.EthernetManager.TetheredInterfaceRequest;
|
||||
import android.net.TetheringManager.StartTetheringCallback;
|
||||
import android.net.TetheringManager.TetheringEventCallback;
|
||||
import android.net.TetheringManager.TetheringRequest;
|
||||
import android.net.dhcp.DhcpAckPacket;
|
||||
import android.net.dhcp.DhcpOfferPacket;
|
||||
import android.net.dhcp.DhcpPacket;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
@@ -75,7 +71,6 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
@@ -97,15 +92,6 @@ public class EthernetTetheringTest {
|
||||
|
||||
private static final String TAG = EthernetTetheringTest.class.getSimpleName();
|
||||
private static final int TIMEOUT_MS = 5000;
|
||||
private static final int PACKET_READ_TIMEOUT_MS = 100;
|
||||
private static final int DHCP_DISCOVER_ATTEMPTS = 10;
|
||||
private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
|
||||
DhcpPacket.DHCP_SUBNET_MASK,
|
||||
DhcpPacket.DHCP_ROUTER,
|
||||
DhcpPacket.DHCP_DNS_SERVER,
|
||||
DhcpPacket.DHCP_LEASE_TIME,
|
||||
};
|
||||
private static final String DHCP_HOSTNAME = "testhostname";
|
||||
private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8");
|
||||
private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64");
|
||||
private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8");
|
||||
@@ -254,11 +240,12 @@ public class EthernetTetheringTest {
|
||||
|
||||
FileDescriptor fd = mDownstreamIface.getFileDescriptor().getFileDescriptor();
|
||||
mDownstreamReader = makePacketReader(fd, getMTU(mDownstreamIface));
|
||||
DhcpResults dhcpResults = runDhcp(fd, client1);
|
||||
TetheringTester tester = new TetheringTester(mDownstreamReader);
|
||||
DhcpResults dhcpResults = tester.runDhcp(client1);
|
||||
assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
|
||||
|
||||
try {
|
||||
runDhcp(fd, client2);
|
||||
tester.runDhcp(client2);
|
||||
fail("Only one client should get an IP address");
|
||||
} catch (TimeoutException expected) { }
|
||||
|
||||
@@ -558,38 +545,16 @@ public class EthernetTetheringTest {
|
||||
FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
|
||||
mDownstreamReader = makePacketReader(fd, mtu);
|
||||
mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName());
|
||||
checkTetheredClientCallbacks(fd);
|
||||
checkTetheredClientCallbacks(mDownstreamReader);
|
||||
}
|
||||
|
||||
private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception {
|
||||
// We have to retransmit DHCP requests because IpServer declares itself to be ready before
|
||||
// its DhcpServer is actually started. TODO: fix this race and remove this loop.
|
||||
DhcpPacket offerPacket = null;
|
||||
for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) {
|
||||
Log.d(TAG, "Sending DHCP discover");
|
||||
sendDhcpDiscover(fd, clientMacAddr);
|
||||
offerPacket = getNextDhcpPacket();
|
||||
if (offerPacket instanceof DhcpOfferPacket) break;
|
||||
}
|
||||
if (!(offerPacket instanceof DhcpOfferPacket)) {
|
||||
throw new TimeoutException("No DHCPOFFER received on interface within timeout");
|
||||
}
|
||||
|
||||
sendDhcpRequest(fd, offerPacket, clientMacAddr);
|
||||
DhcpPacket ackPacket = getNextDhcpPacket();
|
||||
if (!(ackPacket instanceof DhcpAckPacket)) {
|
||||
throw new TimeoutException("No DHCPACK received on interface within timeout");
|
||||
}
|
||||
|
||||
return ackPacket.toDhcpResults();
|
||||
}
|
||||
|
||||
private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
|
||||
private void checkTetheredClientCallbacks(TapPacketReader packetReader) throws Exception {
|
||||
// Create a fake client.
|
||||
byte[] clientMacAddr = new byte[6];
|
||||
new Random().nextBytes(clientMacAddr);
|
||||
|
||||
DhcpResults dhcpResults = runDhcp(fd, clientMacAddr);
|
||||
TetheringTester tester = new TetheringTester(packetReader);
|
||||
DhcpResults dhcpResults = tester.runDhcp(clientMacAddr);
|
||||
|
||||
final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
|
||||
assertEquals(1, clients.size());
|
||||
@@ -602,7 +567,7 @@ public class EthernetTetheringTest {
|
||||
// Check the hostname.
|
||||
assertEquals(1, client.getAddresses().size());
|
||||
TetheredClient.AddressInfo info = client.getAddresses().get(0);
|
||||
assertEquals(DHCP_HOSTNAME, info.getHostname());
|
||||
assertEquals(TetheringTester.DHCP_HOSTNAME, info.getHostname());
|
||||
|
||||
// Check the address is the one that was handed out in the DHCP ACK.
|
||||
assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
|
||||
@@ -615,18 +580,6 @@ public class EthernetTetheringTest {
|
||||
assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10);
|
||||
}
|
||||
|
||||
private DhcpPacket getNextDhcpPacket() throws ParseException {
|
||||
byte[] packet;
|
||||
while ((packet = mDownstreamReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) {
|
||||
try {
|
||||
return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2);
|
||||
} catch (DhcpPacket.ParseException e) {
|
||||
// Not a DHCP packet. Continue.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback {
|
||||
private final Handler mHandler;
|
||||
private final EthernetManager mEm;
|
||||
@@ -670,31 +623,6 @@ public class EthernetTetheringTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception {
|
||||
ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2,
|
||||
new Random().nextInt() /* transactionId */, (short) 0 /* secs */,
|
||||
macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS,
|
||||
false /* rapid commit */, DHCP_HOSTNAME);
|
||||
sendPacket(fd, packet);
|
||||
}
|
||||
|
||||
private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress)
|
||||
throws Exception {
|
||||
DhcpResults results = offerPacket.toDhcpResults();
|
||||
Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress();
|
||||
Inet4Address serverIdentifier = results.serverAddress;
|
||||
ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2,
|
||||
0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */,
|
||||
false /* broadcast */, macAddress, clientIp /* requestedIpAddress */,
|
||||
serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME);
|
||||
sendPacket(fd, packet);
|
||||
}
|
||||
|
||||
private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception {
|
||||
assertNotNull("Only tests on virtual interfaces can send packets", fd);
|
||||
Os.write(fd, packet);
|
||||
}
|
||||
|
||||
public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) {
|
||||
// Check all fields except the deprecation and expiry times.
|
||||
String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2);
|
||||
|
||||
155
Tethering/tests/integration/src/android/net/TetheringTester.java
Normal file
155
Tethering/tests/integration/src/android/net/TetheringTester.java
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.net.dhcp.DhcpAckPacket;
|
||||
import android.net.dhcp.DhcpOfferPacket;
|
||||
import android.net.dhcp.DhcpPacket;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.testutils.TapPacketReader;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A class simulate tethered client. When caller create TetheringTester, it would connect to
|
||||
* tethering module that do the dhcp and slaac to obtain ipv4 and ipv6 address. Then caller can
|
||||
* send/receive packets by this class.
|
||||
*/
|
||||
public final class TetheringTester {
|
||||
private static final String TAG = TetheringTester.class.getSimpleName();
|
||||
private static final int PACKET_READ_TIMEOUT_MS = 100;
|
||||
private static final int DHCP_DISCOVER_ATTEMPTS = 10;
|
||||
private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
|
||||
DhcpPacket.DHCP_SUBNET_MASK,
|
||||
DhcpPacket.DHCP_ROUTER,
|
||||
DhcpPacket.DHCP_DNS_SERVER,
|
||||
DhcpPacket.DHCP_LEASE_TIME,
|
||||
};
|
||||
|
||||
public static final String DHCP_HOSTNAME = "testhostname";
|
||||
|
||||
private final ArrayMap<MacAddress, TetheredDevice> mTetheredDevices;
|
||||
private final TapPacketReader mDownstreamReader;
|
||||
|
||||
public TetheringTester(TapPacketReader downstream) {
|
||||
if (downstream == null) fail("Downstream reader could not be NULL");
|
||||
|
||||
mDownstreamReader = downstream;
|
||||
mTetheredDevices = new ArrayMap<>();
|
||||
}
|
||||
|
||||
public TetheredDevice createTetheredDevice(MacAddress macAddr) throws Exception {
|
||||
if (mTetheredDevices.get(macAddr) != null) {
|
||||
fail("Tethered device already created");
|
||||
}
|
||||
|
||||
TetheredDevice tethered = new TetheredDevice(macAddr);
|
||||
mTetheredDevices.put(macAddr, tethered);
|
||||
|
||||
return tethered;
|
||||
}
|
||||
|
||||
public class TetheredDevice {
|
||||
private final MacAddress mMacAddr;
|
||||
|
||||
public final Inet4Address mIpv4Addr;
|
||||
|
||||
private TetheredDevice(MacAddress mac) throws Exception {
|
||||
mMacAddr = mac;
|
||||
|
||||
DhcpResults dhcpResults = runDhcp(mMacAddr.toByteArray());
|
||||
mIpv4Addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/** Simulate dhcp client to obtain ipv4 address. */
|
||||
public DhcpResults runDhcp(byte[] clientMacAddr)
|
||||
throws Exception {
|
||||
// We have to retransmit DHCP requests because IpServer declares itself to be ready before
|
||||
// its DhcpServer is actually started. TODO: fix this race and remove this loop.
|
||||
DhcpPacket offerPacket = null;
|
||||
for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) {
|
||||
Log.d(TAG, "Sending DHCP discover");
|
||||
sendDhcpDiscover(clientMacAddr);
|
||||
offerPacket = getNextDhcpPacket();
|
||||
if (offerPacket instanceof DhcpOfferPacket) break;
|
||||
}
|
||||
if (!(offerPacket instanceof DhcpOfferPacket)) {
|
||||
throw new TimeoutException("No DHCPOFFER received on interface within timeout");
|
||||
}
|
||||
|
||||
sendDhcpRequest(offerPacket, clientMacAddr);
|
||||
DhcpPacket ackPacket = getNextDhcpPacket();
|
||||
if (!(ackPacket instanceof DhcpAckPacket)) {
|
||||
throw new TimeoutException("No DHCPACK received on interface within timeout");
|
||||
}
|
||||
|
||||
return ackPacket.toDhcpResults();
|
||||
}
|
||||
|
||||
private void sendDhcpDiscover(byte[] macAddress) throws Exception {
|
||||
ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2,
|
||||
new Random().nextInt() /* transactionId */, (short) 0 /* secs */,
|
||||
macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS,
|
||||
false /* rapid commit */, DHCP_HOSTNAME);
|
||||
mDownstreamReader.sendResponse(packet);
|
||||
}
|
||||
|
||||
private void sendDhcpRequest(DhcpPacket offerPacket, byte[] macAddress)
|
||||
throws Exception {
|
||||
DhcpResults results = offerPacket.toDhcpResults();
|
||||
Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress();
|
||||
Inet4Address serverIdentifier = results.serverAddress;
|
||||
ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2,
|
||||
0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */,
|
||||
false /* broadcast */, macAddress, clientIp /* requestedIpAddress */,
|
||||
serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME);
|
||||
mDownstreamReader.sendResponse(packet);
|
||||
}
|
||||
|
||||
private DhcpPacket getNextDhcpPacket() {
|
||||
return getNextMatchedPacket((p) -> {
|
||||
try {
|
||||
return DhcpPacket.decodeFullPacket(p, p.length, DhcpPacket.ENCAP_L2);
|
||||
} catch (DhcpPacket.ParseException e) {
|
||||
// Not a DHCP packet. Continue.
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private <R> R getNextMatchedPacket(Function<byte[], R> match) {
|
||||
byte[] packet;
|
||||
R result;
|
||||
while ((packet = mDownstreamReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) {
|
||||
result = match.apply(packet);
|
||||
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user