Пример #1
0
int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
{
	struct rtable *rt;
	if (addr_len < sizeof(*usin)) 
	  	return(-EINVAL);

	if (usin->sin_family && usin->sin_family != AF_INET) 
	  	return(-EAFNOSUPPORT);
	if (usin->sin_addr.s_addr==INADDR_ANY)
		usin->sin_addr.s_addr=ip_my_addr();

	if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
		return -EACCES;			/* Must turn broadcast on first */
  	
  	rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute, sk->bound_device);
  	if (rt==NULL)
  		return -ENETUNREACH;
  	if(!sk->saddr)
	  	sk->saddr = rt->rt_src;		/* Update source address */
	if(!sk->rcv_saddr)
		sk->rcv_saddr = rt->rt_src;
	sk->daddr = usin->sin_addr.s_addr;
	sk->dummy_th.dest = usin->sin_port;
	sk->state = TCP_ESTABLISHED;
	if (sk->ip_route_cache)
	        ip_rt_put(sk->ip_route_cache);
	sk->ip_route_cache = rt;
	return(0);
}
Пример #2
0
int icmp_chkaddr(struct sk_buff *skb)
{
	struct icmphdr *icmph=(struct icmphdr *)(skb->h.raw + skb->h.iph->ihl*4);
	struct iphdr *iph = (struct iphdr *) (icmph + 1);
	void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len) = icmp_pointers[icmph->type].handler;

	if (handler == icmp_unreach || handler == icmp_redirect) {
		struct sock *sk;

		switch (iph->protocol) {
		case IPPROTO_TCP:
			{
			struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));

			sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest, skb->dev);
			if (!sk) return 0;
			if (sk->saddr != iph->saddr) return 0;
			if (sk->daddr != iph->daddr) return 0;
			if (sk->dummy_th.dest != th->dest) return 0;
			/*
			 * This packet came from us.
			 */
			return 1;
			}
		case IPPROTO_UDP:
			{
			struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));

			sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest, skb->dev);
			if (!sk) return 0;
			if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR)
				return 0;
			/*
			 * This packet may have come from us.
			 * Assume it did.
			 */
			return 1;
			}
		}
	}
	return 0;
}
Пример #3
0
int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
	   unsigned long saddr, struct sk_buff *skb)
{
	struct arp_table *entry;
	unsigned long hash;
#ifdef CONFIG_IP_MULTICAST
	unsigned long taddr;
#endif	

	switch (ip_chk_addr(paddr))
	{
		case IS_MYADDR:
			printk("ARP: arp called for own IP address\n");
			memcpy(haddr, dev->dev_addr, dev->addr_len);
			skb->arp = 1;
			return 0;
#ifdef CONFIG_IP_MULTICAST
		case IS_MULTICAST:
			if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
			{
				haddr[0]=0x01;
				haddr[1]=0x00;
				haddr[2]=0x5e;
				taddr=ntohl(paddr);
				haddr[5]=taddr&0xff;
				taddr=taddr>>8;
				haddr[4]=taddr&0xff;
				taddr=taddr>>8;
				haddr[3]=taddr&0x7f;
				return 0;
			}
		/*
		 *	If a device does not support multicast broadcast the stuff (eg AX.25 for now)
		 */
#endif
		
		case IS_BROADCAST:
			memcpy(haddr, dev->broadcast, dev->addr_len);
			skb->arp = 1;
			return 0;
	}
Пример #4
0
void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev)
{
	struct rt_req * rtr;
	struct rtable * rt;

	rt = ip_rt_route(dst, 0, NULL);
	if (!rt)
		return;

	if (rt->rt_gateway != src ||
	    rt->rt_dev != dev ||
	    ((gw^dev->pa_addr)&dev->pa_mask) ||
	    ip_chk_addr(gw))
	{
		ip_rt_put(rt);
		return;
	}
	ip_rt_put(rt);

	ip_rt_fast_lock();
	if (ip_rt_lock == 1)
	{
		rt_redirect_1(dst, gw, dev);
		ip_rt_unlock();
		return;
	}

	rtr = kmalloc(sizeof(struct rt_req), GFP_ATOMIC);
	if (rtr)
	{
		rtr->dst = dst;
		rtr->gw = gw;
		rtr->dev = dev;
		rt_req_enqueue(&rt_backlog, rtr);
		ip_rt_bh_mask |= RT_BH_REDIRECT;
	}
	ip_rt_unlock();
}
Пример #5
0
int 
ip_options_compile(unsigned char *iph)
{
  int l;
  unsigned char *optptr;
  int optlen;
  unsigned char *pp_ptr = 0;
  char optholder[16];
  struct options *opt;
  int skb = 1;
  int skb_pa_addr = 314159;

  opt = (struct options *) optholder;
  memset(opt, 0, sizeof(struct options));
  opt->optlen = ((struct iphdr *) iph)->ihl * 4 - sizeof(struct iphdr);
  optptr = iph + sizeof(struct iphdr);
  opt->is_data = 0;

  for (l = opt->optlen; l > 0;) {
    switch (*optptr) {
    case IPOPT_END:
      for (optptr++, l--; l > 0; l--) {
	if (*optptr != IPOPT_END) {
	  *optptr = IPOPT_END;
	  opt->is_changed = 1;
	}
      }
      goto eol;
    case IPOPT_NOOP:
      l--;
      optptr++;
      continue;
    }
    optlen = optptr[1];
    if (optlen < 2 || optlen > l) {
      pp_ptr = optptr;
      goto error;
    }
    switch (*optptr) {
    case IPOPT_SSRR:
    case IPOPT_LSRR:
      if (optlen < 3) {
	pp_ptr = optptr + 1;
	goto error;
      }
      if (optptr[2] < 4) {
	pp_ptr = optptr + 2;
	goto error;
      }
      /* NB: cf RFC-1812 5.2.4.1 */
      if (opt->srr) {
	pp_ptr = optptr;
	goto error;
      }
      if (!skb) {
	if (optptr[2] != 4 || optlen < 7 || ((optlen - 3) & 3)) {
	  pp_ptr = optptr + 1;
	  goto error;
	}
	memcpy(&opt->faddr, &optptr[3], 4);
	if (optlen > 7)
	  memmove(&optptr[3], &optptr[7], optlen - 7);
      }
      opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
      opt->srr = optptr - iph;
      break;
    case IPOPT_RR:
      if (opt->rr) {
	pp_ptr = optptr;
	goto error;
      }
      if (optlen < 3) {
	pp_ptr = optptr + 1;
	goto error;
      }
      if (optptr[2] < 4) {
	pp_ptr = optptr + 2;
	goto error;
      }
      if (optptr[2] <= optlen) {
	if (optptr[2] + 3 > optlen) {
	  pp_ptr = optptr + 2;
	  goto error;
	}
	if (skb) {
	  memcpy(&optptr[optptr[2] - 1], &skb_pa_addr, 4);
	  opt->is_changed = 1;
	}
	optptr[2] += 4;
	opt->rr_needaddr = 1;
      }
      opt->rr = optptr - iph;
      break;
    case IPOPT_TIMESTAMP:
      if (opt->ts) {
	pp_ptr = optptr;
	goto error;
      }
      if (optlen < 4) {
	pp_ptr = optptr + 1;
	goto error;
      }
      if (optptr[2] < 5) {
	pp_ptr = optptr + 2;
	goto error;
      }
      if (optptr[2] <= optlen) {
	struct timestamp *ts = (struct timestamp *) (optptr + 1);
	__u32 *timeptr = 0;

	if (ts->ptr + 3 > ts->len) {
	  pp_ptr = optptr + 2;
	  goto error;
	}
	switch (ts->flags) {
	case IPOPT_TS_TSONLY:
	  opt->ts = optptr - iph;
	  if (skb)
	    timeptr = (__u32 *) & optptr[ts->ptr - 1];
	  opt->ts_needtime = 1;
	  ts->ptr += 4;
	  break;
	case IPOPT_TS_TSANDADDR:
	  if (ts->ptr + 7 > ts->len) {
	    pp_ptr = optptr + 2;
	    goto error;
	  }
	  opt->ts = optptr - iph;
	  if (skb) {
	    memcpy(&optptr[ts->ptr - 1], &skb_pa_addr, 4);
	    timeptr = (__u32 *) & optptr[ts->ptr + 3];
	  }
	  opt->ts_needaddr = 1;
	  opt->ts_needtime = 1;
	  ts->ptr += 8;
	  break;
	case IPOPT_TS_PRESPEC:
	  if (ts->ptr + 7 > ts->len) {
	    pp_ptr = optptr + 2;
	    goto error;
	  }
	  opt->ts = optptr - iph;
	  {
	    __u32 addr;

	    memcpy(&addr, &optptr[ts->ptr - 1], 4);
	    if (ip_chk_addr(addr) == 0)
	      break;
	    if (skb)
	      timeptr = (__u32 *) & optptr[ts->ptr + 3];
	  }
	  opt->ts_needaddr = 1;
	  opt->ts_needtime = 1;
	  ts->ptr += 8;
	  break;
	default:
	  pp_ptr = optptr + 3;
	  goto error;
	}
	if (timeptr) {
	  //struct timeval tv;
	  __u32 midtime = 1;

	  //do_gettimeofday(&tv);
	  //midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
	  memcpy(timeptr, &midtime, sizeof(__u32));
	  opt->is_changed = 1;
	}
      }
      else {
	struct timestamp *ts = (struct timestamp *) (optptr + 1);

	if (ts->overflow == 15) {
	  pp_ptr = optptr + 3;
	  goto error;
	}
	opt->ts = optptr - iph;
	if (skb) {
	  ts->overflow++;
	  opt->is_changed = 1;
	}
      }
      break;
    case IPOPT_SEC:
    case IPOPT_SID:
    default:
      if (!skb) {
	pp_ptr = optptr;
	goto error;
      }
      break;
    }
    l -= optlen;
    optptr += optlen;
  }

eol:
  if (!pp_ptr)
    if (!((struct options *) optholder)->srr)
      return 0;

error:
  return -1;
}
Пример #6
0
static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
	       int addr_len)
{
	struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
	struct sock *sk=(struct sock *)sock->data, *sk2;
	unsigned short snum = 0 /* Stoopid compiler.. this IS ok */;
	int chk_addr_ret;

	/* check this error. */
	if (sk->state != TCP_CLOSE)
		return(-EIO);
	if(addr_len<sizeof(struct sockaddr_in))
		return -EINVAL;
		
	if(sock->type != SOCK_RAW)
	{
		/* 验证sk已经加入到对应传输层协议的管理结构中 */
		if (sk->num != 0) 
			return(-EINVAL);

		snum = ntohs(addr->sin_port);

		/*
		 * We can't just leave the socket bound wherever it is, it might
		 * be bound to a privileged port. However, since there seems to
		 * be a bug here, we will leave it if the port is not privileged.
		 */
		if (snum == 0) 
		{
			snum = get_new_socknum(sk->prot, 0);
		}
		if (snum < PROT_SOCK && !suser()) 
			return(-EACCES);
	}
	
	chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST)
		return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
	  	
	if (chk_addr_ret || addr->sin_addr.s_addr == 0)
		sk->saddr = addr->sin_addr.s_addr;
	
	if(sock->type != SOCK_RAW)
	{
		/* Make sure we are allowed to bind here. */
		cli();
		for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
					sk2 != NULL; sk2 = sk2->next) 
		{
		/* should be below! */
			if (sk2->num != snum) 
				continue;
			if (!sk->reuse)
			{
				sti();
				return(-EADDRINUSE);
			}
			
			if (sk2->num != snum) 
				continue;		/* more than one */
			if (sk2->saddr != sk->saddr) 
				continue;	/* socket per slot ! -FB */
			if (!sk2->reuse || sk2->state==TCP_LISTEN) 
			{
				sti();
				return(-EADDRINUSE);
			}
		}
		sti();

		remove_sock(sk);
		put_sock(snum, sk);
		sk->dummy_th.source = ntohs(sk->num);
		sk->daddr = 0;
		sk->dummy_th.dest = 0;
	}
	return(0);
}
Пример #7
0
static int raw_sendto(struct sock *sk, const unsigned char *from, 
	int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len)
{
	int err;
	struct sockaddr_in sin;

	/*
	 *	Check the flags. Only MSG_DONTROUTE is permitted.
	 */

	if (flags & MSG_OOB)		/* Mirror BSD error message compatibility */
		return -EOPNOTSUPP;
			 
	if (flags & ~MSG_DONTROUTE)
		return(-EINVAL);
	/*
	 *	Get and verify the address. 
	 */

	if (usin) 
	{
		if (addr_len < sizeof(sin)) 
			return(-EINVAL);
		memcpy(&sin, usin, sizeof(sin));
		if (sin.sin_family && sin.sin_family != AF_INET) 
			return(-EINVAL);
	}
	else 
	{
		if (sk->state != TCP_ESTABLISHED) 
			return(-EINVAL);
		sin.sin_family = AF_INET;
		sin.sin_port = sk->num;
		sin.sin_addr.s_addr = sk->daddr;
	}
	if (sin.sin_port == 0) 
		sin.sin_port = sk->num;
  
	if (sin.sin_addr.s_addr == INADDR_ANY)
		sin.sin_addr.s_addr = ip_my_addr();

	/*
	 *	BSD raw sockets forget to check SO_BROADCAST ....
	 */
	 
	if (!sk->bsdism && sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
		return -EACCES;

	if(sk->ip_hdrincl)
	{
		if(len>65535)
			return -EMSGSIZE;
		err=ip_build_xmit(sk, raw_getrawfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port, noblock);
	}
	else
	{
		if(len>65535-sizeof(struct iphdr))
			return -EMSGSIZE;
		err=ip_build_xmit(sk, raw_getfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port, noblock);
	}
	return err<0?err:len;
}
Пример #8
0
int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
	__u32 daddr, unsigned short len,
	__u32 saddr, int redo, struct inet_protocol *protocol)
{
  	struct sock *sk;
  	struct udphdr *uh;
	unsigned short ulen;
	int addr_type;

	/*
	 * If we're doing a "redo" (the socket was busy last time
	 * around), we can just queue the packet now..
	 */
	if (redo) {
		udp_queue_rcv_skb(skb->sk, skb);
		return 0;
	}

	/*
	 * First time through the loop.. Do all the setup stuff
	 * (including finding out the socket we go to etc)
	 */

	addr_type = IS_MYADDR;
	if(!dev || dev->pa_addr!=daddr)
		addr_type=ip_chk_addr(daddr);
		
	/*
	 *	Get the header.
	 */
	 
  	uh = (struct udphdr *) skb->h.uh;
  	
  	ip_statistics.IpInDelivers++;

	/*
	 *	Validate the packet and the UDP length.
	 */
	 
	ulen = ntohs(uh->len);
	
	if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) 
	{
		NETDEBUG(printk("UDP: short packet: %d/%d\n", ulen, len));
		udp_statistics.UdpInErrors++;
		kfree_skb(skb, FREE_WRITE);
		return(0);
	}

	/* RFC1122 warning: According to 4.1.3.6, we MUST discard any */
	/* datagram which has an invalid source address, either here or */
	/* in IP. */
	/* Right now, IP isn't doing it, and neither is UDP. It's on the */
	/* FIXME list for IP, though, so I wouldn't worry about it. */
	/* (That's the Right Place to do it, IMHO.) -- MS */

	if (uh->check && (
		( (skb->ip_summed == CHECKSUM_HW) && udp_check(uh, len, saddr, daddr, skb->csum ) ) ||
		( (skb->ip_summed == CHECKSUM_NONE) && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0)))
			  /* skip if CHECKSUM_UNNECESSARY */
		         )
	   )
	{
		/* <*****@*****.**> wants to know, who sent it, to
		   go and stomp on the garbage sender... */

	  /* RFC1122: OK.  Discards the bad packet silently (as far as */
	  /* the network is concerned, anyway) as per 4.1.3.4 (MUST). */

		NETDEBUG(printk("UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
		       ntohl(saddr),ntohs(uh->source),
		       ntohl(daddr),ntohs(uh->dest),
		       ulen));
		udp_statistics.UdpInErrors++;
		kfree_skb(skb, FREE_WRITE);
		return(0);
	}

	/*
	 *	These are supposed to be switched. 
	 */
	 
	skb->daddr = saddr;
	skb->saddr = daddr;

	len=ulen;

	skb->dev = dev;
	skb_trim(skb,len);

#ifdef CONFIG_IP_MULTICAST
	if (addr_type==IS_BROADCAST || addr_type==IS_MULTICAST)
		return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
#endif
#ifdef CONFIG_IP_TRANSPARENT_PROXY
	if(skb->redirport)
		sk = udp_v4_proxy_lookup(saddr, uh->source, daddr, uh->dest,
					 dev->pa_addr, skb->redirport, dev);
	else
#endif
	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, dev);
	
	if (sk == NULL) 
  	{
  		udp_statistics.UdpNoPorts++;
		if (addr_type != IS_BROADCAST && addr_type != IS_MULTICAST) 
		{
			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
		}
		/*
		 * Hmm.  We got an UDP broadcast to a port to which we
		 * don't wanna listen.  Ignore it.
		 */
		skb->sk = NULL;
		kfree_skb(skb, FREE_WRITE);
		return(0);
  	}
	udp_deliver(sk, skb);
	return 0;
}
Пример #9
0
static int udp_sendto(struct sock *sk, const unsigned char *from, int len, int noblock,
	   unsigned flags, struct sockaddr_in *usin, int addr_len)
{
	struct sockaddr_in sin;
	int tmp;
	__u32 saddr=0;

	/* 
	 *	Check the flags. We support no flags for UDP sending
	 */

#ifdef CONFIG_IP_TRANSPARENT_PROXY
	if (flags&~(MSG_DONTROUTE|MSG_PROXY))
#else
	if (flags&~MSG_DONTROUTE) 
#endif
	  	return(-EINVAL);
	/*
	 *	Get and verify the address. 
	 */
	 
	if (usin) 
	{
		if (addr_len < sizeof(sin)) 
			return(-EINVAL);
		if (usin->sin_family && usin->sin_family != AF_INET) 
			return(-EINVAL);
		if (usin->sin_port == 0) 
			return(-EINVAL);
	} 
	else 
	{
#ifdef CONFIG_IP_TRANSPARENT_PROXY
		/* We need to provide a sockaddr_in when using MSG_PROXY. */
		if (flags&MSG_PROXY)
			return(-EINVAL);
#endif
		if (sk->state != TCP_ESTABLISHED) 
			return(-EINVAL);
		sin.sin_family = AF_INET;
		sin.sin_port = sk->dummy_th.dest;
		sin.sin_addr.s_addr = sk->daddr;
		usin = &sin;
  	}
  
  	/*
  	 *	BSD socket semantics. You must set SO_BROADCAST to permit
  	 *	broadcasting of data.
  	 */
  	 
	/* RFC1122: OK.  Allows the application to select the specific */
	/* source address for an outgoing packet (MUST) as per 4.1.3.5. */
	/* Optional addition: a mechanism for telling the application what */
	/* address was used. (4.1.3.5, MAY) -- MS */

	/* RFC1122: MUST ensure that all outgoing packets have one */
	/* of this host's addresses as a source addr.(4.1.3.6) - bind in  */
	/* af_inet.c checks these. It does need work to allow BSD style */
	/* bind to multicast as is done by xntpd		*/

  	if(usin->sin_addr.s_addr==INADDR_ANY)
  		usin->sin_addr.s_addr=ip_my_addr();
  		
  	if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
	    	return -EACCES;			/* Must turn broadcast on first */

	lock_sock(sk);

	/* Send the packet. */
	tmp = udp_send(sk, usin, from, len, flags, saddr, noblock);

	/* The datagram has been sent off.  Release the socket. */
	release_sock(sk);
	return(tmp);
}
Пример #10
0
void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, struct device *dev)
{
	struct iphdr *iph;
	struct icmphdr *icmph;
	int atype, room;
	struct icmp_bxm icmp_param;
	__u32 saddr;
	
	/*
	 *	Find the original header
	 */
	 
	iph = skb_in->ip_hdr;
	
	/*
	 *	No replies to physical multicast/broadcast
	 */
	 
	if(skb_in->pkt_type!=PACKET_HOST)
		return;
		
	/*
	 *	Now check at the protocol level
	 */
	 
	atype=ip_chk_addr(iph->daddr);
	if(atype==IS_BROADCAST||atype==IS_MULTICAST)
		return;
		
	/*
	 *	Only reply to fragment 0. We byte re-order the constant
	 *	mask for efficiency.
	 */
	 
	if(iph->frag_off&htons(IP_OFFSET))
		return;
		
	/* 
	 *	If we send an ICMP error to an ICMP error a mess would result..
	 */
	 
	if(icmp_pointers[type].error)
	{
		/*
		 *	We are an error, check if we are replying to an ICMP error
		 */
		 
		if(iph->protocol==IPPROTO_ICMP)
		{
			icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
			/*
			 *	Assume any unknown ICMP type is an error. This isn't
			 *	specified by the RFC, but think about it..
			 */
			if(icmph->type>18 || icmp_pointers[icmph->type].error)
				return;
		}
	}

	/*
	 *	Check the rate limit
	 */

#ifndef CONFIG_NO_ICMP_LIMIT
	if (!xrlim_allow(type, iph->saddr))
		return;
#endif	

	/*
	 *	Construct source address and options.
	 */
	 
	saddr=iph->daddr;
	if(saddr!=dev->pa_addr && dev->pa_addr != 0 && ip_chk_addr(saddr)!=IS_MYADDR)
		saddr=dev->pa_addr;
	if(ip_options_echo(&icmp_param.replyopts, NULL, saddr, iph->saddr, skb_in))
		return;

	/*
	 *	Prepare data for ICMP header.
	 */

	icmp_param.icmph.type=type;
	icmp_param.icmph.code=code;
	icmp_param.icmph.un.gateway = info;
	icmp_param.data_ptr=iph;
	room = 576 - sizeof(struct iphdr) - icmp_param.replyopts.optlen;
	icmp_param.data_len=(iph->ihl<<2)+skb_in->len;	/* RFC says return as much as we can without exceeding 576 bytes */
	if (icmp_param.data_len > room)
		icmp_param.data_len = room;
	
	/*
	 *	Build and send the packet.
	 */

	icmp_build_xmit(&icmp_param, saddr, iph->saddr, 
			icmp_pointers[type].error ? 
			(iph->tos & 0x1E) | 0xC0 : iph->tos);
}
Пример #11
0
int icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
	 __u32 daddr, unsigned short len,
	 __u32 saddr, int redo, struct inet_protocol *protocol)
{
	struct icmphdr *icmph=(void *)skb->h.raw;
#ifdef CONFIG_IP_TRANSPARENT_PROXY
	int r;
#endif
	icmp_statistics.IcmpInMsgs++;
	
	if(len < sizeof(struct icmphdr))
	{
		icmp_statistics.IcmpInErrors++;
		NETDEBUG(printk(KERN_INFO "ICMP: runt packet\n"));
		kfree_skb(skb, FREE_READ);
		return 0;
	}
	
  	/*
	 *	Validate the packet
  	 */
	
	if (ip_compute_csum((unsigned char *) icmph, len)) 
	{
		/* Failed checksum! */
		icmp_statistics.IcmpInErrors++;
		NETDEBUG(printk(KERN_INFO "ICMP: failed checksum from %s!\n", in_ntoa(saddr)));
		kfree_skb(skb, FREE_READ);
		return(0);
	}
	
	/*
	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
	 *
	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently discarded.
	 */
	 
	if(icmph->type > 18)
	{
		icmp_statistics.IcmpInErrors++;		/* Is this right - or do we ignore ? */
		kfree_skb(skb,FREE_READ);
		return(0);
	}
	
	/*
	 *	Parse the ICMP message 
	 */

#ifdef CONFIG_IP_TRANSPARENT_PROXY
	/*
	 *	We may get non-local addresses and still want to handle them
	 *	locally, due to transparent proxying.
	 *	Thus, narrow down the test to what is really meant.
	 */
	if (daddr!=dev->pa_addr && ((r = ip_chk_addr(daddr)) == IS_BROADCAST || r == IS_MULTICAST))
#else
	if (daddr && daddr!=dev->pa_addr && ip_chk_addr(daddr) != IS_MYADDR)
#endif
	{
		/*
		 *	RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be silently ignored (we don't as it is used
		 *	by some network mapping tools).
		 *	RFC 1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently discarded if to broadcast/multicast.
		 */
		if (icmph->type != ICMP_ECHO) 
		{
			icmp_statistics.IcmpInErrors++;
			kfree_skb(skb, FREE_READ);
			return(0);
  		}
  		/*
  		 *	Reply the multicast/broadcast using a legal
  		 *	interface - in this case the device we got
  		 *	it from.
  		 */
		daddr=dev->pa_addr;
	}
	
	len-=sizeof(struct icmphdr);
	(*icmp_pointers[icmph->type].input)++;
	(icmp_pointers[icmph->type].handler)(icmph,skb,skb->dev,saddr,daddr,len);
	return 0;
}
Пример #12
0
/*
 * IPSEC <> netlink interface
 * Copyright (C) 1996, 1997  John Ioannidis.
 * Copyright (C) 1998, 1999, 2000, 2001  Richard Guy Briggs.
 * 
 * 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.  See <http://www.fsf.org/copyleft/gpl.txt>.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

char ipsec_netlink_c_version[] = "RCSID $Id: ipsec_netlink.c,v 1.56 2002/01/29 17:17:55 mcr Exp $";

#include <linux/config.h>
#include <linux/version.h>
#include <linux/kernel.h> /* printk() */

#include "ipsec_param.h"

#ifdef MALLOC_SLAB
# include <linux/slab.h> /* kmalloc() */
#else /* MALLOC_SLAB */
# include <linux/malloc.h> /* kmalloc() */
#endif /* MALLOC_SLAB */
#include <linux/errno.h>  /* error codes */
#include <linux/types.h>  /* size_t */
#include <linux/interrupt.h> /* mark_bh */

#include <linux/netdevice.h>   /* struct device, and other headers */
#include <linux/etherdevice.h> /* eth_type_trans */
#include <linux/ip.h>          /* struct iphdr */
#include <linux/skbuff.h>
#include <freeswan.h>
#ifdef SPINLOCK
# ifdef SPINLOCK_23
#  include <linux/spinlock.h> /* *lock* */
# else /* 23_SPINLOCK */
#  include <asm/spinlock.h> /* *lock* */
# endif /* 23_SPINLOCK */
#endif /* SPINLOCK */
#ifdef NET_21
# include <asm/uaccess.h>
# include <linux/in6.h>
# define ip_chk_addr inet_addr_type
# define IS_MYADDR RTN_LOCAL
#endif
#include <asm/checksum.h>
#include <net/ip.h>
#ifdef NETLINK_SOCK
# include <linux/netlink.h>
#else
# include <net/netlink.h>
#endif

#include "radij.h"
#include "ipsec_encap.h"
#include "ipsec_radij.h"
#include "ipsec_netlink.h"
#include "ipsec_xform.h"

#include "ipsec_rcv.h"
#include "ipsec_ah.h"
#include "ipsec_esp.h"

#ifdef CONFIG_IPSEC_DEBUG
# include "ipsec_tunnel.h"
#endif /* CONFIG_IPSEC_DEBUG */

#include <pfkeyv2.h>
#include <pfkey.h>

#ifdef CONFIG_IPSEC_DEBUG
 int debug_netlink = 0;
#endif /* CONFIG_IPSEC_DEBUG */

#define SENDERR(_x) do { len = -(_x); goto errlab; } while (0)

#if 0
int 
#ifdef NETLINK_SOCK
ipsec_callback(int proto, struct sk_buff *skb)
#else /* NETLINK_SOCK */
ipsec_callback(struct sk_buff *skb)
#endif /* NETLINK_SOCK */
{
	/*
	 * this happens when we write to /dev/ipsec (c 36 10)
	 */
	int len = skb->len;
	u_char *dat = (u_char *)skb->data;
	struct encap_msghdr *em	= (struct encap_msghdr *)dat;
	struct tdb *tdbp, *tprev;
	int i, nspis, error = 0;
#ifdef CONFIG_IPSEC_DEBUG
	struct eroute *eret;
	char sa[SATOA_BUF];
	size_t sa_len;
	struct sk_buff *first, *last;


	sa_len = satoa(em->em_said, 0, sa, SATOA_BUF);

	if(debug_netlink) {
		printk("klips_debug:ipsec_callback: "
		       "skb=0x%p skblen=%ld em_magic=%d em_type=%d\n",
		       skb,
		       (unsigned long int)skb->len,
		       em->em_magic,
		       em->em_type);
		switch(em->em_type) {
		case EMT_SETDEBUG:
			printk("klips_debug:ipsec_callback: "
			       "set ipsec_debug level\n");
			break;
		case EMT_DELEROUTE:
		case EMT_CLREROUTE:
		case EMT_CLRSPIS:
			break;
		default:
			printk("klips_debug:ipsec_callback: "
			       "called for SA:%s\n",
			       sa_len ? sa : " (error)");
		}
	}
#endif /* CONFIG_IPSEC_DEBUG */

	/* XXXX Temporarily disable netlink I/F code until it gets permanantly
	   ripped out in favour of PF_KEYv2 I/F. */
	SENDERR(EPROTONOSUPPORT);

	/*	em = (struct encap_msghdr *)dat; */
	if (em->em_magic != EM_MAGIC) {
		printk("klips_debug:ipsec_callback: "
		       "bad magic=%d failed, should be %d\n",
		       em->em_magic,
		       EM_MAGIC);
		SENDERR(EINVAL);
	}
	switch (em->em_type) {
	case EMT_SETDEBUG:
#ifdef CONFIG_IPSEC_DEBUG
		if(em->em_db_nl >> (sizeof(em->em_db_nl) * 8 - 1)) {
			em->em_db_nl &= ~(1 << (sizeof(em->em_db_nl) * 8 -1));
			debug_tunnel  |= em->em_db_tn;
			debug_netlink |= em->em_db_nl;
			debug_xform   |= em->em_db_xf;
			debug_eroute  |= em->em_db_er;
			debug_spi     |= em->em_db_sp;
			debug_radij   |= em->em_db_rj;
			debug_esp     |= em->em_db_es;
			debug_ah      |= em->em_db_ah;
			debug_rcv     |= em->em_db_rx;
			debug_pfkey   |= em->em_db_ky;
			if(debug_netlink)
				printk("klips_debug:ipsec_callback: set\n");
		} else {
			if(debug_netlink)
				printk("klips_debug:ipsec_callback: unset\n");
			debug_tunnel  &= em->em_db_tn;
			debug_netlink &= em->em_db_nl;
			debug_xform   &= em->em_db_xf;
			debug_eroute  &= em->em_db_er;
			debug_spi     &= em->em_db_sp;
			debug_radij   &= em->em_db_rj;
			debug_esp     &= em->em_db_es;
			debug_ah      &= em->em_db_ah;
			debug_rcv     &= em->em_db_rx;
			debug_pfkey   &= em->em_db_ky;
		}
#else /* CONFIG_IPSEC_DEBUG */
		printk("klips_debug:ipsec_callback: "
		       "debugging not enabled\n");
		SENDERR(EINVAL);
#endif /* CONFIG_IPSEC_DEBUG */
		break;

	case EMT_SETEROUTE:
		if ((error = ipsec_makeroute(&(em->em_eaddr), &(em->em_emask), em->em_ersaid, 0, NULL, NULL, NULL)))
			SENDERR(-error);
		break;

	case EMT_REPLACEROUTE:
		if ((error = ipsec_breakroute(&(em->em_eaddr), &(em->em_emask), &first, &last)) == EINVAL) {
			kfree_skb(first);
			kfree_skb(last);
			SENDERR(-error);
		}
		if ((error = ipsec_makeroute(&(em->em_eaddr), &(em->em_emask), em->em_ersaid, NULL, NULL)))
			SENDERR(-error);
		break;

	case EMT_DELEROUTE:
		if ((error = ipsec_breakroute(&(em->em_eaddr), &(em->em_emask), &first, &last)))
			kfree_skb(first);
			kfree_skb(last);
			SENDERR(-error);
		break;

	case EMT_CLREROUTE:
		if ((error = ipsec_cleareroutes()))
			SENDERR(-error);
		break;

	case EMT_SETSPI:
		if (em->em_if >= 5)	/* XXX -- why 5? */
			SENDERR(ENODEV);
		
		tdbp = gettdb(&(em->em_said));
		if (tdbp == NULL) {
			tdbp = (struct tdb *)kmalloc(sizeof (*tdbp), GFP_ATOMIC);

			if (tdbp == NULL)
				SENDERR(ENOBUFS);
			
			memset((caddr_t)tdbp, 0, sizeof(*tdbp));
			
			tdbp->tdb_said = em->em_said;
			tdbp->tdb_flags = em->em_flags;
			
			if(ip_chk_addr((unsigned long)em->em_said.dst.s_addr) == IS_MYADDR) {
				tdbp->tdb_flags |= EMT_INBOUND;
			}
			KLIPS_PRINT(debug_netlink & DB_NL_TDBCB,
				    "klips_debug:ipsec_callback: "
				    "existing Tunnel Descriptor Block not found (this is good) for SA: %s, %s-bound, allocating.\n",
				    sa_len ? sa : " (error)",
				    (tdbp->tdb_flags & EMT_INBOUND) ? "in" : "out");

/* XXX  		tdbp->tdb_rcvif = &(enc_softc[em->em_if].enc_if);*/
			tdbp->tdb_rcvif = NULL;
		} else {
			KLIPS_PRINT(debug_netlink & DB_NL_TDBCB,
				    "klips_debug:ipsec_callback: "
				    "EMT_SETSPI found an old Tunnel Descriptor Block for SA: %s, delete it first.\n",
				    sa_len ? sa : " (error)");
			SENDERR(EEXIST);
		}
		
		if ((error = tdb_init(tdbp, em))) {
			KLIPS_PRINT(debug_netlink & DB_NL_TDBCB,
				    "klips_debug:ipsec_callback: "
				    "EMT_SETSPI not successful for SA: %s, deleting.\n",
				    sa_len ? sa : " (error)");
			ipsec_tdbwipe(tdbp);
			
			SENDERR(-error);
		}

		tdbp->tdb_lifetime_addtime_c = jiffies/HZ;
		tdbp->tdb_state = 1;
		if(!tdbp->tdb_lifetime_allocations_c) {
			tdbp->tdb_lifetime_allocations_c += 1;
		}

		puttdb(tdbp);
		KLIPS_PRINT(debug_netlink & DB_NL_TDBCB,
			    "klips_debug:ipsec_callback: "
			    "EMT_SETSPI successful for SA: %s\n",
			    sa_len ? sa : " (error)");
		break;
		
	case EMT_DELSPI:
		if (em->em_if >= 5)	/* XXX -- why 5? */
			SENDERR(ENODEV);
		
		spin_lock_bh(&tdb_lock);
		
		tdbp = gettdb(&(em->em_said));
		if (tdbp == NULL) {
			KLIPS_PRINT(debug_netlink & DB_NL_TDBCB,
				    "klips_debug:ipsec_callback: "
				    "EMT_DELSPI Tunnel Descriptor Block not found for SA%s, could not delete.\n",
				    sa_len ? sa : " (error)");
			spin_unlock_bh(&tdb_lock);
			SENDERR(ENXIO);  /* XXX -- wrong error message... */
		} else {
			if((error = deltdbchain(tdbp))) {
				spin_unlock_bh(&tdb_lock);
				SENDERR(-error);
			}
		}
		spin_unlock_bh(&tdb_lock);

		break;
		
	case EMT_GRPSPIS:
		nspis = (len - EMT_GRPSPIS_FLEN) / sizeof(em->em_rel[0]);
		if ((nspis * (sizeof(em->em_rel[0]))) != (len - EMT_GRPSPIS_FLEN)) {
			printk("klips_debug:ipsec_callback: "
			       "EMT_GRPSPI message size incorrect, expected nspis(%d)*%d, got %d.\n",
			       nspis,
			       sizeof(em->em_rel[0]),
			       (len - EMT_GRPSPIS_FLEN));
			SENDERR(EINVAL);
			break;
		}
		
		spin_lock_bh(&tdb_lock);

		for (i = 0; i < nspis; i++) {
			KLIPS_PRINT(debug_netlink,
				    "klips_debug:ipsec_callback: "
				    "EMT_GRPSPI for SA(%d) %s,\n",
				    i,
				    sa_len ? sa : " (error)");
			if ((tdbp = gettdb(&(em->em_rel[i].emr_said))) == NULL) {
				KLIPS_PRINT(debug_netlink,
					    "klips_debug:ipsec_callback: "
					    "EMT_GRPSPI Tunnel Descriptor Block not found for SA%s, could not group.\n",
					    sa_len ? sa : " (error)");
				spin_unlock_bh(&tdb_lock);
				SENDERR(ENXIO);
			} else {
				if(tdbp->tdb_inext || tdbp->tdb_onext) {
					KLIPS_PRINT(debug_netlink,
						    "klips_debug:ipsec_callback: "
						    "EMT_GRPSPI Tunnel Descriptor Block already grouped for SA: %s, can't regroup.\n",
						    sa_len ? sa : " (error)");
					spin_unlock_bh(&tdb_lock);
					SENDERR(EBUSY);
				}
				em->em_rel[i].emr_tdb = tdbp;
			}
		}
		tprev = em->em_rel[0].emr_tdb;
		tprev->tdb_inext = NULL;
		for (i = 1; i < nspis; i++) {
			tdbp = em->em_rel[i].emr_tdb;
			tprev->tdb_onext = tdbp;
			tdbp->tdb_inext = tprev;
			tprev = tdbp;
		}
		tprev->tdb_onext = NULL;

		spin_unlock_bh(&tdb_lock);

		error = 0;
		break;
		
	case EMT_UNGRPSPIS:
		if (len != (8 + (sizeof(struct sa_id) + sizeof(struct tdb *)) /* 12 */) ) {
			printk("klips_debug:ipsec_callback: "
			       "EMT_UNGRPSPIS message size incorrect, expected %d, got %d.\n",
			       8 + (sizeof(struct sa_id) + sizeof(struct tdb *)),
				    len);
			SENDERR(EINVAL);
			break;
		}
		
		spin_lock_bh(&tdb_lock);

		if ((tdbp = gettdb(&(em->em_rel[0].emr_said))) == NULL) {
			KLIPS_PRINT(debug_netlink,
				    "klips_debug:ipsec_callback: "
				    "EMT_UGRPSPI Tunnel Descriptor Block not found for SA%s, could not ungroup.\n",
				    sa_len ? sa : " (error)");
			spin_unlock_bh(&tdb_lock);
			SENDERR(ENXIO);
		}
		while(tdbp->tdb_onext) {
			tdbp = tdbp->tdb_onext;
		}
		while(tdbp->tdb_inext) {
			tprev = tdbp;
			tdbp = tdbp->tdb_inext;
			tprev->tdb_inext = NULL;
			tdbp->tdb_onext = NULL;
		}

		spin_unlock_bh(&tdb_lock);

		break;
		
	case EMT_CLRSPIS:
		KLIPS_PRINT(debug_netlink,
			    "klips_debug:ipsec_callback: "
			    "spi clear called.\n");
		if (em->em_if >= 5)	/* XXX -- why 5? */
			SENDERR(ENODEV);
		ipsec_tdbcleanup(0);
		break;
	default:
		KLIPS_PRINT(debug_netlink,
			    "klips_debug:ipsec_callback: "
			    "unknown message type\n");
		SENDERR(EINVAL);
	}
Пример #13
0
int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
	struct iphdr *iph = skb->h.iph;
	struct sock *raw_sk=NULL;
	unsigned char hash;
	unsigned char flag = 0;
	struct inet_protocol *ipprot;
	int brd=IS_MYADDR;
	struct options * opt = NULL;
	int is_frag=0;
	__u32 daddr;

#ifdef CONFIG_FIREWALL
	int fwres;
	__u16 rport;
#endif	
#ifdef CONFIG_IP_MROUTE
	int mroute_pkt=0;
#endif	

#ifdef CONFIG_NET_IPV6
	/* 
	 *	Intercept IPv6 frames. We dump ST-II and invalid types just below..
	 */
	 
	if(iph->version == 6)
		return ipv6_rcv(skb,dev,pt);
#endif		

	ip_statistics.IpInReceives++;


	/*
	 *	Tag the ip header of this packet so we can find it
	 */

	skb->ip_hdr = iph;

	/*
	 *	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
	 *	RFC1122: 3.1.2.3 MUST discard a frame with invalid source address [NEEDS FIXING].
	 *
	 *	Is the datagram acceptable?
	 *
	 *	1.	Length at least the size of an ip header
	 *	2.	Version of 4
	 *	3.	Checksums correctly. [Speed optimisation for later, skip loopback checksums]
	 *	4.	Doesn't have a bogus length
	 *	(5.	We ought to check for IP multicast addresses and undefined types.. does this matter ?)
	 */

	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
		|| skb->len < ntohs(iph->tot_len))
	{
		ip_statistics.IpInHdrErrors++;
		kfree_skb(skb, FREE_WRITE);
		return(0);
	}

	/*
	 *	Our transport medium may have padded the buffer out. Now we know it
	 *	is IP we can trim to the true length of the frame.
	 *	Note this now means skb->len holds ntohs(iph->tot_len).
	 */

	skb_trim(skb,ntohs(iph->tot_len));
	
	if(skb->len < (iph->ihl<<2))
	{
		ip_statistics.IpInHdrErrors++;
		kfree_skb(skb, FREE_WRITE);
		return 0;
	}

	/*
	 *	Account for the packet (even if the packet is
	 *	not accepted by the firewall!). We do this after
	 *	the sanity checks and the additional ihl check
	 *	so we dont account garbage as we might do before.
	 */

#ifdef CONFIG_IP_ACCT
	ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
#endif	

	/*
	 *	Try to select closest <src,dst> alias device, if any.
	 *	net_alias_dev_rx32 returns main device if it 
	 *	fails to found other.
 	 *  	If successful, also incr. alias rx count.
	 *
	 *	Only makes sense for unicasts - Thanks ANK.
	 */

#ifdef CONFIG_NET_ALIAS
	if (skb->pkt_type == PACKET_HOST && iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) {
		skb->dev = dev = net_alias_dev_rx32(skb->dev, AF_INET, iph->saddr, iph->daddr);
	}
#endif

	if (iph->ihl > 5) 
	{
		skb->ip_summed = 0;
		if (ip_options_compile(NULL, skb))
			return(0);
		opt = (struct options*)skb->proto_priv;
#ifdef CONFIG_IP_NOSR
		if (opt->srr) 
		{
			kfree_skb(skb, FREE_READ);
			return -EINVAL;
		}
#endif					
	}
	
#if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG)
#define CONFIG_IP_ALWAYS_DEFRAG 1
#endif
#ifdef CONFIG_IP_ALWAYS_DEFRAG
	/*
	 * Defragment all incoming traffic before even looking at it.
	 * If you have forwarding enabled, this makes the system a
	 * defragmenting router.  Not a common thing.
	 * You probably DON'T want to enable this unless you have to.
	 * You NEED to use this if you want to use transparent proxying,
	 * otherwise, we can't vouch for your sanity.
	 */

	/*
	 *	See if the frame is fragmented.
	 */
	 
	if(iph->frag_off)
	{
		if (iph->frag_off & htons(IP_MF))
			is_frag|=IPFWD_FRAGMENT;
		/*
		 *	Last fragment ?
		 */
	
		if (iph->frag_off & htons(IP_OFFSET))
			is_frag|=IPFWD_LASTFRAG;
	
		/*
		 *	Reassemble IP fragments.
		 */

		if(is_frag)
		{
			/* Defragment. Obtain the complete packet if there is one */
			skb=ip_defrag(iph,skb,dev);
			if(skb==NULL)
				return 0;
			skb->dev = dev;
			iph=skb->h.iph;
			is_frag = 0;
			/*
			 * When the reassembled packet gets forwarded, the ip
			 * header checksum should be correct.
			 * For better performance, this should actually only
			 * be done in that particular case, i.e. set a flag
			 * here and calculate the checksum in ip_forward.
			 */
			ip_send_check(iph);
		}
	}

#endif
	/*
	 *	See if the firewall wants to dispose of the packet. 
	 */
	
#ifdef	CONFIG_FIREWALL

	if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport))<FW_ACCEPT)
	{
		if(fwres==FW_REJECT)
			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
		kfree_skb(skb, FREE_WRITE);
		return 0;	
	}

#ifdef	CONFIG_IP_TRANSPARENT_PROXY
	if (fwres==FW_REDIRECT)
		skb->redirport = rport;
	else
#endif
		skb->redirport = 0;
#endif
	
#ifndef CONFIG_IP_ALWAYS_DEFRAG
	/*
	 *	Remember if the frame is fragmented.
	 */
	 
	if(iph->frag_off)
	{
		if (iph->frag_off & htons(IP_MF))
			is_frag|=IPFWD_FRAGMENT;
		/*
		 *	Last fragment ?
		 */
	
		if (iph->frag_off & htons(IP_OFFSET))
			is_frag|=IPFWD_LASTFRAG;
	}
	
#endif
	/*
	 *	Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday.
	 *
	 *	This is inefficient. While finding out if it is for us we could also compute
	 *	the routing table entry. This is where the great unified cache theory comes
	 *	in as and when someone implements it
	 *
	 *	For most hosts over 99% of packets match the first conditional
	 *	and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at
	 *	function entry.
	 */
	daddr = iph->daddr;
#ifdef CONFIG_IP_TRANSPARENT_PROXY
	/*
	 *	ip_chksock adds still more overhead for forwarded traffic...
	 */
	if ( iph->daddr == skb->dev->pa_addr || skb->redirport || (brd = ip_chk_addr(iph->daddr)) != 0 || ip_chksock(skb))
#else
	if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
#endif
	{
		if (opt && opt->srr) 
	        {
			int srrspace, srrptr;
			__u32 nexthop;
			unsigned char * optptr = ((unsigned char *)iph) + opt->srr;

			if (brd != IS_MYADDR || skb->pkt_type != PACKET_HOST) 
			{
				kfree_skb(skb, FREE_WRITE);
				return 0;
			}

			for ( srrptr=optptr[2], srrspace = optptr[1];
			      srrptr <= srrspace;
			      srrptr += 4
			     ) 
			{
				int brd2;
				if (srrptr + 3 > srrspace) 
				{
					icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2,
						  skb->dev);
					kfree_skb(skb, FREE_WRITE);
					return 0;
				}
				memcpy(&nexthop, &optptr[srrptr-1], 4);
				if ((brd2 = ip_chk_addr(nexthop)) == 0)
					break;
				if (brd2 != IS_MYADDR) 
				{

					/*
					 *	ANK: should we implement weak tunneling of multicasts?
					 *	Are they obsolete? DVMRP specs (RFC-1075) is old enough...
					 *	[They are obsolete]
					 */
					kfree_skb(skb, FREE_WRITE);
					return -EINVAL;
				}
				memcpy(&daddr, &optptr[srrptr-1], 4);
			}
			if (srrptr <= srrspace) 
			{
				opt->srr_is_hit = 1;
				opt->is_changed = 1;
				if (sysctl_ip_forward) {
					if (ip_forward(skb, dev, is_frag, nexthop))
						kfree_skb(skb, FREE_WRITE);
				} else {
					ip_statistics.IpInAddrErrors++;
					kfree_skb(skb, FREE_WRITE);
				}
				return 0;
			}
		}

#ifdef CONFIG_IP_MULTICAST	
		if(!(dev->flags&IFF_ALLMULTI) && brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))
		{
			/*
			 *	Check it is for one of our groups
			 */
			struct ip_mc_list *ip_mc=dev->ip_mc_list;
			do
			{
				if(ip_mc==NULL)
				{	
					kfree_skb(skb, FREE_WRITE);
					return 0;
				}
				if(ip_mc->multiaddr==iph->daddr)
					break;
				ip_mc=ip_mc->next;
			}
			while(1);
		}
#endif

#ifndef CONFIG_IP_ALWAYS_DEFRAG
		/*
		 *	Reassemble IP fragments.
		 */

		if(is_frag)
		{
			/* Defragment. Obtain the complete packet if there is one */
			skb=ip_defrag(iph,skb,dev);
			if(skb==NULL)
				return 0;
			skb->dev = dev;
			iph=skb->h.iph;
		}

#endif

#ifdef CONFIG_IP_MASQUERADE
		/*
		 * Do we need to de-masquerade this packet?
		 */
		{
			int ret = ip_fw_demasquerade(&skb,dev);
			if (ret < 0) {
				kfree_skb(skb, FREE_WRITE);
				return 0;
			}

			if (ret)
			{
				struct iphdr *iph=skb->h.iph;
				if (ip_forward(skb, dev, IPFWD_MASQUERADED, iph->daddr))
					kfree_skb(skb, FREE_WRITE);
				return 0;
			}
		}
#endif

		/*
		 *	Point into the IP datagram, just past the header.
		 */

		skb->ip_hdr = iph;
		skb->h.raw += iph->ihl*4;

#ifdef CONFIG_IP_MROUTE		
		/*
		 *	Check the state on multicast routing (multicast and not 224.0.0.z)
		 */
		 
		if(brd==IS_MULTICAST && (iph->daddr&htonl(0xFFFFFF00))!=htonl(0xE0000000))
			mroute_pkt=1;

#endif
		/*
		 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
		 *
		 *	RFC 1122: SHOULD pass TOS value up to the transport layer.
		 */
 
		/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
		hash = iph->protocol & (MAX_INET_PROTOS - 1);

		/* 
		 *	If there maybe a raw socket we must check - if not we don't care less 
		 */
		 
		if((raw_sk = raw_v4_htable[hash]) != NULL) {
			struct sock *sknext = NULL;
			struct sk_buff *skb1;

			raw_sk = raw_v4_lookup(raw_sk, iph->protocol,
					       iph->saddr, iph->daddr);
			if(raw_sk) {	/* Any raw sockets */
				do {
					/* Find the next */
					sknext = raw_v4_lookup(raw_sk->next,
							       iph->protocol,
							       iph->saddr,
							       iph->daddr);
					if(sknext)
						skb1 = skb_clone(skb, GFP_ATOMIC);
					else
						break;	/* One pending raw socket left */
					if(skb1)
						raw_rcv(raw_sk, skb1, dev, iph->saddr,daddr);
					raw_sk = sknext;
				} while(raw_sk!=NULL);
				
				/*
				 *	Here either raw_sk is the last raw socket, or NULL if none 
				 */
				 
				/*
				 *	We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy 
				 */
			}
		}
	
		/*
		 *	skb->h.raw now points at the protocol beyond the IP header.
		 */
	
		for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
		{
			struct sk_buff *skb2;
	
			if (ipprot->protocol != iph->protocol)
				continue;
		       /*
			* 	See if we need to make a copy of it.  This will
			* 	only be set if more than one protocol wants it.
			* 	and then not for the last one. If there is a pending
			*	raw delivery wait for that
			*/
	
#ifdef CONFIG_IP_MROUTE
			if (ipprot->copy || raw_sk || mroute_pkt)
#else	
			if (ipprot->copy || raw_sk)
#endif			
			{
				skb2 = skb_clone(skb, GFP_ATOMIC);
				if(skb2==NULL)
					continue;
			}
			else
			{
				skb2 = skb;
			}
			flag = 1;

		       /*
			*	Pass on the datagram to each protocol that wants it,
			*	based on the datagram protocol.  We should really
			*	check the protocol handler's return values here...
			*/

			ipprot->handler(skb2, dev, opt, daddr,
				(ntohs(iph->tot_len) - (iph->ihl * 4)),
				iph->saddr, 0, ipprot);
		}

		/*
		 *	All protocols checked.
		 *	If this packet was a broadcast, we may *not* reply to it, since that
		 *	causes (proven, grin) ARP storms and a leakage of memory (i.e. all
		 *	ICMP reply messages get queued up for transmission...)
		 */

#ifdef CONFIG_IP_MROUTE		 
		/*
		 *	Forward the last copy to the multicast router. If
		 *	there is a pending raw delivery however make a copy
		 *	and forward that.
		 */
		 
		if(mroute_pkt)
		{
			flag=1;
			if(raw_sk==NULL)
				ipmr_forward(skb, is_frag);
			else
			{
				struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
				if(skb2)
				{
					skb2->free=1;
					ipmr_forward(skb2, is_frag);
				}
			}
		}
#endif		

		if(raw_sk!=NULL)	/* Shift to last raw user */
			raw_rcv(raw_sk, skb, dev, iph->saddr, daddr);
		else if (!flag)		/* Free and report errors */
		{
			if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);	
			kfree_skb(skb, FREE_WRITE);
		}

		return(0);
	}

	/*
	 *	Do any unicast IP forwarding required.
	 */
	
	/*
	 *	Don't forward multicast or broadcast frames.
	 */

	if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)
	{
		kfree_skb(skb,FREE_WRITE);
		return 0;
	}

	/*
	 *	The packet is for another target. Forward the frame
	 */

	if (sysctl_ip_forward) {
		if (opt && opt->is_strictroute) 
		{
			icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev);
			kfree_skb(skb, FREE_WRITE);
			return -1;
		}
		if (ip_forward(skb, dev, is_frag, iph->daddr))
			kfree_skb(skb, FREE_WRITE);
	} else {
/*	printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
			iph->saddr,iph->daddr);*/
		ip_statistics.IpInAddrErrors++;
		kfree_skb(skb, FREE_WRITE);
	}
	return(0);
}
Пример #14
0
int ip_options_echo(struct options * dopt, struct options * sopt,
		     __u32 daddr, __u32 saddr,
		     struct sk_buff * skb) 
{
	unsigned char *sptr, *dptr;
	int soffset, doffset;
	int	optlen;

	memset(dopt, 0, sizeof(struct options));

	dopt->is_data = 1;

	if (!sopt)
		sopt = (struct options*)skb->proto_priv;

	if (sopt->optlen == 0) 
	{
		dopt->optlen = 0;
		return 0;
	}

	sptr = (sopt->is_data ? sopt->__data - sizeof(struct iphdr) :
		(unsigned char *)skb->ip_hdr);
	dptr = dopt->__data;

	if (sopt->rr) 
	{
		optlen  = sptr[sopt->rr+1];
		soffset = sptr[sopt->rr+2];
		dopt->rr = dopt->optlen + sizeof(struct iphdr);
		memcpy(dptr, sptr+sopt->rr, optlen);
		if (sopt->rr_needaddr && soffset <= optlen) {
			if (soffset + 3 > optlen)
			  return -EINVAL;
			dptr[2] = soffset + 4;
			dopt->rr_needaddr = 1;
		}
		dptr 	 += optlen;
		dopt->optlen += optlen;
	}
	if (sopt->ts) 
	{
		optlen = sptr[sopt->ts+1];
		soffset = sptr[sopt->ts+2];
		dopt->ts = dopt->optlen + sizeof(struct iphdr);
		memcpy(dptr, sptr+sopt->ts, optlen);
		if (soffset <= optlen) 
		{
			if (sopt->ts_needaddr) 
			{
				if (soffset + 3 > optlen)
					return -EINVAL;
				dopt->ts_needaddr = 1;
				soffset += 4;
			}
			if (sopt->ts_needtime) 
			{
				if (soffset + 3 > optlen)
					return -EINVAL;
				dopt->ts_needtime = 1;
				soffset += 4;
			}
			if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC) 
			{
				__u32 addr;
				memcpy(&addr, sptr+soffset-9, 4);
				if (ip_chk_addr(addr) == 0) 
				{
					dopt->ts_needtime = 0;
					dopt->ts_needaddr = 0;
					soffset -= 8;
				}
			}
			dptr[2] = soffset;
		}
		dptr += optlen;
		dopt->optlen += optlen;
	}
	if (sopt->srr) 
	{
		unsigned char * start = sptr+sopt->srr;
		__u32 faddr;

		optlen  = start[1];
		soffset = start[2];
		doffset = 0;
		if (soffset > optlen)
			soffset = optlen + 1;
		soffset -= 4;
		if (soffset > 3) 
		{
			memcpy(&faddr, &start[soffset-1], 4);
			for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
				memcpy(&dptr[doffset-1], &start[soffset-1], 4);
			/*
			 * RFC1812 requires to fix illegal source routes.
			 */
			if (memcmp(&saddr, &start[soffset+3], 4) == 0)
				doffset -= 4;
		}
		if (doffset > 3) 
		{
			memcpy(&start[doffset-1], &daddr, 4);
			dopt->faddr = faddr;
			dptr[0] = start[0];
			dptr[1] = doffset+3;
			dptr[2] = 4;
			dptr += doffset+3;
			dopt->srr = dopt->optlen + sizeof(struct iphdr);
			dopt->optlen += doffset+3;
			dopt->is_strictroute = sopt->is_strictroute;
		}
	}
	while (dopt->optlen & 3) 
	{
		*dptr++ = IPOPT_END;
		dopt->optlen++;
	}
	return 0;
}
Пример #15
0
int ip_options_compile(struct options * opt, struct sk_buff * skb)
{
	int l;
	unsigned char * iph;
	unsigned char * optptr;
	int optlen;
	unsigned char * pp_ptr = NULL;

	if (!opt) 
	{
		opt = (struct options*)skb->proto_priv;
		memset(opt, 0, sizeof(struct options));
		iph = (unsigned char*)skb->ip_hdr;
		opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
		optptr = iph + sizeof(struct iphdr);
		opt->is_data = 0;
	}
	else 
	{
		optptr = opt->is_data ? opt->__data : (unsigned char*)&skb->ip_hdr[1];
		iph = optptr - sizeof(struct iphdr);
	}

	for (l = opt->optlen; l > 0; ) 
	{
		switch (*optptr) 
		{
		      case IPOPT_END:
			for (optptr++, l--; l>0; l--) 
			{
				if (*optptr != IPOPT_END) 
				{
					*optptr = IPOPT_END;
					opt->is_changed = 1;
				}
			}
			goto eol;
		      case IPOPT_NOOP:
			l--;
			optptr++;
			continue;
		}
		optlen = optptr[1];
		if (optlen<2 || optlen>l)
		{
			pp_ptr = optptr;
			goto error;
		}
		switch (*optptr) 
		{
		      case IPOPT_SSRR:
		      case IPOPT_LSRR:
			if (optlen < 3) 
			{
				pp_ptr = optptr + 1;
				goto error;
			}
			if (optptr[2] < 4) 
			{
				pp_ptr = optptr + 2;
				goto error;
			}
			/* NB: cf RFC-1812 5.2.4.1 */
			if (opt->srr) 
			{
				pp_ptr = optptr;
				goto error;
			}
			if (!skb) 
			{
				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) 
				{
					pp_ptr = optptr + 1;
					goto error;
				}
				memcpy(&opt->faddr, &optptr[3], 4);
				if (optlen > 7)
					memmove(&optptr[3], &optptr[7], optlen-7);
			}
			opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
			opt->srr = optptr - iph;
			break;
		      case IPOPT_RR:
			if (opt->rr) 
			{
				pp_ptr = optptr;
				goto error;
			}
			if (optlen < 3) 
			{
				pp_ptr = optptr + 1;
				goto error;
			}
			if (optptr[2] < 4) 
			{
				pp_ptr = optptr + 2;
				goto error;
			}
			if (optptr[2] <= optlen) 
			{
				if (optptr[2]+3 > optlen) 
				{
					pp_ptr = optptr + 2;
					goto error;
				}
				if (skb) 
				{
					memcpy(&optptr[optptr[2]-1], &skb->dev->pa_addr, 4);
					opt->is_changed = 1;
				}
				optptr[2] += 4;
				opt->rr_needaddr = 1;
			}
			opt->rr = optptr - iph;
			break;
		      case IPOPT_TIMESTAMP:
			if (opt->ts) 
			{
				pp_ptr = optptr;
				goto error;
			}
			if (optlen < 4) 
			{
				pp_ptr = optptr + 1;
				goto error;
			}
			if (optptr[2] < 5) 
			{
				pp_ptr = optptr + 2;
				goto error;
			}
			if (optptr[2] <= optlen) 
			{
				struct timestamp * ts = (struct timestamp*)(optptr+1);
				__u32 * timeptr = NULL;
				if (ts->ptr+3 > ts->len) 
				{
					pp_ptr = optptr + 2;
					goto error;
				}
				switch (ts->flags) 
				{
				      case IPOPT_TS_TSONLY:
					opt->ts = optptr - iph;
					if (skb) 
						timeptr = (__u32*)&optptr[ts->ptr-1];
					opt->ts_needtime = 1;
					ts->ptr += 4;
					break;
				      case IPOPT_TS_TSANDADDR:
					if (ts->ptr+7 > ts->len) 
					{
						pp_ptr = optptr + 2;
						goto error;
					}
					opt->ts = optptr - iph;
					if (skb) 
					{
						memcpy(&optptr[ts->ptr-1], &skb->dev->pa_addr, 4);
						timeptr = (__u32*)&optptr[ts->ptr+3];
					}
					opt->ts_needaddr = 1;
					opt->ts_needtime = 1;
					ts->ptr += 8;
					break;
				      case IPOPT_TS_PRESPEC:
					if (ts->ptr+7 > ts->len) 
					{
						pp_ptr = optptr + 2;
						goto error;
					}
					opt->ts = optptr - iph;
					{
						__u32 addr;
						memcpy(&addr, &optptr[ts->ptr-1], 4);
						if (ip_chk_addr(addr) == 0)
							break;
						if (skb)
							timeptr = (__u32*)&optptr[ts->ptr+3];
					}
					opt->ts_needaddr = 1;
					opt->ts_needtime = 1;
					ts->ptr += 8;
					break;
				      default:
					pp_ptr = optptr + 3;
					goto error;
				}
				if (timeptr) 
				{
					struct timeval tv;
					__u32  midtime;
					do_gettimeofday(&tv);
					midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
					memcpy(timeptr, &midtime, sizeof(__u32));
					opt->is_changed = 1;
				}
			} 
			else 
			{
				struct timestamp * ts = (struct timestamp*)(optptr+1);
				if (ts->overflow == 15) 
				{
					pp_ptr = optptr + 3;
					goto error;
				}
				opt->ts = optptr - iph;
				if (skb) 
				{
					ts->overflow++;
					opt->is_changed = 1;
				}
			}
			break;
		      case IPOPT_SEC:
		      case IPOPT_SID:
		      default:
			if (!skb) 
			{
				pp_ptr = optptr;
				goto error;
			}
			break;
		}
		l -= optlen;
		optptr += optlen;
	}

eol:
	if (!pp_ptr)
		return 0;

error:
	if (skb) 
	{
		icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph, skb->dev);
		kfree_skb(skb, FREE_READ);
	}
	return -EINVAL;
}
Пример #16
0
int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
/*
 *	We shouldn't use this type conversion. Check later.
 */
	
	struct arphdr *arp = (struct arphdr *)skb->h.raw;
	unsigned char *arp_ptr= (unsigned char *)(arp+1);
	struct arp_table *entry;
	struct arp_table *proxy_entry;
	int addr_hint,hlen,htype;
	unsigned long hash;
	unsigned char ha[MAX_ADDR_LEN];	/* So we can enable ints again. */
	long sip,tip;
	unsigned char *sha,*tha;

/*
 *	The hardware length of the packet should match the hardware length
 *	of the device.  Similarly, the hardware types should match.  The
 *	device should be ARP-able.  Also, if pln is not 4, then the lookup
 *	is not from an IP number.  We can't currently handle this, so toss
 *	it. 
 */  
	if (arp->ar_hln != dev->addr_len    || 
     		dev->type != ntohs(arp->ar_hrd) || 
		dev->flags & IFF_NOARP          ||
		arp->ar_pln != 4)
	{
		kfree_skb(skb, FREE_READ);
		return 0;
	}

/*
 *	Another test.
 *	The logic here is that the protocol being looked up by arp should 
 *	match the protocol the device speaks.  If it doesn't, there is a
 *	problem, so toss the packet.
 */
  	switch(dev->type)
  	{
#ifdef CONFIG_AX25
		case ARPHRD_AX25:
			if(arp->ar_pro != htons(AX25_P_IP))
			{
				kfree_skb(skb, FREE_READ);
				return 0;
			}
			break;
#endif
		case ARPHRD_ETHER:
		case ARPHRD_ARCNET:
			if(arp->ar_pro != htons(ETH_P_IP))
			{
				kfree_skb(skb, FREE_READ);
				return 0;
			}
			break;

		default:
			printk("ARP: dev->type mangled!\n");
			kfree_skb(skb, FREE_READ);
			return 0;
	}

/*
 *	Extract fields
 */

	hlen  = dev->addr_len;
	htype = dev->type;

	sha=arp_ptr;
	arp_ptr+=hlen;
	memcpy(&sip,arp_ptr,4);
	arp_ptr+=4;
	tha=arp_ptr;
	arp_ptr+=hlen;
	memcpy(&tip,arp_ptr,4);
  
/* 
 *	Check for bad requests for 127.0.0.1.  If this is one such, delete it.
 */
	if(tip == INADDR_LOOPBACK)
	{
		kfree_skb(skb, FREE_READ);
		return 0;
	}

/*
 *  Process entry.  The idea here is we want to send a reply if it is a
 *  request for us or if it is a request for someone else that we hold
 *  a proxy for.  We want to add an entry to our cache if it is a reply
 *  to us or if it is a request for our address.  
 *  (The assumption for this last is that if someone is requesting our 
 *  address, they are probably intending to talk to us, so it saves time 
 *  if we cache their address.  Their address is also probably not in 
 *  our cache, since ours is not in their cache.)
 * 
 *  Putting this another way, we only care about replies if they are to
 *  us, in which case we add them to the cache.  For requests, we care
 *  about those for us and those for our proxies.  We reply to both,
 *  and in the case of requests for us we add the requester to the arp 
 *  cache.
 */

	addr_hint = ip_chk_addr(tip);

	if(arp->ar_op == htons(ARPOP_REPLY))
	{
		if(addr_hint!=IS_MYADDR)
		{
/* 
 *	Replies to other machines get tossed. 
 */
			kfree_skb(skb, FREE_READ);
			return 0;
		}
/*
 *	Fall through to code below that adds sender to cache. 
 */
	}
	else
	{ 
/* 
 * 	It is now an arp request 
 */
/*
 * Only reply for the real device address or when it's in our proxy tables
 */
		if(tip!=dev->pa_addr)
		{
/*
 * 	To get in here, it is a request for someone else.  We need to
 * 	check if that someone else is one of our proxies.  If it isn't,
 * 	we can toss it.
 */
			cli();
			for(proxy_entry=arp_tables[PROXY_HASH];
			    proxy_entry;
			    proxy_entry = proxy_entry->next)
			{
			  /* we will respond to a proxy arp request
			     if the masked arp table ip matches the masked
			     tip. This allows a single proxy arp table
			     entry to be used on a gateway machine to handle
			     all requests for a whole network, rather than
			     having to use a huge number of proxy arp entries
			     and having to keep them uptodate.
			     */
			  if (proxy_entry->dev != dev && proxy_entry->htype == htype &&
			      !((proxy_entry->ip^tip)&proxy_entry->mask))
			    break;

			}
			if (proxy_entry)
			{
				memcpy(ha, proxy_entry->ha, hlen);
				sti();
				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha);
				kfree_skb(skb, FREE_READ);
				return 0;
			}
			else
			{
				sti();
				kfree_skb(skb, FREE_READ);
				return 0;
			}
		}
		else
		{
/*
 * 	To get here, it must be an arp request for us.  We need to reply.
 */
			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr);
		}
	}


/*
 * Now all replies are handled.  Next, anything that falls through to here
 * needs to be added to the arp cache, or have its entry updated if it is 
 * there.
 */

	hash = HASH(sip);
	cli();
	for(entry=arp_tables[hash];entry;entry=entry->next)
		if(entry->ip==sip && entry->htype==htype)
			break;

	if(entry)
	{
/*
 *	Entry found; update it.
 */
		memcpy(entry->ha, sha, hlen);
		entry->hlen = hlen;
		entry->last_used = jiffies;
		if (!(entry->flags & ATF_COM))
		{
/*
 *	This entry was incomplete.  Delete the retransmit timer
 *	and switch to complete status.
 */
			del_timer(&entry->timer);
			entry->flags |= ATF_COM;
			sti();
/* 
 *	Send out waiting packets. We might have problems, if someone is 
 *	manually removing entries right now -- entry might become invalid 
 *	underneath us.
 */
			arp_send_q(entry, sha);
		}
		else
		{
			sti();
		}
	}
	else
	{
/*
 * 	No entry found.  Need to add a new entry to the arp table.
 */
		entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
		if(entry == NULL)
		{
			sti();
			printk("ARP: no memory for new arp entry\n");

			kfree_skb(skb, FREE_READ);
			return 0;
		}

                entry->mask = DEF_ARP_NETMASK;
		entry->ip = sip;
		entry->hlen = hlen;
		entry->htype = htype;
		entry->flags = ATF_COM;
		init_timer(&entry->timer);
		memcpy(entry->ha, sha, hlen);
		entry->last_used = jiffies;
		entry->dev = skb->dev;
		skb_queue_head_init(&entry->skb);
		entry->next = arp_tables[hash];
		arp_tables[hash] = entry;
		sti();
	}

/*
 *	Replies have been sent, and entries have been added.  All done.
 */
	kfree_skb(skb, FREE_READ);
	return 0;
}