#include <linux/hash.h>
#include <linux/sort.h>
#include <linux/proc_fs.h>
+#include <linux/nsproxy.h>
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <net/dst.h>
#include <net/ip.h>
#include <net/udp.h>
struct sock *sock; /* Parent socket */
struct list_head list; /* Keep a list of all open
* prepared sockets */
+ struct net *pppol2tp_net; /* the net we belong to */
atomic_t ref_count;
};
static atomic_t pppol2tp_tunnel_count;
static atomic_t pppol2tp_session_count;
static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL };
-static struct proto_ops pppol2tp_ops;
-static LIST_HEAD(pppol2tp_tunnel_list);
-static DEFINE_RWLOCK(pppol2tp_tunnel_list_lock);
+static const struct proto_ops pppol2tp_ops;
+
+/* per-net private data for this module */
+static int pppol2tp_net_id __read_mostly;
+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.
*/
/* 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);
- list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) {
+ read_lock_bh(&pn->pppol2tp_tunnel_list_lock);
+ list_for_each_entry(tunnel, &pn->pppol2tp_tunnel_list, list) {
if (tunnel->stats.tunnel_id == tunnel_id) {
- read_unlock_bh(&pppol2tp_tunnel_list_lock);
+ read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
return tunnel;
}
}
- read_unlock_bh(&pppol2tp_tunnel_list_lock);
+ read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
return NULL;
}
* to the inner packet either
*/
secpath_reset(skb);
- dst_release(skb->dst);
- skb->dst = NULL;
+ skb_dst_drop(skb);
nf_reset(skb);
po = pppox_sk(session_sock);
return 0;
inet = inet_sk(sk);
- psum = csum_tcpudp_nofold(inet->saddr, inet->daddr, ulen,
+ psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, ulen,
IPPROTO_UDP, 0);
if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
/* Try to dequeue as many skbs from reorder_q as we can. */
pppol2tp_recv_dequeue(session);
+ sock_put(sock);
return 0;
UDP_INC_STATS_USER(&init_net, UDP_MIB_INERRORS, 0);
tunnel->stats.rx_errors++;
kfree_skb(skb);
+ sock_put(sock);
return 0;
inet = inet_sk(sk_tun);
udp_len = hdr_len + sizeof(ppph) + total_len;
uh = (struct udphdr *) skb->data;
- uh->source = inet->sport;
- uh->dest = inet->dport;
+ uh->source = inet->inet_sport;
+ uh->dest = inet->inet_dport;
uh->len = htons(udp_len);
uh->check = 0;
skb_put(skb, sizeof(struct udphdr));
/* Calculate UDP checksum if configured to do so */
if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
skb->ip_summed = CHECKSUM_NONE;
- else if (!(skb->dst->dev->features & NETIF_F_V4_CSUM)) {
+ else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
skb->ip_summed = CHECKSUM_COMPLETE;
csum = skb_checksum(skb, 0, udp_len, 0);
- uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr,
+ uh->check = csum_tcpudp_magic(inet->inet_saddr,
+ inet->inet_daddr,
udp_len, IPPROTO_UDP, csum);
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct udphdr, check);
- uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr,
+ uh->check = ~csum_tcpudp_magic(inet->inet_saddr,
+ inet->inet_daddr,
udp_len, IPPROTO_UDP, 0);
}
__skb_push(skb, sizeof(*uh));
skb_reset_transport_header(skb);
uh = udp_hdr(skb);
- uh->source = inet->sport;
- uh->dest = inet->dport;
+ uh->source = inet->inet_sport;
+ uh->dest = inet->inet_dport;
uh->len = htons(udp_len);
uh->check = 0;
nf_reset(skb);
/* Get routing info from the tunnel socket */
- dst_release(skb->dst);
- skb->dst = dst_clone(__sk_dst_get(sk_tun));
+ skb_dst_drop(skb);
+ skb_dst_set(skb, dst_clone(__sk_dst_get(sk_tun)));
pppol2tp_skb_set_owner_w(skb, sk_tun);
/* Calculate UDP checksum if configured to do so */
if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
skb->ip_summed = CHECKSUM_NONE;
- else if (!(skb->dst->dev->features & NETIF_F_V4_CSUM)) {
+ else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
+ (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) {
skb->ip_summed = CHECKSUM_COMPLETE;
csum = skb_checksum(skb, 0, udp_len, 0);
- uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr,
+ uh->check = csum_tcpudp_magic(inet->inet_saddr,
+ inet->inet_daddr,
udp_len, IPPROTO_UDP, csum);
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct udphdr, check);
- uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr,
+ uh->check = ~csum_tcpudp_magic(inet->inet_saddr,
+ inet->inet_daddr,
udp_len, IPPROTO_UDP, 0);
}
struct pppol2tp_session *session;
struct sock *sk;
- if (tunnel == NULL)
- BUG();
+ BUG_ON(tunnel == NULL);
PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: closing all sessions...\n", tunnel->name);
*/
static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel)
{
+ struct pppol2tp_net *pn = pppol2tp_pernet(tunnel->pppol2tp_net);
+
/* Remove from socket list */
- write_lock_bh(&pppol2tp_tunnel_list_lock);
+ write_lock_bh(&pn->pppol2tp_tunnel_list_lock);
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);
kfree(tunnel);
/* Internal function to prepare a tunnel (UDP) socket to have PPPoX
* sockets attached to it.
*/
-static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id,
- int *error)
+static struct sock *pppol2tp_prepare_tunnel_socket(struct net *net,
+ int fd, u16 tunnel_id, int *error)
{
int err;
struct socket *sock = NULL;
struct sock *sk;
struct pppol2tp_tunnel *tunnel;
+ struct pppol2tp_net *pn;
struct sock *ret = NULL;
/* Get the tunnel UDP socket from the fd, which was opened by
* if the tunnel socket goes away.
*/
tunnel->old_sk_destruct = sk->sk_destruct;
- sk->sk_destruct = &pppol2tp_tunnel_destruct;
+ sk->sk_destruct = pppol2tp_tunnel_destruct;
tunnel->sock = sk;
sk->sk_allocation = GFP_ATOMIC;
/* Misc init */
rwlock_init(&tunnel->hlist_lock);
+ /* The net we belong to */
+ tunnel->pppol2tp_net = net;
+ pn = pppol2tp_pernet(net);
+
/* Add tunnel to our list */
INIT_LIST_HEAD(&tunnel->list);
- write_lock_bh(&pppol2tp_tunnel_list_lock);
- list_add(&tunnel->list, &pppol2tp_tunnel_list);
- write_unlock_bh(&pppol2tp_tunnel_list_lock);
+ write_lock_bh(&pn->pppol2tp_tunnel_list_lock);
+ list_add(&tunnel->list, &pn->pppol2tp_tunnel_list);
+ write_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
atomic_inc(&pppol2tp_tunnel_count);
/* Bump the reference count. The tunnel context is deleted
* tunnel id.
*/
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,
&error);
if (tunnel_sock == NULL)
goto end;
+ sock_hold(tunnel_sock);
tunnel = tunnel_sock->sk_user_data;
} 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 = -ENOENT;
po->chan.ops = &pppol2tp_chan_ops;
po->chan.mtu = session->mtu;
- error = ppp_register_channel(&po->chan);
+ error = ppp_register_net_channel(sock_net(sk), &po->chan);
if (error)
goto end_put_tun;
* session or the special tunnel type.
*/
static int pppol2tp_setsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int optlen)
+ char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct pppol2tp_session *session = sk->sk_user_data;
#include <linux/seq_file.h>
struct pppol2tp_seq_data {
- struct pppol2tp_tunnel *tunnel; /* current tunnel */
- struct pppol2tp_session *session; /* NULL means get first session in tunnel */
+ struct seq_net_private p;
+ struct pppol2tp_tunnel *tunnel; /* current tunnel */
+ struct pppol2tp_session *session; /* NULL means get first session in tunnel */
};
static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, struct pppol2tp_session *curr)
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;
- read_lock_bh(&pppol2tp_tunnel_list_lock);
- if (list_is_last(&curr->list, &pppol2tp_tunnel_list)) {
+ read_lock_bh(&pn->pppol2tp_tunnel_list_lock);
+ if (list_is_last(&curr->list, &pn->pppol2tp_tunnel_list)) {
goto out;
}
tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list);
out:
- read_unlock_bh(&pppol2tp_tunnel_list_lock);
+ read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
return tunnel;
}
static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs)
{
struct pppol2tp_seq_data *pd = SEQ_START_TOKEN;
+ struct pppol2tp_net *pn;
loff_t pos = *offs;
if (!pos)
BUG_ON(m->private == NULL);
pd = m->private;
+ pn = pppol2tp_pernet(seq_file_net(m));
if (pd->tunnel == NULL) {
- if (!list_empty(&pppol2tp_tunnel_list))
- pd->tunnel = list_entry(pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list);
+ if (!list_empty(&pn->pppol2tp_tunnel_list))
+ pd->tunnel = list_entry(pn->pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list);
} else {
pd->session = next_session(pd->tunnel, pd->session);
if (pd->session == NULL) {
- pd->tunnel = next_tunnel(pd->tunnel);
+ pd->tunnel = next_tunnel(pn, pd->tunnel);
}
}
*/
static int pppol2tp_proc_open(struct inode *inode, struct file *file)
{
- struct seq_file *m;
- struct pppol2tp_seq_data *pd;
- int ret = 0;
-
- ret = seq_open(file, &pppol2tp_seq_ops);
- if (ret < 0)
- goto out;
-
- m = file->private_data;
-
- /* Allocate and fill our proc_data for access later */
- ret = -ENOMEM;
- m->private = kzalloc(sizeof(struct pppol2tp_seq_data), GFP_KERNEL);
- if (m->private == NULL)
- goto out;
-
- pd = m->private;
- ret = 0;
-
-out:
- return ret;
-}
-
-/* Called when /proc file access completes.
- */
-static int pppol2tp_proc_release(struct inode *inode, struct file *file)
-{
- struct seq_file *m = (struct seq_file *)file->private_data;
-
- kfree(m->private);
- m->private = NULL;
-
- return seq_release(inode, file);
+ return seq_open_net(inode, file, &pppol2tp_seq_ops,
+ sizeof(struct pppol2tp_seq_data));
}
static const struct file_operations pppol2tp_proc_fops = {
.open = pppol2tp_proc_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = pppol2tp_proc_release,
+ .release = seq_release_net,
};
-static struct proc_dir_entry *pppol2tp_proc;
-
#endif /* CONFIG_PROC_FS */
/*****************************************************************************
* Init and cleanup
*****************************************************************************/
-static struct proto_ops pppol2tp_ops = {
+static const struct proto_ops pppol2tp_ops = {
.family = AF_PPPOX,
.owner = THIS_MODULE,
.release = pppol2tp_release,
.ioctl = pppol2tp_ioctl
};
+static __net_init int pppol2tp_init_net(struct net *net)
+{
+ struct pppol2tp_net *pn = pppol2tp_pernet(net);
+ struct proc_dir_entry *pde;
+
+ INIT_LIST_HEAD(&pn->pppol2tp_tunnel_list);
+ rwlock_init(&pn->pppol2tp_tunnel_list_lock);
+
+ pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops);
+#ifdef CONFIG_PROC_FS
+ if (!pde)
+ return -ENOMEM;
+#endif
+
+ return 0;
+}
+
+static __net_exit void pppol2tp_exit_net(struct net *net)
+{
+ proc_net_remove(net, "pppol2tp");
+}
+
+static struct pernet_operations pppol2tp_net_ops = {
+ .init = pppol2tp_init_net,
+ .exit = pppol2tp_exit_net,
+ .id = &pppol2tp_net_id,
+ .size = sizeof(struct pppol2tp_net),
+};
+
static int __init pppol2tp_init(void)
{
int err;
if (err)
goto out_unregister_pppol2tp_proto;
-#ifdef CONFIG_PROC_FS
- pppol2tp_proc = proc_net_fops_create(&init_net, "pppol2tp", 0,
- &pppol2tp_proc_fops);
- if (!pppol2tp_proc) {
- err = -ENOMEM;
+ err = register_pernet_device(&pppol2tp_net_ops);
+ if (err)
goto out_unregister_pppox_proto;
- }
-#endif /* CONFIG_PROC_FS */
+
printk(KERN_INFO "PPPoL2TP kernel driver, %s\n",
PPPOL2TP_DRV_VERSION);
out:
return err;
-#ifdef CONFIG_PROC_FS
out_unregister_pppox_proto:
unregister_pppox_proto(PX_PROTO_OL2TP);
-#endif
out_unregister_pppol2tp_proto:
proto_unregister(&pppol2tp_sk_proto);
goto out;
static void __exit pppol2tp_exit(void)
{
unregister_pppox_proto(PX_PROTO_OL2TP);
-
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("pppol2tp", init_net.proc_net);
-#endif
+ unregister_pernet_device(&pppol2tp_net_ops);
proto_unregister(&pppol2tp_sk_proto);
}