| /******************************************************************************* |
| Copyright (C) Marvell Interfdbional Ltd. and its affiliates |
| |
| This software file (the "File") is owned and distributed by Marvell |
| Interfdbional Ltd. and/or its affiliates ("Marvell") under the following |
| alterfdbive licensing terms. Once you have made an election to distribute the |
| File under one of the following license alterfdbives, please (i) delete this |
| introductory statement regarding license alterfdbives, (ii) delete the two |
| license alterfdbives that you have not elected to use and (iii) preserve the |
| Marvell copyright notice above. |
| |
| ******************************************************************************** |
| Marvell Commercial License Option |
| |
| If you received this File from Marvell and you have entered into a commercial |
| license agreement (a "Commercial License") with Marvell, the File is licensed |
| to you under the terms of the applicable Commercial License. |
| |
| ******************************************************************************** |
| Marvell GPL License Option |
| |
| If you received this File from Marvell, you may opt to use, redistribute and/or |
| modify this File in accordance with the terms and conditions of the General |
| Public License Version 2, June 1991 (the "GPL License"), a copy of which is |
| available along with the File in the license.txt file or by writing to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or |
| on the worldwide web at http://www.gnu.org/licenses/gpl.txt. |
| |
| THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY |
| DISCLAIMED. The GPL License provides additional details about this warranty |
| disclaimer. |
| ******************************************************************************** |
| Marvell BSD License Option |
| |
| If you received this File from Marvell, you may opt to use, redistribute and/or |
| modify this File under the following licensing terms. |
| Redistribution and use in source and binary forms, with or without modification, |
| are permitted provided that the following conditions are met: |
| |
| * Redistributions of source code must retain the above copyright notice, |
| this list of conditions and the following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| |
| * Neither the name of Marvell nor the names of its contributors may be |
| used to endorse or promote products derived from this software without |
| specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| * mvNfpCt.c - Marvell Fast Network Processing |
| * |
| * DESCRIPTION: |
| * |
| * Supported Features: |
| * - OS independent. |
| * |
| *******************************************************************************/ |
| |
| /* includes */ |
| #include "mvOs.h" |
| #include "mvDebug.h" |
| #include "mvList.h" |
| #include "gbe/mvNeta.h" |
| #include "mvNfpDefs.h" |
| #include "mvNfp.h" |
| |
| MV_LIST_ELEMENT *ct_inv_list = NULL; |
| NFP_RULE_CT **ct_hash = NULL; |
| int ct_iterator_row = 0; |
| #ifdef NFP_LIMIT |
| MV_LIST_ELEMENT *tbfs; |
| #endif /* NFP_LIMIT */ |
| |
| MV_STATUS _INIT mvNfpCtInit(MV_VOID) |
| { |
| MV_U32 bytes = sizeof(NFP_RULE_CT *) * NFP_CT_HASH_SIZE; |
| |
| ct_hash = (NFP_RULE_CT **)mvOsMalloc(bytes); |
| if (ct_hash == NULL) { |
| mvOsPrintf("NFP (ct): not enough memory for CT database\n"); |
| return MV_NO_RESOURCE; |
| } |
| |
| mvOsMemset(ct_hash, 0, bytes); |
| ct_inv_list = mvListCreate(); |
| if (ct_inv_list == NULL) { |
| mvOsPrintf("NFP (ct): not enough memory for CT database\n"); |
| mvOsFree(ct_hash); |
| return MV_NO_RESOURCE; |
| } |
| |
| mvOsPrintf("NFP (ct) init %d entries, %d bytes\n", NFP_CT_HASH_SIZE, bytes); |
| #ifdef NFP_LIMIT |
| tbfs = mvListCreate(); |
| if (tbfs == NULL) { |
| mvOsPrintf("NFP (ct): not enough memory for TBF database\n"); |
| mvListDestroy(ct_inv_list); |
| mvOsFree(ct_hash); |
| return MV_NO_RESOURCE; |
| } |
| #endif /* NFP_LIMIT */ |
| return MV_OK; |
| } |
| |
| static INLINE MV_U32 mvNfpCtHash(NFP_RULE_CT *ct) |
| { |
| MV_U32 hash = mv_jhash_2addr(ct->family, (const MV_U8 *)&ct->srcL3, (const MV_U8 *)&ct->dstL3, |
| (ct->ports | ct->proto), nfp_jhash_iv); |
| hash &= NFP_CT_HASH_MASK; |
| return hash; |
| } |
| |
| static INLINE NFP_RULE_CT *mvNfpCtLookup(NFP_RULE_CT *ct2) |
| { |
| MV_U32 hash; |
| NFP_RULE_CT *ct; |
| |
| if (!ct_hash) |
| return NULL; |
| |
| hash = mvNfpCtHash(ct2); |
| ct = ct_hash[hash]; |
| while (ct) { |
| if ((ct->family == ct2->family) && |
| l3_addr_eq(ct->family, ct->srcL3, ct2->srcL3) && |
| l3_addr_eq(ct->family, ct->dstL3, ct2->dstL3) && |
| (ct->ports == ct2->ports) && (ct->proto == ct2->proto)) |
| return ct; |
| ct = ct->next; |
| } |
| |
| return NULL; |
| } |
| |
| /* Move CT rule from ct_inv_list to ct_hash */ |
| static MV_STATUS mvNfpCtRuleValid(NFP_RULE_CT *ct, MV_LIST_ELEMENT *curr) |
| { |
| MV_U32 hash; |
| |
| if (((ct->flags & NFP_F_CT_DROP) || !(ct->flags & NFP_F_CT_FIB_INV)) && !(ct->flags & NFP_F_CT_NOT_EXIST)) { |
| /* CT rule became ready */ |
| |
| /* Delete CT rule from incomplete list */ |
| mvListDel(curr); |
| |
| /* Add ct to hash table */ |
| hash = mvNfpCtHash(ct); |
| ct->next = ct_hash[hash]; |
| ct_hash[hash] = ct; |
| ct->visited = 0; |
| |
| return MV_OK; |
| } |
| return MV_BAD_PARAM; |
| } |
| |
| MV_VOID mvNfpCtRuleFibUpdate(NFP_RULE_FIB *fib) |
| { |
| NFP_RULE_CT *ct; |
| MV_LIST_ELEMENT *curr, *tmp; |
| int i; |
| |
| /* Update FIB rule pointers in CT hash */ |
| for (i = 0; i < NFP_CT_HASH_SIZE; i++) { |
| ct = ct_hash[i]; |
| while (ct) { |
| if ((fib->family == ct->family) && |
| (l3_addr_eq(fib->family, fib->srcL3, ct->srcL3)) && |
| #ifdef NFP_NAT |
| ((!(ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) || |
| ((ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, (MV_U8 *)&(ct->new_dip)))))) { |
| #else |
| (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) { |
| #endif /* NFP_NAT */ |
| /* Invalidate CT route */ |
| if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_ref--; |
| if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_hwf_ref--; |
| ct->flags &= ~NFP_F_CT_FIB_INV; |
| ct->fib = fib; |
| (ct->fib)->ct_ref++; |
| if (ct->flags & NFP_F_CT_HWF) |
| (ct->fib)->ct_hwf_ref++; |
| } |
| ct = ct->next; |
| } |
| } |
| |
| /* Update FIB rule pointers in CT invalid list */ |
| if (!ct_inv_list) |
| return; |
| |
| curr = ct_inv_list->next; |
| while (curr) { |
| ct = (NFP_RULE_CT *)curr->data; |
| tmp = curr->next; |
| if ((fib->family == ct->family) && |
| (l3_addr_eq(fib->family, fib->srcL3, ct->srcL3)) && |
| #ifdef NFP_NAT |
| ((!(ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) || |
| ((ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, (MV_U8 *)&(ct->new_dip)))))) { |
| #else |
| (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) { |
| #endif /* NFP_NAT */ |
| /* Found incomplete CT entry with a matching FIB rule */ |
| if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_ref--; |
| if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_hwf_ref--; |
| ct->fib = fib; |
| (ct->fib)->ct_ref++; |
| if (ct->flags & NFP_F_CT_HWF) |
| (ct->fib)->ct_hwf_ref++; |
| ct->flags &= ~NFP_F_CT_FIB_INV; |
| mvNfpCtRuleValid(ct, curr); |
| } |
| curr = tmp; |
| } |
| } |
| |
| MV_VOID mvNfpCtRuleFibInvalidate(NFP_RULE_FIB *fib) |
| { |
| int i; |
| NFP_RULE_CT *ct, *ct_prev; |
| MV_LIST_ELEMENT *curr; |
| |
| for (i = 0; i < NFP_CT_HASH_SIZE; i++) { |
| ct = ct_hash[i]; |
| ct_prev = NULL; |
| while (ct) { |
| if ((fib->family == ct->family) && |
| (l3_addr_eq(fib->family, fib->srcL3, ct->srcL3)) && |
| #ifdef NFP_NAT |
| ((!(ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) || |
| ((ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, (MV_U8 *)&(ct->new_dip)))))) { |
| #else |
| (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) { |
| #endif /* NFP_NAT */ |
| /* Invalidate CT route */ |
| if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_ref--; |
| if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_hwf_ref--; |
| ct->flags |= NFP_F_CT_FIB_INV; |
| ct->fib = NULL; |
| |
| if (!(ct->flags & NFP_F_CT_DROP)) { |
| /* Remove CT rule from hash table only if it is not a DROP rule */ |
| if (ct_prev) |
| ct_prev->next = ct->next; |
| else |
| ct_hash[i] = ct->next; |
| |
| /* Add CT rule to incomplete list */ |
| mvListAddHead(ct_inv_list, (MV_ULONG)ct); |
| } |
| } |
| ct_prev = ct; |
| ct = ct->next; |
| } |
| } |
| |
| /* Invalidate FIB rule pointers in CT invalid list (possible in classification context rules) */ |
| if (!ct_inv_list) |
| return; |
| |
| curr = ct_inv_list->next; |
| while (curr) { |
| ct = (NFP_RULE_CT *)curr->data; |
| if ((fib->family == ct->family) && |
| (l3_addr_eq(fib->family, fib->srcL3, ct->srcL3)) && |
| #ifdef NFP_NAT |
| ((!(ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) || |
| ((ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, (MV_U8 *)&(ct->new_dip)))))) { |
| #else |
| (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) { |
| #endif /* NFP_NAT */ |
| if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_ref--; |
| if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_hwf_ref--; |
| ct->flags |= NFP_F_CT_FIB_INV; |
| ct->fib = NULL; |
| } |
| curr = curr->next; |
| } |
| } |
| |
| |
| static MV_VOID mvNfpCtRulePrint(NFP_RULE_CT *ct) |
| { |
| if (ct->family == MV_INET) |
| mvOsPrintf("IPv4: "MV_IPQUAD_FMT":%d->"MV_IPQUAD_FMT":%d", |
| MV_IPQUAD(ct->srcL3), MV_16BIT_BE(ct->ports & 0xFFFF), |
| MV_IPQUAD(ct->dstL3), MV_16BIT_BE(ct->ports >> 16)); |
| else /* MV_INET6 */ |
| mvOsPrintf(" IPv6: "MV_IP6_FMT":%d->"MV_IP6_FMT":%d", |
| MV_IP6_ARG(ct->srcL3), MV_16BIT_BE(ct->ports & 0xFFFF), |
| MV_IP6_ARG(ct->dstL3), MV_16BIT_BE(ct->ports >> 16)); |
| |
| if (ct->proto == MV_IP_PROTO_TCP) |
| mvOsPrintf(" TCP - "); |
| else if (ct->proto == MV_IP_PROTO_UDP) |
| mvOsPrintf(" UDP - "); |
| else |
| mvOsPrintf(" %-2d - ", ct->proto); |
| |
| if (ct->flags & NFP_F_CT_DROP) |
| mvOsPrintf("Drop, "); |
| else if (ct->flags & NFP_F_CT_NOT_EXIST) |
| mvOsPrintf("Not Exist, "); |
| else |
| mvOsPrintf("Forward, "); |
| |
| mvOsPrintf("flags=0x%04x, age=%d, hit=%d\n", ct->flags, ct->age, ct->hit_cntr); |
| |
| #ifdef NFP_NAT |
| if (ct->flags & NFP_F_CT_DNAT) |
| mvOsPrintf("DNAT: "MV_IPQUAD_FMT":%d, ", MV_IPQUAD(((MV_U8 *)&ct->new_dip)), MV_16BIT_BE(ct->new_dport)); |
| |
| if (ct->flags & NFP_F_CT_SNAT) |
| mvOsPrintf("SNAT: "MV_IPQUAD_FMT":%d, ", MV_IPQUAD(((MV_U8 *)&ct->new_sip)), MV_16BIT_BE(ct->new_sport)); |
| #endif /* NFP_NAT */ |
| |
| #ifdef NFP_CLASSIFY |
| if (ct->flags & NFP_F_CT_SET_MH) |
| mvOsPrintf("MH=0x%X ", ct->mh); |
| |
| if (ct->flags & NFP_F_CT_SET_TXP) |
| mvOsPrintf("txp=0x%X ", ct->txp); |
| |
| if (ct->flags & NFP_F_CT_SET_TXQ) { |
| int i; |
| mvOsPrintf(" TXQ Map: DSCP ----> TXQ\n"); |
| for (i = 0; i <= NFP_DSCP_MAP_GLOBAL; i++) { |
| if (ct->txq_map[i].valid) { |
| if (i == NFP_DSCP_MAP_GLOBAL) |
| mvOsPrintf(" Global %2d\n", ct->txq_map[i].txq); |
| else |
| mvOsPrintf(" %6d %2d\n", i, ct->txq_map[i].txq); |
| } |
| } |
| } |
| |
| if (ct->flags & NFP_F_CT_SET_DSCP) { |
| int i; |
| mvOsPrintf(" DSCP Map: Old ----> New\n"); |
| for (i = 0; i <= NFP_DSCP_MAP_GLOBAL; i++) { |
| if (ct->dscp_map[i].valid) { |
| if (i == NFP_DSCP_MAP_GLOBAL) |
| mvOsPrintf(" Global %2d\n", ct->dscp_map[i].new_dscp); |
| else |
| mvOsPrintf(" %6d %2d\n", i, ct->dscp_map[i].new_dscp); |
| } |
| } |
| } |
| |
| if (ct->flags & NFP_F_CT_SET_VLAN_PRIO) { |
| int i; |
| mvOsPrintf(" VPri Map: Old New\n"); |
| for (i = 0; i <= NFP_VPRI_MAP_GLOBAL; i++) { |
| if (ct->vpri_map[i].valid) { |
| if (i == NFP_VPRI_MAP_GLOBAL) |
| mvOsPrintf(" Global %2d\n", ct->vpri_map[i].new_prio); |
| else |
| mvOsPrintf(" %6d %2d\n", i, ct->vpri_map[i].new_prio); |
| } |
| } |
| } |
| #endif /* NFP_CLASSIFY */ |
| } |
| |
| static NFP_RULE_CT *mvNfpCtRuleInvalidLookup(NFP_RULE_CT *ct2) |
| { |
| MV_LIST_ELEMENT *curr; |
| NFP_RULE_CT *ct; |
| |
| if (!ct_inv_list) |
| return NULL; |
| |
| curr = ct_inv_list->next; |
| while (curr) { |
| ct = (NFP_RULE_CT *)curr->data; |
| if ((ct->family == ct2->family) && |
| l3_addr_eq(ct->family, ct->srcL3, ct2->srcL3) && |
| l3_addr_eq(ct->family, ct->dstL3, ct2->dstL3) && |
| (ct->ports == ct2->ports) && |
| (ct->proto == ct2->proto)) |
| return ct; |
| |
| curr = curr->next; |
| } |
| return NULL; |
| } |
| |
| MV_STATUS mvNfpCtRuleAge(NFP_RULE_CT *ct2) |
| { |
| NFP_RULE_CT *ct; |
| |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| if (ct) { |
| ct2->age = (ct->flags & NFP_F_CT_HWF) ? 1 : ct->age; |
| ct->age = (ct->flags & NFP_F_CT_HWF) ? ct->age : 0; |
| return MV_OK; |
| } |
| |
| ct = mvNfpCtLookup(ct2); |
| if (ct) { |
| ct2->age = (ct->flags & NFP_F_CT_HWF) ? 1 : ct->age; |
| ct->age = (ct->flags & NFP_F_CT_HWF) ? ct->age : 0; |
| return MV_OK; |
| } |
| |
| ct2->age = 0; |
| return MV_NOT_FOUND; |
| } |
| |
| /* create rule from classification context * |
| * - search and update if relevant FIB rule exist * |
| * - set NFP_F_CT_NOT_EXIST flag * |
| * - add rule to invalid rule list (NFP_F_CT_NOT_EXIST is set) * |
| * - if mvNfpCtFilterModeSet is called later, then clear NFP_F_CT_NOT_EXIST flag * |
| * Return: pointer to created rule */ |
| NFP_RULE_CT *mvNfpCtClassifyRuleCreate(NFP_RULE_CT *ct2) |
| { |
| /* rule not exist - create rule and mark it as "not exist" */ |
| NFP_RULE_CT *ct = (NFP_RULE_CT *)mvOsMalloc(sizeof(NFP_RULE_CT)); |
| if (!ct) { |
| mvOsPrintf("%s: OOM\n", __func__); |
| return NULL; |
| } |
| |
| mvOsMemcpy(ct, ct2, sizeof(NFP_RULE_CT)); |
| |
| ct->flags |= NFP_F_CT_NOT_EXIST; |
| ct->flags |= NFP_F_CT_FIB_INV; |
| /* this rule is invalid until mvNfpCtFilterModeSet will be called */ |
| mvListAddHead(ct_inv_list, (MV_ULONG)ct); |
| return ct; |
| } |
| |
| #ifdef NFP_NAT |
| MV_STATUS mvNfpCtNatRuleAdd(NFP_RULE_CT *nat2) |
| { |
| NFP_RULE_CT *nat; |
| MV_U32 hash; |
| |
| /* Update rule if it exists as a valid rule or as an invalid rule */ |
| nat = mvNfpCtLookup(nat2); |
| if (!nat) |
| nat = mvNfpCtRuleInvalidLookup(nat2); |
| |
| if (nat) { |
| if (nat->flags & NFP_F_CT_NOT_EXIST) |
| nat->flags &= ~NFP_F_CT_NOT_EXIST; |
| if (nat2->flags & NFP_F_CT_SNAT) { |
| nat->new_sip = nat2->new_sip; |
| nat->new_sport = nat2->new_sport; |
| nat->flags |= NFP_F_CT_SNAT; |
| } |
| if (nat2->flags & NFP_F_CT_DNAT) { |
| nat->new_dip = nat2->new_dip; |
| nat->new_dport = nat2->new_dport; |
| nat->flags |= NFP_F_CT_DNAT; |
| } |
| /* copy other information to nat2 - classification, tbf, etc.. */ |
| mvOsMemcpy(nat2, nat, sizeof(NFP_RULE_CT)); |
| |
| /* delete previous rule, and add it again later (will search for fib again) */ |
| /* delete is important because it updates fib->ct_ref, rate limit, etc.. */ |
| mvNfpCtRuleDel(nat); |
| } |
| |
| nat = (NFP_RULE_CT *)mvOsMalloc(sizeof(NFP_RULE_CT)); |
| if (!nat) { |
| mvOsPrintf("%s: OOM\n", __func__); |
| return MV_FAIL; |
| } |
| mvOsMemcpy(nat, nat2, sizeof(NFP_RULE_CT)); |
| |
| if (nat->flags & NFP_F_CT_DNAT) |
| nat->fib = mvNfpFibLookup(nat2->family, nat2->srcL3, (const MV_U8 *)&(nat2->new_dip)); |
| else |
| nat->fib = mvNfpFibLookup(nat2->family, nat2->srcL3, nat2->dstL3); |
| |
| if (nat->fib) { |
| (nat->fib)->ct_ref++; /* update FIB reference count */ |
| hash = mvNfpCtHash(nat2); |
| nat->next = ct_hash[hash]; |
| ct_hash[hash] = nat; |
| nat->visited = 0; |
| } else { |
| mvListAddHead(ct_inv_list, (MV_ULONG)nat); |
| } |
| |
| NFP_DBG("NFP (nat) add %p\n", nat); |
| |
| return MV_OK; |
| } |
| #endif /* NFP_NAT */ |
| |
| MV_STATUS mvNfpCtFilterModeSet(NFP_RULE_CT *ct2) |
| { |
| NFP_RULE_CT *ct, *new_ct; |
| MV_LIST_ELEMENT *element; |
| MV_U32 hash; |
| |
| /* The rule can be in one of several initial states, each requires different handling */ |
| /* The rule can exists as valid, exist as invalid or not exist at this point */ |
| |
| ct = mvNfpCtLookup(ct2); |
| if (ct) { |
| /* Rule exists as valid */ |
| /* Either it has valid FIB information, or it is a DROP rule */ |
| |
| if (ct2->flags & NFP_F_CT_DROP) { |
| /* Updated rule says DROP, so we don't care if FIB information exists or not */ |
| ct->flags |= NFP_F_CT_DROP; |
| } else { |
| /* Updated rule says FORWARD */ |
| ct->flags &= ~NFP_F_CT_DROP; |
| if (ct->flags & NFP_F_CT_FIB_INV) { |
| /* need to move this rule to the invalid list */ |
| new_ct = (NFP_RULE_CT *)mvOsMalloc(sizeof(NFP_RULE_CT)); |
| if (!new_ct) { |
| mvOsPrintf("%s: OOM\n", __func__); |
| return MV_FAIL; |
| } |
| mvOsMemcpy(new_ct, ct, sizeof(NFP_RULE_CT)); |
| #ifdef NFP_LIMIT |
| if (new_ct->tbfInfo) |
| new_ct->tbfInfo->refCnt++; |
| #endif /* NFP_LIMIT */ |
| mvNfpCtRuleDel(ct); |
| mvListAddHead(ct_inv_list, (MV_ULONG)new_ct); |
| } |
| } |
| return MV_OK; |
| } |
| |
| |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| /* Rule exists as invalid */ |
| if (ct) { |
| /* this rule was created by classification API * |
| * mark it as "real" rule * |
| * search for relevant fib rule */ |
| if (ct->flags & NFP_F_CT_NOT_EXIST) { |
| ct->flags &= ~NFP_F_CT_NOT_EXIST; |
| ct->flags |= NFP_F_CT_FIB_INV; |
| ct->fib = mvNfpFibLookup(ct2->family, ct2->srcL3, ct2->dstL3); |
| if (ct->fib) { |
| (ct->fib)->ct_ref++; |
| ct->flags &= ~NFP_F_CT_FIB_INV; |
| } |
| } |
| if (ct2->flags & NFP_F_CT_DROP || !(ct->flags & NFP_F_CT_FIB_INV)) { |
| /* need to move this rule to the valid rules database */ |
| if (ct2->flags & NFP_F_CT_DROP) |
| ct->flags |= NFP_F_CT_DROP; |
| element = mvListFind(ct_inv_list, (MV_ULONG)ct); |
| if (!element) |
| return MV_FAIL; |
| return mvNfpCtRuleValid(ct, element); |
| } else { |
| /* no need to do anything special - clearing DROP flag just for clarity, it is already cleared */ |
| ct->flags &= ~NFP_F_CT_DROP; |
| return MV_OK; |
| } |
| } |
| |
| |
| /* Rule doesn't exist, need to create a new one */ |
| ct = (NFP_RULE_CT *)mvOsMalloc(sizeof(NFP_RULE_CT)); |
| if (!ct) { |
| mvOsPrintf("%s: OOM\n", __func__); |
| return MV_FAIL; |
| } |
| |
| mvOsMemcpy(ct, ct2, sizeof(NFP_RULE_CT)); |
| |
| ct->flags |= NFP_F_CT_FIB_INV; |
| ct->fib = mvNfpFibLookup(ct2->family, ct2->srcL3, ct2->dstL3); |
| if (ct->fib) { |
| (ct->fib)->ct_ref++; |
| ct->flags &= ~NFP_F_CT_FIB_INV; |
| } |
| |
| if ((ct->flags & NFP_F_CT_DROP) || !(ct->flags & NFP_F_CT_FIB_INV)) { |
| hash = mvNfpCtHash(ct2); |
| ct->next = ct_hash[hash]; |
| ct_hash[hash] = ct; |
| ct->visited = 0; |
| } else { |
| mvListAddHead(ct_inv_list, (MV_ULONG)ct); |
| } |
| |
| NFP_DBG("NFP (ct filter mode) set %p\n", ct); |
| |
| return MV_OK; |
| } |
| |
| #ifdef NFP_LIMIT |
| void mvNfpCtTbfsDump(void) |
| { |
| MV_LIST_ELEMENT *curr; |
| NFP_TBF_INFO *tbf_data; |
| mvOsPrintf("Tbfs list:\n"); |
| if (!tbfs) |
| return; |
| curr = tbfs->next; |
| while (curr) { |
| tbf_data = (NFP_TBF_INFO *)curr->data; |
| mvOsPrintf("index: %d, limit: %d, burst: %d, refCnt: %d\n", tbf_data->index, |
| tbf_data->creditPerTick * mvOsGetTicksFreq(), tbf_data->creditMax, tbf_data->refCnt); |
| curr = curr->next; |
| } |
| mvOsPrintf("\n"); |
| } |
| |
| /* limit units = KBytes/sec, burst_limit units = Kbytes */ |
| int mvNfpTbfCreate(int limit, int burst_limit) |
| { |
| NFP_TBF_INFO *tbf; |
| |
| if (limit < 0 || burst_limit < 0 || !tbfs) |
| return -1; |
| |
| tbf = mvOsMalloc(sizeof(NFP_TBF_INFO)); |
| if (!tbf) { |
| mvOsPrintf("%s: OOM\n", __func__); |
| return -1; |
| } |
| |
| tbf->refCnt = 0; |
| tbf->credit = tbf->creditMax = (burst_limit * 1000); /* Bytes */ |
| /* convert from KBytes/sec to Bytes/tick */ |
| tbf->creditPerTick = (limit * 1000) / mvOsGetTicksFreq(); /* Bytes per tick */ |
| if (tbf->creditPerTick == 0) |
| tbf->maxElapsedTicks = 0; |
| else |
| tbf->maxElapsedTicks = tbf->creditMax / tbf->creditPerTick; /* ticks */ |
| tbf->lastUpdate = mvOsGetTicks(); /* ticks */ |
| |
| /* calculate index */ |
| if (tbfs->next) |
| tbf->index = ((NFP_TBF_INFO *)(tbfs->next->data))->index + 1; |
| else |
| tbf->index = 0; |
| |
| if (!mvListAddHead(tbfs, (MV_U32)tbf)) { |
| mvOsPrintf("%s: OOM\n", __func__); |
| mvOsFree(tbf); |
| return -1; |
| } |
| return tbf->index; |
| } |
| |
| NFP_TBF_INFO *mvNfpTbfGet(int tbf) |
| { |
| MV_LIST_ELEMENT *curr; |
| NFP_TBF_INFO *tbf_data; |
| if (tbf < 0 || !tbfs) |
| return NULL; |
| curr = tbfs->next; |
| while (curr) { |
| tbf_data = (NFP_TBF_INFO *)curr->data; |
| if (tbf_data->index == tbf) |
| return tbf_data; |
| curr = curr->next; |
| } |
| return NULL; |
| } |
| |
| MV_STATUS mvNfpTbfDel(int tbf) |
| { |
| MV_LIST_ELEMENT *element; |
| NFP_TBF_INFO *tbf_data = mvNfpTbfGet(tbf); |
| if (!tbf_data) { |
| mvOsPrintf("%s: Invalid Token Bucket Filter index (%d)\n", __func__, tbf); |
| return MV_BAD_PARAM; |
| } |
| /* delete tbf only if there are no rules attached */ |
| if (!tbf_data->refCnt) { |
| element = mvListFind(tbfs, (MV_ULONG)tbf_data); |
| mvListDel(element); |
| mvOsFree(tbf_data); |
| } |
| return MV_OK; |
| } |
| |
| MV_STATUS mvNfpCtRateLimitSet(NFP_RULE_CT *ct2, int tbf_index) |
| { |
| NFP_RULE_CT *ct; |
| NFP_TBF_INFO *tbf = mvNfpTbfGet(tbf_index); |
| |
| if (!ct2) |
| return MV_BAD_PARAM; |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| |
| if (ct) { |
| if (tbf) { |
| if (ct->tbfInfo) |
| (ct->tbfInfo)->refCnt--; |
| tbf->refCnt++; |
| ct->tbfInfo = tbf; |
| ct->flags |= NFP_F_CT_LIMIT; |
| } else { |
| mvOsPrintf("%s: Invalid Token Bucket Filter index (%d)\n", __func__, tbf_index); |
| return MV_BAD_PARAM; |
| } |
| } else { |
| mvOsPrintf("%s Error: Could not find existing 5 tuple rule\n", __func__); |
| return MV_NOT_FOUND; |
| } |
| |
| NFP_DBG("NFP (ct rate limit) set %p\n", ct); |
| |
| return MV_OK; |
| } |
| |
| MV_STATUS mvNfpCtRateLimitDel(NFP_RULE_CT *ct2) |
| { |
| NFP_RULE_CT *ct; |
| |
| if (!ct2) |
| return MV_BAD_PARAM; |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| |
| if (ct) { |
| if (ct->tbfInfo) |
| (ct->tbfInfo)->refCnt--; |
| |
| ct->tbfInfo = NULL; |
| ct->flags &= ~NFP_F_CT_LIMIT; |
| return MV_OK; |
| } |
| |
| return MV_NOT_FOUND; |
| } |
| |
| MV_STATUS mvNfpTbfProcess(NFP_TBF_INFO *tbf, MV_U32 packetSize) |
| { |
| MV_U32 ticks = mvOsGetTicks(); |
| MV_U32 elapsed; |
| |
| if (!tbf) |
| return MV_CONTINUE; |
| /* Update credit */ |
| elapsed = ticks - tbf->lastUpdate; |
| tbf->lastUpdate = ticks; |
| /* safe check if elapsed time is higher than "time that gives maximum credit" */ |
| if (elapsed > tbf->maxElapsedTicks) { |
| tbf->credit = tbf->creditMax; |
| } else { |
| tbf->credit += elapsed * tbf->creditPerTick; |
| if (tbf->credit > tbf->creditMax) |
| tbf->credit = tbf->creditMax; |
| } |
| |
| /* Check result */ |
| if (packetSize > tbf->credit) |
| return MV_DROPPED; |
| tbf->credit -= packetSize; |
| return MV_CONTINUE; |
| } |
| #endif /* NFP_LIMIT */ |
| |
| #ifdef NFP_CLASSIFY |
| /* Add DSCP mapping for an existing 5 tuple rule */ |
| MV_STATUS mvNfpCtDscpRuleAdd(NFP_RULE_CT *ct2, int dscp, int new_dscp) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* sanity: chack new_dscp parameter */ |
| if ((new_dscp < NFP_DSCP_MIN) || (new_dscp > NFP_DSCP_MAX)) { |
| mvOsPrintf("%s Error: new_dscp value (%d) is out of range\n", __func__, new_dscp); |
| return MV_BAD_PARAM; |
| } |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| /* rule is not exist - create rule from classification context */ |
| if (!ct) { |
| ct = mvNfpCtClassifyRuleCreate(ct2); |
| if (!ct) |
| return MV_FAIL; |
| } |
| |
| /* Update rule DSCP map table */ |
| if (ct2->flags & NFP_F_CT_SET_DSCP) { |
| if (dscp == MV_ETH_NFP_GLOBAL_MAP) { |
| ct->dscp_map[NFP_DSCP_MAP_GLOBAL].new_dscp = new_dscp; |
| ct->dscp_map[NFP_DSCP_MAP_GLOBAL].valid = MV_TRUE; |
| } else if ((dscp >= NFP_DSCP_MIN) && (dscp <= NFP_DSCP_MAX)) { |
| ct->dscp_map[dscp].new_dscp = new_dscp; |
| ct->dscp_map[dscp].valid = MV_TRUE; |
| } else { |
| mvOsPrintf("%s Error: dscp value (%d) is out of range\n", __func__, dscp); |
| return MV_BAD_PARAM; |
| } |
| ct->flags |= NFP_F_CT_SET_DSCP; |
| } else { |
| mvOsPrintf("%s Error: NFP_F_CT_SET_DSCP flag is not set\n", __func__); |
| return MV_BAD_PARAM; |
| } |
| |
| NFP_DBG("NFP (ct dscp) set %p\n", ct); |
| |
| return MV_OK; |
| } |
| |
| static INLINE MV_STATUS mvNfpIsDscpSet(NFP_RULE_CT *ct) |
| { |
| int i; |
| |
| for (i = 0; i <= NFP_DSCP_MAP_GLOBAL; i++) |
| if (ct->dscp_map[i].valid) |
| return MV_TRUE; |
| |
| return MV_FALSE; |
| } |
| |
| MV_STATUS mvNfpCtDscpRuleDel(NFP_RULE_CT *ct2, int dscp) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| |
| if (ct) { |
| if (dscp == MV_ETH_NFP_GLOBAL_MAP) { |
| ct->dscp_map[NFP_DSCP_MAP_GLOBAL].new_dscp = 0; |
| ct->dscp_map[NFP_DSCP_MAP_GLOBAL].valid = MV_FALSE; |
| } else if ((dscp >= NFP_DSCP_MIN) && (dscp <= NFP_DSCP_MAX)) { |
| ct->dscp_map[dscp].new_dscp = 0; |
| ct->dscp_map[dscp].valid = MV_FALSE; |
| } else { |
| mvOsPrintf("%s Error: dscp value (%d) is out of range\n", __func__, dscp); |
| return MV_BAD_PARAM; |
| } |
| |
| if (!mvNfpIsDscpSet(ct)) |
| ct->flags &= ~NFP_F_CT_SET_DSCP; |
| |
| return MV_OK; |
| } |
| |
| return MV_NOT_FOUND; |
| } |
| |
| MV_STATUS mvNfpCtVlanPrioRuleAdd(NFP_RULE_CT *ct2, int prio, int new_prio) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* sanity: chack new_prio parameter */ |
| if ((new_prio < NFP_VPRI_MIN) || (new_prio > NFP_VPRI_MAX)) { |
| mvOsPrintf("%s Error: new_prio value (%d) is out of range\n", __func__, new_prio); |
| return MV_BAD_PARAM; |
| } |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| /* rule is not exist - create rule from classification context */ |
| if (!ct) { |
| ct = mvNfpCtClassifyRuleCreate(ct2); |
| if (!ct) |
| return MV_FAIL; |
| } |
| |
| /* Update rule VLAN Priority map table */ |
| if (ct2->flags & NFP_F_CT_SET_VLAN_PRIO) { |
| if (prio == MV_ETH_NFP_GLOBAL_MAP) { |
| ct->vpri_map[NFP_VPRI_MAP_GLOBAL].new_prio = new_prio; |
| ct->vpri_map[NFP_VPRI_MAP_GLOBAL].valid = MV_TRUE; |
| } else if ((prio >= NFP_VPRI_MIN) && (prio <= NFP_VPRI_MAX)) { |
| ct->vpri_map[prio].new_prio = new_prio; |
| ct->vpri_map[prio].valid = MV_TRUE; |
| } else { |
| mvOsPrintf("%s Error: prio value (%d) is out of range\n", __func__, prio); |
| return MV_BAD_PARAM; |
| } |
| ct->flags |= NFP_F_CT_SET_VLAN_PRIO; |
| } else { |
| mvOsPrintf("%s: NFP_F_CT_SET_VLAN_PRIO flag is not set\n", __func__); |
| return MV_BAD_PARAM; |
| } |
| |
| NFP_DBG("NFP (ct vpri) set %p\n", ct); |
| |
| return MV_OK; |
| } |
| |
| static INLINE MV_STATUS mvNfpIsVpriSet(NFP_RULE_CT *ct) |
| { |
| int i; |
| |
| for (i = 0; i <= NFP_VPRI_MAP_GLOBAL; i++) |
| if (ct->vpri_map[i].valid) |
| return MV_TRUE; |
| |
| return MV_FALSE; |
| } |
| |
| MV_STATUS mvNfpCtVlanPrioRuleDel(NFP_RULE_CT *ct2, int prio) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* sanity: chack prio parameter */ |
| if ((prio < MV_ETH_NFP_GLOBAL_MAP) || (prio > NFP_VPRI_MAP_GLOBAL)) { |
| mvOsPrintf("%s Error: prio value (%d) is out of range\n", __func__, prio); |
| return MV_BAD_PARAM; |
| } |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| |
| if (ct) { |
| if (prio == MV_ETH_NFP_GLOBAL_MAP) { |
| ct->vpri_map[NFP_VPRI_MAP_GLOBAL].new_prio = 0; |
| ct->vpri_map[NFP_VPRI_MAP_GLOBAL].valid = MV_FALSE; |
| } else { |
| ct->vpri_map[prio].new_prio = 0; |
| ct->vpri_map[prio].valid = MV_FALSE; |
| } |
| if (!mvNfpIsVpriSet(ct)) |
| ct->flags &= ~NFP_F_CT_SET_VLAN_PRIO; |
| |
| return MV_OK; |
| } |
| |
| return MV_NOT_FOUND; |
| } |
| |
| MV_STATUS mvNfpCtTxqRuleAdd(NFP_RULE_CT *ct2, int dscp, int txq) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* sanity: chack txq parameter */ |
| if ((txq < 0) || (txq >= CONFIG_MV_ETH_TXQ)) { |
| mvOsPrintf("%s Error: txq (%d) is out of range\n", __func__, txq); |
| return MV_BAD_PARAM; |
| } |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| /* rule is not exist - create rule from classification context */ |
| if (!ct) { |
| ct = mvNfpCtClassifyRuleCreate(ct2); |
| if (!ct) |
| return MV_FAIL; |
| } |
| |
| /* Update rule DSCP to TXQ map table */ |
| if (ct2->flags & NFP_F_CT_SET_TXQ) { |
| if (dscp == MV_ETH_NFP_GLOBAL_MAP) { |
| ct->txq_map[NFP_DSCP_MAP_GLOBAL].txq = txq; |
| ct->txq_map[NFP_DSCP_MAP_GLOBAL].valid = MV_TRUE; |
| } else if ((dscp >= NFP_DSCP_MIN) && (dscp <= NFP_DSCP_MAX)) { |
| ct->txq_map[dscp].txq = txq; |
| ct->txq_map[dscp].valid = MV_TRUE; |
| } else { |
| mvOsPrintf("%s Error: dscp value (%d) is out of range\n", __func__, dscp); |
| return MV_BAD_PARAM; |
| } |
| ct->flags |= NFP_F_CT_SET_TXQ; |
| } else { |
| mvOsPrintf("%s Error: NFP_F_CT_SET_TXQ flag is not set\n", __func__); |
| return MV_BAD_PARAM; |
| } |
| |
| NFP_DBG("NFP (ct txq) set %p\n", ct); |
| |
| return MV_OK; |
| } |
| |
| static INLINE MV_STATUS mvNfpIsTxqSet(NFP_RULE_CT *ct) |
| { |
| int i; |
| |
| for (i = 0; i <= NFP_DSCP_MAP_GLOBAL; i++) |
| if (ct->txq_map[i].valid) |
| return MV_TRUE; |
| |
| return MV_FALSE; |
| } |
| |
| MV_STATUS mvNfpCtTxqRuleDel(NFP_RULE_CT *ct2, int dscp) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| |
| if (ct) { |
| if (dscp == MV_ETH_NFP_GLOBAL_MAP) { |
| ct->txq_map[NFP_DSCP_MAP_GLOBAL].txq = 0; |
| ct->txq_map[NFP_DSCP_MAP_GLOBAL].valid = MV_FALSE; |
| } else if ((dscp >= NFP_DSCP_MIN) && (dscp <= NFP_DSCP_MAX)) { |
| ct->txq_map[dscp].txq = 0; |
| ct->txq_map[dscp].valid = MV_FALSE; |
| } else { |
| mvOsPrintf("%s Error: dscp value (%d) is out of range\n", __func__, dscp); |
| return MV_BAD_PARAM; |
| } |
| |
| if (!mvNfpIsTxqSet(ct)) |
| ct->flags &= ~NFP_F_CT_SET_TXQ; |
| |
| return MV_OK; |
| } |
| |
| return MV_NOT_FOUND; |
| } |
| |
| MV_STATUS mvNfpCtTxpRuleAdd(NFP_RULE_CT *ct2) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| /* rule is not exist - create rule from classification context */ |
| if (!ct) { |
| ct = mvNfpCtClassifyRuleCreate(ct2); |
| if (!ct) |
| return MV_FAIL; |
| } |
| |
| /* Update rule Txq table */ |
| if (ct2->flags & NFP_F_CT_SET_TXP) { |
| ct->txp = ct2->txp; |
| ct->flags |= NFP_F_CT_SET_TXP; |
| } else { |
| mvOsPrintf("%s: NFP_F_CT_SET_TXP flag is not set\n", __func__); |
| return MV_BAD_PARAM; |
| } |
| |
| NFP_DBG("NFP (ct txp) set %p\n", ct); |
| |
| return MV_OK; |
| } |
| |
| MV_STATUS mvNfpCtTxpRuleDel(NFP_RULE_CT *ct2) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| |
| if (ct) { |
| ct->txp = 0; |
| ct->flags &= ~NFP_F_CT_SET_TXP; |
| return MV_OK; |
| } |
| |
| return MV_NOT_FOUND; |
| } |
| |
| MV_STATUS mvNfpCtMhRuleAdd(NFP_RULE_CT *ct2) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* sanity: chack mh parameter */ |
| if (ct2->mh < 0) { |
| mvOsPrintf("%s Error: mh (%d) is out of range\n", __func__, ct2->mh); |
| return MV_BAD_PARAM; |
| } |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| /* rule is not exist - create rule from classification context */ |
| if (!ct) { |
| ct = mvNfpCtClassifyRuleCreate(ct2); |
| if (!ct) |
| return MV_FAIL; |
| } |
| |
| /* Update rule MH table */ |
| if (ct2->flags & NFP_F_CT_SET_MH) { |
| ct->mh = ct2->mh; |
| ct->flags |= NFP_F_CT_SET_MH; |
| } else { |
| mvOsPrintf("%s: NFP_F_CT_SET_MH flag is not set\n", __func__); |
| return MV_BAD_PARAM; |
| } |
| |
| NFP_DBG("NFP (ct mh) set %p\n", ct); |
| |
| return MV_OK; |
| } |
| |
| MV_STATUS mvNfpCtMhRuleDel(NFP_RULE_CT *ct2) |
| { |
| NFP_RULE_CT *ct; |
| |
| /* Rule must exist already, but it can be valid or invalid */ |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| |
| if (ct) { |
| ct->mh = 0; |
| ct->flags &= ~NFP_F_CT_SET_MH; |
| return MV_OK; |
| } |
| |
| return MV_NOT_FOUND; |
| } |
| |
| #endif /* NFP_CLASSIFY */ |
| |
| MV_STATUS mvNfpCtRuleDel(NFP_RULE_CT *ct2) |
| { |
| MV_U32 hash; |
| NFP_RULE_CT *ct, *prev; |
| MV_LIST_ELEMENT *element; |
| |
| /* If this rule currently exists in the Invalid Rules DB, delete it */ |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| if (ct) { |
| if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_ref--; |
| if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_hwf_ref--; |
| element = mvListFind(ct_inv_list, (MV_ULONG)ct); |
| #ifdef NFP_LIMIT |
| if (ct->tbfInfo) |
| mvNfpCtRateLimitDel(ct); |
| #endif /* NFP_LIMIT */ |
| mvListDel(element); |
| mvOsFree(ct); |
| return MV_OK; |
| } |
| |
| hash = mvNfpCtHash(ct2); |
| ct = ct_hash[hash]; |
| |
| prev = NULL; |
| while (ct) { |
| if ((ct->family == ct2->family) && |
| l3_addr_eq(ct->family, ct->srcL3, ct2->srcL3) && |
| l3_addr_eq(ct->family, ct->dstL3, ct2->dstL3) && |
| (ct->ports == ct2->ports) && |
| (ct->proto == ct2->proto)) { |
| if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_ref--; |
| if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV)) |
| (ct->fib)->ct_hwf_ref--; |
| if (prev) |
| prev->next = ct->next; |
| else |
| ct_hash[hash] = ct->next; |
| |
| NFP_DBG("NFP (ct) del %p\n", ct); |
| #ifdef NFP_LIMIT |
| if (ct->tbfInfo) |
| mvNfpCtRateLimitDel(ct); |
| #endif /* NFP_LIMIT */ |
| mvOsFree(ct); |
| return MV_OK; |
| } |
| prev = ct; |
| ct = ct->next; |
| } |
| |
| return MV_NOT_FOUND; |
| } |
| |
| void mvNfpCtClean(int family) |
| { |
| int i; |
| NFP_RULE_CT *ct, *next; |
| #ifdef NFP_LIMIT |
| NFP_TBF_INFO *tbf_data; |
| #endif /* NFP_LIMIT */ |
| MV_LIST_ELEMENT *curr, *tmp; |
| |
| /* Clean CT incomplete rules list */ |
| if (ct_inv_list) { |
| curr = ct_inv_list->next; |
| while (curr) { |
| tmp = curr->next; |
| ct = (NFP_RULE_CT *)curr->data; |
| if (ct->family == family) { |
| #ifdef NFP_LIMIT |
| if (ct->tbfInfo) |
| mvNfpCtRateLimitDel(ct); |
| #endif /* NFP_LIMIT */ |
| mvOsFree(ct); |
| mvListDel(curr); |
| } |
| curr = tmp; |
| } |
| } |
| |
| /* Clean CT hash table */ |
| for (i = 0; i < NFP_CT_HASH_SIZE; i++) { |
| ct = ct_hash[i]; |
| |
| while (ct) { |
| next = ct->next; |
| if (ct->family == family) { |
| #ifdef NFP_LIMIT |
| if (ct->tbfInfo) |
| mvNfpCtRateLimitDel(ct); |
| #endif /* NFP_LIMIT */ |
| mvOsFree(ct); |
| } |
| ct = next; |
| } |
| ct_hash[i] = NULL; |
| } |
| #ifdef NFP_LIMIT |
| /* clean tbfs */ |
| if (tbfs) { |
| curr = tbfs->next; |
| while (curr) { |
| tbf_data = (NFP_TBF_INFO *)mvListDel(curr); |
| mvOsFree(tbf_data); |
| curr = tbfs->next; |
| } |
| } |
| |
| #endif /* NFP_LIMIT */ |
| } |
| |
| void mvNfpCtDestroy(void) |
| { |
| if (ct_hash) |
| mvOsFree(ct_hash); |
| #ifdef NFP_LIMIT |
| if (tbfs) |
| mvOsFree(tbfs); |
| #endif /* NFP_LIMIT */ |
| mvListDestroy(ct_inv_list); |
| } |
| |
| MV_VOID mvNfpCtCleanVisited(MV_U32 row, MV_U32 iterator_id) |
| { |
| NFP_RULE_CT *curr = ct_hash[row]; |
| |
| while (curr) { |
| curr->visited = curr->visited & ~(1 << iterator_id); |
| curr = curr->next; |
| } |
| } |
| |
| MV_STATUS mvNfpCtFirstRuleGet(NFP_RULE_CT **rule, MV_U32 iterator_id) |
| { |
| mvNfpCtCleanVisited(ct_iterator_row, iterator_id); |
| ct_iterator_row = 0; |
| return mvNfpCtNextRuleGet(rule, iterator_id); |
| } |
| |
| MV_STATUS mvNfpCtNextRuleGet(NFP_RULE_CT **rule, MV_U32 iterator_id) |
| { |
| NFP_RULE_CT *curr; |
| |
| while (ct_iterator_row < NFP_CT_HASH_SIZE) { |
| curr = ct_hash[ct_iterator_row]; |
| /* skip visited and HWF processed rules */ |
| while (curr && ((curr->visited & (1 << iterator_id)) |
| || (curr->flags & NFP_F_CT_HWF))) |
| curr = curr->next; |
| if (!curr) { /* reached end of line */ |
| mvNfpCtCleanVisited(ct_iterator_row, iterator_id); |
| ct_iterator_row++; |
| continue; |
| } |
| curr->visited = 1; /* update - this rule is now visited by iterator */ |
| *rule = curr; |
| return MV_OK; |
| } |
| /* reached end of DB - no rule is found */ |
| ct_iterator_row = 0; /* next call start from the begining of the DB */ |
| return MV_NOT_FOUND; |
| } |
| |
| MV_STATUS mvNfpCtRuleMaxHitCntrGet(NFP_RULE_CT **rule) |
| { |
| int i; |
| MV_U32 max = -1; |
| NFP_RULE_CT *curr, *max_rule = NULL; |
| |
| for (i = 0; i < NFP_CT_HASH_SIZE; i++) { |
| curr = ct_hash[i]; |
| while (curr) { |
| if (curr->flags & NFP_F_CT_HWF) { |
| curr = curr->next; |
| continue; |
| } |
| if (curr->hit_cntr > max || !max_rule) { |
| max = curr->hit_cntr; |
| max_rule = curr; |
| } |
| curr = curr->next; |
| } |
| } |
| if (!max_rule) |
| return MV_NOT_FOUND; |
| *rule = max_rule; |
| return MV_OK; |
| } |
| |
| /* 0 - set zero to UDP csum on TX, 1 - recalculate UDP csum on TX */ |
| MV_STATUS mvNfpCtRuleUdpCsumSet(NFP_RULE_CT *ct2, int mode) |
| { |
| NFP_RULE_CT *ct; |
| |
| if (!ct2 || (mode < 0) || (mode > 1) || (ct2->proto != MV_IP_PROTO_UDP)) |
| return MV_BAD_PARAM; |
| |
| ct = mvNfpCtLookup(ct2); |
| if (!ct) |
| ct = mvNfpCtRuleInvalidLookup(ct2); |
| |
| if (!ct) |
| return MV_NOT_FOUND; |
| |
| if (mode) |
| ct->flags |= NFP_F_CT_UDP_CSUM; |
| else |
| ct->flags &= ~NFP_F_CT_UDP_CSUM; |
| |
| return MV_OK; |
| } |
| |
| /* mode = 1:HWF, 0:NFP */ |
| MV_STATUS mvNfpCtRuleHwfSet(MV_NFP_CT_KEY *key, int mode) |
| { |
| NFP_RULE_CT *rule; |
| MV_U32 nflags = 0, ports; |
| |
| if (!key || (mode < 0) || (mode > 1)) |
| return MV_BAD_PARAM; |
| |
| ports = (MV_16BIT_BE(key->dport) << 16) | MV_16BIT_BE(key->sport); |
| rule = mvNfpCtLookupByTuple(key->family, key->src_l3, key->dst_l3, ports, key->proto); |
| if (!rule) |
| return MV_NOT_FOUND; |
| |
| /* sanity check */ |
| if (!rule->fib || (rule->flags & NFP_F_CT_FIB_INV)) |
| return MV_NOT_FOUND; |
| |
| if (mode) |
| nflags = (rule->flags | NFP_F_CT_HWF); |
| else |
| nflags = (rule->flags & ~NFP_F_CT_HWF); |
| |
| if (rule->flags != nflags) { |
| /* Mode is changed - update hwf ref count */ |
| if (mode) /* go to HWF mode */ |
| rule->fib->ct_hwf_ref++; |
| else { |
| /* exit from HWF mode */ |
| rule->fib->ct_hwf_ref--; |
| rule->hit_cntr = 0; |
| } |
| } |
| rule->flags = nflags; |
| return MV_OK; |
| } |
| |
| MV_STATUS mvNfpCtRuleHitCntrGet(MV_NFP_CT_KEY *key, MV_U32 *hit_cntr) |
| { |
| NFP_RULE_CT *rule; |
| MV_U32 ports; |
| |
| if (!key || !hit_cntr) |
| return MV_BAD_PARAM; |
| ports = (MV_16BIT_BE(key->dport) << 16) | MV_16BIT_BE(key->sport); |
| rule = mvNfpCtLookupByTuple(key->family, key->src_l3, key->dst_l3, ports, key->proto); |
| if (!rule) |
| return MV_NOT_FOUND; |
| *hit_cntr = rule->hit_cntr; |
| return MV_OK; |
| } |
| |
| MV_STATUS mvNfpCtRuleHitCntrSet(MV_NFP_CT_KEY *key, MV_U32 val) |
| { |
| NFP_RULE_CT *rule; |
| MV_U32 ports; |
| |
| if (!key || val < 0) |
| return MV_BAD_PARAM; |
| ports = (MV_16BIT_BE(key->dport) << 16) | MV_16BIT_BE(key->sport); |
| rule = mvNfpCtLookupByTuple(key->family, key->src_l3, key->dst_l3, ports, key->proto); |
| if (!rule) |
| return MV_NOT_FOUND; |
| rule->hit_cntr = val; |
| return MV_OK; |
| } |
| |
| MV_STATUS mvNfpCtRuleInfoGet(MV_NFP_CT_KEY *key, MV_NFP_CT_INFO *ct_info) |
| { |
| NFP_RULE_CT *rule; |
| NFP_IF_MAP *ifMap; |
| MV_U32 ports; |
| |
| if (!key || !ct_info) |
| return MV_BAD_PARAM; |
| ports = (MV_16BIT_BE(key->dport) << 16) | MV_16BIT_BE(key->sport); |
| rule = mvNfpCtLookupByTuple(key->family, key->src_l3, key->dst_l3, ports, key->proto); |
| if (!rule) |
| return MV_NOT_FOUND; |
| ct_info->flags = rule->flags; |
| ct_info->new_sip = rule->new_sip; |
| ct_info->new_dip = rule->new_dip; |
| ct_info->new_sport = rule->new_sport; |
| ct_info->new_dport = rule->new_dport; |
| if (rule->fib) { |
| memcpy(ct_info->sa, (rule->fib)->sa, MV_MAC_ADDR_SIZE); |
| memcpy(ct_info->da, (rule->fib)->da, MV_MAC_ADDR_SIZE); |
| ifMap = mvNfpIfMapGet((rule->fib)->oif); |
| ct_info->out_port = ifMap->port; |
| } |
| #ifdef NFP_CLASSIFY |
| ct_info->mh = rule->mh; |
| ct_info->txp = rule->txp; |
| /* return only global txq/dscp/vprio mapping */ |
| ct_info->txq = (rule->txq_map[NFP_DSCP_MAP_GLOBAL]).txq; |
| if (!(rule->txq_map[NFP_DSCP_MAP_GLOBAL]).valid) |
| ct_info->flags = ct_info->flags & ~NFP_F_CT_SET_TXQ; |
| ct_info->dscp = (rule->dscp_map[NFP_DSCP_MAP_GLOBAL]).new_dscp; |
| if (!(rule->dscp_map[NFP_DSCP_MAP_GLOBAL]).valid) |
| ct_info->flags = ct_info->flags & ~NFP_F_CT_SET_DSCP; |
| ct_info->vprio = (rule->vpri_map[NFP_VPRI_MAP_GLOBAL]).new_prio; |
| if (!(rule->vpri_map[NFP_VPRI_MAP_GLOBAL]).valid) |
| ct_info->flags = ct_info->flags & ~NFP_F_CT_SET_VLAN_PRIO; |
| #endif /* NFP_CLASSIFY */ |
| return MV_OK; |
| } |
| |
| void mvNfpCtDump(void) |
| { |
| MV_U32 i; |
| NFP_RULE_CT *ct; |
| MV_LIST_ELEMENT *curr; |
| |
| mvOsPrintf("\n(ct hash)\n"); |
| for (i = 0; i < NFP_CT_HASH_SIZE; i++) { |
| ct = ct_hash[i]; |
| |
| while (ct) { |
| mvOsPrintf(" [%2d] ", i); |
| mvNfpCtRulePrint(ct); |
| |
| ct = ct->next; |
| } |
| } |
| if (!ct_inv_list) { |
| mvOsPrintf("(ct_inv_list) does not exist\n"); |
| return; |
| } |
| mvOsPrintf("(ct_inv_list)\n"); |
| curr = ct_inv_list->next; |
| while (curr) { |
| ct = (NFP_RULE_CT *)curr->data; |
| mvNfpCtRulePrint(ct); |
| curr = curr->next; |
| } |
| } |
| |