patch-2.3.15 linux/net/decnet/dn_nsp_in.c

Next file: linux/net/decnet/dn_nsp_out.c
Previous file: linux/net/decnet/dn_neigh.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.14/linux/net/decnet/dn_nsp_in.c linux/net/decnet/dn_nsp_in.c
@@ -19,6 +19,7 @@
  *    Steve Whitehouse:  More checks on skb->len to catch bogus packets
  *                       Fixed various race conditions and possible nasties.
  *    Steve Whitehouse:  Now handles returned conninit frames.
+ *     David S. Miller:  New socket locking
  */
 
 /******************************************************************************
@@ -60,6 +61,7 @@
 #include <linux/stat.h>
 #include <linux/init.h>
 #include <linux/poll.h>
+#include <linux/netfilter_decnet.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
 #include <net/dn_nsp.h>
@@ -149,9 +151,8 @@
  * to find its sockets, since it searches on object name/number
  * rather than port numbers
  */
-static int dn_conninit_rx(struct sk_buff *skb)
+static struct sock *dn_find_listener(struct sk_buff *skb)
 {
-	struct sock *sk;
 	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
 	struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data;
 	struct sockaddr_dn addr;
@@ -169,29 +170,29 @@
 
 	/* printk(KERN_DEBUG "username2sockaddr 1\n"); */
 	if (dn_username2sockaddr(skb->data, skb->len, &addr, &type) < 0)
-		goto free_out;
+		goto err_out;
 
 	if (type > 1)
-		goto free_out;
+		goto err_out;
 
 	/* printk(KERN_DEBUG "looking for listener...\n"); */
-	if ((sk = dn_sklist_find_listener(&addr)) == NULL)
-		return 1;
+	return dn_sklist_find_listener(&addr);
+err_out:
+	return NULL;
+}
 
+static void dn_nsp_conn_init(struct sock *sk, struct sk_buff *skb)
+{
 	/* printk(KERN_DEBUG "checking backlog...\n"); */
-	if (sk->ack_backlog >= sk->max_ack_backlog)
-		goto free_out;
+	if (sk->ack_backlog >= sk->max_ack_backlog) {
+		kfree_skb(skb);
+		return;
+	}
 
 	/* printk(KERN_DEBUG "waking up socket...\n"); */
 	sk->ack_backlog++;
 	skb_queue_tail(&sk->receive_queue, skb);
 	sk->state_change(sk);
-
-	return 0;
-
-free_out:
-	kfree_skb(skb);
-	return 0;
 }
 
 static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
@@ -393,13 +394,17 @@
 }
 
 /*
- * Copy of sock_queue_rcv_skb (from net/core/datagram.c) to
+ * Copy of sock_queue_rcv_skb (from sock.h) to
  * queue other data segments. Also we send SIGURG here instead
  * of the normal SIGIO, 'cos its out of band data.
  */
 static __inline__ int dn_queue_other_skb(struct sock *sk, struct sk_buff *skb)
 {
+#ifdef CONFIG_FILTER
+	struct sk_filter *filter;
+#endif
 	struct dn_scp *scp = &sk->protinfo.dn;
+	unsigned long flags;
 
         /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
            number of warnings when compiling with -W --ANK
@@ -409,22 +414,28 @@
                 return -ENOMEM;
 
 #ifdef CONFIG_FILTER
-        if (sk->filter)
-        {
-                if (sk_filter(skb, sk->filter))
-                        return -EPERM;  /* Toss packet */
+        if (sk->filter) {
+		int err = 0;
+		bh_lock_sock(sk);
+                if ((filter = sk->filter) != NULL && sk_filter(skb, sk->filter))
+                        err = -EPERM;  /* Toss packet */
+		bh_unlock_sock(sk);
+		if (err)
+			return err;
         }
 #endif /* CONFIG_FILTER */
 
         skb_set_owner_r(skb, sk);
         skb_queue_tail(&scp->other_receive_queue, skb);
 
+	read_lock_irqsave(&sk->callback_lock, flags);
         if (!sk->dead) {
 		struct socket *sock = sk->socket;
 		wake_up_interruptible(sk->sleep);
 		if (!(sock->flags & SO_WAITDATA) && sock->fasync_list)
 			kill_fasync(sock->fasync_list, SIGURG);
 	}
+	read_unlock_irqrestore(&sk->callback_lock, flags);
 
         return 0;
 }
@@ -495,28 +506,21 @@
  * deals with it. It puts the socket into the NO_COMMUNICATION
  * state.
  */
-static void dn_returned_conninit(struct sk_buff *skb)
+static void dn_returned_conn_init(struct sock *sk, struct sk_buff *skb)
 {
-	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
-	struct sock *sk;
-
-	cb->dst_port = cb->src_port;
-	cb->src_port = 0;
+	struct dn_scp *scp = &sk->protinfo.dn;
 
-	if ((sk = dn_find_by_skb(skb)) != NULL) {
-		struct dn_scp *scp = &sk->protinfo.dn;
-		if (scp->state == DN_CI) {
-			scp->state = DN_NC;
-			sk->state = TCP_CLOSE;
-			if (!sk->dead)
-				sk->state_change(sk);
-		}
+	if (scp->state == DN_CI) {
+		scp->state = DN_NC;
+		sk->state = TCP_CLOSE;
+		if (!sk->dead)
+			sk->state_change(sk);
 	}
 
 	kfree_skb(skb);
 }
 
-int dn_nsp_rx(struct sk_buff *skb)
+static int dn_nsp_rx_packet(struct sk_buff *skb)
 {
 	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
 	struct sock *sk = NULL;
@@ -525,7 +529,7 @@
 	skb->h.raw    = skb->data;
 	cb->nsp_flags = *ptr++;
 
-	if (decnet_debug_level & 1)
+	if (decnet_debug_level & 2)
 		printk(KERN_DEBUG "dn_nsp_rx: Message type 0x%02x\n", (int)cb->nsp_flags);
 
 #ifdef CONFIG_DECNET_RAW
@@ -540,17 +544,17 @@
 
 	/*
 	 * Returned packets...
+	 * Swap src & dst and look up in the normal way.
 	 */
 	if (cb->rt_flags & DN_RT_F_RTS) {
-		if ((cb->nsp_flags & 0x0c) == 0x08) {
-			switch(cb->nsp_flags & 0x70) {
-				case 0x10:
-				case 0x60:
-					dn_returned_conninit(skb);
-					goto out;
-			}
-		}
-		goto free_out;
+		unsigned short tmp = cb->dst_port;
+		cb->dst_port = cb->src_port;
+		cb->src_port = tmp;
+		tmp = cb->dst;
+		cb->dst = cb->src;
+		cb->src = tmp;
+		sk = dn_find_by_skb(skb);
+		goto got_it;
 	}
 
 	/*
@@ -564,7 +568,8 @@
 				goto free_out;
 			case 0x10:
 			case 0x60:
-				return dn_conninit_rx(skb);
+				sk = dn_find_listener(skb);
+				goto got_it;
 		}
 	}
 
@@ -590,7 +595,9 @@
 	/*
 	 * Find the socket to which this skb is destined.
 	 */
-	if ((sk = dn_find_by_skb(skb)) != NULL) {
+	sk = dn_find_by_skb(skb);
+got_it:
+	if (sk != NULL) {
 		struct dn_scp *scp = &sk->protinfo.dn;
 		int ret;
 		/* printk(KERN_DEBUG "dn_nsp_rx: Found a socket\n"); */
@@ -605,6 +612,7 @@
 		else
 			sk_add_backlog(sk, skb);
 		bh_unlock_sock(sk);
+		sock_put(sk);
 
 		return ret;
 	}
@@ -612,10 +620,14 @@
 
 free_out:
 	kfree_skb(skb);
-out:
 	return 0;
 }
 
+int dn_nsp_rx(struct sk_buff *skb)
+{
+	return NF_HOOK(PF_DECnet, NF_DN_LOCAL_IN, skb, skb->rx_dev, NULL, dn_nsp_rx_packet);
+}
+
 /*
  * This is the main receive routine for sockets. It is called
  * from the above when the socket is not busy, and also from
@@ -626,12 +638,21 @@
 	struct dn_scp *scp = &sk->protinfo.dn;
 	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
 
+	if (cb->rt_flags & DN_RT_F_RTS) {
+		dn_returned_conn_init(sk, skb);
+		return 0;
+	}
+
 	/*
 	 * Control packet.
 	 */
 	if ((cb->nsp_flags & 0x0c) == 0x08) {
 		/* printk(KERN_DEBUG "control type\n"); */
 		switch(cb->nsp_flags & 0x70) {
+			case 0x10:
+			case 0x60:
+				dn_nsp_conn_init(sk, skb);
+				break;
 			case 0x20:
 				dn_nsp_conn_conf(sk, skb);
 				break;

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