summaryrefslogtreecommitdiffstats
path: root/main/linux-grsec/0015-xfrm-cache-bundles-instead-of-policies-for-outgoing-.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/linux-grsec/0015-xfrm-cache-bundles-instead-of-policies-for-outgoing-.patch')
-rw-r--r--main/linux-grsec/0015-xfrm-cache-bundles-instead-of-policies-for-outgoing-.patch1068
1 files changed, 0 insertions, 1068 deletions
diff --git a/main/linux-grsec/0015-xfrm-cache-bundles-instead-of-policies-for-outgoing-.patch b/main/linux-grsec/0015-xfrm-cache-bundles-instead-of-policies-for-outgoing-.patch
deleted file mode 100644
index 0d066c84d..000000000
--- a/main/linux-grsec/0015-xfrm-cache-bundles-instead-of-policies-for-outgoing-.patch
+++ /dev/null
@@ -1,1068 +0,0 @@
-From f89d21648e6dc06db2aeabc8926c270894c41446 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
-Date: Wed, 7 Apr 2010 00:30:05 +0000
-Subject: [PATCH 15/18] xfrm: cache bundles instead of policies for outgoing flows
-
-__xfrm_lookup() is called for each packet transmitted out of
-system. The xfrm_find_bundle() does a linear search which can
-kill system performance depending on how many bundles are
-required per policy.
-
-This modifies __xfrm_lookup() to store bundles directly in
-the flow cache. If we did not get a hit, we just create a new
-bundle instead of doing slow search. This means that we can now
-get multiple xfrm_dst's for same flow (on per-cpu basis).
-
-Signed-off-by: Timo Teras <timo.teras@iki.fi>
-Signed-off-by: David S. Miller <davem@davemloft.net>
-(backported from commit 80c802f3073e84c956846e921e8a0b02dfa3755f)
----
- include/net/xfrm.h | 10 +-
- net/ipv4/xfrm4_policy.c | 22 --
- net/ipv6/xfrm6_policy.c | 31 --
- net/xfrm/xfrm_policy.c | 710 +++++++++++++++++++++++++----------------------
- 4 files changed, 383 insertions(+), 390 deletions(-)
-
-diff --git a/include/net/xfrm.h b/include/net/xfrm.h
-index 6023a48..d51ef61 100644
---- a/include/net/xfrm.h
-+++ b/include/net/xfrm.h
-@@ -266,7 +266,6 @@ struct xfrm_policy_afinfo {
- xfrm_address_t *saddr,
- xfrm_address_t *daddr);
- int (*get_saddr)(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr);
-- struct dst_entry *(*find_bundle)(struct flowi *fl, struct xfrm_policy *policy);
- void (*decode_session)(struct sk_buff *skb,
- struct flowi *fl,
- int reverse);
-@@ -485,12 +484,12 @@ struct xfrm_policy
- struct timer_list timer;
-
- struct flow_cache_object flo;
-+ atomic_t genid;
- u32 priority;
- u32 index;
- struct xfrm_selector selector;
- struct xfrm_lifetime_cfg lft;
- struct xfrm_lifetime_cur curlft;
-- struct dst_entry *bundles;
- struct xfrm_policy_walk_entry walk;
- u8 type;
- u8 action;
-@@ -883,11 +882,15 @@ struct xfrm_dst
- struct rt6_info rt6;
- } u;
- struct dst_entry *route;
-+ struct flow_cache_object flo;
-+ struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
-+ int num_pols, num_xfrms;
- #ifdef CONFIG_XFRM_SUB_POLICY
- struct flowi *origin;
- struct xfrm_selector *partner;
- #endif
-- u32 genid;
-+ u32 xfrm_genid;
-+ u32 policy_genid;
- u32 route_mtu_cached;
- u32 child_mtu_cached;
- u32 route_cookie;
-@@ -897,6 +900,7 @@ struct xfrm_dst
- #ifdef CONFIG_XFRM
- static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
- {
-+ xfrm_pols_put(xdst->pols, xdst->num_pols);
- dst_release(xdst->route);
- if (likely(xdst->u.dst.xfrm))
- xfrm_state_put(xdst->u.dst.xfrm);
-diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
-index 7009886..651a3e7 100644
---- a/net/ipv4/xfrm4_policy.c
-+++ b/net/ipv4/xfrm4_policy.c
-@@ -60,27 +60,6 @@ static int xfrm4_get_saddr(struct net *net,
- return 0;
- }
-
--static struct dst_entry *
--__xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
--{
-- struct dst_entry *dst;
--
-- read_lock_bh(&policy->lock);
-- for (dst = policy->bundles; dst; dst = dst->next) {
-- struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
-- if (xdst->u.rt.fl.oif == fl->oif && /*XXX*/
-- xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
-- xdst->u.rt.fl.fl4_src == fl->fl4_src &&
-- xdst->u.rt.fl.fl4_tos == fl->fl4_tos &&
-- xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) {
-- dst_clone(dst);
-- break;
-- }
-- }
-- read_unlock_bh(&policy->lock);
-- return dst;
--}
--
- static int xfrm4_get_tos(struct flowi *fl)
- {
- return fl->fl4_tos;
-@@ -258,7 +237,6 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
- .dst_ops = &xfrm4_dst_ops,
- .dst_lookup = xfrm4_dst_lookup,
- .get_saddr = xfrm4_get_saddr,
-- .find_bundle = __xfrm4_find_bundle,
- .decode_session = _decode_session4,
- .get_tos = xfrm4_get_tos,
- .init_path = xfrm4_init_path,
-diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
-index 3f89ab7..fb2a5b7 100644
---- a/net/ipv6/xfrm6_policy.c
-+++ b/net/ipv6/xfrm6_policy.c
-@@ -68,36 +68,6 @@ static int xfrm6_get_saddr(struct net *net,
- return 0;
- }
-
--static struct dst_entry *
--__xfrm6_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
--{
-- struct dst_entry *dst;
--
-- /* Still not clear if we should set fl->fl6_{src,dst}... */
-- read_lock_bh(&policy->lock);
-- for (dst = policy->bundles; dst; dst = dst->next) {
-- struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
-- struct in6_addr fl_dst_prefix, fl_src_prefix;
--
-- ipv6_addr_prefix(&fl_dst_prefix,
-- &fl->fl6_dst,
-- xdst->u.rt6.rt6i_dst.plen);
-- ipv6_addr_prefix(&fl_src_prefix,
-- &fl->fl6_src,
-- xdst->u.rt6.rt6i_src.plen);
-- if (ipv6_addr_equal(&xdst->u.rt6.rt6i_dst.addr, &fl_dst_prefix) &&
-- ipv6_addr_equal(&xdst->u.rt6.rt6i_src.addr, &fl_src_prefix) &&
-- xfrm_bundle_ok(policy, xdst, fl, AF_INET6,
-- (xdst->u.rt6.rt6i_dst.plen != 128 ||
-- xdst->u.rt6.rt6i_src.plen != 128))) {
-- dst_clone(dst);
-- break;
-- }
-- }
-- read_unlock_bh(&policy->lock);
-- return dst;
--}
--
- static int xfrm6_get_tos(struct flowi *fl)
- {
- return 0;
-@@ -290,7 +260,6 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
- .dst_ops = &xfrm6_dst_ops,
- .dst_lookup = xfrm6_dst_lookup,
- .get_saddr = xfrm6_get_saddr,
-- .find_bundle = __xfrm6_find_bundle,
- .decode_session = _decode_session6,
- .get_tos = xfrm6_get_tos,
- .init_path = xfrm6_init_path,
-diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
-index d1eb2b5..0379d82 100644
---- a/net/xfrm/xfrm_policy.c
-+++ b/net/xfrm/xfrm_policy.c
-@@ -37,6 +37,8 @@
- DEFINE_MUTEX(xfrm_cfg_mutex);
- EXPORT_SYMBOL(xfrm_cfg_mutex);
-
-+static DEFINE_SPINLOCK(xfrm_policy_sk_bundle_lock);
-+static struct dst_entry *xfrm_policy_sk_bundles;
- static DEFINE_RWLOCK(xfrm_policy_lock);
-
- static DEFINE_RWLOCK(xfrm_policy_afinfo_lock);
-@@ -50,6 +52,7 @@ 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 void xfrm_init_pmtu(struct dst_entry *dst);
-+static int stale_bundle(struct dst_entry *dst);
-
- static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
- int dir);
-@@ -277,8 +280,6 @@ void xfrm_policy_destroy(struct xfrm_policy *policy)
- {
- BUG_ON(!policy->walk.dead);
-
-- BUG_ON(policy->bundles);
--
- if (del_timer(&policy->timer))
- BUG();
-
-@@ -289,12 +290,7 @@ EXPORT_SYMBOL(xfrm_policy_destroy);
-
- static void xfrm_policy_gc_kill(struct xfrm_policy *policy)
- {
-- struct dst_entry *dst;
--
-- while ((dst = policy->bundles) != NULL) {
-- policy->bundles = dst->next;
-- dst_free(dst);
-- }
-+ atomic_inc(&policy->genid);
-
- if (del_timer(&policy->timer))
- atomic_dec(&policy->refcnt);
-@@ -572,7 +568,6 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
- struct xfrm_policy *delpol;
- struct hlist_head *chain;
- struct hlist_node *entry, *newpos;
-- struct dst_entry *gc_list;
-
- write_lock_bh(&xfrm_policy_lock);
- chain = policy_hash_bysel(net, &policy->selector, policy->family, dir);
-@@ -620,34 +615,6 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
- 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;
-- entry = &policy->bydst;
-- hlist_for_each_entry_continue(policy, entry, bydst) {
-- struct dst_entry *dst;
--
-- write_lock(&policy->lock);
-- dst = policy->bundles;
-- if (dst) {
-- struct dst_entry *tail = dst;
-- while (tail->next)
-- tail = tail->next;
-- tail->next = gc_list;
-- gc_list = dst;
--
-- policy->bundles = NULL;
-- }
-- write_unlock(&policy->lock);
-- }
-- read_unlock_bh(&xfrm_policy_lock);
--
-- while (gc_list) {
-- struct dst_entry *dst = gc_list;
--
-- gc_list = dst->next;
-- dst_free(dst);
-- }
--
- return 0;
- }
- EXPORT_SYMBOL(xfrm_policy_insert);
-@@ -990,6 +957,19 @@ fail:
- return ret;
- }
-
-+static struct xfrm_policy *
-+__xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family, u8 dir)
-+{
-+#ifdef CONFIG_XFRM_SUB_POLICY
-+ struct xfrm_policy *pol;
-+
-+ pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir);
-+ if (pol != NULL)
-+ return pol;
-+#endif
-+ return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
-+}
-+
- static struct flow_cache_object *
- xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family,
- u8 dir, struct flow_cache_object *old_obj, void *ctx)
-@@ -999,21 +979,10 @@ xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family,
- if (old_obj)
- xfrm_pol_put(container_of(old_obj, struct xfrm_policy, flo));
-
--#ifdef CONFIG_XFRM_SUB_POLICY
-- pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir);
-- if (IS_ERR(pol))
-+ pol = __xfrm_policy_lookup(net, fl, family, dir);
-+ if (pol == NULL || IS_ERR(pol))
- return ERR_CAST(pol);
-- if (pol)
-- goto found;
--#endif
-- pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
-- if (IS_ERR(pol))
-- return ERR_CAST(pol);
-- if (pol)
-- goto found;
-- return NULL;
-
--found:
- /* Resolver returns two references:
- * one for cache and one for caller of flow_cache_lookup() */
- xfrm_pol_hold(pol);
-@@ -1299,18 +1268,6 @@ xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, struct flowi *fl,
- * still valid.
- */
-
--static struct dst_entry *
--xfrm_find_bundle(struct flowi *fl, struct xfrm_policy *policy, unsigned short family)
--{
-- struct dst_entry *x;
-- struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
-- if (unlikely(afinfo == NULL))
-- return ERR_PTR(-EINVAL);
-- x = afinfo->find_bundle(fl, policy);
-- xfrm_policy_put_afinfo(afinfo);
-- return x;
--}
--
- static inline int xfrm_get_tos(struct flowi *fl, int family)
- {
- struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
-@@ -1326,6 +1283,54 @@ static inline int xfrm_get_tos(struct flowi *fl, int family)
- return tos;
- }
-
-+static struct flow_cache_object *xfrm_bundle_flo_get(struct flow_cache_object *flo)
-+{
-+ struct xfrm_dst *xdst = container_of(flo, struct xfrm_dst, flo);
-+ struct dst_entry *dst = &xdst->u.dst;
-+
-+ if (xdst->route == NULL) {
-+ /* Dummy bundle - if it has xfrms we were not
-+ * able to build bundle as template resolution failed.
-+ * It means we need to try again resolving. */
-+ if (xdst->num_xfrms > 0)
-+ return NULL;
-+ } else {
-+ /* Real bundle */
-+ if (stale_bundle(dst))
-+ return NULL;
-+ }
-+
-+ dst_hold(dst);
-+ return flo;
-+}
-+
-+static int xfrm_bundle_flo_check(struct flow_cache_object *flo)
-+{
-+ struct xfrm_dst *xdst = container_of(flo, struct xfrm_dst, flo);
-+ struct dst_entry *dst = &xdst->u.dst;
-+
-+ if (!xdst->route)
-+ return 0;
-+ if (stale_bundle(dst))
-+ return 0;
-+
-+ return 1;
-+}
-+
-+static void xfrm_bundle_flo_delete(struct flow_cache_object *flo)
-+{
-+ struct xfrm_dst *xdst = container_of(flo, struct xfrm_dst, flo);
-+ struct dst_entry *dst = &xdst->u.dst;
-+
-+ dst_free(dst);
-+}
-+
-+static const struct flow_cache_ops xfrm_bundle_fc_ops = {
-+ .get = xfrm_bundle_flo_get,
-+ .check = xfrm_bundle_flo_check,
-+ .delete = xfrm_bundle_flo_delete,
-+};
-+
- static inline struct xfrm_dst *xfrm_alloc_dst(int family)
- {
- struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
-@@ -1338,6 +1343,8 @@ static inline struct xfrm_dst *xfrm_alloc_dst(int family)
-
- xfrm_policy_put_afinfo(afinfo);
-
-+ xdst->flo.ops = &xfrm_bundle_fc_ops;
-+
- return xdst;
- }
-
-@@ -1375,6 +1382,7 @@ static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
- return err;
- }
-
-+
- /* Allocate chain of dst_entry's, attach known xfrm's, calculate
- * all the metrics... Shortly, bundle a bundle.
- */
-@@ -1437,7 +1445,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
- dst_hold(dst);
-
- dst1->xfrm = xfrm[i];
-- xdst->genid = xfrm[i]->genid;
-+ xdst->xfrm_genid = xfrm[i]->genid;
-
- dst1->obsolete = -1;
- dst1->flags |= DST_HOST;
-@@ -1530,7 +1538,186 @@ xfrm_dst_update_origin(struct dst_entry *dst, struct flowi *fl)
- #endif
- }
-
--static int stale_bundle(struct dst_entry *dst);
-+static int xfrm_expand_policies(struct flowi *fl, u16 family,
-+ struct xfrm_policy **pols,
-+ int *num_pols, int *num_xfrms)
-+{
-+ int i;
-+
-+ if (*num_pols == 0 || !pols[0]) {
-+ *num_pols = 0;
-+ *num_xfrms = 0;
-+ return 0;
-+ }
-+ if (IS_ERR(pols[0]))
-+ return PTR_ERR(pols[0]);
-+
-+ *num_xfrms = pols[0]->xfrm_nr;
-+
-+#ifdef CONFIG_XFRM_SUB_POLICY
-+ if (pols[0] && pols[0]->action == XFRM_POLICY_ALLOW &&
-+ pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
-+ pols[1] = xfrm_policy_lookup_bytype(xp_net(pols[0]),
-+ XFRM_POLICY_TYPE_MAIN,
-+ fl, family,
-+ XFRM_POLICY_OUT);
-+ if (pols[1]) {
-+ if (IS_ERR(pols[1])) {
-+ xfrm_pols_put(pols, *num_pols);
-+ return PTR_ERR(pols[1]);
-+ }
-+ (*num_pols) ++;
-+ (*num_xfrms) += pols[1]->xfrm_nr;
-+ }
-+ }
-+#endif
-+ for (i = 0; i < *num_pols; i++) {
-+ if (pols[i]->action != XFRM_POLICY_ALLOW) {
-+ *num_xfrms = -1;
-+ break;
-+ }
-+ }
-+
-+ return 0;
-+
-+}
-+
-+static struct xfrm_dst *
-+xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
-+ struct flowi *fl, u16 family,
-+ struct dst_entry *dst_orig)
-+{
-+ struct net *net = xp_net(pols[0]);
-+ struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
-+ struct dst_entry *dst;
-+ struct xfrm_dst *xdst;
-+ int err;
-+
-+ /* Try to instantiate a bundle */
-+ err = xfrm_tmpl_resolve(pols, num_pols, fl, xfrm, family);
-+ if (err < 0) {
-+ if (err != -EAGAIN)
-+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
-+ return ERR_PTR(err);
-+ }
-+
-+ dst = xfrm_bundle_create(pols[0], xfrm, err, fl, dst_orig);
-+ if (IS_ERR(dst)) {
-+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLEGENERROR);
-+ return ERR_CAST(dst);
-+ }
-+
-+ xdst = (struct xfrm_dst *)dst;
-+ xdst->num_xfrms = err;
-+ if (num_pols > 1)
-+ err = xfrm_dst_update_parent(dst, &pols[1]->selector);
-+ else
-+ err = xfrm_dst_update_origin(dst, fl);
-+ if (unlikely(err)) {
-+ dst_free(dst);
-+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
-+ return ERR_PTR(err);
-+ }
-+
-+ xdst->num_pols = num_pols;
-+ memcpy(xdst->pols, pols, sizeof(struct xfrm_policy*) * num_pols);
-+ xdst->policy_genid = atomic_read(&pols[0]->genid);
-+
-+ return xdst;
-+}
-+
-+static struct flow_cache_object *
-+xfrm_bundle_lookup(struct net *net, struct flowi *fl, u16 family, u8 dir,
-+ struct flow_cache_object *oldflo, void *ctx)
-+{
-+ struct dst_entry *dst_orig = (struct dst_entry *)ctx;
-+ struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
-+ struct xfrm_dst *xdst, *new_xdst;
-+ int num_pols = 0, num_xfrms = 0, i, err, pol_dead;
-+
-+ /* Check if the policies from old bundle are usable */
-+ xdst = NULL;
-+ if (oldflo) {
-+ xdst = container_of(oldflo, struct xfrm_dst, flo);
-+ num_pols = xdst->num_pols;
-+ num_xfrms = xdst->num_xfrms;
-+ pol_dead = 0;
-+ for (i = 0; i < num_pols; i++) {
-+ pols[i] = xdst->pols[i];
-+ pol_dead |= pols[i]->walk.dead;
-+ }
-+ if (pol_dead) {
-+ dst_free(&xdst->u.dst);
-+ xdst = NULL;
-+ num_pols = 0;
-+ num_xfrms = 0;
-+ oldflo = NULL;
-+ }
-+ }
-+
-+ /* Resolve policies to use if we couldn't get them from
-+ * previous cache entry */
-+ if (xdst == NULL) {
-+ num_pols = 1;
-+ pols[0] = __xfrm_policy_lookup(net, fl, family, dir);
-+ err = xfrm_expand_policies(fl, family, pols,
-+ &num_pols, &num_xfrms);
-+ if (err < 0)
-+ goto inc_error;
-+ if (num_pols == 0)
-+ return NULL;
-+ if (num_xfrms <= 0)
-+ goto make_dummy_bundle;
-+ }
-+
-+ new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, dst_orig);
-+ if (IS_ERR(new_xdst)) {
-+ err = PTR_ERR(new_xdst);
-+ if (err != -EAGAIN)
-+ goto error;
-+ if (oldflo == NULL)
-+ goto make_dummy_bundle;
-+ dst_hold(&xdst->u.dst);
-+ return oldflo;
-+ }
-+
-+ /* Kill the previous bundle */
-+ if (xdst) {
-+ /* The policies were stolen for newly generated bundle */
-+ xdst->num_pols = 0;
-+ dst_free(&xdst->u.dst);
-+ }
-+
-+ /* Flow cache does not have reference, it dst_free()'s,
-+ * but we do need to return one reference for original caller */
-+ dst_hold(&new_xdst->u.dst);
-+ return &new_xdst->flo;
-+
-+make_dummy_bundle:
-+ /* We found policies, but there's no bundles to instantiate:
-+ * either because the policy blocks, has no transformations or
-+ * we could not build template (no xfrm_states).*/
-+ xdst = xfrm_alloc_dst(family);
-+ if (IS_ERR(xdst)) {
-+ xfrm_pols_put(pols, num_pols);
-+ return ERR_CAST(xdst);
-+ }
-+ xdst->num_pols = num_pols;
-+ xdst->num_xfrms = num_xfrms;
-+ memcpy(xdst->pols, pols, sizeof(struct xfrm_policy*) * num_pols);
-+
-+ dst_hold(&xdst->u.dst);
-+ return &xdst->flo;
-+
-+inc_error:
-+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
-+error:
-+ if (xdst != NULL)
-+ dst_free(&xdst->u.dst);
-+ else
-+ xfrm_pols_put(pols, num_pols);
-+ return ERR_PTR(err);
-+}
-
- /* Main function: finds/creates a bundle for given flow.
- *
-@@ -1540,248 +1727,152 @@ static int stale_bundle(struct dst_entry *dst);
- 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];
-- int npols;
-- int pol_dead;
-- int xfrm_nr;
-- int pi;
-- struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
-- struct dst_entry *dst, *dst_orig = *dst_p;
-- int nx = 0;
-- int err;
-- u32 genid;
-- u16 family;
-+ struct flow_cache_object *flo;
-+ struct xfrm_dst *xdst;
-+ struct dst_entry *dst, *dst_orig = *dst_p, *route;
-+ u16 family = dst_orig->ops->family;
- u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
-+ int i, err, num_pols, num_xfrms, drop_pols = 0;
-
- restart:
-- genid = atomic_read(&flow_cache_genid);
-- policy = NULL;
-- for (pi = 0; pi < ARRAY_SIZE(pols); pi++)
-- pols[pi] = NULL;
-- npols = 0;
-- pol_dead = 0;
-- xfrm_nr = 0;
-+ dst = NULL;
-+ xdst = NULL;
-+ route = NULL;
-
- if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
-- policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
-- err = PTR_ERR(policy);
-- if (IS_ERR(policy)) {
-- XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
-+ num_pols = 1;
-+ pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
-+ err = xfrm_expand_policies(fl, family, pols,
-+ &num_pols, &num_xfrms);
-+ if (err < 0)
- goto dropdst;
-+
-+ if (num_pols) {
-+ if (num_xfrms <= 0) {
-+ drop_pols = num_pols;
-+ goto no_transform;
-+ }
-+
-+ xdst = xfrm_resolve_and_create_bundle(
-+ pols, num_pols, fl,
-+ family, dst_orig);
-+ if (IS_ERR(xdst)) {
-+ xfrm_pols_put(pols, num_pols);
-+ err = PTR_ERR(xdst);
-+ goto dropdst;
-+ }
-+
-+ spin_lock_bh(&xfrm_policy_sk_bundle_lock);
-+ xdst->u.dst.next = xfrm_policy_sk_bundles;
-+ xfrm_policy_sk_bundles = &xdst->u.dst;
-+ spin_unlock_bh(&xfrm_policy_sk_bundle_lock);
-+
-+ route = xdst->route;
- }
- }
-
-- if (!policy) {
-- struct flow_cache_object *flo;
--
-+ if (xdst == NULL) {
- /* To accelerate a bit... */
- if ((dst_orig->flags & DST_NOXFRM) ||
- !net->xfrm.policy_count[XFRM_POLICY_OUT])
- goto nopol;
-
-- flo = flow_cache_lookup(net, fl, dst_orig->ops->family,
-- dir, xfrm_policy_lookup, NULL);
-- err = PTR_ERR(flo);
-+ flo = flow_cache_lookup(net, fl, family, dir,
-+ xfrm_bundle_lookup, dst_orig);
-+ if (flo == NULL)
-+ goto nopol;
- if (IS_ERR(flo)) {
-- XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
-+ err = PTR_ERR(flo);
- goto dropdst;
- }
-- if (flo)
-- policy = container_of(flo, struct xfrm_policy, flo);
-- else
-- policy = NULL;
-+ xdst = container_of(flo, struct xfrm_dst, flo);
-+
-+ num_pols = xdst->num_pols;
-+ num_xfrms = xdst->num_xfrms;
-+ memcpy(pols, xdst->pols, sizeof(struct xfrm_policy*) * num_pols);
-+ route = xdst->route;
-+ }
-+
-+ dst = &xdst->u.dst;
-+ if (route == NULL && num_xfrms > 0) {
-+ /* The only case when xfrm_bundle_lookup() returns a
-+ * bundle with null route, is when the template could
-+ * not be resolved. It means policies are there, but
-+ * bundle could not be created, since we don't yet
-+ * have the xfrm_state's. We need to wait for KM to
-+ * negotiate new SA's or bail out with error.*/
-+ if (net->xfrm.sysctl_larval_drop) {
-+ /* EREMOTE tells the caller to generate
-+ * a one-shot blackhole route. */
-+ dst_release(dst);
-+ xfrm_pols_put(pols, num_pols);
-+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
-+ return -EREMOTE;
-+ }
-+ if (flags & XFRM_LOOKUP_WAIT) {
-+ DECLARE_WAITQUEUE(wait, current);
-+
-+ add_wait_queue(&net->xfrm.km_waitq, &wait);
-+ set_current_state(TASK_INTERRUPTIBLE);
-+ schedule();
-+ set_current_state(TASK_RUNNING);
-+ remove_wait_queue(&net->xfrm.km_waitq, &wait);
-+
-+ if (!signal_pending(current)) {
-+ dst_release(dst);
-+ goto restart;
-+ }
-+
-+ err = -ERESTART;
-+ } else
-+ err = -EAGAIN;
-+
-+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
-+ goto error;
- }
-
-- if (!policy)
-+no_transform:
-+ if (num_pols == 0)
- goto nopol;
-
-- family = dst_orig->ops->family;
-- pols[0] = policy;
-- npols ++;
-- xfrm_nr += pols[0]->xfrm_nr;
--
-- err = -ENOENT;
-- if ((flags & XFRM_LOOKUP_ICMP) && !(policy->flags & XFRM_POLICY_ICMP))
-+ if ((flags & XFRM_LOOKUP_ICMP) &&
-+ !(pols[0]->flags & XFRM_POLICY_ICMP)) {
-+ err = -ENOENT;
- goto error;
-+ }
-
-- policy->curlft.use_time = get_seconds();
-+ for (i = 0; i < num_pols; i++)
-+ pols[i]->curlft.use_time = get_seconds();
-
-- switch (policy->action) {
-- default:
-- case XFRM_POLICY_BLOCK:
-+ if (num_xfrms < 0) {
- /* Prohibit the flow */
- XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLBLOCK);
- err = -EPERM;
- goto error;
--
-- case XFRM_POLICY_ALLOW:
--#ifndef CONFIG_XFRM_SUB_POLICY
-- if (policy->xfrm_nr == 0) {
-- /* Flow passes not transformed. */
-- xfrm_pol_put(policy);
-- return 0;
-- }
--#endif
--
-- /* Try to find matching bundle.
-- *
-- * LATER: help from flow cache. It is optional, this
-- * is required only for output policy.
-- */
-- dst = xfrm_find_bundle(fl, policy, family);
-- if (IS_ERR(dst)) {
-- XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
-- err = PTR_ERR(dst);
-- goto error;
-- }
--
-- if (dst)
-- break;
--
--#ifdef CONFIG_XFRM_SUB_POLICY
-- if (pols[0]->type != 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;
-- }
-- npols ++;
-- xfrm_nr += pols[1]->xfrm_nr;
-- }
-- }
--
-- /*
-- * Because neither flowi nor bundle information knows about
-- * transformation template size. On more than one policy usage
-- * we can realize whether all of them is bypass or not after
-- * they are searched. See above not-transformed bypass
-- * is surrounded by non-sub policy configuration, too.
-- */
-- if (xfrm_nr == 0) {
-- /* Flow passes not transformed. */
-- xfrm_pols_put(pols, npols);
-- return 0;
-- }
--
--#endif
-- nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
--
-- if (unlikely(nx<0)) {
-- err = nx;
-- 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(&net->xfrm.km_waitq, &wait);
-- set_current_state(TASK_INTERRUPTIBLE);
-- schedule();
-- set_current_state(TASK_RUNNING);
-- 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;
-- }
-- if (nx == -EAGAIN ||
-- genid != atomic_read(&flow_cache_genid)) {
-- xfrm_pols_put(pols, npols);
-- goto restart;
-- }
-- err = nx;
-- }
-- if (err < 0) {
-- XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
-- goto error;
-- }
-- }
-- if (nx == 0) {
-- /* Flow passes not transformed. */
-- xfrm_pols_put(pols, npols);
-- return 0;
-- }
--
-- 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++)
-- pol_dead |= pols[pi]->walk.dead;
--
-- write_lock_bh(&policy->lock);
-- if (unlikely(pol_dead || stale_bundle(dst))) {
-- /* Wow! While we worked on resolving, this
-- * policy has gone. Retry. It is not paranoia,
-- * we just cannot enlist new bundle to dead object.
-- * We can't enlist stable bundles either.
-- */
-- write_unlock_bh(&policy->lock);
-- 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);
-- write_unlock_bh(&policy->lock);
-+ } else if (num_xfrms > 0) {
-+ /* Flow transformed */
-+ *dst_p = dst;
-+ dst_release(dst_orig);
-+ } else {
-+ /* Flow passes untransformed */
-+ dst_release(dst);
- }
-- *dst_p = dst;
-- dst_release(dst_orig);
-- xfrm_pols_put(pols, npols);
-+ok:
-+ xfrm_pols_put(pols, drop_pols);
- return 0;
-
-+nopol:
-+ if (!(flags & XFRM_LOOKUP_ICMP))
-+ goto ok;
-+ err = -ENOENT;
- error:
-- xfrm_pols_put(pols, npols);
-+ dst_release(dst);
- dropdst:
- dst_release(dst_orig);
- *dst_p = NULL;
-+ xfrm_pols_put(pols, drop_pols);
- return err;
--
--nopol:
-- err = -ENOENT;
-- if (flags & XFRM_LOOKUP_ICMP)
-- goto dropdst;
-- return 0;
- }
- EXPORT_SYMBOL(__xfrm_lookup);
-
-@@ -2134,71 +2225,24 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
- return dst;
- }
-
--static void prune_one_bundle(struct xfrm_policy *pol, int (*func)(struct dst_entry *), struct dst_entry **gc_list_p)
--{
-- struct dst_entry *dst, **dstp;
--
-- write_lock(&pol->lock);
-- dstp = &pol->bundles;
-- while ((dst=*dstp) != NULL) {
-- if (func(dst)) {
-- *dstp = dst->next;
-- dst->next = *gc_list_p;
-- *gc_list_p = dst;
-- } else {
-- dstp = &dst->next;
-- }
-- }
-- write_unlock(&pol->lock);
--}
--
--static void xfrm_prune_bundles(struct net *net, int (*func)(struct dst_entry *))
-+static void __xfrm_garbage_collect(struct net *net)
- {
-- struct dst_entry *gc_list = NULL;
-- int dir;
-+ struct dst_entry *head, *next;
-
-- read_lock_bh(&xfrm_policy_lock);
-- for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
-- struct xfrm_policy *pol;
-- struct hlist_node *entry;
-- struct hlist_head *table;
-- int i;
-+ flow_cache_flush();
-
-- hlist_for_each_entry(pol, entry,
-- &net->xfrm.policy_inexact[dir], bydst)
-- prune_one_bundle(pol, func, &gc_list);
-+ spin_lock_bh(&xfrm_policy_sk_bundle_lock);
-+ head = xfrm_policy_sk_bundles;
-+ xfrm_policy_sk_bundles = NULL;
-+ spin_unlock_bh(&xfrm_policy_sk_bundle_lock);
-
-- 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);
-- }
-- }
-- read_unlock_bh(&xfrm_policy_lock);
--
-- while (gc_list) {
-- struct dst_entry *dst = gc_list;
-- gc_list = dst->next;
-- dst_free(dst);
-+ while (head) {
-+ next = head->next;
-+ dst_free(head);
-+ head = next;
- }
- }
-
--static int unused_bundle(struct dst_entry *dst)
--{
-- return !atomic_read(&dst->__refcnt);
--}
--
--static void __xfrm_garbage_collect(struct net *net)
--{
-- xfrm_prune_bundles(net, unused_bundle);
--}
--
--static int xfrm_flush_bundles(struct net *net)
--{
-- xfrm_prune_bundles(net, stale_bundle);
-- return 0;
--}
--
- static void xfrm_init_pmtu(struct dst_entry *dst)
- {
- do {
-@@ -2256,7 +2300,9 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
- return 0;
- if (dst->xfrm->km.state != XFRM_STATE_VALID)
- return 0;
-- if (xdst->genid != dst->xfrm->genid)
-+ if (xdst->xfrm_genid != dst->xfrm->genid)
-+ return 0;
-+ if (xdst->policy_genid != atomic_read(&xdst->pols[0]->genid))
- return 0;
-
- if (strict && fl &&
-@@ -2383,7 +2429,7 @@ static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void
-
- switch (event) {
- case NETDEV_DOWN:
-- xfrm_flush_bundles(dev_net(dev));
-+ __xfrm_garbage_collect(dev_net(dev));
- }
- return NOTIFY_DONE;
- }
-@@ -2714,7 +2760,6 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol,
- struct xfrm_migrate *m, int num_migrate)
- {
- struct xfrm_migrate *mp;
-- struct dst_entry *dst;
- int i, j, n = 0;
-
- write_lock_bh(&pol->lock);
-@@ -2739,10 +2784,7 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol,
- sizeof(pol->xfrm_vec[i].saddr));
- pol->xfrm_vec[i].encap_family = mp->new_family;
- /* flush bundles */
-- while ((dst = pol->bundles) != NULL) {
-- pol->bundles = dst->next;
-- dst_free(dst);
-- }
-+ atomic_inc(&pol->genid);
- }
- }
-
---
-1.7.0.2
-