📄 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. */intarp_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. */voidarp_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. */intarp_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! */voidarp_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. */voidarp_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. */voidarp_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? */intarp_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 intarp_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 intarp_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 intarp_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. */intarp_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 + -