diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
commit | d6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch) | |
tree | e2be02f33984c48ec019c654051d27964e42c441 /net/decnet/dn_nsp_in.c | |
parent | 609d1e803baf519487233b765eb487f9ec227a18 (diff) |
Merge with 2.3.19.
Diffstat (limited to 'net/decnet/dn_nsp_in.c')
-rw-r--r-- | net/decnet/dn_nsp_in.c | 129 |
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; |