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