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