From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C706E5275A4 for ; Thu, 15 Oct 2026 12:41:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792068117; cv=none; b=aPZ+X7dUrnzNUO0zINLYl6KnhgWPG72qLlFY9TU1707GkzyGTfZBHqrypgtHE5YtOqC5DLeM2CzlsZVoMH0GGSme58Kd1ksOWr67S3wAcLl97x4QnL6whCSPmSWnjyT+GVStnrLNc47TZKdqlQK19LX6DQ2r9Kwe96t7+kvC2AM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792068117; c=relaxed/simple; bh=qrg0eJdV2vP7kYY8ClAjBocw4aBzi4WlBp77YFs9kk8=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=tK0+ATtKrHF3+BUSKNgTgSEzq7WIt9OoG1cVg97BJA1Qem+RRNWwnxw+7n7RfIwtv9VDmAup2buYDmwr8YWrZvjn+pzqtQa6h8F4o+sBOFA8vlw+WYWpUB96mTMHqZIT8an+7W316gCNcnnKGV40eAS4kV2+QblEw7EB9c5gV6c= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=bDfrIW4Z; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b=Ac4yv0Rp; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="bDfrIW4Z"; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b="Ac4yv0Rp" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792068114; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=YBIQURysnHAtYsYZBEyvPCoDT2vBfeJzj7rU3gaKycI=; b=bDfrIW4Z4rrWSSg99NL0XBW7MVspbcxIw4XMkNNyuO6ncGZXnZQB5gy3ZLVi7trqwtLyFD W7uz6hNJrppQtWpOEg+Fy+9REGZ+TdZXbBV0xYxEnFY7U8O/hTG1tDslUzdbMcnWcH2mY5 5V79Xo+079eu4D+ynll+7Sj2gYOkiqQ= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-338-aXQbyHylNhKSx1qM4MLfMA-1; Thu, 15 Oct 2026 12:41:53 +0000 X-MC-Unique: aXQbyHylNhKSx1qM4MLfMA-1 X-Mimecast-MFC-AGG-ID: aXQbyHylNhKSx1qM4MLfMA_1792068112 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-4a1895ffc83so40285295e9.3 for ; Thu, 15 Oct 2026 05:41:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1792068112; x=1792672912; darn=vger.kernel.org; h=in-reply-to:content-disposition:content-type:mime-version :references:message-id:subject:cc:to:from:date:from:to:cc:subject :date:message-id:reply-to:content-type; bh=YBIQURysnHAtYsYZBEyvPCoDT2vBfeJzj7rU3gaKycI=; b=Ac4yv0RpkJed7a5ha/rqX/XFO81cINkLz90X9vgSBZyjWL7/2InWiEA9pil2RPVEKm Vy1UFzRT2SpakV+CtTWSrFsajizoRJ5TEyzhRr2Zlo8nj1TzpTN4Mney1dY02lC1P4pA ih2H//j3SmGm2vrCZzp/h2lpaP9OdjEeNMTLtfnpmxWjm1Mesy2Nb49XhFq0Umt/XYGS 9+s8hWYDcHzFJdCoeIGS2mYVSvLc6ljacn//nhHvmVuA0eFgjUnHce2QziRYlIdEsmaB LhMHJMvH8XyMj/Hd99WOeEMtylI9c5k4UHhx+rMWf1ii/Dh/wiXUjc5Y0f4sZZWdXyFV BIhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792068112; x=1792672912; h=in-reply-to:content-disposition:content-type:mime-version :references:message-id:subject:cc:to:from:date:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=YBIQURysnHAtYsYZBEyvPCoDT2vBfeJzj7rU3gaKycI=; b=V3Ck9U7yTNbtewE43Qsz4QtdJMMDrrsd58+4pgkDBdSTYSpfPIYxHSrvITfgtERxOM umO60mCgHbMiQ66JpBNDw3CLGAcZiSWGeeQ9+I3K5P8390eHlfEJAcckuFgPuK0R/pNn 0t451YWOocK43glAU8ZrPFgMtjb3f4XXI2UMOAGfG1FyAK8/DasNlec5sa65ZsRnO2LA 4AC7fI2W1LidV/iv9iQJvkCrjfMnOfcO5+FvWmPyPNe8LV817xaVA9HuEoQIWhZLIXe4 D8GzIm6VzlEuxD6IJV8dALBtOd5cW7pVkKKlJ/I1M28/FmXfNuqmWqVJslARYTBwPsWx q7sA== X-Gm-Message-State: AFq9FYKrDYJnxBI70Ct1kHOkJMrku+u8ut+TQcVc17S3av/QV+gDevI0 Xo5rT4Ikhx8RJV7UEdOqwSUhGSbHmHpGlEbkstMpy0+WqMVy+t8vgh7BCi2vLOuNZ7fKoEK93DZ cbeQWff/323vW7xIWyIa3hpUCPpxivYfvCCwrFXs7rXh6/YZdgY674cxGF+8zDOtNSPm5L60SPz Pg7GyHbf5dTlyIG2qzv/MyxKxBxYL0Hur3P7z7psO57xw= X-Gm-Gg: AYBFou29cwK89VAKR3xkkkYWp6DzJequ9pqStsCxVBvnDKd+l5mauGDtZRXky+BzPII 3HekQktLlY54q8CV19n5HzimDRKkU0Zk32ifMQZZ+DY+AepDpDFLzEssibJb2327tARTklPQonY QuH+TEy1wwKfFpMVtNaiecJ7HQntVz1T3kCtO69JDeOZJC7IRd1HzZcBx0jASgueuG7R6RSTobE C4M8DykUEdgMtiy//BwFelvYHCh8UiA+qlCWi8cst6mDCZzc8NhzYadHS+sXA8qniGJ/6si55XP QLO8zAJ4KvyjiHNSYqZEfm2umj7IhWeXgp9Y1BfJg4JFlLogHeavmv/iS/ls6SzYZjfmNA== X-Received: by 2002:a05:600c:4594:b0:49d:1fa8:b266 with SMTP id 5b1f17b1804b1-4a18e4d6ac3mr375378265e9.26.1792068112053; Thu, 15 Oct 2026 05:41:52 -0700 (PDT) X-Received: by 2002:a05:600c:4594:b0:49d:1fa8:b266 with SMTP id 5b1f17b1804b1-4a18e4d6ac3mr375376365e9.26.1792068109456; Thu, 15 Oct 2026 05:41:49 -0700 (PDT) Received: from redhat.com ([2a0d:6fc0:3fd7:5300:3d6b:52a4:a23f:9d0b]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4a1bd640be0sm52746555e9.1.2026.10.15.05.41.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Oct 2026 05:41:48 -0700 (PDT) Date: Thu, 15 Oct 2026 08:41:45 -0400 From: "Michael S. Tsirkin" To: linux-kernel@vger.kernel.org Cc: Willem de Bruijn , Jason Wang , Eugenio =?utf-8?B?UMOpcmV6?= , Xuan Zhuo , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , netdev@vger.kernel.org, virtualization@lists.linux.dev, Rusty Russell , Max Krasnyansky , Pablo Neira Ayuso , Florian Westphal , Phil Sutter , Nikolay Aleksandrov , Ido Schimmel , Simon Horman , David Ahern , netfilter-devel@vger.kernel.org, coreteam@netfilter.org, bridge@lists.linux.dev Subject: [PATCH net-next v2 6/7] net: add ip4_parse() Message-ID: <2645c102b2f73292d1973b82c95def24e48064e1.1792067836.git.mst@redhat.com> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: X-Mailer: git-send-email 2.51.2.2891.g4157995a80.dirty X-Mutt-Fcc: =sent Multiple places in the net stack open-code the same minimal IPv4 header validation: pull the fixed header, check version 4 and IHL >= 5 (where 5 is the fixed header size in 32-bit units) - and locate the transport header at IHL * 4 past the network header. Add a helper for doing that: it returns the validated header and the transport header offset, or NULL if truncated or malformed. Signed-off-by: Michael S. Tsirkin Assisted-by: LLM --- include/linux/ip.h | 24 ++++++++++++++++++++++++ include/linux/virtio_net.h | 28 +--------------------------- net/bridge/br_netfilter_hooks.c | 15 +++++---------- net/ipv4/ip_input.c | 13 +++++-------- net/ipv4/netfilter/nf_reject_ipv4.c | 13 +++++-------- 5 files changed, 40 insertions(+), 53 deletions(-) diff --git a/include/linux/ip.h b/include/linux/ip.h index d11c25f5030a..98012f6d0efb 100644 --- a/include/linux/ip.h +++ b/include/linux/ip.h @@ -56,4 +56,28 @@ static inline void iph_set_totlen(struct iphdr *iph, unsigned int len) { iph->tot_len = len <= IP_MAX_MTU ? htons(len) : 0; } + +/* + * Pull and minimally validate the IPv4 header at @nhoff. + * Returns the header and stores the transport header offset in + * @thoff, or NULL if truncated or malformed. Only the fixed header + * is guaranteed linear; callers that need the options or the + * transport header must pull further. + */ +static inline struct iphdr *ip4_parse(struct sk_buff *skb, + unsigned int nhoff, + unsigned int *thoff) +{ + struct iphdr *iph; + + if (!pskb_may_pull(skb, nhoff + sizeof(struct iphdr))) + return NULL; + + iph = (struct iphdr *)(skb->data + nhoff); + if (iph->version != 4 || iph->ihl < sizeof(struct iphdr) / sizeof(u32)) + return NULL; + + *thoff = nhoff + iph->ihl * sizeof(u32); + return iph; +} #endif /* _LINUX_IP_H */ diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index aa2f8ac4657c..85ec1039f73a 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -53,32 +53,6 @@ static inline int virtio_net_hdr_set_proto(struct sk_buff *skb, return 0; } -/* TODO: nothing virtio specific here, move to ip.h */ - -/* - * Pull and minimally validate the IPv4 header at @nhoff. - * Returns the header and stores the transport header offset in - * @thoff, or NULL if truncated or malformed. Only the fixed header - * is guaranteed linear; callers that need the options or the - * transport header must pull further. - */ -static inline struct iphdr *virtio_net_ip4_parse(struct sk_buff *skb, - unsigned int nhoff, - unsigned int *thoff) -{ - struct iphdr *iph; - - if (!pskb_may_pull(skb, nhoff + sizeof(struct iphdr))) - return NULL; - - iph = (struct iphdr *)(skb->data + nhoff); - if (iph->version != 4 || - iph->ihl < sizeof(struct iphdr) / sizeof(u32)) - return NULL; - - *thoff = nhoff + iph->ihl * sizeof(u32); - return iph; -} /* Defined in net/core/dev.c, enabled while any network namespace * has net.core.virtio_net_hdr_csum_check set to 0. */ @@ -126,7 +100,7 @@ virtio_net_l4_start(struct sk_buff *skb, unsigned int nhoff, __be16 proto, unsigned int *thoff, int *ip_proto) { if (proto == htons(ETH_P_IP)) { - const struct iphdr *iph = virtio_net_ip4_parse(skb, nhoff, thoff); + const struct iphdr *iph = ip4_parse(skb, nhoff, thoff); if (!iph || ip_is_fragment(iph)) return false; diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 0a394e5f4391..e89da0e41542 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -206,18 +206,13 @@ static inline void nf_bridge_pull_encap_header_rcsum(struct sk_buff *skb) static int br_validate_ipv4(struct net *net, struct sk_buff *skb) { const struct iphdr *iph; - u32 len; + u32 len, thoff; - if (!pskb_may_pull(skb, sizeof(struct iphdr))) + iph = ip4_parse(skb, 0, &thoff); + if (!iph) goto inhdr_error; - iph = ip_hdr(skb); - - /* Basic sanity checks */ - if (iph->ihl < 5 || iph->version != 4) - goto inhdr_error; - - if (!pskb_may_pull(skb, iph->ihl*4)) + if (!pskb_may_pull(skb, thoff)) goto inhdr_error; iph = ip_hdr(skb); @@ -228,7 +223,7 @@ static int br_validate_ipv4(struct net *net, struct sk_buff *skb) if (skb->len < len) { __IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; - } else if (len < (iph->ihl*4)) + } else if (len < thoff) goto inhdr_error; if (pskb_trim_rcsum(skb, len)) { diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 9860178752b8..5adf01763e85 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -500,7 +500,7 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) { const struct iphdr *iph; int drop_reason; - u32 len; + u32 len, thoff; /* When the interface is in promisc. mode, drop all the crap * that it receives, do not try to analyse it. @@ -520,10 +520,6 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) } drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; - if (!pskb_may_pull(skb, sizeof(struct iphdr))) - goto inhdr_error; - - iph = ip_hdr(skb); /* * RFC1122: 3.2.1.2 MUST silently discard any IP frame that fails the checksum. @@ -536,7 +532,8 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) * 4. Doesn't have a bogus length */ - if (iph->ihl < 5 || iph->version != 4) + iph = ip4_parse(skb, 0, &thoff); + if (!iph) goto inhdr_error; BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1); @@ -546,7 +543,7 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK), max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs)); - if (!pskb_may_pull(skb, iph->ihl*4)) + if (!pskb_may_pull(skb, thoff)) goto inhdr_error; iph = ip_hdr(skb); @@ -559,7 +556,7 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) drop_reason = SKB_DROP_REASON_PKT_TOO_SMALL; __IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; - } else if (len < (iph->ihl*4)) + } else if (len < thoff) goto inhdr_error; /* Our transport medium may have padded the buffer out. Now we know it diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index 59ec465a9df9..42d639854ddf 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -25,22 +25,19 @@ nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, static int nf_reject_iphdr_validate(struct sk_buff *skb) { struct iphdr *iph; - u32 len; + u32 len, thoff; - if (!pskb_may_pull(skb, sizeof(struct iphdr))) - return 0; - - iph = ip_hdr(skb); - if (iph->ihl < 5 || iph->version != 4) + iph = ip4_parse(skb, 0, &thoff); + if (!iph) return 0; len = ntohs(iph->tot_len); if (skb->len < len) return 0; - else if (len < (iph->ihl*4)) + else if (len < thoff) return 0; - if (!pskb_may_pull(skb, iph->ihl*4)) + if (!pskb_may_pull(skb, thoff)) return 0; return 1; -- MST