⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 arp.c

📁 基于linux1.0内核的linux源码
💻 C
📖 第 1 页 / 共 2 页
字号:
  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 + -