netns PF_KEY: part 1
[safe/jmp/linux-2.6] / net / key / af_key.c
index f202ba6..e80b264 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/proc_fs.h>
 #include <linux/init.h>
 #include <net/net_namespace.h>
+#include <net/netns/generic.h>
 #include <net/xfrm.h>
 
 #include <net/sock.h>
 #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))
 #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))
 
-
-/* List of all pfkey sockets. */
-static HLIST_HEAD(pfkey_table);
+static int pfkey_net_id;
+struct netns_pfkey {
+       /* List of all pfkey sockets. */
+       struct hlist_head table;
+       atomic_t socks_nr;
+};
 static DECLARE_WAIT_QUEUE_HEAD(pfkey_table_wait);
 static DEFINE_RWLOCK(pfkey_table_lock);
 static atomic_t pfkey_table_users = ATOMIC_INIT(0);
 
-static atomic_t pfkey_socks_nr = ATOMIC_INIT(0);
-
 struct pfkey_sock {
        /* struct sock must be the first member of struct pfkey_sock */
        struct sock     sk;
@@ -89,6 +91,9 @@ static void pfkey_terminate_dump(struct pfkey_sock *pfk)
 
 static void pfkey_sock_destruct(struct sock *sk)
 {
+       struct net *net = sock_net(sk);
+       struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
        pfkey_terminate_dump(pfkey_sk(sk));
        skb_queue_purge(&sk->sk_receive_queue);
 
@@ -100,7 +105,7 @@ static void pfkey_sock_destruct(struct sock *sk)
        WARN_ON(atomic_read(&sk->sk_rmem_alloc));
        WARN_ON(atomic_read(&sk->sk_wmem_alloc));
 
-       atomic_dec(&pfkey_socks_nr);
+       atomic_dec(&net_pfkey->socks_nr);
 }
 
 static void pfkey_table_grab(void)
@@ -151,8 +156,11 @@ static const struct proto_ops pfkey_ops;
 
 static void pfkey_insert(struct sock *sk)
 {
+       struct net *net = sock_net(sk);
+       struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
        pfkey_table_grab();
-       sk_add_node(sk, &pfkey_table);
+       sk_add_node(sk, &net_pfkey->table);
        pfkey_table_ungrab();
 }
 
@@ -171,12 +179,10 @@ static struct proto key_proto = {
 
 static int pfkey_create(struct net *net, struct socket *sock, int protocol)
 {
+       struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
        struct sock *sk;
        int err;
 
-       if (net != &init_net)
-               return -EAFNOSUPPORT;
-
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
        if (sock->type != SOCK_RAW)
@@ -195,7 +201,7 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol)
        sk->sk_family = PF_KEY;
        sk->sk_destruct = pfkey_sock_destruct;
 
-       atomic_inc(&pfkey_socks_nr);
+       atomic_inc(&net_pfkey->socks_nr);
 
        pfkey_insert(sk);
 
@@ -257,6 +263,8 @@ static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
 static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
                           int broadcast_flags, struct sock *one_sk)
 {
+       struct net *net = &init_net;
+       struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
        struct sock *sk;
        struct hlist_node *node;
        struct sk_buff *skb2 = NULL;
@@ -269,7 +277,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
                return -ENOMEM;
 
        pfkey_lock_table();
-       sk_for_each(sk, node, &pfkey_table) {
+       sk_for_each(sk, node, &net_pfkey->table) {
                struct pfkey_sock *pfk = pfkey_sk(sk);
                int err2;
 
@@ -683,7 +691,7 @@ static struct  xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **
        if (!xaddr)
                return NULL;
 
-       return xfrm_state_lookup(xaddr, sa->sadb_sa_spi, proto, family);
+       return xfrm_state_lookup(&init_net, xaddr, sa->sadb_sa_spi, proto, family);
 }
 
 #define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
@@ -1348,7 +1356,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
        }
 
        if (hdr->sadb_msg_seq) {
-               x = xfrm_find_acq_byseq(hdr->sadb_msg_seq);
+               x = xfrm_find_acq_byseq(&init_net, hdr->sadb_msg_seq);
                if (x && xfrm_addr_cmp(&x->id.daddr, xdaddr, family)) {
                        xfrm_state_put(x);
                        x = NULL;
@@ -1356,7 +1364,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
        }
 
        if (!x)
-               x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family);
+               x = xfrm_find_acq(&init_net, mode, reqid, proto, xdaddr, xsaddr, 1, family);
 
        if (x == NULL)
                return -ENOENT;
@@ -1404,7 +1412,7 @@ static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, struct sadb_msg *
        if (hdr->sadb_msg_seq == 0 || hdr->sadb_msg_errno == 0)
                return 0;
 
-       x = xfrm_find_acq_byseq(hdr->sadb_msg_seq);
+       x = xfrm_find_acq_byseq(&init_net, hdr->sadb_msg_seq);
        if (x == NULL)
                return 0;
 
@@ -1732,13 +1740,14 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hd
        audit_info.loginuid = audit_get_loginuid(current);
        audit_info.sessionid = audit_get_sessionid(current);
        audit_info.secid = 0;
-       err = xfrm_state_flush(proto, &audit_info);
+       err = xfrm_state_flush(&init_net, proto, &audit_info);
        if (err)
                return err;
        c.data.proto = proto;
        c.seq = hdr->sadb_msg_seq;
        c.pid = hdr->sadb_msg_pid;
        c.event = XFRM_MSG_FLUSHSA;
+       c.net = &init_net;
        km_state_notify(NULL, &c);
 
        return 0;
@@ -1776,7 +1785,7 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr)
 
 static int pfkey_dump_sa(struct pfkey_sock *pfk)
 {
-       return xfrm_state_walk(&pfk->dump.u.state, dump_sa, (void *) pfk);
+       return xfrm_state_walk(&init_net, &pfk->dump.u.state, dump_sa, (void *) pfk);
 }
 
 static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
@@ -1846,7 +1855,7 @@ static u32 gen_reqid(void)
                if (reqid == 0)
                        reqid = IPSEC_MANUAL_REQID_MAX+1;
                xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
-               rc = xfrm_policy_walk(&walk, check_reqid, (void*)&reqid);
+               rc = xfrm_policy_walk(&init_net, &walk, check_reqid, (void*)&reqid);
                xfrm_policy_walk_done(&walk);
                if (rc != -EEXIST)
                        return reqid;
@@ -2174,7 +2183,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
        if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
                return -EINVAL;
 
-       xp = xfrm_policy_alloc(GFP_KERNEL);
+       xp = xfrm_policy_alloc(&init_net, GFP_KERNEL);
        if (xp == NULL)
                return -ENOBUFS;
 
@@ -2324,7 +2333,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
                        return err;
        }
 
-       xp = xfrm_policy_bysel_ctx(XFRM_POLICY_TYPE_MAIN,
+       xp = xfrm_policy_bysel_ctx(&init_net, XFRM_POLICY_TYPE_MAIN,
                                   pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
                                   1, &err);
        security_xfrm_policy_free(pol_ctx);
@@ -2571,8 +2580,8 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
                return -EINVAL;
 
        delete = (hdr->sadb_msg_type == SADB_X_SPDDELETE2);
-       xp = xfrm_policy_byid(XFRM_POLICY_TYPE_MAIN, dir, pol->sadb_x_policy_id,
-                             delete, &err);
+       xp = xfrm_policy_byid(&init_net, XFRM_POLICY_TYPE_MAIN, dir,
+                             pol->sadb_x_policy_id, delete, &err);
        if (xp == NULL)
                return -ENOENT;
 
@@ -2633,7 +2642,7 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
 
 static int pfkey_dump_sp(struct pfkey_sock *pfk)
 {
-       return xfrm_policy_walk(&pfk->dump.u.policy, dump_sp, (void *) pfk);
+       return xfrm_policy_walk(&init_net, &pfk->dump.u.policy, dump_sp, (void *) pfk);
 }
 
 static void pfkey_dump_sp_done(struct pfkey_sock *pfk)
@@ -2686,13 +2695,14 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg
        audit_info.loginuid = audit_get_loginuid(current);
        audit_info.sessionid = audit_get_sessionid(current);
        audit_info.secid = 0;
-       err = xfrm_policy_flush(XFRM_POLICY_TYPE_MAIN, &audit_info);
+       err = xfrm_policy_flush(&init_net, XFRM_POLICY_TYPE_MAIN, &audit_info);
        if (err)
                return err;
        c.data.type = XFRM_POLICY_TYPE_MAIN;
        c.event = XFRM_MSG_FLUSHPOLICY;
        c.pid = hdr->sadb_msg_pid;
        c.seq = hdr->sadb_msg_seq;
+       c.net = &init_net;
        km_policy_notify(NULL, 0, &c);
 
        return 0;
@@ -2941,7 +2951,10 @@ static int key_notify_sa_expire(struct xfrm_state *x, struct km_event *c)
 
 static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c)
 {
-       if (atomic_read(&pfkey_socks_nr) == 0)
+       struct net *net = &init_net;
+       struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
+       if (atomic_read(&net_pfkey->socks_nr) == 0)
                return 0;
 
        switch (c->event) {
@@ -3141,7 +3154,7 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
            (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
                return NULL;
 
-       xp = xfrm_policy_alloc(GFP_ATOMIC);
+       xp = xfrm_policy_alloc(&init_net, GFP_ATOMIC);
        if (xp == NULL) {
                *dir = -ENOBUFS;
                return NULL;
@@ -3645,6 +3658,8 @@ static int pfkey_seq_show(struct seq_file *f, void *v)
 
 static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
 {
+       struct net *net = &init_net;
+       struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
        struct sock *s;
        struct hlist_node *node;
        loff_t pos = *ppos;
@@ -3653,7 +3668,7 @@ static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
        if (pos == 0)
                return SEQ_START_TOKEN;
 
-       sk_for_each(s, node, &pfkey_table)
+       sk_for_each(s, node, &net_pfkey->table)
                if (pos-- == 1)
                        return s;
 
@@ -3662,9 +3677,12 @@ static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
 
 static void *pfkey_seq_next(struct seq_file *f, void *v, loff_t *ppos)
 {
+       struct net *net = &init_net;
+       struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
        ++*ppos;
        return (v == SEQ_START_TOKEN) ?
-               sk_head(&pfkey_table) :
+               sk_head(&net_pfkey->table) :
                        sk_next((struct sock *)v);
 }
 
@@ -3729,8 +3747,45 @@ static struct xfrm_mgr pfkeyv2_mgr =
        .migrate        = pfkey_send_migrate,
 };
 
+static int __net_init pfkey_net_init(struct net *net)
+{
+       struct netns_pfkey *net_pfkey;
+       int rv;
+
+       net_pfkey = kmalloc(sizeof(struct netns_pfkey), GFP_KERNEL);
+       if (!net_pfkey) {
+               rv = -ENOMEM;
+               goto out_kmalloc;
+       }
+       INIT_HLIST_HEAD(&net_pfkey->table);
+       atomic_set(&net_pfkey->socks_nr, 0);
+       rv = net_assign_generic(net, pfkey_net_id, net_pfkey);
+       if (rv < 0)
+               goto out_assign;
+       return 0;
+
+out_assign:
+       kfree(net_pfkey);
+out_kmalloc:
+       return rv;
+}
+
+static void __net_exit pfkey_net_exit(struct net *net)
+{
+       struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
+       BUG_ON(!hlist_empty(&net_pfkey->table));
+       kfree(net_pfkey);
+}
+
+static struct pernet_operations pfkey_net_ops = {
+       .init = pfkey_net_init,
+       .exit = pfkey_net_exit,
+};
+
 static void __exit ipsec_pfkey_exit(void)
 {
+       unregister_pernet_gen_subsys(pfkey_net_id, &pfkey_net_ops);
        xfrm_unregister_km(&pfkeyv2_mgr);
        pfkey_exit_proc();
        sock_unregister(PF_KEY);
@@ -3753,8 +3808,13 @@ static int __init ipsec_pfkey_init(void)
        err = xfrm_register_km(&pfkeyv2_mgr);
        if (err != 0)
                goto out_remove_proc_entry;
+       err = register_pernet_gen_subsys(&pfkey_net_id, &pfkey_net_ops);
+       if (err != 0)
+               goto out_xfrm_unregister_km;
 out:
        return err;
+out_xfrm_unregister_km:
+       xfrm_unregister_km(&pfkeyv2_mgr);
 out_remove_proc_entry:
        pfkey_exit_proc();
 out_sock_unregister: