/* SPDX-License-Identifier: BSD-3-Clause * Copyright(C) 2021 Marvell. */ #include "roc_api.h" #include "roc_priv.h" const struct roc_npc_item_info * npc_parse_skip_void_and_any_items(const struct roc_npc_item_info *pattern) { while ((pattern->type == ROC_NPC_ITEM_TYPE_VOID) || (pattern->type == ROC_NPC_ITEM_TYPE_ANY)) pattern++; return pattern; } int npc_parse_meta_items(struct npc_parse_state *pst) { PLT_SET_USED(pst); return 0; } int npc_parse_cpt_hdr(struct npc_parse_state *pst) { uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt; int rc; /* Identify the pattern type into lid, lt */ if (pst->pattern->type != ROC_NPC_ITEM_TYPE_CPT_HDR) return 0; lid = NPC_LID_LA; lt = NPC_LT_LA_CPT_HDR; info.hw_hdr_len = 0; /* Prepare for parsing the item */ info.def_mask = NULL; info.hw_mask = &hw_mask; info.len = pst->pattern->size; npc_get_hw_supp_mask(pst, &info, lid, lt); info.spec = NULL; info.mask = NULL; /* Basic validation of item parameters */ rc = npc_parse_item_basic(pst->pattern, &info); if (rc) return rc; /* Update pst if not validate only? clash check? */ return npc_update_parse_state(pst, &info, lid, lt, 0); } int npc_parse_higig2_hdr(struct npc_parse_state *pst) { uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt; int rc; /* Identify the pattern type into lid, lt */ if (pst->pattern->type != ROC_NPC_ITEM_TYPE_HIGIG2) return 0; lid = NPC_LID_LA; lt = NPC_LT_LA_HIGIG2_ETHER; info.hw_hdr_len = 0; if (pst->flow->nix_intf == NIX_INTF_TX) { lt = NPC_LT_LA_IH_NIX_HIGIG2_ETHER; info.hw_hdr_len = NPC_IH_LENGTH; } /* Prepare for parsing the item */ info.def_mask = NULL; info.hw_mask = &hw_mask; info.len = pst->pattern->size; npc_get_hw_supp_mask(pst, &info, lid, lt); info.spec = NULL; info.mask = NULL; /* Basic validation of item parameters */ rc = npc_parse_item_basic(pst->pattern, &info); if (rc) return rc; /* Update pst if not validate only? clash check? */ return npc_update_parse_state(pst, &info, lid, lt, 0); } int npc_parse_la(struct npc_parse_state *pst) { uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt; int rc; /* Identify the pattern type into lid, lt */ if (pst->pattern->type != ROC_NPC_ITEM_TYPE_ETH) return 0; lid = NPC_LID_LA; lt = NPC_LT_LA_ETHER; info.hw_hdr_len = 0; if (pst->flow->nix_intf == NIX_INTF_TX) { lt = NPC_LT_LA_IH_NIX_ETHER; info.hw_hdr_len = NPC_IH_LENGTH; if (pst->npc->switch_header_type == ROC_PRIV_FLAGS_HIGIG) { lt = NPC_LT_LA_IH_NIX_HIGIG2_ETHER; info.hw_hdr_len += NPC_HIGIG2_LENGTH; } } else { if (pst->npc->switch_header_type == ROC_PRIV_FLAGS_HIGIG) { lt = NPC_LT_LA_HIGIG2_ETHER; info.hw_hdr_len = NPC_HIGIG2_LENGTH; } } /* Prepare for parsing the item */ info.def_mask = NULL; info.hw_mask = &hw_mask; info.len = pst->pattern->size; npc_get_hw_supp_mask(pst, &info, lid, lt); info.spec = NULL; info.mask = NULL; /* Basic validation of item parameters */ rc = npc_parse_item_basic(pst->pattern, &info); if (rc) return rc; /* Update pst if not validate only? clash check? */ return npc_update_parse_state(pst, &info, lid, lt, 0); } static int npc_flow_raw_item_prepare(const struct roc_npc_flow_item_raw *raw_spec, const struct roc_npc_flow_item_raw *raw_mask, struct npc_parse_item_info *info, uint8_t *spec_buf, uint8_t *mask_buf) { uint32_t custom_hdr_size = 0; memset(spec_buf, 0, NPC_MAX_RAW_ITEM_LEN); memset(mask_buf, 0, NPC_MAX_RAW_ITEM_LEN); custom_hdr_size = raw_spec->offset + raw_spec->length; memcpy(spec_buf + raw_spec->offset, raw_spec->pattern, raw_spec->length); if (raw_mask->pattern) { memcpy(mask_buf + raw_spec->offset, raw_mask->pattern, raw_spec->length); } else { memset(mask_buf + raw_spec->offset, 0xFF, raw_spec->length); } info->len = custom_hdr_size; info->spec = spec_buf; info->mask = mask_buf; return 0; } #define NPC_MAX_SUPPORTED_VLANS 3 static int npc_parse_vlan_count(const struct roc_npc_item_info *pattern, const struct roc_npc_item_info **pattern_list, const struct roc_npc_flow_item_vlan **vlan_items, int *vlan_count) { *vlan_count = 0; while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) { if (*vlan_count > NPC_MAX_SUPPORTED_VLANS - 1) return NPC_ERR_PATTERN_NOTSUP; /* Don't support ranges */ if (pattern->last != NULL) return NPC_ERR_INVALID_RANGE; /* If spec is NULL, both mask and last must be NULL, this * makes it to match ANY value (eq to mask = 0). * Setting either mask or last without spec is an error */ if (pattern->spec == NULL) { if (pattern->last != NULL && pattern->mask != NULL) return NPC_ERR_INVALID_SPEC; } pattern_list[*vlan_count] = pattern; vlan_items[*vlan_count] = pattern->spec; (*vlan_count)++; pattern++; pattern = npc_parse_skip_void_and_any_items(pattern); } return 0; } static int npc_parse_vlan_ltype_get(struct npc_parse_state *pst, const struct roc_npc_flow_item_vlan **vlan_item, int vlan_count, int *ltype, int *lflags) { switch (vlan_count) { case 1: *ltype = NPC_LT_LB_CTAG; if (vlan_item[0] && vlan_item[0]->has_more_vlan) *ltype = NPC_LT_LB_STAG_QINQ; break; case 2: if (vlan_item[1] && vlan_item[1]->has_more_vlan) { if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] & 0x3ULL << NPC_LFLAG_LB_OFFSET)) return NPC_ERR_PATTERN_NOTSUP; /* This lflag value will match either one of * NPC_F_LB_L_WITH_STAG_STAG, * NPC_F_LB_L_WITH_QINQ_CTAG, * NPC_F_LB_L_WITH_QINQ_QINQ and * NPC_F_LB_L_WITH_ITAG (0b0100 to 0b0111). For * NPC_F_LB_L_WITH_ITAG, ltype is NPC_LT_LB_ETAG * hence will not match. */ *lflags = NPC_F_LB_L_WITH_QINQ_CTAG & NPC_F_LB_L_WITH_QINQ_QINQ & NPC_F_LB_L_WITH_STAG_STAG; } *ltype = NPC_LT_LB_STAG_QINQ; break; case 3: if (vlan_item[2] && vlan_item[2]->has_more_vlan) return NPC_ERR_PATTERN_NOTSUP; if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] & 0x3ULL << NPC_LFLAG_LB_OFFSET)) return NPC_ERR_PATTERN_NOTSUP; *ltype = NPC_LT_LB_STAG_QINQ; *lflags = NPC_F_STAG_STAG_CTAG; break; default: return NPC_ERR_PATTERN_NOTSUP; } return 0; } static int npc_update_vlan_parse_state(struct npc_parse_state *pst, const struct roc_npc_item_info *pattern, int lid, int lt, uint8_t lflags, int vlan_count) { uint8_t vlan_spec[NPC_MAX_SUPPORTED_VLANS * sizeof(struct roc_vlan_hdr)]; uint8_t vlan_mask[NPC_MAX_SUPPORTED_VLANS * sizeof(struct roc_vlan_hdr)]; int rc = 0, i, offset = NPC_TPID_LENGTH; struct npc_parse_item_info parse_info; char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; memset(vlan_spec, 0, sizeof(struct roc_vlan_hdr) * NPC_MAX_SUPPORTED_VLANS); memset(vlan_mask, 0, sizeof(struct roc_vlan_hdr) * NPC_MAX_SUPPORTED_VLANS); memset(&parse_info, 0, sizeof(parse_info)); if (vlan_count > 2) vlan_count = 2; for (i = 0; i < vlan_count; i++) { if (pattern[i].spec) memcpy(vlan_spec + offset, pattern[i].spec, sizeof(struct roc_vlan_hdr)); if (pattern[i].mask) memcpy(vlan_mask + offset, pattern[i].mask, sizeof(struct roc_vlan_hdr)); offset += 4; } parse_info.def_mask = NULL; parse_info.spec = vlan_spec; parse_info.mask = vlan_mask; parse_info.def_mask = NULL; parse_info.hw_hdr_len = 0; lid = NPC_LID_LB; parse_info.hw_mask = hw_mask; if (lt == NPC_LT_LB_CTAG) parse_info.len = sizeof(struct roc_vlan_hdr) + NPC_TPID_LENGTH; if (lt == NPC_LT_LB_STAG_QINQ) parse_info.len = sizeof(struct roc_vlan_hdr) * 2 + NPC_TPID_LENGTH; memset(hw_mask, 0, sizeof(hw_mask)); parse_info.hw_mask = &hw_mask; npc_get_hw_supp_mask(pst, &parse_info, lid, lt); rc = npc_mask_is_supported(parse_info.mask, parse_info.hw_mask, parse_info.len); if (!rc) return NPC_ERR_INVALID_MASK; /* Point pattern to last item consumed */ pst->pattern = pattern; return npc_update_parse_state(pst, &parse_info, lid, lt, lflags); } static int npc_parse_lb_vlan(struct npc_parse_state *pst) { const struct roc_npc_flow_item_vlan *vlan_items[NPC_MAX_SUPPORTED_VLANS]; const struct roc_npc_item_info *pattern_list[NPC_MAX_SUPPORTED_VLANS]; const struct roc_npc_item_info *last_pattern; int vlan_count = 0, rc = 0; int lid, lt, lflags; lid = NPC_LID_LB; lflags = 0; last_pattern = pst->pattern; rc = npc_parse_vlan_count(pst->pattern, pattern_list, vlan_items, &vlan_count); if (rc) return rc; rc = npc_parse_vlan_ltype_get(pst, vlan_items, vlan_count, <, &lflags); if (rc) return rc; if (vlan_count == 3) { if (pattern_list[2]->spec != NULL && pattern_list[2]->mask != NULL && pattern_list[2]->last != NULL) return NPC_ERR_PATTERN_NOTSUP; /* Matching can be done only for two tags. */ vlan_count = 2; last_pattern++; } rc = npc_update_vlan_parse_state(pst, pattern_list[0], lid, lt, lflags, vlan_count); if (rc) return rc; if (vlan_count > 1) pst->pattern = last_pattern + vlan_count; return 0; } int npc_parse_lb(struct npc_parse_state *pst) { const struct roc_npc_item_info *pattern = pst->pattern; const struct roc_npc_item_info *last_pattern; const struct roc_npc_flow_item_raw *raw_spec; uint8_t raw_spec_buf[NPC_MAX_RAW_ITEM_LEN]; uint8_t raw_mask_buf[NPC_MAX_RAW_ITEM_LEN]; char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt, lflags, len = 0; int rc; info.def_mask = NULL; info.spec = NULL; info.mask = NULL; info.def_mask = NULL; info.hw_hdr_len = NPC_TPID_LENGTH; lid = NPC_LID_LB; lflags = 0; last_pattern = pattern; if (pst->pattern->type == ROC_NPC_ITEM_TYPE_VLAN) { /* RTE vlan is either 802.1q or 802.1ad, * this maps to either CTAG/STAG. We need to decide * based on number of VLANS present. Matching is * supported on first two tags. */ return npc_parse_lb_vlan(pst); } else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_E_TAG) { /* we can support ETAG and match a subsequent CTAG * without any matching support. */ lt = NPC_LT_LB_ETAG; lflags = 0; last_pattern = pst->pattern; pattern = npc_parse_skip_void_and_any_items(pst->pattern + 1); if (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) { /* set supported mask to NULL for vlan tag */ info.hw_mask = NULL; info.len = pattern->size; rc = npc_parse_item_basic(pattern, &info); if (rc != 0) return rc; lflags = NPC_F_ETAG_CTAG; last_pattern = pattern; } info.len = pattern->size; } else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_QINQ) { info.hw_mask = NULL; info.len = pst->pattern->size; lt = NPC_LT_LB_STAG_QINQ; lflags = NPC_F_STAG_CTAG; } else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_RAW) { raw_spec = pst->pattern->spec; if (raw_spec->relative) return 0; len = raw_spec->length + raw_spec->offset; if (len > NPC_MAX_RAW_ITEM_LEN) return -EINVAL; if (pst->npc->switch_header_type == ROC_PRIV_FLAGS_VLAN_EXDSA) { lt = NPC_LT_LB_VLAN_EXDSA; } else if (pst->npc->switch_header_type == ROC_PRIV_FLAGS_EXDSA) { lt = NPC_LT_LB_EXDSA; } else { return -EINVAL; } npc_flow_raw_item_prepare((const struct roc_npc_flow_item_raw *) pst->pattern->spec, (const struct roc_npc_flow_item_raw *) pst->pattern->mask, &info, raw_spec_buf, raw_mask_buf); info.hw_hdr_len = 0; } else { return 0; } info.hw_mask = &hw_mask; npc_get_hw_supp_mask(pst, &info, lid, lt); rc = npc_parse_item_basic(pst->pattern, &info); if (rc != 0) return rc; /* Point pattern to last item consumed */ pst->pattern = last_pattern; return npc_update_parse_state(pst, &info, lid, lt, lflags); } static int npc_parse_mpls_label_stack(struct npc_parse_state *pst, int *flag) { uint8_t flag_list[] = {0, NPC_F_MPLS_2_LABELS, NPC_F_MPLS_3_LABELS, NPC_F_MPLS_4_LABELS}; const struct roc_npc_item_info *pattern = pst->pattern; struct npc_parse_item_info info; int nr_labels = 0; int rc; /* * pst->pattern points to first MPLS label. We only check * that subsequent labels do not have anything to match. */ info.def_mask = NULL; info.hw_mask = NULL; info.len = pattern->size; info.spec = NULL; info.mask = NULL; info.hw_hdr_len = 0; while (pattern->type == ROC_NPC_ITEM_TYPE_MPLS) { nr_labels++; /* Basic validation of Second/Third/Fourth mpls item */ if (nr_labels > 1) { rc = npc_parse_item_basic(pattern, &info); if (rc != 0) return rc; } pst->last_pattern = pattern; pattern++; pattern = npc_parse_skip_void_and_any_items(pattern); } if (nr_labels < 1 || nr_labels > 4) return NPC_ERR_PATTERN_NOTSUP; *flag = flag_list[nr_labels - 1]; return 0; } static int npc_parse_mpls(struct npc_parse_state *pst, int lid) { /* Find number of MPLS labels */ uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lt, lflags; int rc; lflags = 0; if (lid == NPC_LID_LC) lt = NPC_LT_LC_MPLS; else if (lid == NPC_LID_LD) lt = NPC_LT_LD_TU_MPLS_IN_IP; else lt = NPC_LT_LE_TU_MPLS_IN_UDP; /* Prepare for parsing the first item */ info.hw_mask = &hw_mask; info.len = pst->pattern->size; info.spec = NULL; info.mask = NULL; info.def_mask = NULL; info.hw_hdr_len = 0; npc_get_hw_supp_mask(pst, &info, lid, lt); rc = npc_parse_item_basic(pst->pattern, &info); if (rc != 0) return rc; /* * Parse for more labels. * This sets lflags and pst->last_pattern correctly. */ rc = npc_parse_mpls_label_stack(pst, &lflags); if (rc != 0) return rc; pst->tunnel = 1; pst->pattern = pst->last_pattern; return npc_update_parse_state(pst, &info, lid, lt, lflags); } static inline void npc_check_lc_ip_tunnel(struct npc_parse_state *pst) { const struct roc_npc_item_info *pattern = pst->pattern + 1; pattern = npc_parse_skip_void_and_any_items(pattern); if (pattern->type == ROC_NPC_ITEM_TYPE_MPLS || pattern->type == ROC_NPC_ITEM_TYPE_IPV4 || pattern->type == ROC_NPC_ITEM_TYPE_IPV6) pst->tunnel = 1; } int npc_parse_lc(struct npc_parse_state *pst) { const struct roc_npc_flow_item_raw *raw_spec; uint8_t raw_spec_buf[NPC_MAX_RAW_ITEM_LEN]; uint8_t raw_mask_buf[NPC_MAX_RAW_ITEM_LEN]; uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt, len = 0; int rc; if (pst->pattern->type == ROC_NPC_ITEM_TYPE_MPLS) return npc_parse_mpls(pst, NPC_LID_LC); info.def_mask = NULL; info.hw_mask = &hw_mask; info.spec = NULL; info.mask = NULL; info.hw_hdr_len = 0; lid = NPC_LID_LC; switch (pst->pattern->type) { case ROC_NPC_ITEM_TYPE_IPV4: lt = NPC_LT_LC_IP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_IPV6: lid = NPC_LID_LC; lt = NPC_LT_LC_IP6; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_ARP_ETH_IPV4: lt = NPC_LT_LC_ARP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_IPV6_EXT: lid = NPC_LID_LC; lt = NPC_LT_LC_IP6_EXT; info.len = pst->pattern->size; info.hw_hdr_len = 40; break; case ROC_NPC_ITEM_TYPE_L3_CUSTOM: lt = NPC_LT_LC_CUSTOM0; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_RAW: raw_spec = pst->pattern->spec; if (!raw_spec->relative) return 0; len = raw_spec->length + raw_spec->offset; if (len > NPC_MAX_RAW_ITEM_LEN) return -EINVAL; npc_flow_raw_item_prepare((const struct roc_npc_flow_item_raw *) pst->pattern->spec, (const struct roc_npc_flow_item_raw *) pst->pattern->mask, &info, raw_spec_buf, raw_mask_buf); lid = NPC_LID_LC; lt = NPC_LT_LC_NGIO; info.hw_mask = &hw_mask; npc_get_hw_supp_mask(pst, &info, lid, lt); break; default: /* No match at this layer */ return 0; } /* Identify if IP tunnels MPLS or IPv4/v6 */ npc_check_lc_ip_tunnel(pst); npc_get_hw_supp_mask(pst, &info, lid, lt); rc = npc_parse_item_basic(pst->pattern, &info); if (rc != 0) return rc; return npc_update_parse_state(pst, &info, lid, lt, 0); } int npc_parse_ld(struct npc_parse_state *pst) { char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt, lflags; int rc; if (pst->tunnel) { /* We have already parsed MPLS or IPv4/v6 followed * by MPLS or IPv4/v6. Subsequent TCP/UDP etc * would be parsed as tunneled versions. Skip * this layer, except for tunneled MPLS. If LC is * MPLS, we have anyway skipped all stacked MPLS * labels. */ if (pst->pattern->type == ROC_NPC_ITEM_TYPE_MPLS) return npc_parse_mpls(pst, NPC_LID_LD); return 0; } info.def_mask = NULL; info.hw_mask = &hw_mask; info.spec = NULL; info.mask = NULL; info.len = 0; info.hw_hdr_len = 0; lid = NPC_LID_LD; lflags = 0; switch (pst->pattern->type) { case ROC_NPC_ITEM_TYPE_ICMP: if (pst->lt[NPC_LID_LC] == NPC_LT_LC_IP6) lt = NPC_LT_LD_ICMP6; else lt = NPC_LT_LD_ICMP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_UDP: lt = NPC_LT_LD_UDP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_IGMP: lt = NPC_LT_LD_IGMP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_TCP: lt = NPC_LT_LD_TCP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_SCTP: lt = NPC_LT_LD_SCTP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_GRE: lt = NPC_LT_LD_GRE; info.len = pst->pattern->size; pst->tunnel = 1; break; case ROC_NPC_ITEM_TYPE_GRE_KEY: lt = NPC_LT_LD_GRE; info.len = pst->pattern->size; info.hw_hdr_len = 4; pst->tunnel = 1; break; case ROC_NPC_ITEM_TYPE_NVGRE: lt = NPC_LT_LD_NVGRE; lflags = NPC_F_GRE_NVGRE; info.len = pst->pattern->size; /* Further IP/Ethernet are parsed as tunneled */ pst->tunnel = 1; break; default: return 0; } npc_get_hw_supp_mask(pst, &info, lid, lt); rc = npc_parse_item_basic(pst->pattern, &info); if (rc != 0) return rc; return npc_update_parse_state(pst, &info, lid, lt, lflags); } int npc_parse_le(struct npc_parse_state *pst) { const struct roc_npc_item_info *pattern = pst->pattern; char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt, lflags; int rc; if (pst->tunnel) return 0; if (pst->pattern->type == ROC_NPC_ITEM_TYPE_MPLS) return npc_parse_mpls(pst, NPC_LID_LE); info.spec = NULL; info.mask = NULL; info.hw_mask = NULL; info.def_mask = NULL; info.len = 0; info.hw_hdr_len = 0; lid = NPC_LID_LE; lflags = 0; /* Ensure we are not matching anything in UDP */ rc = npc_parse_item_basic(pattern, &info); if (rc) return rc; info.hw_mask = &hw_mask; pattern = npc_parse_skip_void_and_any_items(pattern); switch (pattern->type) { case ROC_NPC_ITEM_TYPE_VXLAN: lflags = NPC_F_UDP_VXLAN; info.len = pattern->size; lt = NPC_LT_LE_VXLAN; break; case ROC_NPC_ITEM_TYPE_GTPC: lflags = NPC_F_UDP_GTP_GTPC; info.len = pattern->size; lt = NPC_LT_LE_GTPC; break; case ROC_NPC_ITEM_TYPE_GTPU: lflags = NPC_F_UDP_GTP_GTPU_G_PDU; info.len = pattern->size; lt = NPC_LT_LE_GTPU; break; case ROC_NPC_ITEM_TYPE_GENEVE: lflags = NPC_F_UDP_GENEVE; info.len = pattern->size; lt = NPC_LT_LE_GENEVE; break; case ROC_NPC_ITEM_TYPE_VXLAN_GPE: lflags = NPC_F_UDP_VXLANGPE; info.len = pattern->size; lt = NPC_LT_LE_VXLANGPE; break; case ROC_NPC_ITEM_TYPE_ESP: lt = NPC_LT_LE_ESP; info.len = pst->pattern->size; break; default: return 0; } pst->tunnel = 1; npc_get_hw_supp_mask(pst, &info, lid, lt); rc = npc_parse_item_basic(pattern, &info); if (rc != 0) return rc; return npc_update_parse_state(pst, &info, lid, lt, lflags); } int npc_parse_lf(struct npc_parse_state *pst) { const struct roc_npc_item_info *pattern, *last_pattern; char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt, lflags; int nr_vlans = 0; int rc; /* We hit this layer if there is a tunneling protocol */ if (!pst->tunnel) return 0; if (pst->pattern->type != ROC_NPC_ITEM_TYPE_ETH) return 0; lid = NPC_LID_LF; lt = NPC_LT_LF_TU_ETHER; lflags = 0; /* No match support for vlan tags */ info.def_mask = NULL; info.hw_mask = NULL; info.len = pst->pattern->size; info.spec = NULL; info.mask = NULL; info.hw_hdr_len = 0; /* Look ahead and find out any VLAN tags. These can be * detected but no data matching is available. */ last_pattern = pst->pattern; pattern = pst->pattern + 1; pattern = npc_parse_skip_void_and_any_items(pattern); while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) { nr_vlans++; last_pattern = pattern; pattern++; pattern = npc_parse_skip_void_and_any_items(pattern); } switch (nr_vlans) { case 0: break; case 1: lflags = NPC_F_TU_ETHER_CTAG; break; case 2: lflags = NPC_F_TU_ETHER_STAG_CTAG; break; default: return NPC_ERR_PATTERN_NOTSUP; } info.hw_mask = &hw_mask; info.len = pst->pattern->size; info.hw_hdr_len = 0; npc_get_hw_supp_mask(pst, &info, lid, lt); info.spec = NULL; info.mask = NULL; rc = npc_parse_item_basic(pst->pattern, &info); if (rc != 0) return rc; pst->pattern = last_pattern; return npc_update_parse_state(pst, &info, lid, lt, lflags); } int npc_parse_lg(struct npc_parse_state *pst) { char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt; int rc; if (!pst->tunnel) return 0; info.def_mask = NULL; info.hw_mask = &hw_mask; info.spec = NULL; info.mask = NULL; info.hw_hdr_len = 0; lid = NPC_LID_LG; if (pst->pattern->type == ROC_NPC_ITEM_TYPE_IPV4) { lt = NPC_LT_LG_TU_IP; info.len = pst->pattern->size; } else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_IPV6) { lt = NPC_LT_LG_TU_IP6; info.len = pst->pattern->size; } else { /* There is no tunneled IP header */ return 0; } npc_get_hw_supp_mask(pst, &info, lid, lt); rc = npc_parse_item_basic(pst->pattern, &info); if (rc != 0) return rc; return npc_update_parse_state(pst, &info, lid, lt, 0); } int npc_parse_lh(struct npc_parse_state *pst) { char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; struct npc_parse_item_info info; int lid, lt; int rc; if (!pst->tunnel) return 0; info.def_mask = NULL; info.hw_mask = &hw_mask; info.spec = NULL; info.mask = NULL; info.hw_hdr_len = 0; lid = NPC_LID_LH; switch (pst->pattern->type) { case ROC_NPC_ITEM_TYPE_UDP: lt = NPC_LT_LH_TU_UDP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_TCP: lt = NPC_LT_LH_TU_TCP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_SCTP: lt = NPC_LT_LH_TU_SCTP; info.len = pst->pattern->size; break; case ROC_NPC_ITEM_TYPE_ESP: lt = NPC_LT_LH_TU_ESP; info.len = pst->pattern->size; break; default: return 0; } npc_get_hw_supp_mask(pst, &info, lid, lt); rc = npc_parse_item_basic(pst->pattern, &info); if (rc != 0) return rc; return npc_update_parse_state(pst, &info, lid, lt, 0); }