patch-2.4.0-test6 linux/drivers/net/pppoe.c
Next file: linux/drivers/net/sis900.c
Previous file: linux/drivers/net/pcmcia/ray_cs.c
Back to the patch index
Back to the overall index
- Lines: 500
- Date:
Wed Aug 9 13:51:09 2000
- Orig file:
v2.4.0-test5/linux/drivers/net/pppoe.c
- Orig date:
Mon Jul 10 16:47:23 2000
diff -u --recursive --new-file v2.4.0-test5/linux/drivers/net/pppoe.c linux/drivers/net/pppoe.c
@@ -5,9 +5,17 @@
* PPPoE --- PPP over Ethernet (RFC 2516)
*
*
- * Version: 0.5.1
+ * Version: 0.6.1
*
- * 030700 : Fixed connect logic to allow for disconnect
+ * 030700 : Fixed connect logic to allow for disconnect.
+ * 270700 : Fixed potential SMP problems; we must protect against
+ * simultaneous invocation of ppp_input
+ * and ppp_unregister_channel.
+ * 040800 : Respect reference count mechanisms on net-devices.
+ *
+ * Module reference count is decremented in the right spot now,
+ * guards against sock_put not actually freeing the sk
+ * in pppoe_release.
*
* Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca>
*
@@ -162,36 +170,6 @@
return ret;
}
-static struct pppox_opt *__find_on_dev(struct net_device *dev,
- struct pppox_opt *start)
-{
- struct pppox_opt *po;
- int hash;
-
- if (start != NULL) {
- hash = hash_item(start->pppoe_pa.sid, start->pppoe_pa.remote);
- po = start;
- } else {
- hash = 0;
- po = NULL;
-
- while (!po && ++hash < PPPOE_HASH_SIZE)
- po = item_hash_table[hash];
- }
-
- while (po && (po->pppoe_dev != dev)){
- if (po->next) {
- po = po->next;
- } else {
- po = NULL;
- while (!po && ++hash < PPPOE_HASH_SIZE)
- po = item_hash_table[hash];
- }
- }
-
- return po;
-}
-
/**********************************************************************
*
* Set/get/delete/rehash items
@@ -204,6 +182,8 @@
read_lock_bh(&pppoe_hash_lock);
po = __get_item(sid, addr);
+ if(po)
+ sock_hold(po->sk);
read_unlock_bh(&pppoe_hash_lock);
return po;
@@ -239,20 +219,12 @@
return ret;
}
-static struct pppox_opt *find_on_dev(struct net_device *dev,
- struct pppox_opt *start)
-{
- struct pppox_opt *po;
- read_lock_bh(&pppoe_hash_lock);
- po = __find_on_dev(dev,start);
- read_unlock_bh(&pppoe_hash_lock);
- return po;
-}
+
/***************************************************************************
*
- * Handler for device events
- * Certain device events require that sockets be unconnected
+ * Handler for device events.
+ * Certain device events require that sockets be unconnected.
*
**************************************************************************/
static int pppoe_device_event(struct notifier_block *this,
@@ -261,6 +233,7 @@
int error = NOTIFY_DONE;
struct net_device *dev = (struct net_device *) ptr;
struct pppox_opt *po = NULL;
+ int hash = 0;
/* Only look at sockets that are using this specific device. */
switch (event) {
@@ -270,26 +243,40 @@
*/
case NETDEV_GOING_DOWN:
case NETDEV_DOWN:
- do {
- po = find_on_dev(dev, po);
- if(!po)
- break;
- if (po->sk->state & PPPOX_CONNECTED)
- pppox_unbind_sock(po->sk);
+ /* Find every socket on this device and kill it. */
+ read_lock_bh(&pppoe_hash_lock);
- if (po->sk->state & PPPOX_CONNECTED) {
+ while (!po && hash < PPPOE_HASH_SIZE){
+ po = item_hash_table[hash];
+ ++hash;
+ }
+
+ while (po && hash < PPPOE_HASH_SIZE){
+ if(po->pppoe_dev == dev){
lock_sock(po->sk);
- po->sk->shutdown = RCV_SHUTDOWN&SEND_SHUTDOWN;
+ if (po->sk->state & (PPPOX_CONNECTED|PPPOX_BOUND)){
+ pppox_unbind_sock(po->sk);
- po->sk->state = PPPOX_DEAD;
- po->pppoe_dev = NULL;
+ dev_put(po->pppoe_dev);
+ po->pppoe_dev = NULL;
- wake_up(po->sk->sleep);
- release_sock(po->sk);
+ po->sk->state = PPPOX_DEAD;
+ po->sk->state_change(po->sk);
+ }
+ release_sock(po->sk);
}
- } while (1);
-
+ if (po->next) {
+ po = po->next;
+ } else {
+ po = NULL;
+ while (!po && hash < PPPOE_HASH_SIZE){
+ po = item_hash_table[hash];
+ ++hash;
+ }
+ }
+ }
+ read_unlock_bh(&pppoe_hash_lock);
break;
default:
break;
@@ -310,60 +297,102 @@
/************************************************************************
*
- * Receive a PPPoE Session frame.
+ * Do the real work of receiving a PPPoE Session frame.
*
***********************************************************************/
-static int pppoe_rcv(struct sk_buff *skb,
- struct net_device *dev,
- struct packet_type *pt)
-
-{
- struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw;
- struct pppox_opt *po;
- struct sock *sk ;
-
- po = get_item((unsigned long) ph->sid, skb->mac.ethernet->h_source);
-
- if(!po)
- goto abort;
-
- sk = po->sk;
-
- if (!sk || !(sk->state & PPPOX_CONNECTED))
- goto abort;
+int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb){
+ struct pppox_opt *po=sk->protinfo.pppox;
+ struct pppox_opt *relay_po = NULL;
if (sk->state & PPPOX_BOUND) {
skb_pull(skb, sizeof(struct pppoe_hdr));
-
+
ppp_input(&po->chan, skb);
} else if( sk->state & PPPOX_RELAY ){
- struct pppox_opt *relay_po;
relay_po = get_item_by_addr( &po->pppoe_relay );
if( relay_po == NULL ||
- !( relay_po->sk->state & PPPOX_CONNECTED ) )
+ !( relay_po->sk->state & PPPOX_CONNECTED ) ){
goto abort;
-
+ }
+
skb_pull(skb, sizeof(struct pppoe_hdr));
- if( !__pppoe_xmit( relay_po->sk , skb) )
+ if( !__pppoe_xmit( relay_po->sk , skb) ){
goto abort;
-
+ }
} else {
sock_queue_rcv_skb(sk, skb);
}
-
return 1;
-
abort:
- kfree_skb(skb);
+ if(relay_po)
+ sock_put(relay_po->sk);
return 0;
+
}
+
+
+
+/************************************************************************
+ *
+ * Receive wrapper called in BH context.
+ *
+ ***********************************************************************/
+static int pppoe_rcv(struct sk_buff *skb,
+ struct net_device *dev,
+ struct packet_type *pt)
+
+{
+ struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw;
+ struct pppox_opt *po;
+ struct sock *sk ;
+ int ret;
+
+ po = get_item((unsigned long) ph->sid, skb->mac.ethernet->h_source);
+
+ if(!po){
+ kfree(skb);
+ return 0;
+ }
+
+ sk = po->sk;
+ bh_lock_sock(sk);
+
+ /* Socket state is unknown, must put skb into backlog. */
+ if( sk->lock.users != 0 ){
+ sk_add_backlog( sk, skb);
+ ret = 1;
+ }else{
+ ret = pppoe_rcv_core(sk, skb);
+ }
+
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ return ret;
+}
+
+
+/************************************************************************
+ *
+ * Receive wrapper called in process context.
+ *
+ ***********************************************************************/
+int pppoe_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ lock_sock(sk);
+ pppoe_rcv_core(sk, skb);
+ release_sock(sk);
+ return 0;
+}
+
+
+
/************************************************************************
*
* Receive a PPPoE Discovery frame.
- * -- This is solely for detection of PADT frames
+ * This is solely for detection of PADT frames
*
***********************************************************************/
static int pppoe_disc_rcv(struct sk_buff *skb,
@@ -373,7 +402,7 @@
{
struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw;
struct pppox_opt *po;
- struct sock *sk ;
+ struct sock *sk = NULL;
if (ph->code != PADT_CODE)
goto abort;
@@ -381,13 +410,15 @@
po = get_item((unsigned long) ph->sid, skb->mac.ethernet->h_source);
if (!po)
- goto abort;
+ goto abort_put;
sk = po->sk;
pppox_unbind_sock(sk);
-abort:
+ abort_put:
+ sock_put(sk);
+ abort:
kfree_skb(skb);
return 0;
}
@@ -411,34 +442,18 @@
NULL
};
-/**********************************************************************
+/***********************************************************************
*
- * The destruct hook --- this can be trashed if there is no need for
- * the sock to clear its receive queue?
+ * Really kill the socket. (Called from sock_put if refcnt == 0.)
*
- *********************************************************************/
-void sock_pppoe_destruct(struct sock *sk)
+ **********************************************************************/
+void pppoe_sock_destruct(struct sock *sk)
{
if (sk->protinfo.destruct_hook)
kfree(sk->protinfo.destruct_hook);
-
- while (skb_queue_len(&sk->receive_queue) > 0) {
- struct sk_buff *skb = skb_dequeue(&sk->receive_queue);
- if (skb)
- kfree_skb(skb);
- }
+ MOD_DEC_USE_COUNT;
}
-int pppoe_backlog_rcv(struct sock *sk, struct sk_buff *skb)
-{
- /* Never seen this called, don't expect it to be called,
- though I've curious whether or not it ever will be. */
- DEBUG(KERN_CRIT "Backlog rcv called: %p\n", sk);
-
- kfree_skb(skb);
-
- return 0;
-}
/***********************************************************************
*
@@ -469,6 +484,7 @@
sk->pprev = NULL;
sk->state = PPPOX_NONE;
sk->type = SOCK_STREAM;
+ sk->destruct = pppoe_sock_destruct;
sk->protinfo.pppox = kmalloc(sizeof(struct pppox_opt), GFP_KERNEL);
if (!sk->protinfo.pppox) {
@@ -487,7 +503,6 @@
free_sk:
sk_free(sk);
- MOD_DEC_USE_COUNT;
return error;
}
@@ -505,27 +520,24 @@
pppox_unbind_sock(sk);
- sock_orphan(sk);
-
/* Signal the death of the socket. */
sk->state = PPPOX_DEAD;
po = sk->protinfo.pppox;
if (po->pppoe_pa.sid)
delete_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
-
- kfree(po);
-
+
+ if (po->pppoe_dev)
+ dev_put(po->pppoe_dev);
/* Should also do a queue purge here */
- sk->protinfo.pppox = NULL;
+ sock_orphan(sk);
sock->sk = NULL;
skb_queue_purge(&sk->receive_queue);
sock_put(sk);
- MOD_DEC_USE_COUNT;
return error;
}
@@ -546,14 +558,15 @@
if (sp->sa_protocol != PX_PROTO_OE)
goto end;
+ /* Check for already bound sockets */
error = -EBUSY;
if ((sk->state & PPPOX_CONNECTED) && sp->sa_addr.pppoe.sid)
goto end;
- dev = dev_get_by_name(sp->sa_addr.pppoe.dev);
-
- error = -ENODEV;
- if (!dev)
+ /* Check for already disconnected sockets,
+ on attempts to disconnect */
+ error = -EALREADY;
+ if((sk->state & PPPOX_DEAD) && !sp->sa_addr.pppoe.sid )
goto end;
error = 0;
@@ -563,6 +576,8 @@
/* Delete the old binding */
delete_item(po->pppoe_pa.sid,po->pppoe_pa.remote);
+ dev_put(po->pppoe_dev);
+
memset(po, 0, sizeof(struct pppox_opt));
po->sk = sk;
@@ -571,6 +586,14 @@
/* Don't re-bind if sid==0 */
if (sp->sa_addr.pppoe.sid != 0) {
+ dev = dev_get_by_name(sp->sa_addr.pppoe.dev);
+
+ error = -ENODEV;
+ if (!dev)
+ goto end;
+
+ if( ! (dev->flags & IFF_UP) )
+ goto end;
memcpy(&po->pppoe_pa,
&sp->sa_addr.pppoe,
sizeof(struct pppoe_addr));
@@ -743,6 +766,8 @@
hdr.code = 0;
hdr.sid = sk->num;
+ lock_sock(sk);
+
dev = sk->protinfo.pppox->pppoe_dev;
skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32,
@@ -755,14 +780,19 @@
/* Reserve space for headers. */
skb_reserve(skb, dev->hard_header_len);
skb->nh.raw = skb->data;
- skb->dev = dev;
+
+ skb->rx_dev = skb->dev = dev;
+ dev_hold(skb->rx_dev);
+
skb->priority = sk->priority;
skb->protocol = __constant_htons(ETH_P_PPP_SES);
ph = (struct pppoe_hdr *) skb_put(skb, total_len + sizeof(struct pppoe_hdr));
start = (char *) &ph->tag[0];
- copied = memcpy_fromiovec( start, m->msg_iov, m->msg_iovlen);
+ error = copied = memcpy_fromiovec( start, m->msg_iov, m->msg_iovlen);
+ if( error <= 0 )
+ goto end;
dev->hard_header(skb, dev, ETH_P_PPP_SES,
sk->protinfo.pppox->pppoe_pa.remote,
@@ -773,9 +803,9 @@
ph->length = htons(copied);
dev_queue_xmit(skb);
- return copied;
end:
+ release_sock(sk);
return error;
}
@@ -830,7 +860,12 @@
skb->protocol = __constant_htons(ETH_P_PPP_SES);
skb->nh.raw = skb->data;
- skb->dev = dev;
+
+ /* Change device of skb, update reference counts */
+ if(skb->rx_dev)
+ dev_put(skb->rx_dev);
+ skb->rx_dev = skb->dev = dev;
+ dev_hold(skb->rx_dev);
dev->hard_header(skb, dev, ETH_P_PPP_SES,
sk->protinfo.pppox->pppoe_pa.remote,
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)