ArpPeer.java revision 21326d8b77cfd1567d87d5ebe3e327b744632e24
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 if (mac != null) { 57 for (int i = 0; i < MAC_ADDR_LENGTH; i++) { 58 mMyMac[i] = (byte) Integer.parseInt(mac.substring( 59 i*3, (i*3) + 2), 16); 60 } 61 } 62 63 if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) { 64 throw new IllegalArgumentException("IPv6 unsupported"); 65 } 66 67 mPeer = peer; 68 L2_BROADCAST = new byte[MAC_ADDR_LENGTH]; 69 Arrays.fill(L2_BROADCAST, (byte) 0xFF); 70 71 mSocket = new RawSocket(mInterfaceName, RawSocket.ETH_P_ARP); 72 } 73 74 /** 75 * Returns the MAC address (or null if timeout) for the requested 76 * peer. 77 */ 78 public byte[] doArp(int timeoutMillis) { 79 ByteBuffer buf = ByteBuffer.allocate(MAX_LENGTH); 80 byte[] desiredIp = mPeer.getAddress(); 81 long timeout = SystemClock.elapsedRealtime() + timeoutMillis; 82 83 // construct ARP request packet, using a ByteBuffer as a 84 // convenient container 85 buf.clear(); 86 buf.order(ByteOrder.BIG_ENDIAN); 87 88 buf.putShort((short) ETHERNET_TYPE); // Ethernet type, 16 bits 89 buf.putShort(RawSocket.ETH_P_IP); // Protocol type IP, 16 bits 90 buf.put((byte)MAC_ADDR_LENGTH); // MAC address length, 6 bytes 91 buf.put((byte)IPV4_LENGTH); // IPv4 protocol size 92 buf.putShort((short) 1); // ARP opcode 1: 'request' 93 buf.put(mMyMac); // six bytes: sender MAC 94 buf.put(mMyAddr.getAddress()); // four bytes: sender IP address 95 buf.put(new byte[MAC_ADDR_LENGTH]); // target MAC address: unknown 96 buf.put(desiredIp); // target IP address, 4 bytes 97 buf.flip(); 98 mSocket.write(L2_BROADCAST, buf.array(), 0, buf.limit()); 99 100 byte[] recvBuf = new byte[MAX_LENGTH]; 101 102 while (SystemClock.elapsedRealtime() < timeout) { 103 long duration = (long) timeout - SystemClock.elapsedRealtime(); 104 int readLen = mSocket.read(recvBuf, 0, recvBuf.length, -1, 105 (int) duration); 106 107 // Verify packet details. see RFC 826 108 if ((readLen >= ARP_LENGTH) // trailing bytes at times 109 && (recvBuf[0] == 0) && (recvBuf[1] == ETHERNET_TYPE) // type Ethernet 110 && (recvBuf[2] == 8) && (recvBuf[3] == 0) // protocol IP 111 && (recvBuf[4] == MAC_ADDR_LENGTH) // mac length 112 && (recvBuf[5] == IPV4_LENGTH) // IPv4 protocol size 113 && (recvBuf[6] == 0) && (recvBuf[7] == 2) // ARP reply 114 // verify desired IP address 115 && (recvBuf[14] == desiredIp[0]) && (recvBuf[15] == desiredIp[1]) 116 && (recvBuf[16] == desiredIp[2]) && (recvBuf[17] == desiredIp[3])) 117 { 118 // looks good. copy out the MAC 119 byte[] result = new byte[MAC_ADDR_LENGTH]; 120 System.arraycopy(recvBuf, 8, result, 0, MAC_ADDR_LENGTH); 121 return result; 122 } 123 } 124 125 return null; 126 } 127 128 public void close() { 129 try { 130 mSocket.close(); 131 } catch (IOException ex) { 132 } 133 } 134} 135