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