Received: by 10.223.185.116 with SMTP id b49csp6372354wrg; Wed, 28 Feb 2018 08:20:18 -0800 (PST) X-Google-Smtp-Source: AG47ELtdLKdScQI0dFLtH/iFDNQW5rKUBT4MjgeXDMCNEIKIWv0ID9LN912NZVFZ9tKtBo108kgi X-Received: by 10.98.72.10 with SMTP id v10mr11241695pfa.148.1519834818130; Wed, 28 Feb 2018 08:20:18 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519834818; cv=none; d=google.com; s=arc-20160816; b=fsCNanpQXMDuAIFgQ/j3N4QXTodN9Z2H4ajhYn93JhtVsBgtVdonCE40ts8JbglahL vctoCvf267E/QpPq2UCI7sC1g2cPNCENrK9xF4pYCeDd7Px3gA8goHdFdUvQPNN/ElMR o0xOme7xY4NKB6w6uj+G7ch9foTMTS/Dy1flLrglwyh8QWILx+qAgk5haMSOzVMs9/q1 odxAycMnyLDPkox8JAmCf83NkPwRis34esIDkG+RvgXgydGB6yn35JOLDQTm8jAtkgBW AkJYMbV/mFlsVpHEiGwqK1wJ/lVYf21QHkpTCn+z3Tt0cgNG1dcFoLub79C5INlFJKor 9uLA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:in-reply-to:subject:message-id:date:cc:to :from:mime-version:content-transfer-encoding:content-disposition :arc-authentication-results; bh=cNPoqbn+NmsaV6N40ipqHzVKUwAh+X56C2baoVj0iTc=; b=KszYtKgesG68PMhrnmNtC5dT6pmAsxNKkOWIUsy890ltaPfcZAst9PigYU2SIvNuv5 AYDNPBlbdFhVLMR3a/3LTDBcAhGxv02TWxsPHfEeiBsWXgZJsEB3R21kzkLKACj3H4qH 1snAVIVnSNOyjy0YSfK80S9bTGc51jftvZWtoad5nLTGZuWrs+LQKA0+4haxqVvUCNys +Wpc2aDHNg0Vb3/NtdcwdNWgHUncAJn+tBlpir8DAYjE4WWoBA1MdSuocI6VI6oBusKP kg76WF+Hf7mfr2gN8n5BtSx+LXOQmXcFV3uXOgS5ydIWCoB2+d1GTA3dvSdBBh4+0oll xTxA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id u12-v6si1492803plr.462.2018.02.28.08.20.03; Wed, 28 Feb 2018 08:20:18 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S935082AbeB1QSK (ORCPT + 99 others); Wed, 28 Feb 2018 11:18:10 -0500 Received: from shadbolt.e.decadent.org.uk ([88.96.1.126]:35292 "EHLO shadbolt.e.decadent.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S935065AbeB1QSI (ORCPT ); Wed, 28 Feb 2018 11:18:08 -0500 Received: from [2a02:8011:400e:2:6f00:88c8:c921:d332] (helo=deadeye) by shadbolt.decadent.org.uk with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1er3Yi-0006Xe-3Q; Wed, 28 Feb 2018 15:22:20 +0000 Received: from ben by deadeye with local (Exim 4.90_1) (envelope-from ) id 1er3Yg-000072-WE; Wed, 28 Feb 2018 15:22:19 +0000 Content-Type: text/plain; charset="UTF-8" Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 From: Ben Hutchings To: linux-kernel@vger.kernel.org, stable@vger.kernel.org CC: akpm@linux-foundation.org, "Herbert Xu" , "Steffen Klassert" Date: Wed, 28 Feb 2018 15:20:18 +0000 Message-ID: X-Mailer: LinuxStableQueue (scripts by bwh) Subject: [PATCH 3.16 125/254] xfrm: Reinject transport-mode packets through tasklet In-Reply-To: X-SA-Exim-Connect-IP: 2a02:8011:400e:2:6f00:88c8:c921:d332 X-SA-Exim-Mail-From: ben@decadent.org.uk X-SA-Exim-Scanned: No (on shadbolt.decadent.org.uk); SAEximRunCond expanded to false Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 3.16.55-rc1 review patch. If anyone has any objections, please let me know. ------------------ From: Herbert Xu commit acf568ee859f098279eadf551612f103afdacb4e upstream. This is an old bugbear of mine: https://www.mail-archive.com/netdev@vger.kernel.org/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: b05e106698d9 ("[IPV4/6]: Netfilter IPsec input hooks") Signed-off-by: Herbert Xu Signed-off-by: Steffen Klassert [bwh: Backported to 3.16: - netfilter finish callbacks only receive an sk_buff pointer - Adjust context] Signed-off-by: Ben Hutchings --- --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1496,6 +1496,7 @@ int xfrm_init_state(struct xfrm_state *x int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb); int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); int xfrm_input_resume(struct sk_buff *skb, int nexthdr); +int xfrm_trans_queue(struct sk_buff *skb, int (*finish)(struct sk_buff *)); int xfrm_output_resume(struct sk_buff *skb, int err); int xfrm_output(struct sk_buff *skb); int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb); --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -22,6 +22,11 @@ int xfrm4_extract_input(struct xfrm_stat return xfrm4_extract_header(skb); } +static int xfrm4_rcv_encap_finish2(struct sk_buff *skb) +{ + return dst_input(skb); +} + static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb) { if (skb_dst(skb) == NULL) { @@ -31,7 +36,11 @@ static inline int xfrm4_rcv_encap_finish iph->tos, skb->dev)) goto drop; } - return dst_input(skb); + + if (xfrm_trans_queue(skb, xfrm4_rcv_encap_finish2)) + goto drop; + + return 0; drop: kfree_skb(skb); return NET_RX_DROP; --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -29,6 +29,13 @@ int xfrm6_rcv_spi(struct sk_buff *skb, i } EXPORT_SYMBOL(xfrm6_rcv_spi); +static int xfrm6_transport_finish2(struct sk_buff *skb) +{ + if (xfrm_trans_queue(skb, ip6_rcv_finish)) + __kfree_skb(skb); + return -1; +} + int xfrm6_transport_finish(struct sk_buff *skb, int async) { skb_network_header(skb)[IP6CB(skb)->nhoff] = @@ -43,7 +50,7 @@ int xfrm6_transport_finish(struct sk_buf __skb_push(skb, skb->data - skb_network_header(skb)); NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, - ip6_rcv_finish); + xfrm6_transport_finish2); return -1; } --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -7,18 +7,34 @@ * */ +#include +#include #include #include #include +#include #include #include #include +struct xfrm_trans_tasklet { + struct tasklet_struct tasklet; + struct sk_buff_head queue; +}; + +struct xfrm_trans_cb { + int (*finish)(struct sk_buff *skb); +}; + +#define XFRM_TRANS_SKB_CB(__skb) ((struct xfrm_trans_cb *)&((__skb)->cb[0])) + static struct kmem_cache *secpath_cachep __read_mostly; static DEFINE_SPINLOCK(xfrm_input_afinfo_lock); static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO]; +static DEFINE_PER_CPU(struct xfrm_trans_tasklet, xfrm_trans_tasklet); + int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo) { int err = 0; @@ -375,10 +391,50 @@ int xfrm_input_resume(struct sk_buff *sk } EXPORT_SYMBOL(xfrm_input_resume); +static void xfrm_trans_reinject(unsigned long data) +{ + struct xfrm_trans_tasklet *trans = (void *)data; + struct sk_buff_head queue; + struct sk_buff *skb; + + __skb_queue_head_init(&queue); + skb_queue_splice_init(&trans->queue, &queue); + + while ((skb = __skb_dequeue(&queue))) + XFRM_TRANS_SKB_CB(skb)->finish(skb); +} + +int xfrm_trans_queue(struct sk_buff *skb, int (*finish)(struct sk_buff *)) +{ + struct xfrm_trans_tasklet *trans; + + trans = this_cpu_ptr(&xfrm_trans_tasklet); + + if (skb_queue_len(&trans->queue) >= netdev_max_backlog) + return -ENOBUFS; + + XFRM_TRANS_SKB_CB(skb)->finish = finish; + skb_queue_tail(&trans->queue, skb); + tasklet_schedule(&trans->tasklet); + return 0; +} +EXPORT_SYMBOL(xfrm_trans_queue); + void __init xfrm_input_init(void) { + int i; + secpath_cachep = kmem_cache_create("secpath_cache", sizeof(struct sec_path), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); + + for_each_possible_cpu(i) { + struct xfrm_trans_tasklet *trans; + + trans = &per_cpu(xfrm_trans_tasklet, i); + __skb_queue_head_init(&trans->queue); + tasklet_init(&trans->tasklet, xfrm_trans_reinject, + (unsigned long)trans); + } }