📄 arp.c
字号:
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;
}
if(arp_q==NULL)
arp_queue_kick();
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);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -