From: netdev-bot+sashiko@kernel.org
To: echaudro@redhat.com
Cc: netdev@vger.kernel.org, dev@openvswitch.org, aconole@redhat.com,
i.maximets@ovn.org, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
cpp.code.lv@gmail.com, stable@vger.kernel.org
Subject: Re: [PATCH net v2] openvswitch: fix wrong flag value in get_ipv6_ext_hdrs()
Date: Thu, 10 Sep 2026 15:52:30 +0000 [thread overview]
Message-ID: <178905555085.219967.6617611783437228045@kernel.org> (raw)
In-Reply-To: <1b1582eb07550d71f3cbe210e5cb31eeb8d0ad86.1788876917.git.echaudro@redhat.com>
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 (1) · Medium: 1 · Low: 0
- [Medium] The `case IPPROTO_ESP:` arm of get_ipv6_ext_hdrs()
(net/openvswitch/flow.c:287-297), whose whitelist this patch changes…
Pre-existing issues:
- [High] In get_ipv6_ext_hdrs() the loop tail unconditionally advances
the parse offset with `start += ipv6_optlen(hp)`…
--
openvswitch: fix wrong flag value in get_ipv6_ext_hdrs()
This replaces two uses of IPPROTO_FRAGMENT with OFPIEH12_FRAG in the ESP
and AH arms of get_ipv6_ext_hdrs(), where the constant was being used as a
bitmask of already-seen extension headers. The stated effect is that a
packet with two AH headers now reports OFPIEH12_UNSEQ in addition to
OFPIEH12_UNREP.
A couple of questions on the ESP arm and on the offset arithmetic in the
same loop.
> diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
> index 46c1d66aad8c..868d9fdf3afd 100644
> --- a/net/openvswitch/flow.c
> +++ b/net/openvswitch/flow.c
> @@ -288,7 +288,7 @@ static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh,
> if (*ext_hdrs & OFPIEH12_ESP)
> *ext_hdrs |= OFPIEH12_UNREP;
> if ((*ext_hdrs & ~(OFPIEH12_HOP | OFPIEH12_DEST |
> - OFPIEH12_ROUTER | IPPROTO_FRAGMENT |
> + OFPIEH12_ROUTER | OFPIEH12_FRAG |
> OFPIEH12_AUTH | OFPIEH12_UNREP)) ||
> dest_options_header_count >= 2) {
> *ext_hdrs |= OFPIEH12_UNSEQ;
[Severity: Medium]
Can the case IPPROTO_ESP arm ever run? The switch in get_ipv6_ext_hdrs()
is inside:
while (ipv6_ext_hdr(next_type)) {
and ipv6_ext_hdr() in net/ipv6/exthdrs_core.c only accepts a fixed set of
next-header values:
return (nexthdr == NEXTHDR_HOP) ||
(nexthdr == NEXTHDR_ROUTING) ||
(nexthdr == NEXTHDR_FRAGMENT) ||
(nexthdr == NEXTHDR_AUTH) ||
(nexthdr == NEXTHDR_NONE) ||
(nexthdr == NEXTHDR_DEST);
NEXTHDR_ESP (50) is not in that list, so the loop exits before the ESP arm
is entered, and *ext_hdrs |= OFPIEH12_ESP at the end of that arm never
executes. That would also mean OFPIEH12_ESP is never reported in
key->ipv6.exthdrs, and the OFPIEH12_ESP entry in the second-DSTOPTS
whitelist can never be exercised.
If that reading is right, is this part of the commit message accurate?
This caused incorrect OFPIEH12_UNSEQ verdicts in
both the ESP and AH arms: the ESP arm failed to whitelist OFPIEH12_FRAG,
Only the AH hunk seems able to change an observable verdict.
Should making the ESP arm reachable be part of this change, for example by
admitting ESP into the walk:
while (ipv6_ext_hdr(next_type) || next_type == IPPROTO_ESP) {
Note that would also need a separate advance for ESP, since the ESP header
has no ipv6_opt_hdr-style hdrlen field.
> @@ -301,7 +301,7 @@ static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh,
> *ext_hdrs |= OFPIEH12_UNREP;
> if ((*ext_hdrs &
> ~(OFPIEH12_HOP | OFPIEH12_DEST | OFPIEH12_ROUTER |
> - IPPROTO_FRAGMENT | OFPIEH12_UNREP)) ||
> + OFPIEH12_FRAG | OFPIEH12_UNREP)) ||
> dest_options_header_count >= 2) {
> *ext_hdrs |= OFPIEH12_UNSEQ;
> }
[Severity: High]
This isn't a bug introduced by this patch, it dates back to 28a3f0601727,
but since the AH arm is being touched here: is the offset advance at the
bottom of the same loop in get_ipv6_ext_hdrs() correct for AH and Fragment
headers?
hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
if (!hp)
break;
next_type = hp->nexthdr;
start += ipv6_optlen(hp);
ipv6_optlen() is only the right arithmetic for HOPOPTS/DSTOPTS/ROUTING.
>From include/linux/ipv6.h:
#define ipv6_optlen(p) (((p)->hdrlen+1) << 3)
#define ipv6_authlen(p) (((p)->hdrlen+2) << 2)
For an AH with hdrlen=4 the header is 24 bytes, but ipv6_optlen() returns
40, so start lands 16 bytes inside the ICV/payload. For a Fragment header,
the byte read as hdrlen is the Reserved field, which the sender controls and
receivers are required to ignore; the correct advance is a fixed 8.
The kernel's own chain walker handles both cases explicitly in
ipv6_find_hdr() (net/ipv6/exthdrs_core.c):
hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH) {
if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
break;
hdrlen = ipv6_authlen(hp);
} else
hdrlen = ipv6_optlen(hp);
With the current code, any header parsed after an AH header, or after a
Fragment header with a non-zero Reserved byte, has its nexthdr and hdrlen
read from sender-chosen payload bytes, so the OFPIEH12_* bits stored in
key->ipv6.exthdrs and exported as OVS_KEY_ATTR_IPV6_EXTHDRS can be steered
remotely. Payload bytes that look like a second AH or a HOPOPTS header
would raise OFPIEH12_UNREP/OFPIEH12_UNSEQ, and genuine trailing headers can
be missed. There is no memory-safety concern here since
skb_header_pointer() is bounds-checked and start advances by at least 8 each
iteration.
The two-AH example given in the commit message still behaves as described,
since next_type comes from the previous header's correctly-read nexthdr
field. Would it make sense to fix the per-type length handling in a
follow-up?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/1b1582eb07550d71f3cbe210e5cb31eeb8d0ad86.1788876917.git.echaudro%40redhat.com
next prev parent reply other threads:[~2026-09-10 15:52 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-08 14:15 [PATCH net v2] openvswitch: fix wrong flag value in get_ipv6_ext_hdrs() Eelco Chaudron
2026-09-10 15:52 ` netdev-bot+sashiko [this message]
2026-09-10 16:00 ` patchwork-bot+netdevbpf
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=178905555085.219967.6617611783437228045@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=aconole@redhat.com \
--cc=cpp.code.lv@gmail.com \
--cc=davem@davemloft.net \
--cc=dev@openvswitch.org \
--cc=echaudro@redhat.com \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=i.maximets@ovn.org \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.