diff options
Diffstat (limited to 'drivers/net/ppp/ppp_generic.c')
| -rw-r--r-- | drivers/net/ppp/ppp_generic.c | 777 |
1 files changed, 548 insertions, 229 deletions
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index c708400fff4a..f9f0f16c41d1 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Generic PPP layer for Linux. * * Copyright 1999-2002 Paul Mackerras. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * * The generic PPP layer handles the PPP network interfaces, the * /dev/ppp device, packet and VJ compression, and multilink. * It talks to PPP `channels' via the interface defined in @@ -37,6 +33,7 @@ #include <linux/ppp_channel.h> #include <linux/ppp-comp.h> #include <linux/skbuff.h> +#include <linux/rculist.h> #include <linux/rtnetlink.h> #include <linux/if_arp.h> #include <linux/ip.h> @@ -48,7 +45,8 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/file.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> +#include <net/netdev_lock.h> #include <net/slhc_vj.h> #include <linux/atomic.h> #include <linux/refcount.h> @@ -73,6 +71,20 @@ #define MPHDRLEN 6 /* multilink protocol header length */ #define MPHDRLEN_SSN 4 /* ditto with short sequence numbers */ +#define PPP_PROTO_LEN 2 +#define PPP_LCP_HDRLEN 4 + +/* The filter instructions generated by libpcap are constructed + * assuming a four-byte PPP header on each packet, where the last + * 2 bytes are the protocol field defined in the RFC and the first + * byte of the first 2 bytes indicates the direction. + * The second byte is currently unused, but we still need to initialize + * it to prevent crafted BPF programs from reading them which would + * cause reading of uninitialized data. + */ +#define PPP_FILTER_OUTBOUND_TAG 0x0100 +#define PPP_FILTER_INBOUND_TAG 0x0000 + /* * An instance of /dev/ppp can be associated with either a ppp * interface unit or a ppp channel. In both cases, file->private_data @@ -96,16 +108,9 @@ struct ppp_file { #define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp) #define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel) -/* - * Data structure to hold primary network stats for which - * we want to use 64 bit storage. Other network stats - * are stored in dev->stats of the ppp strucute. - */ -struct ppp_link_stats { - u64 rx_packets; - u64 tx_packets; - u64 rx_bytes; - u64 tx_bytes; +struct ppp_xmit_recursion { + struct task_struct *owner; + local_lock_t bh_lock; }; /* @@ -121,7 +126,7 @@ struct ppp { int n_channels; /* how many channels are attached 54 */ spinlock_t rlock; /* lock for receive side 58 */ spinlock_t wlock; /* lock for transmit side 5c */ - int __percpu *xmit_recursion; /* xmit recursion detect */ + struct ppp_xmit_recursion __percpu *xmit_recursion; /* xmit recursion detect */ int mru; /* max receive unit 60 */ unsigned int flags; /* control bits 64 */ unsigned int xstate; /* transmit state bits 68 */ @@ -151,7 +156,6 @@ struct ppp { struct bpf_prog *active_filter; /* filter for pkts to reset idle */ #endif /* CONFIG_PPP_FILTER */ struct net *ppp_net; /* the net we belong to */ - struct ppp_link_stats stats64; /* 64 bit network stats */ }; /* @@ -175,10 +179,12 @@ struct channel { struct ppp_channel *chan; /* public channel data structure */ struct rw_semaphore chan_sem; /* protects `chan' during chan ioctl */ spinlock_t downl; /* protects `chan', file.xq dequeue */ - struct ppp *ppp; /* ppp unit we're connected to */ + struct ppp __rcu *ppp; /* ppp unit we're connected to */ struct net *chan_net; /* the net channel belongs to */ + netns_tracker ns_tracker; struct list_head clist; /* link in list of channels per unit */ - rwlock_t upl; /* protects `ppp' */ + spinlock_t upl; /* protects `ppp' and 'bridge' */ + struct channel __rcu *bridge; /* "bridged" ppp channel */ #ifdef CONFIG_PPP_MULTILINK u8 avail; /* flag used in multilink stuff */ u8 had_frag; /* >= 1 fragments have been sent */ @@ -274,7 +280,7 @@ static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb); static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp); static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb); #endif /* CONFIG_PPP_MULTILINK */ -static int ppp_set_compress(struct ppp *ppp, unsigned long arg); +static int ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data); static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound); static void ppp_ccp_closed(struct ppp *ppp); static struct compressor *find_compressor(int type); @@ -287,7 +293,7 @@ static struct channel *ppp_find_channel(struct ppp_net *pn, int unit); static int ppp_connect_channel(struct channel *pch, int unit); static int ppp_disconnect_channel(struct channel *pch); static void ppp_destroy_channel(struct channel *pch); -static int unit_get(struct idr *p, void *ptr); +static int unit_get(struct idr *p, void *ptr, int min); static int unit_set(struct idr *p, void *ptr, int n); static void unit_put(struct idr *p, int n); static void *unit_find(struct idr *p, int n); @@ -295,13 +301,13 @@ static void ppp_setup(struct net_device *dev); static const struct net_device_ops ppp_netdev_ops; -static struct class *ppp_class; +static const struct class ppp_class = { + .name = "ppp", +}; /* per net-namespace data */ static inline struct ppp_net *ppp_pernet(struct net *net) { - BUG_ON(!net); - return net_generic(net, ppp_net_id); } @@ -482,7 +488,7 @@ static ssize_t ppp_read(struct file *file, char __user *buf, ret = -EFAULT; iov.iov_base = buf; iov.iov_len = count; - iov_iter_init(&to, READ, &iov, 1, count); + iov_iter_init(&to, ITER_DEST, &iov, 1, count); if (skb_copy_datagram_iter(skb, 0, &to, skb->len)) goto outf; ret = skb->len; @@ -493,6 +499,15 @@ static ssize_t ppp_read(struct file *file, char __user *buf, return ret; } +static bool ppp_check_packet(struct sk_buff *skb, size_t count) +{ + /* LCP packets must include LCP header which 4 bytes long: + * 1-byte code, 1-byte identifier, and 2-byte length. + */ + return get_unaligned_be16(skb->data) != PPP_LCP || + count >= PPP_PROTO_LEN + PPP_LCP_HDRLEN; +} + static ssize_t ppp_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -502,6 +517,9 @@ static ssize_t ppp_write(struct file *file, const char __user *buf, if (!pf) return -ENXIO; + /* All PPP packets should start with the 2-byte protocol */ + if (count < PPP_PROTO_LEN) + return -EINVAL; ret = -ENOMEM; skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL); if (!skb) @@ -512,6 +530,11 @@ static ssize_t ppp_write(struct file *file, const char __user *buf, kfree_skb(skb); goto out; } + ret = -EINVAL; + if (unlikely(!ppp_check_packet(skb, count))) { + kfree_skb(skb); + goto out; + } switch (pf->kind) { case INTERFACE: @@ -558,36 +581,150 @@ static __poll_t ppp_poll(struct file *file, poll_table *wait) } #ifdef CONFIG_PPP_FILTER -static int get_filter(void __user *arg, struct sock_filter **p) +static struct bpf_prog *get_filter(struct sock_fprog *uprog) +{ + struct sock_fprog_kern fprog; + struct bpf_prog *res = NULL; + int err; + + if (!uprog->len) + return NULL; + + /* uprog->len is unsigned short, so no overflow here */ + fprog.len = uprog->len; + fprog.filter = memdup_array_user(uprog->filter, + uprog->len, sizeof(struct sock_filter)); + if (IS_ERR(fprog.filter)) + return ERR_CAST(fprog.filter); + + err = bpf_prog_create(&res, &fprog); + kfree(fprog.filter); + + return err ? ERR_PTR(err) : res; +} + +static struct bpf_prog *ppp_get_filter(struct sock_fprog __user *p) { struct sock_fprog uprog; - struct sock_filter *code = NULL; - int len; - if (copy_from_user(&uprog, arg, sizeof(uprog))) - return -EFAULT; + if (copy_from_user(&uprog, p, sizeof(struct sock_fprog))) + return ERR_PTR(-EFAULT); + return get_filter(&uprog); +} - if (!uprog.len) { - *p = NULL; - return 0; +#ifdef CONFIG_COMPAT +struct sock_fprog32 { + unsigned short len; + compat_caddr_t filter; +}; + +#define PPPIOCSPASS32 _IOW('t', 71, struct sock_fprog32) +#define PPPIOCSACTIVE32 _IOW('t', 70, struct sock_fprog32) + +static struct bpf_prog *compat_ppp_get_filter(struct sock_fprog32 __user *p) +{ + struct sock_fprog32 uprog32; + struct sock_fprog uprog; + + if (copy_from_user(&uprog32, p, sizeof(struct sock_fprog32))) + return ERR_PTR(-EFAULT); + uprog.len = uprog32.len; + uprog.filter = compat_ptr(uprog32.filter); + return get_filter(&uprog); +} +#endif +#endif + +/* Bridge one PPP channel to another. + * When two channels are bridged, ppp_input on one channel is redirected to + * the other's ops->start_xmit handler. + * In order to safely bridge channels we must reject channels which are already + * part of a bridge instance, or which form part of an existing unit. + * Once successfully bridged, each channel holds a reference on the other + * to prevent it being freed while the bridge is extant. + */ +static int ppp_bridge_channels(struct channel *pch, struct channel *pchb) +{ + spin_lock(&pch->upl); + if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) || + rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) { + spin_unlock(&pch->upl); + return -EALREADY; + } + refcount_inc(&pchb->file.refcnt); + rcu_assign_pointer(pch->bridge, pchb); + spin_unlock(&pch->upl); + + spin_lock(&pchb->upl); + if (rcu_dereference_protected(pchb->ppp, lockdep_is_held(&pchb->upl)) || + rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl))) { + spin_unlock(&pchb->upl); + goto err_unset; } + refcount_inc(&pch->file.refcnt); + rcu_assign_pointer(pchb->bridge, pch); + spin_unlock(&pchb->upl); + + return 0; + +err_unset: + spin_lock(&pch->upl); + /* Re-read pch->bridge with upl held in case it was modified concurrently */ + pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)); + RCU_INIT_POINTER(pch->bridge, NULL); + spin_unlock(&pch->upl); + synchronize_rcu(); - len = uprog.len * sizeof(struct sock_filter); - code = memdup_user(uprog.filter, len); - if (IS_ERR(code)) - return PTR_ERR(code); + if (pchb) + if (refcount_dec_and_test(&pchb->file.refcnt)) + ppp_destroy_channel(pchb); - *p = code; - return uprog.len; + return -EALREADY; +} + +static int ppp_unbridge_channels(struct channel *pch) +{ + struct channel *pchb, *pchbb; + + spin_lock(&pch->upl); + pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)); + if (!pchb) { + spin_unlock(&pch->upl); + return -EINVAL; + } + RCU_INIT_POINTER(pch->bridge, NULL); + spin_unlock(&pch->upl); + + /* Only modify pchb if phcb->bridge points back to pch. + * If not, it implies that there has been a race unbridging (and possibly + * even rebridging) pchb. We should leave pchb alone to avoid either a + * refcount underflow, or breaking another established bridge instance. + */ + spin_lock(&pchb->upl); + pchbb = rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl)); + if (pchbb == pch) + RCU_INIT_POINTER(pchb->bridge, NULL); + spin_unlock(&pchb->upl); + + synchronize_rcu(); + + if (pchbb == pch) + if (refcount_dec_and_test(&pch->file.refcnt)) + ppp_destroy_channel(pch); + + if (refcount_dec_and_test(&pchb->file.refcnt)) + ppp_destroy_channel(pchb); + + return 0; } -#endif /* CONFIG_PPP_FILTER */ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ppp_file *pf; struct ppp *ppp; int err = -EFAULT, val, val2, i; - struct ppp_idle idle; + struct ppp_idle32 idle32; + struct ppp_idle64 idle64; struct npioctl npi; int unit, cflags; struct slcompress *vj; @@ -616,8 +753,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } if (pf->kind == CHANNEL) { - struct channel *pch; + struct channel *pch, *pchb; struct ppp_channel *chan; + struct ppp_net *pn; pch = PF_TO_CHANNEL(pf); @@ -632,6 +770,31 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = ppp_disconnect_channel(pch); break; + case PPPIOCBRIDGECHAN: + if (get_user(unit, p)) + break; + err = -ENXIO; + pn = ppp_pernet(current->nsproxy->net_ns); + spin_lock_bh(&pn->all_channels_lock); + pchb = ppp_find_channel(pn, unit); + /* Hold a reference to prevent pchb being freed while + * we establish the bridge. + */ + if (pchb) + refcount_inc(&pchb->file.refcnt); + spin_unlock_bh(&pn->all_channels_lock); + if (!pchb) + break; + err = ppp_bridge_channels(pch, pchb); + /* Drop earlier refcount now bridge establishment is complete */ + if (refcount_dec_and_test(&pchb->file.refcnt)) + ppp_destroy_channel(pchb); + break; + + case PPPIOCUNBRIDGECHAN: + err = ppp_unbridge_channels(pch); + break; + default: down_read(&pch->chan_sem); chan = pch->chan; @@ -683,9 +846,14 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case PPPIOCSCOMPRESS: - err = ppp_set_compress(ppp, arg); + { + struct ppp_option_data data; + if (copy_from_user(&data, argp, sizeof(data))) + err = -EFAULT; + else + err = ppp_set_compress(ppp, &data); break; - + } case PPPIOCGUNIT: if (put_user(ppp->file.index, p)) break; @@ -705,10 +873,18 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = 0; break; - case PPPIOCGIDLE: - idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ; - idle.recv_idle = (jiffies - ppp->last_recv) / HZ; - if (copy_to_user(argp, &idle, sizeof(idle))) + case PPPIOCGIDLE32: + idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ; + idle32.recv_idle = (jiffies - ppp->last_recv) / HZ; + if (copy_to_user(argp, &idle32, sizeof(idle32))) + break; + err = 0; + break; + + case PPPIOCGIDLE64: + idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ; + idle64.recv_idle = (jiffies - ppp->last_recv) / HZ; + if (copy_to_user(argp, &idle64, sizeof(idle64))) break; err = 0; break; @@ -757,55 +933,25 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #ifdef CONFIG_PPP_FILTER case PPPIOCSPASS: - { - struct sock_filter *code; - - err = get_filter(argp, &code); - if (err >= 0) { - struct bpf_prog *pass_filter = NULL; - struct sock_fprog_kern fprog = { - .len = err, - .filter = code, - }; - - err = 0; - if (fprog.filter) - err = bpf_prog_create(&pass_filter, &fprog); - if (!err) { - ppp_lock(ppp); - if (ppp->pass_filter) - bpf_prog_destroy(ppp->pass_filter); - ppp->pass_filter = pass_filter; - ppp_unlock(ppp); - } - kfree(code); - } - break; - } case PPPIOCSACTIVE: { - struct sock_filter *code; + struct bpf_prog *filter = ppp_get_filter(argp); + struct bpf_prog **which; - err = get_filter(argp, &code); - if (err >= 0) { - struct bpf_prog *active_filter = NULL; - struct sock_fprog_kern fprog = { - .len = err, - .filter = code, - }; - - err = 0; - if (fprog.filter) - err = bpf_prog_create(&active_filter, &fprog); - if (!err) { - ppp_lock(ppp); - if (ppp->active_filter) - bpf_prog_destroy(ppp->active_filter); - ppp->active_filter = active_filter; - ppp_unlock(ppp); - } - kfree(code); + if (IS_ERR(filter)) { + err = PTR_ERR(filter); + break; } + if (cmd == PPPIOCSPASS) + which = &ppp->pass_filter; + else + which = &ppp->active_filter; + ppp_lock(ppp); + if (*which) + bpf_prog_destroy(*which); + *which = filter; + ppp_unlock(ppp); + err = 0; break; } #endif /* CONFIG_PPP_FILTER */ @@ -831,6 +977,77 @@ out: return err; } +#ifdef CONFIG_COMPAT +struct ppp_option_data32 { + compat_uptr_t ptr; + u32 length; + compat_int_t transmit; +}; +#define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) + +static long ppp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ppp_file *pf; + int err = -ENOIOCTLCMD; + void __user *argp = (void __user *)arg; + + mutex_lock(&ppp_mutex); + + pf = file->private_data; + if (pf && pf->kind == INTERFACE) { + struct ppp *ppp = PF_TO_PPP(pf); + switch (cmd) { +#ifdef CONFIG_PPP_FILTER + case PPPIOCSPASS32: + case PPPIOCSACTIVE32: + { + struct bpf_prog *filter = compat_ppp_get_filter(argp); + struct bpf_prog **which; + + if (IS_ERR(filter)) { + err = PTR_ERR(filter); + break; + } + if (cmd == PPPIOCSPASS32) + which = &ppp->pass_filter; + else + which = &ppp->active_filter; + ppp_lock(ppp); + if (*which) + bpf_prog_destroy(*which); + *which = filter; + ppp_unlock(ppp); + err = 0; + break; + } +#endif /* CONFIG_PPP_FILTER */ + case PPPIOCSCOMPRESS32: + { + struct ppp_option_data32 data32; + if (copy_from_user(&data32, argp, sizeof(data32))) { + err = -EFAULT; + } else { + struct ppp_option_data data = { + .ptr = compat_ptr(data32.ptr), + .length = data32.length, + .transmit = data32.transmit + }; + err = ppp_set_compress(ppp, &data); + } + break; + } + } + } + mutex_unlock(&ppp_mutex); + + /* all other commands have compatible arguments */ + if (err == -ENOIOCTLCMD) + err = ppp_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); + + return err; +} +#endif + static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, struct file *file, unsigned int cmd, unsigned long arg) { @@ -899,11 +1116,16 @@ static const struct file_operations ppp_device_fops = { .write = ppp_write, .poll = ppp_poll, .unlocked_ioctl = ppp_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ppp_compat_ioctl, +#endif .open = ppp_open, .release = ppp_release, .llseek = noop_llseek, }; +static void ppp_nl_dellink(struct net_device *dev, struct list_head *head); + static __net_init int ppp_init_net(struct net *net) { struct ppp_net *pn = net_generic(net, ppp_net_id); @@ -919,28 +1141,20 @@ static __net_init int ppp_init_net(struct net *net) return 0; } -static __net_exit void ppp_exit_net(struct net *net) +static __net_exit void ppp_exit_rtnl_net(struct net *net, + struct list_head *dev_to_kill) { struct ppp_net *pn = net_generic(net, ppp_net_id); - struct net_device *dev; - struct net_device *aux; struct ppp *ppp; - LIST_HEAD(list); int id; - rtnl_lock(); - for_each_netdev_safe(net, dev, aux) { - if (dev->netdev_ops == &ppp_netdev_ops) - unregister_netdevice_queue(dev, &list); - } - idr_for_each_entry(&pn->units_idr, ppp, id) - /* Skip devices already unregistered by previous loop */ - if (!net_eq(dev_net(ppp->dev), net)) - unregister_netdevice_queue(ppp->dev, &list); + ppp_nl_dellink(ppp->dev, dev_to_kill); +} - unregister_netdevice_many(&list); - rtnl_unlock(); +static __net_exit void ppp_exit_net(struct net *net) +{ + struct ppp_net *pn = net_generic(net, ppp_net_id); mutex_destroy(&pn->all_ppp_mutex); idr_destroy(&pn->units_idr); @@ -950,6 +1164,7 @@ static __net_exit void ppp_exit_net(struct net *net) static struct pernet_operations ppp_net_ops = { .init = ppp_init_net, + .exit_rtnl = ppp_exit_rtnl_net, .exit = ppp_exit_net, .id = &ppp_net_id, .size = sizeof(struct ppp_net), @@ -963,9 +1178,20 @@ static int ppp_unit_register(struct ppp *ppp, int unit, bool ifname_is_set) mutex_lock(&pn->all_ppp_mutex); if (unit < 0) { - ret = unit_get(&pn->units_idr, ppp); + ret = unit_get(&pn->units_idr, ppp, 0); if (ret < 0) goto err; + if (!ifname_is_set) { + while (1) { + snprintf(ppp->dev->name, IFNAMSIZ, "ppp%i", ret); + if (!netdev_name_in_use(ppp->ppp_net, ppp->dev->name)) + break; + unit_put(&pn->units_idr, ret); + ret = unit_get(&pn->units_idr, ppp, ret + 1); + if (ret < 0) + goto err; + } + } } else { /* Caller asked for a specific unit number. Fail with -EEXIST * if unavailable. For backward compatibility, return -EEXIST @@ -1029,13 +1255,18 @@ static int ppp_dev_configure(struct net *src_net, struct net_device *dev, spin_lock_init(&ppp->rlock); spin_lock_init(&ppp->wlock); - ppp->xmit_recursion = alloc_percpu(int); + ppp->xmit_recursion = alloc_percpu(struct ppp_xmit_recursion); if (!ppp->xmit_recursion) { err = -ENOMEM; goto err1; } - for_each_possible_cpu(cpu) - (*per_cpu_ptr(ppp->xmit_recursion, cpu)) = 0; + for_each_possible_cpu(cpu) { + struct ppp_xmit_recursion *xmit_recursion; + + xmit_recursion = per_cpu_ptr(ppp->xmit_recursion, cpu); + xmit_recursion->owner = NULL; + local_lock_init(&xmit_recursion->bh_lock); + } #ifdef CONFIG_PPP_MULTILINK ppp->minseq = -1; @@ -1077,10 +1308,13 @@ static int ppp_nl_validate(struct nlattr *tb[], struct nlattr *data[], return 0; } -static int ppp_nl_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[], +static int ppp_nl_newlink(struct net_device *dev, + struct rtnl_newlink_params *params, struct netlink_ext_ack *extack) { + struct net *link_net = rtnl_newlink_link_net(params); + struct nlattr **data = params->data; + struct nlattr **tb = params->tb; struct ppp_config conf = { .unit = -1, .ifname_is_set = true, @@ -1114,10 +1348,10 @@ static int ppp_nl_newlink(struct net *src_net, struct net_device *dev, * the PPP unit identifer as suffix (i.e. ppp<unit_id>). This allows * userspace to infer the device name using to the PPPIOCGUNIT ioctl. */ - if (!tb[IFLA_IFNAME]) + if (!tb[IFLA_IFNAME] || !nla_len(tb[IFLA_IFNAME]) || !*(char *)nla_data(tb[IFLA_IFNAME])) conf.ifname_is_set = false; - err = ppp_dev_configure(src_net, dev, &conf); + err = ppp_dev_configure(link_net, dev, &conf); out_unlock: mutex_unlock(&ppp_mutex); @@ -1146,7 +1380,7 @@ static struct net *ppp_nl_get_link_net(const struct net_device *dev) { struct ppp *ppp = netdev_priv(dev); - return ppp->ppp_net; + return READ_ONCE(ppp->ppp_net); } static struct rtnl_link_ops ppp_link_ops __read_mostly = { @@ -1185,11 +1419,9 @@ static int __init ppp_init(void) goto out_net; } - ppp_class = class_create(THIS_MODULE, "ppp"); - if (IS_ERR(ppp_class)) { - err = PTR_ERR(ppp_class); + err = class_register(&ppp_class); + if (err) goto out_chrdev; - } err = rtnl_link_register(&ppp_link_ops); if (err) { @@ -1198,12 +1430,12 @@ static int __init ppp_init(void) } /* not a big deal if we fail here :-) */ - device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp"); + device_create(&ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp"); return 0; out_class: - class_destroy(ppp_class); + class_unregister(&ppp_class); out_chrdev: unregister_chrdev(PPP_MAJOR, "ppp"); out_net: @@ -1260,11 +1492,11 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) } static int -ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +ppp_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr, + void __user *addr, int cmd) { struct ppp *ppp = netdev_priv(dev); int err = -EFAULT; - void __user *addr = (void __user *) ifr->ifr_ifru.ifru_data; struct ppp_stats stats; struct ppp_comp_stats cstats; char *vers; @@ -1305,23 +1537,12 @@ ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64) { - struct ppp *ppp = netdev_priv(dev); - - ppp_recv_lock(ppp); - stats64->rx_packets = ppp->stats64.rx_packets; - stats64->rx_bytes = ppp->stats64.rx_bytes; - ppp_recv_unlock(ppp); - - ppp_xmit_lock(ppp); - stats64->tx_packets = ppp->stats64.tx_packets; - stats64->tx_bytes = ppp->stats64.tx_bytes; - ppp_xmit_unlock(ppp); - stats64->rx_errors = dev->stats.rx_errors; stats64->tx_errors = dev->stats.tx_errors; stats64->rx_dropped = dev->stats.rx_dropped; stats64->tx_dropped = dev->stats.tx_dropped; stats64->rx_length_errors = dev->stats.rx_length_errors; + dev_fetch_sw_netstats(stats64, dev->tstats); } static int ppp_dev_init(struct net_device *dev) @@ -1368,15 +1589,40 @@ static void ppp_dev_priv_destructor(struct net_device *dev) ppp_destroy_interface(ppp); } +static int ppp_fill_forward_path(struct net_device_path_ctx *ctx, + struct net_device_path *path) +{ + struct ppp *ppp = netdev_priv(ctx->dev); + struct ppp_channel *chan; + struct channel *pch; + + if (ppp->flags & SC_MULTILINK) + return -EOPNOTSUPP; + + pch = list_first_or_null_rcu(&ppp->channels, struct channel, clist); + if (!pch) + return -ENODEV; + + chan = READ_ONCE(pch->chan); + if (!chan) + return -ENODEV; + + if (!chan->ops->fill_forward_path) + return -EOPNOTSUPP; + + return chan->ops->fill_forward_path(ctx, path, chan); +} + static const struct net_device_ops ppp_netdev_ops = { .ndo_init = ppp_dev_init, .ndo_uninit = ppp_dev_uninit, .ndo_start_xmit = ppp_start_xmit, - .ndo_do_ioctl = ppp_net_ioctl, + .ndo_siocdevprivate = ppp_net_siocdevprivate, .ndo_get_stats64 = ppp_get_stats64, + .ndo_fill_forward_path = ppp_fill_forward_path, }; -static struct device_type ppp_type = { +static const struct device_type ppp_type = { .name = "ppp", }; @@ -1385,7 +1631,7 @@ static void ppp_setup(struct net_device *dev) dev->netdev_ops = &ppp_netdev_ops; SET_NETDEV_DEVTYPE(dev, &ppp_type); - dev->features |= NETIF_F_LLTX; + dev->lltx = true; dev->hard_header_len = PPP_HDRLEN; dev->mtu = PPP_MRU; @@ -1394,6 +1640,7 @@ static void ppp_setup(struct net_device *dev) dev->type = ARPHRD_PPP; dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->priv_destructor = ppp_dev_priv_destructor; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; netif_keep_dst(dev); } @@ -1419,21 +1666,28 @@ static void __ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb) netif_wake_queue(ppp->dev); else netif_stop_queue(ppp->dev); + } else { + kfree_skb(skb); } ppp_xmit_unlock(ppp); } static void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb) { + struct ppp_xmit_recursion *xmit_recursion; + local_bh_disable(); - if (unlikely(*this_cpu_ptr(ppp->xmit_recursion))) + xmit_recursion = this_cpu_ptr(ppp->xmit_recursion); + if (xmit_recursion->owner == current) goto err; + local_lock_nested_bh(&ppp->xmit_recursion->bh_lock); + xmit_recursion->owner = current; - (*this_cpu_ptr(ppp->xmit_recursion))++; __ppp_xmit_process(ppp, skb); - (*this_cpu_ptr(ppp->xmit_recursion))--; + xmit_recursion->owner = NULL; + local_unlock_nested_bh(&ppp->xmit_recursion->bh_lock); local_bh_enable(); return; @@ -1490,7 +1744,6 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb) */ if (net_ratelimit()) netdev_err(ppp->dev, "ppp: compressor dropped pkt\n"); - kfree_skb(skb); consume_skb(new_skb); new_skb = NULL; } @@ -1510,14 +1763,16 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) int len; unsigned char *cp; + skb->dev = ppp->dev; + if (proto < 0x8000) { #ifdef CONFIG_PPP_FILTER - /* check if we should pass this packet */ - /* the filter instructions are constructed assuming - a four-byte PPP header on each packet */ - *(u8 *)skb_push(skb, 2) = 1; + /* check if the packet passes the pass and active filters. + * See comment for PPP_FILTER_OUTBOUND_TAG above. + */ + *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_OUTBOUND_TAG); if (ppp->pass_filter && - BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { + bpf_prog_run(ppp->pass_filter, skb) == 0) { if (ppp->debug & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: outbound frame " @@ -1527,7 +1782,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) } /* if this packet passes the active filter, record the time */ if (!(ppp->active_filter && - BPF_PROG_RUN(ppp->active_filter, skb) == 0)) + bpf_prog_run(ppp->active_filter, skb) == 0)) ppp->last_xmit = jiffies; skb_pull(skb, 2); #else @@ -1536,8 +1791,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) #endif /* CONFIG_PPP_FILTER */ } - ++ppp->stats64.tx_packets; - ppp->stats64.tx_bytes += skb->len - 2; + dev_sw_netstats_tx_add(ppp->dev, 1, skb->len - PPP_PROTO_LEN); switch (proto) { case PPP_IP: @@ -1590,9 +1844,10 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) "down - pkt dropped.\n"); goto drop; } - skb = pad_compress_skb(ppp, skb); - if (!skb) + new_skb = pad_compress_skb(ppp, skb); + if (!new_skb) goto drop; + skb = new_skb; } /* @@ -1903,10 +2158,9 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) #endif /* CONFIG_PPP_MULTILINK */ /* Try to send data out on a channel */ -static void __ppp_channel_push(struct channel *pch) +static void __ppp_channel_push(struct channel *pch, struct ppp *ppp) { struct sk_buff *skb; - struct ppp *ppp; spin_lock(&pch->downl); if (pch->chan) { @@ -1925,7 +2179,6 @@ static void __ppp_channel_push(struct channel *pch) spin_unlock(&pch->downl); /* see if there is anything from the attached unit to be sent */ if (skb_queue_empty(&pch->file.xq)) { - ppp = pch->ppp; if (ppp) __ppp_xmit_process(ppp, NULL); } @@ -1933,15 +2186,22 @@ static void __ppp_channel_push(struct channel *pch) static void ppp_channel_push(struct channel *pch) { - read_lock_bh(&pch->upl); - if (pch->ppp) { - (*this_cpu_ptr(pch->ppp->xmit_recursion))++; - __ppp_channel_push(pch); - (*this_cpu_ptr(pch->ppp->xmit_recursion))--; + struct ppp_xmit_recursion *xmit_recursion; + struct ppp *ppp; + + rcu_read_lock_bh(); + ppp = rcu_dereference_bh(pch->ppp); + if (ppp) { + xmit_recursion = this_cpu_ptr(ppp->xmit_recursion); + local_lock_nested_bh(&ppp->xmit_recursion->bh_lock); + xmit_recursion->owner = current; + __ppp_channel_push(pch, ppp); + xmit_recursion->owner = NULL; + local_unlock_nested_bh(&ppp->xmit_recursion->bh_lock); } else { - __ppp_channel_push(pch); + __ppp_channel_push(pch, NULL); } - read_unlock_bh(&pch->upl); + rcu_read_unlock_bh(); } /* @@ -2005,10 +2265,45 @@ static bool ppp_decompress_proto(struct sk_buff *skb) return pskb_may_pull(skb, 2); } +/* Attempt to handle a frame via. a bridged channel, if one exists. + * If the channel is bridged, the frame is consumed by the bridge. + * If not, the caller must handle the frame by normal recv mechanisms. + * Returns true if the frame is consumed, false otherwise. + */ +static bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb) +{ + struct channel *pchb; + + rcu_read_lock(); + pchb = rcu_dereference(pch->bridge); + if (!pchb) + goto out_rcu; + + spin_lock_bh(&pchb->downl); + if (!pchb->chan) { + /* channel got unregistered */ + kfree_skb(skb); + goto outl; + } + + skb_scrub_packet(skb, !net_eq(pch->chan_net, pchb->chan_net)); + if (!pchb->chan->ops->start_xmit(pchb->chan, skb)) + kfree_skb(skb); + +outl: + spin_unlock_bh(&pchb->downl); +out_rcu: + rcu_read_unlock(); + + /* If pchb is set then we've consumed the packet */ + return !!pchb; +} + void ppp_input(struct ppp_channel *chan, struct sk_buff *skb) { struct channel *pch = chan->ppp; + struct ppp *ppp; int proto; if (!pch) { @@ -2016,18 +2311,23 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) return; } - read_lock_bh(&pch->upl); + /* If the channel is bridged, transmit via. bridge */ + if (ppp_channel_bridge_input(pch, skb)) + return; + + rcu_read_lock_bh(); + ppp = rcu_dereference_bh(pch->ppp); if (!ppp_decompress_proto(skb)) { kfree_skb(skb); - if (pch->ppp) { - ++pch->ppp->dev->stats.rx_length_errors; - ppp_receive_error(pch->ppp); + if (ppp) { + ++ppp->dev->stats.rx_length_errors; + ppp_receive_error(ppp); } goto done; } proto = PPP_PROTO(skb); - if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { + if (!ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { /* put it on the channel queue */ skb_queue_tail(&pch->file.rq, skb); /* drop old frames if queue too long */ @@ -2036,11 +2336,11 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) kfree_skb(skb); wake_up_interruptible(&pch->file.rwait); } else { - ppp_do_recv(pch->ppp, skb, pch); + ppp_do_recv(ppp, skb, pch); } done: - read_unlock_bh(&pch->upl); + rcu_read_unlock_bh(); } /* Put a 0-length skb in the receive queue as an error indication */ @@ -2049,20 +2349,22 @@ ppp_input_error(struct ppp_channel *chan, int code) { struct channel *pch = chan->ppp; struct sk_buff *skb; + struct ppp *ppp; if (!pch) return; - read_lock_bh(&pch->upl); - if (pch->ppp) { + rcu_read_lock_bh(); + ppp = rcu_dereference_bh(pch->ppp); + if (ppp) { skb = alloc_skb(0, GFP_ATOMIC); if (skb) { skb->len = 0; /* probably unnecessary */ skb->cb[0] = code; - ppp_do_recv(pch->ppp, skb, pch); + ppp_do_recv(ppp, skb, pch); } } - read_unlock_bh(&pch->upl); + rcu_read_unlock_bh(); } /* @@ -2176,8 +2478,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) break; } - ++ppp->stats64.rx_packets; - ppp->stats64.rx_bytes += skb->len - 2; + dev_sw_netstats_rx_add(ppp->dev, skb->len - PPP_PROTO_LEN); npi = proto_to_npindex(proto); if (npi < 0) { @@ -2194,16 +2495,15 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) /* network protocol frame - give it to the kernel */ #ifdef CONFIG_PPP_FILTER - /* check if the packet passes the pass and active filters */ - /* the filter instructions are constructed assuming - a four-byte PPP header on each packet */ if (ppp->pass_filter || ppp->active_filter) { if (skb_unclone(skb, GFP_ATOMIC)) goto err; - - *(u8 *)skb_push(skb, 2) = 0; + /* Check if the packet passes the pass and active filters. + * See comment for PPP_FILTER_INBOUND_TAG above. + */ + *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_INBOUND_TAG); if (ppp->pass_filter && - BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { + bpf_prog_run(ppp->pass_filter, skb) == 0) { if (ppp->debug & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: inbound frame " @@ -2212,7 +2512,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) return; } if (!(ppp->active_filter && - BPF_PROG_RUN(ppp->active_filter, skb) == 0)) + bpf_prog_run(ppp->active_filter, skb) == 0)) ppp->last_recv = jiffies; __skb_pull(skb, 2); } else @@ -2612,9 +2912,8 @@ int ppp_register_net_channel(struct net *net, struct ppp_channel *chan) pn = ppp_pernet(net); - pch->ppp = NULL; pch->chan = chan; - pch->chan_net = get_net(net); + pch->chan_net = get_net_track(net, &pch->ns_tracker, GFP_KERNEL); chan->ppp = pch; init_ppp_file(&pch->file, CHANNEL); pch->file.hdrlen = chan->hdrlen; @@ -2623,7 +2922,7 @@ int ppp_register_net_channel(struct net *net, struct ppp_channel *chan) #endif /* CONFIG_PPP_MULTILINK */ init_rwsem(&pch->chan_sem); spin_lock_init(&pch->downl); - rwlock_init(&pch->upl); + spin_lock_init(&pch->upl); spin_lock_bh(&pn->all_channels_lock); pch->file.index = ++pn->last_channel_index; @@ -2652,13 +2951,15 @@ int ppp_channel_index(struct ppp_channel *chan) int ppp_unit_number(struct ppp_channel *chan) { struct channel *pch = chan->ppp; + struct ppp *ppp; int unit = -1; if (pch) { - read_lock_bh(&pch->upl); - if (pch->ppp) - unit = pch->ppp->file.index; - read_unlock_bh(&pch->upl); + rcu_read_lock(); + ppp = rcu_dereference(pch->ppp); + if (ppp) + unit = ppp->file.index; + rcu_read_unlock(); } return unit; } @@ -2670,12 +2971,14 @@ char *ppp_dev_name(struct ppp_channel *chan) { struct channel *pch = chan->ppp; char *name = NULL; + struct ppp *ppp; if (pch) { - read_lock_bh(&pch->upl); - if (pch->ppp && pch->ppp->dev) - name = pch->ppp->dev->name; - read_unlock_bh(&pch->upl); + rcu_read_lock(); + ppp = rcu_dereference(pch->ppp); + if (ppp && ppp->dev) + name = ppp->dev->name; + rcu_read_unlock(); } return name; } @@ -2697,12 +3000,12 @@ ppp_unregister_channel(struct ppp_channel *chan) chan->ppp = NULL; /* - * This ensures that we have returned from any calls into the + * This ensures that we have returned from any calls into * the channel's start_xmit or ioctl routine before we proceed. */ down_write(&pch->chan_sem); spin_lock_bh(&pch->downl); - pch->chan = NULL; + WRITE_ONCE(pch->chan, NULL); spin_unlock_bh(&pch->downl); up_write(&pch->chan_sem); ppp_disconnect_channel(pch); @@ -2712,8 +3015,11 @@ ppp_unregister_channel(struct ppp_channel *chan) list_del(&pch->list); spin_unlock_bh(&pn->all_channels_lock); + ppp_unbridge_channels(pch); + pch->file.dead = 1; wake_up_interruptible(&pch->file.rwait); + if (refcount_dec_and_test(&pch->file.refcnt)) ppp_destroy_channel(pch); } @@ -2738,24 +3044,20 @@ ppp_output_wakeup(struct ppp_channel *chan) /* Process the PPPIOCSCOMPRESS ioctl. */ static int -ppp_set_compress(struct ppp *ppp, unsigned long arg) +ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data) { - int err; + int err = -EFAULT; struct compressor *cp, *ocomp; - struct ppp_option_data data; void *state, *ostate; unsigned char ccp_option[CCP_MAX_OPTION_LENGTH]; - err = -EFAULT; - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + if (data->length > CCP_MAX_OPTION_LENGTH) goto out; - if (data.length > CCP_MAX_OPTION_LENGTH) - goto out; - if (copy_from_user(ccp_option, (void __user *) data.ptr, data.length)) + if (copy_from_user(ccp_option, data->ptr, data->length)) goto out; err = -EINVAL; - if (data.length < 2 || ccp_option[1] < 2 || ccp_option[1] > data.length) + if (data->length < 2 || ccp_option[1] < 2 || ccp_option[1] > data->length) goto out; cp = try_then_request_module( @@ -2765,8 +3067,8 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg) goto out; err = -ENOBUFS; - if (data.transmit) { - state = cp->comp_alloc(ccp_option, data.length); + if (data->transmit) { + state = cp->comp_alloc(ccp_option, data->length); if (state) { ppp_xmit_lock(ppp); ppp->xstate &= ~SC_COMP_RUN; @@ -2784,7 +3086,7 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg) module_put(cp->owner); } else { - state = cp->decomp_alloc(ccp_option, data.length); + state = cp->decomp_alloc(ccp_option, data->length); if (state) { ppp_recv_lock(ppp); ppp->rstate &= ~SC_DECOMP_RUN; @@ -3007,14 +3309,25 @@ static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) { struct slcompress *vj = ppp->vj; + int cpu; memset(st, 0, sizeof(*st)); - st->p.ppp_ipackets = ppp->stats64.rx_packets; + for_each_possible_cpu(cpu) { + struct pcpu_sw_netstats *p = per_cpu_ptr(ppp->dev->tstats, cpu); + u64 rx_packets, rx_bytes, tx_packets, tx_bytes; + + rx_packets = u64_stats_read(&p->rx_packets); + rx_bytes = u64_stats_read(&p->rx_bytes); + tx_packets = u64_stats_read(&p->tx_packets); + tx_bytes = u64_stats_read(&p->tx_bytes); + + st->p.ppp_ipackets += rx_packets; + st->p.ppp_ibytes += rx_bytes; + st->p.ppp_opackets += tx_packets; + st->p.ppp_obytes += tx_bytes; + } st->p.ppp_ierrors = ppp->dev->stats.rx_errors; - st->p.ppp_ibytes = ppp->stats64.rx_bytes; - st->p.ppp_opackets = ppp->stats64.tx_packets; st->p.ppp_oerrors = ppp->dev->stats.tx_errors; - st->p.ppp_obytes = ppp->stats64.tx_bytes; if (!vj) return; st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed; @@ -3188,9 +3501,10 @@ ppp_connect_channel(struct channel *pch, int unit) ppp = ppp_find_unit(pn, unit); if (!ppp) goto out; - write_lock_bh(&pch->upl); + spin_lock(&pch->upl); ret = -EINVAL; - if (pch->ppp) + if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) || + rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) goto outl; ppp_lock(ppp); @@ -3202,21 +3516,25 @@ ppp_connect_channel(struct channel *pch, int unit) ret = -ENOTCONN; goto outl; } + if (pch->chan->direct_xmit) + ppp->dev->priv_flags |= IFF_NO_QUEUE; + else + ppp->dev->priv_flags &= ~IFF_NO_QUEUE; spin_unlock_bh(&pch->downl); if (pch->file.hdrlen > ppp->file.hdrlen) ppp->file.hdrlen = pch->file.hdrlen; hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */ if (hdrlen > ppp->dev->hard_header_len) ppp->dev->hard_header_len = hdrlen; - list_add_tail(&pch->clist, &ppp->channels); + list_add_tail_rcu(&pch->clist, &ppp->channels); ++ppp->n_channels; - pch->ppp = ppp; + rcu_assign_pointer(pch->ppp, ppp); refcount_inc(&ppp->file.refcnt); ppp_unlock(ppp); ret = 0; outl: - write_unlock_bh(&pch->upl); + spin_unlock(&pch->upl); out: mutex_unlock(&pn->all_ppp_mutex); return ret; @@ -3231,17 +3549,17 @@ ppp_disconnect_channel(struct channel *pch) struct ppp *ppp; int err = -EINVAL; - write_lock_bh(&pch->upl); - ppp = pch->ppp; - pch->ppp = NULL; - write_unlock_bh(&pch->upl); + spin_lock(&pch->upl); + ppp = rcu_replace_pointer(pch->ppp, NULL, lockdep_is_held(&pch->upl)); + spin_unlock(&pch->upl); if (ppp) { /* remove it from the ppp unit's list */ ppp_lock(ppp); - list_del(&pch->clist); + list_del_rcu(&pch->clist); if (--ppp->n_channels == 0) wake_up_interruptible(&ppp->file.rwait); ppp_unlock(ppp); + synchronize_net(); if (refcount_dec_and_test(&ppp->file.refcnt)) ppp_destroy_interface(ppp); err = 0; @@ -3254,7 +3572,7 @@ ppp_disconnect_channel(struct channel *pch) */ static void ppp_destroy_channel(struct channel *pch) { - put_net(pch->chan_net); + put_net_track(pch->chan_net, &pch->ns_tracker); pch->chan_net = NULL; atomic_dec(&channel_count); @@ -3276,8 +3594,8 @@ static void __exit ppp_cleanup(void) pr_err("PPP: removing module but units remain!\n"); rtnl_link_unregister(&ppp_link_ops); unregister_chrdev(PPP_MAJOR, "ppp"); - device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0)); - class_destroy(ppp_class); + device_destroy(&ppp_class, MKDEV(PPP_MAJOR, 0)); + class_unregister(&ppp_class); unregister_pernet_device(&ppp_net_ops); } @@ -3298,9 +3616,9 @@ static int unit_set(struct idr *p, void *ptr, int n) } /* get new free unit number and associate pointer with it */ -static int unit_get(struct idr *p, void *ptr) +static int unit_get(struct idr *p, void *ptr, int min) { - return idr_alloc(p, ptr, 0, 0, GFP_KERNEL); + return idr_alloc(p, ptr, min, 0, GFP_KERNEL); } /* put unit number back to a pool */ @@ -3331,6 +3649,7 @@ EXPORT_SYMBOL(ppp_input_error); EXPORT_SYMBOL(ppp_output_wakeup); EXPORT_SYMBOL(ppp_register_compressor); EXPORT_SYMBOL(ppp_unregister_compressor); +MODULE_DESCRIPTION("Generic PPP layer driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0); MODULE_ALIAS_RTNL_LINK("ppp"); |
