summaryrefslogtreecommitdiffstats
path: root/net/decnet/dn_nsp_in.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
commitd6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch)
treee2be02f33984c48ec019c654051d27964e42c441 /net/decnet/dn_nsp_in.c
parent609d1e803baf519487233b765eb487f9ec227a18 (diff)
Merge with 2.3.19.
Diffstat (limited to 'net/decnet/dn_nsp_in.c')
-rw-r--r--net/decnet/dn_nsp_in.c129
1 files changed, 74 insertions, 55 deletions
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
index 6d10df045..23119cd9f 100644
--- a/net/decnet/dn_nsp_in.c
+++ b/net/decnet/dn_nsp_in.c
@@ -19,6 +19,8 @@
* 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
+ * Steve Whitehouse: Fixed lockup when socket filtering was enabled.
*/
/******************************************************************************
@@ -60,6 +62,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 +152,8 @@ static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
* 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 +171,29 @@ static int dn_conninit_rx(struct sk_buff *skb)
/* 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 +395,16 @@ out:
}
/*
- * Copy of sock_queue_rcv_skb (from net/core/datagram.c) to
- * queue other data segments. Also we send SIGURG here instead
- * of the normal SIGIO, 'cos its out of band data.
+ * Copy of sock_queue_rcv_skb (from sock.h) with out
+ * bh_lock_sock() (its already held when this is called) which
+ * also allows data and other data to be queued to a socket.
*/
-static __inline__ int dn_queue_other_skb(struct sock *sk, struct sk_buff *skb)
+static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig, struct sk_buff_head *queue)
{
- struct dn_scp *scp = &sk->protinfo.dn;
+#ifdef CONFIG_FILTER
+ struct sk_filter *filter;
+#endif
+ unsigned long flags;
/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
number of warnings when compiling with -W --ANK
@@ -409,22 +414,26 @@ static __inline__ int dn_queue_other_skb(struct sock *sk, struct sk_buff *skb)
return -ENOMEM;
#ifdef CONFIG_FILTER
- if (sk->filter)
- {
- if (sk_filter(skb, sk->filter))
- return -EPERM; /* Toss packet */
+ if (sk->filter) {
+ int err = 0;
+ if ((filter = sk->filter) != NULL && sk_filter(skb, sk->filter))
+ err = -EPERM; /* Toss packet */
+ if (err)
+ return err;
}
#endif /* CONFIG_FILTER */
skb_set_owner_r(skb, sk);
- skb_queue_tail(&scp->other_receive_queue, skb);
+ skb_queue_tail(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);
+ kill_fasync(sock->fasync_list, sig);
}
+ read_unlock_irqrestore(&sk->callback_lock, flags);
return 0;
}
@@ -444,7 +453,7 @@ static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
if (((sk->protinfo.dn.numoth_rcv + 1) & 0x0fff) == (segnum & 0x0fff)) {
- if (dn_queue_other_skb(sk, skb) == 0) {
+ if (dn_queue_skb(sk, skb, SIGURG, &scp->other_receive_queue) == 0) {
sk->protinfo.dn.numoth_rcv++;
scp->other_report = 0;
queued = 1;
@@ -473,7 +482,7 @@ static void dn_nsp_data(struct sock *sk, struct sk_buff *skb)
if (((sk->protinfo.dn.numdat_rcv + 1) & 0x0FFF) ==
(segnum & 0x0FFF)) {
- if (sock_queue_rcv_skb(sk, skb) == 0) {
+ if (dn_queue_skb(sk, skb, SIGIO, &sk->receive_queue) == 0) {
sk->protinfo.dn.numdat_rcv++;
queued = 1;
}
@@ -495,28 +504,21 @@ out:
* 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 +527,7 @@ int dn_nsp_rx(struct sk_buff *skb)
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 +542,17 @@ int dn_nsp_rx(struct sk_buff *skb)
/*
* 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 +566,8 @@ int dn_nsp_rx(struct sk_buff *skb)
goto free_out;
case 0x10:
case 0x60:
- return dn_conninit_rx(skb);
+ sk = dn_find_listener(skb);
+ goto got_it;
}
}
@@ -590,7 +593,9 @@ int dn_nsp_rx(struct sk_buff *skb)
/*
* 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 +610,7 @@ int dn_nsp_rx(struct sk_buff *skb)
else
sk_add_backlog(sk, skb);
bh_unlock_sock(sk);
+ sock_put(sk);
return ret;
}
@@ -612,10 +618,14 @@ int dn_nsp_rx(struct sk_buff *skb)
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 +636,21 @@ int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
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;