Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 273ec51d authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by David S. Miller
Browse files

net: ppp_generic - introduce net-namespace functionality v2



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

Signed-off-by: default avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4e9fb801
Loading
Loading
Loading
Loading
+198 −77
Original line number Diff line number Diff line
@@ -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,25 +179,34 @@ struct channel {
 * channel.downl.
 */

static atomic_t ppp_unit_count = ATOMIC_INIT(0);
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 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.
	 * 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.
	 */
static DEFINE_MUTEX(all_ppp_mutex);
static atomic_t ppp_unit_count = ATOMIC_INIT(0);
static DEFINE_IDR(ppp_units_idr);
	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.
	 * 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);
	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_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;
	}

	err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
	if (!err) {
	if (err) {
		printk(KERN_ERR "failed to register PPP device (%d)\n", err);
		goto out_net;
	}

	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");
	}

out:
	if (err)
		printk(KERN_ERR "failed to register PPP device (%d)\n", err);
	return err;
	/* 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);
+4 −0
Original line number Diff line number Diff line
@@ -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 *);