> -----Original Message-----
> From: Petr Oros <[email protected]>
> Sent: Wednesday, September 16, 2026 4:41 PM
> To: [email protected]
> Cc: Oros, Petr <[email protected]>; Loktionov, Aleksandr
> <[email protected]>; Nguyen, Anthony L
> <[email protected]>; Kitszel, Przemyslaw
> <[email protected]>; Andrew Lunn <[email protected]>;
> David S. Miller <[email protected]>; Eric Dumazet
> <[email protected]>; Jakub Kicinski <[email protected]>; Paolo Abeni
> <[email protected]>; Kushwaha, Priyalee <[email protected]>;
> Kiran Patil <[email protected]>; Drewek, Wojciech
> <[email protected]>; Michal Swiatkowski
> <[email protected]>; Simon Horman <[email protected]>;
> [email protected]; [email protected]
> Subject: [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than
> the ip_proto key
> 
> ice_parse_cls_flower() stores the ip_proto key from the flow rule but never
> programs a matching lookup unless the filter also matches on L4 ports or the
> L2TPv3 session ID. A filter like:
> 
>   tc filter add dev $pf ingress protocol ip flower skip_sw \
>       ip_proto udp action drop
> 
> is silently programmed into the hardware as a match on eth_type ipv4 alone
> and drops every IPv4 packet, not just UDP.
> 
> Program the IP protocol match through the protocol field of the IPv4 header
> lookup and the next header field of the IPv6 header lookup, the same lookups
> that are already used for ToS and TTL. The OS default and comms DDP
> packages provide no profile that extracts the IPv6 next header word, so the
> IPv6 rule programming currently fails with "Required profiles not found" and
> the filter falls back to software evaluation instead of over-matching, and the
> offload starts working with a DDP package that can extract it. Note that the
> lookup matches the next header byte of the base IPv6 header, so packets
> carrying extension headers are not matched in hardware and fall back to
> software evaluation, which under-matches only for skip_sw filters.
> 
> GTP tunnel and PPPoE filters rewrite the parsed ethertype, the IP header
> lookups are not available there, so reject an unconsumed ip_proto for them
> instead of silently widening the match. Filters where ip_proto is implied by 
> an
> L4 ports or L2TPv3 session ID lookup are not affected. Based on an earlier
> unapplied patch from Michal Swiatkowski that implemented the IPv4 part [1].
> 
> Link: https://lore.kernel.org/intel-wired-lan/20240222123956.2393-3-
> [email protected]/ [1]
> Fixes: 0d08a441fb1a ("ice: ndo_setup_tc implementation for PF")
> Reviewed-by: Aleksandr Loktionov <[email protected]>
> Signed-off-by: Petr Oros <[email protected]>
> ---
> v2:
> - no code changes, collect Reviewed-by from Aleksandr Loktionov
> 
> v1: https://lore.kernel.org/all/20260910154824.3603687-2-
> [email protected]/
>  drivers/net/ethernet/intel/ice/ice_tc_lib.c | 37 +++++++++++++++++++--
> drivers/net/ethernet/intel/ice/ice_tc_lib.h |  1 +
>  2 files changed, 35 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c
> b/drivers/net/ethernet/intel/ice/ice_tc_lib.c
> index d20357c0412731..fbd8cbad150a98 100644
> --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c
> +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c
> @@ -78,7 +78,8 @@ static int ice_tc_count_lkups(u32 flags, struct
> ice_tc_flower_fltr *fltr)
>                    ICE_TC_FLWR_FIELD_DEST_IPV6 |
> ICE_TC_FLWR_FIELD_SRC_IPV6))
>               lkups_cnt++;
> 
> -     if (flags & (ICE_TC_FLWR_FIELD_IP_TOS |
> ICE_TC_FLWR_FIELD_IP_TTL))
> +     if (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL
> |
> +                  ICE_TC_FLWR_FIELD_IP_PROTO))
>               lkups_cnt++;
> 
>       /* are L2TPv3 options specified? */
> @@ -552,7 +553,8 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags,
>       }
> 
>       if (headers->l2_key.n_proto == htons(ETH_P_IP) &&
> -         (flags & (ICE_TC_FLWR_FIELD_IP_TOS |
> ICE_TC_FLWR_FIELD_IP_TTL))) {
> +         (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL
> |
> +                   ICE_TC_FLWR_FIELD_IP_PROTO))) {
>               list[i].type = ice_proto_type_from_ipv4(inner);
> 
>               if (flags & ICE_TC_FLWR_FIELD_IP_TOS) { @@ -567,11
> +569,19 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags,
>                               headers->l3_mask.ttl;
>               }
> 
> +             if (flags & ICE_TC_FLWR_FIELD_IP_PROTO) {
> +                     list[i].h_u.ipv4_hdr.protocol =
> +                             headers->l3_key.ip_proto;
> +                     list[i].m_u.ipv4_hdr.protocol =
> +                             headers->l3_mask.ip_proto;
> +             }
> +
>               i++;
>       }
> 
>       if (headers->l2_key.n_proto == htons(ETH_P_IPV6) &&
> -         (flags & (ICE_TC_FLWR_FIELD_IP_TOS |
> ICE_TC_FLWR_FIELD_IP_TTL))) {
> +         (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL
> |
> +                   ICE_TC_FLWR_FIELD_IP_PROTO))) {
>               struct ice_ipv6_hdr *hdr_h, *hdr_m;
> 
>               hdr_h = &list[i].h_u.ipv6_hdr;
> @@ -592,6 +602,11 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags,
>                       hdr_m->hop_limit = headers->l3_mask.ttl;
>               }
> 
> +             if (flags & ICE_TC_FLWR_FIELD_IP_PROTO) {
> +                     hdr_h->next_hdr = headers->l3_key.ip_proto;
> +                     hdr_m->next_hdr = headers->l3_mask.ip_proto;
> +             }
> +
>               i++;
>       }
> 
> @@ -1737,6 +1752,9 @@ ice_parse_cls_flower(struct net_device *filter_dev,
> struct ice_vsi *vsi,
>               headers->l2_key.n_proto = cpu_to_be16(n_proto_key);
>               headers->l2_mask.n_proto = cpu_to_be16(n_proto_mask);
>               headers->l3_key.ip_proto = match.key->ip_proto;
> +             headers->l3_mask.ip_proto = match.mask->ip_proto;
> +             if (match.mask->ip_proto)
> +                     fltr->flags |= ICE_TC_FLWR_FIELD_IP_PROTO;
>       }
> 
>       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
> @@ -1910,6 +1928,19 @@ ice_parse_cls_flower(struct net_device
> *filter_dev, struct ice_vsi *vsi,
>               }
>       }
> 
> +     if (fltr->flags & (ICE_TC_FLWR_FIELD_DEST_L4_PORT |
> +                        ICE_TC_FLWR_FIELD_SRC_L4_PORT |
> +                        ICE_TC_FLWR_FIELD_L2TPV3_SESSID))
> +             fltr->flags &= ~ICE_TC_FLWR_FIELD_IP_PROTO;
> +
> +     if ((fltr->flags & ICE_TC_FLWR_FIELD_IP_PROTO) &&
> +         headers->l2_key.n_proto != htons(ETH_P_IP) &&
> +         headers->l2_key.n_proto != htons(ETH_P_IPV6)) {
> +             NL_SET_ERR_MSG_MOD(fltr->extack,
> +                                "IP protocol match is not supported with
> GTP or PPPoE");
> +             return -EOPNOTSUPP;
> +     }
> +
>       /* Ingress filter on representor results in an egress filter in HW
>        * and vice versa
>        */
> diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h
> b/drivers/net/ethernet/intel/ice/ice_tc_lib.h
> index 8a3ab2f22af9ba..752af65e70b7bf 100644
> --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h
> +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h
> @@ -38,6 +38,7 @@
>  #define ICE_TC_FLWR_FIELD_CVLAN_PRIO         BIT(28)
>  #define ICE_TC_FLWR_FIELD_VLAN_TPID          BIT(29)
>  #define ICE_TC_FLWR_FIELD_PFCP_OPTS          BIT(30)
> +#define ICE_TC_FLWR_FIELD_IP_PROTO           BIT(31)
> 
>  #define ICE_TC_FLOWER_MASK_32   0xFFFFFFFF
> 
> --
> 2.55.0

Tested-by: Patryk Holda <[email protected]> 


Reply via email to