• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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