vserver 2.0 rc7
[linux-2.6.git] / drivers / net / ppp_generic.c
index ffd5ac3..3b377f6 100644 (file)
@@ -19,7 +19,7 @@
  * PPP driver, written by Michael Callahan and Al Longyear, and
  * subsequently hacked by Paul Mackerras.
  *
- * ==FILEVERSION 20020217==
+ * ==FILEVERSION 20041108==
  */
 
 #include <linux/config.h>
@@ -129,8 +129,9 @@ struct ppp {
 #endif /* CONFIG_PPP_MULTILINK */
        struct net_device_stats stats;  /* statistics */
 #ifdef CONFIG_PPP_FILTER
-       struct sock_fprog pass_filter;  /* filter for packets to pass */
-       struct sock_fprog active_filter;/* filter for pkts to reset idle */
+       struct sock_filter *pass_filter;        /* filter for packets to pass */
+       struct sock_filter *active_filter;/* filter for pkts to reset idle */
+       unsigned pass_len, active_len;
 #endif /* CONFIG_PPP_FILTER */
 };
 
@@ -209,7 +210,7 @@ static atomic_t ppp_unit_count = ATOMIC_INIT(0);
  * and the atomicity of find a channel and updating its file.refcnt
  * field.
  */
-static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(all_channels_lock);
 static LIST_HEAD(all_channels);
 static LIST_HEAD(new_channels);
 static int last_channel_index;
@@ -370,7 +371,7 @@ static int ppp_release(struct inode *inode, struct file *file)
        struct ppp *ppp;
 
        if (pf != 0) {
-               file->private_data = 0;
+               file->private_data = NULL;
                if (pf->kind == INTERFACE) {
                        ppp = PF_TO_PPP(pf);
                        if (file == ppp->owner)
@@ -396,7 +397,7 @@ static ssize_t ppp_read(struct file *file, char __user *buf,
        struct ppp_file *pf = file->private_data;
        DECLARE_WAITQUEUE(wait, current);
        ssize_t ret;
-       struct sk_buff *skb = 0;
+       struct sk_buff *skb = NULL;
 
        ret = count;
 
@@ -411,6 +412,17 @@ static ssize_t ppp_read(struct file *file, char __user *buf,
                ret = 0;
                if (pf->dead)
                        break;
+               if (pf->kind == INTERFACE) {
+                       /*
+                        * Return 0 (EOF) on an interface that has no
+                        * channels connected, unless it is looping
+                        * network traffic (demand mode).
+                        */
+                       struct ppp *ppp = PF_TO_PPP(pf);
+                       if (ppp->n_channels == 0
+                           && (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+                               break;
+               }
                ret = -EAGAIN;
                if (file->f_flags & O_NONBLOCK)
                        break;
@@ -490,9 +502,56 @@ static unsigned int ppp_poll(struct file *file, poll_table *wait)
                mask |= POLLIN | POLLRDNORM;
        if (pf->dead)
                mask |= POLLHUP;
+       else if (pf->kind == INTERFACE) {
+               /* see comment in ppp_read */
+               struct ppp *ppp = PF_TO_PPP(pf);
+               if (ppp->n_channels == 0
+                   && (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+                       mask |= POLLIN | POLLRDNORM;
+       }
+
        return mask;
 }
 
+#ifdef CONFIG_PPP_FILTER
+static int get_filter(void __user *arg, struct sock_filter **p)
+{
+       struct sock_fprog uprog;
+       struct sock_filter *code = NULL;
+       int len, err;
+
+       if (copy_from_user(&uprog, arg, sizeof(uprog)))
+               return -EFAULT;
+
+       if (uprog.len > BPF_MAXINSNS)
+               return -EINVAL;
+
+       if (!uprog.len) {
+               *p = NULL;
+               return 0;
+       }
+
+       len = uprog.len * sizeof(struct sock_filter);
+       code = kmalloc(len, GFP_KERNEL);
+       if (code == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(code, uprog.filter, len)) {
+               kfree(code);
+               return -EFAULT;
+       }
+
+       err = sk_chk_filter(code, uprog.len);
+       if (err) {
+               kfree(code);
+               return err;
+       }
+
+       *p = code;
+       return uprog.len;
+}
+#endif /* CONFIG_PPP_FILTER */
+
 static int ppp_ioctl(struct inode *inode, struct file *file,
                     unsigned int cmd, unsigned long arg)
 {
@@ -503,6 +562,8 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
        struct npioctl npi;
        int unit, cflags;
        struct slcompress *vj;
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
 
        if (pf == 0)
                return ppp_unattached_ioctl(pf, file, cmd, arg);
@@ -540,7 +601,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
 
                switch (cmd) {
                case PPPIOCCONNECT:
-                       if (get_user(unit, (int *) arg))
+                       if (get_user(unit, p))
                                break;
                        err = ppp_connect_channel(pch, unit);
                        break;
@@ -569,14 +630,14 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
        ppp = PF_TO_PPP(pf);
        switch (cmd) {
        case PPPIOCSMRU:
-               if (get_user(val, (int *) arg))
+               if (get_user(val, p))
                        break;
                ppp->mru = val;
                err = 0;
                break;
 
        case PPPIOCSFLAGS:
-               if (get_user(val, (int *) arg))
+               if (get_user(val, p))
                        break;
                ppp_lock(ppp);
                cflags = ppp->flags & ~val;
@@ -589,7 +650,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
 
        case PPPIOCGFLAGS:
                val = ppp->flags | ppp->xstate | ppp->rstate;
-               if (put_user(val, (int *) arg))
+               if (put_user(val, p))
                        break;
                err = 0;
                break;
@@ -599,20 +660,20 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
                break;
 
        case PPPIOCGUNIT:
-               if (put_user(ppp->file.index, (int *) arg))
+               if (put_user(ppp->file.index, p))
                        break;
                err = 0;
                break;
 
        case PPPIOCSDEBUG:
-               if (get_user(val, (int *) arg))
+               if (get_user(val, p))
                        break;
                ppp->debug = val;
                err = 0;
                break;
 
        case PPPIOCGDEBUG:
-               if (put_user(ppp->debug, (int *) arg))
+               if (put_user(ppp->debug, p))
                        break;
                err = 0;
                break;
@@ -620,13 +681,13 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
        case PPPIOCGIDLE:
                idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
                idle.recv_idle = (jiffies - ppp->last_recv) / HZ;
-               if (copy_to_user((void __user *) arg, &idle, sizeof(idle)))
+               if (copy_to_user(argp, &idle, sizeof(idle)))
                        break;
                err = 0;
                break;
 
        case PPPIOCSMAXCID:
-               if (get_user(val, (int *) arg))
+               if (get_user(val, p))
                        break;
                val2 = 15;
                if ((val >> 16) != 0) {
@@ -649,7 +710,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
 
        case PPPIOCGNPMODE:
        case PPPIOCSNPMODE:
-               if (copy_from_user(&npi, (void __user *) arg, sizeof(npi)))
+               if (copy_from_user(&npi, argp, sizeof(npi)))
                        break;
                err = proto_to_npindex(npi.protocol);
                if (err < 0)
@@ -658,7 +719,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
                if (cmd == PPPIOCGNPMODE) {
                        err = -EFAULT;
                        npi.mode = ppp->npmode[i];
-                       if (copy_to_user((void __user *) arg, &npi, sizeof(npi)))
+                       if (copy_to_user(argp, &npi, sizeof(npi)))
                                break;
                } else {
                        ppp->npmode[i] = npi.mode;
@@ -670,49 +731,38 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
 
 #ifdef CONFIG_PPP_FILTER
        case PPPIOCSPASS:
+       {
+               struct sock_filter *code;
+               err = get_filter(argp, &code);
+               if (err >= 0) {
+                       ppp_lock(ppp);
+                       kfree(ppp->pass_filter);
+                       ppp->pass_filter = code;
+                       ppp->pass_len = err;
+                       ppp_unlock(ppp);
+                       err = 0;
+               }
+               break;
+       }
        case PPPIOCSACTIVE:
        {
-               struct sock_fprog uprog, *filtp;
-               struct sock_filter *code = NULL;
-               int len;
-
-               if (copy_from_user(&uprog, (void __user *) arg, sizeof(uprog)))
-                       break;
-               err = -EINVAL;
-               if (uprog.len > BPF_MAXINSNS)
-                       break;
-               err = -ENOMEM;
-               if (uprog.len > 0) {
-                       len = uprog.len * sizeof(struct sock_filter);
-                       code = kmalloc(len, GFP_KERNEL);
-                       if (code == NULL)
-                               break;
-                       err = -EFAULT;
-                       if (copy_from_user(code, (void __user *) uprog.filter, len)) {
-                               kfree(code);
-                               break;
-                       }
-                       err = sk_chk_filter(code, uprog.len);
-                       if (err) {
-                               kfree(code);
-                               break;
-                       }
+               struct sock_filter *code;
+               err = get_filter(argp, &code);
+               if (err >= 0) {
+                       ppp_lock(ppp);
+                       kfree(ppp->active_filter);
+                       ppp->active_filter = code;
+                       ppp->active_len = err;
+                       ppp_unlock(ppp);
+                       err = 0;
                }
-               filtp = (cmd == PPPIOCSPASS)? &ppp->pass_filter: &ppp->active_filter;
-               ppp_lock(ppp);
-               if (filtp->filter)
-                       kfree(filtp->filter);
-               filtp->filter = code;
-               filtp->len = uprog.len;
-               ppp_unlock(ppp);
-               err = 0;
                break;
        }
 #endif /* CONFIG_PPP_FILTER */
 
 #ifdef CONFIG_PPP_MULTILINK
        case PPPIOCSMRRU:
-               if (get_user(val, (int *) arg))
+               if (get_user(val, p))
                        break;
                ppp_recv_lock(ppp);
                ppp->mrru = val;
@@ -734,11 +784,12 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
        int unit, err = -EFAULT;
        struct ppp *ppp;
        struct channel *chan;
+       int __user *p = (int __user *)arg;
 
        switch (cmd) {
        case PPPIOCNEWUNIT:
                /* Create a new ppp unit */
-               if (get_user(unit, (int *) arg))
+               if (get_user(unit, p))
                        break;
                ppp = ppp_create_interface(unit, &err);
                if (ppp == 0)
@@ -746,14 +797,14 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
                file->private_data = &ppp->file;
                ppp->owner = file;
                err = -EFAULT;
-               if (put_user(ppp->file.index, (int *) arg))
+               if (put_user(ppp->file.index, p))
                        break;
                err = 0;
                break;
 
        case PPPIOCATTACH:
                /* Attach to an existing ppp unit */
-               if (get_user(unit, (int *) arg))
+               if (get_user(unit, p))
                        break;
                down(&all_ppp_sem);
                err = -ENXIO;
@@ -767,7 +818,7 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
                break;
 
        case PPPIOCATTCHAN:
-               if (get_user(unit, (int *) arg))
+               if (get_user(unit, p))
                        break;
                spin_lock_bh(&all_channels_lock);
                err = -ENXIO;
@@ -994,23 +1045,19 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
                /* check if we should pass this packet */
                /* the filter instructions are constructed assuming
                   a four-byte PPP header on each packet */
-               {
-                       u_int16_t *p = (u_int16_t *) skb_push(skb, 2);
-
-                       *p = htons(4); /* indicate outbound in DLT_LINUX_SLL */;
-               }
-               if (ppp->pass_filter.filter
-                   && sk_run_filter(skb, ppp->pass_filter.filter,
-                                    ppp->pass_filter.len) == 0) {
+               *skb_push(skb, 2) = 1;
+               if (ppp->pass_filter
+                   && sk_run_filter(skb, ppp->pass_filter,
+                                    ppp->pass_len) == 0) {
                        if (ppp->debug & 1)
                                printk(KERN_DEBUG "PPP: outbound frame not passed\n");
                        kfree_skb(skb);
                        return;
                }
                /* if this packet passes the active filter, record the time */
-               if (!(ppp->active_filter.filter
-                     && sk_run_filter(skb, ppp->active_filter.filter,
-                                      ppp->active_filter.len) == 0))
+               if (!(ppp->active_filter
+                     && sk_run_filter(skb, ppp->active_filter,
+                                      ppp->active_len) == 0))
                        ppp->last_xmit = jiffies;
                skb_pull(skb, 2);
 #else
@@ -1129,7 +1176,7 @@ ppp_push(struct ppp *ppp)
        list = &ppp->channels;
        if (list_empty(list)) {
                /* nowhere to send the packet, just drop it */
-               ppp->xmit_pending = 0;
+               ppp->xmit_pending = NULL;
                kfree_skb(skb);
                return;
        }
@@ -1142,11 +1189,11 @@ ppp_push(struct ppp *ppp)
                spin_lock_bh(&pch->downl);
                if (pch->chan) {
                        if (pch->chan->ops->start_xmit(pch->chan, skb))
-                               ppp->xmit_pending = 0;
+                               ppp->xmit_pending = NULL;
                } else {
                        /* channel got unregistered */
                        kfree_skb(skb);
-                       ppp->xmit_pending = 0;
+                       ppp->xmit_pending = NULL;
                }
                spin_unlock_bh(&pch->downl);
                return;
@@ -1159,7 +1206,7 @@ ppp_push(struct ppp *ppp)
                return;
 #endif /* CONFIG_PPP_MULTILINK */
 
-       ppp->xmit_pending = 0;
+       ppp->xmit_pending = NULL;
        kfree_skb(skb);
 }
 
@@ -1541,22 +1588,18 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                /* check if the packet passes the pass and active filters */
                /* the filter instructions are constructed assuming
                   a four-byte PPP header on each packet */
-               {
-                       u_int16_t *p = (u_int16_t *) skb_push(skb, 2);
-
-                       *p = 0; /* indicate inbound in DLT_LINUX_SLL */
-               }
-               if (ppp->pass_filter.filter
-                   && sk_run_filter(skb, ppp->pass_filter.filter,
-                                    ppp->pass_filter.len) == 0) {
+               *skb_push(skb, 2) = 0;
+               if (ppp->pass_filter
+                   && sk_run_filter(skb, ppp->pass_filter,
+                                    ppp->pass_len) == 0) {
                        if (ppp->debug & 1)
                                printk(KERN_DEBUG "PPP: inbound frame not passed\n");
                        kfree_skb(skb);
                        return;
                }
-               if (!(ppp->active_filter.filter
-                     && sk_run_filter(skb, ppp->active_filter.filter,
-                                      ppp->active_filter.len) == 0))
+               if (!(ppp->active_filter
+                     && sk_run_filter(skb, ppp->active_filter,
+                                      ppp->active_len) == 0))
                        ppp->last_recv = jiffies;
                skb_pull(skb, 2);
 #else
@@ -1571,6 +1614,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                        skb->dev = ppp->dev;
                        skb->protocol = htons(npindex_to_ethertype[npi]);
                        skb->mac.raw = skb->data;
+                       skb->input_dev = ppp->dev;
                        netif_rx(skb);
                        ppp->dev->last_rx = jiffies;
                }
@@ -1895,7 +1939,7 @@ ppp_register_channel(struct ppp_channel *chan)
 #endif /* CONFIG_PPP_MULTILINK */
        init_rwsem(&pch->chan_sem);
        spin_lock_init(&pch->downl);
-       pch->upl = RW_LOCK_UNLOCKED;
+       rwlock_init(&pch->upl);
        spin_lock_bh(&all_channels_lock);
        pch->file.index = ++last_channel_index;
        list_add(&pch->list, &new_channels);
@@ -1944,7 +1988,7 @@ ppp_unregister_channel(struct ppp_channel *chan)
 
        if (pch == 0)
                return;         /* should never happen */
-       chan->ppp = 0;
+       chan->ppp = NULL;
 
        /*
         * This ensures that we have returned from any calls into the
@@ -1952,7 +1996,7 @@ ppp_unregister_channel(struct ppp_channel *chan)
         */
        down_write(&pch->chan_sem);
        spin_lock_bh(&pch->downl);
-       pch->chan = 0;
+       pch->chan = NULL;
        spin_unlock_bh(&pch->downl);
        up_write(&pch->chan_sem);
        ppp_disconnect_channel(pch);
@@ -2155,11 +2199,11 @@ ppp_ccp_closed(struct ppp *ppp)
        ppp->xstate = 0;
        xcomp = ppp->xcomp;
        xstate = ppp->xc_state;
-       ppp->xc_state = 0;
+       ppp->xc_state = NULL;
        ppp->rstate = 0;
        rcomp = ppp->rcomp;
        rstate = ppp->rc_state;
-       ppp->rc_state = 0;
+       ppp->rc_state = NULL;
        ppp_unlock(ppp);
 
        if (xstate) {
@@ -2174,7 +2218,7 @@ ppp_ccp_closed(struct ppp *ppp)
 
 /* List of compressors. */
 static LIST_HEAD(compressor_list);
-static spinlock_t compressor_list_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(compressor_list_lock);
 
 struct compressor_entry {
        struct list_head list;
@@ -2192,7 +2236,7 @@ find_comp_entry(int proto)
                if (ce->comp->compress_proto == proto)
                        return ce;
        }
-       return 0;
+       return NULL;
 }
 
 /* Register a compressor */
@@ -2237,7 +2281,7 @@ static struct compressor *
 find_compressor(int type)
 {
        struct compressor_entry *ce;
-       struct compressor *cp = 0;
+       struct compressor *cp = NULL;
 
        spin_lock(&compressor_list_lock);
        ce = find_comp_entry(type);
@@ -2381,7 +2425,7 @@ static void ppp_shutdown_interface(struct ppp *ppp)
        down(&all_ppp_sem);
        ppp_lock(ppp);
        dev = ppp->dev;
-       ppp->dev = 0;
+       ppp->dev = NULL;
        ppp_unlock(ppp);
        /* This will call dev_close() for us. */
        if (dev) {
@@ -2415,7 +2459,7 @@ static void ppp_destroy_interface(struct ppp *ppp)
        ppp_ccp_closed(ppp);
        if (ppp->vj) {
                slhc_free(ppp->vj);
-               ppp->vj = 0;
+               ppp->vj = NULL;
        }
        skb_queue_purge(&ppp->file.xq);
        skb_queue_purge(&ppp->file.rq);
@@ -2423,14 +2467,10 @@ static void ppp_destroy_interface(struct ppp *ppp)
        skb_queue_purge(&ppp->mrq);
 #endif /* CONFIG_PPP_MULTILINK */
 #ifdef CONFIG_PPP_FILTER
-       if (ppp->pass_filter.filter) {
-               kfree(ppp->pass_filter.filter);
-               ppp->pass_filter.filter = NULL;
-       }
-       if (ppp->active_filter.filter) {
-               kfree(ppp->active_filter.filter);
-               ppp->active_filter.filter = 0;
-       }
+       kfree(ppp->pass_filter);
+       ppp->pass_filter = NULL;
+       kfree(ppp->active_filter);
+       ppp->active_filter = NULL;
 #endif /* CONFIG_PPP_FILTER */
 
        kfree(ppp);
@@ -2475,7 +2515,7 @@ ppp_find_channel(int unit)
                if (pch->file.index == unit)
                        return pch;
        }
-       return 0;
+       return NULL;
 }
 
 /*
@@ -2534,7 +2574,8 @@ ppp_disconnect_channel(struct channel *pch)
                /* remove it from the ppp unit's list */
                ppp_lock(ppp);
                list_del(&pch->clist);
-               --ppp->n_channels;
+               if (--ppp->n_channels == 0)
+                       wake_up_interruptible(&ppp->file.rwait);
                ppp_unlock(ppp);
                if (atomic_dec_and_test(&ppp->file.refcnt))
                        ppp_destroy_interface(ppp);
@@ -2696,8 +2737,6 @@ EXPORT_SYMBOL(ppp_input_error);
 EXPORT_SYMBOL(ppp_output_wakeup);
 EXPORT_SYMBOL(ppp_register_compressor);
 EXPORT_SYMBOL(ppp_unregister_compressor);
-EXPORT_SYMBOL(all_ppp_units); /* for debugging */
-EXPORT_SYMBOL(all_channels); /* for debugging */
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_CHARDEV_MAJOR(PPP_MAJOR);
 MODULE_ALIAS("/dev/ppp");