1 /*
2  * Copyright (C) 2019 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 com.android.net.module.util.arp;
18 
19 import static android.system.OsConstants.ETH_P_ARP;
20 import static android.system.OsConstants.ETH_P_IP;
21 
22 import static com.android.net.module.util.NetworkStackConstants.ARP_ETHER_IPV4_LEN;
23 import static com.android.net.module.util.NetworkStackConstants.ARP_HWTYPE_ETHER;
24 import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY;
25 import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
26 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
27 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_LEN;
28 
29 import android.net.MacAddress;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 
33 import java.net.Inet4Address;
34 import java.net.InetAddress;
35 import java.net.UnknownHostException;
36 import java.nio.BufferUnderflowException;
37 import java.nio.ByteBuffer;
38 
39 /**
40  * Defines basic data and operations needed to build and parse packets for the
41  * ARP protocol.
42  *
43  * @hide
44  */
45 public class ArpPacket {
46     private static final String TAG = "ArpPacket";
47 
48     public final MacAddress destination;
49     public final MacAddress source;
50     public final short opCode;
51     public final Inet4Address senderIp;
52     public final Inet4Address targetIp;
53     public final MacAddress senderHwAddress;
54     public final MacAddress targetHwAddress;
55 
ArpPacket(MacAddress destination, MacAddress source, short opCode, MacAddress senderHwAddress, Inet4Address senderIp, MacAddress targetHwAddress, Inet4Address targetIp)56     ArpPacket(MacAddress destination, MacAddress source, short opCode, MacAddress senderHwAddress,
57             Inet4Address senderIp, MacAddress targetHwAddress, Inet4Address targetIp) {
58         this.destination = destination;
59         this.source = source;
60         this.opCode = opCode;
61         this.senderHwAddress = senderHwAddress;
62         this.senderIp = senderIp;
63         this.targetHwAddress = targetHwAddress;
64         this.targetIp = targetIp;
65     }
66 
67     /**
68      * Build an ARP packet from the required specified parameters.
69      */
70     @VisibleForTesting
buildArpPacket(final byte[] dstMac, final byte[] srcMac, final byte[] targetIp, final byte[] targetHwAddress, byte[] senderIp, final short opCode)71     public static ByteBuffer buildArpPacket(final byte[] dstMac, final byte[] srcMac,
72             final byte[] targetIp, final byte[] targetHwAddress, byte[] senderIp,
73             final short opCode) {
74         final ByteBuffer buf = ByteBuffer.allocate(ARP_ETHER_IPV4_LEN);
75 
76         // Ether header
77         buf.put(dstMac);
78         buf.put(srcMac);
79         buf.putShort((short) ETH_P_ARP);
80 
81         // ARP header
82         buf.putShort((short) ARP_HWTYPE_ETHER);  // hrd
83         buf.putShort((short) ETH_P_IP);          // pro
84         buf.put((byte) ETHER_ADDR_LEN);          // hln
85         buf.put((byte) IPV4_ADDR_LEN);           // pln
86         buf.putShort(opCode);                    // op
87         buf.put(srcMac);                         // sha
88         buf.put(senderIp);                       // spa
89         buf.put(targetHwAddress);                // tha
90         buf.put(targetIp);                       // tpa
91         buf.flip();
92         return buf;
93     }
94 
95     /**
96      * Parse an ARP packet from a ByteBuffer object.
97      */
98     @VisibleForTesting
parseArpPacket(final byte[] recvbuf, final int length)99     public static ArpPacket parseArpPacket(final byte[] recvbuf, final int length)
100             throws ParseException {
101         try {
102             if (length < ARP_ETHER_IPV4_LEN || recvbuf.length < length) {
103                 throw new ParseException("Invalid packet length: " + length);
104             }
105 
106             final ByteBuffer buffer = ByteBuffer.wrap(recvbuf, 0, length);
107             byte[] l2dst = new byte[ETHER_ADDR_LEN];
108             byte[] l2src = new byte[ETHER_ADDR_LEN];
109             buffer.get(l2dst);
110             buffer.get(l2src);
111 
112             final short etherType = buffer.getShort();
113             if (etherType != ETH_P_ARP) {
114                 throw new ParseException("Incorrect Ether Type: " + etherType);
115             }
116 
117             final short hwType = buffer.getShort();
118             if (hwType != ARP_HWTYPE_ETHER) {
119                 throw new ParseException("Incorrect HW Type: " + hwType);
120             }
121 
122             final short protoType = buffer.getShort();
123             if (protoType != ETH_P_IP) {
124                 throw new ParseException("Incorrect Protocol Type: " + protoType);
125             }
126 
127             final byte hwAddrLength = buffer.get();
128             if (hwAddrLength != ETHER_ADDR_LEN) {
129                 throw new ParseException("Incorrect HW address length: " + hwAddrLength);
130             }
131 
132             final byte ipAddrLength = buffer.get();
133             if (ipAddrLength != IPV4_ADDR_LEN) {
134                 throw new ParseException("Incorrect Protocol address length: " + ipAddrLength);
135             }
136 
137             final short opCode = buffer.getShort();
138             if (opCode != ARP_REQUEST && opCode != ARP_REPLY) {
139                 throw new ParseException("Incorrect opCode: " + opCode);
140             }
141 
142             byte[] senderHwAddress = new byte[ETHER_ADDR_LEN];
143             byte[] senderIp = new byte[IPV4_ADDR_LEN];
144             buffer.get(senderHwAddress);
145             buffer.get(senderIp);
146 
147             byte[] targetHwAddress = new byte[ETHER_ADDR_LEN];
148             byte[] targetIp = new byte[IPV4_ADDR_LEN];
149             buffer.get(targetHwAddress);
150             buffer.get(targetIp);
151 
152             return new ArpPacket(MacAddress.fromBytes(l2dst),
153                     MacAddress.fromBytes(l2src), opCode,
154                     MacAddress.fromBytes(senderHwAddress),
155                     (Inet4Address) InetAddress.getByAddress(senderIp),
156                     MacAddress.fromBytes(targetHwAddress),
157                     (Inet4Address) InetAddress.getByAddress(targetIp));
158         } catch (IndexOutOfBoundsException e) {
159             throw new ParseException("Invalid index when wrapping a byte array into a buffer");
160         } catch (BufferUnderflowException e) {
161             throw new ParseException("Invalid buffer position");
162         } catch (IllegalArgumentException e) {
163             throw new ParseException("Invalid MAC address representation");
164         } catch (UnknownHostException e) {
165             throw new ParseException("Invalid IP address of Host");
166         }
167     }
168 
169     /**
170      * Thrown when parsing ARP packet failed.
171      */
172     public static class ParseException extends Exception {
ParseException(String message)173         ParseException(String message) {
174             super(message);
175         }
176     }
177 }
178