1/* 2 * Copyright (C) 2012 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 17package android.net.arp; 18 19import android.os.SystemClock; 20import android.util.Log; 21import java.io.IOException; 22import java.net.InetAddress; 23import java.net.Inet6Address; 24import java.net.SocketException; 25import java.nio.ByteBuffer; 26import java.nio.ByteOrder; 27import java.util.Arrays; 28 29import libcore.net.RawSocket; 30 31/** 32 * This class allows simple ARP exchanges over an uninitialized network 33 * interface. 34 * 35 * @hide 36 */ 37public class ArpPeer { 38 private String mInterfaceName; 39 private final InetAddress mMyAddr; 40 private final byte[] mMyMac = new byte[6]; 41 private final InetAddress mPeer; 42 private final RawSocket mSocket; 43 private final byte[] L2_BROADCAST; // TODO: refactor from DhcpClient.java 44 private static final int MAX_LENGTH = 1500; // refactor from DhcpPacket.java 45 private static final int ETHERNET_TYPE = 1; 46 private static final int ARP_LENGTH = 28; 47 private static final int MAC_ADDR_LENGTH = 6; 48 private static final int IPV4_LENGTH = 4; 49 private static final String TAG = "ArpPeer"; 50 51 public ArpPeer(String interfaceName, InetAddress myAddr, String mac, 52 InetAddress peer) throws SocketException { 53 mInterfaceName = interfaceName; 54 mMyAddr = myAddr; 55 56 for (int i = 0; i < MAC_ADDR_LENGTH; i++) { 57 mMyMac[i] = (byte) Integer.parseInt(mac.substring( 58 i*3, (i*3) + 2), 16); 59 } 60 61 if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) { 62 throw new IllegalArgumentException("IPv6 unsupported"); 63 } 64 65 mPeer = peer; 66 L2_BROADCAST = new byte[MAC_ADDR_LENGTH]; 67 Arrays.fill(L2_BROADCAST, (byte) 0xFF); 68 69 mSocket = new RawSocket(mInterfaceName, RawSocket.ETH_P_ARP); 70 } 71 72 /** 73 * Returns the MAC address (or null if timeout) for the requested 74 * peer. 75 */ 76 public byte[] doArp(int timeoutMillis) { 77 ByteBuffer buf = ByteBuffer.allocate(MAX_LENGTH); 78 byte[] desiredIp = mPeer.getAddress(); 79 long timeout = SystemClock.elapsedRealtime() + timeoutMillis; 80 81 // construct ARP request packet, using a ByteBuffer as a 82 // convenient container 83 buf.clear(); 84 buf.order(ByteOrder.BIG_ENDIAN); 85 86 buf.putShort((short) ETHERNET_TYPE); // Ethernet type, 16 bits 87 buf.putShort(RawSocket.ETH_P_IP); // Protocol type IP, 16 bits 88 buf.put((byte)MAC_ADDR_LENGTH); // MAC address length, 6 bytes 89 buf.put((byte)IPV4_LENGTH); // IPv4 protocol size 90 buf.putShort((short) 1); // ARP opcode 1: 'request' 91 buf.put(mMyMac); // six bytes: sender MAC 92 buf.put(mMyAddr.getAddress()); // four bytes: sender IP address 93 buf.put(new byte[MAC_ADDR_LENGTH]); // target MAC address: unknown 94 buf.put(desiredIp); // target IP address, 4 bytes 95 buf.flip(); 96 mSocket.write(L2_BROADCAST, buf.array(), 0, buf.limit()); 97 98 byte[] recvBuf = new byte[MAX_LENGTH]; 99 100 while (SystemClock.elapsedRealtime() < timeout) { 101 long duration = (long) timeout - SystemClock.elapsedRealtime(); 102 int readLen = mSocket.read(recvBuf, 0, recvBuf.length, -1, 103 (int) duration); 104 105 // Verify packet details. see RFC 826 106 if ((readLen >= ARP_LENGTH) // trailing bytes at times 107 && (recvBuf[0] == 0) && (recvBuf[1] == ETHERNET_TYPE) // type Ethernet 108 && (recvBuf[2] == 8) && (recvBuf[3] == 0) // protocol IP 109 && (recvBuf[4] == MAC_ADDR_LENGTH) // mac length 110 && (recvBuf[5] == IPV4_LENGTH) // IPv4 protocol size 111 && (recvBuf[6] == 0) && (recvBuf[7] == 2) // ARP reply 112 // verify desired IP address 113 && (recvBuf[14] == desiredIp[0]) && (recvBuf[15] == desiredIp[1]) 114 && (recvBuf[16] == desiredIp[2]) && (recvBuf[17] == desiredIp[3])) 115 { 116 // looks good. copy out the MAC 117 byte[] result = new byte[MAC_ADDR_LENGTH]; 118 System.arraycopy(recvBuf, 8, result, 0, MAC_ADDR_LENGTH); 119 return result; 120 } 121 } 122 123 return null; 124 } 125 126 public void close() { 127 try { 128 mSocket.close(); 129 } catch (IOException ex) { 130 } 131 } 132} 133