Esempio n. 1
0
int udp_chkaddr(struct sk_buff *skb)
{
	struct iphdr *iph = skb->h.iph;
	struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4);
	struct sock *sk;

	sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest,
			   skb->dev);
	if (!sk)
		return 0;
	/* 0 means accept all LOCAL addresses here, not all the world... */
	if (sk->rcv_saddr == 0)
		return 0;
	return 1;
}
Esempio n. 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;
}
Esempio n. 3
0
int icmp_chkaddr(struct sk_buff *skb)
{
	struct icmphdr *icmph=(struct icmphdr *)(skb->nh.raw + skb->nh.iph->ihl*4);
	struct iphdr *iph = (struct iphdr *) (icmph + 1);
	void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, 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->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex);
			if (!sk || (sk->state == TCP_LISTEN))
				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->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex);
			if (!sk) return 0;
			if (sk->saddr != iph->saddr && inet_addr_type(iph->saddr) != RTN_LOCAL)
				return 0;
			/*
			 * This packet may have come from us.
			 * Assume it did.
			 */
			return 1;
			}
		}
	}
	return 0;
}
Esempio n. 4
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;
}
Esempio n. 5
0
/* Shared by v4/v6 tcp. */
unsigned short udp_good_socknum(void)
{
	int result;
	static int start = 0;
	int i, best, best_size_so_far;

	SOCKHASH_LOCK();

	/* Select initial not-so-random "best" */
	best = PROT_SOCK + 1 + (start & 1023);
	best_size_so_far = 32767;	/* "big" num */
	result = best;
	for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
		struct sock *sk;
		int size;

		sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];

		/* No clashes - take it */
		if (!sk)
			goto out;

		/* Is this one better than our best so far? */
		size = 0;
		do {
			if(++size >= best_size_so_far)
				goto next;
		} while((sk = sk->next) != NULL);
		best_size_so_far = size;
		best = result;
next:
	}

	while (udp_lport_inuse(best))
		best += UDP_HTABLE_SIZE;
	result = best;
out:
	start = result;
	SOCKHASH_UNLOCK();
	return result;
}

static void udp_v4_hash(struct sock *sk)
{
	struct sock **skp;
	int num = sk->num;

	num &= (UDP_HTABLE_SIZE - 1);
	skp = &udp_hash[num];

	SOCKHASH_LOCK();
	sk->next = *skp;
	*skp = sk;
	sk->hashent = num;
	SOCKHASH_UNLOCK();
}

static void udp_v4_unhash(struct sock *sk)
{
	struct sock **skp;
	int num = sk->num;

	num &= (UDP_HTABLE_SIZE - 1);
	skp = &udp_hash[num];

	SOCKHASH_LOCK();
	while(*skp != NULL) {
		if(*skp == sk) {
			*skp = sk->next;
			break;
		}
		skp = &((*skp)->next);
	}
	SOCKHASH_UNLOCK();
}

static void udp_v4_rehash(struct sock *sk)
{
	struct sock **skp;
	int num = sk->num;
	int oldnum = sk->hashent;

	num &= (UDP_HTABLE_SIZE - 1);
	skp = &udp_hash[oldnum];

	SOCKHASH_LOCK();
	while(*skp != NULL) {
		if(*skp == sk) {
			*skp = sk->next;
			break;
		}
		skp = &((*skp)->next);
	}
	sk->next = udp_hash[num];
	udp_hash[num] = sk;
	sk->hashent = num;
	SOCKHASH_UNLOCK();
}

/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
 * harder than this. -DaveM
 */
__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport,
				      struct device *dev)
{
	struct sock *sk, *result = NULL;
	unsigned short hnum = ntohs(dport);
	int badness = -1;

	for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
		if((sk->num == hnum) && !(sk->dead && (sk->state == TCP_CLOSE))) {
			int score = 0;
			if(sk->rcv_saddr) {
				if(sk->rcv_saddr != daddr)
					continue;
				score++;
			}
			if(sk->daddr) {
				if(sk->daddr != saddr)
					continue;
				score++;
			}
			if(sk->dummy_th.dest) {
				if(sk->dummy_th.dest != sport)
					continue;
				score++;
			}
			/* If this socket is bound to a particular interface,
			 * did the packet come in on it? */
			if (sk->bound_device) {
				if (dev == sk->bound_device)
					score++;
				else
					continue;  /* mismatch--not this sock */
			}
			if(score == 4) {
				result = sk;
				break;
			} else if(score > badness) {
				result = sk;
				badness = score;
			}
		}
	}
	return result;
}

#ifdef CONFIG_IP_TRANSPARENT_PROXY
struct sock *udp_v4_proxy_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, u32 paddr, u16 rport,
				 struct device *dev)
{
	struct sock *hh[3], *sk, *result = NULL;
	int i;
	int badness = -1;
	unsigned short hnum = ntohs(dport);
	unsigned short hpnum = ntohs(rport);

	SOCKHASH_LOCK();
	hh[0] = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)];
	hh[1] = udp_hash[hpnum & (UDP_HTABLE_SIZE - 1)];
	for (i = 0; i < 2; i++) {
		for(sk = hh[i]; sk != NULL; sk = sk->next) {
			if(sk->num == hnum || sk->num == hpnum) {
				int score = 0;
				if(sk->dead && (sk->state == TCP_CLOSE))
					continue;
				if(sk->rcv_saddr) {
					if((sk->num != hpnum || sk->rcv_saddr != paddr) &&
					   (sk->num != hnum || sk->rcv_saddr != daddr))
						continue;
					score++;
				}
				if(sk->daddr) {
					if(sk->daddr != saddr)
						continue;
					score++;
				}
				if(sk->dummy_th.dest) {
					if(sk->dummy_th.dest != sport)
						continue;
					score++;
				}
				/* If this socket is bound to a particular interface,
				 * did the packet come in on it? */
				if(sk->bound_device) {
					if (sk->bound_device != dev)
						continue;
					score++;
				}
				if(score == 4 && sk->num == hnum) {
					result = sk;
					break;
				} else if(score > badness && (sk->num == hpnum || sk->rcv_saddr)) {
					result = sk;
					badness = score;
				}
			}
		}
	}
	SOCKHASH_UNLOCK();
	return result;
}
#endif

static inline struct sock *udp_v4_mcast_next(struct sock *sk,
					     unsigned short num,
					     unsigned long raddr,
					     unsigned short rnum,
					     unsigned long laddr,
					     struct device *dev)
{
	struct sock *s = sk;
	unsigned short hnum = ntohs(num);
	for(; s; s = s->next) {
		if ((s->num != hnum)					||
		    (s->dead && (s->state == TCP_CLOSE))		||
		    (s->daddr && s->daddr!=raddr)			||
		    (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) ||
		    ((s->bound_device) && (s->bound_device!=dev))       ||
		    (s->rcv_saddr  && s->rcv_saddr != laddr))
			continue;
		break;
  	}
  	return s;
}

#define min(a,b)	((a)<(b)?(a):(b))


/*
 * This routine is called by the ICMP module when it gets some
 * sort of error condition.  If err < 0 then the socket should
 * be closed and the error returned to the user.  If err > 0
 * it's just the icmp type << 8 | icmp code.  
 * Header points to the ip header of the error packet. We move
 * on past this. Then (as it used to claim before adjustment)
 * header points to the first 8 bytes of the udp header.  We need
 * to find the appropriate port.
 */

void udp_err(int type, int code, unsigned char *header, __u32 daddr,
	__u32 saddr, struct inet_protocol *protocol, int len)
{
	struct udphdr *uh;
	struct sock *sk;

	/*
	 *	Find the 8 bytes of post IP header ICMP included for us
	 */  
	 
	if(len<sizeof(struct udphdr))
		return;
	
	uh = (struct udphdr *)header;  
   
	sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source, NULL);
	if (sk == NULL) 
	  	return;	/* No socket for error */
  	
	if (type == ICMP_SOURCE_QUENCH) 
	{	/* Slow down! */
		if (sk->cong_window > 1) 
			sk->cong_window = sk->cong_window/2;
		return;
	}

	if (type == ICMP_PARAMETERPROB)
	{
		sk->err = EPROTO;
		sk->error_report(sk);
		return;
	}
			
	/*
	 *	Various people wanted BSD UDP semantics. Well they've come 
	 *	back out because they slow down response to stuff like dead
	 *	or unreachable name servers and they screw term users something
	 *	chronic. Oh and it violates RFC1122. So basically fix your 
	 *	client code people.
	 */
	 
	/* RFC1122: OK.  Passes ICMP errors back to application, as per */
	/* 4.1.3.3. */
	/* After the comment above, that should be no surprise. */

	if(code<=NR_ICMP_UNREACH && icmp_err_convert[code].fatal)
	{
		/*
		 *	4.x BSD compatibility item. Break RFC1122 to
		 *	get BSD socket semantics.
		 */
		if(sk->bsdism && sk->state!=TCP_ESTABLISHED)
			return;
		sk->err = icmp_err_convert[code].errno;
		sk->error_report(sk);
	}
}


static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr, unsigned long base)
{
	return(csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base));
}

struct udpfakehdr 
{
	struct udphdr uh;
	__u32 daddr;
	__u32 other;
	const char *from;
	__u32 wcheck;
};

/*
 *	Copy and checksum a UDP packet from user space into a buffer. We still have to do the planning to
 *	get ip_build_xmit to spot direct transfer to network card and provide an additional callback mode
 *	for direct user->board I/O transfers. That one will be fun.
 */
 
static void udp_getfrag(const void *p, __u32 saddr, char * to, unsigned int offset, unsigned int fraglen) 
{
	struct udpfakehdr *ufh = (struct udpfakehdr *)p;
	const char *src;
	char *dst;
	unsigned int len;

	if (offset) 
	{
		len = fraglen;
	 	src = ufh->from+(offset-sizeof(struct udphdr));
	 	dst = to;
	}
	else 
	{
		len = fraglen-sizeof(struct udphdr);
 		src = ufh->from;
		dst = to+sizeof(struct udphdr);
	}
	ufh->wcheck = csum_partial_copy_fromuser(src, dst, len, ufh->wcheck);
	if (offset == 0) 
	{
 		ufh->wcheck = csum_partial((char *)ufh, sizeof(struct udphdr),
 				   ufh->wcheck);
		ufh->uh.check = csum_tcpudp_magic(saddr, ufh->daddr, 
					  ntohs(ufh->uh.len),
					  IPPROTO_UDP, ufh->wcheck);
		if (ufh->uh.check == 0)
			ufh->uh.check = -1;
		memcpy(to, ufh, sizeof(struct udphdr));
	}
}

/*
 *	Unchecksummed UDP is sufficiently critical to stuff like ATM video conferencing
 *	that we use two routines for this for speed. Probably we ought to have a CONFIG_FAST_NET
 *	set for >10Mb/second boards to activate this sort of coding. Timing needed to verify if
 *	this is a valid decision.
 */
 
static void udp_getfrag_nosum(const void *p, __u32 saddr, char * to, unsigned int offset, unsigned int fraglen) 
{
	struct udpfakehdr *ufh = (struct udpfakehdr *)p;
	const char *src;
	char *dst;
	unsigned int len;

	if (offset) 
	{
		len = fraglen;
	 	src = ufh->from+(offset-sizeof(struct udphdr));
	 	dst = to;
	}
	else 
	{
		len = fraglen-sizeof(struct udphdr);
 		src = ufh->from;
		dst = to+sizeof(struct udphdr);
	}
	memcpy_fromfs(dst,src,len);
	if (offset == 0) 
		memcpy(to, ufh, sizeof(struct udphdr));
}


/*
 *	Send UDP frames.
 */

static int udp_send(struct sock *sk, struct sockaddr_in *sin,
		      const unsigned char *from, int len, int rt,
		    __u32 saddr, int noblock) 
{
	int ulen = len + sizeof(struct udphdr);
	int a;
	struct udpfakehdr ufh;
	
	if(ulen>65535-sizeof(struct iphdr))
		return -EMSGSIZE;

	ufh.uh.source = sk->dummy_th.source;
	ufh.uh.dest = sin->sin_port;
	ufh.uh.len = htons(ulen);
	ufh.uh.check = 0;
	ufh.daddr = sin->sin_addr.s_addr;
	ufh.other = (htons(ulen) << 16) + IPPROTO_UDP*256;
	ufh.from = from;
	ufh.wcheck = 0;

#ifdef CONFIG_IP_TRANSPARENT_PROXY
	if (rt&MSG_PROXY)
	{
		/*
		 * We map the first 8 bytes of a second sockaddr_in
		 * into the last 8 (unused) bytes of a sockaddr_in.
		 * This _is_ ugly, but it's the only way to do it
		 * easily,  without adding system calls.
		 */
		struct sockaddr_in *sinfrom =
			(struct sockaddr_in *) sin->sin_zero;

		if (!suser())
			return(-EPERM);
		if (sinfrom->sin_family && sinfrom->sin_family != AF_INET)
			return(-EINVAL);
		if (sinfrom->sin_port == 0)
			return(-EINVAL);
		saddr = sinfrom->sin_addr.s_addr;
		ufh.uh.source = sinfrom->sin_port;
	}
#endif

	/* RFC1122: OK.  Provides the checksumming facility (MUST) as per */
	/* 4.1.3.4. It's configurable by the application via setsockopt() */
	/* (MAY) and it defaults to on (MUST).  Almost makes up for the */
	/* violation above. -- MS */

	if(sk->no_check)
		a = ip_build_xmit(sk, udp_getfrag_nosum, &ufh, ulen, 
			sin->sin_addr.s_addr, saddr, sk->opt, rt, IPPROTO_UDP, noblock);
	else
		a = ip_build_xmit(sk, udp_getfrag, &ufh, ulen, 
			sin->sin_addr.s_addr, saddr, sk->opt, rt, IPPROTO_UDP, noblock);
	if(a<0)
		return a;
	udp_statistics.UdpOutDatagrams++;
	return len;
}
Esempio n. 6
0
void udp_err(struct sk_buff *skb, u32 info)
{
	struct iphdr *iph = (struct iphdr*)skb->data;
	struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2));
	int type = skb->h.icmph->type;
	int code = skb->h.icmph->code;
	struct sock *sk;
	int harderr;
	int err;

	sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex);
	if (sk == NULL) {
		ICMP_INC_STATS_BH(IcmpInErrors);
    	  	return;	/* No socket for error */
	}

	err = 0;
	harderr = 0;

	switch (type) {
	default:
	case ICMP_TIME_EXCEEDED:
		err = EHOSTUNREACH;
		break;
	case ICMP_SOURCE_QUENCH:
		goto out;
	case ICMP_PARAMETERPROB:
		err = EPROTO;
		harderr = 1;
		break;
	case ICMP_DEST_UNREACH:
		if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
			if (sk->protinfo.af_inet.pmtudisc != IP_PMTUDISC_DONT) {
				err = EMSGSIZE;
				harderr = 1;
				break;
			}
			goto out;
		}
		err = EHOSTUNREACH;
		if (code <= NR_ICMP_UNREACH) {
			harderr = icmp_err_convert[code].fatal;
			err = icmp_err_convert[code].errno;
		}
		break;
	}

	/*
	 *      RFC1122: OK.  Passes ICMP errors back to application, as per 
	 *	4.1.3.3.
	 */
	if (!sk->protinfo.af_inet.recverr) {
		if (!harderr || sk->state != TCP_ESTABLISHED)
			goto out;
	} else {
		ip_icmp_error(sk, skb, err, uh->dest, info, (u8*)(uh+1));
	}
	sk->err = err;
	sk->error_report(sk);
out:
	sock_put(sk);
}