1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net; 18 19 import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY; 20 import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST; 21 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN; 22 import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST; 23 24 import static org.junit.Assert.assertNotNull; 25 import static org.junit.Assert.fail; 26 27 import android.net.dhcp.DhcpAckPacket; 28 import android.net.dhcp.DhcpOfferPacket; 29 import android.net.dhcp.DhcpPacket; 30 import android.util.ArrayMap; 31 import android.util.Log; 32 33 import androidx.annotation.Nullable; 34 35 import com.android.networkstack.arp.ArpPacket; 36 import com.android.testutils.TapPacketReader; 37 38 import java.net.Inet4Address; 39 import java.nio.ByteBuffer; 40 import java.util.Random; 41 import java.util.concurrent.TimeoutException; 42 import java.util.function.Predicate; 43 44 /** 45 * A class simulate tethered client. When caller create TetheringTester, it would connect to 46 * tethering module that do the dhcp and slaac to obtain ipv4 and ipv6 address. Then caller can 47 * send/receive packets by this class. 48 */ 49 public final class TetheringTester { 50 private static final String TAG = TetheringTester.class.getSimpleName(); 51 private static final int PACKET_READ_TIMEOUT_MS = 100; 52 private static final int DHCP_DISCOVER_ATTEMPTS = 10; 53 private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] { 54 DhcpPacket.DHCP_SUBNET_MASK, 55 DhcpPacket.DHCP_ROUTER, 56 DhcpPacket.DHCP_DNS_SERVER, 57 DhcpPacket.DHCP_LEASE_TIME, 58 }; 59 60 public static final String DHCP_HOSTNAME = "testhostname"; 61 62 private final ArrayMap<MacAddress, TetheredDevice> mTetheredDevices; 63 private final TapPacketReader mDownstreamReader; 64 TetheringTester(TapPacketReader downstream)65 public TetheringTester(TapPacketReader downstream) { 66 if (downstream == null) fail("Downstream reader could not be NULL"); 67 68 mDownstreamReader = downstream; 69 mTetheredDevices = new ArrayMap<>(); 70 } 71 createTetheredDevice(MacAddress macAddr)72 public TetheredDevice createTetheredDevice(MacAddress macAddr) throws Exception { 73 if (mTetheredDevices.get(macAddr) != null) { 74 fail("Tethered device already created"); 75 } 76 77 TetheredDevice tethered = new TetheredDevice(macAddr); 78 mTetheredDevices.put(macAddr, tethered); 79 80 return tethered; 81 } 82 83 public class TetheredDevice { 84 public final MacAddress macAddr; 85 public final MacAddress routerMacAddr; 86 public final Inet4Address ipv4Addr; 87 TetheredDevice(MacAddress mac)88 private TetheredDevice(MacAddress mac) throws Exception { 89 macAddr = mac; 90 91 DhcpResults dhcpResults = runDhcp(macAddr.toByteArray()); 92 ipv4Addr = (Inet4Address) dhcpResults.ipAddress.getAddress(); 93 routerMacAddr = getRouterMacAddressFromArp(ipv4Addr, macAddr, 94 dhcpResults.serverAddress); 95 } 96 } 97 98 /** Simulate dhcp client to obtain ipv4 address. */ runDhcp(byte[] clientMacAddr)99 public DhcpResults runDhcp(byte[] clientMacAddr) 100 throws Exception { 101 // We have to retransmit DHCP requests because IpServer declares itself to be ready before 102 // its DhcpServer is actually started. TODO: fix this race and remove this loop. 103 DhcpPacket offerPacket = null; 104 for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) { 105 Log.d(TAG, "Sending DHCP discover"); 106 sendDhcpDiscover(clientMacAddr); 107 offerPacket = getNextDhcpPacket(); 108 if (offerPacket instanceof DhcpOfferPacket) break; 109 } 110 if (!(offerPacket instanceof DhcpOfferPacket)) { 111 throw new TimeoutException("No DHCPOFFER received on interface within timeout"); 112 } 113 114 sendDhcpRequest(offerPacket, clientMacAddr); 115 DhcpPacket ackPacket = getNextDhcpPacket(); 116 if (!(ackPacket instanceof DhcpAckPacket)) { 117 throw new TimeoutException("No DHCPACK received on interface within timeout"); 118 } 119 120 return ackPacket.toDhcpResults(); 121 } 122 sendDhcpDiscover(byte[] macAddress)123 private void sendDhcpDiscover(byte[] macAddress) throws Exception { 124 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2, 125 new Random().nextInt() /* transactionId */, (short) 0 /* secs */, 126 macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS, 127 false /* rapid commit */, DHCP_HOSTNAME); 128 mDownstreamReader.sendResponse(packet); 129 } 130 sendDhcpRequest(DhcpPacket offerPacket, byte[] macAddress)131 private void sendDhcpRequest(DhcpPacket offerPacket, byte[] macAddress) 132 throws Exception { 133 DhcpResults results = offerPacket.toDhcpResults(); 134 Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress(); 135 Inet4Address serverIdentifier = results.serverAddress; 136 ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2, 137 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */, 138 false /* broadcast */, macAddress, clientIp /* requestedIpAddress */, 139 serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME); 140 mDownstreamReader.sendResponse(packet); 141 } 142 getNextDhcpPacket()143 private DhcpPacket getNextDhcpPacket() throws Exception { 144 final byte[] packet = getNextMatchedPacket((p) -> { 145 // Test whether this is DHCP packet. 146 try { 147 DhcpPacket.decodeFullPacket(p, p.length, DhcpPacket.ENCAP_L2); 148 } catch (DhcpPacket.ParseException e) { 149 // Not a DHCP packet. 150 return false; 151 } 152 153 return true; 154 }); 155 156 return packet == null ? null : 157 DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2); 158 } 159 160 @Nullable parseArpPacket(final byte[] packet)161 private ArpPacket parseArpPacket(final byte[] packet) { 162 try { 163 return ArpPacket.parseArpPacket(packet, packet.length); 164 } catch (ArpPacket.ParseException e) { 165 return null; 166 } 167 } 168 maybeReplyArp(byte[] packet)169 private void maybeReplyArp(byte[] packet) { 170 ByteBuffer buf = ByteBuffer.wrap(packet); 171 172 final ArpPacket arpPacket = parseArpPacket(packet); 173 if (arpPacket == null || arpPacket.opCode != ARP_REQUEST) return; 174 175 for (int i = 0; i < mTetheredDevices.size(); i++) { 176 TetheredDevice tethered = mTetheredDevices.valueAt(i); 177 if (!arpPacket.targetIp.equals(tethered.ipv4Addr)) continue; 178 179 final ByteBuffer arpReply = ArpPacket.buildArpPacket( 180 arpPacket.senderHwAddress.toByteArray() /* dst */, 181 tethered.macAddr.toByteArray() /* srcMac */, 182 arpPacket.senderIp.getAddress() /* target IP */, 183 arpPacket.senderHwAddress.toByteArray() /* target HW address */, 184 tethered.ipv4Addr.getAddress() /* sender IP */, 185 (short) ARP_REPLY); 186 try { 187 sendPacket(arpReply); 188 } catch (Exception e) { 189 fail("Failed to reply ARP for " + tethered.ipv4Addr); 190 } 191 return; 192 } 193 } 194 getRouterMacAddressFromArp(final Inet4Address tetherIp, final MacAddress tetherMac, final Inet4Address routerIp)195 private MacAddress getRouterMacAddressFromArp(final Inet4Address tetherIp, 196 final MacAddress tetherMac, final Inet4Address routerIp) throws Exception { 197 final ByteBuffer arpProbe = ArpPacket.buildArpPacket(ETHER_BROADCAST /* dst */, 198 tetherMac.toByteArray() /* srcMac */, routerIp.getAddress() /* target IP */, 199 new byte[ETHER_ADDR_LEN] /* target HW address */, 200 tetherIp.getAddress() /* sender IP */, (short) ARP_REQUEST); 201 sendPacket(arpProbe); 202 203 final byte[] packet = getNextMatchedPacket((p) -> { 204 final ArpPacket arpPacket = parseArpPacket(p); 205 if (arpPacket == null || arpPacket.opCode != ARP_REPLY) return false; 206 return arpPacket.targetIp.equals(tetherIp); 207 }); 208 209 if (packet != null) { 210 Log.d(TAG, "Get Mac address from ARP"); 211 final ArpPacket arpReply = ArpPacket.parseArpPacket(packet, packet.length); 212 return arpReply.senderHwAddress; 213 } 214 215 fail("Could not get ARP packet"); 216 return null; 217 } 218 sendPacket(ByteBuffer packet)219 public void sendPacket(ByteBuffer packet) throws Exception { 220 mDownstreamReader.sendResponse(packet); 221 } 222 getNextMatchedPacket(Predicate<byte[]> filter)223 public byte[] getNextMatchedPacket(Predicate<byte[]> filter) { 224 byte[] packet; 225 while ((packet = mDownstreamReader.poll(PACKET_READ_TIMEOUT_MS)) != null) { 226 if (filter.test(packet)) return packet; 227 228 maybeReplyArp(packet); 229 } 230 231 return null; 232 } 233 verifyUpload(final RemoteResponder dst, final ByteBuffer packet, final Predicate<byte[]> filter)234 public void verifyUpload(final RemoteResponder dst, final ByteBuffer packet, 235 final Predicate<byte[]> filter) throws Exception { 236 sendPacket(packet); 237 assertNotNull("Upload fail", dst.getNextMatchedPacket(filter)); 238 } 239 240 public static class RemoteResponder { 241 final TapPacketReader mUpstreamReader; RemoteResponder(TapPacketReader reader)242 public RemoteResponder(TapPacketReader reader) { 243 mUpstreamReader = reader; 244 } 245 sendPacket(ByteBuffer packet)246 public void sendPacket(ByteBuffer packet) throws Exception { 247 mUpstreamReader.sendResponse(packet); 248 } 249 getNextMatchedPacket(Predicate<byte[]> filter)250 public byte[] getNextMatchedPacket(Predicate<byte[]> filter) throws Exception { 251 return mUpstreamReader.poll(PACKET_READ_TIMEOUT_MS, filter); 252 } 253 verifyDownload(final TetheringTester dst, final ByteBuffer packet, final Predicate<byte[]> filter)254 public void verifyDownload(final TetheringTester dst, final ByteBuffer packet, 255 final Predicate<byte[]> filter) throws Exception { 256 sendPacket(packet); 257 assertNotNull("Download fail", dst.getNextMatchedPacket(filter)); 258 } 259 } 260 } 261