netns xfrm: fix "ip xfrm state|policy count" misreport
[safe/jmp/linux-2.6] / net / xfrm / xfrm_policy.c
index dbf9d96..d2c8cb5 100644 (file)
@@ -13,6 +13,7 @@
  *
  */
 
+#include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/kmod.h>
 #include <linux/list.h>
 #include <linux/netfilter.h>
 #include <linux/module.h>
 #include <linux/cache.h>
+#include <linux/audit.h>
+#include <net/dst.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
-#include <linux/audit.h>
+#ifdef CONFIG_XFRM_STATISTICS
+#include <net/snmp.h>
+#endif
 
 #include "xfrm_hash.h"
 
@@ -34,22 +39,20 @@ EXPORT_SYMBOL(xfrm_cfg_mutex);
 
 static DEFINE_RWLOCK(xfrm_policy_lock);
 
-unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];
-EXPORT_SYMBOL(xfrm_policy_count);
-
 static DEFINE_RWLOCK(xfrm_policy_afinfo_lock);
 static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO];
 
 static struct kmem_cache *xfrm_dst_cache __read_mostly;
 
-static struct work_struct xfrm_policy_gc_work;
 static HLIST_HEAD(xfrm_policy_gc_list);
 static DEFINE_SPINLOCK(xfrm_policy_gc_lock);
 
 static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
 static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
-static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family);
-static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo);
+static void xfrm_init_pmtu(struct dst_entry *dst);
+
+static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
+                                               int dir);
 
 static inline int
 __xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
@@ -85,176 +88,54 @@ int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
        return 0;
 }
 
-int xfrm_register_type(struct xfrm_type *type, unsigned short family)
-{
-       struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
-       struct xfrm_type **typemap;
-       int err = 0;
-
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-       typemap = afinfo->type_map;
-
-       if (likely(typemap[type->proto] == NULL))
-               typemap[type->proto] = type;
-       else
-               err = -EEXIST;
-       xfrm_policy_unlock_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_register_type);
-
-int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
-{
-       struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
-       struct xfrm_type **typemap;
-       int err = 0;
-
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-       typemap = afinfo->type_map;
-
-       if (unlikely(typemap[type->proto] != type))
-               err = -ENOENT;
-       else
-               typemap[type->proto] = NULL;
-       xfrm_policy_unlock_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_unregister_type);
-
-struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
+static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos,
+                                                 xfrm_address_t *saddr,
+                                                 xfrm_address_t *daddr,
+                                                 int family)
 {
        struct xfrm_policy_afinfo *afinfo;
-       struct xfrm_type **typemap;
-       struct xfrm_type *type;
-       int modload_attempted = 0;
+       struct dst_entry *dst;
 
-retry:
        afinfo = xfrm_policy_get_afinfo(family);
        if (unlikely(afinfo == NULL))
-               return NULL;
-       typemap = afinfo->type_map;
+               return ERR_PTR(-EAFNOSUPPORT);
 
-       type = typemap[proto];
-       if (unlikely(type && !try_module_get(type->owner)))
-               type = NULL;
-       if (!type && !modload_attempted) {
-               xfrm_policy_put_afinfo(afinfo);
-               request_module("xfrm-type-%d-%d",
-                              (int) family, (int) proto);
-               modload_attempted = 1;
-               goto retry;
-       }
+       dst = afinfo->dst_lookup(net, tos, saddr, daddr);
 
        xfrm_policy_put_afinfo(afinfo);
-       return type;
-}
-
-int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl,
-                   unsigned short family)
-{
-       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
-       int err = 0;
-
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-
-       if (likely(afinfo->dst_lookup != NULL))
-               err = afinfo->dst_lookup(dst, fl);
-       else
-               err = -EINVAL;
-       xfrm_policy_put_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_dst_lookup);
 
-void xfrm_put_type(struct xfrm_type *type)
-{
-       module_put(type->owner);
+       return dst;
 }
 
-int xfrm_register_mode(struct xfrm_mode *mode, int family)
+static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
+                                               xfrm_address_t *prev_saddr,
+                                               xfrm_address_t *prev_daddr,
+                                               int family)
 {
-       struct xfrm_policy_afinfo *afinfo;
-       struct xfrm_mode **modemap;
-       int err;
-
-       if (unlikely(mode->encap >= XFRM_MODE_MAX))
-               return -EINVAL;
-
-       afinfo = xfrm_policy_lock_afinfo(family);
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
+       struct net *net = xs_net(x);
+       xfrm_address_t *saddr = &x->props.saddr;
+       xfrm_address_t *daddr = &x->id.daddr;
+       struct dst_entry *dst;
 
-       err = -EEXIST;
-       modemap = afinfo->mode_map;
-       if (likely(modemap[mode->encap] == NULL)) {
-               modemap[mode->encap] = mode;
-               err = 0;
+       if (x->type->flags & XFRM_TYPE_LOCAL_COADDR) {
+               saddr = x->coaddr;
+               daddr = prev_daddr;
        }
-
-       xfrm_policy_unlock_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_register_mode);
-
-int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
-{
-       struct xfrm_policy_afinfo *afinfo;
-       struct xfrm_mode **modemap;
-       int err;
-
-       if (unlikely(mode->encap >= XFRM_MODE_MAX))
-               return -EINVAL;
-
-       afinfo = xfrm_policy_lock_afinfo(family);
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-
-       err = -ENOENT;
-       modemap = afinfo->mode_map;
-       if (likely(modemap[mode->encap] == mode)) {
-               modemap[mode->encap] = NULL;
-               err = 0;
+       if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) {
+               saddr = prev_saddr;
+               daddr = x->coaddr;
        }
 
-       xfrm_policy_unlock_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_unregister_mode);
-
-struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
-{
-       struct xfrm_policy_afinfo *afinfo;
-       struct xfrm_mode *mode;
-       int modload_attempted = 0;
+       dst = __xfrm_dst_lookup(net, tos, saddr, daddr, family);
 
-       if (unlikely(encap >= XFRM_MODE_MAX))
-               return NULL;
-
-retry:
-       afinfo = xfrm_policy_get_afinfo(family);
-       if (unlikely(afinfo == NULL))
-               return NULL;
-
-       mode = afinfo->mode_map[encap];
-       if (unlikely(mode && !try_module_get(mode->owner)))
-               mode = NULL;
-       if (!mode && !modload_attempted) {
-               xfrm_policy_put_afinfo(afinfo);
-               request_module("xfrm-mode-%d-%d", family, encap);
-               modload_attempted = 1;
-               goto retry;
+       if (!IS_ERR(dst)) {
+               if (prev_saddr != saddr)
+                       memcpy(prev_saddr, saddr,  sizeof(*prev_saddr));
+               if (prev_daddr != daddr)
+                       memcpy(prev_daddr, daddr,  sizeof(*prev_daddr));
        }
 
-       xfrm_policy_put_afinfo(afinfo);
-       return mode;
-}
-
-void xfrm_put_mode(struct xfrm_mode *mode)
-{
-       module_put(mode->owner);
+       return dst;
 }
 
 static inline unsigned long make_jiffies(long secs)
@@ -275,7 +156,7 @@ static void xfrm_policy_timer(unsigned long data)
 
        read_lock(&xp->lock);
 
-       if (xp->dead)
+       if (xp->walk.dead)
                goto out;
 
        dir = xfrm_policy_id2dir(xp->index);
@@ -340,20 +221,21 @@ expired:
  * SPD calls.
  */
 
-struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
+struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
 {
        struct xfrm_policy *policy;
 
        policy = kzalloc(sizeof(struct xfrm_policy), gfp);
 
        if (policy) {
+               write_pnet(&policy->xp_net, net);
+               INIT_LIST_HEAD(&policy->walk.all);
                INIT_HLIST_NODE(&policy->bydst);
                INIT_HLIST_NODE(&policy->byidx);
                rwlock_init(&policy->lock);
                atomic_set(&policy->refcnt, 1);
-               init_timer(&policy->timer);
-               policy->timer.data = (unsigned long)policy;
-               policy->timer.function = xfrm_policy_timer;
+               setup_timer(&policy->timer, xfrm_policy_timer,
+                               (unsigned long)policy);
        }
        return policy;
 }
@@ -361,19 +243,19 @@ EXPORT_SYMBOL(xfrm_policy_alloc);
 
 /* Destroy xfrm_policy: descendant resources must be released to this moment. */
 
-void __xfrm_policy_destroy(struct xfrm_policy *policy)
+void xfrm_policy_destroy(struct xfrm_policy *policy)
 {
-       BUG_ON(!policy->dead);
+       BUG_ON(!policy->walk.dead);
 
        BUG_ON(policy->bundles);
 
        if (del_timer(&policy->timer))
                BUG();
 
-       security_xfrm_policy_free(policy);
+       security_xfrm_policy_free(policy->security);
        kfree(policy);
 }
-EXPORT_SYMBOL(__xfrm_policy_destroy);
+EXPORT_SYMBOL(xfrm_policy_destroy);
 
 static void xfrm_policy_gc_kill(struct xfrm_policy *policy)
 {
@@ -407,6 +289,7 @@ static void xfrm_policy_gc_task(struct work_struct *work)
        hlist_for_each_entry_safe(policy, entry, tmp, &gc_list, bydst)
                xfrm_policy_gc_kill(policy);
 }
+static DECLARE_WORK(xfrm_policy_gc_work, xfrm_policy_gc_task);
 
 /* Rule must be locked. Release descentant resources, announce
  * entry dead. The rule must be unlinked from lists to the moment.
@@ -417,8 +300,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
        int dead;
 
        write_lock_bh(&policy->lock);
-       dead = policy->dead;
-       policy->dead = 1;
+       dead = policy->walk.dead;
+       policy->walk.dead = 1;
        write_unlock_bh(&policy->lock);
 
        if (unlikely(dead)) {
@@ -426,60 +309,67 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
                return;
        }
 
-       spin_lock(&xfrm_policy_gc_lock);
+       spin_lock_bh(&xfrm_policy_gc_lock);
        hlist_add_head(&policy->bydst, &xfrm_policy_gc_list);
-       spin_unlock(&xfrm_policy_gc_lock);
+       spin_unlock_bh(&xfrm_policy_gc_lock);
 
        schedule_work(&xfrm_policy_gc_work);
 }
 
-struct xfrm_policy_hash {
-       struct hlist_head       *table;
-       unsigned int            hmask;
-};
-
-static struct hlist_head xfrm_policy_inexact[XFRM_POLICY_MAX*2];
-static struct xfrm_policy_hash xfrm_policy_bydst[XFRM_POLICY_MAX*2] __read_mostly;
-static struct hlist_head *xfrm_policy_byidx __read_mostly;
-static unsigned int xfrm_idx_hmask __read_mostly;
 static unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024;
 
-static inline unsigned int idx_hash(u32 index)
+static inline unsigned int idx_hash(struct net *net, u32 index)
 {
-       return __idx_hash(index, xfrm_idx_hmask);
+       return __idx_hash(index, net->xfrm.policy_idx_hmask);
 }
 
-static struct hlist_head *policy_hash_bysel(struct xfrm_selector *sel, unsigned short family, int dir)
+static struct hlist_head *policy_hash_bysel(struct net *net, struct xfrm_selector *sel, unsigned short family, int dir)
 {
-       unsigned int hmask = xfrm_policy_bydst[dir].hmask;
+       unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
        unsigned int hash = __sel_hash(sel, family, hmask);
 
        return (hash == hmask + 1 ?
-               &xfrm_policy_inexact[dir] :
-               xfrm_policy_bydst[dir].table + hash);
+               &net->xfrm.policy_inexact[dir] :
+               net->xfrm.policy_bydst[dir].table + hash);
 }
 
-static struct hlist_head *policy_hash_direct(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, int dir)
+static struct hlist_head *policy_hash_direct(struct net *net, xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, int dir)
 {
-       unsigned int hmask = xfrm_policy_bydst[dir].hmask;
+       unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
        unsigned int hash = __addr_hash(daddr, saddr, family, hmask);
 
-       return xfrm_policy_bydst[dir].table + hash;
+       return net->xfrm.policy_bydst[dir].table + hash;
 }
 
 static void xfrm_dst_hash_transfer(struct hlist_head *list,
                                   struct hlist_head *ndsttable,
                                   unsigned int nhashmask)
 {
-       struct hlist_node *entry, *tmp;
+       struct hlist_node *entry, *tmp, *entry0 = NULL;
        struct xfrm_policy *pol;
+       unsigned int h0 = 0;
 
+redo:
        hlist_for_each_entry_safe(pol, entry, tmp, list, bydst) {
                unsigned int h;
 
                h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
                                pol->family, nhashmask);
-               hlist_add_head(&pol->bydst, ndsttable+h);
+               if (!entry0) {
+                       hlist_del(entry);
+                       hlist_add_head(&pol->bydst, ndsttable+h);
+                       h0 = h;
+               } else {
+                       if (h != h0)
+                               continue;
+                       hlist_del(entry);
+                       hlist_add_after(entry0, &pol->bydst);
+               }
+               entry0 = entry;
+       }
+       if (!hlist_empty(list)) {
+               entry0 = NULL;
+               goto redo;
        }
 }
 
@@ -503,12 +393,12 @@ static unsigned long xfrm_new_hash_mask(unsigned int old_hmask)
        return ((old_hmask + 1) << 1) - 1;
 }
 
-static void xfrm_bydst_resize(int dir)
+static void xfrm_bydst_resize(struct net *net, int dir)
 {
-       unsigned int hmask = xfrm_policy_bydst[dir].hmask;
+       unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
        unsigned int nhashmask = xfrm_new_hash_mask(hmask);
        unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
-       struct hlist_head *odst = xfrm_policy_bydst[dir].table;
+       struct hlist_head *odst = net->xfrm.policy_bydst[dir].table;
        struct hlist_head *ndst = xfrm_hash_alloc(nsize);
        int i;
 
@@ -520,20 +410,20 @@ static void xfrm_bydst_resize(int dir)
        for (i = hmask; i >= 0; i--)
                xfrm_dst_hash_transfer(odst + i, ndst, nhashmask);
 
-       xfrm_policy_bydst[dir].table = ndst;
-       xfrm_policy_bydst[dir].hmask = nhashmask;
+       net->xfrm.policy_bydst[dir].table = ndst;
+       net->xfrm.policy_bydst[dir].hmask = nhashmask;
 
        write_unlock_bh(&xfrm_policy_lock);
 
        xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));
 }
 
-static void xfrm_byidx_resize(int total)
+static void xfrm_byidx_resize(struct net *net, int total)
 {
-       unsigned int hmask = xfrm_idx_hmask;
+       unsigned int hmask = net->xfrm.policy_idx_hmask;
        unsigned int nhashmask = xfrm_new_hash_mask(hmask);
        unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
-       struct hlist_head *oidx = xfrm_policy_byidx;
+       struct hlist_head *oidx = net->xfrm.policy_byidx;
        struct hlist_head *nidx = xfrm_hash_alloc(nsize);
        int i;
 
@@ -545,18 +435,18 @@ static void xfrm_byidx_resize(int total)
        for (i = hmask; i >= 0; i--)
                xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask);
 
-       xfrm_policy_byidx = nidx;
-       xfrm_idx_hmask = nhashmask;
+       net->xfrm.policy_byidx = nidx;
+       net->xfrm.policy_idx_hmask = nhashmask;
 
        write_unlock_bh(&xfrm_policy_lock);
 
        xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));
 }
 
-static inline int xfrm_bydst_should_resize(int dir, int *total)
+static inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total)
 {
-       unsigned int cnt = xfrm_policy_count[dir];
-       unsigned int hmask = xfrm_policy_bydst[dir].hmask;
+       unsigned int cnt = net->xfrm.policy_count[dir];
+       unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
 
        if (total)
                *total += cnt;
@@ -568,9 +458,9 @@ static inline int xfrm_bydst_should_resize(int dir, int *total)
        return 0;
 }
 
-static inline int xfrm_byidx_should_resize(int total)
+static inline int xfrm_byidx_should_resize(struct net *net, int total)
 {
-       unsigned int hmask = xfrm_idx_hmask;
+       unsigned int hmask = net->xfrm.policy_idx_hmask;
 
        if ((hmask + 1) < xfrm_policy_hashmax &&
            total > hmask)
@@ -579,44 +469,43 @@ static inline int xfrm_byidx_should_resize(int total)
        return 0;
 }
 
-void xfrm_spd_getinfo(struct xfrm_spdinfo *si)
+void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si)
 {
        read_lock_bh(&xfrm_policy_lock);
-       si->incnt = xfrm_policy_count[XFRM_POLICY_IN];
-       si->outcnt = xfrm_policy_count[XFRM_POLICY_OUT];
-       si->fwdcnt = xfrm_policy_count[XFRM_POLICY_FWD];
-       si->inscnt = xfrm_policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
-       si->outscnt = xfrm_policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
-       si->fwdscnt = xfrm_policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
-       si->spdhcnt = xfrm_idx_hmask;
+       si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN];
+       si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT];
+       si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD];
+       si->inscnt = net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
+       si->outscnt = net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
+       si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
+       si->spdhcnt = net->xfrm.policy_idx_hmask;
        si->spdhmcnt = xfrm_policy_hashmax;
        read_unlock_bh(&xfrm_policy_lock);
 }
 EXPORT_SYMBOL(xfrm_spd_getinfo);
 
 static DEFINE_MUTEX(hash_resize_mutex);
-static void xfrm_hash_resize(struct work_struct *__unused)
+static void xfrm_hash_resize(struct work_struct *work)
 {
+       struct net *net = container_of(work, struct net, xfrm.policy_hash_work);
        int dir, total;
 
        mutex_lock(&hash_resize_mutex);
 
        total = 0;
        for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
-               if (xfrm_bydst_should_resize(dir, &total))
-                       xfrm_bydst_resize(dir);
+               if (xfrm_bydst_should_resize(net, dir, &total))
+                       xfrm_bydst_resize(net, dir);
        }
-       if (xfrm_byidx_should_resize(total))
-               xfrm_byidx_resize(total);
+       if (xfrm_byidx_should_resize(net, total))
+               xfrm_byidx_resize(net, total);
 
        mutex_unlock(&hash_resize_mutex);
 }
 
-static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
-
 /* Generate new index... KAME seems to generate them ordered by cost
  * of an absolute inpredictability of ordering of rules. This will not pass. */
-static u32 xfrm_gen_index(u8 type, int dir)
+static u32 xfrm_gen_index(struct net *net, int dir)
 {
        static u32 idx_generator;
 
@@ -631,7 +520,7 @@ static u32 xfrm_gen_index(u8 type, int dir)
                idx_generator += 8;
                if (idx == 0)
                        idx = 8;
-               list = xfrm_policy_byidx + idx_hash(idx);
+               list = net->xfrm.policy_byidx + idx_hash(net, idx);
                found = 0;
                hlist_for_each_entry(p, entry, list, byidx) {
                        if (p->index == idx) {
@@ -661,6 +550,7 @@ static inline int selector_cmp(struct xfrm_selector *s1, struct xfrm_selector *s
 
 int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
 {
+       struct net *net = xp_net(policy);
        struct xfrm_policy *pol;
        struct xfrm_policy *delpol;
        struct hlist_head *chain;
@@ -668,7 +558,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
        struct dst_entry *gc_list;
 
        write_lock_bh(&xfrm_policy_lock);
-       chain = policy_hash_bysel(&policy->selector, policy->family, dir);
+       chain = policy_hash_bysel(net, &policy->selector, policy->family, dir);
        delpol = NULL;
        newpos = NULL;
        hlist_for_each_entry(pol, entry, chain, bydst) {
@@ -695,25 +585,23 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
        else
                hlist_add_head(&policy->bydst, chain);
        xfrm_pol_hold(policy);
-       xfrm_policy_count[dir]++;
+       net->xfrm.policy_count[dir]++;
        atomic_inc(&flow_cache_genid);
-       if (delpol) {
-               hlist_del(&delpol->bydst);
-               hlist_del(&delpol->byidx);
-               xfrm_policy_count[dir]--;
-       }
-       policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);
-       hlist_add_head(&policy->byidx, xfrm_policy_byidx+idx_hash(policy->index));
+       if (delpol)
+               __xfrm_policy_unlink(delpol, dir);
+       policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir);
+       hlist_add_head(&policy->byidx, net->xfrm.policy_byidx+idx_hash(net, policy->index));
        policy->curlft.add_time = get_seconds();
        policy->curlft.use_time = 0;
        if (!mod_timer(&policy->timer, jiffies + HZ))
                xfrm_pol_hold(policy);
+       list_add(&policy->walk.all, &net->xfrm.policy_all);
        write_unlock_bh(&xfrm_policy_lock);
 
        if (delpol)
                xfrm_policy_kill(delpol);
-       else if (xfrm_bydst_should_resize(dir, NULL))
-               schedule_work(&xfrm_hash_work);
+       else if (xfrm_bydst_should_resize(net, dir, NULL))
+               schedule_work(&net->xfrm.policy_hash_work);
 
        read_lock_bh(&xfrm_policy_lock);
        gc_list = NULL;
@@ -747,7 +635,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
 }
 EXPORT_SYMBOL(xfrm_policy_insert);
 
-struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
+struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u8 type, int dir,
                                          struct xfrm_selector *sel,
                                          struct xfrm_sec_ctx *ctx, int delete,
                                          int *err)
@@ -758,7 +646,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
 
        *err = 0;
        write_lock_bh(&xfrm_policy_lock);
-       chain = policy_hash_bysel(sel, sel->family, dir);
+       chain = policy_hash_bysel(net, sel, sel->family, dir);
        ret = NULL;
        hlist_for_each_entry(pol, entry, chain, bydst) {
                if (pol->type == type &&
@@ -766,14 +654,13 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
                    xfrm_sec_ctx_match(ctx, pol->security)) {
                        xfrm_pol_hold(pol);
                        if (delete) {
-                               *err = security_xfrm_policy_delete(pol);
+                               *err = security_xfrm_policy_delete(
+                                                               pol->security);
                                if (*err) {
                                        write_unlock_bh(&xfrm_policy_lock);
                                        return pol;
                                }
-                               hlist_del(&pol->bydst);
-                               hlist_del(&pol->byidx);
-                               xfrm_policy_count[dir]--;
+                               __xfrm_policy_unlink(pol, dir);
                        }
                        ret = pol;
                        break;
@@ -789,29 +676,32 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
 }
 EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
 
-struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete,
-                                    int *err)
+struct xfrm_policy *xfrm_policy_byid(struct net *net, u8 type, int dir, u32 id,
+                                    int delete, int *err)
 {
        struct xfrm_policy *pol, *ret;
        struct hlist_head *chain;
        struct hlist_node *entry;
 
+       *err = -ENOENT;
+       if (xfrm_policy_id2dir(id) != dir)
+               return NULL;
+
        *err = 0;
        write_lock_bh(&xfrm_policy_lock);
-       chain = xfrm_policy_byidx + idx_hash(id);
+       chain = net->xfrm.policy_byidx + idx_hash(net, id);
        ret = NULL;
        hlist_for_each_entry(pol, entry, chain, byidx) {
                if (pol->type == type && pol->index == id) {
                        xfrm_pol_hold(pol);
                        if (delete) {
-                               *err = security_xfrm_policy_delete(pol);
+                               *err = security_xfrm_policy_delete(
+                                                               pol->security);
                                if (*err) {
                                        write_unlock_bh(&xfrm_policy_lock);
                                        return pol;
                                }
-                               hlist_del(&pol->bydst);
-                               hlist_del(&pol->byidx);
-                               xfrm_policy_count[dir]--;
+                               __xfrm_policy_unlink(pol, dir);
                        }
                        ret = pol;
                        break;
@@ -827,122 +717,186 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete,
 }
 EXPORT_SYMBOL(xfrm_policy_byid);
 
-void xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+static inline int
+xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info)
 {
-       int dir;
+       int dir, err = 0;
+
+       for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
+               struct xfrm_policy *pol;
+               struct hlist_node *entry;
+               int i;
+
+               hlist_for_each_entry(pol, entry,
+                                    &net->xfrm.policy_inexact[dir], bydst) {
+                       if (pol->type != type)
+                               continue;
+                       err = security_xfrm_policy_delete(pol->security);
+                       if (err) {
+                               xfrm_audit_policy_delete(pol, 0,
+                                                        audit_info->loginuid,
+                                                        audit_info->sessionid,
+                                                        audit_info->secid);
+                               return err;
+                       }
+               }
+               for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
+                       hlist_for_each_entry(pol, entry,
+                                            net->xfrm.policy_bydst[dir].table + i,
+                                            bydst) {
+                               if (pol->type != type)
+                                       continue;
+                               err = security_xfrm_policy_delete(
+                                                               pol->security);
+                               if (err) {
+                                       xfrm_audit_policy_delete(pol, 0,
+                                                       audit_info->loginuid,
+                                                       audit_info->sessionid,
+                                                       audit_info->secid);
+                                       return err;
+                               }
+                       }
+               }
+       }
+       return err;
+}
+#else
+static inline int
+xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info)
+{
+       return 0;
+}
+#endif
+
+int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
+{
+       int dir, err = 0;
 
        write_lock_bh(&xfrm_policy_lock);
+
+       err = xfrm_policy_flush_secctx_check(net, type, audit_info);
+       if (err)
+               goto out;
+
        for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
                struct xfrm_policy *pol;
                struct hlist_node *entry;
-               int i, killed;
+               int i;
 
-               killed = 0;
        again1:
                hlist_for_each_entry(pol, entry,
-                                    &xfrm_policy_inexact[dir], bydst) {
+                                    &net->xfrm.policy_inexact[dir], bydst) {
                        if (pol->type != type)
                                continue;
-                       hlist_del(&pol->bydst);
-                       hlist_del(&pol->byidx);
+                       __xfrm_policy_unlink(pol, dir);
                        write_unlock_bh(&xfrm_policy_lock);
 
-                       xfrm_audit_log(audit_info->loginuid, audit_info->secid,
-                                      AUDIT_MAC_IPSEC_DELSPD, 1, pol, NULL);
+                       xfrm_audit_policy_delete(pol, 1, audit_info->loginuid,
+                                                audit_info->sessionid,
+                                                audit_info->secid);
 
                        xfrm_policy_kill(pol);
-                       killed++;
 
                        write_lock_bh(&xfrm_policy_lock);
                        goto again1;
                }
 
-               for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
+               for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
        again2:
                        hlist_for_each_entry(pol, entry,
-                                            xfrm_policy_bydst[dir].table + i,
+                                            net->xfrm.policy_bydst[dir].table + i,
                                             bydst) {
                                if (pol->type != type)
                                        continue;
-                               hlist_del(&pol->bydst);
-                               hlist_del(&pol->byidx);
+                               __xfrm_policy_unlink(pol, dir);
                                write_unlock_bh(&xfrm_policy_lock);
 
-                               xfrm_audit_log(audit_info->loginuid,
-                                              audit_info->secid,
-                                              AUDIT_MAC_IPSEC_DELSPD, 1,
-                                              pol, NULL);
-
+                               xfrm_audit_policy_delete(pol, 1,
+                                                        audit_info->loginuid,
+                                                        audit_info->sessionid,
+                                                        audit_info->secid);
                                xfrm_policy_kill(pol);
-                               killed++;
 
                                write_lock_bh(&xfrm_policy_lock);
                                goto again2;
                        }
                }
 
-               xfrm_policy_count[dir] -= killed;
        }
        atomic_inc(&flow_cache_genid);
+out:
        write_unlock_bh(&xfrm_policy_lock);
+       return err;
 }
 EXPORT_SYMBOL(xfrm_policy_flush);
 
-int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*),
+int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
+                    int (*func)(struct xfrm_policy *, int, int, void*),
                     void *data)
 {
-       struct xfrm_policy *pol, *last = NULL;
-       struct hlist_node *entry;
-       int dir, last_dir = 0, count, error;
+       struct xfrm_policy *pol;
+       struct xfrm_policy_walk_entry *x;
+       int error = 0;
 
-       read_lock_bh(&xfrm_policy_lock);
-       count = 0;
+       if (walk->type >= XFRM_POLICY_TYPE_MAX &&
+           walk->type != XFRM_POLICY_TYPE_ANY)
+               return -EINVAL;
 
-       for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) {
-               struct hlist_head *table = xfrm_policy_bydst[dir].table;
-               int i;
+       if (list_empty(&walk->walk.all) && walk->seq != 0)
+               return 0;
 
-               hlist_for_each_entry(pol, entry,
-                                    &xfrm_policy_inexact[dir], bydst) {
-                       if (pol->type != type)
-                               continue;
-                       if (last) {
-                               error = func(last, last_dir % XFRM_POLICY_MAX,
-                                            count, data);
-                               if (error)
-                                       goto out;
-                       }
-                       last = pol;
-                       last_dir = dir;
-                       count++;
-               }
-               for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
-                       hlist_for_each_entry(pol, entry, table + i, bydst) {
-                               if (pol->type != type)
-                                       continue;
-                               if (last) {
-                                       error = func(last, last_dir % XFRM_POLICY_MAX,
-                                                    count, data);
-                                       if (error)
-                                               goto out;
-                               }
-                               last = pol;
-                               last_dir = dir;
-                               count++;
-                       }
+       write_lock_bh(&xfrm_policy_lock);
+       if (list_empty(&walk->walk.all))
+               x = list_first_entry(&net->xfrm.policy_all, struct xfrm_policy_walk_entry, all);
+       else
+               x = list_entry(&walk->walk.all, struct xfrm_policy_walk_entry, all);
+       list_for_each_entry_from(x, &net->xfrm.policy_all, all) {
+               if (x->dead)
+                       continue;
+               pol = container_of(x, struct xfrm_policy, walk);
+               if (walk->type != XFRM_POLICY_TYPE_ANY &&
+                   walk->type != pol->type)
+                       continue;
+               error = func(pol, xfrm_policy_id2dir(pol->index),
+                            walk->seq, data);
+               if (error) {
+                       list_move_tail(&walk->walk.all, &x->all);
+                       goto out;
                }
+               walk->seq++;
        }
-       if (count == 0) {
+       if (walk->seq == 0) {
                error = -ENOENT;
                goto out;
        }
-       error = func(last, last_dir % XFRM_POLICY_MAX, 0, data);
+       list_del_init(&walk->walk.all);
 out:
-       read_unlock_bh(&xfrm_policy_lock);
+       write_unlock_bh(&xfrm_policy_lock);
        return error;
 }
 EXPORT_SYMBOL(xfrm_policy_walk);
 
+void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type)
+{
+       INIT_LIST_HEAD(&walk->walk.all);
+       walk->walk.dead = 1;
+       walk->type = type;
+       walk->seq = 0;
+}
+EXPORT_SYMBOL(xfrm_policy_walk_init);
+
+void xfrm_policy_walk_done(struct xfrm_policy_walk *walk)
+{
+       if (list_empty(&walk->walk.all))
+               return;
+
+       write_lock_bh(&xfrm_policy_lock);
+       list_del(&walk->walk.all);
+       write_unlock_bh(&xfrm_policy_lock);
+}
+EXPORT_SYMBOL(xfrm_policy_walk_done);
+
 /*
  * Find policy to apply to this flow.
  *
@@ -960,12 +914,14 @@ static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
 
        match = xfrm_selector_match(sel, fl, family);
        if (match)
-               ret = security_xfrm_policy_lookup(pol, fl->secid, dir);
+               ret = security_xfrm_policy_lookup(pol->security, fl->secid,
+                                                 dir);
 
        return ret;
 }
 
-static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
+static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
+                                                    struct flowi *fl,
                                                     u16 family, u8 dir)
 {
        int err;
@@ -981,7 +937,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
                return NULL;
 
        read_lock_bh(&xfrm_policy_lock);
-       chain = policy_hash_direct(daddr, saddr, family, dir);
+       chain = policy_hash_direct(net, daddr, saddr, family, dir);
        ret = NULL;
        hlist_for_each_entry(pol, entry, chain, bydst) {
                err = xfrm_policy_match(pol, fl, type, family, dir);
@@ -998,7 +954,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
                        break;
                }
        }
-       chain = &xfrm_policy_inexact[dir];
+       chain = &net->xfrm.policy_inexact[dir];
        hlist_for_each_entry(pol, entry, chain, bydst) {
                err = xfrm_policy_match(pol, fl, type, family, dir);
                if (err) {
@@ -1021,14 +977,14 @@ fail:
        return ret;
 }
 
-static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
-                              void **objp, atomic_t **obj_refp)
+static int xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family,
+                             u8 dir, void **objp, atomic_t **obj_refp)
 {
        struct xfrm_policy *pol;
        int err = 0;
 
 #ifdef CONFIG_XFRM_SUB_POLICY
-       pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir);
+       pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir);
        if (IS_ERR(pol)) {
                err = PTR_ERR(pol);
                pol = NULL;
@@ -1036,7 +992,7 @@ static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
        if (pol || err)
                goto end;
 #endif
-       pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir);
+       pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
        if (IS_ERR(pol)) {
                err = PTR_ERR(pol);
                pol = NULL;
@@ -1077,8 +1033,9 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc
                int err = 0;
 
                if (match) {
-                       err = security_xfrm_policy_lookup(pol, fl->secid,
-                                       policy_to_flow_dir(dir));
+                       err = security_xfrm_policy_lookup(pol->security,
+                                                     fl->secid,
+                                                     policy_to_flow_dir(dir));
                        if (!err)
                                xfrm_pol_hold(pol);
                        else if (err == -ESRCH)
@@ -1094,27 +1051,32 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc
 
 static void __xfrm_policy_link(struct xfrm_policy *pol, int dir)
 {
-       struct hlist_head *chain = policy_hash_bysel(&pol->selector,
+       struct net *net = xp_net(pol);
+       struct hlist_head *chain = policy_hash_bysel(net, &pol->selector,
                                                     pol->family, dir);
 
+       list_add(&pol->walk.all, &net->xfrm.policy_all);
        hlist_add_head(&pol->bydst, chain);
-       hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index));
-       xfrm_policy_count[dir]++;
+       hlist_add_head(&pol->byidx, net->xfrm.policy_byidx+idx_hash(net, pol->index));
+       net->xfrm.policy_count[dir]++;
        xfrm_pol_hold(pol);
 
-       if (xfrm_bydst_should_resize(dir, NULL))
-               schedule_work(&xfrm_hash_work);
+       if (xfrm_bydst_should_resize(net, dir, NULL))
+               schedule_work(&net->xfrm.policy_hash_work);
 }
 
 static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
                                                int dir)
 {
+       struct net *net = xp_net(pol);
+
        if (hlist_unhashed(&pol->bydst))
                return NULL;
 
        hlist_del(&pol->bydst);
        hlist_del(&pol->byidx);
-       xfrm_policy_count[dir]--;
+       list_del(&pol->walk.all);
+       net->xfrm.policy_count[dir]--;
 
        return pol;
 }
@@ -1136,6 +1098,7 @@ EXPORT_SYMBOL(xfrm_policy_delete);
 
 int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
 {
+       struct net *net = xp_net(pol);
        struct xfrm_policy *old_pol;
 
 #ifdef CONFIG_XFRM_SUB_POLICY
@@ -1148,7 +1111,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
        sk->sk_policy[dir] = pol;
        if (pol) {
                pol->curlft.add_time = get_seconds();
-               pol->index = xfrm_gen_index(pol->type, XFRM_POLICY_MAX+dir);
+               pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir);
                __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir);
        }
        if (old_pol)
@@ -1163,11 +1126,12 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
 
 static struct xfrm_policy *clone_policy(struct xfrm_policy *old, int dir)
 {
-       struct xfrm_policy *newp = xfrm_policy_alloc(GFP_ATOMIC);
+       struct xfrm_policy *newp = xfrm_policy_alloc(xp_net(old), GFP_ATOMIC);
 
        if (newp) {
                newp->selector = old->selector;
-               if (security_xfrm_policy_clone(old, newp)) {
+               if (security_xfrm_policy_clone(old->security,
+                                              &newp->security)) {
                        kfree(newp);
                        return NULL;  /* ENOMEM */
                }
@@ -1202,7 +1166,7 @@ int __xfrm_sk_clone_policy(struct sock *sk)
 }
 
 static int
-xfrm_get_saddr(xfrm_address_t *local, xfrm_address_t *remote,
+xfrm_get_saddr(struct net *net, xfrm_address_t *local, xfrm_address_t *remote,
               unsigned short family)
 {
        int err;
@@ -1210,7 +1174,7 @@ xfrm_get_saddr(xfrm_address_t *local, xfrm_address_t *remote,
 
        if (unlikely(afinfo == NULL))
                return -EINVAL;
-       err = afinfo->get_saddr(local, remote);
+       err = afinfo->get_saddr(net, local, remote);
        xfrm_policy_put_afinfo(afinfo);
        return err;
 }
@@ -1222,6 +1186,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
                      struct xfrm_state **xfrm,
                      unsigned short family)
 {
+       struct net *net = xp_net(policy);
        int nx;
        int i, error;
        xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
@@ -1234,12 +1199,13 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
                xfrm_address_t *local  = saddr;
                struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
 
-               if (tmpl->mode == XFRM_MODE_TUNNEL) {
+               if (tmpl->mode == XFRM_MODE_TUNNEL ||
+                   tmpl->mode == XFRM_MODE_BEET) {
                        remote = &tmpl->id.daddr;
                        local = &tmpl->saddr;
                        family = tmpl->encap_family;
                        if (xfrm_addr_any(local, family)) {
-                               error = xfrm_get_saddr(&tmp, remote, family);
+                               error = xfrm_get_saddr(net, &tmp, remote, family);
                                if (error)
                                        goto fail;
                                local = &tmp;
@@ -1259,6 +1225,8 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
                                 -EINVAL : -EAGAIN);
                        xfrm_state_put(x);
                }
+               else if (error == -ESRCH)
+                       error = -EAGAIN;
 
                if (!tmpl->optional)
                        goto fail;
@@ -1326,24 +1294,223 @@ xfrm_find_bundle(struct flowi *fl, struct xfrm_policy *policy, unsigned short fa
        return x;
 }
 
-/* Allocate chain of dst_entry's, attach known xfrm's, calculate
- * all the metrics... Shortly, bundle a bundle.
- */
+static inline int xfrm_get_tos(struct flowi *fl, int family)
+{
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+       int tos;
 
-static int
-xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
-                  struct flowi *fl, struct dst_entry **dst_p,
-                  unsigned short family)
+       if (!afinfo)
+               return -EINVAL;
+
+       tos = afinfo->get_tos(fl);
+
+       xfrm_policy_put_afinfo(afinfo);
+
+       return tos;
+}
+
+static inline struct xfrm_dst *xfrm_alloc_dst(int family)
 {
-       int err;
        struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
-       if (unlikely(afinfo == NULL))
+       struct xfrm_dst *xdst;
+
+       if (!afinfo)
+               return ERR_PTR(-EINVAL);
+
+       xdst = dst_alloc(afinfo->dst_ops) ?: ERR_PTR(-ENOBUFS);
+
+       xfrm_policy_put_afinfo(afinfo);
+
+       return xdst;
+}
+
+static inline int xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst,
+                                int nfheader_len)
+{
+       struct xfrm_policy_afinfo *afinfo =
+               xfrm_policy_get_afinfo(dst->ops->family);
+       int err;
+
+       if (!afinfo)
                return -EINVAL;
-       err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p);
+
+       err = afinfo->init_path(path, dst, nfheader_len);
+
        xfrm_policy_put_afinfo(afinfo);
+
        return err;
 }
 
+static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev)
+{
+       struct xfrm_policy_afinfo *afinfo =
+               xfrm_policy_get_afinfo(xdst->u.dst.ops->family);
+       int err;
+
+       if (!afinfo)
+               return -EINVAL;
+
+       err = afinfo->fill_dst(xdst, dev);
+
+       xfrm_policy_put_afinfo(afinfo);
+
+       return err;
+}
+
+/* Allocate chain of dst_entry's, attach known xfrm's, calculate
+ * all the metrics... Shortly, bundle a bundle.
+ */
+
+static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+                                           struct xfrm_state **xfrm, int nx,
+                                           struct flowi *fl,
+                                           struct dst_entry *dst)
+{
+       unsigned long now = jiffies;
+       struct net_device *dev;
+       struct dst_entry *dst_prev = NULL;
+       struct dst_entry *dst0 = NULL;
+       int i = 0;
+       int err;
+       int header_len = 0;
+       int nfheader_len = 0;
+       int trailer_len = 0;
+       int tos;
+       int family = policy->selector.family;
+       xfrm_address_t saddr, daddr;
+
+       xfrm_flowi_addr_get(fl, &saddr, &daddr, family);
+
+       tos = xfrm_get_tos(fl, family);
+       err = tos;
+       if (tos < 0)
+               goto put_states;
+
+       dst_hold(dst);
+
+       for (; i < nx; i++) {
+               struct xfrm_dst *xdst = xfrm_alloc_dst(family);
+               struct dst_entry *dst1 = &xdst->u.dst;
+
+               err = PTR_ERR(xdst);
+               if (IS_ERR(xdst)) {
+                       dst_release(dst);
+                       goto put_states;
+               }
+
+               if (!dst_prev)
+                       dst0 = dst1;
+               else {
+                       dst_prev->child = dst_clone(dst1);
+                       dst1->flags |= DST_NOHASH;
+               }
+
+               xdst->route = dst;
+               memcpy(&dst1->metrics, &dst->metrics, sizeof(dst->metrics));
+
+               if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
+                       family = xfrm[i]->props.family;
+                       dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr,
+                                             family);
+                       err = PTR_ERR(dst);
+                       if (IS_ERR(dst))
+                               goto put_states;
+               } else
+                       dst_hold(dst);
+
+               dst1->xfrm = xfrm[i];
+               xdst->genid = xfrm[i]->genid;
+
+               dst1->obsolete = -1;
+               dst1->flags |= DST_HOST;
+               dst1->lastuse = now;
+
+               dst1->input = dst_discard;
+               dst1->output = xfrm[i]->outer_mode->afinfo->output;
+
+               dst1->next = dst_prev;
+               dst_prev = dst1;
+
+               header_len += xfrm[i]->props.header_len;
+               if (xfrm[i]->type->flags & XFRM_TYPE_NON_FRAGMENT)
+                       nfheader_len += xfrm[i]->props.header_len;
+               trailer_len += xfrm[i]->props.trailer_len;
+       }
+
+       dst_prev->child = dst;
+       dst0->path = dst;
+
+       err = -ENODEV;
+       dev = dst->dev;
+       if (!dev)
+               goto free_dst;
+
+       /* Copy neighbour for reachability confirmation */
+       dst0->neighbour = neigh_clone(dst->neighbour);
+
+       xfrm_init_path((struct xfrm_dst *)dst0, dst, nfheader_len);
+       xfrm_init_pmtu(dst_prev);
+
+       for (dst_prev = dst0; dst_prev != dst; dst_prev = dst_prev->child) {
+               struct xfrm_dst *xdst = (struct xfrm_dst *)dst_prev;
+
+               err = xfrm_fill_dst(xdst, dev);
+               if (err)
+                       goto free_dst;
+
+               dst_prev->header_len = header_len;
+               dst_prev->trailer_len = trailer_len;
+               header_len -= xdst->u.dst.xfrm->props.header_len;
+               trailer_len -= xdst->u.dst.xfrm->props.trailer_len;
+       }
+
+out:
+       return dst0;
+
+put_states:
+       for (; i < nx; i++)
+               xfrm_state_put(xfrm[i]);
+free_dst:
+       if (dst0)
+               dst_free(dst0);
+       dst0 = ERR_PTR(err);
+       goto out;
+}
+
+static int inline
+xfrm_dst_alloc_copy(void **target, void *src, int size)
+{
+       if (!*target) {
+               *target = kmalloc(size, GFP_ATOMIC);
+               if (!*target)
+                       return -ENOMEM;
+       }
+       memcpy(*target, src, size);
+       return 0;
+}
+
+static int inline
+xfrm_dst_update_parent(struct dst_entry *dst, struct xfrm_selector *sel)
+{
+#ifdef CONFIG_XFRM_SUB_POLICY
+       struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
+       return xfrm_dst_alloc_copy((void **)&(xdst->partner),
+                                  sel, sizeof(*sel));
+#else
+       return 0;
+#endif
+}
+
+static int inline
+xfrm_dst_update_origin(struct dst_entry *dst, struct flowi *fl)
+{
+#ifdef CONFIG_XFRM_SUB_POLICY
+       struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
+       return xfrm_dst_alloc_copy((void **)&(xdst->origin), fl, sizeof(*fl));
+#else
+       return 0;
+#endif
+}
 
 static int stale_bundle(struct dst_entry *dst);
 
@@ -1352,8 +1519,8 @@ static int stale_bundle(struct dst_entry *dst);
  * At the moment we eat a raw IP route. Mostly to speed up lookups
  * on interfaces with disabled IPsec.
  */
-int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
-               struct sock *sk, int flags)
+int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl,
+                 struct sock *sk, int flags)
 {
        struct xfrm_policy *policy;
        struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
@@ -1378,36 +1545,49 @@ restart:
        pol_dead = 0;
        xfrm_nr = 0;
 
-       if (sk && sk->sk_policy[1]) {
+       if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
                policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
-               if (IS_ERR(policy))
-                       return PTR_ERR(policy);
+               err = PTR_ERR(policy);
+               if (IS_ERR(policy)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
+                       goto dropdst;
+               }
        }
 
        if (!policy) {
                /* To accelerate a bit...  */
                if ((dst_orig->flags & DST_NOXFRM) ||
-                   !xfrm_policy_count[XFRM_POLICY_OUT])
-                       return 0;
+                   !net->xfrm.policy_count[XFRM_POLICY_OUT])
+                       goto nopol;
 
-               policy = flow_cache_lookup(fl, dst_orig->ops->family,
+               policy = flow_cache_lookup(net, fl, dst_orig->ops->family,
                                           dir, xfrm_policy_lookup);
-               if (IS_ERR(policy))
-                       return PTR_ERR(policy);
+               err = PTR_ERR(policy);
+               if (IS_ERR(policy)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
+                       goto dropdst;
+               }
        }
 
        if (!policy)
-               return 0;
+               goto nopol;
 
        family = dst_orig->ops->family;
-       policy->curlft.use_time = get_seconds();
        pols[0] = policy;
        npols ++;
        xfrm_nr += pols[0]->xfrm_nr;
 
+       err = -ENOENT;
+       if ((flags & XFRM_LOOKUP_ICMP) && !(policy->flags & XFRM_POLICY_ICMP))
+               goto error;
+
+       policy->curlft.use_time = get_seconds();
+
        switch (policy->action) {
+       default:
        case XFRM_POLICY_BLOCK:
                /* Prohibit the flow */
+               XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLBLOCK);
                err = -EPERM;
                goto error;
 
@@ -1427,6 +1607,7 @@ restart:
                 */
                dst = xfrm_find_bundle(fl, policy, family);
                if (IS_ERR(dst)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
                        err = PTR_ERR(dst);
                        goto error;
                }
@@ -1436,15 +1617,18 @@ restart:
 
 #ifdef CONFIG_XFRM_SUB_POLICY
                if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
-                       pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
+                       pols[1] = xfrm_policy_lookup_bytype(net,
+                                                           XFRM_POLICY_TYPE_MAIN,
                                                            fl, family,
                                                            XFRM_POLICY_OUT);
                        if (pols[1]) {
                                if (IS_ERR(pols[1])) {
+                                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
                                        err = PTR_ERR(pols[1]);
                                        goto error;
                                }
                                if (pols[1]->action == XFRM_POLICY_BLOCK) {
+                                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLBLOCK);
                                        err = -EPERM;
                                        goto error;
                                }
@@ -1471,18 +1655,27 @@ restart:
 
                if (unlikely(nx<0)) {
                        err = nx;
-                       if (err == -EAGAIN && flags) {
+                       if (err == -EAGAIN && net->xfrm.sysctl_larval_drop) {
+                               /* EREMOTE tells the caller to generate
+                                * a one-shot blackhole route.
+                                */
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
+                               xfrm_pol_put(policy);
+                               return -EREMOTE;
+                       }
+                       if (err == -EAGAIN && (flags & XFRM_LOOKUP_WAIT)) {
                                DECLARE_WAITQUEUE(wait, current);
 
-                               add_wait_queue(&km_waitq, &wait);
+                               add_wait_queue(&net->xfrm.km_waitq, &wait);
                                set_current_state(TASK_INTERRUPTIBLE);
                                schedule();
                                set_current_state(TASK_RUNNING);
-                               remove_wait_queue(&km_waitq, &wait);
+                               remove_wait_queue(&net->xfrm.km_waitq, &wait);
 
                                nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
 
                                if (nx == -EAGAIN && signal_pending(current)) {
+                                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
                                        err = -ERESTART;
                                        goto error;
                                }
@@ -1493,8 +1686,10 @@ restart:
                                }
                                err = nx;
                        }
-                       if (err < 0)
+                       if (err < 0) {
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
                                goto error;
+                       }
                }
                if (nx == 0) {
                        /* Flow passes not transformed. */
@@ -1502,19 +1697,16 @@ restart:
                        return 0;
                }
 
-               dst = dst_orig;
-               err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family);
-
-               if (unlikely(err)) {
-                       int i;
-                       for (i=0; i<nx; i++)
-                               xfrm_state_put(xfrm[i]);
+               dst = xfrm_bundle_create(policy, xfrm, nx, fl, dst_orig);
+               err = PTR_ERR(dst);
+               if (IS_ERR(dst)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLEGENERROR);
                        goto error;
                }
 
                for (pi = 0; pi < npols; pi++) {
                        read_lock_bh(&pols[pi]->lock);
-                       pol_dead |= pols[pi]->dead;
+                       pol_dead |= pols[pi]->walk.dead;
                        read_unlock_bh(&pols[pi]->lock);
                }
 
@@ -1526,12 +1718,27 @@ restart:
                         * We can't enlist stable bundles either.
                         */
                        write_unlock_bh(&policy->lock);
-                       if (dst)
-                               dst_free(dst);
+                       dst_free(dst);
 
+                       if (pol_dead)
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLDEAD);
+                       else
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
                        err = -EHOSTUNREACH;
                        goto error;
                }
+
+               if (npols > 1)
+                       err = xfrm_dst_update_parent(dst, &pols[1]->selector);
+               else
+                       err = xfrm_dst_update_origin(dst, fl);
+               if (unlikely(err)) {
+                       write_unlock_bh(&policy->lock);
+                       dst_free(dst);
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
+                       goto error;
+               }
+
                dst->next = policy->bundles;
                policy->bundles = dst;
                dst_hold(dst);
@@ -1543,10 +1750,32 @@ restart:
        return 0;
 
 error:
-       dst_release(dst_orig);
        xfrm_pols_put(pols, npols);
+dropdst:
+       dst_release(dst_orig);
        *dst_p = NULL;
        return err;
+
+nopol:
+       err = -ENOENT;
+       if (flags & XFRM_LOOKUP_ICMP)
+               goto dropdst;
+       return 0;
+}
+EXPORT_SYMBOL(__xfrm_lookup);
+
+int xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl,
+               struct sock *sk, int flags)
+{
+       int err = __xfrm_lookup(net, dst_p, fl, sk, flags);
+
+       if (err == -EREMOTE) {
+               dst_release(*dst_p);
+               *dst_p = NULL;
+               err = -EAGAIN;
+       }
+
+       return err;
 }
 EXPORT_SYMBOL(xfrm_lookup);
 
@@ -1554,17 +1783,13 @@ static inline int
 xfrm_secpath_reject(int idx, struct sk_buff *skb, struct flowi *fl)
 {
        struct xfrm_state *x;
-       int err;
 
        if (!skb->sp || idx < 0 || idx >= skb->sp->len)
                return 0;
        x = skb->sp->xvec[idx];
        if (!x->type->reject)
                return 0;
-       xfrm_state_hold(x);
-       err = x->type->reject(x, skb, fl);
-       xfrm_state_put(x);
-       return err;
+       return x->type->reject(x, skb, fl);
 }
 
 /* When skb is transformed back to its "native" form, we have to
@@ -1583,7 +1808,7 @@ xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x,
                (x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
                (x->props.reqid == tmpl->reqid || !tmpl->reqid) &&
                x->props.mode == tmpl->mode &&
-               ((tmpl->aalgos & (1<<x->props.aalgo)) ||
+               (tmpl->allalgs || (tmpl->aalgos & (1<<x->props.aalgo)) ||
                 !(xfrm_id_proto_match(tmpl->id.proto, IPSEC_PROTO_ANY))) &&
                !(x->props.mode != XFRM_MODE_TRANSPORT &&
                  xfrm_state_addr_cmp(tmpl, x, family));
@@ -1619,8 +1844,8 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
        return start;
 }
 
-int
-xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family)
+int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
+                         unsigned int family, int reverse)
 {
        struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
        int err;
@@ -1628,12 +1853,12 @@ xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family
        if (unlikely(afinfo == NULL))
                return -EAFNOSUPPORT;
 
-       afinfo->decode_session(skb, fl);
+       afinfo->decode_session(skb, fl, reverse);
        err = security_xfrm_decode_session(skb, &fl->secid);
        xfrm_policy_put_afinfo(afinfo);
        return err;
 }
-EXPORT_SYMBOL(xfrm_decode_session);
+EXPORT_SYMBOL(__xfrm_decode_session);
 
 static inline int secpath_has_nontransport(struct sec_path *sp, int k, int *idxp)
 {
@@ -1650,17 +1875,26 @@ static inline int secpath_has_nontransport(struct sec_path *sp, int k, int *idxp
 int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
                        unsigned short family)
 {
+       struct net *net = dev_net(skb->dev);
        struct xfrm_policy *pol;
        struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
        int npols = 0;
        int xfrm_nr;
        int pi;
+       int reverse;
        struct flowi fl;
-       u8 fl_dir = policy_to_flow_dir(dir);
+       u8 fl_dir;
        int xerr_idx = -1;
 
-       if (xfrm_decode_session(skb, &fl, family) < 0)
+       reverse = dir & ~XFRM_POLICY_MASK;
+       dir &= XFRM_POLICY_MASK;
+       fl_dir = policy_to_flow_dir(dir);
+
+       if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) {
+               XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
                return 0;
+       }
+
        nf_nat_decode_session(skb, &fl, family);
 
        /* First, check used SA against their selectors. */
@@ -1669,28 +1903,35 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 
                for (i=skb->sp->len-1; i>=0; i--) {
                        struct xfrm_state *x = skb->sp->xvec[i];
-                       if (!xfrm_selector_match(&x->sel, &fl, family))
+                       if (!xfrm_selector_match(&x->sel, &fl, family)) {
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
                                return 0;
+                       }
                }
        }
 
        pol = NULL;
        if (sk && sk->sk_policy[dir]) {
                pol = xfrm_sk_policy_lookup(sk, dir, &fl);
-               if (IS_ERR(pol))
+               if (IS_ERR(pol)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
                        return 0;
+               }
        }
 
        if (!pol)
-               pol = flow_cache_lookup(&fl, family, fl_dir,
+               pol = flow_cache_lookup(net, &fl, family, fl_dir,
                                        xfrm_policy_lookup);
 
-       if (IS_ERR(pol))
+       if (IS_ERR(pol)) {
+               XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
                return 0;
+       }
 
        if (!pol) {
                if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
                        xfrm_secpath_reject(xerr_idx, skb, &fl);
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
                        return 0;
                }
                return 1;
@@ -1702,12 +1943,14 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
        npols ++;
 #ifdef CONFIG_XFRM_SUB_POLICY
        if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
-               pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
+               pols[1] = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN,
                                                    &fl, family,
                                                    XFRM_POLICY_IN);
                if (pols[1]) {
-                       if (IS_ERR(pols[1]))
+                       if (IS_ERR(pols[1])) {
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
                                return 0;
+                       }
                        pols[1]->curlft.use_time = get_seconds();
                        npols ++;
                }
@@ -1728,10 +1971,14 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 
                for (pi = 0; pi < npols; pi++) {
                        if (pols[pi] != pol &&
-                           pols[pi]->action != XFRM_POLICY_ALLOW)
+                           pols[pi]->action != XFRM_POLICY_ALLOW) {
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK);
                                goto reject;
-                       if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH)
+                       }
+                       if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) {
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
                                goto reject_error;
+                       }
                        for (i = 0; i < pols[pi]->xfrm_nr; i++)
                                tpp[ti++] = &pols[pi]->xfrm_vec[i];
                }
@@ -1753,16 +2000,20 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
                                if (k < -1)
                                        /* "-2 - errored_index" returned */
                                        xerr_idx = -(2+k);
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMINTMPLMISMATCH);
                                goto reject;
                        }
                }
 
-               if (secpath_has_nontransport(sp, k, &xerr_idx))
+               if (secpath_has_nontransport(sp, k, &xerr_idx)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMINTMPLMISMATCH);
                        goto reject;
+               }
 
                xfrm_pols_put(pols, npols);
                return 1;
        }
+       XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK);
 
 reject:
        xfrm_secpath_reject(xerr_idx, skb, &fl);
@@ -1774,12 +2025,22 @@ EXPORT_SYMBOL(__xfrm_policy_check);
 
 int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 {
+       struct net *net = dev_net(skb->dev);
        struct flowi fl;
+       struct dst_entry *dst;
+       int res;
 
-       if (xfrm_decode_session(skb, &fl, family) < 0)
+       if (xfrm_decode_session(skb, &fl, family) < 0) {
+               /* XXX: we should have something like FWDHDRERROR here. */
+               XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
                return 0;
+       }
+
+       dst = skb_dst(skb);
 
-       return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
+       res = xfrm_lookup(net, &dst, &fl, NULL, 0) == 0;
+       skb_dst_set(skb, dst);
+       return res;
 }
 EXPORT_SYMBOL(__xfrm_route_forward);
 
@@ -1821,8 +2082,8 @@ static int stale_bundle(struct dst_entry *dst)
 void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
 {
        while ((dst = dst->child) && dst->xfrm && dst->dev == dev) {
-               dst->dev = &loopback_dev;
-               dev_hold(&loopback_dev);
+               dst->dev = dev_net(dev)->loopback_dev;
+               dev_hold(dst->dev);
                dev_put(dev);
        }
 }
@@ -1863,7 +2124,7 @@ static void prune_one_bundle(struct xfrm_policy *pol, int (*func)(struct dst_ent
        write_unlock(&pol->lock);
 }
 
-static void xfrm_prune_bundles(int (*func)(struct dst_entry *))
+static void xfrm_prune_bundles(struct net *net, int (*func)(struct dst_entry *))
 {
        struct dst_entry *gc_list = NULL;
        int dir;
@@ -1876,11 +2137,11 @@ static void xfrm_prune_bundles(int (*func)(struct dst_entry *))
                int i;
 
                hlist_for_each_entry(pol, entry,
-                                    &xfrm_policy_inexact[dir], bydst)
+                                    &net->xfrm.policy_inexact[dir], bydst)
                        prune_one_bundle(pol, func, &gc_list);
 
-               table = xfrm_policy_bydst[dir].table;
-               for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
+               table = net->xfrm.policy_bydst[dir].table;
+               for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
                        hlist_for_each_entry(pol, entry, table + i, bydst)
                                prune_one_bundle(pol, func, &gc_list);
                }
@@ -1899,18 +2160,18 @@ static int unused_bundle(struct dst_entry *dst)
        return !atomic_read(&dst->__refcnt);
 }
 
-static void __xfrm_garbage_collect(void)
+static void __xfrm_garbage_collect(struct net *net)
 {
-       xfrm_prune_bundles(unused_bundle);
+       xfrm_prune_bundles(net, unused_bundle);
 }
 
-static int xfrm_flush_bundles(void)
+static int xfrm_flush_bundles(struct net *net)
 {
-       xfrm_prune_bundles(stale_bundle);
+       xfrm_prune_bundles(net, stale_bundle);
        return 0;
 }
 
-void xfrm_init_pmtu(struct dst_entry *dst)
+static void xfrm_init_pmtu(struct dst_entry *dst)
 {
        do {
                struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
@@ -1931,8 +2192,6 @@ void xfrm_init_pmtu(struct dst_entry *dst)
        } while ((dst = dst->next));
 }
 
-EXPORT_SYMBOL(xfrm_init_pmtu);
-
 /* Check that the bundle accepts the flow and its components are
  * still valid.
  */
@@ -1947,6 +2206,15 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
        if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) ||
            (dst->dev && !netif_running(dst->dev)))
                return 0;
+#ifdef CONFIG_XFRM_SUB_POLICY
+       if (fl) {
+               if (first->origin && !flow_cache_uli_match(first->origin, fl))
+                       return 0;
+               if (first->partner &&
+                   !xfrm_selector_match(first->partner, fl, family))
+                       return 0;
+       }
+#endif
 
        last = NULL;
 
@@ -1963,7 +2231,8 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
                if (xdst->genid != dst->xfrm->genid)
                        return 0;
 
-               if (strict && fl && dst->xfrm->props.mode != XFRM_MODE_TUNNEL &&
+               if (strict && fl &&
+                   !(dst->xfrm->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
                    !xfrm_state_addr_flow_check(dst->xfrm, fl, family))
                        return 0;
 
@@ -1999,7 +2268,7 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
                if (last == first)
                        break;
 
-               last = last->u.next;
+               last = (struct xfrm_dst *)last->u.dst.next;
                last->child_mtu_cached = mtu;
        }
 
@@ -2008,122 +2277,6 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
 
 EXPORT_SYMBOL(xfrm_bundle_ok);
 
-#ifdef CONFIG_AUDITSYSCALL
-/* Audit addition and deletion of SAs and ipsec policy */
-
-void xfrm_audit_log(uid_t auid, u32 sid, int type, int result,
-                   struct xfrm_policy *xp, struct xfrm_state *x)
-{
-
-       char *secctx;
-       u32 secctx_len;
-       struct xfrm_sec_ctx *sctx = NULL;
-       struct audit_buffer *audit_buf;
-       int family;
-       extern int audit_enabled;
-
-       if (audit_enabled == 0)
-               return;
-
-       BUG_ON((type == AUDIT_MAC_IPSEC_ADDSA ||
-               type == AUDIT_MAC_IPSEC_DELSA) && !x);
-       BUG_ON((type == AUDIT_MAC_IPSEC_ADDSPD ||
-               type == AUDIT_MAC_IPSEC_DELSPD) && !xp);
-
-       audit_buf = audit_log_start(current->audit_context, GFP_ATOMIC, type);
-       if (audit_buf == NULL)
-               return;
-
-       switch(type) {
-       case AUDIT_MAC_IPSEC_ADDSA:
-               audit_log_format(audit_buf, "SAD add: auid=%u", auid);
-               break;
-       case AUDIT_MAC_IPSEC_DELSA:
-               audit_log_format(audit_buf, "SAD delete: auid=%u", auid);
-               break;
-       case AUDIT_MAC_IPSEC_ADDSPD:
-               audit_log_format(audit_buf, "SPD add: auid=%u", auid);
-               break;
-       case AUDIT_MAC_IPSEC_DELSPD:
-               audit_log_format(audit_buf, "SPD delete: auid=%u", auid);
-               break;
-       default:
-               return;
-       }
-
-       if (sid != 0 &&
-               security_secid_to_secctx(sid, &secctx, &secctx_len) == 0)
-               audit_log_format(audit_buf, " subj=%s", secctx);
-       else
-               audit_log_task_context(audit_buf);
-
-       if (xp) {
-               family = xp->selector.family;
-               if (xp->security)
-                       sctx = xp->security;
-       } else {
-               family = x->props.family;
-               if (x->security)
-                       sctx = x->security;
-       }
-
-       if (sctx)
-               audit_log_format(audit_buf,
-                               " sec_alg=%u sec_doi=%u sec_obj=%s",
-                               sctx->ctx_alg, sctx->ctx_doi, sctx->ctx_str);
-
-       switch(family) {
-       case AF_INET:
-               {
-                       struct in_addr saddr, daddr;
-                       if (xp) {
-                               saddr.s_addr = xp->selector.saddr.a4;
-                               daddr.s_addr = xp->selector.daddr.a4;
-                       } else {
-                               saddr.s_addr = x->props.saddr.a4;
-                               daddr.s_addr = x->id.daddr.a4;
-                       }
-                       audit_log_format(audit_buf,
-                                        " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
-                                        NIPQUAD(saddr), NIPQUAD(daddr));
-               }
-                       break;
-       case AF_INET6:
-               {
-                       struct in6_addr saddr6, daddr6;
-                       if (xp) {
-                               memcpy(&saddr6, xp->selector.saddr.a6,
-                                       sizeof(struct in6_addr));
-                               memcpy(&daddr6, xp->selector.daddr.a6,
-                                       sizeof(struct in6_addr));
-                       } else {
-                               memcpy(&saddr6, x->props.saddr.a6,
-                                       sizeof(struct in6_addr));
-                               memcpy(&daddr6, x->id.daddr.a6,
-                                       sizeof(struct in6_addr));
-                       }
-                       audit_log_format(audit_buf,
-                                        " src=" NIP6_FMT " dst=" NIP6_FMT,
-                                        NIP6(saddr6), NIP6(daddr6));
-               }
-               break;
-       }
-
-       if (x)
-               audit_log_format(audit_buf, " spi=%lu(0x%lx) protocol=%s",
-                               (unsigned long)ntohl(x->id.spi),
-                               (unsigned long)ntohl(x->id.spi),
-                               x->id.proto == IPPROTO_AH ? "AH" :
-                               (x->id.proto == IPPROTO_ESP ?
-                               "ESP" : "IPCOMP"));
-
-       audit_log_format(audit_buf, " res=%u", result);
-       audit_log_end(audit_buf);
-}
-
-EXPORT_SYMBOL(xfrm_audit_log);
-#endif /* CONFIG_AUDITSYSCALL */
-
 int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
 {
        int err = 0;
@@ -2196,79 +2349,251 @@ static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
        read_unlock(&xfrm_policy_afinfo_lock);
 }
 
-static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family)
-{
-       struct xfrm_policy_afinfo *afinfo;
-       if (unlikely(family >= NPROTO))
-               return NULL;
-       write_lock_bh(&xfrm_policy_afinfo_lock);
-       afinfo = xfrm_policy_afinfo[family];
-       if (unlikely(!afinfo))
-               write_unlock_bh(&xfrm_policy_afinfo_lock);
-       return afinfo;
-}
-
-static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo)
-{
-       write_unlock_bh(&xfrm_policy_afinfo_lock);
-}
-
 static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
+       struct net_device *dev = ptr;
+
        switch (event) {
        case NETDEV_DOWN:
-               xfrm_flush_bundles();
+               xfrm_flush_bundles(dev_net(dev));
        }
        return NOTIFY_DONE;
 }
 
 static struct notifier_block xfrm_dev_notifier = {
-       xfrm_dev_event,
-       NULL,
-       0
+       .notifier_call  = xfrm_dev_event,
 };
 
-static void __init xfrm_policy_init(void)
+#ifdef CONFIG_XFRM_STATISTICS
+static int __net_init xfrm_statistics_init(struct net *net)
+{
+       int rv;
+
+       if (snmp_mib_init((void **)net->mib.xfrm_statistics,
+                         sizeof(struct linux_xfrm_mib)) < 0)
+               return -ENOMEM;
+       rv = xfrm_proc_init(net);
+       if (rv < 0)
+               snmp_mib_free((void **)net->mib.xfrm_statistics);
+       return rv;
+}
+
+static void xfrm_statistics_fini(struct net *net)
+{
+       xfrm_proc_fini(net);
+       snmp_mib_free((void **)net->mib.xfrm_statistics);
+}
+#else
+static int __net_init xfrm_statistics_init(struct net *net)
+{
+       return 0;
+}
+
+static void xfrm_statistics_fini(struct net *net)
+{
+}
+#endif
+
+static int __net_init xfrm_policy_init(struct net *net)
 {
        unsigned int hmask, sz;
        int dir;
 
-       xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
+       if (net_eq(net, &init_net))
+               xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
                                           sizeof(struct xfrm_dst),
                                           0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
-                                          NULL, NULL);
+                                          NULL);
 
        hmask = 8 - 1;
        sz = (hmask+1) * sizeof(struct hlist_head);
 
-       xfrm_policy_byidx = xfrm_hash_alloc(sz);
-       xfrm_idx_hmask = hmask;
-       if (!xfrm_policy_byidx)
-               panic("XFRM: failed to allocate byidx hash\n");
+       net->xfrm.policy_byidx = xfrm_hash_alloc(sz);
+       if (!net->xfrm.policy_byidx)
+               goto out_byidx;
+       net->xfrm.policy_idx_hmask = hmask;
 
        for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
                struct xfrm_policy_hash *htab;
 
-               INIT_HLIST_HEAD(&xfrm_policy_inexact[dir]);
+               net->xfrm.policy_count[dir] = 0;
+               INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
 
-               htab = &xfrm_policy_bydst[dir];
+               htab = &net->xfrm.policy_bydst[dir];
                htab->table = xfrm_hash_alloc(sz);
-               htab->hmask = hmask;
                if (!htab->table)
-                       panic("XFRM: failed to allocate bydst hash\n");
+                       goto out_bydst;
+               htab->hmask = hmask;
        }
 
-       INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task);
-       register_netdevice_notifier(&xfrm_dev_notifier);
+       INIT_LIST_HEAD(&net->xfrm.policy_all);
+       INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize);
+       if (net_eq(net, &init_net))
+               register_netdevice_notifier(&xfrm_dev_notifier);
+       return 0;
+
+out_bydst:
+       for (dir--; dir >= 0; dir--) {
+               struct xfrm_policy_hash *htab;
+
+               htab = &net->xfrm.policy_bydst[dir];
+               xfrm_hash_free(htab->table, sz);
+       }
+       xfrm_hash_free(net->xfrm.policy_byidx, sz);
+out_byidx:
+       return -ENOMEM;
 }
 
+static void xfrm_policy_fini(struct net *net)
+{
+       struct xfrm_audit audit_info;
+       unsigned int sz;
+       int dir;
+
+       flush_work(&net->xfrm.policy_hash_work);
+#ifdef CONFIG_XFRM_SUB_POLICY
+       audit_info.loginuid = -1;
+       audit_info.sessionid = -1;
+       audit_info.secid = 0;
+       xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info);
+#endif
+       audit_info.loginuid = -1;
+       audit_info.sessionid = -1;
+       audit_info.secid = 0;
+       xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
+       flush_work(&xfrm_policy_gc_work);
+
+       WARN_ON(!list_empty(&net->xfrm.policy_all));
+
+       for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
+               struct xfrm_policy_hash *htab;
+
+               WARN_ON(!hlist_empty(&net->xfrm.policy_inexact[dir]));
+
+               htab = &net->xfrm.policy_bydst[dir];
+               sz = (htab->hmask + 1);
+               WARN_ON(!hlist_empty(htab->table));
+               xfrm_hash_free(htab->table, sz);
+       }
+
+       sz = (net->xfrm.policy_idx_hmask + 1) * sizeof(struct hlist_head);
+       WARN_ON(!hlist_empty(net->xfrm.policy_byidx));
+       xfrm_hash_free(net->xfrm.policy_byidx, sz);
+}
+
+static int __net_init xfrm_net_init(struct net *net)
+{
+       int rv;
+
+       rv = xfrm_statistics_init(net);
+       if (rv < 0)
+               goto out_statistics;
+       rv = xfrm_state_init(net);
+       if (rv < 0)
+               goto out_state;
+       rv = xfrm_policy_init(net);
+       if (rv < 0)
+               goto out_policy;
+       rv = xfrm_sysctl_init(net);
+       if (rv < 0)
+               goto out_sysctl;
+       return 0;
+
+out_sysctl:
+       xfrm_policy_fini(net);
+out_policy:
+       xfrm_state_fini(net);
+out_state:
+       xfrm_statistics_fini(net);
+out_statistics:
+       return rv;
+}
+
+static void __net_exit xfrm_net_exit(struct net *net)
+{
+       xfrm_sysctl_fini(net);
+       xfrm_policy_fini(net);
+       xfrm_state_fini(net);
+       xfrm_statistics_fini(net);
+}
+
+static struct pernet_operations __net_initdata xfrm_net_ops = {
+       .init = xfrm_net_init,
+       .exit = xfrm_net_exit,
+};
+
 void __init xfrm_init(void)
 {
-       xfrm_state_init();
-       xfrm_policy_init();
+       register_pernet_subsys(&xfrm_net_ops);
        xfrm_input_init();
 }
 
+#ifdef CONFIG_AUDITSYSCALL
+static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
+                                        struct audit_buffer *audit_buf)
+{
+       struct xfrm_sec_ctx *ctx = xp->security;
+       struct xfrm_selector *sel = &xp->selector;
+
+       if (ctx)
+               audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
+                                ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
+
+       switch(sel->family) {
+       case AF_INET:
+               audit_log_format(audit_buf, " src=%pI4", &sel->saddr.a4);
+               if (sel->prefixlen_s != 32)
+                       audit_log_format(audit_buf, " src_prefixlen=%d",
+                                        sel->prefixlen_s);
+               audit_log_format(audit_buf, " dst=%pI4", &sel->daddr.a4);
+               if (sel->prefixlen_d != 32)
+                       audit_log_format(audit_buf, " dst_prefixlen=%d",
+                                        sel->prefixlen_d);
+               break;
+       case AF_INET6:
+               audit_log_format(audit_buf, " src=%pI6", sel->saddr.a6);
+               if (sel->prefixlen_s != 128)
+                       audit_log_format(audit_buf, " src_prefixlen=%d",
+                                        sel->prefixlen_s);
+               audit_log_format(audit_buf, " dst=%pI6", sel->daddr.a6);
+               if (sel->prefixlen_d != 128)
+                       audit_log_format(audit_buf, " dst_prefixlen=%d",
+                                        sel->prefixlen_d);
+               break;
+       }
+}
+
+void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
+                          uid_t auid, u32 sessionid, u32 secid)
+{
+       struct audit_buffer *audit_buf;
+
+       audit_buf = xfrm_audit_start("SPD-add");
+       if (audit_buf == NULL)
+               return;
+       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       audit_log_format(audit_buf, " res=%u", result);
+       xfrm_audit_common_policyinfo(xp, audit_buf);
+       audit_log_end(audit_buf);
+}
+EXPORT_SYMBOL_GPL(xfrm_audit_policy_add);
+
+void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
+                             uid_t auid, u32 sessionid, u32 secid)
+{
+       struct audit_buffer *audit_buf;
+
+       audit_buf = xfrm_audit_start("SPD-delete");
+       if (audit_buf == NULL)
+               return;
+       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       audit_log_format(audit_buf, " res=%u", result);
+       xfrm_audit_common_policyinfo(xp, audit_buf);
+       audit_log_end(audit_buf);
+}
+EXPORT_SYMBOL_GPL(xfrm_audit_policy_delete);
+#endif
+
 #ifdef CONFIG_XFRM_MIGRATE
 static int xfrm_migrate_selector_match(struct xfrm_selector *sel_cmp,
                                       struct xfrm_selector *sel_tgt)
@@ -2300,7 +2625,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(struct xfrm_selector *sel,
        u32 priority = ~0U;
 
        read_lock_bh(&xfrm_policy_lock);
-       chain = policy_hash_direct(&sel->daddr, &sel->saddr, sel->family, dir);
+       chain = policy_hash_direct(&init_net, &sel->daddr, &sel->saddr, sel->family, dir);
        hlist_for_each_entry(pol, entry, chain, bydst) {
                if (xfrm_migrate_selector_match(sel, &pol->selector) &&
                    pol->type == type) {
@@ -2309,7 +2634,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(struct xfrm_selector *sel,
                        break;
                }
        }
-       chain = &xfrm_policy_inexact[dir];
+       chain = &init_net.xfrm.policy_inexact[dir];
        hlist_for_each_entry(pol, entry, chain, bydst) {
                if (xfrm_migrate_selector_match(sel, &pol->selector) &&
                    pol->type == type &&
@@ -2365,7 +2690,7 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol,
        int i, j, n = 0;
 
        write_lock_bh(&pol->lock);
-       if (unlikely(pol->dead)) {
+       if (unlikely(pol->walk.dead)) {
                /* target policy has been deleted */
                write_unlock_bh(&pol->lock);
                return -ENOENT;
@@ -2376,7 +2701,8 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol,
                        if (!migrate_tmpl_match(mp, &pol->xfrm_vec[i]))
                                continue;
                        n++;
-                       if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL)
+                       if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL &&
+                           pol->xfrm_vec[i].mode != XFRM_MODE_BEET)
                                continue;
                        /* update endpoints */
                        memcpy(&pol->xfrm_vec[i].id.daddr, &mp->new_daddr,
@@ -2435,7 +2761,8 @@ static int xfrm_migrate_check(struct xfrm_migrate *m, int num_migrate)
 }
 
 int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
-                struct xfrm_migrate *m, int num_migrate)
+                struct xfrm_migrate *m, int num_migrate,
+                struct xfrm_kmaddress *k)
 {
        int i, err, nx_cur = 0, nx_new = 0;
        struct xfrm_policy *pol = NULL;
@@ -2479,7 +2806,7 @@ int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
        }
 
        /* Stage 5 - announce */
-       km_migrate(sel, dir, type, m, num_migrate);
+       km_migrate(sel, dir, type, m, num_migrate, k);
 
        xfrm_pol_put(pol);
 
@@ -2499,4 +2826,3 @@ restore_state:
 }
 EXPORT_SYMBOL(xfrm_migrate);
 #endif
-