netns PF_KEY: part 1
[safe/jmp/linux-2.6] / net / key / af_key.c
index e9ef9af..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;
        int             registered;
        int             promisc;
+
+       struct {
+               uint8_t         msg_version;
+               uint32_t        msg_pid;
+               int             (*dump)(struct pfkey_sock *sk);
+               void            (*done)(struct pfkey_sock *sk);
+               union {
+                       struct xfrm_policy_walk policy;
+                       struct xfrm_state_walk  state;
+               } u;
+               struct sk_buff  *skb;
+       } dump;
 };
 
 static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
@@ -55,8 +69,32 @@ static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
        return (struct pfkey_sock *)sk;
 }
 
+static int pfkey_can_dump(struct sock *sk)
+{
+       if (3 * atomic_read(&sk->sk_rmem_alloc) <= 2 * sk->sk_rcvbuf)
+               return 1;
+       return 0;
+}
+
+static void pfkey_terminate_dump(struct pfkey_sock *pfk)
+{
+       if (pfk->dump.dump) {
+               if (pfk->dump.skb) {
+                       kfree_skb(pfk->dump.skb);
+                       pfk->dump.skb = NULL;
+               }
+               pfk->dump.done(pfk);
+               pfk->dump.dump = NULL;
+               pfk->dump.done = NULL;
+       }
+}
+
 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);
 
        if (!sock_flag(sk, SOCK_DEAD)) {
@@ -64,10 +102,10 @@ static void pfkey_sock_destruct(struct sock *sk)
                return;
        }
 
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+       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)
@@ -118,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();
 }
 
@@ -138,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)
@@ -162,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);
 
@@ -224,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;
@@ -236,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;
 
@@ -278,6 +319,31 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
        return err;
 }
 
+static int pfkey_do_dump(struct pfkey_sock *pfk)
+{
+       struct sadb_msg *hdr;
+       int rc;
+
+       rc = pfk->dump.dump(pfk);
+       if (rc == -ENOBUFS)
+               return 0;
+
+       if (pfk->dump.skb) {
+               if (!pfkey_can_dump(&pfk->sk))
+                       return 0;
+
+               hdr = (struct sadb_msg *) pfk->dump.skb->data;
+               hdr->sadb_msg_seq = 0;
+               hdr->sadb_msg_errno = rc;
+               pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
+                               &pfk->sk);
+               pfk->dump.skb = NULL;
+       }
+
+       pfkey_terminate_dump(pfk);
+       return rc;
+}
+
 static inline void pfkey_hdr_dup(struct sadb_msg *new, struct sadb_msg *orig)
 {
        *new = *orig;
@@ -340,6 +406,7 @@ static u8 sadb_ext_min_len[] = {
        [SADB_X_EXT_NAT_T_DPORT]        = (u8) sizeof(struct sadb_x_nat_t_port),
        [SADB_X_EXT_NAT_T_OA]           = (u8) sizeof(struct sadb_address),
        [SADB_X_EXT_SEC_CTX]            = (u8) sizeof(struct sadb_x_sec_ctx),
+       [SADB_X_EXT_KMADDRESS]          = (u8) sizeof(struct sadb_x_kmaddress),
 };
 
 /* Verify sadb_address_{len,prefixlen} against sa_family.  */
@@ -547,25 +614,43 @@ static uint8_t pfkey_proto_from_xfrm(uint8_t proto)
        return (proto ? proto : IPSEC_PROTO_ANY);
 }
 
-static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
-                                    xfrm_address_t *xaddr)
+static inline int pfkey_sockaddr_len(sa_family_t family)
+{
+       switch (family) {
+       case AF_INET:
+               return sizeof(struct sockaddr_in);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       case AF_INET6:
+               return sizeof(struct sockaddr_in6);
+#endif
+       }
+       return 0;
+}
+
+static
+int pfkey_sockaddr_extract(const struct sockaddr *sa, xfrm_address_t *xaddr)
 {
-       switch (((struct sockaddr*)(addr + 1))->sa_family) {
+       switch (sa->sa_family) {
        case AF_INET:
                xaddr->a4 =
-                       ((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr;
+                       ((struct sockaddr_in *)sa)->sin_addr.s_addr;
                return AF_INET;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case AF_INET6:
                memcpy(xaddr->a6,
-                      &((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
+                      &((struct sockaddr_in6 *)sa)->sin6_addr,
                       sizeof(struct in6_addr));
                return AF_INET6;
 #endif
-       default:
-               return 0;
        }
-       /* NOTREACHED */
+       return 0;
+}
+
+static
+int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr, xfrm_address_t *xaddr)
+{
+       return pfkey_sockaddr_extract((struct sockaddr *)(addr + 1),
+                                     xaddr);
 }
 
 static struct  xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **ext_hdrs)
@@ -606,24 +691,15 @@ 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)))
+
 static int
 pfkey_sockaddr_size(sa_family_t family)
 {
-       switch (family) {
-       case AF_INET:
-               return PFKEY_ALIGN8(sizeof(struct sockaddr_in));
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               return PFKEY_ALIGN8(sizeof(struct sockaddr_in6));
-#endif
-       default:
-               return 0;
-       }
-       /* NOTREACHED */
+       return PFKEY_ALIGN8(pfkey_sockaddr_len(family));
 }
 
 static inline int pfkey_mode_from_xfrm(int mode)
@@ -655,6 +731,36 @@ static inline int pfkey_mode_to_xfrm(int mode)
        }
 }
 
+static unsigned int pfkey_sockaddr_fill(xfrm_address_t *xaddr, __be16 port,
+                                      struct sockaddr *sa,
+                                      unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+           {
+               struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+               sin->sin_family = AF_INET;
+               sin->sin_port = port;
+               sin->sin_addr.s_addr = xaddr->a4;
+               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+               return 32;
+           }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       case AF_INET6:
+           {
+               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+               sin6->sin6_family = AF_INET6;
+               sin6->sin6_port = port;
+               sin6->sin6_flowinfo = 0;
+               ipv6_addr_copy(&sin6->sin6_addr, (struct in6_addr *)xaddr->a6);
+               sin6->sin6_scope_id = 0;
+               return 128;
+           }
+#endif
+       }
+       return 0;
+}
+
 static struct sk_buff *__pfkey_xfrm_state2msg(struct xfrm_state *x,
                                              int add_keys, int hsc)
 {
@@ -665,13 +771,9 @@ static struct sk_buff *__pfkey_xfrm_state2msg(struct xfrm_state *x,
        struct sadb_address *addr;
        struct sadb_key *key;
        struct sadb_x_sa2 *sa2;
-       struct sockaddr_in *sin;
        struct sadb_x_sec_ctx *sec_ctx;
        struct xfrm_sec_ctx *xfrm_ctx;
        int ctx_size = 0;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       struct sockaddr_in6 *sin6;
-#endif
        int size;
        int auth_key_size = 0;
        int encrypt_key_size = 0;
@@ -700,14 +802,7 @@ static struct sk_buff *__pfkey_xfrm_state2msg(struct xfrm_state *x,
        }
 
        /* identity & sensitivity */
-
-       if ((x->props.family == AF_INET &&
-            x->sel.saddr.a4 != x->props.saddr.a4)
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-           || (x->props.family == AF_INET6 &&
-               memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct in6_addr)))
-#endif
-               )
+       if (xfrm_addr_cmp(&x->sel.saddr, &x->props.saddr, x->props.family))
                size += sizeof(struct sadb_address) + sockaddr_size;
 
        if (add_keys) {
@@ -829,29 +924,12 @@ static struct sk_buff *__pfkey_xfrm_state2msg(struct xfrm_state *x,
           protocol's number." - RFC2367 */
        addr->sadb_address_proto = 0;
        addr->sadb_address_reserved = 0;
-       if (x->props.family == AF_INET) {
-               addr->sadb_address_prefixlen = 32;
 
-               sin = (struct sockaddr_in *) (addr + 1);
-               sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->props.saddr.a4;
-               sin->sin_port = 0;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (x->props.family == AF_INET6) {
-               addr->sadb_address_prefixlen = 128;
-
-               sin6 = (struct sockaddr_in6 *) (addr + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_flowinfo = 0;
-               memcpy(&sin6->sin6_addr, x->props.saddr.a6,
-                      sizeof(struct in6_addr));
-               sin6->sin6_scope_id = 0;
-       }
-#endif
-       else
+       addr->sadb_address_prefixlen =
+               pfkey_sockaddr_fill(&x->props.saddr, 0,
+                                   (struct sockaddr *) (addr + 1),
+                                   x->props.family);
+       if (!addr->sadb_address_prefixlen)
                BUG();
 
        /* dst address */
@@ -862,70 +940,32 @@ static struct sk_buff *__pfkey_xfrm_state2msg(struct xfrm_state *x,
                        sizeof(uint64_t);
        addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
        addr->sadb_address_proto = 0;
-       addr->sadb_address_prefixlen = 32; /* XXX */
        addr->sadb_address_reserved = 0;
-       if (x->props.family == AF_INET) {
-               sin = (struct sockaddr_in *) (addr + 1);
-               sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->id.daddr.a4;
-               sin->sin_port = 0;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
 
-               if (x->sel.saddr.a4 != x->props.saddr.a4) {
-                       addr = (struct sadb_address*) skb_put(skb,
-                               sizeof(struct sadb_address)+sockaddr_size);
-                       addr->sadb_address_len =
-                               (sizeof(struct sadb_address)+sockaddr_size)/
-                               sizeof(uint64_t);
-                       addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
-                       addr->sadb_address_proto =
-                               pfkey_proto_from_xfrm(x->sel.proto);
-                       addr->sadb_address_prefixlen = x->sel.prefixlen_s;
-                       addr->sadb_address_reserved = 0;
-
-                       sin = (struct sockaddr_in *) (addr + 1);
-                       sin->sin_family = AF_INET;
-                       sin->sin_addr.s_addr = x->sel.saddr.a4;
-                       sin->sin_port = x->sel.sport;
-                       memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-               }
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (x->props.family == AF_INET6) {
-               addr->sadb_address_prefixlen = 128;
+       addr->sadb_address_prefixlen =
+               pfkey_sockaddr_fill(&x->id.daddr, 0,
+                                   (struct sockaddr *) (addr + 1),
+                                   x->props.family);
+       if (!addr->sadb_address_prefixlen)
+               BUG();
 
-               sin6 = (struct sockaddr_in6 *) (addr + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_flowinfo = 0;
-               memcpy(&sin6->sin6_addr, x->id.daddr.a6, sizeof(struct in6_addr));
-               sin6->sin6_scope_id = 0;
+       if (xfrm_addr_cmp(&x->sel.saddr, &x->props.saddr,
+                         x->props.family)) {
+               addr = (struct sadb_address*) skb_put(skb,
+                       sizeof(struct sadb_address)+sockaddr_size);
+               addr->sadb_address_len =
+                       (sizeof(struct sadb_address)+sockaddr_size)/
+                       sizeof(uint64_t);
+               addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+               addr->sadb_address_proto =
+                       pfkey_proto_from_xfrm(x->sel.proto);
+               addr->sadb_address_prefixlen = x->sel.prefixlen_s;
+               addr->sadb_address_reserved = 0;
 
-               if (memcmp (x->sel.saddr.a6, x->props.saddr.a6,
-                           sizeof(struct in6_addr))) {
-                       addr = (struct sadb_address *) skb_put(skb,
-                               sizeof(struct sadb_address)+sockaddr_size);
-                       addr->sadb_address_len =
-                               (sizeof(struct sadb_address)+sockaddr_size)/
-                               sizeof(uint64_t);
-                       addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
-                       addr->sadb_address_proto =
-                               pfkey_proto_from_xfrm(x->sel.proto);
-                       addr->sadb_address_prefixlen = x->sel.prefixlen_s;
-                       addr->sadb_address_reserved = 0;
-
-                       sin6 = (struct sockaddr_in6 *) (addr + 1);
-                       sin6->sin6_family = AF_INET6;
-                       sin6->sin6_port = x->sel.sport;
-                       sin6->sin6_flowinfo = 0;
-                       memcpy(&sin6->sin6_addr, x->sel.saddr.a6,
-                              sizeof(struct in6_addr));
-                       sin6->sin6_scope_id = 0;
-               }
+               pfkey_sockaddr_fill(&x->sel.saddr, x->sel.sport,
+                                   (struct sockaddr *) (addr + 1),
+                                   x->props.family);
        }
-#endif
-       else
-               BUG();
 
        /* auth key */
        if (add_keys && auth_key_size) {
@@ -1090,7 +1130,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
             (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
                return ERR_PTR(-EINVAL);
 
-       x = xfrm_state_alloc();
+       x = xfrm_state_alloc(&init_net);
        if (x == NULL)
                return ERR_PTR(-ENOBUFS);
 
@@ -1219,7 +1259,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
                x->sel.prefixlen_s = addr->sadb_address_prefixlen;
        }
 
-       if (x->props.mode == XFRM_MODE_TRANSPORT)
+       if (!x->sel.family)
                x->sel.family = x->props.family;
 
        if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
@@ -1316,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;
@@ -1324,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;
@@ -1372,14 +1412,14 @@ 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;
 
        spin_lock_bh(&x->lock);
        if (x->km.state == XFRM_STATE_ACQ) {
                x->km.state = XFRM_STATE_ERROR;
-               wake_up(&km_waitq);
+               wake_up(&init_net.xfrm.km_waitq);
        }
        spin_unlock_bh(&x->lock);
        xfrm_state_put(x);
@@ -1466,7 +1506,8 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
                err = xfrm_state_update(x);
 
        xfrm_audit_state_add(x, err ? 0 : 1,
-                            audit_get_loginuid(current), 0);
+                            audit_get_loginuid(current),
+                            audit_get_sessionid(current), 0);
 
        if (err < 0) {
                x->km.state = XFRM_STATE_DEAD;
@@ -1520,7 +1561,8 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
        km_state_notify(x, &c);
 out:
        xfrm_audit_state_delete(x, err ? 0 : 1,
-                              audit_get_loginuid(current), 0);
+                               audit_get_loginuid(current),
+                               audit_get_sessionid(current), 0);
        xfrm_state_put(x);
 
        return err;
@@ -1696,58 +1738,80 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hd
                return -EINVAL;
 
        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;
 }
 
-struct pfkey_dump_data
-{
-       struct sk_buff *skb;
-       struct sadb_msg *hdr;
-       struct sock *sk;
-};
-
 static int dump_sa(struct xfrm_state *x, int count, void *ptr)
 {
-       struct pfkey_dump_data *data = ptr;
+       struct pfkey_sock *pfk = ptr;
        struct sk_buff *out_skb;
        struct sadb_msg *out_hdr;
 
+       if (!pfkey_can_dump(&pfk->sk))
+               return -ENOBUFS;
+
        out_skb = pfkey_xfrm_state2msg(x);
        if (IS_ERR(out_skb))
                return PTR_ERR(out_skb);
 
        out_hdr = (struct sadb_msg *) out_skb->data;
-       out_hdr->sadb_msg_version = data->hdr->sadb_msg_version;
+       out_hdr->sadb_msg_version = pfk->dump.msg_version;
        out_hdr->sadb_msg_type = SADB_DUMP;
        out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
        out_hdr->sadb_msg_errno = 0;
        out_hdr->sadb_msg_reserved = 0;
-       out_hdr->sadb_msg_seq = count;
-       out_hdr->sadb_msg_pid = data->hdr->sadb_msg_pid;
-       pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, data->sk);
+       out_hdr->sadb_msg_seq = count + 1;
+       out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
+
+       if (pfk->dump.skb)
+               pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
+                               &pfk->sk);
+       pfk->dump.skb = out_skb;
+
        return 0;
 }
 
+static int pfkey_dump_sa(struct pfkey_sock *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)
+{
+       xfrm_state_walk_done(&pfk->dump.u.state);
+}
+
 static int pfkey_dump(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
 {
        u8 proto;
-       struct pfkey_dump_data data = { .skb = skb, .hdr = hdr, .sk = sk };
+       struct pfkey_sock *pfk = pfkey_sk(sk);
+
+       if (pfk->dump.dump != NULL)
+               return -EBUSY;
 
        proto = pfkey_satype2proto(hdr->sadb_msg_satype);
        if (proto == 0)
                return -EINVAL;
 
-       return xfrm_state_walk(proto, dump_sa, &data);
+       pfk->dump.msg_version = hdr->sadb_msg_version;
+       pfk->dump.msg_pid = hdr->sadb_msg_pid;
+       pfk->dump.dump = pfkey_dump_sa;
+       pfk->dump.done = pfkey_dump_sa_done;
+       xfrm_state_walk_init(&pfk->dump.u.state, proto);
+
+       return pfkey_do_dump(pfk);
 }
 
 static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
@@ -1780,7 +1844,9 @@ static int check_reqid(struct xfrm_policy *xp, int dir, int count, void *ptr)
 
 static u32 gen_reqid(void)
 {
+       struct xfrm_policy_walk walk;
        u32 start;
+       int rc;
        static u32 reqid = IPSEC_MANUAL_REQID_MAX;
 
        start = reqid;
@@ -1788,8 +1854,10 @@ static u32 gen_reqid(void)
                ++reqid;
                if (reqid == 0)
                        reqid = IPSEC_MANUAL_REQID_MAX+1;
-               if (xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, check_reqid,
-                                    (void*)&reqid) != -EEXIST)
+               xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
+               rc = xfrm_policy_walk(&init_net, &walk, check_reqid, (void*)&reqid);
+               xfrm_policy_walk_done(&walk);
+               if (rc != -EEXIST)
                        return reqid;
        } while (reqid != start);
        return 0;
@@ -1799,10 +1867,6 @@ static int
 parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
 {
        struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr;
-       struct sockaddr_in *sin;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       struct sockaddr_in6 *sin6;
-#endif
        int mode;
 
        if (xp->xfrm_nr >= XFRM_MAX_DEPTH)
@@ -1827,36 +1891,24 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
 
        /* addresses present only in tunnel mode */
        if (t->mode == XFRM_MODE_TUNNEL) {
-               struct sockaddr *sa;
-               sa = (struct sockaddr *)(rq+1);
-               switch(sa->sa_family) {
-               case AF_INET:
-                       sin = (struct sockaddr_in*)sa;
-                       t->saddr.a4 = sin->sin_addr.s_addr;
-                       sin++;
-                       if (sin->sin_family != AF_INET)
-                               return -EINVAL;
-                       t->id.daddr.a4 = sin->sin_addr.s_addr;
-                       break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       sin6 = (struct sockaddr_in6*)sa;
-                       memcpy(t->saddr.a6, &sin6->sin6_addr, sizeof(struct in6_addr));
-                       sin6++;
-                       if (sin6->sin6_family != AF_INET6)
-                               return -EINVAL;
-                       memcpy(t->id.daddr.a6, &sin6->sin6_addr, sizeof(struct in6_addr));
-                       break;
-#endif
-               default:
+               u8 *sa = (u8 *) (rq + 1);
+               int family, socklen;
+
+               family = pfkey_sockaddr_extract((struct sockaddr *)sa,
+                                               &t->saddr);
+               if (!family)
                        return -EINVAL;
-               }
-               t->encap_family = sa->sa_family;
+
+               socklen = pfkey_sockaddr_len(family);
+               if (pfkey_sockaddr_extract((struct sockaddr *)(sa + socklen),
+                                          &t->id.daddr) != family)
+                       return -EINVAL;
+               t->encap_family = family;
        } else
                t->encap_family = xp->family;
 
        /* No way to set this via kame pfkey */
-       t->aalgos = t->ealgos = t->calgos = ~0;
+       t->allalgs = 1;
        xp->xfrm_nr++;
        return 0;
 }
@@ -1898,9 +1950,7 @@ static int pfkey_xfrm_policy2msg_size(struct xfrm_policy *xp)
 
        for (i=0; i<xp->xfrm_nr; i++) {
                t = xp->xfrm_vec + i;
-               socklen += (t->encap_family == AF_INET ?
-                           sizeof(struct sockaddr_in) :
-                           sizeof(struct sockaddr_in6));
+               socklen += pfkey_sockaddr_len(t->encap_family);
        }
 
        return sizeof(struct sadb_msg) +
@@ -1933,18 +1983,12 @@ static int pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, in
        struct sadb_address *addr;
        struct sadb_lifetime *lifetime;
        struct sadb_x_policy *pol;
-       struct sockaddr_in   *sin;
        struct sadb_x_sec_ctx *sec_ctx;
        struct xfrm_sec_ctx *xfrm_ctx;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       struct sockaddr_in6  *sin6;
-#endif
        int i;
        int size;
        int sockaddr_size = pfkey_sockaddr_size(xp->family);
-       int socklen = (xp->family == AF_INET ?
-                      sizeof(struct sockaddr_in) :
-                      sizeof(struct sockaddr_in6));
+       int socklen = pfkey_sockaddr_len(xp->family);
 
        size = pfkey_xfrm_policy2msg_size(xp);
 
@@ -1962,26 +2006,10 @@ static int pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, in
        addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
        addr->sadb_address_prefixlen = xp->selector.prefixlen_s;
        addr->sadb_address_reserved = 0;
-       /* src address */
-       if (xp->family == AF_INET) {
-               sin = (struct sockaddr_in *) (addr + 1);
-               sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = xp->selector.saddr.a4;
-               sin->sin_port = xp->selector.sport;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (xp->family == AF_INET6) {
-               sin6 = (struct sockaddr_in6 *) (addr + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = xp->selector.sport;
-               sin6->sin6_flowinfo = 0;
-               memcpy(&sin6->sin6_addr, xp->selector.saddr.a6,
-                      sizeof(struct in6_addr));
-               sin6->sin6_scope_id = 0;
-       }
-#endif
-       else
+       if (!pfkey_sockaddr_fill(&xp->selector.saddr,
+                                xp->selector.sport,
+                                (struct sockaddr *) (addr + 1),
+                                xp->family))
                BUG();
 
        /* dst address */
@@ -1994,26 +2022,10 @@ static int pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, in
        addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
        addr->sadb_address_prefixlen = xp->selector.prefixlen_d;
        addr->sadb_address_reserved = 0;
-       if (xp->family == AF_INET) {
-               sin = (struct sockaddr_in *) (addr + 1);
-               sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = xp->selector.daddr.a4;
-               sin->sin_port = xp->selector.dport;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (xp->family == AF_INET6) {
-               sin6 = (struct sockaddr_in6 *) (addr + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = xp->selector.dport;
-               sin6->sin6_flowinfo = 0;
-               memcpy(&sin6->sin6_addr, xp->selector.daddr.a6,
-                      sizeof(struct in6_addr));
-               sin6->sin6_scope_id = 0;
-       }
-#endif
-       else
-               BUG();
+
+       pfkey_sockaddr_fill(&xp->selector.daddr, xp->selector.dport,
+                           (struct sockaddr *) (addr + 1),
+                           xp->family);
 
        /* hard time */
        lifetime = (struct sadb_lifetime *)  skb_put(skb,
@@ -2067,12 +2079,12 @@ static int pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, in
                int mode;
 
                req_size = sizeof(struct sadb_x_ipsecrequest);
-               if (t->mode == XFRM_MODE_TUNNEL)
-                       req_size += ((t->encap_family == AF_INET ?
-                                    sizeof(struct sockaddr_in) :
-                                    sizeof(struct sockaddr_in6)) * 2);
-               else
+               if (t->mode == XFRM_MODE_TUNNEL) {
+                       socklen = pfkey_sockaddr_len(t->encap_family);
+                       req_size += socklen * 2;
+               } else {
                        size -= 2*socklen;
+               }
                rq = (void*)skb_put(skb, req_size);
                pol->sadb_x_policy_len += req_size/8;
                memset(rq, 0, sizeof(*rq));
@@ -2087,42 +2099,15 @@ static int pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, in
                if (t->optional)
                        rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_USE;
                rq->sadb_x_ipsecrequest_reqid = t->reqid;
+
                if (t->mode == XFRM_MODE_TUNNEL) {
-                       switch (t->encap_family) {
-                       case AF_INET:
-                               sin = (void*)(rq+1);
-                               sin->sin_family = AF_INET;
-                               sin->sin_addr.s_addr = t->saddr.a4;
-                               sin->sin_port = 0;
-                               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-                               sin++;
-                               sin->sin_family = AF_INET;
-                               sin->sin_addr.s_addr = t->id.daddr.a4;
-                               sin->sin_port = 0;
-                               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-                               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-                       case AF_INET6:
-                               sin6 = (void*)(rq+1);
-                               sin6->sin6_family = AF_INET6;
-                               sin6->sin6_port = 0;
-                               sin6->sin6_flowinfo = 0;
-                               memcpy(&sin6->sin6_addr, t->saddr.a6,
-                                      sizeof(struct in6_addr));
-                               sin6->sin6_scope_id = 0;
-
-                               sin6++;
-                               sin6->sin6_family = AF_INET6;
-                               sin6->sin6_port = 0;
-                               sin6->sin6_flowinfo = 0;
-                               memcpy(&sin6->sin6_addr, t->id.daddr.a6,
-                                      sizeof(struct in6_addr));
-                               sin6->sin6_scope_id = 0;
-                               break;
-#endif
-                       default:
-                               break;
-                       }
+                       u8 *sa = (void *)(rq + 1);
+                       pfkey_sockaddr_fill(&t->saddr, 0,
+                                           (struct sockaddr *)sa,
+                                           t->encap_family);
+                       pfkey_sockaddr_fill(&t->id.daddr, 0,
+                                           (struct sockaddr *) (sa + socklen),
+                                           t->encap_family);
                }
        }
 
@@ -2198,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;
 
@@ -2241,7 +2226,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
                        goto out;
                }
 
-               err = security_xfrm_policy_alloc(xp, uctx);
+               err = security_xfrm_policy_alloc(&xp->security, uctx);
                kfree(uctx);
 
                if (err)
@@ -2273,7 +2258,8 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
                                 hdr->sadb_msg_type != SADB_X_SPDUPDATE);
 
        xfrm_audit_policy_add(xp, err ? 0 : 1,
-                            audit_get_loginuid(current), 0);
+                             audit_get_loginuid(current),
+                             audit_get_sessionid(current), 0);
 
        if (err)
                goto out;
@@ -2291,7 +2277,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
        return 0;
 
 out:
-       xp->dead = 1;
+       xp->walk.dead = 1;
        xfrm_policy_destroy(xp);
        return err;
 }
@@ -2301,10 +2287,11 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
        int err;
        struct sadb_address *sa;
        struct sadb_x_policy *pol;
-       struct xfrm_policy *xp, tmp;
+       struct xfrm_policy *xp;
        struct xfrm_selector sel;
        struct km_event c;
        struct sadb_x_sec_ctx *sec_ctx;
+       struct xfrm_sec_ctx *pol_ctx = NULL;
 
        if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
                                     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
@@ -2334,36 +2321,35 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
                sel.dport_mask = htons(0xffff);
 
        sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
-       memset(&tmp, 0, sizeof(struct xfrm_policy));
-
        if (sec_ctx != NULL) {
                struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
 
                if (!uctx)
                        return -ENOMEM;
 
-               err = security_xfrm_policy_alloc(&tmp, uctx);
+               err = security_xfrm_policy_alloc(&pol_ctx, uctx);
                kfree(uctx);
-
                if (err)
                        return err;
        }
 
-       xp = xfrm_policy_bysel_ctx(XFRM_POLICY_TYPE_MAIN, pol->sadb_x_policy_dir-1,
-                                  &sel, tmp.security, 1, &err);
-       security_xfrm_policy_free(&tmp);
-
+       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);
        if (xp == NULL)
                return -ENOENT;
 
        xfrm_audit_policy_delete(xp, err ? 0 : 1,
-                               audit_get_loginuid(current), 0);
+                                audit_get_loginuid(current),
+                                audit_get_sessionid(current), 0);
 
        if (err)
                goto out;
 
        c.seq = hdr->sadb_msg_seq;
        c.pid = hdr->sadb_msg_pid;
+       c.data.byid = 0;
        c.event = XFRM_MSG_DELPOLICY;
        km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
 
@@ -2405,61 +2391,28 @@ out:
 #ifdef CONFIG_NET_KEY_MIGRATE
 static int pfkey_sockaddr_pair_size(sa_family_t family)
 {
-       switch (family) {
-       case AF_INET:
-               return PFKEY_ALIGN8(sizeof(struct sockaddr_in) * 2);
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               return PFKEY_ALIGN8(sizeof(struct sockaddr_in6) * 2);
-#endif
-       default:
-               return 0;
-       }
-       /* NOTREACHED */
+       return PFKEY_ALIGN8(pfkey_sockaddr_len(family) * 2);
 }
 
-static int parse_sockaddr_pair(struct sadb_x_ipsecrequest *rq,
+static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
                               xfrm_address_t *saddr, xfrm_address_t *daddr,
                               u16 *family)
 {
-       struct sockaddr *sa = (struct sockaddr *)(rq + 1);
-       if (rq->sadb_x_ipsecrequest_len <
-           pfkey_sockaddr_pair_size(sa->sa_family))
+       int af, socklen;
+
+       if (ext_len < pfkey_sockaddr_pair_size(sa->sa_family))
                return -EINVAL;
 
-       switch (sa->sa_family) {
-       case AF_INET:
-               {
-                       struct sockaddr_in *sin;
-                       sin = (struct sockaddr_in *)sa;
-                       if ((sin+1)->sin_family != AF_INET)
-                               return -EINVAL;
-                       memcpy(&saddr->a4, &sin->sin_addr, sizeof(saddr->a4));
-                       sin++;
-                       memcpy(&daddr->a4, &sin->sin_addr, sizeof(daddr->a4));
-                       *family = AF_INET;
-                       break;
-               }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               {
-                       struct sockaddr_in6 *sin6;
-                       sin6 = (struct sockaddr_in6 *)sa;
-                       if ((sin6+1)->sin6_family != AF_INET6)
-                               return -EINVAL;
-                       memcpy(&saddr->a6, &sin6->sin6_addr,
-                              sizeof(saddr->a6));
-                       sin6++;
-                       memcpy(&daddr->a6, &sin6->sin6_addr,
-                              sizeof(daddr->a6));
-                       *family = AF_INET6;
-                       break;
-               }
-#endif
-       default:
+       af = pfkey_sockaddr_extract(sa, saddr);
+       if (!af)
+               return -EINVAL;
+
+       socklen = pfkey_sockaddr_len(af);
+       if (pfkey_sockaddr_extract((struct sockaddr *) (((u8 *)sa) + socklen),
+                                  daddr) != af)
                return -EINVAL;
-       }
 
+       *family = af;
        return 0;
 }
 
@@ -2475,7 +2428,9 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
                return -EINVAL;
 
        /* old endoints */
-       err = parse_sockaddr_pair(rq1, &m->old_saddr, &m->old_daddr,
+       err = parse_sockaddr_pair((struct sockaddr *)(rq1 + 1),
+                                 rq1->sadb_x_ipsecrequest_len,
+                                 &m->old_saddr, &m->old_daddr,
                                  &m->old_family);
        if (err)
                return err;
@@ -2488,7 +2443,9 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
                return -EINVAL;
 
        /* new endpoints */
-       err = parse_sockaddr_pair(rq2, &m->new_saddr, &m->new_daddr,
+       err = parse_sockaddr_pair((struct sockaddr *)(rq2 + 1),
+                                 rq2->sadb_x_ipsecrequest_len,
+                                 &m->new_saddr, &m->new_daddr,
                                  &m->new_family);
        if (err)
                return err;
@@ -2514,29 +2471,40 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
        int i, len, ret, err = -EINVAL;
        u8 dir;
        struct sadb_address *sa;
+       struct sadb_x_kmaddress *kma;
        struct sadb_x_policy *pol;
        struct sadb_x_ipsecrequest *rq;
        struct xfrm_selector sel;
        struct xfrm_migrate m[XFRM_MAX_DEPTH];
+       struct xfrm_kmaddress k;
 
        if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC - 1],
-           ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) ||
+                                    ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) ||
            !ext_hdrs[SADB_X_EXT_POLICY - 1]) {
                err = -EINVAL;
                goto out;
        }
 
+       kma = ext_hdrs[SADB_X_EXT_KMADDRESS - 1];
        pol = ext_hdrs[SADB_X_EXT_POLICY - 1];
-       if (!pol) {
-               err = -EINVAL;
-               goto out;
-       }
 
        if (pol->sadb_x_policy_dir >= IPSEC_DIR_MAX) {
                err = -EINVAL;
                goto out;
        }
 
+       if (kma) {
+               /* convert sadb_x_kmaddress to xfrm_kmaddress */
+               k.reserved = kma->sadb_x_kmaddress_reserved;
+               ret = parse_sockaddr_pair((struct sockaddr *)(kma + 1),
+                                         8*(kma->sadb_x_kmaddress_len) - sizeof(*kma),
+                                         &k.local, &k.remote, &k.family);
+               if (ret < 0) {
+                       err = ret;
+                       goto out;
+               }
+       }
+
        dir = pol->sadb_x_policy_dir - 1;
        memset(&sel, 0, sizeof(sel));
 
@@ -2581,7 +2549,8 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
                goto out;
        }
 
-       return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i);
+       return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i,
+                           kma ? &k : NULL);
 
  out:
        return err;
@@ -2611,14 +2580,15 @@ 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;
 
        if (delete) {
                xfrm_audit_policy_delete(xp, err ? 0 : 1,
-                               audit_get_loginuid(current), 0);
+                               audit_get_loginuid(current),
+                               audit_get_sessionid(current), 0);
 
                if (err)
                        goto out;
@@ -2638,11 +2608,14 @@ out:
 
 static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
 {
-       struct pfkey_dump_data *data = ptr;
+       struct pfkey_sock *pfk = ptr;
        struct sk_buff *out_skb;
        struct sadb_msg *out_hdr;
        int err;
 
+       if (!pfkey_can_dump(&pfk->sk))
+               return -ENOBUFS;
+
        out_skb = pfkey_xfrm_policy2msg_prep(xp);
        if (IS_ERR(out_skb))
                return PTR_ERR(out_skb);
@@ -2652,21 +2625,45 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
                return err;
 
        out_hdr = (struct sadb_msg *) out_skb->data;
-       out_hdr->sadb_msg_version = data->hdr->sadb_msg_version;
+       out_hdr->sadb_msg_version = pfk->dump.msg_version;
        out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
        out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
        out_hdr->sadb_msg_errno = 0;
-       out_hdr->sadb_msg_seq = count;
-       out_hdr->sadb_msg_pid = data->hdr->sadb_msg_pid;
-       pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, data->sk);
+       out_hdr->sadb_msg_seq = count + 1;
+       out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
+
+       if (pfk->dump.skb)
+               pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
+                               &pfk->sk);
+       pfk->dump.skb = out_skb;
+
        return 0;
 }
 
+static int pfkey_dump_sp(struct pfkey_sock *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)
+{
+       xfrm_policy_walk_done(&pfk->dump.u.policy);
+}
+
 static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
 {
-       struct pfkey_dump_data data = { .skb = skb, .hdr = hdr, .sk = sk };
+       struct pfkey_sock *pfk = pfkey_sk(sk);
+
+       if (pfk->dump.dump != NULL)
+               return -EBUSY;
+
+       pfk->dump.msg_version = hdr->sadb_msg_version;
+       pfk->dump.msg_pid = hdr->sadb_msg_pid;
+       pfk->dump.dump = pfkey_dump_sp;
+       pfk->dump.done = pfkey_dump_sp_done;
+       xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN);
 
-       return xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, dump_sp, &data);
+       return pfkey_do_dump(pfk);
 }
 
 static int key_notify_policy_flush(struct km_event *c)
@@ -2696,14 +2693,16 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg
        int err;
 
        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;
@@ -2952,6 +2951,12 @@ 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)
 {
+       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) {
        case XFRM_MSG_EXPIRE:
                return key_notify_sa_expire(x, c);
@@ -3013,10 +3018,6 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
        struct sadb_msg *hdr;
        struct sadb_address *addr;
        struct sadb_x_policy *pol;
-       struct sockaddr_in *sin;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       struct sockaddr_in6 *sin6;
-#endif
        int sockaddr_size;
        int size;
        struct sadb_x_sec_ctx *sec_ctx;
@@ -3065,29 +3066,11 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
        addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
        addr->sadb_address_proto = 0;
        addr->sadb_address_reserved = 0;
-       if (x->props.family == AF_INET) {
-               addr->sadb_address_prefixlen = 32;
-
-               sin = (struct sockaddr_in *) (addr + 1);
-               sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->props.saddr.a4;
-               sin->sin_port = 0;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (x->props.family == AF_INET6) {
-               addr->sadb_address_prefixlen = 128;
-
-               sin6 = (struct sockaddr_in6 *) (addr + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_flowinfo = 0;
-               memcpy(&sin6->sin6_addr,
-                      x->props.saddr.a6, sizeof(struct in6_addr));
-               sin6->sin6_scope_id = 0;
-       }
-#endif
-       else
+       addr->sadb_address_prefixlen =
+               pfkey_sockaddr_fill(&x->props.saddr, 0,
+                                   (struct sockaddr *) (addr + 1),
+                                   x->props.family);
+       if (!addr->sadb_address_prefixlen)
                BUG();
 
        /* dst address */
@@ -3099,29 +3082,11 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
        addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
        addr->sadb_address_proto = 0;
        addr->sadb_address_reserved = 0;
-       if (x->props.family == AF_INET) {
-               addr->sadb_address_prefixlen = 32;
-
-               sin = (struct sockaddr_in *) (addr + 1);
-               sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->id.daddr.a4;
-               sin->sin_port = 0;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (x->props.family == AF_INET6) {
-               addr->sadb_address_prefixlen = 128;
-
-               sin6 = (struct sockaddr_in6 *) (addr + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_flowinfo = 0;
-               memcpy(&sin6->sin6_addr,
-                      x->id.daddr.a6, sizeof(struct in6_addr));
-               sin6->sin6_scope_id = 0;
-       }
-#endif
-       else
+       addr->sadb_address_prefixlen =
+               pfkey_sockaddr_fill(&x->id.daddr, 0,
+                                   (struct sockaddr *) (addr + 1),
+                                   x->props.family);
+       if (!addr->sadb_address_prefixlen)
                BUG();
 
        pol = (struct sadb_x_policy *)  skb_put(skb, sizeof(struct sadb_x_policy));
@@ -3189,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;
@@ -3225,7 +3190,7 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
                if ((*dir = verify_sec_ctx_len(p)))
                        goto out;
                uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
-               *dir = security_xfrm_policy_alloc(xp, uctx);
+               *dir = security_xfrm_policy_alloc(&xp->security, uctx);
                kfree(uctx);
 
                if (*dir)
@@ -3236,6 +3201,7 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
        return xp;
 
 out:
+       xp->walk.dead = 1;
        xfrm_policy_destroy(xp);
        return NULL;
 }
@@ -3247,10 +3213,6 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
        struct sadb_sa *sa;
        struct sadb_address *addr;
        struct sadb_x_nat_t_port *n_port;
-       struct sockaddr_in *sin;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       struct sockaddr_in6 *sin6;
-#endif
        int sockaddr_size;
        int size;
        __u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0);
@@ -3314,29 +3276,11 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
        addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
        addr->sadb_address_proto = 0;
        addr->sadb_address_reserved = 0;
-       if (x->props.family == AF_INET) {
-               addr->sadb_address_prefixlen = 32;
-
-               sin = (struct sockaddr_in *) (addr + 1);
-               sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->props.saddr.a4;
-               sin->sin_port = 0;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (x->props.family == AF_INET6) {
-               addr->sadb_address_prefixlen = 128;
-
-               sin6 = (struct sockaddr_in6 *) (addr + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_flowinfo = 0;
-               memcpy(&sin6->sin6_addr,
-                      x->props.saddr.a6, sizeof(struct in6_addr));
-               sin6->sin6_scope_id = 0;
-       }
-#endif
-       else
+       addr->sadb_address_prefixlen =
+               pfkey_sockaddr_fill(&x->props.saddr, 0,
+                                   (struct sockaddr *) (addr + 1),
+                                   x->props.family);
+       if (!addr->sadb_address_prefixlen)
                BUG();
 
        /* NAT_T_SPORT (old port) */
@@ -3355,28 +3299,11 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
        addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
        addr->sadb_address_proto = 0;
        addr->sadb_address_reserved = 0;
-       if (x->props.family == AF_INET) {
-               addr->sadb_address_prefixlen = 32;
-
-               sin = (struct sockaddr_in *) (addr + 1);
-               sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = ipaddr->a4;
-               sin->sin_port = 0;
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (x->props.family == AF_INET6) {
-               addr->sadb_address_prefixlen = 128;
-
-               sin6 = (struct sockaddr_in6 *) (addr + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_flowinfo = 0;
-               memcpy(&sin6->sin6_addr, &ipaddr->a6, sizeof(struct in6_addr));
-               sin6->sin6_scope_id = 0;
-       }
-#endif
-       else
+       addr->sadb_address_prefixlen =
+               pfkey_sockaddr_fill(ipaddr, 0,
+                                   (struct sockaddr *) (addr + 1),
+                                   x->props.family);
+       if (!addr->sadb_address_prefixlen)
                BUG();
 
        /* NAT_T_DPORT (new port) */
@@ -3394,10 +3321,6 @@ static int set_sadb_address(struct sk_buff *skb, int sasize, int type,
                            struct xfrm_selector *sel)
 {
        struct sadb_address *addr;
-       struct sockaddr_in *sin;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       struct sockaddr_in6 *sin6;
-#endif
        addr = (struct sadb_address *)skb_put(skb, sizeof(struct sadb_address) + sasize);
        addr->sadb_address_len = (sizeof(struct sadb_address) + sasize)/8;
        addr->sadb_address_exttype = type;
@@ -3406,50 +3329,16 @@ static int set_sadb_address(struct sk_buff *skb, int sasize, int type,
 
        switch (type) {
        case SADB_EXT_ADDRESS_SRC:
-               if (sel->family == AF_INET) {
-                       addr->sadb_address_prefixlen = sel->prefixlen_s;
-                       sin = (struct sockaddr_in *)(addr + 1);
-                       sin->sin_family = AF_INET;
-                       memcpy(&sin->sin_addr.s_addr, &sel->saddr,
-                              sizeof(sin->sin_addr.s_addr));
-                       sin->sin_port = 0;
-                       memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-               }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               else if (sel->family == AF_INET6) {
-                       addr->sadb_address_prefixlen = sel->prefixlen_s;
-                       sin6 = (struct sockaddr_in6 *)(addr + 1);
-                       sin6->sin6_family = AF_INET6;
-                       sin6->sin6_port = 0;
-                       sin6->sin6_flowinfo = 0;
-                       sin6->sin6_scope_id = 0;
-                       memcpy(&sin6->sin6_addr.s6_addr, &sel->saddr,
-                              sizeof(sin6->sin6_addr.s6_addr));
-               }
-#endif
+               addr->sadb_address_prefixlen = sel->prefixlen_s;
+               pfkey_sockaddr_fill(&sel->saddr, 0,
+                                   (struct sockaddr *)(addr + 1),
+                                   sel->family);
                break;
        case SADB_EXT_ADDRESS_DST:
-               if (sel->family == AF_INET) {
-                       addr->sadb_address_prefixlen = sel->prefixlen_d;
-                       sin = (struct sockaddr_in *)(addr + 1);
-                       sin->sin_family = AF_INET;
-                       memcpy(&sin->sin_addr.s_addr, &sel->daddr,
-                              sizeof(sin->sin_addr.s_addr));
-                       sin->sin_port = 0;
-                       memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-               }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               else if (sel->family == AF_INET6) {
-                       addr->sadb_address_prefixlen = sel->prefixlen_d;
-                       sin6 = (struct sockaddr_in6 *)(addr + 1);
-                       sin6->sin6_family = AF_INET6;
-                       sin6->sin6_port = 0;
-                       sin6->sin6_flowinfo = 0;
-                       sin6->sin6_scope_id = 0;
-                       memcpy(&sin6->sin6_addr.s6_addr, &sel->daddr,
-                              sizeof(sin6->sin6_addr.s6_addr));
-               }
-#endif
+               addr->sadb_address_prefixlen = sel->prefixlen_d;
+               pfkey_sockaddr_fill(&sel->daddr, 0,
+                                   (struct sockaddr *)(addr + 1),
+                                   sel->family);
                break;
        default:
                return -EINVAL;
@@ -3458,16 +3347,40 @@ static int set_sadb_address(struct sk_buff *skb, int sasize, int type,
        return 0;
 }
 
+
+static int set_sadb_kmaddress(struct sk_buff *skb, struct xfrm_kmaddress *k)
+{
+       struct sadb_x_kmaddress *kma;
+       u8 *sa;
+       int family = k->family;
+       int socklen = pfkey_sockaddr_len(family);
+       int size_req;
+
+       size_req = (sizeof(struct sadb_x_kmaddress) +
+                   pfkey_sockaddr_pair_size(family));
+
+       kma = (struct sadb_x_kmaddress *)skb_put(skb, size_req);
+       memset(kma, 0, size_req);
+       kma->sadb_x_kmaddress_len = size_req / 8;
+       kma->sadb_x_kmaddress_exttype = SADB_X_EXT_KMADDRESS;
+       kma->sadb_x_kmaddress_reserved = k->reserved;
+
+       sa = (u8 *)(kma + 1);
+       if (!pfkey_sockaddr_fill(&k->local, 0, (struct sockaddr *)sa, family) ||
+           !pfkey_sockaddr_fill(&k->remote, 0, (struct sockaddr *)(sa+socklen), family))
+               return -EINVAL;
+
+       return 0;
+}
+
 static int set_ipsecrequest(struct sk_buff *skb,
                            uint8_t proto, uint8_t mode, int level,
                            uint32_t reqid, uint8_t family,
                            xfrm_address_t *src, xfrm_address_t *dst)
 {
        struct sadb_x_ipsecrequest *rq;
-       struct sockaddr_in *sin;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       struct sockaddr_in6 *sin6;
-#endif
+       u8 *sa;
+       int socklen = pfkey_sockaddr_len(family);
        int size_req;
 
        size_req = sizeof(struct sadb_x_ipsecrequest) +
@@ -3481,38 +3394,10 @@ static int set_ipsecrequest(struct sk_buff *skb,
        rq->sadb_x_ipsecrequest_level = level;
        rq->sadb_x_ipsecrequest_reqid = reqid;
 
-       switch (family) {
-       case AF_INET:
-               sin = (struct sockaddr_in *)(rq + 1);
-               sin->sin_family = AF_INET;
-               memcpy(&sin->sin_addr.s_addr, src,
-                      sizeof(sin->sin_addr.s_addr));
-               sin++;
-               sin->sin_family = AF_INET;
-               memcpy(&sin->sin_addr.s_addr, dst,
-                      sizeof(sin->sin_addr.s_addr));
-               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               sin6 = (struct sockaddr_in6 *)(rq + 1);
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_flowinfo = 0;
-               sin6->sin6_scope_id = 0;
-               memcpy(&sin6->sin6_addr.s6_addr, src,
-                      sizeof(sin6->sin6_addr.s6_addr));
-               sin6++;
-               sin6->sin6_family = AF_INET6;
-               sin6->sin6_port = 0;
-               sin6->sin6_flowinfo = 0;
-               sin6->sin6_scope_id = 0;
-               memcpy(&sin6->sin6_addr.s6_addr, dst,
-                      sizeof(sin6->sin6_addr.s6_addr));
-               break;
-#endif
-       default:
+       sa = (u8 *) (rq + 1);
+       if (!pfkey_sockaddr_fill(src, 0, (struct sockaddr *)sa, family) ||
+           !pfkey_sockaddr_fill(dst, 0, (struct sockaddr *)(sa + socklen), family))
                return -EINVAL;
-       }
 
        return 0;
 }
@@ -3520,7 +3405,8 @@ static int set_ipsecrequest(struct sk_buff *skb,
 
 #ifdef CONFIG_NET_KEY_MIGRATE
 static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
-                             struct xfrm_migrate *m, int num_bundles)
+                             struct xfrm_migrate *m, int num_bundles,
+                             struct xfrm_kmaddress *k)
 {
        int i;
        int sasize_sel;
@@ -3537,6 +3423,12 @@ static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
        if (num_bundles <= 0 || num_bundles > XFRM_MAX_DEPTH)
                return -EINVAL;
 
+       if (k != NULL) {
+               /* addresses for KM */
+               size += PFKEY_ALIGN8(sizeof(struct sadb_x_kmaddress) +
+                                    pfkey_sockaddr_pair_size(k->family));
+       }
+
        /* selector */
        sasize_sel = pfkey_sockaddr_size(sel->family);
        if (!sasize_sel)
@@ -3573,6 +3465,10 @@ static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
        hdr->sadb_msg_seq = 0;
        hdr->sadb_msg_pid = 0;
 
+       /* Addresses to be used by KM for negotiation, if ext is available */
+       if (k != NULL && (set_sadb_kmaddress(skb, k) < 0))
+               return -EINVAL;
+
        /* selector src */
        set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_SRC, sel);
 
@@ -3618,7 +3514,8 @@ err:
 }
 #else
 static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
-                             struct xfrm_migrate *m, int num_bundles)
+                             struct xfrm_migrate *m, int num_bundles,
+                             struct xfrm_kmaddress *k)
 {
        return -ENOPROTOOPT;
 }
@@ -3671,6 +3568,7 @@ static int pfkey_recvmsg(struct kiocb *kiocb,
                         int flags)
 {
        struct sock *sk = sock->sk;
+       struct pfkey_sock *pfk = pfkey_sk(sk);
        struct sk_buff *skb;
        int copied, err;
 
@@ -3698,6 +3596,10 @@ static int pfkey_recvmsg(struct kiocb *kiocb,
 
        err = (flags & MSG_TRUNC) ? skb->len : copied;
 
+       if (pfk->dump.dump != NULL &&
+           3 * atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
+               pfkey_do_dump(pfk);
+
 out_free:
        skb_free_datagram(sk, skb);
 out:
@@ -3756,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;
@@ -3764,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;
 
@@ -3773,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);
 }
 
@@ -3840,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);
@@ -3864,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: