On 8 Sep 2026, at 10:21, [email protected] wrote:

> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider:
>
> Critical: 0 · High: 0 (2) · Medium: 1 · Low: 0
>
> - [Medium] Undisclosed second behavioural change in the IPPROTO_AH arm of
>   get_ipv6_ext_hdrs() (net/openvswitch/flow.c:299-309).

For this I sent a new revision with an updated commit message.

> Pre-existing issues:
> - [High] This problem wasn't introduced by this patch, but the `case
>   IPPROTO_ESP:` arm of get_ipv6_ext_hdrs()…
> - [High] This problem wasn't introduced by this patch, but
>   get_ipv6_ext_hdrs() advances the parse offset with `start +=…

I'll add an item to my todo list, to take a look at those at a later time.

> --
>
> openvswitch: fix wrong flag value in get_ipv6_ext_hdrs()
>
> The ESP and AH arms of get_ipv6_ext_hdrs() masked out the numeric constant
> IPPROTO_FRAGMENT instead of the OFPIEH12_FRAG bit when deciding whether the
> extension headers seen so far are out of order.  Both masks are changed to
> use OFPIEH12_FRAG.
>
> A few questions below about the two hunks and the surrounding parser.
>
>> 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: High]
> This isn't a bug introduced by this patch, but is the "case IPPROTO_ESP:"
> arm being fixed here reachable at all?
>
> The parse loop in get_ipv6_ext_hdrs() is gated on:
>
>       while (ipv6_ext_hdr(next_type)) {
>
> and ipv6_ext_hdr() in net/ipv6/exthdrs_core.c only accepts:
>
>       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 when the header walk reaches an
> ESP header the loop exits before the switch is entered.
>
> Does that mean OFPIEH12_ESP is never OR-ed into key->ipv6.exthdrs, and
> therefore the bit exported to userspace through OVS_KEY_ATTR_IPV6_EXTHDRS
> (struct ovs_key_ipv6_exthdrs.hdrs) is always zero, so a flow matching
> OFPIEH12_ESP=1 can never match?
>
> If so, the ESP repeat test above, and the OFPIEH12_ESP term in the second
> Destination Options mask, would also be dead, and the kernel-doc for
> get_ipv6_ext_hdrs() which lists the "Encapsulating Security Payload header"
> in the recommended ordering would describe behaviour the code cannot
> produce.  Would handling ESP as an explicitly admitted terminal header,
> rather than relying on the generic ipv6_ext_hdr() gate, be the right
> direction here?
>
>> @@ -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: Medium]
> Does this hunk also change the reported flags for packets carrying two
> Authentication Headers?
>
>> From net/openvswitch/flow.h:
>
>       OFPIEH12_ESP    = 1 << 1,
>       OFPIEH12_AUTH   = 1 << 2,
>       OFPIEH12_DEST   = 1 << 3,
>       OFPIEH12_FRAG   = 1 << 4,
>       OFPIEH12_ROUTER = 1 << 5,
>
> IPPROTO_FRAGMENT is 44, which is 32 | 8 | 4, i.e. OFPIEH12_ROUTER |
> OFPIEH12_DEST | OFPIEH12_AUTH.  So the old mask here happened to whitelist
> OFPIEH12_AUTH as well, while the new mask (HOP | DEST | ROUTER | FRAG |
> UNREP) does not.
>
> A packet with two AH headers previously got only OFPIEH12_UNREP set, and
> now gets OFPIEH12_UNSEQ in addition, which changes the userspace-visible
> OVS_KEY_ATTR_IPV6_EXTHDRS value.  The new behaviour matches the ESP arm,
> which already sets UNSEQ on a repeat, so it may well be intended, but since
> this carries a Fixes: tag and is a stable candidate, could the commit
> message mention this second change instead of only the fragment header
> predecessor case?
>
> [Severity: High]
> This is a pre-existing issue, but the AH ordering verdict computed here
> depends on the chain walk that follows it being correct, and the tail of the
> loop in get_ipv6_ext_hdrs() advances with:
>
>       hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
>       if (!hp)
>               break;
>       next_type = hp->nexthdr;
>       start += ipv6_optlen(hp);
>
> Is ipv6_optlen() correct for an Authentication Header?  include/linux/ipv6.h
> has:
>
>       #define ipv6_optlen(p)  (((p)->hdrlen+1) << 3)
>       #define ipv6_authlen(p) (((p)->hdrlen+2) << 2)
>
> and ipv6_skip_exthdr() in net/ipv6/exthdrs_core.c picks the latter:
>
>       } else if (nexthdr == NEXTHDR_AUTH)
>               hdrlen = ipv6_authlen(hp);
>       else
>               hdrlen = ipv6_optlen(hp);
>
> For a common AH with a 96-bit ICV (hdrlen == 4) this walker advances 40
> bytes instead of 24, so the next skb_header_pointer() reads ICV or payload
> bytes chosen by the sender.
>
> That value becomes next_type and drives all further advancement.  Can a
> crafted AH packet therefore hide real headers that follow the AH (a second
> Destination Options, Fragment, ESP), or fabricate OFPIEH12_FRAG /
> OFPIEH12_ROUTER / OFPIEH12_DEST / OFPIEH12_UNSEQ / OFPIEH12_UNREP bits out
> of payload data, letting flows keyed on the extension-header pseudo-field be
> evaded?
>
> Should this use ipv6_authlen() when next_type is IPPROTO_AH, the way the
> core stack does?
>
> -- 
> Sashiko AI review · 
> https://netdev-ai.bots.linux.dev/sashiko/#/patchset/d42d6f04596dacab6cabfb1f06aaf4bd53394d3d.1788423539.git.echaudro%40redhat.com

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to