Home | History | Annotate | Download | only in util
      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