From: "Michael S. Tsirkin" <mst@redhat.com>
To: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Cc: linux-kernel@vger.kernel.org,
"Chris Arges" <carges@cloudflare.com>,
"Björn Töpel" <bjorn@kernel.org>, "Phil Sutter" <phil@nwl.cc>,
"Jason Wang" <jasowangio@gmail.com>,
"Eugenio Pérez" <eperezma@redhat.com>,
"Xuan Zhuo" <xuanzhuo@linux.alibaba.com>,
"Andrew Lunn" <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@kernel.org>,
"Jakub Kicinski" <kuba@kernel.org>,
"Paolo Abeni" <pabeni@redhat.com>,
netdev@vger.kernel.org, virtualization@lists.linux.dev,
"Max Krasnyansky" <maxk@qualcomm.com>,
"Rusty Russell" <rusty@rustcorp.com.au>,
"Paulos Yibelo" <habte.yibelo@gmail.com>
Subject: Re: [PATCH net v3 5/8] net: resolve uncommon partial checksums at ingress
Date: Wed, 21 Oct 2026 02:11:51 -0400 [thread overview]
Message-ID: <20261021020654-mutt-send-email-mst@kernel.org> (raw)
In-Reply-To: <willemdebruijn.kernel.1da723f98c23a@gmail.com>
On Tue, Oct 20, 2026 at 08:05:29PM -0400, Willem de Bruijn wrote:
> Michael S. Tsirkin wrote:
> > On Tue, Oct 20, 2026 at 03:44:35PM -0400, Willem de Bruijn wrote:
> > > > +/* Walk the L3 header at @nhoff: where does its transport header
> > > > + * start, and which protocol is it? Returns false if the header
> > > > + * is truncated, malformed or fragmented.
> > > > + */
> > > > +static inline bool
> > > > +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);
> > > > +
> > > > + if (!iph || ip_is_fragment(iph))
> > > > + return false;
> > > > + *ip_proto = iph->protocol;
> > > > + return true;
> > > > + }
> > > > +
> > > > + if (proto == htons(ETH_P_IPV6)) {
> > > > + const struct ipv6hdr *iph;
> > > > + int flags = 0;
> > > > +
> > > > + if (!pskb_may_pull(skb, nhoff + sizeof(*iph)))
> > > > + return false;
> > > > + iph = (const void *)(skb->data + nhoff);
> > > > + if (iph->version != 6)
> > > > + return false;
> > > > + *thoff = nhoff + sizeof(*iph);
> > > > + *ip_proto = iph->nexthdr;
> > > > + if (*ip_proto != IPPROTO_TCP && *ip_proto != IPPROTO_UDP) {
> > > > + *thoff = nhoff;
> > > > + *ip_proto = ipv6_find_hdr(skb, thoff, -1, NULL,
> > > > + &flags);
> > > > + if (*ip_proto < 0 || flags & IP6_FH_F_FRAG)
> > > > + return false;
> > > > + }
> > > > + return true;
> > > > + }
> > > > +
> > > > + return false;
> > > > +}
> > >
> > > for all this parsing: could we use flow dissection, like the
> > > !VIRTIO_NET_HDR_F_NEEDS_CSUM case?
> >
> > Hmm I don't see how. That one trusts the caller's protocol and network
> > header. We could parse L2 ourselves and use the dissector for L3+. But
> > it stops at whatever it does not know, and every miss would turn into a
> > drop or a software checksum for a packet the stack delivers fine. Our
> > parser deliberately covers everything the stack can deliver.
>
> You're right, the protocol and network offset need to be parsed
> manually. After that it could be passed to the flow dissector.
>
> Totally untested:
>
> static inline bool virtio_net_setup_l3_input(struct sk_buff *skb, int *nhoff, __be16 *proto)
> {
> const struct net_device *dev = skb->dev;
>
> /* Devices with Ethernet framing: ARPHRD_ETHER, loopback, macvlan, etc. */
> if (dev->header_ops == ð_header_ops || dev->type == ARPHRD_ETHER) {
> if (!pskb_may_pull(skb, ETH_HLEN))
> return false;
> *nhoff = ETH_HLEN;
> *proto = eth_hdr(skb)->h_proto;
> } else if (dev->type == ARPHRD_NONE) {
> /* Raw L3 devices (e.g. tun in IFF_TUN mode) */
> *nhoff = 0;
> *proto = skb->protocol;
> if (!*proto) {
> if (!pskb_may_pull(skb, 1))
> return false;
> if ((skb->data[0] >> 4) == 4)
> *proto = htons(ETH_P_IP);
> else if ((skb->data[0] >> 4) == 6)
> *proto = htons(ETH_P_IPV6);
> else
> return false;
> }
> } else {
> /* Fallback for other netdevs using caller's network header */
> *nhoff = skb_network_offset(skb);
> if (*nhoff < 0 || (unsigned int)*nhoff >= skb->len)
> return false;
> *proto = skb->protocol;
> }
>
> return true;
> }
>
> static inline bool virtio_net_csum_partial_ok(struct sk_buff *skb, u8 hdr_gso_type)
> {
> struct flow_keys_basic keys;
> __be16 orig_proto = skb->protocol;
> u16 orig_net_hdr = skb->network_header;
> __be16 proto;
> int nhoff;
> u16 csum_offset;
> size_t min_thlen;
>
> if (!virtio_net_setup_l3_input(skb, &nhoff, &proto))
> return false;
>
> skb_set_network_header(skb, nhoff);
> skb->protocol = proto;
>
> if (!skb_flow_dissect_flow_keys_basic(dev_net(skb->dev), skb, &keys,
> NULL, 0, 0, 0, 0))
> goto restore_fail;
>
> /* Disallow fragments: partial checksums cannot be retained across frags */
> if (keys.control.flags & FLOW_DIS_IS_FRAGMENT)
> goto restore_fail;
>
> /* Validate protocol matching */
> switch (keys.basic.ip_proto) {
> case IPPROTO_TCP:
> if (!virtio_net_hdr_match_proto(keys.basic.n_proto, hdr_gso_type))
> goto restore_fail;
> min_thlen = sizeof(struct tcphdr);
> csum_offset = offsetof(struct tcphdr, check);
> break;
> case IPPROTO_UDP:
> if (!virtio_net_hdr_match_proto(keys.basic.n_proto, hdr_gso_type))
> goto restore_fail;
> min_thlen = sizeof(struct udphdr);
> csum_offset = offsetof(struct udphdr, check);
> break;
> default:
> goto restore_fail;
> }
>
> /* csum_start must match the dissected transport header offset */
> if (skb_transport_offset(skb) != keys.control.thoff)
> goto restore_fail;
>
> /* csum_offset must point to the transport checksum field */
> if (skb->csum_offset != csum_offset)
> goto restore_fail;
>
> /* Ensure the transport header is linear */
> if (!pskb_may_pull(skb, keys.control.thoff + min_thlen))
> goto restore_fail;
>
> return true;
>
> restore_fail:
> skb->protocol = orig_proto;
> skb->network_header = orig_net_hdr;
> return false;
> }
Hmm, an interesting trick, but I don't see how this works for tunnels?
We need to dissect at inner_nh, and on success we can not leave
network_header there - the packet continues to outer IP processing.
I fear making all this work will be more code than the parsing it
replaces.
--
MST
next prev parent reply other threads:[~2026-10-21 6:12 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 12:03 [PATCH net v3 0/8] more virtio-net csum hardening Michael S. Tsirkin
2026-10-19 12:03 ` [PATCH net v3 1/8] net: account for ingress-resolved virtio net hdr state Michael S. Tsirkin
2026-10-20 19:27 ` Willem de Bruijn
2026-10-20 19:38 ` Michael S. Tsirkin
2026-10-20 20:02 ` Willem de Bruijn
2026-10-20 20:09 ` Michael S. Tsirkin
2026-10-19 12:03 ` [PATCH net v3 2/8] um: vector: honor virtio net hdr conversion failures Michael S. Tsirkin
2026-10-20 19:28 ` Willem de Bruijn
2026-10-19 12:03 ` [PATCH net v3 3/8] um: vector: trim skbs before verifying the header Michael S. Tsirkin
2026-10-19 12:03 ` [PATCH net v3 4/8] virtio_net: parse tunnel csum_start little-endian Michael S. Tsirkin
2026-10-19 12:03 ` [PATCH net v3 5/8] net: resolve uncommon partial checksums at ingress Michael S. Tsirkin
2026-10-20 19:44 ` Willem de Bruijn
2026-10-20 21:49 ` Michael S. Tsirkin
2026-10-21 0:05 ` Willem de Bruijn
2026-10-21 6:11 ` Michael S. Tsirkin [this message]
2026-10-21 7:52 ` Michael S. Tsirkin
2026-10-19 12:03 ` [PATCH net v3 6/8] net: add a sysctl to opt out of virtio net hdr checksum validation Michael S. Tsirkin
2026-10-19 14:19 ` Björn Töpel
2026-10-20 14:04 ` Michael S. Tsirkin
2026-10-20 19:54 ` Willem de Bruijn
2026-10-20 20:01 ` Michael S. Tsirkin
2026-10-20 20:19 ` Willem de Bruijn
2026-10-20 20:22 ` Michael S. Tsirkin
2026-10-20 23:12 ` Willem de Bruijn
2026-10-20 23:21 ` Michael S. Tsirkin
2026-10-20 20:56 ` Michael S. Tsirkin
2026-10-20 23:13 ` Willem de Bruijn
2026-10-20 23:20 ` Michael S. Tsirkin
2026-10-19 12:03 ` [PATCH net v3 7/8] net: add ip4_parse() Michael S. Tsirkin
2026-10-19 12:03 ` [PATCH net v3 8/8] virtio_net: cleanup protocol calculation Michael S. Tsirkin
2026-10-19 12:09 ` [PATCH net v3 0/8] more virtio-net csum hardening netdev-bot+sinfo
2026-10-19 12:21 ` Michael S. Tsirkin
2026-10-19 16:51 ` Chris Arges
2026-10-21 2:32 ` Jakub Kicinski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261021020654-mutt-send-email-mst@kernel.org \
--to=mst@redhat.com \
--cc=andrew+netdev@lunn.ch \
--cc=bjorn@kernel.org \
--cc=carges@cloudflare.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=eperezma@redhat.com \
--cc=habte.yibelo@gmail.com \
--cc=jasowangio@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maxk@qualcomm.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=phil@nwl.cc \
--cc=rusty@rustcorp.com.au \
--cc=virtualization@lists.linux.dev \
--cc=willemdebruijn.kernel@gmail.com \
--cc=xuanzhuo@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox