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