Commit 4e9fb801 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by David S. Miller

net: pppol2tp - introduce net-namespace functionality

- Each tunnel and appropriate lock are inside own namespace now.
- pppox code allows to create per-namespace sockets for
  both PX_PROTO_OE and PX_PROTO_OL2TP protocols. Actually since
  now pppox_create support net-namespaces new PPPo... protocols
  (if they ever will be) should support net-namespace too otherwise
  explicit check for &init_net would be needed.
Signed-off-by: default avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: default avatarJames Chapman <jchapman@katalix.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a6bcf1c1
...@@ -90,7 +90,9 @@ ...@@ -90,7 +90,9 @@
#include <linux/hash.h> #include <linux/hash.h>
#include <linux/sort.h> #include <linux/sort.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/nsproxy.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/dst.h> #include <net/dst.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/udp.h> #include <net/udp.h>
...@@ -204,6 +206,7 @@ struct pppol2tp_tunnel ...@@ -204,6 +206,7 @@ struct pppol2tp_tunnel
struct sock *sock; /* Parent socket */ struct sock *sock; /* Parent socket */
struct list_head list; /* Keep a list of all open struct list_head list; /* Keep a list of all open
* prepared sockets */ * prepared sockets */
struct net *pppol2tp_net; /* the net we belong to */
atomic_t ref_count; atomic_t ref_count;
}; };
...@@ -227,8 +230,20 @@ static atomic_t pppol2tp_tunnel_count; ...@@ -227,8 +230,20 @@ static atomic_t pppol2tp_tunnel_count;
static atomic_t pppol2tp_session_count; static atomic_t pppol2tp_session_count;
static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL }; static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL };
static struct proto_ops pppol2tp_ops; static struct proto_ops pppol2tp_ops;
static LIST_HEAD(pppol2tp_tunnel_list);
static DEFINE_RWLOCK(pppol2tp_tunnel_list_lock); /* per-net private data for this module */
static unsigned int pppol2tp_net_id;
struct pppol2tp_net {
struct list_head pppol2tp_tunnel_list;
rwlock_t pppol2tp_tunnel_list_lock;
};
static inline struct pppol2tp_net *pppol2tp_pernet(struct net *net)
{
BUG_ON(!net);
return net_generic(net, pppol2tp_net_id);
}
/* Helpers to obtain tunnel/session contexts from sockets. /* Helpers to obtain tunnel/session contexts from sockets.
*/ */
...@@ -321,18 +336,19 @@ pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id) ...@@ -321,18 +336,19 @@ pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id)
/* Lookup a tunnel by id /* Lookup a tunnel by id
*/ */
static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id) static struct pppol2tp_tunnel *pppol2tp_tunnel_find(struct net *net, u16 tunnel_id)
{ {
struct pppol2tp_tunnel *tunnel = NULL; struct pppol2tp_tunnel *tunnel;
struct pppol2tp_net *pn = pppol2tp_pernet(net);
read_lock_bh(&pppol2tp_tunnel_list_lock); read_lock_bh(&pn->pppol2tp_tunnel_list_lock);
list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) { list_for_each_entry(tunnel, &pn->pppol2tp_tunnel_list, list) {
if (tunnel->stats.tunnel_id == tunnel_id) { if (tunnel->stats.tunnel_id == tunnel_id) {
read_unlock_bh(&pppol2tp_tunnel_list_lock); read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
return tunnel; return tunnel;
} }
} }
read_unlock_bh(&pppol2tp_tunnel_list_lock); read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
return NULL; return NULL;
} }
...@@ -1287,10 +1303,12 @@ static void pppol2tp_tunnel_closeall(struct pppol2tp_tunnel *tunnel) ...@@ -1287,10 +1303,12 @@ static void pppol2tp_tunnel_closeall(struct pppol2tp_tunnel *tunnel)
*/ */
static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel) static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel)
{ {
struct pppol2tp_net *pn = pppol2tp_pernet(tunnel->pppol2tp_net);
/* Remove from socket list */ /* Remove from socket list */
write_lock_bh(&pppol2tp_tunnel_list_lock); write_lock_bh(&pn->pppol2tp_tunnel_list_lock);
list_del_init(&tunnel->list); list_del_init(&tunnel->list);
write_unlock_bh(&pppol2tp_tunnel_list_lock); write_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
atomic_dec(&pppol2tp_tunnel_count); atomic_dec(&pppol2tp_tunnel_count);
kfree(tunnel); kfree(tunnel);
...@@ -1444,13 +1462,14 @@ static int pppol2tp_release(struct socket *sock) ...@@ -1444,13 +1462,14 @@ static int pppol2tp_release(struct socket *sock)
/* Internal function to prepare a tunnel (UDP) socket to have PPPoX /* Internal function to prepare a tunnel (UDP) socket to have PPPoX
* sockets attached to it. * sockets attached to it.
*/ */
static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id, static struct sock *pppol2tp_prepare_tunnel_socket(struct net *net,
int *error) int fd, u16 tunnel_id, int *error)
{ {
int err; int err;
struct socket *sock = NULL; struct socket *sock = NULL;
struct sock *sk; struct sock *sk;
struct pppol2tp_tunnel *tunnel; struct pppol2tp_tunnel *tunnel;
struct pppol2tp_net *pn;
struct sock *ret = NULL; struct sock *ret = NULL;
/* Get the tunnel UDP socket from the fd, which was opened by /* Get the tunnel UDP socket from the fd, which was opened by
...@@ -1524,11 +1543,15 @@ static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id, ...@@ -1524,11 +1543,15 @@ static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id,
/* Misc init */ /* Misc init */
rwlock_init(&tunnel->hlist_lock); rwlock_init(&tunnel->hlist_lock);
/* The net we belong to */
tunnel->pppol2tp_net = net;
pn = pppol2tp_pernet(net);
/* Add tunnel to our list */ /* Add tunnel to our list */
INIT_LIST_HEAD(&tunnel->list); INIT_LIST_HEAD(&tunnel->list);
write_lock_bh(&pppol2tp_tunnel_list_lock); write_lock_bh(&pn->pppol2tp_tunnel_list_lock);
list_add(&tunnel->list, &pppol2tp_tunnel_list); list_add(&tunnel->list, &pn->pppol2tp_tunnel_list);
write_unlock_bh(&pppol2tp_tunnel_list_lock); write_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
atomic_inc(&pppol2tp_tunnel_count); atomic_inc(&pppol2tp_tunnel_count);
/* Bump the reference count. The tunnel context is deleted /* Bump the reference count. The tunnel context is deleted
...@@ -1629,7 +1652,8 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, ...@@ -1629,7 +1652,8 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
* tunnel id. * tunnel id.
*/ */
if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) { if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) {
tunnel_sock = pppol2tp_prepare_tunnel_socket(sp->pppol2tp.fd, tunnel_sock = pppol2tp_prepare_tunnel_socket(sock_net(sk),
sp->pppol2tp.fd,
sp->pppol2tp.s_tunnel, sp->pppol2tp.s_tunnel,
&error); &error);
if (tunnel_sock == NULL) if (tunnel_sock == NULL)
...@@ -1637,7 +1661,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, ...@@ -1637,7 +1661,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
tunnel = tunnel_sock->sk_user_data; tunnel = tunnel_sock->sk_user_data;
} else { } else {
tunnel = pppol2tp_tunnel_find(sp->pppol2tp.s_tunnel); tunnel = pppol2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel);
/* Error if we can't find the tunnel */ /* Error if we can't find the tunnel */
error = -ENOENT; error = -ENOENT;
...@@ -2347,6 +2371,7 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, ...@@ -2347,6 +2371,7 @@ static int pppol2tp_getsockopt(struct socket *sock, int level,
#include <linux/seq_file.h> #include <linux/seq_file.h>
struct pppol2tp_seq_data { struct pppol2tp_seq_data {
struct net *seq_net; /* net of inode */
struct pppol2tp_tunnel *tunnel; /* current tunnel */ struct pppol2tp_tunnel *tunnel; /* current tunnel */
struct pppol2tp_session *session; /* NULL means get first session in tunnel */ struct pppol2tp_session *session; /* NULL means get first session in tunnel */
}; };
...@@ -2384,17 +2409,18 @@ static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, str ...@@ -2384,17 +2409,18 @@ static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, str
return session; return session;
} }
static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_tunnel *curr) static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_net *pn,
struct pppol2tp_tunnel *curr)
{ {
struct pppol2tp_tunnel *tunnel = NULL; struct pppol2tp_tunnel *tunnel = NULL;
read_lock_bh(&pppol2tp_tunnel_list_lock); read_lock_bh(&pn->pppol2tp_tunnel_list_lock);
if (list_is_last(&curr->list, &pppol2tp_tunnel_list)) { if (list_is_last(&curr->list, &pn->pppol2tp_tunnel_list)) {
goto out; goto out;
} }
tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list); tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list);
out: out:
read_unlock_bh(&pppol2tp_tunnel_list_lock); read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
return tunnel; return tunnel;
} }
...@@ -2402,6 +2428,7 @@ static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_tunnel *curr) ...@@ -2402,6 +2428,7 @@ static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_tunnel *curr)
static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs) static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs)
{ {
struct pppol2tp_seq_data *pd = SEQ_START_TOKEN; struct pppol2tp_seq_data *pd = SEQ_START_TOKEN;
struct pppol2tp_net *pn;
loff_t pos = *offs; loff_t pos = *offs;
if (!pos) if (!pos)
...@@ -2409,14 +2436,15 @@ static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs) ...@@ -2409,14 +2436,15 @@ static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs)
BUG_ON(m->private == NULL); BUG_ON(m->private == NULL);
pd = m->private; pd = m->private;
pn = pppol2tp_pernet(pd->seq_net);
if (pd->tunnel == NULL) { if (pd->tunnel == NULL) {
if (!list_empty(&pppol2tp_tunnel_list)) if (!list_empty(&pn->pppol2tp_tunnel_list))
pd->tunnel = list_entry(pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list); pd->tunnel = list_entry(pn->pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list);
} else { } else {
pd->session = next_session(pd->tunnel, pd->session); pd->session = next_session(pd->tunnel, pd->session);
if (pd->session == NULL) { if (pd->session == NULL) {
pd->tunnel = next_tunnel(pd->tunnel); pd->tunnel = next_tunnel(pn, pd->tunnel);
} }
} }
...@@ -2532,6 +2560,7 @@ static int pppol2tp_proc_open(struct inode *inode, struct file *file) ...@@ -2532,6 +2560,7 @@ static int pppol2tp_proc_open(struct inode *inode, struct file *file)
{ {
struct seq_file *m; struct seq_file *m;
struct pppol2tp_seq_data *pd; struct pppol2tp_seq_data *pd;
struct net *net;
int ret = 0; int ret = 0;
ret = seq_open(file, &pppol2tp_seq_ops); ret = seq_open(file, &pppol2tp_seq_ops);
...@@ -2542,12 +2571,15 @@ static int pppol2tp_proc_open(struct inode *inode, struct file *file) ...@@ -2542,12 +2571,15 @@ static int pppol2tp_proc_open(struct inode *inode, struct file *file)
/* Allocate and fill our proc_data for access later */ /* Allocate and fill our proc_data for access later */
ret = -ENOMEM; ret = -ENOMEM;
m->private = kzalloc(sizeof(struct pppol2tp_seq_data), GFP_KERNEL); m->private = kzalloc(sizeof(*pd), GFP_KERNEL);
if (m->private == NULL) if (m->private == NULL)
goto out; goto out;
pd = m->private; pd = m->private;
ret = 0; net = maybe_get_net(PDE_NET(PDE(inode)));
BUG_ON(!net);
pd->seq_net = net;
return 0;
out: out:
return ret; return ret;
...@@ -2558,6 +2590,9 @@ static int pppol2tp_proc_open(struct inode *inode, struct file *file) ...@@ -2558,6 +2590,9 @@ static int pppol2tp_proc_open(struct inode *inode, struct file *file)
static int pppol2tp_proc_release(struct inode *inode, struct file *file) static int pppol2tp_proc_release(struct inode *inode, struct file *file)
{ {
struct seq_file *m = (struct seq_file *)file->private_data; struct seq_file *m = (struct seq_file *)file->private_data;
struct pppol2tp_seq_data *pd = m->private;
put_net(pd->seq_net);
kfree(m->private); kfree(m->private);
m->private = NULL; m->private = NULL;
...@@ -2573,8 +2608,6 @@ static const struct file_operations pppol2tp_proc_fops = { ...@@ -2573,8 +2608,6 @@ static const struct file_operations pppol2tp_proc_fops = {
.release = pppol2tp_proc_release, .release = pppol2tp_proc_release,
}; };
static struct proc_dir_entry *pppol2tp_proc;
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
/***************************************************************************** /*****************************************************************************
...@@ -2606,6 +2639,57 @@ static struct pppox_proto pppol2tp_proto = { ...@@ -2606,6 +2639,57 @@ static struct pppox_proto pppol2tp_proto = {
.ioctl = pppol2tp_ioctl .ioctl = pppol2tp_ioctl
}; };
static __net_init int pppol2tp_init_net(struct net *net)
{
struct pppol2tp_net *pn;
struct proc_dir_entry *pde;
int err;
pn = kzalloc(sizeof(*pn), GFP_KERNEL);
if (!pn)
return -ENOMEM;
INIT_LIST_HEAD(&pn->pppol2tp_tunnel_list);
rwlock_init(&pn->pppol2tp_tunnel_list_lock);
err = net_assign_generic(net, pppol2tp_net_id, pn);
if (err)
goto out;
pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops);
#ifdef CONFIG_PROC_FS
if (!pde) {
err = -ENOMEM;
goto out;
}
#endif
return 0;
out:
kfree(pn);
return err;
}
static __net_exit void pppol2tp_exit_net(struct net *net)
{
struct pppoe_net *pn;
proc_net_remove(net, "pppol2tp");
pn = net_generic(net, pppol2tp_net_id);
/*
* if someone has cached our net then
* further net_generic call will return NULL
*/
net_assign_generic(net, pppol2tp_net_id, NULL);
kfree(pn);
}
static __net_initdata struct pernet_operations pppol2tp_net_ops = {
.init = pppol2tp_init_net,
.exit = pppol2tp_exit_net,
};
static int __init pppol2tp_init(void) static int __init pppol2tp_init(void)
{ {
int err; int err;
...@@ -2617,23 +2701,17 @@ static int __init pppol2tp_init(void) ...@@ -2617,23 +2701,17 @@ static int __init pppol2tp_init(void)
if (err) if (err)
goto out_unregister_pppol2tp_proto; goto out_unregister_pppol2tp_proto;
#ifdef CONFIG_PROC_FS err = register_pernet_gen_device(&pppol2tp_net_id, &pppol2tp_net_ops);
pppol2tp_proc = proc_net_fops_create(&init_net, "pppol2tp", 0, if (err)
&pppol2tp_proc_fops);
if (!pppol2tp_proc) {
err = -ENOMEM;
goto out_unregister_pppox_proto; goto out_unregister_pppox_proto;
}
#endif /* CONFIG_PROC_FS */
printk(KERN_INFO "PPPoL2TP kernel driver, %s\n", printk(KERN_INFO "PPPoL2TP kernel driver, %s\n",
PPPOL2TP_DRV_VERSION); PPPOL2TP_DRV_VERSION);
out: out:
return err; return err;
#ifdef CONFIG_PROC_FS
out_unregister_pppox_proto: out_unregister_pppox_proto:
unregister_pppox_proto(PX_PROTO_OL2TP); unregister_pppox_proto(PX_PROTO_OL2TP);
#endif
out_unregister_pppol2tp_proto: out_unregister_pppol2tp_proto:
proto_unregister(&pppol2tp_sk_proto); proto_unregister(&pppol2tp_sk_proto);
goto out; goto out;
...@@ -2642,10 +2720,6 @@ static int __init pppol2tp_init(void) ...@@ -2642,10 +2720,6 @@ static int __init pppol2tp_init(void)
static void __exit pppol2tp_exit(void) static void __exit pppol2tp_exit(void)
{ {
unregister_pppox_proto(PX_PROTO_OL2TP); unregister_pppox_proto(PX_PROTO_OL2TP);
#ifdef CONFIG_PROC_FS
remove_proc_entry("pppol2tp", init_net.proc_net);
#endif
proto_unregister(&pppol2tp_sk_proto); proto_unregister(&pppol2tp_sk_proto);
} }
......
...@@ -111,10 +111,6 @@ static int pppox_create(struct net *net, struct socket *sock, int protocol) ...@@ -111,10 +111,6 @@ static int pppox_create(struct net *net, struct socket *sock, int protocol)
if (protocol < 0 || protocol > PX_MAX_PROTO) if (protocol < 0 || protocol > PX_MAX_PROTO)
goto out; goto out;
/* we support net-namespaces for PPPoE only (yet) */
if (protocol != PX_PROTO_OE && net != &init_net)
return -EAFNOSUPPORT;
rc = -EPROTONOSUPPORT; rc = -EPROTONOSUPPORT;
if (!pppox_protos[protocol]) if (!pppox_protos[protocol])
request_module("pppox-proto-%d", protocol); request_module("pppox-proto-%d", protocol);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment