net: ppp_generic - introduce net-namespace functionality v2

- Each namespace contains ppp channels and units separately
  with appropriate locks

Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Cyrill Gorcunov 2009-01-21 15:55:35 -08:00 committed by David S. Miller
parent 4e9fb8016a
commit 273ec51dd7
2 changed files with 202 additions and 77 deletions

View File

@ -49,6 +49,10 @@
#include <net/slhc_vj.h>
#include <asm/atomic.h>
#include <linux/nsproxy.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#define PPP_VERSION "2.4.2"
/*
@ -131,6 +135,7 @@ struct ppp {
struct sock_filter *active_filter;/* filter for pkts to reset idle */
unsigned pass_len, active_len;
#endif /* CONFIG_PPP_FILTER */
struct net *ppp_net; /* the net we belong to */
};
/*
@ -155,6 +160,7 @@ struct channel {
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 net *chan_net; /* the net channel belongs to */
struct list_head clist; /* link in list of channels per unit */
rwlock_t upl; /* protects `ppp' */
#ifdef CONFIG_PPP_MULTILINK
@ -173,26 +179,35 @@ struct channel {
* channel.downl.
*/
/*
* all_ppp_mutex protects the all_ppp_units mapping.
* It also ensures that finding a ppp unit in the all_ppp_units map
* and updating its file.refcnt field is atomic.
*/
static DEFINE_MUTEX(all_ppp_mutex);
static atomic_t ppp_unit_count = ATOMIC_INIT(0);
static DEFINE_IDR(ppp_units_idr);
/*
* all_channels_lock protects all_channels and last_channel_index,
* and the atomicity of find a channel and updating its file.refcnt
* field.
*/
static DEFINE_SPINLOCK(all_channels_lock);
static LIST_HEAD(all_channels);
static LIST_HEAD(new_channels);
static int last_channel_index;
static atomic_t channel_count = ATOMIC_INIT(0);
/* per-net private data for this module */
static unsigned int ppp_net_id;
struct ppp_net {
/* units to ppp mapping */
struct idr units_idr;
/*
* all_ppp_mutex protects the units_idr mapping.
* It also ensures that finding a ppp unit in the units_idr
* map and updating its file.refcnt field is atomic.
*/
struct mutex all_ppp_mutex;
/* channels */
struct list_head all_channels;
struct list_head new_channels;
int last_channel_index;
/*
* all_channels_lock protects all_channels and
* last_channel_index, and the atomicity of find
* a channel and updating its file.refcnt field.
*/
spinlock_t all_channels_lock;
};
/* Get the PPP protocol number from a skb */
#define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1])
@ -216,8 +231,8 @@ static atomic_t channel_count = ATOMIC_INIT(0);
#define seq_after(a, b) ((s32)((a) - (b)) > 0)
/* Prototypes. */
static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
unsigned int cmd, unsigned long arg);
static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
struct file *file, unsigned int cmd, unsigned long arg);
static void ppp_xmit_process(struct ppp *ppp);
static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
static void ppp_push(struct ppp *ppp);
@ -240,12 +255,12 @@ 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);
static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
static struct ppp *ppp_create_interface(int unit, int *retp);
static struct ppp *ppp_create_interface(struct net *net, int unit, int *retp);
static void init_ppp_file(struct ppp_file *pf, int kind);
static void ppp_shutdown_interface(struct ppp *ppp);
static void ppp_destroy_interface(struct ppp *ppp);
static struct ppp *ppp_find_unit(int unit);
static struct channel *ppp_find_channel(int unit);
static struct ppp *ppp_find_unit(struct ppp_net *pn, int unit);
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);
@ -256,6 +271,14 @@ static void *unit_find(struct idr *p, int n);
static struct class *ppp_class;
/* per net-namespace data */
static inline struct ppp_net *ppp_pernet(struct net *net)
{
BUG_ON(!net);
return net_generic(net, ppp_net_id);
}
/* Translates a PPP protocol number to a NP index (NP == network protocol) */
static inline int proto_to_npindex(int proto)
{
@ -544,7 +567,8 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
int __user *p = argp;
if (!pf)
return ppp_unattached_ioctl(pf, file, cmd, arg);
return ppp_unattached_ioctl(current->nsproxy->net_ns,
pf, file, cmd, arg);
if (cmd == PPPIOCDETACH) {
/*
@ -763,12 +787,13 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return err;
}
static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
unsigned int cmd, unsigned long arg)
static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
struct file *file, unsigned int cmd, unsigned long arg)
{
int unit, err = -EFAULT;
struct ppp *ppp;
struct channel *chan;
struct ppp_net *pn;
int __user *p = (int __user *)arg;
lock_kernel();
@ -777,7 +802,7 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
/* Create a new ppp unit */
if (get_user(unit, p))
break;
ppp = ppp_create_interface(unit, &err);
ppp = ppp_create_interface(net, unit, &err);
if (!ppp)
break;
file->private_data = &ppp->file;
@ -792,29 +817,31 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
/* Attach to an existing ppp unit */
if (get_user(unit, p))
break;
mutex_lock(&all_ppp_mutex);
err = -ENXIO;
ppp = ppp_find_unit(unit);
pn = ppp_pernet(net);
mutex_lock(&pn->all_ppp_mutex);
ppp = ppp_find_unit(pn, unit);
if (ppp) {
atomic_inc(&ppp->file.refcnt);
file->private_data = &ppp->file;
err = 0;
}
mutex_unlock(&all_ppp_mutex);
mutex_unlock(&pn->all_ppp_mutex);
break;
case PPPIOCATTCHAN:
if (get_user(unit, p))
break;
spin_lock_bh(&all_channels_lock);
err = -ENXIO;
chan = ppp_find_channel(unit);
pn = ppp_pernet(net);
spin_lock_bh(&pn->all_channels_lock);
chan = ppp_find_channel(pn, unit);
if (chan) {
atomic_inc(&chan->file.refcnt);
file->private_data = &chan->file;
err = 0;
}
spin_unlock_bh(&all_channels_lock);
spin_unlock_bh(&pn->all_channels_lock);
break;
default:
@ -834,6 +861,51 @@ static const struct file_operations ppp_device_fops = {
.release = ppp_release
};
static __net_init int ppp_init_net(struct net *net)
{
struct ppp_net *pn;
int err;
pn = kzalloc(sizeof(*pn), GFP_KERNEL);
if (!pn)
return -ENOMEM;
idr_init(&pn->units_idr);
mutex_init(&pn->all_ppp_mutex);
INIT_LIST_HEAD(&pn->all_channels);
INIT_LIST_HEAD(&pn->new_channels);
spin_lock_init(&pn->all_channels_lock);
err = net_assign_generic(net, ppp_net_id, pn);
if (err) {
kfree(pn);
return err;
}
return 0;
}
static __net_exit void ppp_exit_net(struct net *net)
{
struct ppp_net *pn;
pn = net_generic(net, ppp_net_id);
idr_destroy(&pn->units_idr);
/*
* if someone has cached our net then
* further net_generic call will return NULL
*/
net_assign_generic(net, ppp_net_id, NULL);
kfree(pn);
}
static __net_initdata struct pernet_operations ppp_net_ops = {
.init = ppp_init_net,
.exit = ppp_exit_net,
};
#define PPP_MAJOR 108
/* Called at boot time if ppp is compiled into the kernel,
@ -843,25 +915,36 @@ static int __init ppp_init(void)
int err;
printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
if (!err) {
ppp_class = class_create(THIS_MODULE, "ppp");
if (IS_ERR(ppp_class)) {
err = PTR_ERR(ppp_class);
goto out_chrdev;
}
device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL,
"ppp");
err = register_pernet_gen_device(&ppp_net_id, &ppp_net_ops);
if (err) {
printk(KERN_ERR "failed to register PPP pernet device (%d)\n", err);
goto out;
}
out:
if (err)
err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
if (err) {
printk(KERN_ERR "failed to register PPP device (%d)\n", err);
return err;
goto out_net;
}
ppp_class = class_create(THIS_MODULE, "ppp");
if (IS_ERR(ppp_class)) {
err = PTR_ERR(ppp_class);
goto out_chrdev;
}
/* not a big deal if we fail here :-) */
device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp");
return 0;
out_chrdev:
unregister_chrdev(PPP_MAJOR, "ppp");
goto out;
out_net:
unregister_pernet_gen_device(ppp_net_id, &ppp_net_ops);
out:
return err;
}
/*
@ -969,6 +1052,7 @@ static void ppp_setup(struct net_device *dev)
dev->tx_queue_len = 3;
dev->type = ARPHRD_PPP;
dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
dev->features |= NETIF_F_NETNS_LOCAL;
}
/*
@ -1986,19 +2070,27 @@ ppp_mp_reconstruct(struct ppp *ppp)
* Channel interface.
*/
/*
* Create a new, unattached ppp channel.
*/
int
ppp_register_channel(struct ppp_channel *chan)
/* Create a new, unattached ppp channel. */
int ppp_register_channel(struct ppp_channel *chan)
{
return ppp_register_net_channel(current->nsproxy->net_ns, chan);
}
/* Create a new, unattached ppp channel for specified net. */
int ppp_register_net_channel(struct net *net, struct ppp_channel *chan)
{
struct channel *pch;
struct ppp_net *pn;
pch = kzalloc(sizeof(struct channel), GFP_KERNEL);
if (!pch)
return -ENOMEM;
pn = ppp_pernet(net);
pch->ppp = NULL;
pch->chan = chan;
pch->chan_net = net;
chan->ppp = pch;
init_ppp_file(&pch->file, CHANNEL);
pch->file.hdrlen = chan->hdrlen;
@ -2008,11 +2100,13 @@ ppp_register_channel(struct ppp_channel *chan)
init_rwsem(&pch->chan_sem);
spin_lock_init(&pch->downl);
rwlock_init(&pch->upl);
spin_lock_bh(&all_channels_lock);
pch->file.index = ++last_channel_index;
list_add(&pch->list, &new_channels);
spin_lock_bh(&pn->all_channels_lock);
pch->file.index = ++pn->last_channel_index;
list_add(&pch->list, &pn->new_channels);
atomic_inc(&channel_count);
spin_unlock_bh(&all_channels_lock);
spin_unlock_bh(&pn->all_channels_lock);
return 0;
}
@ -2053,9 +2147,11 @@ void
ppp_unregister_channel(struct ppp_channel *chan)
{
struct channel *pch = chan->ppp;
struct ppp_net *pn;
if (!pch)
return; /* should never happen */
chan->ppp = NULL;
/*
@ -2068,9 +2164,12 @@ ppp_unregister_channel(struct ppp_channel *chan)
spin_unlock_bh(&pch->downl);
up_write(&pch->chan_sem);
ppp_disconnect_channel(pch);
spin_lock_bh(&all_channels_lock);
pn = ppp_pernet(pch->chan_net);
spin_lock_bh(&pn->all_channels_lock);
list_del(&pch->list);
spin_unlock_bh(&all_channels_lock);
spin_unlock_bh(&pn->all_channels_lock);
pch->file.dead = 1;
wake_up_interruptible(&pch->file.rwait);
if (atomic_dec_and_test(&pch->file.refcnt))
@ -2395,9 +2494,10 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
* unit == -1 means allocate a new number.
*/
static struct ppp *
ppp_create_interface(int unit, int *retp)
ppp_create_interface(struct net *net, int unit, int *retp)
{
struct ppp *ppp;
struct ppp_net *pn;
struct net_device *dev = NULL;
int ret = -ENOMEM;
int i;
@ -2406,6 +2506,8 @@ ppp_create_interface(int unit, int *retp)
if (!dev)
goto out1;
pn = ppp_pernet(net);
ppp = netdev_priv(dev);
ppp->dev = dev;
ppp->mru = PPP_MRU;
@ -2421,17 +2523,23 @@ ppp_create_interface(int unit, int *retp)
skb_queue_head_init(&ppp->mrq);
#endif /* CONFIG_PPP_MULTILINK */
/*
* drum roll: don't forget to set
* the net device is belong to
*/
dev_net_set(dev, net);
ret = -EEXIST;
mutex_lock(&all_ppp_mutex);
mutex_lock(&pn->all_ppp_mutex);
if (unit < 0) {
unit = unit_get(&ppp_units_idr, ppp);
unit = unit_get(&pn->units_idr, ppp);
if (unit < 0) {
*retp = unit;
goto out2;
}
} else {
if (unit_find(&ppp_units_idr, unit))
if (unit_find(&pn->units_idr, unit))
goto out2; /* unit already exists */
/*
* if caller need a specified unit number
@ -2442,7 +2550,7 @@ ppp_create_interface(int unit, int *retp)
* fair but at least pppd will ask us to allocate
* new unit in this case so user is happy :)
*/
unit = unit_set(&ppp_units_idr, ppp, unit);
unit = unit_set(&pn->units_idr, ppp, unit);
if (unit < 0)
goto out2;
}
@ -2453,20 +2561,22 @@ ppp_create_interface(int unit, int *retp)
ret = register_netdev(dev);
if (ret != 0) {
unit_put(&ppp_units_idr, unit);
unit_put(&pn->units_idr, unit);
printk(KERN_ERR "PPP: couldn't register device %s (%d)\n",
dev->name, ret);
goto out2;
}
ppp->ppp_net = net;
atomic_inc(&ppp_unit_count);
mutex_unlock(&all_ppp_mutex);
mutex_unlock(&pn->all_ppp_mutex);
*retp = 0;
return ppp;
out2:
mutex_unlock(&all_ppp_mutex);
mutex_unlock(&pn->all_ppp_mutex);
free_netdev(dev);
out1:
*retp = ret;
@ -2492,7 +2602,11 @@ init_ppp_file(struct ppp_file *pf, int kind)
*/
static void ppp_shutdown_interface(struct ppp *ppp)
{
mutex_lock(&all_ppp_mutex);
struct ppp_net *pn;
pn = ppp_pernet(ppp->ppp_net);
mutex_lock(&pn->all_ppp_mutex);
/* This will call dev_close() for us. */
ppp_lock(ppp);
if (!ppp->closing) {
@ -2502,11 +2616,12 @@ static void ppp_shutdown_interface(struct ppp *ppp)
} else
ppp_unlock(ppp);
unit_put(&ppp_units_idr, ppp->file.index);
unit_put(&pn->units_idr, ppp->file.index);
ppp->file.dead = 1;
ppp->owner = NULL;
wake_up_interruptible(&ppp->file.rwait);
mutex_unlock(&all_ppp_mutex);
mutex_unlock(&pn->all_ppp_mutex);
}
/*
@ -2554,9 +2669,9 @@ static void ppp_destroy_interface(struct ppp *ppp)
* The caller should have locked the all_ppp_mutex.
*/
static struct ppp *
ppp_find_unit(int unit)
ppp_find_unit(struct ppp_net *pn, int unit)
{
return unit_find(&ppp_units_idr, unit);
return unit_find(&pn->units_idr, unit);
}
/*
@ -2568,20 +2683,22 @@ ppp_find_unit(int unit)
* when we have a lot of channels in use.
*/
static struct channel *
ppp_find_channel(int unit)
ppp_find_channel(struct ppp_net *pn, int unit)
{
struct channel *pch;
list_for_each_entry(pch, &new_channels, list) {
list_for_each_entry(pch, &pn->new_channels, list) {
if (pch->file.index == unit) {
list_move(&pch->list, &all_channels);
list_move(&pch->list, &pn->all_channels);
return pch;
}
}
list_for_each_entry(pch, &all_channels, list) {
list_for_each_entry(pch, &pn->all_channels, list) {
if (pch->file.index == unit)
return pch;
}
return NULL;
}
@ -2592,11 +2709,14 @@ static int
ppp_connect_channel(struct channel *pch, int unit)
{
struct ppp *ppp;
struct ppp_net *pn;
int ret = -ENXIO;
int hdrlen;
mutex_lock(&all_ppp_mutex);
ppp = ppp_find_unit(unit);
pn = ppp_pernet(pch->chan_net);
mutex_lock(&pn->all_ppp_mutex);
ppp = ppp_find_unit(pn, unit);
if (!ppp)
goto out;
write_lock_bh(&pch->upl);
@ -2620,7 +2740,7 @@ ppp_connect_channel(struct channel *pch, int unit)
outl:
write_unlock_bh(&pch->upl);
out:
mutex_unlock(&all_ppp_mutex);
mutex_unlock(&pn->all_ppp_mutex);
return ret;
}
@ -2677,7 +2797,7 @@ static void __exit ppp_cleanup(void)
unregister_chrdev(PPP_MAJOR, "ppp");
device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
class_destroy(ppp_class);
idr_destroy(&ppp_units_idr);
unregister_pernet_gen_device(ppp_net_id, &ppp_net_ops);
}
/*
@ -2743,6 +2863,7 @@ static void *unit_find(struct idr *p, int n)
module_init(ppp_init);
module_exit(ppp_cleanup);
EXPORT_SYMBOL(ppp_register_net_channel);
EXPORT_SYMBOL(ppp_register_channel);
EXPORT_SYMBOL(ppp_unregister_channel);
EXPORT_SYMBOL(ppp_channel_index);

View File

@ -22,6 +22,7 @@
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/poll.h>
#include <net/net_namespace.h>
struct ppp_channel;
@ -56,6 +57,9 @@ extern void ppp_input(struct ppp_channel *, struct sk_buff *);
that we may have missed a packet. */
extern void ppp_input_error(struct ppp_channel *, int code);
/* Attach a channel to a given PPP unit in specified net. */
extern int ppp_register_net_channel(struct net *, struct ppp_channel *);
/* Attach a channel to a given PPP unit. */
extern int ppp_register_channel(struct ppp_channel *);