Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec

Steffen Klassert says:

====================
pull request (net): ipsec 2014-09-22

We generate a blackhole or queueing route if a packet
matches an IPsec policy but a state can't be resolved.
Here we assume that dst_output() is called to kill
these packets. Unfortunately this assumption is not
true in all cases, so it is possible that these packets
leave the system without the necessary transformations.

This pull request contains two patches to fix this issue:

1) Fix for blackhole routed packets.

2) Fix for queue routed packets.

Both patches are serious stable candidates.

Please pull or let me know if there are problems.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2014-09-22 16:41:41 -04:00
commit 84de67b298
4 changed files with 60 additions and 14 deletions

View file

@ -480,6 +480,7 @@ void dst_init(void);
/* Flags for xfrm_lookup flags argument. */ /* Flags for xfrm_lookup flags argument. */
enum { enum {
XFRM_LOOKUP_ICMP = 1 << 0, XFRM_LOOKUP_ICMP = 1 << 0,
XFRM_LOOKUP_QUEUE = 1 << 1,
}; };
struct flowi; struct flowi;
@ -490,7 +491,16 @@ static inline struct dst_entry *xfrm_lookup(struct net *net,
int flags) int flags)
{ {
return dst_orig; return dst_orig;
} }
static inline struct dst_entry *xfrm_lookup_route(struct net *net,
struct dst_entry *dst_orig,
const struct flowi *fl,
struct sock *sk,
int flags)
{
return dst_orig;
}
static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst) static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
{ {
@ -502,6 +512,10 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
const struct flowi *fl, struct sock *sk, const struct flowi *fl, struct sock *sk,
int flags); int flags);
struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig,
const struct flowi *fl, struct sock *sk,
int flags);
/* skb attached with this dst needs transformation if dst->xfrm is valid */ /* skb attached with this dst needs transformation if dst->xfrm is valid */
static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst) static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
{ {

View file

@ -2265,9 +2265,9 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4,
return rt; return rt;
if (flp4->flowi4_proto) if (flp4->flowi4_proto)
rt = (struct rtable *) xfrm_lookup(net, &rt->dst, rt = (struct rtable *)xfrm_lookup_route(net, &rt->dst,
flowi4_to_flowi(flp4), flowi4_to_flowi(flp4),
sk, 0); sk, 0);
return rt; return rt;
} }

View file

@ -1009,7 +1009,7 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
if (final_dst) if (final_dst)
fl6->daddr = *final_dst; fl6->daddr = *final_dst;
return xfrm_lookup(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0); return xfrm_lookup_route(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0);
} }
EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow);
@ -1041,7 +1041,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
if (final_dst) if (final_dst)
fl6->daddr = *final_dst; fl6->daddr = *final_dst;
return xfrm_lookup(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0); return xfrm_lookup_route(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0);
} }
EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow);

View file

@ -39,6 +39,11 @@
#define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ)) #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ))
#define XFRM_MAX_QUEUE_LEN 100 #define XFRM_MAX_QUEUE_LEN 100
struct xfrm_flo {
struct dst_entry *dst_orig;
u8 flags;
};
static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock); static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO] static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO]
__read_mostly; __read_mostly;
@ -1877,13 +1882,14 @@ static int xdst_queue_output(struct sock *sk, struct sk_buff *skb)
} }
static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net, static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
struct dst_entry *dst, struct xfrm_flo *xflo,
const struct flowi *fl, const struct flowi *fl,
int num_xfrms, int num_xfrms,
u16 family) u16 family)
{ {
int err; int err;
struct net_device *dev; struct net_device *dev;
struct dst_entry *dst;
struct dst_entry *dst1; struct dst_entry *dst1;
struct xfrm_dst *xdst; struct xfrm_dst *xdst;
@ -1891,9 +1897,12 @@ static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
if (IS_ERR(xdst)) if (IS_ERR(xdst))
return xdst; return xdst;
if (net->xfrm.sysctl_larval_drop || num_xfrms <= 0) if (!(xflo->flags & XFRM_LOOKUP_QUEUE) ||
net->xfrm.sysctl_larval_drop ||
num_xfrms <= 0)
return xdst; return xdst;
dst = xflo->dst_orig;
dst1 = &xdst->u.dst; dst1 = &xdst->u.dst;
dst_hold(dst); dst_hold(dst);
xdst->route = dst; xdst->route = dst;
@ -1935,7 +1944,7 @@ static struct flow_cache_object *
xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
struct flow_cache_object *oldflo, void *ctx) struct flow_cache_object *oldflo, void *ctx)
{ {
struct dst_entry *dst_orig = (struct dst_entry *)ctx; 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; struct xfrm_dst *xdst, *new_xdst;
int num_pols = 0, num_xfrms = 0, i, err, pol_dead; int num_pols = 0, num_xfrms = 0, i, err, pol_dead;
@ -1976,7 +1985,8 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
goto make_dummy_bundle; goto make_dummy_bundle;
} }
new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, dst_orig); new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family,
xflo->dst_orig);
if (IS_ERR(new_xdst)) { if (IS_ERR(new_xdst)) {
err = PTR_ERR(new_xdst); err = PTR_ERR(new_xdst);
if (err != -EAGAIN) if (err != -EAGAIN)
@ -2010,7 +2020,7 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
/* We found policies, but there's no bundles to instantiate: /* We found policies, but there's no bundles to instantiate:
* either because the policy blocks, has no transformations or * either because the policy blocks, has no transformations or
* we could not build template (no xfrm_states).*/ * we could not build template (no xfrm_states).*/
xdst = xfrm_create_dummy_bundle(net, dst_orig, fl, num_xfrms, family); xdst = xfrm_create_dummy_bundle(net, xflo, fl, num_xfrms, family);
if (IS_ERR(xdst)) { if (IS_ERR(xdst)) {
xfrm_pols_put(pols, num_pols); xfrm_pols_put(pols, num_pols);
return ERR_CAST(xdst); return ERR_CAST(xdst);
@ -2104,13 +2114,18 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
} }
if (xdst == NULL) { if (xdst == NULL) {
struct xfrm_flo xflo;
xflo.dst_orig = dst_orig;
xflo.flags = flags;
/* To accelerate a bit... */ /* To accelerate a bit... */
if ((dst_orig->flags & DST_NOXFRM) || if ((dst_orig->flags & DST_NOXFRM) ||
!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, flo = flow_cache_lookup(net, fl, family, dir,
xfrm_bundle_lookup, dst_orig); xfrm_bundle_lookup, &xflo);
if (flo == NULL) if (flo == NULL)
goto nopol; goto nopol;
if (IS_ERR(flo)) { if (IS_ERR(flo)) {
@ -2138,7 +2153,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
xfrm_pols_put(pols, drop_pols); xfrm_pols_put(pols, drop_pols);
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
return make_blackhole(net, family, dst_orig); return ERR_PTR(-EREMOTE);
} }
err = -EAGAIN; err = -EAGAIN;
@ -2195,6 +2210,23 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
} }
EXPORT_SYMBOL(xfrm_lookup); EXPORT_SYMBOL(xfrm_lookup);
/* Callers of xfrm_lookup_route() must ensure a call to dst_output().
* Otherwise we may send out blackholed packets.
*/
struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig,
const struct flowi *fl,
struct sock *sk, int flags)
{
struct dst_entry *dst = xfrm_lookup(net, dst_orig, fl, sk,
flags | XFRM_LOOKUP_QUEUE);
if (IS_ERR(dst) && PTR_ERR(dst) == -EREMOTE)
return make_blackhole(net, dst_orig->ops->family, dst_orig);
return dst;
}
EXPORT_SYMBOL(xfrm_lookup_route);
static inline int static inline int
xfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl) xfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl)
{ {
@ -2460,7 +2492,7 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
skb_dst_force(skb); skb_dst_force(skb);
dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, 0); dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, XFRM_LOOKUP_QUEUE);
if (IS_ERR(dst)) { if (IS_ERR(dst)) {
res = 0; res = 0;
dst = NULL; dst = NULL;