1473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline/*
2473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * Copyright (C) 2016 The Android Open Source Project
3473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline *
4473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * Licensed under the Apache License, Version 2.0 (the "License");
5473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * you may not use this file except in compliance with the License.
6473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * You may obtain a copy of the License at
7473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline *
8473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline *      http://www.apache.org/licenses/LICENSE-2.0
9473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline *
10473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * Unless required by applicable law or agreed to in writing, software
11473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * distributed under the License is distributed on an "AS IS" BASIS,
12473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * See the License for the specific language governing permissions and
14473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * limitations under the License.
15473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline */
16473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
17473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klinepackage android.net.util;
18473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
19473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport android.net.dhcp.DhcpPacket;
20473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
21473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport java.net.InetAddress;
22473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport java.net.UnknownHostException;
23473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport java.nio.ByteBuffer;
24473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport java.nio.ByteOrder;
25473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport java.util.Arrays;
26473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport java.util.StringJoiner;
27473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
28473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport static android.system.OsConstants.*;
29473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klineimport static android.net.util.NetworkConstants.*;
30473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
31473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
32473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline/**
33473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * Critical connectivity packet summarizing class.
34473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline *
35473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * Outputs short descriptions of ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
36473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline *
37473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline * @hide
38473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline */
39473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Klinepublic class ConnectivityPacketSummary {
40473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private static final String TAG = ConnectivityPacketSummary.class.getSimpleName();
41473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
42473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private final byte[] mHwAddr;
43473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private final byte[] mBytes;
44473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private final int mLength;
45473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private final ByteBuffer mPacket;
46473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private final String mSummary;
47473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
48473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    public static String summarize(byte[] hwaddr, byte[] buffer) {
49473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        return summarize(hwaddr, buffer, buffer.length);
50473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
51473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
52473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    // Methods called herein perform some but by no means all error checking.
53473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    // They may throw runtime exceptions on malformed packets.
54473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    public static String summarize(byte[] hwaddr, byte[] buffer, int length) {
55473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if ((hwaddr == null) || (hwaddr.length != ETHER_ADDR_LEN)) return null;
56473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (buffer == null) return null;
57473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        length = Math.min(length, buffer.length);
58473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        return (new ConnectivityPacketSummary(hwaddr, buffer, length)).toString();
59473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
60473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
61473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private ConnectivityPacketSummary(byte[] hwaddr, byte[] buffer, int length) {
62473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mHwAddr = hwaddr;
63473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mBytes = buffer;
64473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mLength = Math.min(length, mBytes.length);
65473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
66473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.order(ByteOrder.BIG_ENDIAN);
67473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
68473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final StringJoiner sj = new StringJoiner(" ");
69473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        // TODO: support other link-layers, or even no link-layer header.
70473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        parseEther(sj);
71473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mSummary = sj.toString();
72473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
73473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
74473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    public String toString() {
75473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        return mSummary;
76473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
77473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
78473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseEther(StringJoiner sj) {
79473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < ETHER_HEADER_LEN) {
80473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
81473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
82473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
83473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
84473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(ETHER_SRC_ADDR_OFFSET);
85473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final ByteBuffer srcMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
86473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        sj.add(ByteBuffer.wrap(mHwAddr).equals(srcMac) ? "TX" : "RX");
87473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        sj.add(getMacAddressString(srcMac));
88473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
89473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(ETHER_DST_ADDR_OFFSET);
90473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final ByteBuffer dstMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
91473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        sj.add(">").add(getMacAddressString(dstMac));
92473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
93473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(ETHER_TYPE_OFFSET);
94473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int etherType = asUint(mPacket.getShort());
95473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        switch (etherType) {
96473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            case ETHER_TYPE_ARP:
97473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("arp");
98473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                parseARP(sj);
99473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
100473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            case ETHER_TYPE_IPV4:
101473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("ipv4");
102473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                parseIPv4(sj);
103473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
104473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            case ETHER_TYPE_IPV6:
105473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("ipv6");
106473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                parseIPv6(sj);
107473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
108473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            default:
109473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                // Unknown ether type.
110473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("ethtype").add(asString(etherType));
111473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
112473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
113473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
114473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
115473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseARP(StringJoiner sj) {
116473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < ARP_PAYLOAD_LEN) {
117473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
118473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
119473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
120473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
121473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (asUint(mPacket.getShort()) != ARP_HWTYPE_ETHER ||
122473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            asUint(mPacket.getShort()) != ETHER_TYPE_IPV4 ||
123473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            asUint(mPacket.get()) != ETHER_ADDR_LEN ||
124473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            asUint(mPacket.get()) != IPV4_ADDR_LEN) {
125473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("unexpected header");
126473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
127473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
128473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
129473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int opCode = asUint(mPacket.getShort());
130473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
131473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final String senderHwAddr = getMacAddressString(mPacket);
132473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final String senderIPv4 = getIPv4AddressString(mPacket);
133473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        getMacAddressString(mPacket);  // target hardware address, unused
134473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final String targetIPv4 = getIPv4AddressString(mPacket);
135473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
136473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (opCode == ARP_REQUEST) {
137473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("who-has").add(targetIPv4);
138473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        } else if (opCode == ARP_REPLY) {
139473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("reply").add(senderIPv4).add(senderHwAddr);
140473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        } else {
141473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("unknown opcode").add(asString(opCode));
142473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
143473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
144473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
145473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseIPv4(StringJoiner sj) {
146473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (!mPacket.hasRemaining()) {
147473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt");
148473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
149473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
150473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
151473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int startOfIpLayer = mPacket.position();
152473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int ipv4HeaderLength = (mPacket.get(startOfIpLayer) & IPV4_IHL_MASK) * 4;
153473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < ipv4HeaderLength ||
154473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            mPacket.remaining() < IPV4_HEADER_MIN_LEN) {
155473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
156473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
157473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
158473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int startOfTransportLayer = startOfIpLayer + ipv4HeaderLength;
159473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
160473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(startOfIpLayer + IPV4_FLAGS_OFFSET);
161473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int flagsAndFragment = asUint(mPacket.getShort());
162473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final boolean isFragment = (flagsAndFragment & IPV4_FRAGMENT_MASK) != 0;
163473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
164473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(startOfIpLayer + IPV4_PROTOCOL_OFFSET);
165473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int protocol = asUint(mPacket.get());
166473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
167473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(startOfIpLayer + IPV4_SRC_ADDR_OFFSET);
168473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final String srcAddr = getIPv4AddressString(mPacket);
169473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
170473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(startOfIpLayer + IPV4_DST_ADDR_OFFSET);
171473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final String dstAddr = getIPv4AddressString(mPacket);
172473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
173473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        sj.add(srcAddr).add(">").add(dstAddr);
174473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
175473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(startOfTransportLayer);
176473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (protocol == IPPROTO_UDP) {
177473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("udp");
178473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            if (isFragment) sj.add("fragment");
179473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            else parseUDP(sj);
180473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        } else {
181473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("proto").add(asString(protocol));
182473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            if (isFragment) sj.add("fragment");
183473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
184473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
185473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
186473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseIPv6(StringJoiner sj) {
187473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < IPV6_HEADER_LEN) {
188473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
189473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
190473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
191473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
192473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int startOfIpLayer = mPacket.position();
193473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
194473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(startOfIpLayer + IPV6_PROTOCOL_OFFSET);
195473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int protocol = asUint(mPacket.get());
196473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
197473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(startOfIpLayer + IPV6_SRC_ADDR_OFFSET);
198473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final String srcAddr = getIPv6AddressString(mPacket);
199473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final String dstAddr = getIPv6AddressString(mPacket);
200473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
201473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        sj.add(srcAddr).add(">").add(dstAddr);
202473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
203473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(startOfIpLayer + IPV6_HEADER_LEN);
204473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (protocol == IPPROTO_ICMPV6) {
205473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("icmp6");
206473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            parseICMPv6(sj);
207473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        } else {
208473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("proto").add(asString(protocol));
209473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
210473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
211473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
212473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseICMPv6(StringJoiner sj) {
213473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < ICMPV6_HEADER_MIN_LEN) {
214473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
215473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
216473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
217473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
218473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int icmp6Type = asUint(mPacket.get());
219473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int icmp6Code = asUint(mPacket.get());
220473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.getShort();  // checksum, unused
221473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
222473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        switch (icmp6Type) {
223473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            case ICMPV6_ROUTER_SOLICITATION:
224473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("rs");
225473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                parseICMPv6RouterSolicitation(sj);
226473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
227473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            case ICMPV6_ROUTER_ADVERTISEMENT:
228473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("ra");
229473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                parseICMPv6RouterAdvertisement(sj);
230473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
231473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            case ICMPV6_NEIGHBOR_SOLICITATION:
232473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("ns");
233473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                parseICMPv6NeighborMessage(sj);
234473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
235473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            case ICMPV6_NEIGHBOR_ADVERTISEMENT:
236473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("na");
237473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                parseICMPv6NeighborMessage(sj);
238473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
239473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            default:
240473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("type").add(asString(icmp6Type));
241473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                sj.add("code").add(asString(icmp6Code));
242473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                break;
243473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
244473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
245473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
246473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseICMPv6RouterSolicitation(StringJoiner sj) {
247473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int RESERVED = 4;
248473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < RESERVED) {
249473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
250473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
251473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
252473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
253473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(mPacket.position() + RESERVED);
254473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        parseICMPv6NeighborDiscoveryOptions(sj);
255473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
256473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
257473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseICMPv6RouterAdvertisement(StringJoiner sj) {
258473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int FLAGS_AND_TIMERS = 3 * 4;
259473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < FLAGS_AND_TIMERS) {
260473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
261473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
262473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
263473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
264473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(mPacket.position() + FLAGS_AND_TIMERS);
265473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        parseICMPv6NeighborDiscoveryOptions(sj);
266473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
267473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
268473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseICMPv6NeighborMessage(StringJoiner sj) {
269473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int RESERVED = 4;
270473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int minReq = RESERVED + IPV6_ADDR_LEN;
271473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < minReq) {
272473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
273473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
274473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
275473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
276473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(mPacket.position() + RESERVED);
277473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        sj.add(getIPv6AddressString(mPacket));
278473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        parseICMPv6NeighborDiscoveryOptions(sj);
279473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
280473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
281473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseICMPv6NeighborDiscoveryOptions(StringJoiner sj) {
282473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        // All ND options are TLV, where T is one byte and L is one byte equal
283473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        // to the length of T + L + V in units of 8 octets.
284473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        while (mPacket.remaining() >= ICMPV6_ND_OPTION_MIN_LENGTH) {
285473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            final int ndType = asUint(mPacket.get());
286473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            final int ndLength = asUint(mPacket.get());
287473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2;
2882f157c421a9fa15660b8f1bc0c64b1ebb45fa64aLorenzo Colitti            if (ndBytes < 0 || ndBytes > mPacket.remaining()) {
2892f157c421a9fa15660b8f1bc0c64b1ebb45fa64aLorenzo Colitti                sj.add("<malformed>");
2902f157c421a9fa15660b8f1bc0c64b1ebb45fa64aLorenzo Colitti                break;
2912f157c421a9fa15660b8f1bc0c64b1ebb45fa64aLorenzo Colitti            }
292473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            final int position = mPacket.position();
293473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
294473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            switch (ndType) {
295473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                    case ICMPV6_ND_OPTION_SLLA:
296473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        sj.add("slla");
297473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        sj.add(getMacAddressString(mPacket));
298473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        break;
299473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                    case ICMPV6_ND_OPTION_TLLA:
300473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        sj.add("tlla");
301473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        sj.add(getMacAddressString(mPacket));
302473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        break;
303473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                    case ICMPV6_ND_OPTION_MTU:
304473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        sj.add("mtu");
305473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        final short reserved = mPacket.getShort();
306473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        sj.add(asString(mPacket.getInt()));
307473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        break;
308473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                    default:
309473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        // Skip.
310473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline                        break;
311473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            }
312473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
313473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            mPacket.position(position + ndBytes);
314473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
315473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
316473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
317473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseUDP(StringJoiner sj) {
318473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mPacket.remaining() < UDP_HEADER_LEN) {
319473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("runt:").add(asString(mPacket.remaining()));
320473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return;
321473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
322473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
323473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int previous = mPacket.position();
324473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int srcPort = asUint(mPacket.getShort());
325473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final int dstPort = asUint(mPacket.getShort());
326473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        sj.add(asString(srcPort)).add(">").add(asString(dstPort));
327473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
328473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mPacket.position(previous + UDP_HEADER_LEN);
329473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (srcPort == DHCP4_CLIENT_PORT || dstPort == DHCP4_CLIENT_PORT) {
330473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("dhcp4");
331473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            parseDHCPv4(sj);
332473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
333473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
334473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
335473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private void parseDHCPv4(StringJoiner sj) {
336473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final DhcpPacket dhcpPacket;
337473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        try {
338473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            dhcpPacket = DhcpPacket.decodeFullPacket(mBytes, mLength, DhcpPacket.ENCAP_L2);
339473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add(dhcpPacket.toString());
340473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        } catch (DhcpPacket.ParseException e) {
341473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            sj.add("parse error: " + e);
342473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
343473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
344473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
345473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private static String getIPv4AddressString(ByteBuffer ipv4) {
346473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        return getIpAddressString(ipv4, IPV4_ADDR_LEN);
347473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
348473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
349473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private static String getIPv6AddressString(ByteBuffer ipv6) {
350473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        return getIpAddressString(ipv6, IPV6_ADDR_LEN);
351473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
352473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
353473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private static String getIpAddressString(ByteBuffer ip, int byteLength) {
354473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (ip == null || ip.remaining() < byteLength) return "invalid";
355473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
356473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        byte[] bytes = new byte[byteLength];
357473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        ip.get(bytes, 0, byteLength);
358473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        try {
359473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            InetAddress addr = InetAddress.getByAddress(bytes);
360473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return addr.getHostAddress();
361473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        } catch (UnknownHostException uhe) {
362473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline            return "unknown";
363473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        }
364473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
365473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
366473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    private static String getMacAddressString(ByteBuffer mac) {
367473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        if (mac == null || mac.remaining() < ETHER_ADDR_LEN) return "invalid";
368473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
369473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        byte[] bytes = new byte[ETHER_ADDR_LEN];
370473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        mac.get(bytes, 0, bytes.length);
3711a4f618812880c2fa4156576da1b7c3c773b52aaErik Kline        Object[] printableBytes = new Object[bytes.length];
372473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        int i = 0;
3731a4f618812880c2fa4156576da1b7c3c773b52aaErik Kline        for (byte b : bytes) printableBytes[i++] = new Byte(b);
374473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline
375473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x";
376473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline        return String.format(MAC48_FORMAT, printableBytes);
377473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline    }
378473355f96b91a1fbeb6f8f8f0bcd3c887da12f40Erik Kline}
379