| /******************************************************************************* |
| Copyright (C) Marvell International Ltd. and its affiliates |
| |
| This software file (the "File") is owned and distributed by Marvell |
| International Ltd. and/or its affiliates ("Marvell") under the following |
| alternative licensing terms. Once you have made an election to distribute the |
| File under one of the following license alternatives, please (i) delete this |
| introductory statement regarding license alternatives, (ii) delete the two |
| license alternatives 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. |
| |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| * mvNfp.c - Marvell Network Fast Processing (Routing and NAT) |
| * |
| * DESCRIPTION: |
| * |
| * Supported Features: |
| * - OS independent. |
| * |
| *******************************************************************************/ |
| |
| /* includes */ |
| #include "mvOs.h" |
| #include "mvDebug.h" |
| #include "eth/nfp/mvNfp.h" |
| #include "eth/nfp/mvNfpSec.h" |
| #include "eth/mvEth.h" |
| |
| static struct ruleHashBucket *ruleDb; |
| static MV_U32 ruleDbSize; |
| |
| static MV_U32 nfpHashMaxDepth; |
| static MV_U32 nfpRuleSetCount; |
| static MV_U32 nfpRuleUpdateCount; |
| static MV_U32 nfpRuleDeleteCount; |
| |
| MV_U32 fp_ip_jhash_iv = 0; |
| |
| MV_STATUS mvFpInit(void) |
| { |
| fp_ip_jhash_iv = mvOsRand(); |
| |
| return MV_OK; |
| } |
| |
| /* Initialize NFP Rule Database (Routing + ARP information table) */ |
| MV_STATUS mvFpRuleDbInit(MV_U32 dbSize) |
| { |
| ruleDb = (struct ruleHashBucket *)mvOsMalloc(sizeof(struct ruleHashBucket) * dbSize); |
| if (ruleDb == NULL) { |
| mvOsPrintf("NFP Rule DB: Not Enough Memory\n"); |
| return MV_NO_RESOURCE; |
| } |
| ruleDbSize = dbSize; |
| memset(ruleDb, 0, sizeof(struct ruleHashBucket) * ruleDbSize); |
| nfpRuleSetCount = nfpRuleUpdateCount = nfpRuleDeleteCount = 0; |
| |
| mvOsPrintf("mvFpRuleDb (%p): %d entries, %d bytes\n", |
| ruleDb, ruleDbSize, sizeof(struct ruleHashBucket) * ruleDbSize); |
| |
| return MV_OK; |
| } |
| |
| /* Clear NFP Rule Database (Routing + ARP information table) */ |
| MV_STATUS mvFpRuleDbClear(void) |
| { |
| MV_U32 i = 0; |
| MV_FP_RULE *currRule; |
| MV_FP_RULE *tmpRule; |
| |
| if (ruleDb == NULL) |
| return MV_NOT_INITIALIZED; |
| |
| for (i = 0; i < ruleDbSize; i++) { |
| currRule = ruleDb[i].ruleChain; |
| while (currRule != NULL) { |
| tmpRule = currRule; |
| currRule = currRule->next; |
| mvOsFree(tmpRule); |
| } |
| ruleDb[i].ruleChain = NULL; |
| } |
| return MV_OK; |
| } |
| |
| /* Free Rule Database memory */ |
| void mvFpRuleDbDestroy(void) |
| { |
| if (ruleDb != NULL) |
| mvOsFree(ruleDb); |
| } |
| |
| /* Print rule action type. Assume rule is not NULL. */ |
| static void mvFpActionTypePrint(const MV_FP_RULE *rule) |
| { |
| switch (rule->mgmtInfo.actionType) { |
| case MV_FP_ROUTE_CMD: |
| mvOsPrintf("A=Route, "); |
| break; |
| case MV_FP_DROP_CMD: |
| mvOsPrintf("A=Drop, "); |
| break; |
| case MV_FP_TO_STACK_CMD: |
| mvOsPrintf("A=Stack, "); |
| break; |
| default: |
| mvOsPrintf("A=Unknown (%d), ", rule->mgmtInfo.actionType); |
| break; |
| } |
| } |
| |
| /* Print rule type (static or dynamic). Assume rule is not NULL. */ |
| static void mvFpRuleTypePrint(const MV_FP_RULE *rule) |
| { |
| switch (rule->mgmtInfo.ruleType) { |
| case MV_FP_STATIC_RULE: |
| mvOsPrintf("T=Static"); |
| break; |
| case MV_FP_DYNAMIC_RULE: |
| mvOsPrintf("T=Dynamic"); |
| break; |
| default: |
| mvOsPrintf("T=Unknown"); |
| break; |
| } |
| } |
| |
| /* Print a NFP Rule */ |
| void mvFpRulePrint(const MV_FP_RULE *rule) |
| { |
| mvFpActionTypePrint(rule); |
| mvFpRuleTypePrint(rule); |
| mvOsPrintf(", SIP="); |
| mvDebugPrintIpAddr(MV_32BIT_BE(rule->routingInfo.srcIp)); |
| mvOsPrintf(", DIP="); |
| mvDebugPrintIpAddr(MV_32BIT_BE(rule->routingInfo.dstIp)); |
| mvOsPrintf(", GTW="); |
| mvDebugPrintIpAddr(MV_32BIT_BE(rule->routingInfo.defGtwIp)); |
| mvOsPrintf(", DA="); |
| mvDebugPrintMacAddr(rule->routingInfo.dstMac); |
| mvOsPrintf(", SA="); |
| mvDebugPrintMacAddr(rule->routingInfo.srcMac); |
| mvOsPrintf(", inIf=%d", rule->routingInfo.inIfIndex); |
| mvOsPrintf(", outIf=%d", rule->routingInfo.outIfIndex); |
| mvOsPrintf(", ToS=0x%x", rule->routingInfo.dscp); |
| mvOsPrintf(", TxQ=%d", rule->routingInfo.txq); |
| |
| mvOsPrintf(", count=%d, aware_flags=0x%X", rule->mgmtInfo.new_count, rule->routingInfo.aware_flags); |
| mvOsPrintf("\n"); |
| } |
| |
| /* Print NFP Rule Database (Routing + ARP information table) */ |
| MV_STATUS mvFpRuleDbPrint(void) |
| { |
| MV_U32 count, i; |
| MV_FP_RULE *currRule; |
| |
| mvOsPrintf("\nPrinting NFP Rule Database\n"); |
| count = 0; |
| |
| for (i = 0; i < ruleDbSize; i++) { |
| currRule = ruleDb[i].ruleChain; |
| |
| if (currRule != NULL) |
| mvOsPrintf("\n%03u: FP DB hash=0x%x", count, i); |
| |
| while (currRule != NULL) { |
| mvOsPrintf("\n%03u: Rule=%p: ", count, currRule); |
| mvFpRulePrint(currRule); |
| currRule = currRule->next; |
| count++; |
| } |
| } |
| return MV_OK; |
| } |
| |
| /* Copy all the information from src_rule to new_rule */ |
| /* Warning - doesn't perform any checks on memory, just copies */ |
| /* count is set to zero in new_rule */ |
| /* Note: the next pointer is not updated . */ |
| void mvFpRuleCopy(MV_FP_RULE *dstRule, const MV_FP_RULE *srcRule) |
| { |
| dstRule->mgmtInfo.actionType = srcRule->mgmtInfo.actionType; |
| dstRule->mgmtInfo.new_count = srcRule->mgmtInfo.new_count; |
| dstRule->mgmtInfo.old_count = srcRule->mgmtInfo.old_count; |
| dstRule->mgmtInfo.ruleType = srcRule->mgmtInfo.ruleType; |
| dstRule->mgmtInfo.snat_aware_refcnt = srcRule->mgmtInfo.snat_aware_refcnt; |
| dstRule->mgmtInfo.dnat_aware_refcnt = srcRule->mgmtInfo.dnat_aware_refcnt; |
| |
| dstRule->routingInfo.aware_flags = srcRule->routingInfo.aware_flags; |
| dstRule->routingInfo.srcIp = srcRule->routingInfo.srcIp; |
| dstRule->routingInfo.dstIp = srcRule->routingInfo.dstIp; |
| dstRule->routingInfo.defGtwIp = srcRule->routingInfo.defGtwIp; |
| memcpy(dstRule->routingInfo.srcMac, srcRule->routingInfo.srcMac, MV_MAC_ADDR_SIZE); |
| memcpy(dstRule->routingInfo.dstMac, srcRule->routingInfo.dstMac, MV_MAC_ADDR_SIZE); |
| dstRule->routingInfo.inIfIndex = srcRule->routingInfo.inIfIndex; |
| dstRule->routingInfo.outIfIndex = srcRule->routingInfo.outIfIndex; |
| dstRule->routingInfo.txq = srcRule->routingInfo.txq; |
| dstRule->routingInfo.dscp = srcRule->routingInfo.dscp; |
| } |
| |
| /* Get the count value for a rule that matches the given SIP, DIP */ |
| MV_U32 mvFpRouteCountGet(MV_U32 srcIp, MV_U32 dstIp) |
| { |
| MV_U32 hash, hash_tr; |
| MV_FP_RULE *pRule; |
| |
| hash = mv_jhash_3words(dstIp, srcIp, (MV_U32) 0, fp_ip_jhash_iv); |
| hash_tr = hash & (ruleDbSize - 1); |
| |
| pRule = ruleDb[hash_tr].ruleChain; |
| while (pRule) { |
| /* look for a matching rule */ |
| if ((pRule->routingInfo.dstIp == dstIp) && (pRule->routingInfo.srcIp == srcIp)) |
| return pRule->mgmtInfo.new_count; |
| |
| pRule = pRule->next; |
| } |
| return 0; |
| } |
| |
| MV_STATUS mvFpRuleAwareSet(MV_FP_RULE *pSetRule) |
| { |
| MV_U32 hash, hash_tr; |
| MV_FP_RULE *pRule; |
| |
| hash = mv_jhash_3words(pSetRule->routingInfo.dstIp, pSetRule->routingInfo.srcIp, (MV_U32) 0, fp_ip_jhash_iv); |
| hash_tr = hash & (ruleDbSize - 1); |
| |
| pRule = ruleDb[hash_tr].ruleChain; |
| while (pRule) { |
| if ((pRule->routingInfo.srcIp == pSetRule->routingInfo.srcIp) && |
| (pRule->routingInfo.dstIp == pSetRule->routingInfo.dstIp)) { |
| |
| pRule->routingInfo.aware_flags = pSetRule->routingInfo.aware_flags; |
| #ifdef MV_FP_DEBUG |
| mvOsPrintf("Update FP aware: DIP=%u.%u.%u.%u, SIP=%u.%u.%u.%u, hash0x%x, flags=0x%x\n", |
| MV_IP_QUAD(pSetRule->routingInfo.dstIp), |
| MV_IP_QUAD(pSetRule->routingInfo.srcIp), hash_tr, pSetRule->routingInfo.aware_flags); |
| #endif |
| return MV_OK; |
| } |
| pRule = pRule->next; |
| } |
| #ifdef MV_FP_DEBUG |
| mvOsPrintf("FP aware NOT found: DIP=%u.%u.%u.%u, SIP=%u.%u.%u.%u, hash=0x%x, flags=0x%x\n", |
| MV_IP_QUAD(pSetRule->routingInfo.dstIp), |
| MV_IP_QUAD(pSetRule->routingInfo.srcIp), hash_tr, pSetRule->routingInfo.aware_flags); |
| #endif |
| return MV_NOT_FOUND; |
| } |
| |
| /* Set a Routing ToS Rule: update an existing rule */ |
| #ifdef CONFIG_MV_ETH_NFP_TOS |
| MV_STATUS mvFpToSSet(MV_FP_RULE *pSetRule) |
| { |
| MV_U32 hash, hash_tr; |
| MV_FP_RULE *pRule; |
| |
| hash = mv_jhash_3words(pSetRule->routingInfo.dstIp, pSetRule->routingInfo.srcIp, (MV_U32) 0, fp_ip_jhash_iv); |
| hash_tr = hash & (ruleDbSize - 1); |
| |
| pRule = ruleDb[hash_tr].ruleChain; |
| while (pRule) { |
| if ((pRule->routingInfo.srcIp == pSetRule->routingInfo.srcIp && |
| pRule->routingInfo.dstIp == pSetRule->routingInfo.dstIp)) { |
| pRule->routingInfo.txq = pSetRule->routingInfo.txq; |
| pRule->routingInfo.dscp = pSetRule->routingInfo.dscp; |
| nfpRuleUpdateCount++; |
| #ifdef MV_FP_DEBUG |
| mvOsPrintf("ToSNFP_%03u: DIP=%u.%u.%u.%u, SIP=%u.%u.%u.%u, hash=0x%04x TOS=0x%x TxQ=%d\n", |
| nfpRuleUpdateCount, MV_IP_QUAD(pSetRule->routingInfo.dstIp), |
| MV_IP_QUAD(pSetRule->routingInfo.srcIp), hash_tr, |
| pRule->routingInfo.dscp, pRule->routingInfo.txq); |
| #endif |
| return MV_OK; |
| } |
| pRule = pRule->next; |
| } |
| return MV_NOT_FOUND; |
| } |
| #endif |
| /* Set a Routing Rule: create a new rule or update an existing rule */ |
| /* in the Routing + ARP information table */ |
| MV_STATUS mvFpRuleSet(MV_FP_RULE *pSetRule) |
| { |
| MV_U32 hash, hash_tr; |
| int depth = 0; |
| MV_FP_RULE *pRule, *pNewRule; |
| |
| hash = mv_jhash_3words(pSetRule->routingInfo.dstIp, pSetRule->routingInfo.srcIp, (MV_U32) 0, fp_ip_jhash_iv); |
| hash_tr = hash & (ruleDbSize - 1); |
| |
| pRule = ruleDb[hash_tr].ruleChain; |
| while (pRule) { |
| if ((pRule->routingInfo.srcIp == pSetRule->routingInfo.srcIp && |
| pRule->routingInfo.dstIp == pSetRule->routingInfo.dstIp)) { |
| |
| mvFpRuleCopy(pRule, pSetRule); |
| nfpRuleUpdateCount++; |
| |
| #ifdef MV_FP_DEBUG |
| mvOsPrintf("UpdNFP_%03u: DIP=%u.%u.%u.%u, SIP=%u.%u.%u.%u, hash=0x%04x TOS=0x%x TxQ=%d\n", |
| nfpRuleUpdateCount, MV_IP_QUAD(pSetRule->routingInfo.dstIp), |
| MV_IP_QUAD(pSetRule->routingInfo.srcIp), hash_tr, |
| pRule->routingInfo.dscp, pRule->routingInfo.txq); |
| #endif |
| return MV_OK; |
| } |
| pRule = pRule->next; |
| } |
| |
| /* Allocate new entry */ |
| pNewRule = mvOsMalloc(sizeof(MV_FP_RULE)); |
| if (pNewRule == NULL) { |
| mvOsPrintf("mvFpRuleSet: Can't allocate new rule\n"); |
| return MV_FAIL; |
| } |
| mvFpRuleCopy(pNewRule, pSetRule); |
| pNewRule->next = NULL; |
| |
| if (ruleDb[hash_tr].ruleChain == NULL) |
| ruleDb[hash_tr].ruleChain = pNewRule; |
| else { |
| pRule = ruleDb[hash_tr].ruleChain; |
| while (pRule->next != NULL) { |
| depth++; |
| pRule = pRule->next; |
| } |
| pRule->next = pNewRule; |
| } |
| if (depth > nfpHashMaxDepth) |
| nfpHashMaxDepth = depth; |
| nfpRuleSetCount++; |
| |
| #ifdef MV_FP_DEBUG |
| mvOsPrintf("SetNFP_%03u: DIP=%u.%u.%u.%u, SIP=%u.%u.%u.%u, hash=0x%04x, aware=0x%02x\n", |
| nfpRuleSetCount, MV_IP_QUAD(pSetRule->routingInfo.dstIp), |
| MV_IP_QUAD(pSetRule->routingInfo.srcIp), hash_tr, pSetRule->routingInfo.aware_flags); |
| #endif |
| return MV_OK; |
| } |
| |
| /* Delete a specified rule from the Routing + ARP information table */ |
| MV_STATUS mvFpRuleDelete(MV_FP_RULE *rule) |
| { |
| MV_U32 hash, hash_tr; |
| MV_FP_RULE *currRule, *prevRule; |
| |
| nfpRuleDeleteCount++; |
| hash = mv_jhash_3words(rule->routingInfo.dstIp, rule->routingInfo.srcIp, (MV_U32) 0, fp_ip_jhash_iv); |
| hash_tr = hash & (ruleDbSize - 1); |
| |
| prevRule = NULL; |
| for (currRule = ruleDb[hash_tr].ruleChain; currRule != NULL; prevRule = currRule, currRule = currRule->next) { |
| if ((currRule->routingInfo.srcIp == rule->routingInfo.srcIp) && |
| (currRule->routingInfo.dstIp == rule->routingInfo.dstIp)) { |
| if (prevRule == NULL) |
| ruleDb[hash_tr].ruleChain = currRule->next; |
| else |
| prevRule->next = currRule->next; |
| #ifdef MV_FP_DEBUG |
| mvOsPrintf("DelNFP_%03u: DIP=%u.%u.%u.%u, SIP=%u.%u.%u.%u, hash=0x%04x\n", |
| nfpRuleDeleteCount, MV_IP_QUAD(currRule->routingInfo.dstIp), |
| MV_IP_QUAD(currRule->routingInfo.srcIp), hash_tr); |
| #endif |
| mvOsFree(currRule); |
| return MV_OK; |
| } |
| } |
| return MV_NOT_FOUND; |
| } |
| |
| /* Find and return the first matching rule in the Routing + ARP information table */ |
| static INLINE MV_FP_RULE *mvFpRuleFind(MV_U32 dstIp, MV_U32 srcIp) |
| { |
| MV_U32 hash, hash_tr; |
| MV_FP_RULE *pRule; |
| int count = 0; |
| |
| hash = mv_jhash_3words(dstIp, srcIp, (MV_U32) 0, fp_ip_jhash_iv); |
| hash_tr = hash & (ruleDbSize - 1); |
| |
| pRule = ruleDb[hash_tr].ruleChain; |
| |
| while (pRule) { |
| /* look for a matching rule */ |
| if ((pRule->routingInfo.dstIp == dstIp) && (pRule->routingInfo.srcIp == srcIp)) { |
| pRule->mgmtInfo.new_count++; |
| return pRule; |
| } |
| pRule = pRule->next; |
| count++; |
| } |
| return NULL; |
| } |
| |
| #ifdef CONFIG_MV_ETH_NFP_FDB |
| static INLINE MV_U32 mvFpFdbMember(MV_U32 ifIndex) |
| { |
| return (ifIndex < ETH_FP_IFINDEX_MAX) ? fdbMember[ifIndex] : 0; |
| } |
| |
| static MV_FP_FDB_RULE *mvFpFdbLookup(MV_U32 ifIndex, MV_U8 * pDA) |
| { |
| MV_U32 hash, hash_tr; |
| MV_FP_FDB_RULE *pRule; |
| MV_U32 bridgeId; |
| int count = 0; |
| |
| if (ifIndex >= ETH_FP_IFINDEX_MAX) |
| return NULL; |
| bridgeId = fdbMember[ifIndex]; |
| if (!(bridgeId)) |
| return NULL; |
| |
| hash = mv_jhash_3words(bridgeId, 0, *(MV_U32 *) (pDA + 2), fp_ip_jhash_iv); |
| hash_tr = hash & (fdbRuleDbSize - 1); |
| |
| pRule = fdbRuleDb[hash_tr].ruleChain; |
| |
| while (pRule) { |
| /* |
| MV_NFP_DBG("%s: looking %d %02x:%02x:%02x:%02x:%02x:%02x\n", |
| __FUNCTION__, bridgeId, |
| pRule->fdbInfo.mac[0], |
| pRule->fdbInfo.mac[1], |
| pRule->fdbInfo.mac[2], |
| pRule->fdbInfo.mac[3], |
| pRule->fdbInfo.mac[4], |
| pRule->fdbInfo.mac[5]); |
| */ |
| if ((bridgeId == pRule->fdbInfo.bridge) && |
| (*((MV_U16 *) (pDA + 0)) == *(MV_U16 *) (&pRule->fdbInfo.mac[0])) && |
| (*((MV_U16 *) (pDA + 2)) == *(MV_U16 *) (&pRule->fdbInfo.mac[2])) && |
| (*((MV_U16 *) (pDA + 4)) == *(MV_U16 *) (&pRule->fdbInfo.mac[4]))) { |
| pRule->mgmtInfo.new_count++; |
| break; |
| } |
| pRule = pRule->next; |
| count++; |
| } |
| /* |
| if (pRule) |
| MV_NFP_DBG("%s: lookup bridge=%d %02x:%02x:%02x:%02x:%02x:%02x => if=%d flags=%x\n", |
| __FUNCTION__, bridgeId, pDA[0],pDA[1],pDA[2],pDA[3],pDA[4],pDA[5], |
| pRule->fdbInfo.ifIndex, pRule->fdbInfo.flags); |
| else |
| MV_NFP_DBG("%s: lookup bridge=%d %02x:%02x:%02x:%02x:%02x:%02x => unknown\n", |
| __FUNCTION__, bridgeId, pDA[0],pDA[1],pDA[2],pDA[3],pDA[4],pDA[5]); |
| |
| */ |
| return pRule; |
| } |
| #endif /* CONFIG_MV_ETH_NFP_FDB */ |
| |
| /* |
| * PPPoE Support |
| */ |
| #ifdef CONFIG_MV_ETH_NFP_PPP |
| static INLINE void mvFpDecTTL(MV_IP_HEADER *pIpHdr) |
| { |
| MV_U32 check = (MV_U32) pIpHdr->checksum; |
| check += (MV_U32) htons(0x0100); |
| pIpHdr->checksum = (MV_U16) (check + (check >= 0xFFFF)); |
| pIpHdr->ttl--; |
| } |
| static INLINE __wsum csum_unfold(__sum16 n) |
| { |
| return (__force __wsum) n; |
| } |
| |
| static INLINE __sum16 csum_fold(__wsum sum) |
| { |
| __asm__("add %0, %1, %1, ror #16 @ csum_fold" : "=r"(sum) |
| : "r"(sum) |
| : "cc"); |
| return (__force __sum16) (~(__force u32) sum >> 16); |
| } |
| |
| __u32 csum_partial(const char *src, int len, __u32 sum); |
| |
| static INLINE void mvFpCSumInc(MV_IP_HEADER *pIpHdr, MV_U32 srcIp, MV_U32 newIp) |
| { |
| MV_TCP_HEADER *pTcpHdr; |
| MV_UDP_HEADER *pUdpHdr; |
| __be32 diff[] = { ~srcIp, newIp }; |
| |
| pIpHdr->checksum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(pIpHdr->checksum))); |
| |
| switch (pIpHdr->protocol) { |
| case MV_IP_PROTO_TCP: |
| pTcpHdr = (MV_TCP_HEADER *) ((unsigned)pIpHdr + sizeof(MV_IP_HEADER)); |
| pTcpHdr->chksum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(pTcpHdr->chksum))); |
| break; |
| case MV_IP_PROTO_UDP: |
| pUdpHdr = (MV_UDP_HEADER *) ((unsigned)pIpHdr + sizeof(MV_IP_HEADER)); |
| pUdpHdr->check = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(pUdpHdr->check))); |
| break; |
| } |
| } |
| |
| static INLINE MV_U32 mvFpPppLookup(MV_U32 ifIndex) |
| { |
| return pppOpen[ifIndex].pppInfo.if_ppp; |
| } |
| #endif /* CONFIG_MV_ETH_NFP_PPP */ |
| |
| /* Check that protocol supported for FP NAT and extract srcPort and dstPort |
| * (or their equivalents) from the packet. |
| */ |
| MV_U8 mvFpPortsGet(MV_IP_HEADER *pIpHdr, MV_U16 *pDstPort, MV_U16 *pSrcPort) |
| { |
| MV_U8 proto = pIpHdr->protocol; |
| MV_UDP_HEADER *pUdpHdr; |
| MV_TCP_HEADER *pTcpHdr; |
| |
| switch (proto) { |
| case MV_IP_PROTO_TCP: |
| pTcpHdr = (MV_TCP_HEADER *) ((unsigned)pIpHdr + sizeof(MV_IP_HEADER)); |
| *pDstPort = pTcpHdr->dest; |
| *pSrcPort = pTcpHdr->source; |
| break; |
| |
| case MV_IP_PROTO_UDP: |
| pUdpHdr = (MV_UDP_HEADER *) ((unsigned)pIpHdr + sizeof(MV_IP_HEADER)); |
| *pDstPort = pUdpHdr->dest; |
| *pSrcPort = pUdpHdr->source; |
| break; |
| |
| /* Other protocols supporting NAT only without ports |
| * case ???????: |
| * case ???????: |
| * *pDstPort = 0; |
| * *pSrcPort = 0; |
| * break |
| * |
| */ |
| |
| default: |
| /* Skip NAT processing at all */ |
| proto = MV_IP_PROTO_NULL; |
| } |
| return proto; |
| } |
| |
| int mvFpProcess(MV_U32 ifIndex, MV_PKT_INFO *pPkt, MV_IP_HEADER *pIpHdr, MV_FP_STATS *pFpStats) |
| { |
| MV_FP_RULE *pRt; |
| MV_U32 dip, sip; |
| MV_U8 proto; |
| MV_U16 srcPort, dstPort; |
| MV_STATUS status; |
| #ifdef CONFIG_MV_ETH_NFP_NAT |
| MV_FP_NAT_RULE *pDnatRule, *pSnatRule; |
| #endif /* CONFIG_MV_ETH_NFP_NAT */ |
| #ifdef CONFIG_MV_ETH_NFP_FDB |
| MV_FP_FDB_RULE *pFdb; |
| #endif |
| MV_U8 *pEth = pPkt->pFrags->bufVirtPtr; |
| #ifdef CONFIG_MV_ETH_NFP_PPP |
| MV_BUF_INFO *pBuf = pPkt->pFrags; |
| #endif |
| #ifdef CONFIG_MV_ETH_NFP_SEC |
| MV_NFP_SEC_SPD_RULE *pSpd; |
| MV_NFP_SEC_SA_ENTRY *pSAEntry; |
| MV_ESP_HEADER *pEspHdr; |
| #endif |
| |
| MV_NFP_STAT(pFpStats->process++); |
| |
| /* Check MAC address: |
| * WAN - non-promiscous mode. |
| * Unicast packets - NFP, |
| * Multicast, Broadcast - Linux |
| * LAN - Promiscous mode. |
| * LAN Unicast MAC - NFP, |
| * Multicast, Broadcast, Unknown Unicast - Linux |
| */ |
| if (pEth[ETH_MV_HEADER_SIZE] == 0x01) { |
| MV_NFP_STAT(pFpStats->multicast++); |
| return -1; |
| } |
| #ifdef CONFIG_MV_ETH_NFP_FDB |
| if (mvFpFdbMember(ifIndex)) { |
| pFdb = mvFpFdbLookup(ifIndex, pEth + ETH_MV_HEADER_SIZE); |
| if (!pFdb) { |
| MV_NFP_STAT(pFpStats->fdb_rx_unknown++); |
| return -1; |
| } |
| if (pFdb->fdbInfo.flags & MV_FP_FDB_IS_LOCAL) { |
| /* DA is local, continue with routing */ |
| MV_NFP_STAT(pFpStats->fdb_rx_local++); |
| } else { |
| /* perform bridging */ |
| MV_NFP_STAT(pFpStats->fdb_bridged++); |
| return pFdb->fdbInfo.ifIndex; |
| } |
| } |
| #endif /* CONFIG_MV_ETH_NFP_FDB */ |
| |
| #ifdef CONFIG_MV_ETH_NFP_PPP |
| /* Decapsulate PPPoE */ |
| if (!pIpHdr) { |
| MV_PPPoE_HEADER *pPPP = (MV_PPPoE_HEADER *) pEth; |
| if ((pPPP->ethertype == 0x6488) && (pPPP->proto == 0x2100)) { |
| pIpHdr = (MV_IP_HEADER *) (pEth + ETH_MV_HEADER_SIZE + |
| sizeof(MV_802_3_HEADER) + ETH_FP_PPPOE_HDR); |
| |
| /* do not process fragments */ |
| if (pIpHdr->fragmentCtrl & 0xFF3F) { |
| MV_NFP_STAT(pFpStats->ppp_rx_frag++); |
| goto out; |
| } |
| |
| pBuf->bufAddrShift -= ETH_FP_PPPOE_HDR; |
| pBuf->bufPhysAddr += ETH_FP_PPPOE_HDR; |
| pBuf->bufVirtPtr += ETH_FP_PPPOE_HDR; |
| pBuf->dataSize -= ETH_FP_PPPOE_HDR; |
| pEth += ETH_FP_PPPOE_HDR; |
| |
| pPkt->status = ETH_TX_IP_NO_FRAG | ETH_TX_GENERATE_IP_CHKSUM_MASK | |
| (0x5 << ETH_TX_IP_HEADER_LEN_OFFSET); |
| |
| switch (pIpHdr->protocol) { |
| case MV_IP_PROTO_TCP: |
| pPkt->status |= ETH_TX_L4_TCP_TYPE | ETH_TX_GENERATE_L4_CHKSUM_MASK; |
| break; |
| case MV_IP_PROTO_UDP: |
| pPkt->status |= ETH_TX_L4_UDP_TYPE | ETH_TX_GENERATE_L4_CHKSUM_MASK; |
| break; |
| } |
| |
| MV_NFP_STAT(pFpStats->ppp_rx++); |
| MV_NFP_STAT(pFpStats->ethertype_unknown--); |
| } |
| } |
| |
| if (!pIpHdr) |
| goto out; |
| #endif /* CONFIG_MV_ETH_NFP_PPP */ |
| |
| proto = mvFpPortsGet(pIpHdr, &dstPort, &srcPort); |
| |
| /* Check TTL value */ |
| if (pIpHdr->ttl <= 1) { |
| /* TTL expired */ |
| MV_NFP_STAT(pFpStats->ip_ttl_expired++); |
| goto out; |
| } |
| |
| dip = pIpHdr->dstIP; |
| sip = pIpHdr->srcIP; |
| |
| #ifdef CONFIG_MV_ETH_NFP_SEC |
| /* TBD - Add statistics counters */ |
| /* inbound ipsec traffic */ |
| if (pIpHdr->protocol == MV_IP_PROTO_ESP) { |
| |
| /* extract esp header */ |
| pEspHdr = (MV_ESP_HEADER *) ((MV_U8 *) pIpHdr + sizeof(MV_IP_HEADER)); |
| |
| /* extract SA according to packet spi */ |
| pSAEntry = mvNfpSecSARuleFind(pEspHdr->spi); |
| if (pSAEntry != NULL) { |
| if (MV_OK == mvNfpSecIncoming(pPkt, pSAEntry)) { |
| MV_NFP_STAT(pFpStats->sec_in++); |
| return MV_NFP_STOLEN; |
| } else { |
| /* TDB- handle pkt gracefully */ |
| MV_NFP_STAT(pFpStats->sec_in_drop++); |
| return MV_NFP_DROP; |
| |
| } |
| } |
| mvOsPrintf("mvFpProcess: no SA found for ESP packet(spi=0x%x)\n", pEspHdr->spi); |
| } else { |
| /* outbound */ |
| pSpd = mvNfpSecSPDRuleFind(dip, sip, proto, dstPort, srcPort, MV_NFP_SEC_RULE_DB_OUT); |
| if (pSpd != NULL) { |
| switch (pSpd->actionType) { |
| case (MV_NFP_SEC_FWD): |
| break; |
| case (MV_NFP_SEC_SECURE): |
| status = mvNfpSecOutgoing(pPkt, pSpd->pSAEntry); |
| if (status == MV_OK) { |
| /* handled by cesa */ |
| MV_NFP_STAT(pFpStats->sec_out++); |
| return MV_NFP_STOLEN; |
| } else if (status == MV_OUT_OF_RANGE) { |
| /* slow path */ |
| MV_NFP_STAT(pFpStats->sec_out_slow++); |
| return MV_NFP_NONE; |
| } else { |
| /* drop packet */ |
| MV_NFP_STAT(pFpStats->sec_out_drop++); |
| return MV_NFP_DROP; |
| } |
| break; |
| case (MV_NFP_SEC_DROP): |
| MV_NFP_STAT(pFpStats->sec_out_drop++); |
| return MV_NFP_DROP; |
| break; |
| } |
| } |
| } |
| #endif |
| |
| #ifdef CONFIG_MV_ETH_NFP_NAT |
| proto = mvFpPortsGet(pIpHdr, &dstPort, &srcPort); |
| if (proto == MV_IP_PROTO_NULL) { |
| /* NAT not supported for this protocol */ |
| MV_NFP_STAT(pFpStats->nat_bad_proto++); |
| pDnatRule = NULL; |
| } else { |
| /* Lookup NAT database accordingly with 5 tuple key */ |
| pDnatRule = mvFpNatRuleFind(dip, sip, proto, dstPort, srcPort); |
| } |
| if (pDnatRule != NULL) { |
| if (pDnatRule->flags & MV_FP_DIP_CMD_MAP) |
| dip = pDnatRule->newIp; |
| if (pDnatRule->flags & MV_FP_DPORT_CMD_MAP) |
| dstPort = pDnatRule->newPort; |
| } else { |
| MV_NFP_STAT(pFpStats->dnat_not_found++); |
| } |
| |
| #endif /* CONFIG_MV_ETH_NFP_NAT */ |
| |
| pRt = mvFpRuleFind(dip, sip); |
| if (pRt == NULL) { |
| /* IP Routing rule is not found: go to Linux IP stack */ |
| MV_NFP_STAT(pFpStats->route_miss++); |
| goto out; |
| } |
| |
| MV_NFP_STAT(pFpStats->route_hit++); |
| |
| #ifdef CONFIG_MV_ETH_NFP_NAT |
| if ((pDnatRule != NULL) && (pDnatRule->flags & MV_FP_DNAT_CMD_MAP)) { |
| MV_NFP_STAT(pFpStats->dnat_found++); |
| pSnatRule = mvFpNatRuleFind(dip, sip, proto, dstPort, srcPort); |
| } else { |
| pSnatRule = pDnatRule; |
| } |
| |
| if ((pSnatRule != NULL) && (pSnatRule->flags & MV_FP_SNAT_CMD_MAP)) |
| MV_NFP_STAT(pFpStats->snat_found++); |
| else |
| MV_NFP_STAT(pFpStats->snat_not_found++); |
| |
| /* Check IP awareness */ |
| if ((pRt->routingInfo.aware_flags & MV_FP_DIP_CMD_MAP) && (pDnatRule == NULL)) { |
| MV_NFP_STAT(pFpStats->dnat_aware++); |
| goto out; |
| } |
| |
| if ((pRt->routingInfo.aware_flags & MV_FP_SIP_CMD_MAP) && (pSnatRule == NULL)) { |
| MV_NFP_STAT(pFpStats->snat_aware++); |
| goto out; |
| } |
| |
| /* Update packet accordingly with NAT rules */ |
| if ((pDnatRule != NULL) || (pSnatRule != NULL)) |
| mvFpNatPktUpdate(pIpHdr, pDnatRule, pSnatRule); |
| #endif /* CONFIG_MV_ETH_NFP_NAT */ |
| |
| ifIndex = pRt->routingInfo.outIfIndex; |
| |
| #ifdef CONFIG_MV_ETH_NFP_PPP |
| /* Encapsulate PPPoE on Tx */ |
| if (mvFpPppLookup(ifIndex)) { |
| if (pBuf->dataSize > 1514 + ETH_MV_HEADER_SIZE - ETH_FP_PPPOE_HDR) { |
| MV_NFP_STAT(pFpStats->ppp_tx_slow++); |
| goto out; |
| } |
| |
| MV_NFP_STAT(pFpStats->ppp_tx++); |
| |
| /* FIXME: pktSize is left unchanged */ |
| pBuf->bufAddrShift += ETH_FP_PPPOE_HDR; |
| pBuf->bufPhysAddr -= ETH_FP_PPPOE_HDR; |
| pBuf->bufVirtPtr -= ETH_FP_PPPOE_HDR; |
| pBuf->dataSize += ETH_FP_PPPOE_HDR; |
| pEth -= ETH_FP_PPPOE_HDR; |
| |
| /* -6B aligment from 32B boundary */ |
| { |
| MV_U32 *d = (MV_U32 *) pEth; |
| MV_U32 *s = pppOpen[ifIndex].pppInfo.u.u32; |
| |
| *(d++) = *(s++); |
| *(d++) = *(s++); |
| *(d++) = *(s++); |
| *(d++) = *(s++); |
| *(d++) = *(s++); |
| *(d++) = *(s++); |
| } |
| |
| /* update payload len */ |
| *(MV_U16 *) (pEth + 20) = htons(pBuf->dataSize - 14 - ETH_FP_PPPOE_HDR); |
| |
| mvFpDecTTL(pIpHdr); |
| #ifdef CONFIG_MV_ETH_NFP_NAT |
| if (pSnatRule) |
| mvFpCSumInc(pIpHdr, pSnatRule->srcIp, pSnatRule->newIp); |
| #endif |
| pPkt->status = 0; |
| ifIndex = pppOpen[ifIndex].pppInfo.if_eth; |
| |
| /* Flush and Invalidate 3rd cacheline */ |
| pEth = (MV_U32) pEth & ~(CPU_D_CACHE_LINE_SIZE - 1); |
| pEth += CPU_D_CACHE_LINE_SIZE * 2; |
| mvOsCacheLineFlushInv(NULL, pEth); |
| goto end; |
| } |
| #endif /* CONFIG_MV_ETH_NFP_PPP */ |
| |
| *(MV_U16 *) (pEth + 2) = *(MV_U16 *) (&pRt->routingInfo.dstMac[0]); |
| *(MV_U32 *) (pEth + 4) = *(MV_U32 *) (&pRt->routingInfo.dstMac[2]); |
| *(MV_U32 *) (pEth + 8) = *(MV_U32 *) (&pRt->routingInfo.srcMac[0]); |
| *(MV_U16 *) (pEth + 12) = *(MV_U16 *) (&pRt->routingInfo.srcMac[4]); |
| #ifdef CONFIG_MV_ETH_NFP_PPP |
| *(MV_U16 *) (pEth + 14) = 0x0008; |
| #endif |
| |
| pIpHdr->ttl--; |
| |
| #ifdef CONFIG_MV_ETH_NFP_TOS |
| pIpHdr->tos = pRt->routingInfo.dscp; |
| pPkt->txq = pRt->routingInfo.txq; |
| #endif |
| |
| #ifdef CONFIG_MV_ETH_NFP_FDB |
| /* find actual port inside bridge, otherwise br->xmit is called */ |
| if (mvFpFdbMember(ifIndex)) { |
| pFdb = mvFpFdbLookup(ifIndex, pEth + ETH_MV_HEADER_SIZE); |
| if (pFdb) { |
| MV_NFP_STAT(pFpStats->fdb_tx_found++); |
| return pFdb->fdbInfo.ifIndex; |
| } |
| } |
| #endif /* CONFIG_MV_ETH_NFP_FDB */ |
| |
| end: |
| return ifIndex; |
| out: |
| #ifdef CONFIG_MV_ETH_NFP_PPP |
| /* restore original packet */ |
| if (pBuf->bufAddrShift) { |
| pBuf->bufPhysAddr += pBuf->bufAddrShift; |
| pBuf->bufVirtPtr += pBuf->bufAddrShift; |
| pBuf->dataSize -= pBuf->bufAddrShift; |
| pBuf->bufAddrShift = 0; |
| MV_NFP_STAT(pFpStats->ppp_rx_slow++); |
| } |
| #endif |
| MV_NFP_STAT(pFpStats->slowpath++); |
| return -1; |
| } |
| |
| void mvFpStatsPrint(MV_FP_STATS *pFpStats) |
| { |
| #ifdef MV_FP_STATISTICS |
| mvOsPrintf("\n====================================================\n"); |
| mvOsPrintf(" NFP statistics"); |
| mvOsPrintf("\n-------------------------------\n"); |
| |
| mvOsPrintf("nfp_parsing....................%10u\n", pFpStats->parsing); |
| mvOsPrintf("nfp_process....................%10u\n", pFpStats->process); |
| mvOsPrintf("nfp_route_hit..................%10u\n", pFpStats->route_hit); |
| mvOsPrintf("nfp_route_miss.................%10u\n", pFpStats->route_miss); |
| mvOsPrintf("nfp_ethertype_unknown..........%10u\n", pFpStats->ethertype_unknown); |
| mvOsPrintf("nfp_ip_frag....................%10u\n", pFpStats->ip_frag); |
| mvOsPrintf("nfp_slowpath...................%10u\n", pFpStats->slowpath); |
| mvOsPrintf("nfp_multicast..................%10u\n", pFpStats->multicast); |
| mvOsPrintf("nfp_vlan_tagged................%10u\n", pFpStats->vlan_tagged); |
| mvOsPrintf("nfp_ttl_expired................%10u\n", pFpStats->ip_ttl_expired); |
| |
| #ifdef CONFIG_MV_ETH_NFP_NAT |
| mvOsPrintf("nfp_nat_bad_proto..............%10u\n", pFpStats->nat_bad_proto); |
| mvOsPrintf("nfp_nat_dnat_found.............%10u\n", pFpStats->dnat_found); |
| mvOsPrintf("nfp_nat_snat_found.............%10u\n", pFpStats->snat_found); |
| mvOsPrintf("nfp_nat_dnat_not_found.........%10u\n", pFpStats->dnat_not_found); |
| mvOsPrintf("nfp_nat_snat_not_found.........%10u\n", pFpStats->snat_not_found); |
| mvOsPrintf("nfp_nat_dnat_aware.............%10u\n", pFpStats->dnat_aware); |
| mvOsPrintf("nfp_nat_snat_aware.............%10u\n", pFpStats->snat_aware); |
| #endif /* CONFIG_MV_ETH_NFP_NAT */ |
| |
| #ifdef CONFIG_MV_ETH_NFP_FDB |
| mvOsPrintf("nfp_fdb_rx_local...............%10u\n", pFpStats->fdb_rx_local); |
| mvOsPrintf("nfp_fdb_rx_unknown.............%10u\n", pFpStats->fdb_rx_unknown); |
| mvOsPrintf("nfp_fdb_tx_found...............%10u\n", pFpStats->fdb_tx_found); |
| mvOsPrintf("nfp_fdb_bridged................%10u\n", pFpStats->fdb_bridged); |
| #endif /* CONFIG_MV_ETH_NFP_FDB */ |
| |
| #ifdef CONFIG_MV_ETH_NFP_PPP |
| mvOsPrintf("nfp_ppp_rx.....................%10u\n", pFpStats->ppp_rx); |
| mvOsPrintf("nfp_ppp_tx.....................%10u\n", pFpStats->ppp_tx); |
| mvOsPrintf("nfp_ppp_err....................%10u\n", pFpStats->ppp_err); |
| mvOsPrintf("nfp_ppp_rx_slow................%10u\n", pFpStats->ppp_rx_slow); |
| mvOsPrintf("nfp_ppp_tx_slow................%10u\n", pFpStats->ppp_tx_slow); |
| mvOsPrintf("nfp_ppp_rx_frag................%10u\n", pFpStats->ppp_rx_frag); |
| mvOsPrintf("nfp_ppp_tx_esp.................%10u\n", pFpStats->ppp_tx_esp); |
| |
| #endif /* CONFIG_MV_ETH_NFP_PPP */ |
| |
| #ifdef CONFIG_MV_ETH_NFP_SEC |
| mvOsPrintf("nfp_sec_in.....................%10u\n", pFpStats->sec_in); |
| mvOsPrintf("nfp_sec_in_drop................%10u\n", pFpStats->sec_in_drop); |
| mvOsPrintf("nfp_sec_out....................%10u\n", pFpStats->sec_out); |
| mvOsPrintf("nfp_sec_out_drop...............%10u\n", pFpStats->sec_out_drop); |
| mvOsPrintf("nfp_sec_out_slow...............%10u\n", pFpStats->sec_out_slow); |
| #endif /* CONFIG_MV_ETH_NFP_SEC */ |
| |
| memset(pFpStats, 0, sizeof(MV_FP_STATS)); |
| #endif /* MV_FP_STATISTICS */ |
| |
| mvOsPrintf("\n"); |
| mvOsPrintf("Routing rules: Set=%u, Update=%u, Delete=%u, maxDepth=%u\n", |
| nfpRuleSetCount, nfpRuleUpdateCount, nfpRuleDeleteCount, nfpHashMaxDepth); |
| |
| #ifdef CONFIG_MV_ETH_NFP_NAT |
| mvOsPrintf("\n"); |
| mvOsPrintf("NAT rules: Set=%u, Update=%u, Delete=%u, maxDepth=%u\n", |
| natRuleSetCount, natRuleUpdateCount, natRuleDeleteCount, natHashMaxDepth); |
| #endif /* CONFIG_MV_ETH_NFP_NAT */ |
| |
| #ifdef CONFIG_MV_ETH_NFP_FDB |
| mvOsPrintf("\n"); |
| mvOsPrintf("FDB rules: Set=%u, Update=%u, Delete=%u, maxDepth=%u\n", |
| fdbRuleSetCount, fdbRuleUpdateCount, fdbRuleDeleteCount, fdbHashMaxDepth); |
| #endif /* CONFIG_MV_ETH_NFP_FDB */ |
| } |