patch-2.3.15 linux/net/ipv6/raw.c

Next file: linux/net/ipv6/reassembly.c
Previous file: linux/net/ipv6/protocol.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.14/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.27 1999/07/02 11:26:40 davem Exp $
+ *	$Id: raw.c,v 1.29 1999/08/20 11:06:26 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -36,16 +36,19 @@
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
 #include <net/transp_v6.h>
+#include <net/udp.h>
+#include <net/inet_common.h>
 
 #include <net/rawv6.h>
 
 struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+rwlock_t raw_v6_lock = RW_LOCK_UNLOCKED;
 
 static void raw_v6_hash(struct sock *sk)
 {
 	struct sock **skp = &raw_v6_htable[sk->num & (RAWV6_HTABLE_SIZE - 1)];
 
-	SOCKHASH_LOCK_WRITE();
+	write_lock_bh(&raw_v6_lock);
 	if ((sk->next = *skp) != NULL)
 		(*skp)->pprev = &sk->next;
 	*skp = sk;
@@ -53,33 +56,34 @@
 	sk->prot->inuse++;
 	if(sk->prot->highestinuse < sk->prot->inuse)
 		sk->prot->highestinuse = sk->prot->inuse;
-	SOCKHASH_UNLOCK_WRITE();
+ 	sock_hold(sk);
+ 	write_unlock_bh(&raw_v6_lock);
 }
 
 static void raw_v6_unhash(struct sock *sk)
 {
-	SOCKHASH_LOCK_WRITE();
+ 	write_lock_bh(&raw_v6_lock);
 	if (sk->pprev) {
 		if (sk->next)
 			sk->next->pprev = sk->pprev;
 		*sk->pprev = sk->next;
 		sk->pprev = NULL;
 		sk->prot->inuse--;
+		__sock_put(sk);
 	}
-	SOCKHASH_UNLOCK_WRITE();
+	write_unlock_bh(&raw_v6_lock);
 }
 
 
 /* Grumble... icmp and ip_input want to get at this... */
-struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
-			   struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
+struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
+			     struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
 {
 	struct sock *s = sk;
 	int addr_type = ipv6_addr_type(loc_addr);
 
 	for(s = sk; s; s = s->next) {
-		if((s->num == num) 		&&
-		   !(s->dead && (s->state == TCP_CLOSE))) {
+		if(s->num == num) {
 			struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
 
 			if (!ipv6_addr_any(&np->daddr) &&
@@ -88,56 +92,136 @@
 
 			if (!ipv6_addr_any(&np->rcv_saddr)) {
 				if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
-					return(s);
+					break;
 				if ((addr_type & IPV6_ADDR_MULTICAST) &&
 				    inet6_mc_check(s, loc_addr))
-					return (s);
+					break;
 				continue;
 			}
-			return(s);
+			break;
+		}
+	}
+	return s;
+}
+
+/*
+ *	0 - deliver
+ *	1 - block
+ */
+static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+{
+	struct icmp6hdr *icmph;
+	struct raw6_opt *opt;
+
+	opt = &sk->tp_pinfo.tp_raw;
+	icmph = (struct icmp6hdr *) (skb->nh.ipv6h + 1);
+	return test_bit(icmph->icmp6_type, &opt->filter);
+}
+
+/*
+ *	demultiplex raw sockets.
+ *	(should consider queueing the skb in the sock receive_queue
+ *	without calling rawv6.c)
+ */
+struct sock * ipv6_raw_deliver(struct sk_buff *skb,
+			       int nexthdr, unsigned long len)
+{
+	struct in6_addr *saddr;
+	struct in6_addr *daddr;
+	struct sock *sk, *sk2;
+	__u8 hash;
+
+	saddr = &skb->nh.ipv6h->saddr;
+	daddr = saddr + 1;
+
+	hash = nexthdr & (MAX_INET_PROTOS - 1);
+
+	read_lock(&raw_v6_lock);
+	sk = raw_v6_htable[hash];
+
+	/*
+	 *	The first socket found will be delivered after
+	 *	delivery to transport protocols.
+	 */
+
+	if (sk == NULL)
+		goto out;
+
+	sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr);
+
+	if (sk) {
+		sk2 = sk;
+
+		while ((sk2 = __raw_v6_lookup(sk2->next, nexthdr, daddr, saddr))) {
+			struct sk_buff *buff;
+
+			if (nexthdr == IPPROTO_ICMPV6 &&
+			    icmpv6_filter(sk2, skb))
+				continue;
+
+			buff = skb_clone(skb, GFP_ATOMIC);
+			if (buff)
+				rawv6_rcv(sk2, buff, len);
 		}
 	}
-	return NULL;
+
+	if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
+		sk = NULL;
+
+out:
+	if (sk)
+		sock_hold(sk);
+	read_unlock(&raw_v6_lock);
+	return sk;
 }
 
+
+
 /* This cleans up af_inet6 a bit. -DaveM */
 static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
 	__u32 v4addr = 0;
 	int addr_type;
+	int err;
 
-	/* Check these errors. */
-	if (sk->state != TCP_CLOSE || (addr_len < sizeof(struct sockaddr_in6)))
+	if (addr_len < sizeof(struct sockaddr_in6))
 		return -EINVAL;
 
 	addr_type = ipv6_addr_type(&addr->sin6_addr);
 
-	/* Check if the address belongs to the host. */
-	if (addr_type == IPV6_ADDR_MAPPED) {
-		/* Raw sockets are IPv6 only */
+	/* Raw sockets are IPv6 only */
+	if (addr_type == IPV6_ADDR_MAPPED)
 		return(-EADDRNOTAVAIL);
-	} else {
-		if (addr_type != IPV6_ADDR_ANY) {
-			/* ipv4 addr of the socket is invalid.  Only the
-			 * unpecified and mapped address have a v4 equivalent.
-			 */
-			v4addr = LOOPBACK4_IPV6;
-			if (!(addr_type & IPV6_ADDR_MULTICAST))	{
-				if (ipv6_chk_addr(&addr->sin6_addr, NULL, 0) == NULL)
-					return(-EADDRNOTAVAIL);
-			}
+
+	lock_sock(sk);
+
+	err = -EINVAL;
+	if (sk->state != TCP_CLOSE)
+		goto out;
+
+	/* Check if the address belongs to the host. */
+	if (addr_type != IPV6_ADDR_ANY) {
+		/* ipv4 addr of the socket is invalid.  Only the
+		 * unpecified and mapped address have a v4 equivalent.
+		 */
+		v4addr = LOOPBACK4_IPV6;
+		if (!(addr_type & IPV6_ADDR_MULTICAST))	{
+			err = -EADDRNOTAVAIL;
+			if (!ipv6_chk_addr(&addr->sin6_addr, NULL))
+				goto out;
 		}
 	}
 
 	sk->rcv_saddr = v4addr;
 	sk->saddr = v4addr;
-	memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr, 
-	       sizeof(struct in6_addr));
+	ipv6_addr_copy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr);
 	if (!(addr_type & IPV6_ADDR_MULTICAST))
-		memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr, 
-		       sizeof(struct in6_addr));
-	return 0;
+		ipv6_addr_copy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr);
+	err = 0;
+out:
+	release_sock(sk);
+	return err;
 }
 
 void rawv6_err(struct sock *sk, struct sk_buff *skb, struct ipv6hdr *hdr,
@@ -193,7 +277,7 @@
  */
 int rawv6_rcv(struct sock *sk, struct sk_buff *skb, unsigned long len)
 {
-	if (sk->ip_hdrincl)
+	if (sk->protinfo.af_inet.hdrincl)
 		skb->h.raw = skb->nh.raw;
 
 	rawv6_rcv_skb(sk, skb);
@@ -341,9 +425,7 @@
 	/* Mirror BSD error message compatibility */
 	if (msg->msg_flags & MSG_OOB)		
 		return -EOPNOTSUPP;
-			 
-	if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
-		return(-EINVAL);
+
 	/*
 	 *	Get and verify the address. 
 	 */
@@ -590,15 +672,10 @@
 
 static void rawv6_close(struct sock *sk, long timeout)
 {
-	bh_lock_sock(sk);
-
-	/* See for explanation: raw_close in ipv4/raw.c */
-	sk->state = TCP_CLOSE;
-	raw_v6_unhash(sk);
 	if (sk->num == IPPROTO_RAW)
 		ip6_ra_control(sk, -1, NULL);
-	sk->dead = 1;
-	destroy_sock(sk);
+
+	inet_sock_release(sk);
 }
 
 static int rawv6_init_sk(struct sock *sk)
@@ -606,6 +683,9 @@
 	return(0);
 }
 
+#define LINE_LEN 190
+#define LINE_FMT "%-190s\n"
+
 static void get_raw6_sock(struct sock *sp, char *tmpbuf, int i)
 {
 	struct in6_addr *dest, *src;
@@ -615,13 +695,13 @@
 
 	dest  = &sp->net_pinfo.af_inet6.daddr;
 	src   = &sp->net_pinfo.af_inet6.rcv_saddr;
-	destp = ntohs(sp->dport);
-	srcp  = ntohs(sp->sport);
+	destp = 0;
+	srcp  = sp->num;
 	timer_active = (sp->timer.prev != NULL) ? 2 : 0;
 	timer_expires = (timer_active == 2 ? sp->timer.expires : jiffies);
 	sprintf(tmpbuf,
 		"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
-		"%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+		"%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld %d %p",
 		i,
 		src->s6_addr32[0], src->s6_addr32[1],
 		src->s6_addr32[2], src->s6_addr32[3], srcp,
@@ -630,8 +710,9 @@
 		sp->state, 
 		atomic_read(&sp->wmem_alloc), atomic_read(&sp->rmem_alloc),
 		timer_active, timer_expires-jiffies, 0,
-		sp->socket->inode->i_uid, timer_active ? sp->timeout : 0,
-		sp->socket ? sp->socket->inode->i_ino : 0);
+		sp->socket->inode->i_uid, 0,
+		sp->socket ? sp->socket->inode->i_ino : 0,
+		atomic_read(&sp->refcnt), sp);
 }
 
 int raw6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
@@ -639,10 +720,10 @@
 	int len = 0, num = 0, i;
 	off_t pos = 0;
 	off_t begin;
-	char tmpbuf[150];
+	char tmpbuf[LINE_LEN+2];
 
-	if (offset < 149)
-		len += sprintf(buffer, "%-148s\n",
+	if (offset < LINE_LEN+1)
+		len += sprintf(buffer, LINE_FMT,
 			       "  sl  "						/* 6 */
 			       "local_address                         "		/* 38 */
 			       "remote_address                        "		/* 38 */
@@ -650,25 +731,25 @@
 			       "   uid  timeout inode");			/* 21 */
 										/*----*/
 										/*144 */
-	pos = 149;
-	SOCKHASH_LOCK_READ();
+	pos = LINE_LEN+1;
+	read_lock(&raw_v6_lock);
 	for (i = 0; i < RAWV6_HTABLE_SIZE; i++) {
 		struct sock *sk;
 
 		for (sk = raw_v6_htable[i]; sk; sk = sk->next, num++) {
 			if (sk->family != PF_INET6)
 				continue;
-			pos += 149;
+			pos += LINE_LEN+1;
 			if (pos < offset)
 				continue;
 			get_raw6_sock(sk, tmpbuf, i);
-			len += sprintf(buffer+len, "%-148s\n", tmpbuf);
+			len += sprintf(buffer+len, LINE_FMT, tmpbuf);
 			if(len >= length)
 				goto out;
 		}
 	}
 out:
-	SOCKHASH_UNLOCK_READ();
+	read_unlock(&raw_v6_lock);
 	begin = len - (pos - offset);
 	*start = buffer + begin;
 	len -= begin;
@@ -682,6 +763,7 @@
 struct proto rawv6_prot = {
 	rawv6_close,			/* close */
 	udpv6_connect,			/* connect */
+	udp_disconnect,			/* disconnect */
 	NULL,				/* accept */
 	NULL,				/* retransmit */
 	NULL,				/* write_wakeup */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)