From: Eli Britstein <[email protected]>

Introduce eth-type field to tunnel metadata that is internally matched
for tunnels, for robust detection of the IP type.

Signed-off-by: Eli Britstein <[email protected]>
---
 include/openvswitch/match.h     |  1 +
 include/openvswitch/meta-flow.h | 14 +++++++++
 include/openvswitch/packets.h   |  3 +-
 lib/dpif.c                      |  6 ++++
 lib/dpif.h                      |  1 +
 lib/match.c                     | 10 ++++++
 lib/meta-flow.c                 | 18 +++++++++++
 lib/meta-flow.xml               |  1 +
 lib/netdev-native-tnl.c         | 20 ++++++++++++
 lib/netdev-offload-dpdk.c       | 55 ++++++++++++++++++++++++++++++---
 lib/odp-util.h                  |  5 ++-
 ofproto/ofproto-dpif-xlate.c    |  6 ++++
 ofproto/ofproto-dpif.c          |  3 ++
 ofproto/tunnel.c                |  1 +
 14 files changed, 137 insertions(+), 7 deletions(-)

diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
index 2e8812048e86..b962badfcfd3 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -127,6 +127,7 @@ void match_set_tun_gtpu_flags_masked(struct match *match, 
uint8_t flags,
 void match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype);
 void match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t msgtype,
                                        uint8_t mask);
+void match_set_tun_eth_type(struct match *, ovs_be16);
 void match_set_in_port(struct match *, ofp_port_t ofp_port);
 void match_set_pkt_mark(struct match *, uint32_t pkt_mark);
 void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t 
mask);
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index 875f122c5f55..e644c684bbf0 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -304,6 +304,20 @@ enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_TUN_ID,
 
+    /* "tun_eth_type".
+     *
+     * Packet's Tunnel Ethernet type.
+     *
+     * Type: be16.
+     * Maskable: no.
+     * Formatting: decimal.
+     * Prerequisites: none.
+     * Access: read-only.
+     * NXM: none.
+     * OXM: none.
+     */
+    MFF_TUN_ETH_TYPE,
+
     /* "tun_src".
      *
      * The IPv4 source address in the outer IP header of a tunneled packet.
diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
index a65cb0d04e77..c9af1b6ceca6 100644
--- a/include/openvswitch/packets.h
+++ b/include/openvswitch/packets.h
@@ -45,7 +45,8 @@ struct flow_tnl {
     uint8_t erspan_hwid;
     uint8_t gtpu_flags;
     uint8_t gtpu_msgtype;
-    uint8_t pad1[4];     /* Pad to 64 bits. */
+    ovs_be16 eth_type;
+    uint8_t pad1[2];     /* Pad to 64 bits. */
     struct tun_metadata metadata;
 };
 
diff --git a/lib/dpif.c b/lib/dpif.c
index a064f717f1a6..441abea3e6fd 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1960,6 +1960,12 @@ dpif_may_support_psample(const struct dpif *dpif)
     return !dpif_is_netdev(dpif);
 }
 
+bool
+dpif_supports_tun_eth_type(const struct dpif *dpif)
+{
+    return dpif_is_netdev(dpif);
+}
+
 /* Meters */
 void
 dpif_meter_get_features(const struct dpif *dpif,
diff --git a/lib/dpif.h b/lib/dpif.h
index 6bef7d5b304d..46633b31a198 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -918,6 +918,7 @@ int dpif_bond_add(struct dpif *, uint32_t bond_id, 
odp_port_t *member_map);
 int dpif_bond_del(struct dpif *, uint32_t bond_id);
 int dpif_bond_stats_get(struct dpif *, uint32_t bond_id, uint64_t *n_bytes);
 bool dpif_supports_lb_output_action(const struct dpif *);
+bool dpif_supports_tun_eth_type(const struct dpif *);
 
 
 /* Cache */
diff --git a/lib/match.c b/lib/match.c
index 9b7e06e0c7f8..bd8b21d592b1 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -402,6 +402,13 @@ match_set_tun_gtpu_msgtype(struct match *match, uint8_t 
msgtype)
     match_set_tun_gtpu_msgtype_masked(match, msgtype, UINT8_MAX);
 }
 
+void
+match_set_tun_eth_type(struct match *match, ovs_be16 eth_type)
+{
+    match->wc.masks.tunnel.eth_type = OVS_BE16_MAX;
+    match->flow.tunnel.eth_type = eth_type;
+}
+
 void
 match_set_in_port(struct match *match, ofp_port_t ofp_port)
 {
@@ -1391,6 +1398,9 @@ format_flow_tunnel(struct ds *s, const struct match 
*match)
     if (wc->masks.tunnel.gtpu_msgtype) {
         ds_put_format(s, "gtpu_msgtype=%"PRIu8",", tnl->gtpu_msgtype);
     }
+    if (wc->masks.tunnel.eth_type) {
+        ds_put_format(s, "tun_eth_type=0x%04"PRIx16",", ntohs(tnl->eth_type));
+    }
     if (wc->masks.tunnel.flags & FLOW_TNL_F_MASK) {
         format_flags_masked(s, "tun_flags", flow_tun_flag_to_string,
                             tnl->flags & FLOW_TNL_F_MASK,
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 2595fd634b4d..2a6ae129a91a 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -213,6 +213,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct 
flow_wildcards *wc)
         return !wc->masks.packet_type;
     case MFF_CONJ_ID:
         return !wc->masks.conj_id;
+    case MFF_TUN_ETH_TYPE:
+        return !wc->masks.tunnel.eth_type;
     case MFF_TUN_SRC:
         return !wc->masks.tunnel.ip_src;
     case MFF_TUN_DST:
@@ -524,6 +526,7 @@ mf_is_value_valid(const struct mf_field *mf, const union 
mf_value *value)
     case MFF_PACKET_TYPE:
     case MFF_CONJ_ID:
     case MFF_TUN_ID:
+    case MFF_TUN_ETH_TYPE:
     case MFF_TUN_SRC:
     case MFF_TUN_DST:
     case MFF_TUN_IPV6_SRC:
@@ -680,6 +683,9 @@ mf_get_value(const struct mf_field *mf, const struct flow 
*flow,
     case MFF_TUN_ID:
         value->be64 = flow->tunnel.tun_id;
         break;
+    case MFF_TUN_ETH_TYPE:
+        value->be16 = flow->tunnel.eth_type;
+        break;
     case MFF_TUN_SRC:
         value->be32 = flow->tunnel.ip_src;
         break;
@@ -1017,6 +1023,9 @@ mf_set_value(const struct mf_field *mf,
     case MFF_TUN_ID:
         match_set_tun_id(match, value->be64);
         break;
+    case MFF_TUN_ETH_TYPE:
+        match_set_tun_eth_type(match, value->be16);
+        break;
     case MFF_TUN_SRC:
         match_set_tun_src(match, value->be32);
         break;
@@ -1439,6 +1448,9 @@ mf_set_flow_value(const struct mf_field *mf,
     case MFF_TUN_ID:
         flow->tunnel.tun_id = value->be64;
         break;
+    case MFF_TUN_ETH_TYPE:
+        flow->tunnel.eth_type = value->be16;
+        break;
     case MFF_TUN_SRC:
         flow->tunnel.ip_src = value->be32;
         break;
@@ -1823,6 +1835,7 @@ mf_is_any_metadata(const struct mf_field *mf)
         return true;
 
     case MFF_TUN_ID:
+    case MFF_TUN_ETH_TYPE:
     case MFF_TUN_SRC:
     case MFF_TUN_DST:
     case MFF_TUN_IPV6_SRC:
@@ -1917,6 +1930,7 @@ mf_is_pipeline_field(const struct mf_field *mf)
 {
     switch (mf->id) {
     case MFF_TUN_ID:
+    case MFF_TUN_ETH_TYPE:
     case MFF_TUN_SRC:
     case MFF_TUN_DST:
     case MFF_TUN_IPV6_SRC:
@@ -2075,6 +2089,9 @@ mf_set_wild(const struct mf_field *mf, struct match 
*match, char **err_str)
     case MFF_TUN_ID:
         match_set_tun_id_masked(match, htonll(0), htonll(0));
         break;
+    case MFF_TUN_ETH_TYPE:
+        match->flow.tunnel.eth_type = 0;
+        break;
     case MFF_TUN_SRC:
         match_set_tun_src_masked(match, htonl(0), htonl(0));
         break;
@@ -2479,6 +2496,7 @@ mf_set(const struct mf_field *mf,
     case MFF_ICMPV6_CODE:
     case MFF_ND_RESERVED:
     case MFF_ND_OPTIONS_TYPE:
+    case MFF_TUN_ETH_TYPE:
         return OFPUTIL_P_NONE;
 
     case MFF_DP_HASH:
diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
index 5c57ab08ff18..558298279b9e 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -1508,6 +1508,7 @@ ovs-ofctl add-flow br-int 
'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
       </diagram>
     </field>
 
+    <field id="MFF_TUN_ETH_TYPE" title="Tunnel Ethernet type" internal="yes"/>
     <field id="MFF_TUN_SRC" title="Tunnel IPv4 Source">
       <p>
         When a packet is received from a tunnel, this field is the
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 439f0679dd4d..aacd10a83b81 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -502,6 +502,7 @@ netdev_gre_pop_header(struct dp_packet *packet)
     struct pkt_metadata *md = &packet->md;
     struct flow_tnl *tnl = &md->tunnel;
     int hlen = sizeof(struct eth_header) + 4;
+    const struct eth_header *eth;
 
     ovs_assert(data_dp);
 
@@ -518,6 +519,11 @@ netdev_gre_pop_header(struct dp_packet *packet)
         goto err;
     }
 
+    eth = dp_packet_eth(packet);
+    if (eth) {
+        tnl->eth_type = eth->eth_type;
+    }
+
     tnl_ol_pop(packet, hlen);
 
     return packet;
@@ -1098,6 +1104,7 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
     unsigned int hlen;
     ovs_be32 vx_flags;
     enum packet_type next_pt = PT_ETH;
+    const struct eth_header *eth;
 
     ovs_assert(packet->l3_ofs > 0);
     ovs_assert(packet->l4_ofs > 0);
@@ -1148,6 +1155,12 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
     tnl->flags |= FLOW_TNL_F_KEY;
 
     packet->packet_type = htonl(next_pt);
+
+    eth = dp_packet_eth(packet);
+    if (eth) {
+        tnl->eth_type = eth->eth_type;
+    }
+
     tnl_ol_pop(packet, hlen + VXLAN_HLEN);
     if (next_pt != PT_ETH) {
         packet->l3_ofs = 0;
@@ -1215,6 +1228,7 @@ netdev_geneve_pop_header(struct dp_packet *packet)
     struct flow_tnl *tnl = &md->tunnel;
     struct genevehdr *gnh;
     unsigned int hlen, opts_len, ulen;
+    const struct eth_header *eth;
 
     pkt_metadata_init_tnl(md);
     if (GENEVE_BASE_HLEN > dp_packet_l4_size(packet)) {
@@ -1256,6 +1270,12 @@ netdev_geneve_pop_header(struct dp_packet *packet)
     tnl->flags |= FLOW_TNL_F_UDPIF;
 
     packet->packet_type = htonl(PT_ETH);
+
+    eth = dp_packet_eth(packet);
+    if (eth) {
+        tnl->eth_type = eth->eth_type;
+    }
+
     tnl_ol_pop(packet, hlen);
 
     return packet;
diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
index 6ca271489d35..f289c45059e5 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -1185,11 +1185,32 @@ parse_tnl_ip_match(struct flow_patterns *patterns,
                    struct match *match,
                    uint8_t proto)
 {
+    ovs_be16 eth_type = match->flow.tunnel.eth_type;
     struct flow *consumed_masks;
+    bool is_ipv4, is_ipv6;
+
+    /* Determine IP version - either explicitly set via eth_type
+     * or inferred from masks.
+     */
+    if (eth_type == htons(ETH_TYPE_IP)) {
+        is_ipv4 = true;
+        is_ipv6 = false;
+    } else if (eth_type == htons(ETH_TYPE_IPV6)) {
+        is_ipv4 = false;
+        is_ipv6 = true;
+    } else {
+        /* eth_type is 0 - infer from which address masks are set */
+        is_ipv4 = (match->wc.masks.tunnel.ip_src ||
+                   match->wc.masks.tunnel.ip_dst);
+        is_ipv6 = (!is_all_zeros(&match->wc.masks.tunnel.ipv6_src,
+                   sizeof(struct in6_addr)) ||
+                   !is_all_zeros(&match->wc.masks.tunnel.ipv6_dst,
+                   sizeof(struct in6_addr)));
+    }
 
     consumed_masks = &match->wc.masks;
     /* IP v4 */
-    if (match->wc.masks.tunnel.ip_src || match->wc.masks.tunnel.ip_dst) {
+    if (is_ipv4) {
         struct rte_flow_item_ipv4 *spec, *mask;
 
         spec = xzalloc(sizeof *spec);
@@ -1212,11 +1233,16 @@ parse_tnl_ip_match(struct flow_patterns *patterns,
         consumed_masks->tunnel.ip_src = 0;
         consumed_masks->tunnel.ip_dst = 0;
 
+        /* Clear IPv6 artifacts when eth_type explicitly indicates IPv4 */
+        if (eth_type == htons(ETH_TYPE_IP)) {
+            memset(&consumed_masks->tunnel.ipv6_src, 0,
+                   sizeof consumed_masks->tunnel.ipv6_src);
+            memset(&consumed_masks->tunnel.ipv6_dst, 0,
+                   sizeof consumed_masks->tunnel.ipv6_dst);
+        }
+
         add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_IPV4, spec, mask, NULL);
-    } else if (!is_all_zeros(&match->wc.masks.tunnel.ipv6_src,
-                             sizeof(struct in6_addr)) ||
-               !is_all_zeros(&match->wc.masks.tunnel.ipv6_dst,
-                             sizeof(struct in6_addr))) {
+    } else if (is_ipv6) {
         /* IP v6 */
         struct rte_flow_item_ipv6 *spec, *mask;
 
@@ -1248,6 +1274,12 @@ parse_tnl_ip_match(struct flow_patterns *patterns,
         memset(&consumed_masks->tunnel.ipv6_dst, 0,
                sizeof consumed_masks->tunnel.ipv6_dst);
 
+        /* Clear IPv4 artifacts when eth_type explicitly indicates IPv6 */
+        if (eth_type == htons(ETH_TYPE_IPV6)) {
+            consumed_masks->tunnel.ip_src = 0;
+            consumed_masks->tunnel.ip_dst = 0;
+        }
+
         add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_IPV6, spec, mask, NULL);
     } else {
         VLOG_ERR_RL(&rl, "Tunnel L3 protocol is neither IPv4 nor IPv6");
@@ -1379,6 +1411,19 @@ parse_flow_tnl_match(struct netdev *tnldev,
         return ret;
     }
 
+    if (match->wc.masks.tunnel.eth_type) {
+        struct rte_flow_item_eth *spec, *mask;
+
+        spec = xzalloc(sizeof *spec);
+        mask = xzalloc(sizeof *mask);
+
+        spec->type = match->flow.tunnel.eth_type;
+        mask->type = match->wc.masks.tunnel.eth_type;
+        match->wc.masks.tunnel.eth_type = 0;
+
+        add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_ETH, spec, mask, NULL);
+    }
+
     if (!strcmp(netdev_get_type(tnldev), "vxlan")) {
         ret = parse_vxlan_match(patterns, match);
     }
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 339ffc14f248..66b5339ed2ba 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -207,7 +207,10 @@ int odp_flow_from_string(const char *s, const struct simap 
*port_names,
                                                                              \
     /* If true, it means that the datapath supports the IPv6 Neigh           \
      * Discovery Extension bits. */                                          \
-    ODP_SUPPORT_FIELD(bool, nd_ext, "IPv6 ND Extension")
+    ODP_SUPPORT_FIELD(bool, nd_ext, "IPv6 ND Extension")                     \
+                                                                             \
+    /* Tunnel ethernet type matching supported. */                           \
+    ODP_SUPPORT_FIELD(bool, tun_eth_type, "Tunnel Ethernet Type")
 
 /* Indicates support for various fields. This defines how flows will be
  * serialised. */
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 2c8197fb7307..88e640f1642d 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -8081,6 +8081,8 @@ xlate_wc_init(struct xlate_ctx *ctx)
 static void
 xlate_wc_finish(struct xlate_ctx *ctx)
 {
+    struct ofproto_dpif *ofproto = ctx->xbridge->ofproto;
+    struct dpif_backer *backer = ofproto->backer;
     int i;
 
     /* Clear the metadata and register wildcard masks, because we won't
@@ -8152,6 +8154,10 @@ xlate_wc_finish(struct xlate_ctx *ctx)
     if (!flow_tnl_dst_is_set(&ctx->xin->upcall_flow->tunnel)) {
         memset(&ctx->wc->masks.tunnel, 0, sizeof ctx->wc->masks.tunnel);
     }
+    /* Clear tunnel eth type if not supported. */
+    if (!backer->rt_support.odp.tun_eth_type) {
+        ctx->wc->masks.tunnel.eth_type = 0;
+    }
 }
 
 /* This will tweak the odp actions generated. For now, it will:
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index ed9e44ce2b03..9a7b005e46c9 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1755,6 +1755,8 @@ check_support(struct dpif_backer *backer)
     backer->rt_support.odp.ct_orig_tuple = check_ct_orig_tuple(backer);
     backer->rt_support.odp.ct_orig_tuple6 = check_ct_orig_tuple6(backer);
     backer->rt_support.odp.nd_ext = check_nd_extensions(backer);
+    backer->rt_support.odp.tun_eth_type =
+        dpif_supports_tun_eth_type(backer->dpif);
 }
 
 /* TC does not support offloading the explicit drop action. As such we need to
@@ -5918,6 +5920,7 @@ get_datapath_cap(const char *datapath_type, struct smap 
*cap)
     smap_add(cap, "ct_orig_tuple", s->odp.ct_orig_tuple ? "true" : "false");
     smap_add(cap, "ct_orig_tuple6", s->odp.ct_orig_tuple6 ? "true" : "false");
     smap_add(cap, "nd_ext", s->odp.nd_ext ? "true" : "false");
+    smap_add(cap, "tun_eth_type", s->odp.tun_eth_type ? "true" : "false");
 
     /* DPIF_SUPPORT_FIELDS */
     smap_add(cap, "masked_set_action",
diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
index f067a6c26c19..4adc87b91baa 100644
--- a/ofproto/tunnel.c
+++ b/ofproto/tunnel.c
@@ -381,6 +381,7 @@ tnl_wc_init(struct flow *flow, struct flow_wildcards *wc)
          * wildcarded, not to unwildcard them here. */
         wc->masks.tunnel.tp_src = 0;
         wc->masks.tunnel.tp_dst = 0;
+        wc->masks.tunnel.eth_type = OVS_BE16_MAX;
 
         if (is_ip_any(flow)
             && IP_ECN_is_ce(flow->tunnel.ip_tos)) {
-- 
2.47.0

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

Reply via email to