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
