/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * This file implements the Address Resolution Protocol (ARP), * which is used by TCP/IP to map the IP addresses from a host * to a low-level hardware address (like an Ethernet address) * which it can use to talk to that host. * * NOTE: This module will be rewritten completely in the near future, * because I want it to become a multi-address-family address * resolver, like it should be. It will be put in a separate * directory under 'net', being a protocol of its own. -FvK * * Version: @(#)arp.c 1.0.15 05/25/93 * * Authors: Ross Biro, * Fred N. van Kempen, * Stephen A. Wood, * Arnt Gulbrandsen, * * Fixes: * 'Mr Linux' : arp problems. * Alan Cox : arp_ioctl now checks memory areas with verify_area. * Alan Cox : Non IP arp message now only appears with debugging on. * Alan Cox : arp queue is volatile (may be altered by arp messages while doing sends) * Generic queue code is urgently needed! * Alan Cox : Deleting your own ip addr now gives EINVAL not a printk message. * Alan Cox : Fix to arp linked list error * Alan Cox : Ignore broadcast arp (Linus' idea 8-)) * Alan Cox : arp_send memory leak removed * Alan Cox : generic skbuff code fixes. * Alan Cox : 'Bad Packet' only reported on debugging * Alan Cox : Proxy arp. * Alan Cox : skb->link3 maintained by letting the other xmit queue kill the packet. * Alan Cox : Knows about type 3 devices (AX.25) using an AX.25 protocol ID not the ethernet * one. * Dominik Kubla : Better checking * Tegge : Assorted corrections on cross port stuff * Alan Cox : ATF_PERM was backwards! - might be useful now (sigh) * * To Fix: * : arp response allocates an skbuff to send. However there is a perfectly * good spare skbuff the right size about to be freed (the query). Use the * query for the reply. This avoids an out of memory case _and_ speeds arp * up. * : FREE_READ v FREE_WRITE errors. Not critical as loopback arps don't occur * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inet.h" #include "dev.h" #include "eth.h" #include "ip.h" #include "route.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" #include "sock.h" #include "arp.h" #define ARP_MAX_TRIES 3 static char *unk_print(unsigned char *, int); static char *eth_aprint(unsigned char *, int); static char *arp_cmds[] = { "0x%04X", "REQUEST", "REPLY", "REVERSE REQUEST", "REVERSE REPLY", NULL }; #define ARP_MAX_CMDS (sizeof(arp_cmds) / sizeof(arp_cmds[0])) static struct { char *name; char *(*print)(unsigned char *ptr, int len); } arp_types[] = { { "0x%04X", unk_print }, { "10 Mbps Ethernet", eth_aprint }, { "3 Mbps Ethernet", eth_aprint }, { "AX.25", unk_print }, { "Pronet", unk_print }, { "Chaos", unk_print }, { "IEEE 802.2 Ethernet (?)", eth_aprint }, { "Arcnet", unk_print }, { "AppleTalk", unk_print }, { NULL, NULL } }; #define ARP_MAX_TYPE (sizeof(arp_types) / sizeof(arp_types[0])) struct arp_table *arp_tables[ARP_TABLE_SIZE] = { NULL, }; static int arp_proxies=0; /* So we can avoid the proxy arp overhead with the usual case of no proxy arps */ struct sk_buff * volatile arp_q = NULL; static struct arp_table *arp_lookup(unsigned long addr); static struct arp_table *arp_lookup_proxy(unsigned long addr); /* Dump the ADDRESS bytes of an unknown hardware type. */ static char * unk_print(unsigned char *ptr, int len) { static char buff[32]; char *bufp = buff; int i; for (i = 0; i < len; i++) bufp += sprintf(bufp, "%02X ", (*ptr++ & 0377)); return(buff); } /* Dump the ADDRESS bytes of an Ethernet hardware type. */ static char * eth_aprint(unsigned char *ptr, int len) { if (len != ETH_ALEN) return(""); return(eth_print(ptr)); } /* Dump an ARP packet. Not complete yet for non-Ethernet packets. */ static void arp_print(struct arphdr *arp) { int len, idx; unsigned char *ptr; if (inet_debug != DBG_ARP) return; printk("ARP: "); if (arp == NULL) { printk("(null)\n"); return; } /* Print the opcode name. */ len = htons(arp->ar_op); if (len < ARP_MAX_CMDS) idx = len; else idx = 0; printk("op "); printk(arp_cmds[idx], len); /* Print the ARP header. */ len = htons(arp->ar_hrd); if (len < ARP_MAX_TYPE) idx = len; else idx = 0; printk(" hrd = "); printk(arp_types[idx].name, len); printk(" pro = 0x%04X\n", htons(arp->ar_pro)); printk(" hlen = %d plen = %d\n", arp->ar_hln, arp->ar_pln); /* * Print the variable data. * When ARP gets redone (after the formal introduction of NET-2), * this part will be redone. ARP will then be a multi-family address * resolver, and the code below will be made more general. -FvK */ ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short); printk(" sender HA = %s ", arp_types[idx].print(ptr, arp->ar_hln)); ptr += arp->ar_hln; printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr)); ptr += arp->ar_pln; printk(" target HA = %s ", arp_types[idx].print(ptr, arp->ar_hln)); ptr += arp->ar_hln; printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr)); } /* This will try to retransmit everything on the queue. */ static void arp_send_q(void) { struct sk_buff *skb; struct sk_buff *volatile work_q; cli(); work_q = arp_q; skb_new_list_head(&work_q); arp_q = NULL; sti(); while((skb=skb_dequeue(&work_q))!=NULL) { IS_SKB(skb); skb->magic = 0; skb->next = NULL; skb->prev = NULL; /* Decrement the 'tries' counter. */ cli(); skb->tries--; if (skb->tries == 0) { /* * Grmpf. * We have tried ARP_MAX_TRIES to resolve the IP address * from this datagram. This means that the machine does * not listen to our ARP requests. Perhaps someone tur- * ned off the thing? * In any case, trying further is useless. So, we kill * this packet from the queue. (grinnik) -FvK */ skb->sk = NULL; if(skb->free) kfree_skb(skb, FREE_WRITE); /* If free was 0, magic is now 0, next is 0 and the write queue will notice and kill */ sti(); continue; } /* Can we now complete this packet? */ sti(); if (skb->arp || !skb->dev->rebuild_header(skb->data, skb->dev)) { skb->arp = 1; skb->dev->queue_xmit(skb, skb->dev, 0); } else { /* Alas. Re-queue it... */ skb->magic = ARP_QUEUE_MAGIC; skb_queue_head(&arp_q,skb); } } } /* Create and send our response to an ARP request. */ static int arp_response(struct arphdr *arp1, struct device *dev, int addrtype) { struct arphdr *arp2; struct sk_buff *skb; unsigned long src, dst; unsigned char *ptr1, *ptr2; int hlen; struct arp_table *apt = NULL;/* =NULL otherwise the compiler gives warnings */ /* Decode the source (REQUEST) message. */ ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short); src = *((unsigned long *) (ptr1 + arp1->ar_hln)); dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln)); if(addrtype!=IS_MYADDR) { apt=arp_lookup_proxy(dst); if(apt==NULL) return(1); } /* Get some mem and initialize it for the return trip. */ skb = alloc_skb(sizeof(struct sk_buff) + sizeof(struct arphdr) + (2 * arp1->ar_hln) + (2 * arp1->ar_pln) + dev->hard_header_len, GFP_ATOMIC); if (skb == NULL) { printk("ARP: no memory available for ARP REPLY!\n"); return(1); } skb->mem_addr = skb; skb->len = sizeof(struct arphdr) + (2 * arp1->ar_hln) + (2 * arp1->ar_pln) + dev->hard_header_len; skb->mem_len = sizeof(struct sk_buff) + skb->len; hlen = dev->hard_header(skb->data, dev, ETH_P_ARP, src, dst, skb->len); if (hlen < 0) { printk("ARP: cannot create HW frame header for REPLY !\n"); kfree_skb(skb, FREE_WRITE); return(1); } /* * Fill in the ARP REPLY packet. * This looks ugly, but we have to deal with the variable-length * ARP packets and such. It is not as bad as it looks- FvK */ arp2 = (struct arphdr *) (skb->data + hlen); ptr2 = ((unsigned char *) &arp2->ar_op) + sizeof(u_short); arp2->ar_hrd = arp1->ar_hrd; arp2->ar_pro = arp1->ar_pro; arp2->ar_hln = arp1->ar_hln; arp2->ar_pln = arp1->ar_pln; arp2->ar_op = htons(ARPOP_REPLY); if(addrtype==IS_MYADDR) memcpy(ptr2, dev->dev_addr, arp2->ar_hln); else /* Proxy arp, so pull from the table */ memcpy(ptr2, apt->ha, arp2->ar_hln); ptr2 += arp2->ar_hln; memcpy(ptr2, ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln, arp2->ar_pln); ptr2 += arp2->ar_pln; memcpy(ptr2, ptr1, arp2->ar_hln); ptr2 += arp2->ar_hln; memcpy(ptr2, ptr1 + arp1->ar_hln, arp2->ar_pln); skb->free = 1; skb->arp = 1; skb->sk = NULL; skb->next = NULL; DPRINTF((DBG_ARP, ">>")); arp_print(arp2); /* Queue the packet for transmission. */ dev->queue_xmit(skb, dev, 0); return(0); } /* This will find an entry in the ARP table by looking at the IP address. */ static struct arp_table * arp_lookup(unsigned long paddr) { struct arp_table *apt; unsigned long hash; DPRINTF((DBG_ARP, "ARP: lookup(%s)\n", in_ntoa(paddr))); /* We don't want to ARP ourselves. */ if (chk_addr(paddr) == IS_MYADDR) { printk("ARP: ARPing my own IP address %s !\n", in_ntoa(paddr)); return(NULL); } /* Loop through the table for the desired address. */ hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); cli(); apt = arp_tables[hash]; while(apt != NULL) { if (apt->ip == paddr) { sti(); return(apt); } apt = apt->next; } sti(); return(NULL); } /* This will find a proxy in the ARP table by looking at the IP address. */ static struct arp_table *arp_lookup_proxy(unsigned long paddr) { struct arp_table *apt; unsigned long hash; DPRINTF((DBG_ARP, "ARP: lookup proxy(%s)\n", in_ntoa(paddr))); /* Loop through the table for the desired address. */ hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); cli(); apt = arp_tables[hash]; while(apt != NULL) { if (apt->ip == paddr && (apt->flags & ATF_PUBL) ) { sti(); return(apt); } apt = apt->next; } sti(); return(NULL); } /* Delete an ARP mapping entry in the cache. */ void arp_destructor(unsigned long paddr, int force) { struct arp_table *apt; struct arp_table **lapt; unsigned long hash; DPRINTF((DBG_ARP, "ARP: destroy(%s)\n", in_ntoa(paddr))); /* We cannot destroy our own ARP entry. */ if (chk_addr(paddr) == IS_MYADDR) { DPRINTF((DBG_ARP, "ARP: Destroying my own IP address %s !\n", in_ntoa(paddr))); return; } hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); cli(); lapt = &arp_tables[hash]; while ((apt = *lapt) != NULL) { if (apt->ip == paddr) { if((apt->flags&ATF_PERM) && !force) return; *lapt = apt->next; if(apt->flags&ATF_PUBL) arp_proxies--; kfree_s(apt, sizeof(struct arp_table)); sti(); return; } lapt = &apt->next; } sti(); } /* * Kill an entry - eg for ioctl() */ void arp_destroy(unsigned long paddr) { arp_destructor(paddr,1); } /* * Delete a possibly invalid entry (see timer.c) */ void arp_destroy_maybe(unsigned long paddr) { arp_destructor(paddr,0); } /* Create an ARP entry. The caller should check for duplicates! */ static struct arp_table * arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype) { struct arp_table *apt; unsigned long hash; DPRINTF((DBG_ARP, "ARP: create(%s, ", in_ntoa(paddr))); DPRINTF((DBG_ARP, "%s, ", eth_print(addr))); DPRINTF((DBG_ARP, "%d, %d)\n", hlen, htype)); apt = (struct arp_table *) kmalloc(sizeof(struct arp_table), GFP_ATOMIC); if (apt == NULL) { printk("ARP: no memory available for new ARP entry!\n"); return(NULL); } /* Fill in the allocated ARP cache entry. */ hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); apt->ip = paddr; apt->hlen = hlen; apt->htype = htype; apt->flags = (ATF_INUSE | ATF_COM); /* USED and COMPLETED entry */ memcpy(apt->ha, addr, hlen); apt->last_used = jiffies; cli(); apt->next = arp_tables[hash]; arp_tables[hash] = apt; sti(); return(apt); } /* * An ARP REQUEST packet has arrived. * We try to be smart here, and fetch the data of the sender of the * packet- we might need it later, so fetching it now can save us a * broadcast later. * Then, if the packet was meant for us (i.e. the TARGET address was * one of our own IP addresses), we set up and send out an ARP REPLY * packet to the sender. */ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { struct arphdr *arp; struct arp_table *tbl; unsigned long src, dst; unsigned char *ptr; int ret; int addr_hint; DPRINTF((DBG_ARP, "<<\n")); arp = skb->h.arp; arp_print(arp); /* If this test doesn't pass, its not IP. Might be DECNET or friends */ if (arp->ar_hln != dev->addr_len || dev->type != NET16(arp->ar_hrd)) { DPRINTF((DBG_ARP,"ARP: Bad packet received on device \"%s\" !\n", dev->name)); kfree_skb(skb, FREE_READ); return(0); } /* For now we will only deal with IP addresses. */ if (((arp->ar_pro != NET16(0x00CC) && dev->type==3) || (arp->ar_pro != NET16(ETH_P_IP) && dev->type!=3) ) || arp->ar_pln != 4) { if (arp->ar_op != NET16(ARPOP_REQUEST)) DPRINTF((DBG_ARP,"ARP: Non-IP request on device \"%s\" !\n", dev->name)); kfree_skb(skb, FREE_READ); return(0); } /* * As said before, we try to be smart by using the * info already present in the packet: the sender's * IP and hardware address. */ ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short); memcpy(&src, ptr + arp->ar_hln, arp->ar_pln); tbl = arp_lookup(src); if (tbl != NULL) { DPRINTF((DBG_ARP, "ARP: udating entry for %s\n", in_ntoa(src))); memcpy(tbl->ha, ptr, arp->ar_hln); tbl->hlen = arp->ar_hln; tbl->flags |= ATF_COM; tbl->last_used = jiffies; } else { memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln); if (chk_addr(dst) != IS_MYADDR && arp_proxies == 0) { kfree_skb(skb, FREE_READ); return(0); } else { tbl = arp_create(src, ptr, arp->ar_hln, arp->ar_hrd); if (tbl == NULL) { kfree_skb(skb, FREE_READ); return(0); } } } /* * Since we updated the ARP cache, we might have enough * information to send out some previously queued IP * datagrams.... */ arp_send_q(); /* * OK, we used that part of the info. Now check if the * request was an ARP REQUEST for one of our own addresses.. */ if (arp->ar_op != NET16(ARPOP_REQUEST)) { kfree_skb(skb, FREE_READ); return(0); } /* * A broadcast arp, ignore it */ if(chk_addr(dst)==IS_BROADCAST) { kfree_skb(skb, FREE_READ); return 0; } memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln); if ((addr_hint=chk_addr(dst)) != IS_MYADDR && arp_proxies==0) { DPRINTF((DBG_ARP, "ARP: request was not for me!\n")); kfree_skb(skb, FREE_READ); return(0); } /* * Yes, it is for us. * Allocate, fill in and send an ARP REPLY packet. */ ret = arp_response(arp, dev, addr_hint); kfree_skb(skb, FREE_READ); return(ret); } /* Create and send an ARP REQUEST packet. */ void arp_send(unsigned long paddr, struct device *dev, unsigned long saddr) { struct sk_buff *skb; struct arphdr *arp; unsigned char *ptr; int tmp; DPRINTF((DBG_ARP, "ARP: send(paddr=%s, ", in_ntoa(paddr))); DPRINTF((DBG_ARP, "dev=%s, ", dev->name)); DPRINTF((DBG_ARP, "saddr=%s)\n", in_ntoa(saddr))); skb = alloc_skb(sizeof(struct sk_buff) + sizeof(struct arphdr) + (2 * dev->addr_len) + dev->hard_header_len + (2 * 4 /* arp->plen */), GFP_ATOMIC); if (skb == NULL) { printk("ARP: No memory available for REQUEST %s\n", in_ntoa(paddr)); return; } /* Fill in the request. */ skb->sk = NULL; skb->mem_addr = skb; skb->len = sizeof(struct arphdr) + dev->hard_header_len + (2 * dev->addr_len) + 8; skb->mem_len = sizeof(struct sk_buff) + skb->len; skb->arp = 1; skb->dev = dev; skb->next = NULL; skb->free = 1; tmp = dev->hard_header(skb->data, dev, ETH_P_ARP, 0, saddr, skb->len); if (tmp < 0) { kfree_skb(skb,FREE_WRITE); return; } arp = (struct arphdr *) (skb->data + tmp); arp->ar_hrd = htons(dev->type); if(dev->type!=3) /* AX.25 */ arp->ar_pro = htons(ETH_P_IP); else arp->ar_pro = htons(0xCC); arp->ar_hln = dev->addr_len; arp->ar_pln = 4; arp->ar_op = htons(ARPOP_REQUEST); ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short); memcpy(ptr, dev->dev_addr, arp->ar_hln); ptr += arp->ar_hln; memcpy(ptr, &saddr, arp->ar_pln); ptr += arp->ar_pln; /*memcpy(ptr, dev->broadcast, arp->ar_hln);*/ memset(ptr,0,arp->ar_hln); ptr += arp->ar_hln; memcpy(ptr, &paddr, arp->ar_pln); DPRINTF((DBG_ARP, ">>\n")); arp_print(arp); dev->queue_xmit(skb, dev, 0); } /* Find an ARP mapping in the cache. If not found, post a REQUEST. */ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, unsigned long saddr) { struct arp_table *apt; DPRINTF((DBG_ARP, "ARP: find(haddr=%s, ", eth_print(haddr))); DPRINTF((DBG_ARP, "paddr=%s, ", in_ntoa(paddr))); DPRINTF((DBG_ARP, "dev=%s, saddr=%s)\n", dev->name, in_ntoa(saddr))); switch(chk_addr(paddr)) { case IS_MYADDR: memcpy(haddr, dev->dev_addr, dev->addr_len); return(0); case IS_BROADCAST: memcpy(haddr, dev->broadcast, dev->addr_len); return(0); } apt = arp_lookup(paddr); if (apt != NULL) { /* * Make sure it's not too old. If it is too old, we will * just pretend we did not find it, and then arp_send will * verify the address for us. */ if ((apt->flags & ATF_PERM) || (apt->last_used < jiffies+ARP_TIMEOUT && apt->hlen != 0)) { apt->last_used = jiffies; memcpy(haddr, apt->ha, dev->addr_len); return(0); } else { DPRINTF((DBG_ARP, "ARP: find: found expired entry for %s\n", in_ntoa(apt->ip))); } } /* * This assume haddr are at least 4 bytes. * If this isn't true we can use a lookup table, one for every dev. * NOTE: this bit of code still looks fishy to me- FvK */ *(unsigned long *)haddr = paddr; /* If we didn't find an entry, we will try to send an ARP packet. */ arp_send(paddr, dev, saddr); return(1); } /* Add an entry to the ARP cache. Check for dupes! */ void arp_add(unsigned long addr, unsigned char *haddr, struct device *dev) { struct arp_table *apt; DPRINTF((DBG_ARP, "ARP: add(%s, ", in_ntoa(addr))); DPRINTF((DBG_ARP, "%s, ", eth_print(haddr))); DPRINTF((DBG_ARP, "%d, %d)\n", dev->hard_header_len, dev->type)); /* This is probably a good check... */ if (addr == 0) { printk("ARP: add: will not add entry for 0.0.0.0 !\n"); return; } /* First see if the address is already in the table. */ apt = arp_lookup(addr); if (apt != NULL) { DPRINTF((DBG_ARP, "ARP: updating entry for %s\n", in_ntoa(addr))); apt->last_used = jiffies; memcpy(apt->ha, haddr , dev->addr_len); return; } arp_create(addr, haddr, dev->addr_len, dev->type); } /* Create an ARP entry for a device's broadcast address. */ void arp_add_broad(unsigned long addr, struct device *dev) { struct arp_table *apt; arp_add(addr, dev->broadcast, dev); apt = arp_lookup(addr); if (apt != NULL) { apt->flags |= ATF_PERM; } } /* Queue an IP packet, while waiting for the ARP reply packet. */ void arp_queue(struct sk_buff *skb) { cli(); skb->tries = ARP_MAX_TRIES; if (skb->next != NULL) { sti(); printk("ARP: arp_queue skb already on queue magic=%X.\n", skb->magic); return; } skb_queue_tail(&arp_q,skb); skb->magic = ARP_QUEUE_MAGIC; sti(); } /* * Write the contents of the ARP cache to a PROCfs file. * This is not by long perfect, as the internal ARP table doesn't * have all the info we would like to have. Oh well, it works for * now, eh? - FvK * Also note, that due to space limits, we cannot generate more than * 4Kbyte worth of data. This usually is enough, but I have seen * machines die from under me because of a *very* large ARP cache. * This can be simply tested by doing: * * # ping 255.255.255.255 * # arp -a * * Perhaps we should redo PROCfs to handle larger buffers? Michael? */ int arp_get_info(char *buffer) { struct arpreq *req; struct arp_table *apt; int i; char *pos; /* Loop over the ARP table and copy structures to the buffer. */ pos = buffer; i = 0; for (i = 0; i < ARP_TABLE_SIZE; i++) { cli(); apt = arp_tables[i]; sti(); while (apt != NULL) { if (pos < (buffer + 4000)) { req = (struct arpreq *) pos; memset((char *) req, 0, sizeof(struct arpreq)); req->arp_pa.sa_family = AF_INET; memcpy((char *) req->arp_pa.sa_data, (char *) &apt->ip, 4); req->arp_ha.sa_family = apt->htype; memcpy((char *) req->arp_ha.sa_data, (char *) &apt->ha, apt->hlen); req->arp_flags = apt->flags; } pos += sizeof(struct arpreq); cli(); apt = apt->next; sti(); } } return(pos - buffer); } /* Set (create) an ARP cache entry. */ static int arp_req_set(struct arpreq *req) { struct arpreq r; struct arp_table *apt; struct sockaddr_in *si; int htype, hlen; /* We only understand about IP addresses... */ memcpy_fromfs(&r, req, sizeof(r)); if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT); /* * Find out about the hardware type. * We have to be compatible with BSD UNIX, so we have to * assume that a "not set" value (i.e. 0) means Ethernet. */ si = (struct sockaddr_in *) &r.arp_pa; switch(r.arp_ha.sa_family) { case 0: case ARPHRD_ETHER: htype = ARPHRD_ETHER; hlen = ETH_ALEN; break; case ARPHRD_AX25: htype = ARPHRD_AX25; hlen = 7; break; default: return(-EPFNOSUPPORT); } /* Is there an existing entry for this address? */ if (si->sin_addr.s_addr == 0) { printk("ARP: SETARP: requested PA is 0.0.0.0 !\n"); return(-EINVAL); } apt = arp_lookup(si->sin_addr.s_addr); if (apt == NULL) { apt = arp_create(si->sin_addr.s_addr, (unsigned char *) r.arp_ha.sa_data, hlen, htype); if (apt == NULL) return(-ENOMEM); } /* We now have a pointer to an ARP entry. Update it! */ memcpy((char *) &apt->ha, (char *) &r.arp_ha.sa_data, hlen); apt->last_used = jiffies; apt->flags = r.arp_flags; if(apt->flags&ATF_PUBL) arp_proxies++; /* Count proxy arps so we know if to use it */ return(0); } /* Get an ARP cache entry. */ static int arp_req_get(struct arpreq *req) { struct arpreq r; struct arp_table *apt; struct sockaddr_in *si; /* We only understand about IP addresses... */ memcpy_fromfs(&r, req, sizeof(r)); if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT); /* Is there an existing entry for this address? */ si = (struct sockaddr_in *) &r.arp_pa; apt = arp_lookup(si->sin_addr.s_addr); if (apt == NULL) return(-ENXIO); /* We found it; copy into structure. */ memcpy((char *) r.arp_ha.sa_data, (char *) &apt->ha, apt->hlen); r.arp_ha.sa_family = apt->htype; /* Copy the information back */ memcpy_tofs(req, &r, sizeof(r)); return(0); } /* Delete an ARP cache entry. */ static int arp_req_del(struct arpreq *req) { struct arpreq r; struct sockaddr_in *si; /* We only understand about IP addresses... */ memcpy_fromfs(&r, req, sizeof(r)); if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT); si = (struct sockaddr_in *) &r.arp_pa; /* The system cope with this but splats up a nasty kernel message We trap it beforehand and tell the user off */ if(chk_addr(si->sin_addr.s_addr)==IS_MYADDR) return -EINVAL; arp_destroy(si->sin_addr.s_addr); return(0); } /* Handle an ARP layer I/O control request. */ int arp_ioctl(unsigned int cmd, void *arg) { int err; switch(cmd) { case DDIOCSDBG: return(dbg_ioctl(arg, DBG_ARP)); case SIOCDARP: if (!suser()) return(-EPERM); err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq)); if(err) return err; return(arp_req_del((struct arpreq *)arg)); case SIOCGARP: err=verify_area(VERIFY_WRITE,arg,sizeof(struct arpreq)); if(err) return err; return(arp_req_get((struct arpreq *)arg)); case SIOCSARP: if (!suser()) return(-EPERM); err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq)); if(err) return err; return(arp_req_set((struct arpreq *)arg)); default: return(-EINVAL); } /*NOTREACHED*/ return(0); }