Skip to content

Commit acf568e

Browse files
herbertxklassert
authored andcommitted
xfrm: Reinject transport-mode packets through tasklet
This is an old bugbear of mine: https://www.mail-archive.com/[email protected]/msg03894.html By crafting special packets, it is possible to cause recursion in our kernel when processing transport-mode packets at levels that are only limited by packet size. The easiest one is with DNAT, but an even worse one is where UDP encapsulation is used in which case you just have to insert an UDP encapsulation header in between each level of recursion. This patch avoids this problem by reinjecting tranport-mode packets through a tasklet. Fixes: b05e106 ("[IPV4/6]: Netfilter IPsec input hooks") Signed-off-by: Herbert Xu <[email protected]> Signed-off-by: Steffen Klassert <[email protected]>
1 parent d295027 commit acf568e

File tree

4 files changed

+80
-2
lines changed

4 files changed

+80
-2
lines changed

include/net/xfrm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,9 @@ int xfrm_init_state(struct xfrm_state *x);
15701570
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb);
15711571
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
15721572
int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
1573+
int xfrm_trans_queue(struct sk_buff *skb,
1574+
int (*finish)(struct net *, struct sock *,
1575+
struct sk_buff *));
15731576
int xfrm_output_resume(struct sk_buff *skb, int err);
15741577
int xfrm_output(struct sock *sk, struct sk_buff *skb);
15751578
int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);

net/ipv4/xfrm4_input.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb)
2323
return xfrm4_extract_header(skb);
2424
}
2525

26+
static int xfrm4_rcv_encap_finish2(struct net *net, struct sock *sk,
27+
struct sk_buff *skb)
28+
{
29+
return dst_input(skb);
30+
}
31+
2632
static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
2733
struct sk_buff *skb)
2834
{
@@ -33,7 +39,11 @@ static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
3339
iph->tos, skb->dev))
3440
goto drop;
3541
}
36-
return dst_input(skb);
42+
43+
if (xfrm_trans_queue(skb, xfrm4_rcv_encap_finish2))
44+
goto drop;
45+
46+
return 0;
3747
drop:
3848
kfree_skb(skb);
3949
return NET_RX_DROP;

net/ipv6/xfrm6_input.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi,
3232
}
3333
EXPORT_SYMBOL(xfrm6_rcv_spi);
3434

35+
static int xfrm6_transport_finish2(struct net *net, struct sock *sk,
36+
struct sk_buff *skb)
37+
{
38+
if (xfrm_trans_queue(skb, ip6_rcv_finish))
39+
__kfree_skb(skb);
40+
return -1;
41+
}
42+
3543
int xfrm6_transport_finish(struct sk_buff *skb, int async)
3644
{
3745
struct xfrm_offload *xo = xfrm_offload(skb);
@@ -56,7 +64,7 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
5664

5765
NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
5866
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
59-
ip6_rcv_finish);
67+
xfrm6_transport_finish2);
6068
return -1;
6169
}
6270

net/xfrm/xfrm_input.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,29 @@
88
*
99
*/
1010

11+
#include <linux/bottom_half.h>
12+
#include <linux/interrupt.h>
1113
#include <linux/slab.h>
1214
#include <linux/module.h>
1315
#include <linux/netdevice.h>
16+
#include <linux/percpu.h>
1417
#include <net/dst.h>
1518
#include <net/ip.h>
1619
#include <net/xfrm.h>
1720
#include <net/ip_tunnels.h>
1821
#include <net/ip6_tunnel.h>
1922

23+
struct xfrm_trans_tasklet {
24+
struct tasklet_struct tasklet;
25+
struct sk_buff_head queue;
26+
};
27+
28+
struct xfrm_trans_cb {
29+
int (*finish)(struct net *net, struct sock *sk, struct sk_buff *skb);
30+
};
31+
32+
#define XFRM_TRANS_SKB_CB(__skb) ((struct xfrm_trans_cb *)&((__skb)->cb[0]))
33+
2034
static struct kmem_cache *secpath_cachep __read_mostly;
2135

2236
static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
@@ -25,6 +39,8 @@ static struct xfrm_input_afinfo const __rcu *xfrm_input_afinfo[AF_INET6 + 1];
2539
static struct gro_cells gro_cells;
2640
static struct net_device xfrm_napi_dev;
2741

42+
static DEFINE_PER_CPU(struct xfrm_trans_tasklet, xfrm_trans_tasklet);
43+
2844
int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo)
2945
{
3046
int err = 0;
@@ -477,9 +493,41 @@ int xfrm_input_resume(struct sk_buff *skb, int nexthdr)
477493
}
478494
EXPORT_SYMBOL(xfrm_input_resume);
479495

496+
static void xfrm_trans_reinject(unsigned long data)
497+
{
498+
struct xfrm_trans_tasklet *trans = (void *)data;
499+
struct sk_buff_head queue;
500+
struct sk_buff *skb;
501+
502+
__skb_queue_head_init(&queue);
503+
skb_queue_splice_init(&trans->queue, &queue);
504+
505+
while ((skb = __skb_dequeue(&queue)))
506+
XFRM_TRANS_SKB_CB(skb)->finish(dev_net(skb->dev), NULL, skb);
507+
}
508+
509+
int xfrm_trans_queue(struct sk_buff *skb,
510+
int (*finish)(struct net *, struct sock *,
511+
struct sk_buff *))
512+
{
513+
struct xfrm_trans_tasklet *trans;
514+
515+
trans = this_cpu_ptr(&xfrm_trans_tasklet);
516+
517+
if (skb_queue_len(&trans->queue) >= netdev_max_backlog)
518+
return -ENOBUFS;
519+
520+
XFRM_TRANS_SKB_CB(skb)->finish = finish;
521+
skb_queue_tail(&trans->queue, skb);
522+
tasklet_schedule(&trans->tasklet);
523+
return 0;
524+
}
525+
EXPORT_SYMBOL(xfrm_trans_queue);
526+
480527
void __init xfrm_input_init(void)
481528
{
482529
int err;
530+
int i;
483531

484532
init_dummy_netdev(&xfrm_napi_dev);
485533
err = gro_cells_init(&gro_cells, &xfrm_napi_dev);
@@ -490,4 +538,13 @@ void __init xfrm_input_init(void)
490538
sizeof(struct sec_path),
491539
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
492540
NULL);
541+
542+
for_each_possible_cpu(i) {
543+
struct xfrm_trans_tasklet *trans;
544+
545+
trans = &per_cpu(xfrm_trans_tasklet, i);
546+
__skb_queue_head_init(&trans->queue);
547+
tasklet_init(&trans->tasklet, xfrm_trans_reinject,
548+
(unsigned long)trans);
549+
}
493550
}

0 commit comments

Comments
 (0)