|
@@ -24,6 +24,7 @@
|
|
|
#include <linux/netfilter.h>
|
|
#include <linux/netfilter.h>
|
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
|
#include <linux/cache.h>
|
|
#include <linux/cache.h>
|
|
|
|
|
+#include <linux/cpu.h>
|
|
|
#include <linux/audit.h>
|
|
#include <linux/audit.h>
|
|
|
#include <net/dst.h>
|
|
#include <net/dst.h>
|
|
|
#include <net/flow.h>
|
|
#include <net/flow.h>
|
|
@@ -44,6 +45,8 @@ struct xfrm_flo {
|
|
|
u8 flags;
|
|
u8 flags;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+static DEFINE_PER_CPU(struct xfrm_dst *, xfrm_last_dst);
|
|
|
|
|
+static struct work_struct *xfrm_pcpu_work __read_mostly;
|
|
|
static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
|
|
static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
|
|
|
static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
|
|
static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
|
|
|
__read_mostly;
|
|
__read_mostly;
|
|
@@ -246,36 +249,6 @@ expired:
|
|
|
xfrm_pol_put(xp);
|
|
xfrm_pol_put(xp);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static struct flow_cache_object *xfrm_policy_flo_get(struct flow_cache_object *flo)
|
|
|
|
|
-{
|
|
|
|
|
- struct xfrm_policy *pol = container_of(flo, struct xfrm_policy, flo);
|
|
|
|
|
-
|
|
|
|
|
- if (unlikely(pol->walk.dead))
|
|
|
|
|
- flo = NULL;
|
|
|
|
|
- else
|
|
|
|
|
- xfrm_pol_hold(pol);
|
|
|
|
|
-
|
|
|
|
|
- return flo;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static int xfrm_policy_flo_check(struct flow_cache_object *flo)
|
|
|
|
|
-{
|
|
|
|
|
- struct xfrm_policy *pol = container_of(flo, struct xfrm_policy, flo);
|
|
|
|
|
-
|
|
|
|
|
- return !pol->walk.dead;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static void xfrm_policy_flo_delete(struct flow_cache_object *flo)
|
|
|
|
|
-{
|
|
|
|
|
- xfrm_pol_put(container_of(flo, struct xfrm_policy, flo));
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static const struct flow_cache_ops xfrm_policy_fc_ops = {
|
|
|
|
|
- .get = xfrm_policy_flo_get,
|
|
|
|
|
- .check = xfrm_policy_flo_check,
|
|
|
|
|
- .delete = xfrm_policy_flo_delete,
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
/* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2
|
|
/* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2
|
|
|
* SPD calls.
|
|
* SPD calls.
|
|
|
*/
|
|
*/
|
|
@@ -298,7 +271,6 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
|
|
|
(unsigned long)policy);
|
|
(unsigned long)policy);
|
|
|
setup_timer(&policy->polq.hold_timer, xfrm_policy_queue_process,
|
|
setup_timer(&policy->polq.hold_timer, xfrm_policy_queue_process,
|
|
|
(unsigned long)policy);
|
|
(unsigned long)policy);
|
|
|
- policy->flo.ops = &xfrm_policy_fc_ops;
|
|
|
|
|
}
|
|
}
|
|
|
return policy;
|
|
return policy;
|
|
|
}
|
|
}
|
|
@@ -798,7 +770,6 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
|
|
|
else
|
|
else
|
|
|
hlist_add_head(&policy->bydst, chain);
|
|
hlist_add_head(&policy->bydst, chain);
|
|
|
__xfrm_policy_link(policy, dir);
|
|
__xfrm_policy_link(policy, dir);
|
|
|
- atomic_inc(&net->xfrm.flow_cache_genid);
|
|
|
|
|
|
|
|
|
|
/* After previous checking, family can either be AF_INET or AF_INET6 */
|
|
/* After previous checking, family can either be AF_INET or AF_INET6 */
|
|
|
if (policy->family == AF_INET)
|
|
if (policy->family == AF_INET)
|
|
@@ -1004,6 +975,8 @@ int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
|
|
|
}
|
|
}
|
|
|
if (!cnt)
|
|
if (!cnt)
|
|
|
err = -ESRCH;
|
|
err = -ESRCH;
|
|
|
|
|
+ else
|
|
|
|
|
+ xfrm_policy_cache_flush();
|
|
|
out:
|
|
out:
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
|
|
return err;
|
|
return err;
|
|
@@ -1175,7 +1148,7 @@ fail:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static struct xfrm_policy *
|
|
static struct xfrm_policy *
|
|
|
-__xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir)
|
|
|
|
|
|
|
+xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir)
|
|
|
{
|
|
{
|
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
|
|
struct xfrm_policy *pol;
|
|
struct xfrm_policy *pol;
|
|
@@ -1187,61 +1160,6 @@ __xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir
|
|
|
return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
|
|
return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static int flow_to_policy_dir(int dir)
|
|
|
|
|
-{
|
|
|
|
|
- if (XFRM_POLICY_IN == FLOW_DIR_IN &&
|
|
|
|
|
- XFRM_POLICY_OUT == FLOW_DIR_OUT &&
|
|
|
|
|
- XFRM_POLICY_FWD == FLOW_DIR_FWD)
|
|
|
|
|
- return dir;
|
|
|
|
|
-
|
|
|
|
|
- switch (dir) {
|
|
|
|
|
- default:
|
|
|
|
|
- case FLOW_DIR_IN:
|
|
|
|
|
- return XFRM_POLICY_IN;
|
|
|
|
|
- case FLOW_DIR_OUT:
|
|
|
|
|
- return XFRM_POLICY_OUT;
|
|
|
|
|
- case FLOW_DIR_FWD:
|
|
|
|
|
- return XFRM_POLICY_FWD;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static struct flow_cache_object *
|
|
|
|
|
-xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family,
|
|
|
|
|
- u8 dir, struct flow_cache_object *old_obj, void *ctx)
|
|
|
|
|
-{
|
|
|
|
|
- struct xfrm_policy *pol;
|
|
|
|
|
-
|
|
|
|
|
- if (old_obj)
|
|
|
|
|
- xfrm_pol_put(container_of(old_obj, struct xfrm_policy, flo));
|
|
|
|
|
-
|
|
|
|
|
- pol = __xfrm_policy_lookup(net, fl, family, flow_to_policy_dir(dir));
|
|
|
|
|
- if (IS_ERR_OR_NULL(pol))
|
|
|
|
|
- return ERR_CAST(pol);
|
|
|
|
|
-
|
|
|
|
|
- /* Resolver returns two references:
|
|
|
|
|
- * one for cache and one for caller of flow_cache_lookup() */
|
|
|
|
|
- xfrm_pol_hold(pol);
|
|
|
|
|
-
|
|
|
|
|
- return &pol->flo;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static inline int policy_to_flow_dir(int dir)
|
|
|
|
|
-{
|
|
|
|
|
- if (XFRM_POLICY_IN == FLOW_DIR_IN &&
|
|
|
|
|
- XFRM_POLICY_OUT == FLOW_DIR_OUT &&
|
|
|
|
|
- XFRM_POLICY_FWD == FLOW_DIR_FWD)
|
|
|
|
|
- return dir;
|
|
|
|
|
- switch (dir) {
|
|
|
|
|
- default:
|
|
|
|
|
- case XFRM_POLICY_IN:
|
|
|
|
|
- return FLOW_DIR_IN;
|
|
|
|
|
- case XFRM_POLICY_OUT:
|
|
|
|
|
- return FLOW_DIR_OUT;
|
|
|
|
|
- case XFRM_POLICY_FWD:
|
|
|
|
|
- return FLOW_DIR_FWD;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
|
static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
|
|
const struct flowi *fl, u16 family)
|
|
const struct flowi *fl, u16 family)
|
|
|
{
|
|
{
|
|
@@ -1261,7 +1179,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
|
|
}
|
|
}
|
|
|
err = security_xfrm_policy_lookup(pol->security,
|
|
err = security_xfrm_policy_lookup(pol->security,
|
|
|
fl->flowi_secid,
|
|
fl->flowi_secid,
|
|
|
- policy_to_flow_dir(dir));
|
|
|
|
|
|
|
+ dir);
|
|
|
if (!err) {
|
|
if (!err) {
|
|
|
if (!xfrm_pol_hold_rcu(pol))
|
|
if (!xfrm_pol_hold_rcu(pol))
|
|
|
goto again;
|
|
goto again;
|
|
@@ -1545,58 +1463,6 @@ static int xfrm_get_tos(const struct flowi *fl, int family)
|
|
|
return tos;
|
|
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 if (dst->flags & DST_XFRM_QUEUE) {
|
|
|
|
|
- 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;
|
|
|
|
|
-
|
|
|
|
|
- /* Mark DST_OBSOLETE_DEAD to fail the next xfrm_dst_check() */
|
|
|
|
|
- dst->obsolete = DST_OBSOLETE_DEAD;
|
|
|
|
|
- dst_release_immediate(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(struct net *net, int family)
|
|
static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family)
|
|
|
{
|
|
{
|
|
|
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
|
|
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
|
|
@@ -1624,7 +1490,6 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family)
|
|
|
struct dst_entry *dst = &xdst->u.dst;
|
|
struct dst_entry *dst = &xdst->u.dst;
|
|
|
|
|
|
|
|
memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst));
|
|
memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst));
|
|
|
- xdst->flo.ops = &xfrm_bundle_fc_ops;
|
|
|
|
|
} else
|
|
} else
|
|
|
xdst = ERR_PTR(-ENOBUFS);
|
|
xdst = ERR_PTR(-ENOBUFS);
|
|
|
|
|
|
|
@@ -1840,6 +1705,102 @@ static int xfrm_expand_policies(const struct flowi *fl, u16 family,
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static void xfrm_last_dst_update(struct xfrm_dst *xdst, struct xfrm_dst *old)
|
|
|
|
|
+{
|
|
|
|
|
+ this_cpu_write(xfrm_last_dst, xdst);
|
|
|
|
|
+ if (old)
|
|
|
|
|
+ dst_release(&old->u.dst);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void __xfrm_pcpu_work_fn(void)
|
|
|
|
|
+{
|
|
|
|
|
+ struct xfrm_dst *old;
|
|
|
|
|
+
|
|
|
|
|
+ old = this_cpu_read(xfrm_last_dst);
|
|
|
|
|
+ if (old && !xfrm_bundle_ok(old))
|
|
|
|
|
+ xfrm_last_dst_update(NULL, old);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void xfrm_pcpu_work_fn(struct work_struct *work)
|
|
|
|
|
+{
|
|
|
|
|
+ local_bh_disable();
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
|
+ __xfrm_pcpu_work_fn();
|
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
+ local_bh_enable();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void xfrm_policy_cache_flush(void)
|
|
|
|
|
+{
|
|
|
|
|
+ struct xfrm_dst *old;
|
|
|
|
|
+ bool found = 0;
|
|
|
|
|
+ int cpu;
|
|
|
|
|
+
|
|
|
|
|
+ local_bh_disable();
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
|
+ for_each_possible_cpu(cpu) {
|
|
|
|
|
+ old = per_cpu(xfrm_last_dst, cpu);
|
|
|
|
|
+ if (old && !xfrm_bundle_ok(old)) {
|
|
|
|
|
+ if (smp_processor_id() == cpu) {
|
|
|
|
|
+ __xfrm_pcpu_work_fn();
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ found = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
+ local_bh_enable();
|
|
|
|
|
+
|
|
|
|
|
+ if (!found)
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ get_online_cpus();
|
|
|
|
|
+
|
|
|
|
|
+ for_each_possible_cpu(cpu) {
|
|
|
|
|
+ bool bundle_release;
|
|
|
|
|
+
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
|
+ old = per_cpu(xfrm_last_dst, cpu);
|
|
|
|
|
+ bundle_release = old && !xfrm_bundle_ok(old);
|
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
+
|
|
|
|
|
+ if (!bundle_release)
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ if (cpu_online(cpu)) {
|
|
|
|
|
+ schedule_work_on(cpu, &xfrm_pcpu_work[cpu]);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
|
+ old = per_cpu(xfrm_last_dst, cpu);
|
|
|
|
|
+ if (old && !xfrm_bundle_ok(old)) {
|
|
|
|
|
+ per_cpu(xfrm_last_dst, cpu) = NULL;
|
|
|
|
|
+ dst_release(&old->u.dst);
|
|
|
|
|
+ }
|
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ put_online_cpus();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static bool xfrm_pol_dead(struct xfrm_dst *xdst)
|
|
|
|
|
+{
|
|
|
|
|
+ unsigned int num_pols = xdst->num_pols;
|
|
|
|
|
+ unsigned int pol_dead = 0, i;
|
|
|
|
|
+
|
|
|
|
|
+ for (i = 0; i < num_pols; i++)
|
|
|
|
|
+ pol_dead |= xdst->pols[i]->walk.dead;
|
|
|
|
|
+
|
|
|
|
|
+ /* Mark DST_OBSOLETE_DEAD to fail the next xfrm_dst_check() */
|
|
|
|
|
+ if (pol_dead)
|
|
|
|
|
+ xdst->u.dst.obsolete = DST_OBSOLETE_DEAD;
|
|
|
|
|
+
|
|
|
|
|
+ return pol_dead;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static struct xfrm_dst *
|
|
static struct xfrm_dst *
|
|
|
xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
|
|
xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
|
|
|
const struct flowi *fl, u16 family,
|
|
const struct flowi *fl, u16 family,
|
|
@@ -1847,10 +1808,22 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
|
|
|
{
|
|
{
|
|
|
struct net *net = xp_net(pols[0]);
|
|
struct net *net = xp_net(pols[0]);
|
|
|
struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
|
|
struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
|
|
|
|
|
+ struct xfrm_dst *xdst, *old;
|
|
|
struct dst_entry *dst;
|
|
struct dst_entry *dst;
|
|
|
- struct xfrm_dst *xdst;
|
|
|
|
|
int err;
|
|
int err;
|
|
|
|
|
|
|
|
|
|
+ xdst = this_cpu_read(xfrm_last_dst);
|
|
|
|
|
+ if (xdst &&
|
|
|
|
|
+ xdst->u.dst.dev == dst_orig->dev &&
|
|
|
|
|
+ xdst->num_pols == num_pols &&
|
|
|
|
|
+ !xfrm_pol_dead(xdst) &&
|
|
|
|
|
+ memcmp(xdst->pols, pols,
|
|
|
|
|
+ sizeof(struct xfrm_policy *) * num_pols) == 0) {
|
|
|
|
|
+ dst_hold(&xdst->u.dst);
|
|
|
|
|
+ return xdst;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ old = xdst;
|
|
|
/* Try to instantiate a bundle */
|
|
/* Try to instantiate a bundle */
|
|
|
err = xfrm_tmpl_resolve(pols, num_pols, fl, xfrm, family);
|
|
err = xfrm_tmpl_resolve(pols, num_pols, fl, xfrm, family);
|
|
|
if (err <= 0) {
|
|
if (err <= 0) {
|
|
@@ -1871,6 +1844,9 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
|
|
|
memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);
|
|
memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);
|
|
|
xdst->policy_genid = atomic_read(&pols[0]->genid);
|
|
xdst->policy_genid = atomic_read(&pols[0]->genid);
|
|
|
|
|
|
|
|
|
|
+ atomic_set(&xdst->u.dst.__refcnt, 2);
|
|
|
|
|
+ xfrm_last_dst_update(xdst, old);
|
|
|
|
|
+
|
|
|
return xdst;
|
|
return xdst;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2051,86 +2027,39 @@ free_dst:
|
|
|
goto out;
|
|
goto out;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static struct flow_cache_object *
|
|
|
|
|
-xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
|
|
|
|
|
- struct flow_cache_object *oldflo, void *ctx)
|
|
|
|
|
|
|
+static struct xfrm_dst *
|
|
|
|
|
+xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct xfrm_flo *xflo)
|
|
|
{
|
|
{
|
|
|
- struct xfrm_flo *xflo = (struct xfrm_flo *)ctx;
|
|
|
|
|
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
|
|
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) {
|
|
|
|
|
- /* Mark DST_OBSOLETE_DEAD to fail the next
|
|
|
|
|
- * xfrm_dst_check()
|
|
|
|
|
- */
|
|
|
|
|
- xdst->u.dst.obsolete = DST_OBSOLETE_DEAD;
|
|
|
|
|
- dst_release_immediate(&xdst->u.dst);
|
|
|
|
|
- xdst = NULL;
|
|
|
|
|
- num_pols = 0;
|
|
|
|
|
- num_xfrms = 0;
|
|
|
|
|
- oldflo = NULL;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ int num_pols = 0, num_xfrms = 0, err;
|
|
|
|
|
+ struct xfrm_dst *xdst;
|
|
|
|
|
|
|
|
/* Resolve policies to use if we couldn't get them from
|
|
/* Resolve policies to use if we couldn't get them from
|
|
|
* previous cache entry */
|
|
* previous cache entry */
|
|
|
- if (xdst == NULL) {
|
|
|
|
|
- num_pols = 1;
|
|
|
|
|
- pols[0] = __xfrm_policy_lookup(net, fl, family,
|
|
|
|
|
- flow_to_policy_dir(dir));
|
|
|
|
|
- err = xfrm_expand_policies(fl, family, pols,
|
|
|
|
|
|
|
+ num_pols = 1;
|
|
|
|
|
+ pols[0] = xfrm_policy_lookup(net, fl, family, dir);
|
|
|
|
|
+ err = xfrm_expand_policies(fl, family, pols,
|
|
|
&num_pols, &num_xfrms);
|
|
&num_pols, &num_xfrms);
|
|
|
- if (err < 0)
|
|
|
|
|
- goto inc_error;
|
|
|
|
|
- if (num_pols == 0)
|
|
|
|
|
- return NULL;
|
|
|
|
|
- if (num_xfrms <= 0)
|
|
|
|
|
- goto make_dummy_bundle;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ 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,
|
|
|
|
|
|
|
+ xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family,
|
|
|
xflo->dst_orig);
|
|
xflo->dst_orig);
|
|
|
- if (IS_ERR(new_xdst)) {
|
|
|
|
|
- err = PTR_ERR(new_xdst);
|
|
|
|
|
|
|
+ if (IS_ERR(xdst)) {
|
|
|
|
|
+ err = PTR_ERR(xdst);
|
|
|
if (err != -EAGAIN)
|
|
if (err != -EAGAIN)
|
|
|
goto error;
|
|
goto error;
|
|
|
- if (oldflo == NULL)
|
|
|
|
|
- goto make_dummy_bundle;
|
|
|
|
|
- dst_hold(&xdst->u.dst);
|
|
|
|
|
- return oldflo;
|
|
|
|
|
- } else if (new_xdst == NULL) {
|
|
|
|
|
|
|
+ goto make_dummy_bundle;
|
|
|
|
|
+ } else if (xdst == NULL) {
|
|
|
num_xfrms = 0;
|
|
num_xfrms = 0;
|
|
|
- if (oldflo == NULL)
|
|
|
|
|
- goto make_dummy_bundle;
|
|
|
|
|
- xdst->num_xfrms = 0;
|
|
|
|
|
- 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;
|
|
|
|
|
- /* Mark DST_OBSOLETE_DEAD to fail the next xfrm_dst_check() */
|
|
|
|
|
- xdst->u.dst.obsolete = DST_OBSOLETE_DEAD;
|
|
|
|
|
- dst_release_immediate(&xdst->u.dst);
|
|
|
|
|
|
|
+ goto make_dummy_bundle;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* We do need to return one reference for original caller */
|
|
|
|
|
- dst_hold(&new_xdst->u.dst);
|
|
|
|
|
- return &new_xdst->flo;
|
|
|
|
|
|
|
+ return xdst;
|
|
|
|
|
|
|
|
make_dummy_bundle:
|
|
make_dummy_bundle:
|
|
|
/* We found policies, but there's no bundles to instantiate:
|
|
/* We found policies, but there's no bundles to instantiate:
|
|
@@ -2146,17 +2075,12 @@ make_dummy_bundle:
|
|
|
memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);
|
|
memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);
|
|
|
|
|
|
|
|
dst_hold(&xdst->u.dst);
|
|
dst_hold(&xdst->u.dst);
|
|
|
- return &xdst->flo;
|
|
|
|
|
|
|
+ return xdst;
|
|
|
|
|
|
|
|
inc_error:
|
|
inc_error:
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
|
|
|
error:
|
|
error:
|
|
|
- if (xdst != NULL) {
|
|
|
|
|
- /* Mark DST_OBSOLETE_DEAD to fail the next xfrm_dst_check() */
|
|
|
|
|
- xdst->u.dst.obsolete = DST_OBSOLETE_DEAD;
|
|
|
|
|
- dst_release_immediate(&xdst->u.dst);
|
|
|
|
|
- } else
|
|
|
|
|
- xfrm_pols_put(pols, num_pols);
|
|
|
|
|
|
|
+ xfrm_pols_put(pols, num_pols);
|
|
|
return ERR_PTR(err);
|
|
return ERR_PTR(err);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2187,11 +2111,10 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
|
|
const struct sock *sk, int flags)
|
|
const struct sock *sk, int flags)
|
|
|
{
|
|
{
|
|
|
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
|
|
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
|
|
|
- struct flow_cache_object *flo;
|
|
|
|
|
struct xfrm_dst *xdst;
|
|
struct xfrm_dst *xdst;
|
|
|
struct dst_entry *dst, *route;
|
|
struct dst_entry *dst, *route;
|
|
|
u16 family = dst_orig->ops->family;
|
|
u16 family = dst_orig->ops->family;
|
|
|
- u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
|
|
|
|
|
|
|
+ u8 dir = XFRM_POLICY_OUT;
|
|
|
int i, err, num_pols, num_xfrms = 0, drop_pols = 0;
|
|
int i, err, num_pols, num_xfrms = 0, drop_pols = 0;
|
|
|
|
|
|
|
|
dst = NULL;
|
|
dst = NULL;
|
|
@@ -2242,15 +2165,13 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
|
|
!net->xfrm.policy_count[XFRM_POLICY_OUT])
|
|
!net->xfrm.policy_count[XFRM_POLICY_OUT])
|
|
|
goto nopol;
|
|
goto nopol;
|
|
|
|
|
|
|
|
- flo = flow_cache_lookup(net, fl, family, dir,
|
|
|
|
|
- xfrm_bundle_lookup, &xflo);
|
|
|
|
|
- if (flo == NULL)
|
|
|
|
|
|
|
+ xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo);
|
|
|
|
|
+ if (xdst == NULL)
|
|
|
goto nopol;
|
|
goto nopol;
|
|
|
- if (IS_ERR(flo)) {
|
|
|
|
|
- err = PTR_ERR(flo);
|
|
|
|
|
|
|
+ if (IS_ERR(xdst)) {
|
|
|
|
|
+ err = PTR_ERR(xdst);
|
|
|
goto dropdst;
|
|
goto dropdst;
|
|
|
}
|
|
}
|
|
|
- xdst = container_of(flo, struct xfrm_dst, flo);
|
|
|
|
|
|
|
|
|
|
num_pols = xdst->num_pols;
|
|
num_pols = xdst->num_pols;
|
|
|
num_xfrms = xdst->num_xfrms;
|
|
num_xfrms = xdst->num_xfrms;
|
|
@@ -2449,12 +2370,10 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
|
|
int pi;
|
|
int pi;
|
|
|
int reverse;
|
|
int reverse;
|
|
|
struct flowi fl;
|
|
struct flowi fl;
|
|
|
- u8 fl_dir;
|
|
|
|
|
int xerr_idx = -1;
|
|
int xerr_idx = -1;
|
|
|
|
|
|
|
|
reverse = dir & ~XFRM_POLICY_MASK;
|
|
reverse = dir & ~XFRM_POLICY_MASK;
|
|
|
dir &= XFRM_POLICY_MASK;
|
|
dir &= XFRM_POLICY_MASK;
|
|
|
- fl_dir = policy_to_flow_dir(dir);
|
|
|
|
|
|
|
|
|
|
if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) {
|
|
if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) {
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
|
|
@@ -2486,16 +2405,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!pol) {
|
|
|
|
|
- struct flow_cache_object *flo;
|
|
|
|
|
-
|
|
|
|
|
- flo = flow_cache_lookup(net, &fl, family, fl_dir,
|
|
|
|
|
- xfrm_policy_lookup, NULL);
|
|
|
|
|
- if (IS_ERR_OR_NULL(flo))
|
|
|
|
|
- pol = ERR_CAST(flo);
|
|
|
|
|
- else
|
|
|
|
|
- pol = container_of(flo, struct xfrm_policy, flo);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!pol)
|
|
|
|
|
+ pol = xfrm_policy_lookup(net, &fl, family, dir);
|
|
|
|
|
|
|
|
if (IS_ERR(pol)) {
|
|
if (IS_ERR(pol)) {
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
|
|
@@ -2641,11 +2552,9 @@ static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie)
|
|
|
* notice. That's what we are validating here via the
|
|
* notice. That's what we are validating here via the
|
|
|
* stale_bundle() check.
|
|
* stale_bundle() check.
|
|
|
*
|
|
*
|
|
|
- * When an xdst is removed from flow cache, DST_OBSOLETE_DEAD will
|
|
|
|
|
- * be marked on it.
|
|
|
|
|
* When a dst is removed from the fib tree, DST_OBSOLETE_DEAD will
|
|
* When a dst is removed from the fib tree, DST_OBSOLETE_DEAD will
|
|
|
* be marked on it.
|
|
* be marked on it.
|
|
|
- * Both will force stable_bundle() to fail on any xdst bundle with
|
|
|
|
|
|
|
+ * This will force stale_bundle() to fail on any xdst bundle with
|
|
|
* this dst linked in it.
|
|
* this dst linked in it.
|
|
|
*/
|
|
*/
|
|
|
if (dst->obsolete < 0 && !stale_bundle(dst))
|
|
if (dst->obsolete < 0 && !stale_bundle(dst))
|
|
@@ -2685,18 +2594,6 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
|
|
|
return dst;
|
|
return dst;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void xfrm_garbage_collect(struct net *net)
|
|
|
|
|
-{
|
|
|
|
|
- flow_cache_flush(net);
|
|
|
|
|
-}
|
|
|
|
|
-EXPORT_SYMBOL(xfrm_garbage_collect);
|
|
|
|
|
-
|
|
|
|
|
-void xfrm_garbage_collect_deferred(struct net *net)
|
|
|
|
|
-{
|
|
|
|
|
- flow_cache_flush_deferred(net);
|
|
|
|
|
-}
|
|
|
|
|
-EXPORT_SYMBOL(xfrm_garbage_collect_deferred);
|
|
|
|
|
-
|
|
|
|
|
static void xfrm_init_pmtu(struct dst_entry *dst)
|
|
static void xfrm_init_pmtu(struct dst_entry *dst)
|
|
|
{
|
|
{
|
|
|
do {
|
|
do {
|
|
@@ -3034,14 +2931,9 @@ static int __net_init xfrm_net_init(struct net *net)
|
|
|
rv = xfrm_sysctl_init(net);
|
|
rv = xfrm_sysctl_init(net);
|
|
|
if (rv < 0)
|
|
if (rv < 0)
|
|
|
goto out_sysctl;
|
|
goto out_sysctl;
|
|
|
- rv = flow_cache_init(net);
|
|
|
|
|
- if (rv < 0)
|
|
|
|
|
- goto out;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
-out:
|
|
|
|
|
- xfrm_sysctl_fini(net);
|
|
|
|
|
out_sysctl:
|
|
out_sysctl:
|
|
|
xfrm_policy_fini(net);
|
|
xfrm_policy_fini(net);
|
|
|
out_policy:
|
|
out_policy:
|
|
@@ -3054,7 +2946,6 @@ out_statistics:
|
|
|
|
|
|
|
|
static void __net_exit xfrm_net_exit(struct net *net)
|
|
static void __net_exit xfrm_net_exit(struct net *net)
|
|
|
{
|
|
{
|
|
|
- flow_cache_fini(net);
|
|
|
|
|
xfrm_sysctl_fini(net);
|
|
xfrm_sysctl_fini(net);
|
|
|
xfrm_policy_fini(net);
|
|
xfrm_policy_fini(net);
|
|
|
xfrm_state_fini(net);
|
|
xfrm_state_fini(net);
|
|
@@ -3068,7 +2959,15 @@ static struct pernet_operations __net_initdata xfrm_net_ops = {
|
|
|
|
|
|
|
|
void __init xfrm_init(void)
|
|
void __init xfrm_init(void)
|
|
|
{
|
|
{
|
|
|
- flow_cache_hp_init();
|
|
|
|
|
|
|
+ int i;
|
|
|
|
|
+
|
|
|
|
|
+ xfrm_pcpu_work = kmalloc_array(NR_CPUS, sizeof(*xfrm_pcpu_work),
|
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
|
+ BUG_ON(!xfrm_pcpu_work);
|
|
|
|
|
+
|
|
|
|
|
+ for (i = 0; i < NR_CPUS; i++)
|
|
|
|
|
+ INIT_WORK(&xfrm_pcpu_work[i], xfrm_pcpu_work_fn);
|
|
|
|
|
+
|
|
|
register_pernet_subsys(&xfrm_net_ops);
|
|
register_pernet_subsys(&xfrm_net_ops);
|
|
|
seqcount_init(&xfrm_policy_hash_generation);
|
|
seqcount_init(&xfrm_policy_hash_generation);
|
|
|
xfrm_input_init();
|
|
xfrm_input_init();
|