/* * Copyright (c) Martin Schoeberl, martin@jopdesign.com * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Martin Schoeberl * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ package ejip; /** * Arp.java: does ARP. * * Author: Martin Schoeberl (martin.schoeberl@chello.at) * * Changelog: * 2002-03-15 ARP works! * 2002-10-21 use Packet buffer, 4 bytes in one word * 2002-11-11 ARP in own class (called from LinkLayer driver). * * */ /** * handle ARP request. */ public class Arp { static class Entry { final static int ENTRY_CNT = 4; // TODO: use this one when the merge is finished // final static int ENTRY_CNT = StackParameters.ARP_ENTRY_POOL_SIZE + 1; static Entry[] list; // TODO: not used - age wraps around after 4 billion requests // static int ageCnt; // TODO: make this list non-static int ip; int[] mac; // could be optimized to use 16-bit words // in intel byte order for CS8900 boolean valid; int age; static void init() { if (list!=null) return; list = new Entry[ENTRY_CNT]; for (int i=0; i<ENTRY_CNT; ++i) list[i] = new Entry(); // Static ARP entry: IP Broadcast -> Ethernet Broadcast list[0].ip = 0xFFFFFFFF; // 255.255.255.255 list[0].mac[0] = 0xFF; // -> resolves to FF:FF:FF:FF:FF:FF list[0].mac[1] = 0xFF; list[0].mac[2] = 0xFF; list[0].mac[3] = 0xFF; list[0].mac[4] = 0xFF; list[0].mac[5] = 0xFF; list[0].valid = true; list[0].age = 1; } Entry() { mac = new int[6]; ip = 0; valid = false; age = 0; } /** * Add an entry into the ARP table * @param p A received ARP request or reply */ static void add(Packet p) { int ip_src = (p.buf[3]<<16) + (p.buf[4]>>>16); int nr = -1; int oldest = list[0].age; int youngest = oldest; for (int i=0; i<ENTRY_CNT; ++i) { if (list[i].ip==ip_src) { // we have an entry for this IP // address nr = i; } if (list[i].age<oldest) { oldest = list[i].age; // replace the oldest entry if (nr==-1) { nr = i; } } else if (list[i].age>youngest) { youngest = list[i].age; } } Entry e = list[nr]; e.ip = ip_src; e.mac[0] = p.buf[2]>>>24; e.mac[1] = (p.buf[2]>>>16)&0xff; e.mac[2] = (p.buf[2]>>>8)&0xff; e.mac[3] = (p.buf[2])&0xff; e.mac[4] = (p.buf[3]>>>24); e.mac[5] = (p.buf[3]>>>16)&0xff; e.valid = true; e.age = youngest++; dump(nr); } static Entry find(int ip) { for (int i=0; i<ENTRY_CNT; ++i) { if (list[i].ip==ip && list[i].valid) { return list[i]; } } return null; } static void dump(int nr) { if (Logging.LOG) Logging.wr("add ARP IP="); if (Logging.LOG) Logging.hexVal(list[nr].ip); for (int i=0; i<6; ++i) { if (Logging.LOG) Logging.hexVal(list[nr].mac[i]); } } } Ejip ejip; int eth[]; int ip; LinkLayer ll; public Arp(Ejip ejip, LinkLayer interf, int ipAddr, int ethAddr[]) { eth = ethAddr; ip = ipAddr; this.ejip = ejip; ll = interf; Entry.init(); } /** * handle ARP request. */ protected void receive(Packet p, int[] eth, int ip) { // if (Logging.LOG) Logging.wr('A'); if (p.buf[6]==ip) { // System.out.println("ARP receive"); /* Ethernet transmission layer (not necessarily accessible to the user): 48.bit: Ethernet address of destination 48.bit: Ethernet address of sender 16.bit: Protocol type = ether_type$ADDRESS_RESOLUTION Ethernet packet data: 16.bit: (ar$hrd) Hardware address space (e.g., Ethernet, Packet Radio Net.) 16.bit: (ar$pro) Protocol address space. For Ethernet hardware, this is from the set of type fields ether_typ$<protocol>. 8.bit: (ar$hln) byte length of each hardware address 8.bit: (ar$pln) byte length of each protocol address 16.bit: (ar$op) opcode (ares_op$REQUEST | ares_op$REPLY) nbytes: (ar$sha) Hardware address of sender of this packet, n from the ar$hln field. mbytes: (ar$spa) Protocol address of sender of this packet, m from the ar$pln field. nbytes: (ar$tha) Hardware address of target of this packet (if known). mbytes: (ar$tpa) Protocol address of target. */ int arp_op = p.buf[1] & 0xffff; Entry.add(p); // Add the entry anyway if (arp_op==1) { // System.out.println("request"); } else if (arp_op==2) { // System.out.println("reply"); ejip.returnPacket(p); return; } else { if (Logging.LOG) Logging.wr("ARP unknown op: "); if (Logging.LOG) Logging.intVal(arp_op); } /* Set the ar$op field to ares_op$REPLY */ p.buf[1] = 0x06040002; // hw-len, sw-len, opcode reply /* Swap hardware and protocol fields, putting the local hardware and protocol addresses in the sender fields. */ int ip_src = (p.buf[3]<<16) + (p.buf[4]>>>16); p.buf[2] = (eth[0]<<24) + (eth[1]<<16) + (eth[2]<<8) + eth[3]; p.buf[3] = (eth[4]<<24) + (eth[5]<<16) + (ip>>>16); p.buf[4] = (ip<<16) + p.llh[3]; p.buf[5] = (p.llh[4]<<16) + p.llh[5]; p.buf[6] = ip_src; /* Send the packet to the (new) target hardware address on the same hardware on which the request was received. */ p.len = 46; p.llh[0] = p.llh[3]; p.llh[1] = p.llh[4]; p.llh[2] = p.llh[5]; p.llh[6] = 0x0806; // ARP frame ll.txQueue.enq(p); // mark packet ready to send } else { ejip.returnPacket(p); // mark packet free } } /** * Send an ARP request from LinkLayer to the source * address from the IP packet. The original IP packet * gets thrown away. * @param ll * @param ip */ protected void sendRequest(Packet p) { int ip_dest = p.buf[4]; p.buf[0] = 0x00010800; // hw addr. space 1, Protocol add. space IP p.buf[1] = 0x06040001; // hw-len, sw-len, opcode request p.buf[2] = (eth[0]<<24) + (eth[1]<<16) + (eth[2]<<8) + eth[3]; // could be this way // p.buf[3] = (eth[4] << 24) + (eth[5] << 16) // + (p.interf.getIpAddress() >>> 16); // p.buf[4] = (p.interf.getIpAddress() << 16); // we don't know the p.buf[3] = (eth[4]<<24) + (eth[5]<<16) + (ip>>>16); p.buf[4] = (ip<<16); // we don't know the dest. eth. addr. p.buf[5] = 0; p.buf[6] = ip_dest; // destination IP address p.len = 46; // why 46? p.llh[0] = 0xffff; // Ethernet broadcast p.llh[1] = 0xffff; p.llh[2] = 0xffff; // own Ethernet address is filled by CS8900 p.llh[6] = 0x0806; // ARP frame } /** * Fill in the destination MAC address. If not in * the cache use this packet for a ARP request. * The IP packet get's lost when not a TCP packet. * Returns the original or created copy for the link * layer to send. Can be null! * * @param p */ Packet fillMAC(Packet p) { Entry e; // IP destination address (without gateway) is // at position 4 for IP packets and at 6 for ARP packets if (p.llh[6] == 0x0806) { e = Entry.find(p.buf[6]); } else { e = Entry.find(p.buf[4]); } // TODO: for dhcp // int firstHopDest = CS8900.isSameSubnet(p.buf[addrPos]) ? // p.buf[addrPos] : Net.linkLayer.gateway; // int firstHopDest = p.buf[addrPos]; // Entry e = Entry.find(firstHopDest); if (e == null) { // If it's a TCP packet we need to make a copy // and rely on the TCP timeout retransmission if (p.isTcpOnFly) { Packet ap = ejip.getFreePacket(ll); if (ap!=null) { ap.copy(p); sendRequest(ap); return ap; } } else { sendRequest(p); } } else { int[] mac = e.mac; // intel byte order !!! p.llh[0] = mac[0] << 8 | mac[1]; p.llh[1] = mac[2] << 8 | mac[3]; p.llh[2] = mac[4] << 8 | mac[5]; } return p; } /** * Returns true if the MAC address of a given IP address is already in the * cache * * @param ip * @return boolean */ public boolean inCache(int ip) { // TODO dhcp // return Entry.find(CS8900.isSameSubnet(ip) ? ip : // Net.linkLayer.gateway) != null; return Entry.find(ip) != null; } }