1 /* 2 * Copyright (C) 2016 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.util; 18 19 import static android.system.OsConstants.IPPROTO_ICMPV6; 20 import static android.system.OsConstants.IPPROTO_UDP; 21 22 import static com.android.server.util.NetworkStackConstants.ARP_HWTYPE_ETHER; 23 import static com.android.server.util.NetworkStackConstants.ARP_PAYLOAD_LEN; 24 import static com.android.server.util.NetworkStackConstants.ARP_REPLY; 25 import static com.android.server.util.NetworkStackConstants.ARP_REQUEST; 26 import static com.android.server.util.NetworkStackConstants.DHCP4_CLIENT_PORT; 27 import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN; 28 import static com.android.server.util.NetworkStackConstants.ETHER_DST_ADDR_OFFSET; 29 import static com.android.server.util.NetworkStackConstants.ETHER_HEADER_LEN; 30 import static com.android.server.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET; 31 import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_ARP; 32 import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV4; 33 import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV6; 34 import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_OFFSET; 35 import static com.android.server.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN; 36 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR; 37 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MIN_LENGTH; 38 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU; 39 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA; 40 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA; 41 import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT; 42 import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION; 43 import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; 44 import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION; 45 import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_LEN; 46 import static com.android.server.util.NetworkStackConstants.IPV4_DST_ADDR_OFFSET; 47 import static com.android.server.util.NetworkStackConstants.IPV4_FLAGS_OFFSET; 48 import static com.android.server.util.NetworkStackConstants.IPV4_FRAGMENT_MASK; 49 import static com.android.server.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN; 50 import static com.android.server.util.NetworkStackConstants.IPV4_IHL_MASK; 51 import static com.android.server.util.NetworkStackConstants.IPV4_PROTOCOL_OFFSET; 52 import static com.android.server.util.NetworkStackConstants.IPV4_SRC_ADDR_OFFSET; 53 import static com.android.server.util.NetworkStackConstants.IPV6_ADDR_LEN; 54 import static com.android.server.util.NetworkStackConstants.IPV6_HEADER_LEN; 55 import static com.android.server.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET; 56 import static com.android.server.util.NetworkStackConstants.IPV6_SRC_ADDR_OFFSET; 57 import static com.android.server.util.NetworkStackConstants.UDP_HEADER_LEN; 58 59 import android.net.MacAddress; 60 import android.net.dhcp.DhcpPacket; 61 62 import java.net.InetAddress; 63 import java.net.UnknownHostException; 64 import java.nio.ByteBuffer; 65 import java.nio.ByteOrder; 66 import java.util.StringJoiner; 67 68 69 /** 70 * Critical connectivity packet summarizing class. 71 * 72 * Outputs short descriptions of ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets. 73 * 74 * @hide 75 */ 76 public class ConnectivityPacketSummary { 77 private static final String TAG = ConnectivityPacketSummary.class.getSimpleName(); 78 79 private final byte[] mHwAddr; 80 private final byte[] mBytes; 81 private final int mLength; 82 private final ByteBuffer mPacket; 83 private final String mSummary; 84 85 /** 86 * Create a string summary of a received packet. 87 * @param hwaddr MacAddress of the interface sending/receiving the packet. 88 * @param buffer The packet bytes. Length is assumed to be the buffer length. 89 * @return A summary of the packet. 90 */ summarize(MacAddress hwaddr, byte[] buffer)91 public static String summarize(MacAddress hwaddr, byte[] buffer) { 92 return summarize(hwaddr, buffer, buffer.length); 93 } 94 95 // Methods called herein perform some but by no means all error checking. 96 // They may throw runtime exceptions on malformed packets. 97 98 /** 99 * Create a string summary of a received packet. 100 * @param macAddr MacAddress of the interface sending/receiving the packet. 101 * @param buffer The packet bytes. 102 * @param length Length of the packet. 103 * @return A summary of the packet. 104 */ summarize(MacAddress macAddr, byte[] buffer, int length)105 public static String summarize(MacAddress macAddr, byte[] buffer, int length) { 106 if ((macAddr == null) || (buffer == null)) return null; 107 length = Math.min(length, buffer.length); 108 return (new ConnectivityPacketSummary(macAddr, buffer, length)).toString(); 109 } 110 ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length)111 private ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length) { 112 mHwAddr = macAddr.toByteArray(); 113 mBytes = buffer; 114 mLength = Math.min(length, mBytes.length); 115 mPacket = ByteBuffer.wrap(mBytes, 0, mLength); 116 mPacket.order(ByteOrder.BIG_ENDIAN); 117 118 final StringJoiner sj = new StringJoiner(" "); 119 // TODO: support other link-layers, or even no link-layer header. 120 parseEther(sj); 121 mSummary = sj.toString(); 122 } 123 toString()124 public String toString() { 125 return mSummary; 126 } 127 parseEther(StringJoiner sj)128 private void parseEther(StringJoiner sj) { 129 if (mPacket.remaining() < ETHER_HEADER_LEN) { 130 sj.add("runt:").add(asString(mPacket.remaining())); 131 return; 132 } 133 134 mPacket.position(ETHER_SRC_ADDR_OFFSET); 135 final ByteBuffer srcMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN); 136 sj.add(ByteBuffer.wrap(mHwAddr).equals(srcMac) ? "TX" : "RX"); 137 sj.add(getMacAddressString(srcMac)); 138 139 mPacket.position(ETHER_DST_ADDR_OFFSET); 140 final ByteBuffer dstMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN); 141 sj.add(">").add(getMacAddressString(dstMac)); 142 143 mPacket.position(ETHER_TYPE_OFFSET); 144 final int etherType = asUint(mPacket.getShort()); 145 switch (etherType) { 146 case ETHER_TYPE_ARP: 147 sj.add("arp"); 148 parseARP(sj); 149 break; 150 case ETHER_TYPE_IPV4: 151 sj.add("ipv4"); 152 parseIPv4(sj); 153 break; 154 case ETHER_TYPE_IPV6: 155 sj.add("ipv6"); 156 parseIPv6(sj); 157 break; 158 default: 159 // Unknown ether type. 160 sj.add("ethtype").add(asString(etherType)); 161 break; 162 } 163 } 164 parseARP(StringJoiner sj)165 private void parseARP(StringJoiner sj) { 166 if (mPacket.remaining() < ARP_PAYLOAD_LEN) { 167 sj.add("runt:").add(asString(mPacket.remaining())); 168 return; 169 } 170 171 if (asUint(mPacket.getShort()) != ARP_HWTYPE_ETHER || 172 asUint(mPacket.getShort()) != ETHER_TYPE_IPV4 || 173 asUint(mPacket.get()) != ETHER_ADDR_LEN || 174 asUint(mPacket.get()) != IPV4_ADDR_LEN) { 175 sj.add("unexpected header"); 176 return; 177 } 178 179 final int opCode = asUint(mPacket.getShort()); 180 181 final String senderHwAddr = getMacAddressString(mPacket); 182 final String senderIPv4 = getIPv4AddressString(mPacket); 183 getMacAddressString(mPacket); // target hardware address, unused 184 final String targetIPv4 = getIPv4AddressString(mPacket); 185 186 if (opCode == ARP_REQUEST) { 187 sj.add("who-has").add(targetIPv4); 188 } else if (opCode == ARP_REPLY) { 189 sj.add("reply").add(senderIPv4).add(senderHwAddr); 190 } else { 191 sj.add("unknown opcode").add(asString(opCode)); 192 } 193 } 194 parseIPv4(StringJoiner sj)195 private void parseIPv4(StringJoiner sj) { 196 if (!mPacket.hasRemaining()) { 197 sj.add("runt"); 198 return; 199 } 200 201 final int startOfIpLayer = mPacket.position(); 202 final int ipv4HeaderLength = (mPacket.get(startOfIpLayer) & IPV4_IHL_MASK) * 4; 203 if (mPacket.remaining() < ipv4HeaderLength || 204 mPacket.remaining() < IPV4_HEADER_MIN_LEN) { 205 sj.add("runt:").add(asString(mPacket.remaining())); 206 return; 207 } 208 final int startOfTransportLayer = startOfIpLayer + ipv4HeaderLength; 209 210 mPacket.position(startOfIpLayer + IPV4_FLAGS_OFFSET); 211 final int flagsAndFragment = asUint(mPacket.getShort()); 212 final boolean isFragment = (flagsAndFragment & IPV4_FRAGMENT_MASK) != 0; 213 214 mPacket.position(startOfIpLayer + IPV4_PROTOCOL_OFFSET); 215 final int protocol = asUint(mPacket.get()); 216 217 mPacket.position(startOfIpLayer + IPV4_SRC_ADDR_OFFSET); 218 final String srcAddr = getIPv4AddressString(mPacket); 219 220 mPacket.position(startOfIpLayer + IPV4_DST_ADDR_OFFSET); 221 final String dstAddr = getIPv4AddressString(mPacket); 222 223 sj.add(srcAddr).add(">").add(dstAddr); 224 225 mPacket.position(startOfTransportLayer); 226 if (protocol == IPPROTO_UDP) { 227 sj.add("udp"); 228 if (isFragment) sj.add("fragment"); 229 else parseUDP(sj); 230 } else { 231 sj.add("proto").add(asString(protocol)); 232 if (isFragment) sj.add("fragment"); 233 } 234 } 235 parseIPv6(StringJoiner sj)236 private void parseIPv6(StringJoiner sj) { 237 if (mPacket.remaining() < IPV6_HEADER_LEN) { 238 sj.add("runt:").add(asString(mPacket.remaining())); 239 return; 240 } 241 242 final int startOfIpLayer = mPacket.position(); 243 244 mPacket.position(startOfIpLayer + IPV6_PROTOCOL_OFFSET); 245 final int protocol = asUint(mPacket.get()); 246 247 mPacket.position(startOfIpLayer + IPV6_SRC_ADDR_OFFSET); 248 final String srcAddr = getIPv6AddressString(mPacket); 249 final String dstAddr = getIPv6AddressString(mPacket); 250 251 sj.add(srcAddr).add(">").add(dstAddr); 252 253 mPacket.position(startOfIpLayer + IPV6_HEADER_LEN); 254 if (protocol == IPPROTO_ICMPV6) { 255 sj.add("icmp6"); 256 parseICMPv6(sj); 257 } else { 258 sj.add("proto").add(asString(protocol)); 259 } 260 } 261 parseICMPv6(StringJoiner sj)262 private void parseICMPv6(StringJoiner sj) { 263 if (mPacket.remaining() < ICMPV6_HEADER_MIN_LEN) { 264 sj.add("runt:").add(asString(mPacket.remaining())); 265 return; 266 } 267 268 final int icmp6Type = asUint(mPacket.get()); 269 final int icmp6Code = asUint(mPacket.get()); 270 mPacket.getShort(); // checksum, unused 271 272 switch (icmp6Type) { 273 case ICMPV6_ROUTER_SOLICITATION: 274 sj.add("rs"); 275 parseICMPv6RouterSolicitation(sj); 276 break; 277 case ICMPV6_ROUTER_ADVERTISEMENT: 278 sj.add("ra"); 279 parseICMPv6RouterAdvertisement(sj); 280 break; 281 case ICMPV6_NEIGHBOR_SOLICITATION: 282 sj.add("ns"); 283 parseICMPv6NeighborMessage(sj); 284 break; 285 case ICMPV6_NEIGHBOR_ADVERTISEMENT: 286 sj.add("na"); 287 parseICMPv6NeighborMessage(sj); 288 break; 289 default: 290 sj.add("type").add(asString(icmp6Type)); 291 sj.add("code").add(asString(icmp6Code)); 292 break; 293 } 294 } 295 parseICMPv6RouterSolicitation(StringJoiner sj)296 private void parseICMPv6RouterSolicitation(StringJoiner sj) { 297 final int RESERVED = 4; 298 if (mPacket.remaining() < RESERVED) { 299 sj.add("runt:").add(asString(mPacket.remaining())); 300 return; 301 } 302 303 mPacket.position(mPacket.position() + RESERVED); 304 parseICMPv6NeighborDiscoveryOptions(sj); 305 } 306 parseICMPv6RouterAdvertisement(StringJoiner sj)307 private void parseICMPv6RouterAdvertisement(StringJoiner sj) { 308 final int FLAGS_AND_TIMERS = 3 * 4; 309 if (mPacket.remaining() < FLAGS_AND_TIMERS) { 310 sj.add("runt:").add(asString(mPacket.remaining())); 311 return; 312 } 313 314 mPacket.position(mPacket.position() + FLAGS_AND_TIMERS); 315 parseICMPv6NeighborDiscoveryOptions(sj); 316 } 317 parseICMPv6NeighborMessage(StringJoiner sj)318 private void parseICMPv6NeighborMessage(StringJoiner sj) { 319 final int RESERVED = 4; 320 final int minReq = RESERVED + IPV6_ADDR_LEN; 321 if (mPacket.remaining() < minReq) { 322 sj.add("runt:").add(asString(mPacket.remaining())); 323 return; 324 } 325 326 mPacket.position(mPacket.position() + RESERVED); 327 sj.add(getIPv6AddressString(mPacket)); 328 parseICMPv6NeighborDiscoveryOptions(sj); 329 } 330 parseICMPv6NeighborDiscoveryOptions(StringJoiner sj)331 private void parseICMPv6NeighborDiscoveryOptions(StringJoiner sj) { 332 // All ND options are TLV, where T is one byte and L is one byte equal 333 // to the length of T + L + V in units of 8 octets. 334 while (mPacket.remaining() >= ICMPV6_ND_OPTION_MIN_LENGTH) { 335 final int ndType = asUint(mPacket.get()); 336 final int ndLength = asUint(mPacket.get()); 337 final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2; 338 if (ndBytes < 0 || ndBytes > mPacket.remaining()) { 339 sj.add("<malformed>"); 340 break; 341 } 342 final int position = mPacket.position(); 343 344 switch (ndType) { 345 case ICMPV6_ND_OPTION_SLLA: 346 sj.add("slla"); 347 sj.add(getMacAddressString(mPacket)); 348 break; 349 case ICMPV6_ND_OPTION_TLLA: 350 sj.add("tlla"); 351 sj.add(getMacAddressString(mPacket)); 352 break; 353 case ICMPV6_ND_OPTION_MTU: 354 sj.add("mtu"); 355 final short reserved = mPacket.getShort(); 356 sj.add(asString(mPacket.getInt())); 357 break; 358 default: 359 // Skip. 360 break; 361 } 362 363 mPacket.position(position + ndBytes); 364 } 365 } 366 parseUDP(StringJoiner sj)367 private void parseUDP(StringJoiner sj) { 368 if (mPacket.remaining() < UDP_HEADER_LEN) { 369 sj.add("runt:").add(asString(mPacket.remaining())); 370 return; 371 } 372 373 final int previous = mPacket.position(); 374 final int srcPort = asUint(mPacket.getShort()); 375 final int dstPort = asUint(mPacket.getShort()); 376 sj.add(asString(srcPort)).add(">").add(asString(dstPort)); 377 378 mPacket.position(previous + UDP_HEADER_LEN); 379 if (srcPort == DHCP4_CLIENT_PORT || dstPort == DHCP4_CLIENT_PORT) { 380 sj.add("dhcp4"); 381 parseDHCPv4(sj); 382 } 383 } 384 parseDHCPv4(StringJoiner sj)385 private void parseDHCPv4(StringJoiner sj) { 386 final DhcpPacket dhcpPacket; 387 try { 388 dhcpPacket = DhcpPacket.decodeFullPacket(mBytes, mLength, DhcpPacket.ENCAP_L2); 389 sj.add(dhcpPacket.toString()); 390 } catch (DhcpPacket.ParseException e) { 391 sj.add("parse error: " + e); 392 } 393 } 394 getIPv4AddressString(ByteBuffer ipv4)395 private static String getIPv4AddressString(ByteBuffer ipv4) { 396 return getIpAddressString(ipv4, IPV4_ADDR_LEN); 397 } 398 getIPv6AddressString(ByteBuffer ipv6)399 private static String getIPv6AddressString(ByteBuffer ipv6) { 400 return getIpAddressString(ipv6, IPV6_ADDR_LEN); 401 } 402 getIpAddressString(ByteBuffer ip, int byteLength)403 private static String getIpAddressString(ByteBuffer ip, int byteLength) { 404 if (ip == null || ip.remaining() < byteLength) return "invalid"; 405 406 byte[] bytes = new byte[byteLength]; 407 ip.get(bytes, 0, byteLength); 408 try { 409 InetAddress addr = InetAddress.getByAddress(bytes); 410 return addr.getHostAddress(); 411 } catch (UnknownHostException uhe) { 412 return "unknown"; 413 } 414 } 415 getMacAddressString(ByteBuffer mac)416 private static String getMacAddressString(ByteBuffer mac) { 417 if (mac == null || mac.remaining() < ETHER_ADDR_LEN) return "invalid"; 418 419 byte[] bytes = new byte[ETHER_ADDR_LEN]; 420 mac.get(bytes, 0, bytes.length); 421 Object[] printableBytes = new Object[bytes.length]; 422 int i = 0; 423 for (byte b : bytes) printableBytes[i++] = new Byte(b); 424 425 final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x"; 426 return String.format(MAC48_FORMAT, printableBytes); 427 } 428 429 /** 430 * Convenience method to convert an int to a String. 431 */ asString(int i)432 public static String asString(int i) { 433 return Integer.toString(i); 434 } 435 436 /** 437 * Convenience method to read a byte as an unsigned int. 438 */ asUint(byte b)439 public static int asUint(byte b) { 440 return (b & 0xff); 441 } 442 443 /** 444 * Convenience method to read a short as an unsigned int. 445 */ asUint(short s)446 public static int asUint(short s) { 447 return (s & 0xffff); 448 } 449 } 450