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.networkstack.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.server.util.NetworkStackConstants.ARP_ETHER_IPV4_LEN; 23 import static com.android.server.util.NetworkStackConstants.ARP_HWTYPE_ETHER; 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.ETHER_ADDR_LEN; 27 import static com.android.server.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 short opCode; 49 public final Inet4Address senderIp; 50 public final Inet4Address targetIp; 51 public final MacAddress senderHwAddress; 52 public final MacAddress targetHwAddress; 53 ArpPacket(short opCode, MacAddress senderHwAddress, Inet4Address senderIp, MacAddress targetHwAddress, Inet4Address targetIp)54 ArpPacket(short opCode, MacAddress senderHwAddress, Inet4Address senderIp, 55 MacAddress targetHwAddress, Inet4Address targetIp) { 56 this.opCode = opCode; 57 this.senderHwAddress = senderHwAddress; 58 this.senderIp = senderIp; 59 this.targetHwAddress = targetHwAddress; 60 this.targetIp = targetIp; 61 } 62 63 /** 64 * Build an ARP packet from the required specified parameters. 65 */ 66 @VisibleForTesting buildArpPacket(final byte[] dstMac, final byte[] srcMac, final byte[] targetIp, final byte[] targetHwAddress, byte[] senderIp, final short opCode)67 public static ByteBuffer buildArpPacket(final byte[] dstMac, final byte[] srcMac, 68 final byte[] targetIp, final byte[] targetHwAddress, byte[] senderIp, 69 final short opCode) { 70 final ByteBuffer buf = ByteBuffer.allocate(ARP_ETHER_IPV4_LEN); 71 72 // Ether header 73 buf.put(dstMac); 74 buf.put(srcMac); 75 buf.putShort((short) ETH_P_ARP); 76 77 // ARP header 78 buf.putShort((short) ARP_HWTYPE_ETHER); // hrd 79 buf.putShort((short) ETH_P_IP); // pro 80 buf.put((byte) ETHER_ADDR_LEN); // hln 81 buf.put((byte) IPV4_ADDR_LEN); // pln 82 buf.putShort(opCode); // op 83 buf.put(srcMac); // sha 84 buf.put(senderIp); // spa 85 buf.put(targetHwAddress); // tha 86 buf.put(targetIp); // tpa 87 buf.flip(); 88 return buf; 89 } 90 91 /** 92 * Parse an ARP packet from an ByteBuffer object. 93 */ 94 @VisibleForTesting parseArpPacket(final byte[] recvbuf, final int length)95 public static ArpPacket parseArpPacket(final byte[] recvbuf, final int length) 96 throws ParseException { 97 try { 98 if (length < ARP_ETHER_IPV4_LEN || recvbuf.length < length) { 99 throw new ParseException("Invalid packet length: " + length); 100 } 101 102 final ByteBuffer buffer = ByteBuffer.wrap(recvbuf, 0, length); 103 byte[] l2dst = new byte[ETHER_ADDR_LEN]; 104 byte[] l2src = new byte[ETHER_ADDR_LEN]; 105 buffer.get(l2dst); 106 buffer.get(l2src); 107 108 final short etherType = buffer.getShort(); 109 if (etherType != ETH_P_ARP) { 110 throw new ParseException("Incorrect Ether Type: " + etherType); 111 } 112 113 final short hwType = buffer.getShort(); 114 if (hwType != ARP_HWTYPE_ETHER) { 115 throw new ParseException("Incorrect HW Type: " + hwType); 116 } 117 118 final short protoType = buffer.getShort(); 119 if (protoType != ETH_P_IP) { 120 throw new ParseException("Incorrect Protocol Type: " + protoType); 121 } 122 123 final byte hwAddrLength = buffer.get(); 124 if (hwAddrLength != ETHER_ADDR_LEN) { 125 throw new ParseException("Incorrect HW address length: " + hwAddrLength); 126 } 127 128 final byte ipAddrLength = buffer.get(); 129 if (ipAddrLength != IPV4_ADDR_LEN) { 130 throw new ParseException("Incorrect Protocol address length: " + ipAddrLength); 131 } 132 133 final short opCode = buffer.getShort(); 134 if (opCode != ARP_REQUEST && opCode != ARP_REPLY) { 135 throw new ParseException("Incorrect opCode: " + opCode); 136 } 137 138 byte[] senderHwAddress = new byte[ETHER_ADDR_LEN]; 139 byte[] senderIp = new byte[IPV4_ADDR_LEN]; 140 buffer.get(senderHwAddress); 141 buffer.get(senderIp); 142 143 byte[] targetHwAddress = new byte[ETHER_ADDR_LEN]; 144 byte[] targetIp = new byte[IPV4_ADDR_LEN]; 145 buffer.get(targetHwAddress); 146 buffer.get(targetIp); 147 148 return new ArpPacket(opCode, MacAddress.fromBytes(senderHwAddress), 149 (Inet4Address) InetAddress.getByAddress(senderIp), 150 MacAddress.fromBytes(targetHwAddress), 151 (Inet4Address) InetAddress.getByAddress(targetIp)); 152 } catch (IndexOutOfBoundsException e) { 153 throw new ParseException("Invalid index when wrapping a byte array into a buffer"); 154 } catch (BufferUnderflowException e) { 155 throw new ParseException("Invalid buffer position"); 156 } catch (IllegalArgumentException e) { 157 throw new ParseException("Invalid MAC address representation"); 158 } catch (UnknownHostException e) { 159 throw new ParseException("Invalid IP address of Host"); 160 } 161 } 162 163 /** 164 * Thrown when parsing ARP packet failed. 165 */ 166 public static class ParseException extends Exception { ParseException(String message)167 ParseException(String message) { 168 super(message); 169 } 170 } 171 } 172