+static int
+enic_fm_copy_item_gtp(struct copy_item_args *arg)
+{
+ const struct rte_flow_item *item = arg->item;
+ const struct rte_flow_item_gtp *spec = item->spec;
+ const struct rte_flow_item_gtp *mask = item->mask;
+ struct fm_tcam_match_entry *entry = arg->fm_tcam_entry;
+ struct fm_header_set *fm_data, *fm_mask;
+ int off;
+ uint16_t udp_gtp_uc_port_be = 0;
+
+ ENICPMD_FUNC_TRACE();
+ /* Only 2 header levels (outer and inner) allowed */
+ if (arg->header_level > 0)
+ return -EINVAL;
+
+ fm_data = &entry->ftm_data.fk_hdrset[0];
+ fm_mask = &entry->ftm_mask.fk_hdrset[0];
+
+ switch (item->type) {
+ case RTE_FLOW_ITEM_TYPE_GTP:
+ {
+ /* For vanilla GTP, the UDP destination port must be specified
+ * but value of the port is not enforced here.
+ */
+ if (!(fm_data->fk_metadata & FKM_UDP) ||
+ !(fm_data->fk_header_select & FKH_UDP) ||
+ fm_data->l4.udp.fk_dest == 0)
+ return -EINVAL;
+ if (!(fm_mask->fk_metadata & FKM_UDP) ||
+ !(fm_mask->fk_header_select & FKH_UDP) ||
+ fm_mask->l4.udp.fk_dest != 0xFFFF)
+ return -EINVAL;
+ break;
+ }
+ case RTE_FLOW_ITEM_TYPE_GTPC:
+ {
+ udp_gtp_uc_port_be = rte_cpu_to_be_16(RTE_GTPC_UDP_PORT);
+ break;
+ }
+ case RTE_FLOW_ITEM_TYPE_GTPU:
+ {
+ udp_gtp_uc_port_be = rte_cpu_to_be_16(RTE_GTPU_UDP_PORT);
+ break;
+ }
+ default:
+ RTE_ASSERT(0);
+ }
+
+ /* The GTP-C or GTP-U UDP destination port must be matched. */
+ if (udp_gtp_uc_port_be) {
+ if (fm_data->fk_metadata & FKM_UDP &&
+ fm_data->fk_header_select & FKH_UDP &&
+ fm_data->l4.udp.fk_dest != udp_gtp_uc_port_be)
+ return -EINVAL;
+ if (fm_mask->fk_metadata & FKM_UDP &&
+ fm_mask->fk_header_select & FKH_UDP &&
+ fm_mask->l4.udp.fk_dest != 0xFFFF)
+ return -EINVAL;
+
+ /* In any case, add match for GTP-C GTP-U UDP dst port */
+ fm_data->fk_metadata |= FKM_UDP;
+ fm_data->fk_header_select |= FKH_UDP;
+ fm_data->l4.udp.fk_dest = udp_gtp_uc_port_be;
+ fm_mask->fk_metadata |= FKM_UDP;
+ fm_mask->fk_header_select |= FKH_UDP;
+ fm_mask->l4.udp.fk_dest = 0xFFFF;
+ }
+
+ /* NIC does not support GTP tunnels. No Items are allowed after this.
+ * This prevents the specification of further items.
+ */
+ arg->header_level = 0;
+
+ /* Match all if no spec */
+ if (!spec)
+ return 0;
+ if (!mask)
+ mask = &rte_flow_item_gtp_mask;
+
+ /*
+ * Use the raw L4 buffer to match GTP as fm_header_set does not have
+ * GTP header. UDP dst port must be specific. Using the raw buffer
+ * does not affect such UDP item, since we skip UDP in the raw buffer.
+ */
+ fm_data->fk_header_select |= FKH_L4RAW;
+ fm_mask->fk_header_select |= FKH_L4RAW;
+ off = sizeof(fm_data->l4.udp);
+ memcpy(&fm_data->l4.rawdata[off], spec, sizeof(*spec));
+ memcpy(&fm_mask->l4.rawdata[off], mask, sizeof(*mask));
+ return 0;
+}
+
+static int
+enic_fm_copy_item_geneve(struct copy_item_args *arg)
+{
+ const struct rte_flow_item *item = arg->item;
+ const struct rte_flow_item_geneve *spec = item->spec;
+ const struct rte_flow_item_geneve *mask = item->mask;
+ struct fm_tcam_match_entry *entry = arg->fm_tcam_entry;
+ struct fm_header_set *fm_data, *fm_mask;
+ int off;
+
+ ENICPMD_FUNC_TRACE();
+ /* Only 2 header levels (outer and inner) allowed */
+ if (arg->header_level > 0)
+ return -EINVAL;
+
+ fm_data = &entry->ftm_data.fk_hdrset[0];
+ fm_mask = &entry->ftm_mask.fk_hdrset[0];
+ fm_data->fk_metadata |= FKM_GENEVE;
+ fm_mask->fk_metadata |= FKM_GENEVE;
+ /* items from here on out are inner header items, except options */
+ arg->header_level = 1;
+
+ /* Match all if no spec */
+ if (!spec)
+ return 0;
+ if (!mask)
+ mask = &rte_flow_item_geneve_mask;
+
+ /*
+ * Use the raw L4 buffer to match geneve as fm_header_set does
+ * not have geneve header. A UDP item may precede the geneve
+ * item. Using the raw buffer does not affect such UDP item,
+ * since we skip UDP in the raw buffer.
+ */
+ fm_data->fk_header_select |= FKH_L4RAW;
+ fm_mask->fk_header_select |= FKH_L4RAW;
+ off = sizeof(fm_data->l4.udp);
+ memcpy(&fm_data->l4.rawdata[off], spec, sizeof(struct rte_geneve_hdr));
+ memcpy(&fm_mask->l4.rawdata[off], mask, sizeof(struct rte_geneve_hdr));
+ return 0;
+}
+
+static int
+enic_fm_copy_item_geneve_opt(struct copy_item_args *arg)
+{
+ const struct rte_flow_item *item = arg->item;
+ const struct rte_flow_item_geneve_opt *spec = item->spec;
+ const struct rte_flow_item_geneve_opt *mask = item->mask;
+ struct fm_tcam_match_entry *entry = arg->fm_tcam_entry;
+ struct fm_header_set *fm_data, *fm_mask;
+ struct rte_geneve_hdr *geneve;
+ int off, len;
+
+ ENICPMD_FUNC_TRACE();
+ fm_data = &entry->ftm_data.fk_hdrset[0];
+ fm_mask = &entry->ftm_mask.fk_hdrset[0];
+ /* Match all if no spec */
+ if (!spec)
+ return 0;
+ if (!mask)
+ mask = &rte_flow_item_geneve_opt_mask;
+
+ if (spec->option_len > 0 &&
+ (spec->data == NULL || mask->data == NULL)) {
+ return rte_flow_error_set(arg->error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ NULL, "enic: geneve_opt unexpected null data");
+ }
+ /*
+ * Geneve item must already be in the raw buffer. Append the
+ * option pattern to it. There are two limitations.
+ * (1) Can match only the 1st option, the first one following Geneve
+ * (2) Geneve header must specify option length, as HW does not
+ * have "has Geneve option" flag.
+ */
+ RTE_ASSERT((fm_data->fk_header_select & FKH_L4RAW) != 0);
+ RTE_ASSERT((fm_mask->fk_header_select & FKH_L4RAW) != 0);
+ off = sizeof(fm_data->l4.udp);
+ geneve = (struct rte_geneve_hdr *)&fm_data->l4.rawdata[off];
+ if (geneve->opt_len == 0) {
+ return rte_flow_error_set(arg->error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ NULL, "enic: geneve_opt requires non-zero geneve option length");
+ }
+ geneve = (struct rte_geneve_hdr *)&fm_mask->l4.rawdata[off];
+ if (geneve->opt_len == 0) {
+ return rte_flow_error_set(arg->error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ NULL, "enic: geneve_opt requires non-zero geneve option length mask");
+ }
+ off = sizeof(fm_data->l4.udp) + sizeof(struct rte_geneve_hdr);
+ if (off + (spec->option_len + 1) * 4 > FM_LAYER_SIZE) {
+ return rte_flow_error_set(arg->error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ NULL, "enic: geneve_opt too large");
+ }
+ /* Copy option header */
+ memcpy(&fm_data->l4.rawdata[off], spec, 4);
+ memcpy(&fm_mask->l4.rawdata[off], mask, 4);
+ /* Copy option data */
+ if (spec->option_len > 0) {
+ off += 4;
+ len = spec->option_len * 4;
+ memcpy(&fm_data->l4.rawdata[off], spec->data, len);
+ memcpy(&fm_mask->l4.rawdata[off], mask->data, len);
+ }
+ return 0;
+}
+