blob: 316c9da0e3c7340110a43e9fe811fceaec98bb55 [file] [log] [blame]
/*******************************************************************************
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 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.
*******************************************************************************/
/*******************************************************************************
* mv_nfp_mgr.c - Marvell Network Fast Processing Manager
*
* DESCRIPTION:
*
* Supported Features:
*
*******************************************************************************/
/* includes */
#include "mvCommon.h" /* Should be included before mvSysHwConfig */
#include "mvTypes.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <net/route.h>
#include <linux/if_arp.h>
#ifdef CONFIG_MV_ETH_NFP_NAT
#include <net/netfilter/nf_nat.h>
#endif
#include "mvDebug.h"
#include "mvOs.h"
#include "mvSysHwConfig.h"
#include "ctrlEnv/mvCtrlEnvLib.h"
#include "eth/nfp/mvNfp.h"
#include "mv_nfp_mgr.h"
/* defines */
#define FP_MAX_STR_SIZE 256
#define AGING_TIMER_PERIOD ((CONFIG_MV_ETH_NFP_AGING_TIMER)*HZ)
#define MV_FTP_CTRL_PORT 21
/* debug control */
#define FP_MGR_DEBUG
#undef FP_MGR_DEBUG
#define FP_MGR_DBG_OFF 0x0000
#define FP_MGR_DBG_INIT 0x0001
#define FP_MGR_DBG_CLR 0x0002
#define FP_MGR_DBG_ARP 0x0004
#define FP_MGR_DBG_ROUTE 0x0008
#define FP_MGR_DBG_NAT 0x0010
#define FP_MGR_DBG_FDB 0x0020
#define FP_MGR_DBG_PPP 0x0040
#define FP_MGR_DBG_ALL 0xffff
#ifdef FP_MGR_DEBUG
static unsigned int mv_fp_mgr_dbg = FP_MGR_DBG_ALL;
#define FP_MGR_DBG(FLG, X) if( (mv_fp_mgr_dbg & (FLG)) == (FLG) ) printk X
#else
#define FP_MGR_DBG(FLG, X)
#endif
#define MAX_MSG_SIZE 128 - 4
/* structure definitions */
#ifdef CONFIG_MV_ETH_NFP_NAT
#define MASQ_TARGET_NAME "MASQUERADE"
#define SNAT_TARGET_NAME "SNAT"
#define DNAT_TARGET_NAME "DNAT"
#define REDIRECT_TARGET_NAME "REDIRECT"
typedef struct _fp_iptables_nat_rule
{
struct _fp_iptables_nat_rule *next;
/* Relevant target names are: */
/* SNAT, DNAT, MASQUERADE, REDIRECT */
char target_name[XT_FUNCTION_MAXNAMELEN-1];
/* Relevant for SNAT/DNAT: */
__be32 sip, dip, smsk, dmsk;
char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
/* Protocol, 0 = ANY */
u_int16_t proto;
} FP_IPTABLES_NAT_RULE;
typedef struct _fp_user_nat_table
{
FP_IPTABLES_NAT_RULE *rule_chain;
} FP_USER_NAT_TABLE;
struct nat_rule_db {
MV_FP_NAT_RULE *rule_chain;
u32 max_size;
};
static struct nat_rule_db mgr_nat_rule_db;
#endif /* CONFIG_MV_ETH_NFP_NAT */
typedef struct _mv_fp_arp_rule {
struct _mv_fp_arp_rule *next;
int if_index;
u32 ip;
u8 mac[MV_MAC_ADDR_SIZE];
int confirmed;
} MV_FP_ARP_RULE;
struct rule_db {
MV_FP_RULE *rule_chain;
u32 max_size;
};
struct arp_rule_db {
MV_FP_ARP_RULE *rule_chain;
u32 max_size;
};
/* global variables */
static struct rule_db mgr_rule_db;
static struct arp_rule_db mgr_arp_rule_db;
static struct timer_list aging_timer;
static spinlock_t nfp_mgr_lock;
int fp_disable_flag = 1;
struct map_eth_devs *fp_eth_devs = NULL;
#ifdef CONFIG_MV_ETH_NFP_FDB
struct fdb_rule_db {
MV_FP_FDB_RULE *rule_chain;
u32 max_size;
};
static struct fdb_rule_db mgr_fdb_rule_db;
#endif /* CONFIG_MV_ETH_NFP_FDB */
#ifdef CONFIG_MV_ETH_NFP_NAT
/* we have two tables: previous and current */
static FP_USER_NAT_TABLE fp_user_nat_table[2];
/* user NAT table: 0 or 1 */
int curr_table, old_table;
#endif /* CONFIG_MV_ETH_NFP_NAT */
#ifdef CONFIG_MV_ETH_NFP_DUAL
int mgr_to_fp_chan, fp_to_mgr_chan;
#endif /* CONFIG_MV_ETH_NFP_DUAL */
static void fp_arp_rule_print(const MV_FP_ARP_RULE *rule);
#ifdef CONFIG_MV_ETH_NFP_NAT
static int fp_nat_db_clear_and_update(void);
#endif
static void __exit fp_exit_module(void)
{
fp_rule_db_clear();
fp_arp_db_clear();
if (fp_eth_devs != NULL)
kfree(fp_eth_devs);
#ifdef CONFIG_MV_ETH_NFP_DUAL
mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_RULE_DB_DESTROY_OPCODE, NULL, 0);
#else
mvFpRuleDbDestroy();
#endif /* CONFIG_MV_ETH_NFP_DUAL */
#ifdef CONFIG_MV_ETH_NFP_NAT
fp_nat_db_clear();
#ifdef CONFIG_MV_ETH_NFP_DUAL
mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_NAT_DB_DESTROY_OPCODE, NULL, 0);
#else
mvFpNatDbDestroy();
#endif /* CONFIG_MV_ETH_NFP_DUAL */
#endif /* CONFIG_MV_ETH_NFP_NAT */
#ifdef CONFIG_MV_ETH_NFP_FDB
fp_fdb_db_clear();
mvFpFdbDestroy();
#endif /* CONFIG_MV_ETH_NFP_FDB */
}
static int is_valid_index(int if_index)
{
if( (if_index < 0) || (if_index >= ETH_FP_IFINDEX_MAX) )
{
/*printk("if_index %d is OUT of RANGE\n", if_index);*/
return 0;
}
if( (fp_eth_devs == NULL) ||
(fp_eth_devs[if_index].if_type == MV_FP_IF_INV) )
return 0;
return 1;
}
/* helper functions */
struct net_device *mv_dev_get_by_name(const char *name)
{
/* TODO: may need to take a semaphore here, see documentation of __dev_get_by_name */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
return __dev_get_by_name(name);
#else
return __dev_get_by_name(&init_net, name);
#endif
}
struct net_device *mv_dev_get_by_index(int ifindex)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
return __dev_get_by_index(ifindex);
#else
return __dev_get_by_index(&init_net, ifindex);
#endif
}
/* Find and return the first matching rule in the ARP Database */
static MV_FP_ARP_RULE* fp_arp_rule_find(u32 ip)
{
MV_FP_ARP_RULE *curr_rule;
for (curr_rule = mgr_arp_rule_db.rule_chain; curr_rule != NULL; curr_rule = curr_rule->next) {
if (curr_rule->ip == ip) {
return curr_rule;
}
}
return NULL;
}
static MV_FP_RULE* fp_rule_find(u32 sip, u32 dip)
{
MV_FP_RULE *curr_rule;
for (curr_rule = mgr_rule_db.rule_chain; curr_rule != NULL; curr_rule = curr_rule->next) {
if( (curr_rule->routingInfo.srcIp == sip) &&
(curr_rule->routingInfo.dstIp == dip) ) {
return curr_rule;
}
}
return NULL;
}
static int ip_in_same_network(u32 ip1, u32 ip2, u32 mask)
{
if ((mask != 0) && ((ip1 & mask) == (ip2 & mask)))
return 1;
return 0;
}
#ifdef CONFIG_MV_ETH_NFP_NAT
static MV_FP_NAT_RULE* fp_nat_rule_find(u32 sip, u32 dip, u16 sport, u16 dport, u8 proto)
{
MV_FP_NAT_RULE *curr_rule;
for (curr_rule = mgr_nat_rule_db.rule_chain; curr_rule != NULL; curr_rule = curr_rule->next) {
if ( curr_rule->srcIp == sip &&
curr_rule->dstIp == dip &&
curr_rule->srcPort == sport &&
curr_rule->dstPort == dport &&
curr_rule->proto == proto) {
return curr_rule;
}
}
return NULL;
}
/* Update NFP Rule "NAT awareness" according to the given iptables rule */
static void update_awareness(FP_IPTABLES_NAT_RULE *nat_rule, MV_FP_RULE *fp_rule, int add_del_flag)
{
int in_dev_ifindex = -1, out_dev_ifindex = -1;
struct net_device *dev;
int inport_avail = 0, outport_avail = 0;
if (!strcmp(nat_rule->target_name, MASQ_TARGET_NAME) || !strcmp(nat_rule->target_name, SNAT_TARGET_NAME)) {
if ((nat_rule->outiface != NULL) && strcmp(nat_rule->outiface, "")) {
dev = mv_dev_get_by_name(nat_rule->outiface);
if (dev)
out_dev_ifindex = dev->ifindex;
if (is_valid_index(out_dev_ifindex)) {
outport_avail = 1;
}
}
}
else if (!strcmp(nat_rule->target_name, REDIRECT_TARGET_NAME) || !strcmp(nat_rule->target_name, DNAT_TARGET_NAME)) {
if ((nat_rule->iniface != NULL) && strcmp(nat_rule->iniface, "")) {
dev = mv_dev_get_by_name(nat_rule->iniface);
if (dev)
in_dev_ifindex = dev->ifindex;
if (is_valid_index(in_dev_ifindex)) {
inport_avail = 1;
}
}
}
if (!strcmp(nat_rule->target_name, MASQ_TARGET_NAME) || !strcmp(nat_rule->target_name, SNAT_TARGET_NAME)) {
if ( (ip_in_same_network(fp_rule->routingInfo.srcIp, nat_rule->sip, nat_rule->smsk)) ||
(outport_avail && fp_rule->routingInfo.outIfIndex == out_dev_ifindex) ) {
/* update SNAT awareness */
if (add_del_flag) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("adding snat awareness\n"));
fp_rule->mgmtInfo.snat_aware_refcnt++;
fp_rule->routingInfo.aware_flags |= MV_FP_SNAT_CMD_MAP;
}
else {
fp_rule->mgmtInfo.snat_aware_refcnt--;
if (fp_rule->mgmtInfo.snat_aware_refcnt == 0) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("deleting snat awareness\n"));
fp_rule->routingInfo.aware_flags &= ~MV_FP_SNAT_CMD_MAP;
}
}
}
if (outport_avail && fp_rule->routingInfo.inIfIndex == out_dev_ifindex) {
/* update DNAT awareness, because this is the reverse direction of the SNAT */
if (add_del_flag) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("adding dnat awareness\n"));
fp_rule->mgmtInfo.dnat_aware_refcnt++;
fp_rule->routingInfo.aware_flags |= MV_FP_DNAT_CMD_MAP;
}
else {
fp_rule->mgmtInfo.dnat_aware_refcnt--;
if (fp_rule->mgmtInfo.dnat_aware_refcnt == 0) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("deleting dnat awareness\n"));
fp_rule->routingInfo.aware_flags &= ~MV_FP_DNAT_CMD_MAP;
}
}
}
}
else if (!strcmp(nat_rule->target_name, REDIRECT_TARGET_NAME) || !strcmp(nat_rule->target_name, DNAT_TARGET_NAME)) {
if ( (ip_in_same_network(fp_rule->routingInfo.dstIp, nat_rule->dip, nat_rule->dmsk)) ||
(inport_avail && fp_rule->routingInfo.inIfIndex == in_dev_ifindex) ) {
/* update DNAT awareness */
if (add_del_flag) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("adding dnat awareness\n"));
fp_rule->mgmtInfo.dnat_aware_refcnt++;
fp_rule->routingInfo.aware_flags |= MV_FP_DNAT_CMD_MAP;
}
else {
fp_rule->mgmtInfo.dnat_aware_refcnt--;
if (fp_rule->mgmtInfo.dnat_aware_refcnt == 0) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("deleting dnat awareness\n"));
fp_rule->routingInfo.aware_flags &= ~MV_FP_DNAT_CMD_MAP;
}
}
}
if (inport_avail && fp_rule->routingInfo.outIfIndex == in_dev_ifindex) {
/* update SNAT awareness, because this is the reverse direction of the DNAT */
if (add_del_flag) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("adding snat awareness\n"));
fp_rule->mgmtInfo.snat_aware_refcnt++;
fp_rule->routingInfo.aware_flags |= MV_FP_SNAT_CMD_MAP;
}
else {
fp_rule->mgmtInfo.snat_aware_refcnt--;
if (fp_rule->mgmtInfo.snat_aware_refcnt == 0) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("deleting snat awareness\n"));
fp_rule->routingInfo.aware_flags &= ~MV_FP_SNAT_CMD_MAP;
}
}
}
}
}
#endif /* CONFIG_MV_ETH_NFP_NAT */
/* When setting a rule, check if it needs to be aware of any currently existing user NAT rules */
static void fp_new_rule_awareness(MV_FP_RULE *rule)
{
#ifdef CONFIG_MV_ETH_NFP_NAT
FP_IPTABLES_NAT_RULE *nat_rule;
#endif
rule->mgmtInfo.snat_aware_refcnt = 0;
rule->mgmtInfo.dnat_aware_refcnt = 0;
rule->routingInfo.aware_flags = 0;
#ifdef CONFIG_MV_ETH_NFP_NAT
nat_rule = fp_user_nat_table[1-curr_table].rule_chain;
while (nat_rule != NULL) {
update_awareness(nat_rule, rule, 1);
nat_rule = nat_rule->next;
}
#endif /* CONFIG_MV_ETH_NFP_NAT */
}
/* Clear all NFP databases and update them with the contents of */
/* the NFP Manager databases. */
static int fp_db_clear_and_update(void)
{
int status = 0;
unsigned long flags;
MV_FP_RULE *curr_rule;
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_RULE_DB_CLEAR_OPCODE, NULL, 0);
#else
status = mvFpRuleDbClear();
#endif /* CONFIG_MV_ETH_NFP_DUAL */
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = mgr_rule_db.rule_chain;
while (curr_rule != NULL) {
if (!is_zero_ether_addr(curr_rule->routingInfo.dstMac)) {
fp_new_rule_awareness(curr_rule);
#ifdef CONFIG_MV_ETH_NFP_DUAL
status |= mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_RULE_SET_OPCODE, curr_rule, sizeof(MV_FP_RULE));
#else
status |= mvFpRuleSet(curr_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
}
curr_rule = curr_rule->next;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
#ifdef CONFIG_MV_ETH_NFP_DUAL
static void aging_timer_function(unsigned long data)
{
unsigned long flags;
MV_FP_ARP_RULE *curr_arp_rule;
MV_FP_RULE *curr_rule;
int rule_index = 0;
ARP_RULE_AGING_MSG arp_rule_aging_msg;
RULE_AGING_MSG rule_aging_msg;
NAT_RULE_AGING_MSG nat_rule_aging_msg;
#ifdef CONFIG_MV_ETH_NFP_NAT
MV_FP_NAT_RULE *curr_nat_rule;
#endif
static int current_arp_rule_position = 0;
static int current_rule_position = 0;
static int current_nat_rule_position = 0;
/* Collect ARP information from DB */
spin_lock_irqsave(&nfp_mgr_lock, flags);
/* move pointer to rule to the next group of rules we want to handle */
for ( curr_arp_rule = mgr_arp_rule_db.rule_chain, rule_index = 0;
(curr_arp_rule != NULL) && (rule_index < (current_arp_rule_position*AGING_QUANTUM));
curr_arp_rule = curr_arp_rule->next, rule_index++);
rule_index = 0;
memset(&arp_rule_aging_msg, 0, sizeof(ARP_RULE_AGING_MSG));
while ((curr_arp_rule != NULL) && (rule_index < AGING_QUANTUM)) {
arp_rule_aging_msg.num_tuples++;
arp_rule_aging_msg.info[rule_index].ip = curr_arp_rule->ip;
curr_arp_rule = curr_arp_rule->next;
rule_index++;
}
current_arp_rule_position++;
if( (curr_arp_rule == NULL) ||
((current_arp_rule_position*AGING_QUANTUM) >= mgr_arp_rule_db.max_size))
current_arp_rule_position = 0;
mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_MAX_ARP_COUNT_GET_OPCODE, &arp_rule_aging_msg, sizeof(arp_rule_aging_msg));
/* Collect routing information from DB */
/* move pointer to rule to the next group of rules we want to handle */
for ( curr_rule = mgr_rule_db.rule_chain, rule_index = 0;
(curr_rule != NULL) && (rule_index < (current_rule_position*AGING_QUANTUM));
curr_rule = curr_rule->next, rule_index++);
rule_index = 0;
memset(&rule_aging_msg, 0, sizeof(RULE_AGING_MSG));
while ((curr_rule != NULL) && (rule_index < AGING_QUANTUM)) {
rule_aging_msg.num_tuples++;
rule_aging_msg.info[rule_index].sip = curr_rule->routingInfo.srcIp;
rule_aging_msg.info[rule_index].dip = curr_rule->routingInfo.dstIp;
curr_rule = curr_rule->next;
rule_index++;
}
current_rule_position++;
if ((curr_rule == NULL) || ((current_rule_position*AGING_QUANTUM) >= mgr_rule_db.max_size))
current_rule_position = 0;
mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_ROUTE_COUNT_GET_OPCODE, &rule_aging_msg, sizeof(rule_aging_msg));
#ifdef CONFIG_MV_ETH_NFP_NAT
/* Collect NAT information from DB */
/* move pointer to rule to the next group of rules we want to handle */
for ( curr_nat_rule = mgr_nat_rule_db.rule_chain, rule_index = 0;
(curr_nat_rule != NULL) && (rule_index < (current_nat_rule_position*AGING_QUANTUM));
curr_nat_rule = curr_nat_rule->next, rule_index++);
rule_index = 0;
memset(&nat_rule_aging_msg, 0, sizeof(NAT_RULE_AGING_MSG));
while ((curr_nat_rule != NULL) && (rule_index < AGING_QUANTUM)) {
nat_rule_aging_msg.num_tuples++;
nat_rule_aging_msg.info[rule_index].sip = curr_nat_rule->srcIp;
nat_rule_aging_msg.info[rule_index].dip = curr_nat_rule->dstIp;
nat_rule_aging_msg.info[rule_index].sport = curr_nat_rule->srcPort;
nat_rule_aging_msg.info[rule_index].dport = curr_nat_rule->dstPort;
nat_rule_aging_msg.info[rule_index].proto = curr_nat_rule->proto;
curr_nat_rule = curr_nat_rule->next;
rule_index++;
}
current_nat_rule_position++;
if( (curr_nat_rule == NULL) ||
( (current_nat_rule_position*AGING_QUANTUM) >= mgr_nat_rule_db.max_size) )
current_nat_rule_position = 0;
mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_NAT_COUNT_GET_OPCODE, &nat_rule_aging_msg, sizeof(nat_rule_aging_msg));
#endif /* CONFIG_MV_ETH_NFP_NAT */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
aging_timer.expires = jiffies + AGING_TIMER_PERIOD;
add_timer(&aging_timer);
}
#else /* not CONFIG_MV_ETH_NFP_DUAL */
static void aging_timer_function(unsigned long data)
{
unsigned long flags;
MV_FP_ARP_RULE *curr_arp_rule;
MV_FP_RULE *curr_rule;
#ifdef CONFIG_MV_ETH_NFP_NAT
MV_FP_NAT_RULE *curr_nat_rule;
#endif
spin_lock_irqsave(&nfp_mgr_lock, flags);
/* Collect routing information from DB */
curr_rule = mgr_rule_db.rule_chain;
while (curr_rule != NULL)
{
curr_rule->mgmtInfo.new_count = mvFpRouteCountGet(curr_rule->routingInfo.srcIp,
curr_rule->routingInfo.dstIp);
if(curr_rule->mgmtInfo.new_count != curr_rule->mgmtInfo.old_count)
{
/* Lookup ARP entry to confirm */
curr_arp_rule = fp_arp_rule_find(curr_rule->routingInfo.srcIp);
if(curr_arp_rule != NULL)
{
/* ARP Entry Found - confirmed */
curr_arp_rule->confirmed = 1;
}
else
{
/* ARP Entry Not Found - Confirm default gateway for incoming interface */
curr_arp_rule = fp_arp_rule_find(fp_eth_devs[curr_rule->routingInfo.inIfIndex].def_gtw_ip);
if(curr_arp_rule != NULL)
{
curr_arp_rule->confirmed = 1;
}
}
}
curr_rule = curr_rule->next;
}
#ifdef CONFIG_MV_ETH_NFP_NAT
/* Collect NAT information from DB */
curr_nat_rule = mgr_nat_rule_db.rule_chain;
while (curr_nat_rule != NULL)
{
curr_nat_rule->new_count = mvFpNatCountGet(curr_nat_rule->srcIp,
curr_nat_rule->dstIp,
curr_nat_rule->srcPort,
curr_nat_rule->dstPort,
curr_nat_rule->proto);
curr_nat_rule = curr_nat_rule->next;
}
#endif /* CONFIG_MV_ETH_NFP_NAT */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
aging_timer.expires = jiffies + AGING_TIMER_PERIOD;
add_timer(&aging_timer);
}
#endif /* CONFIG_MV_ETH_NFP_DUAL */
/* Return ARP rule confirmation status */
int fp_is_arp_confirmed(u32 ip, const u8 *mac)
{
unsigned long flags;
MV_FP_ARP_RULE *curr_rule;
int confirmed;
if (fp_disable_flag == 1)
return 0;
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = mgr_arp_rule_db.rule_chain;
while (curr_rule != NULL) {
if( (curr_rule->ip == ip) && (memcmp(curr_rule->mac, mac, MV_MAC_ADDR_SIZE) == 0) )
{
confirmed = curr_rule->confirmed;
curr_rule->confirmed = 0;
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return confirmed;
}
curr_rule = curr_rule->next;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Return routing rule confirmation status */
int fp_is_route_confirmed(u32 src_ip, u32 dst_ip, int iif, int oif)
{
unsigned long flags;
MV_FP_RULE *curr_rule;
int confirmed = 0;
if (fp_disable_flag == 1)
return 0;
if( !is_valid_index(iif) || !is_valid_index(oif) )
return 0;
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = mgr_rule_db.rule_chain;
while (curr_rule != NULL) {
if( (curr_rule->routingInfo.srcIp == src_ip) &&
(curr_rule->routingInfo.dstIp == dst_ip) )
{
if(curr_rule->mgmtInfo.new_count != curr_rule->mgmtInfo.old_count)
{
confirmed = 1;
curr_rule->mgmtInfo.old_count = curr_rule->mgmtInfo.new_count;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return confirmed;
}
curr_rule = curr_rule->next;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
#ifdef CONFIG_MV_ETH_NFP_DUAL
void fp_rcv_info_msg(int from_cpu, u16 opcode, void* p_msg, u8 size)
{
int i = 0;
ARP_RULE_AGING_MSG *arp_rule_aging_msg;
RULE_AGING_MSG *rule_aging_msg;
NAT_RULE_AGING_MSG *nat_rule_aging_msg;
MV_FP_ARP_RULE *curr_arp_rule;
MV_FP_RULE *curr_rule;
MV_FP_NAT_RULE *curr_nat_rule;
switch(opcode)
{
case MV_FP_MAX_ARP_COUNT_REPLY_OPCODE:
arp_rule_aging_msg = (ARP_RULE_AGING_MSG *)p_msg;
for (i = 0; i < arp_rule_aging_msg->num_tuples; i++) {
curr_arp_rule = fp_arp_rule_find(arp_rule_aging_msg->info[i].ip);
if (curr_arp_rule != NULL)
fp_update_arp_rule_aging(curr_arp_rule, arp_rule_aging_msg->info[i].count);
}
break;
case MV_FP_ROUTE_COUNT_REPLY_OPCODE:
rule_aging_msg = (RULE_AGING_MSG *)p_msg;
for (i = 0; i < rule_aging_msg->num_tuples; i++) {
curr_rule = fp_rule_find(rule_aging_msg->info[i].sip,
rule_aging_msg->info[i].dip);
if (curr_rule != NULL)
fp_update_rule_aging(curr_rule, rule_aging_msg->info[i].count);
}
break;
case MV_FP_NAT_COUNT_REPLY_OPCODE:
nat_rule_aging_msg = (NAT_RULE_AGING_MSG *)p_msg;
for (i = 0; i < nat_rule_aging_msg->num_tuples; i++) {
curr_nat_rule = fp_nat_rule_find(nat_rule_aging_msg->info[i].sip,
nat_rule_aging_msg->info[i].dip,
nat_rule_aging_msg->info[i].sport,
nat_rule_aging_msg->info[i].dport,
nat_rule_aging_msg->info[i].proto);
if (curr_nat_rule != NULL)
fp_update_nat_rule_aging(curr_nat_rule, nat_rule_aging_msg->info[i].count);
}
break;
default:
printk("fp_rcv_info_msg: unknown message type\n");
break;
}
}
#endif /* CONFIG_MV_ETH_NFP_DUAL */
/* Initialize NFP Manager */
int fp_mgr_init(void)
{
int i = 0;
#ifdef CONFIG_MV_ETH_NFP_DUAL
int status = 0;
MV_FP_INIT_STRUCT fpdb_init_struct;
#endif /* CONFIG_MV_ETH_NFP_DUAL */
fp_disable_flag = 1;
fp_eth_devs = kmalloc((ETH_FP_IFINDEX_MAX * sizeof(struct map_eth_devs)), GFP_ATOMIC);
if (fp_eth_devs == NULL)
return -ENOMEM;
for (i = 0; i < ETH_FP_IFINDEX_MAX; i++) {
fp_eth_devs[i].if_type = MV_FP_IF_INV;
fp_eth_devs[i].dev = NULL;
fp_eth_devs[i].def_gtw_ip = 0;
}
spin_lock_init(&nfp_mgr_lock);
memset(&aging_timer, 0, sizeof(struct timer_list));
init_timer(&aging_timer);
aging_timer.function = aging_timer_function;
aging_timer.data = -1;
aging_timer.expires = jiffies + AGING_TIMER_PERIOD;
#ifdef CONFIG_MV_ETH_NFP_DUAL
mgr_to_fp_chan = mvIpcChanCreate(MV_IPC_HOST_ID, MV_IPC_COPROCESSOR_ID,
MV_IPC_RX_CB_MODE, MAX_MSG_SIZE, 64 /*mgr_rule_db.max_size*/);
fp_to_mgr_chan = mvIpcChanCreate(MV_IPC_COPROCESSOR_ID, MV_IPC_HOST_ID,
MV_IPC_RX_CB_MODE, MAX_MSG_SIZE, 64 /*mgr_rule_db.max_size*/);
status = mvIpcChanRcvCbSet(fp_to_mgr_chan, fp_rcv_info_msg);
if ((status != 0) || (mgr_to_fp_chan < 0) || (fp_to_mgr_chan < 0))
{
printk("fp_mgr_init: Error setting up IPC channels\n");
return status;
}
/* Connect Channel Dispatch function */
mvIpcDbConnect(mvIpcChanDoorbellGet(fp_to_mgr_chan), mvIpcChanDispatchIsr, (void*)fp_to_mgr_chan);
/* Enable Doorbell interrupt */
MV_IPC_DOORBELL_ENABLE(MV_IPC_HOST_ID, mvIpcChanDoorbellGet(fp_to_mgr_chan));
memset(&fpdb_init_struct, 0, sizeof(MV_FP_INIT_STRUCT));
fpdb_init_struct.mgr_to_fp_chan = mgr_to_fp_chan;
fpdb_init_struct.fp_to_mgr_chan = fp_to_mgr_chan;
fpdb_init_struct.rule_db_size = mgr_rule_db.max_size;
status = mvIpcChanMsgSend( mvIpcTxSharedChanGet(MV_IPC_COPROCESSOR_ID),
MV_SERVICE_NFP_ID, MV_FP_RULE_DB_INIT_OPCODE,
&fpdb_init_struct, sizeof(MV_FP_INIT_STRUCT));
if (status != 0)
{
printk("fp_mgr_init: Error sending NFP initialization message\n");
return status;
}
return 0;
#else
/* Note at this stage the Manager DB is already initialized so mgr_rule_db.max_size is set */
mvFpRuleDbInit(mgr_rule_db.max_size);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
return 0;
}
static int fp_mgr_get_iftype(int if_index)
{
if (!fp_eth_devs ||
(if_index < 0) ||
(if_index >= ETH_FP_IFINDEX_MAX))
return MV_FP_IF_INV;
return fp_eth_devs[if_index].if_type;
}
/* Register a network interface that works with NFP */
int fp_mgr_if_register( int if_index, MV_FP_IF_TYPE if_type,
struct net_device* dev)
{
/* sanity checks */
if( (fp_eth_devs == NULL) ||
(if_index < 0) || (if_index >= ETH_FP_IFINDEX_MAX) )
return -1;
fp_eth_devs[if_index].dev = dev;
fp_eth_devs[if_index].if_type = if_type;
return 0;
}
int fp_mgr_if_unregister(int if_index)
{
if (fp_eth_devs ||
(if_index < 0) ||
(if_index >= ETH_FP_IFINDEX_MAX))
return -1;
memset(&fp_eth_devs[if_index], 0, sizeof(struct map_eth_devs));
return 0;
}
/* This function is called when user-space tool disables NFP */
/* All databases are cleared, and learning of new rules is disabled */
/* for all types of rules: static, dynamic, arp, routing etc. */
void fp_mgr_disable(void)
{
if (fp_disable_flag == 0) {
fp_disable_flag = 1;
del_timer(&aging_timer);
}
printk("Network Fast Processing Disabled\n");
}
/* This function is called when user-space tool enables NFP */
int fp_mgr_enable(void)
{
int status;
if (fp_disable_flag == 1) {
fp_disable_flag = 0;
status = fp_db_clear_and_update();
#ifdef CONFIG_MV_ETH_NFP_NAT
status |= fp_nat_db_clear_and_update();
#endif /* CONFIG_MV_ETH_NFP_NAT */
aging_timer.expires = jiffies + AGING_TIMER_PERIOD;
add_timer(&aging_timer);
printk("Network Fast Processing Enabled\n");
return status;
}
printk("Network Fast Processing Enabled\n");
return 0;
}
/* This function is called when user-space tool asks about NFP status (enabled/disabled) */
int fp_mgr_status(void)
{
if (fp_disable_flag == 0)
printk("Network Fast Processing is currently Enabled\n");
else
printk("Network Fast Processing is currently Disabled\n");
return 0;
}
/* Initialize NFP Rule Database (Routing + ARP information table) */
int fp_rule_db_init(u32 db_size)
{
FP_MGR_DBG(FP_MGR_DBG_INIT, ("FP_MGR: Initializing Rule Database\n"));
mgr_rule_db.rule_chain = NULL;
mgr_rule_db.max_size = db_size;
/* Initialization of the NFP HAL Database is called from fp_mgr_init */
return 0;
}
/* Initialize NFP Manager ARP Database */
int fp_arp_db_init(u32 db_size)
{
FP_MGR_DBG(FP_MGR_DBG_INIT, ("FP_MGR: Initializing ARP Rule Database\n"));
mgr_arp_rule_db.rule_chain = NULL;
mgr_arp_rule_db.max_size = db_size;
return 0;
}
/* Clear NFP Rule Database (Routing + ARP information table) */
int fp_rule_db_clear(void)
{
int status = 0;
unsigned long flags;
MV_FP_RULE *curr_rule;
MV_FP_RULE *tmp_rule;
FP_MGR_DBG(FP_MGR_DBG_CLR, ("FP_MGR: Clearing Rule Database\n"));
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = mgr_rule_db.rule_chain;
while (curr_rule != NULL) {
tmp_rule = curr_rule;
curr_rule = curr_rule->next;
kfree(tmp_rule);
}
mgr_rule_db.rule_chain = NULL;
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_RULE_DB_CLEAR_OPCODE, NULL, 0);
#else
status = mvFpRuleDbClear();
#endif /* CONFIG_MV_ETH_NFP_DUAL */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
/* Clear NFP Manager ARP Database */
int fp_arp_db_clear(void)
{
unsigned long flags;
MV_FP_ARP_RULE *curr_rule;
MV_FP_ARP_RULE *tmp_rule;
FP_MGR_DBG(FP_MGR_DBG_CLR, ("FP_MGR: Clearing ARP Rule Database\n"));
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = mgr_arp_rule_db.rule_chain;
while (curr_rule != NULL) {
tmp_rule = curr_rule;
curr_rule = curr_rule->next;
kfree(tmp_rule);
}
mgr_arp_rule_db.rule_chain = NULL;
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Print NFP Rule Database (Routing + ARP information table) */
int fp_rule_db_print(MV_FP_OP_TYPE op)
{
int status = 0;
unsigned long flags;
MV_FP_RULE *curr_rule;
spin_lock_irqsave(&nfp_mgr_lock, flags);
if (op == MV_FP_MANAGER) {
printk("Printing NFP Manager Rule Database: \n");
curr_rule = mgr_rule_db.rule_chain;
while (curr_rule != NULL) {
mvFpRulePrint(curr_rule);
curr_rule = curr_rule->next;
}
}
else {
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_RULE_DB_PRINT_OPCODE, NULL, 0);
#else
status = mvFpRuleDbPrint();
#endif /* CONFIG_MV_ETH_NFP_DUAL */
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
/* Print ARP Rule */
static void fp_arp_rule_print(const MV_FP_ARP_RULE *rule)
{
printk("ARP Rule: ");
printk("IP = %u.%u.%u.%u, ", NIPQUAD(rule->ip));
printk("MAC = %02X:%02X:%02X:%02X:%02X:%02X\n", rule->mac[0], rule->mac[1], rule->mac[2],
rule->mac[3], rule->mac[4], rule->mac[5]);
}
/* Print NFP Manager ARP Database */
int fp_arp_db_print(void)
{
unsigned long flags;
MV_FP_ARP_RULE *curr_rule;
spin_lock_irqsave(&nfp_mgr_lock, flags);
printk("Printing NFP Manager ARP Rule Database: \n");
curr_rule = mgr_arp_rule_db.rule_chain;
while (curr_rule != NULL) {
fp_arp_rule_print(curr_rule);
curr_rule = curr_rule->next;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Copy 6 bytes. Warning - doesn't perform any checks on memory, just copies */
static inline void mac_addr_copy(u8 *dst, const u8 *src)
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
dst[4] = src[4];
dst[5] = src[5];
}
/* Update the Routing + ARP database with new ARP information: */
/* Go over the database and update the destination MAC address */
/* for each entry with a matching default gateway IP address. */
static int fp_rule_db_arp_update(u32 ip, const u8 *mac)
{
int status = 0;
unsigned long flags;
MV_FP_RULE *curr_rule;
spin_lock_irqsave(&nfp_mgr_lock, flags);
for (curr_rule = mgr_rule_db.rule_chain; curr_rule != NULL; curr_rule = curr_rule->next)
{
if( (curr_rule->routingInfo.defGtwIp == ip) &&
(curr_rule->mgmtInfo.ruleType != MV_FP_STATIC_RULE))
{
mac_addr_copy(curr_rule->routingInfo.dstMac, mac);
if (!is_zero_ether_addr(mac))
{
/* Now we have a full rule - we can update the NFP database */
fp_new_rule_awareness(curr_rule);
#ifdef CONFIG_MV_ETH_NFP_DUAL
status |= mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_RULE_SET_OPCODE, curr_rule, sizeof(MV_FP_RULE));
#else
status |= mvFpRuleSet(curr_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
}
else
{
#ifdef CONFIG_MV_ETH_NFP_DUAL
status |= mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_RULE_DELETE_OPCODE, curr_rule, sizeof(MV_FP_RULE));
#else
status |= mvFpRuleDelete(curr_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
}
}
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
/* Set ARP information in the ARP database, and update the Routing + ARP database if necessary */
/* If we have a complete rule, update the NFP database */
int fp_arp_info_set(int if_index, u32 ip, const u8 *mac)
{
unsigned long flags;
MV_FP_ARP_RULE *curr_rule;
MV_FP_ARP_RULE *new_rule;
if(!is_valid_index(if_index))
return 0;
FP_MGR_DBG(FP_MGR_DBG_ARP, ("nfp_mgr Set ARP: if=%d, IP=%u.%u.%u.%u, MAC=%02X:%02X:%02X:%02X:%02X:%02X\n",
if_index, NIPQUAD(ip), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]));
spin_lock_irqsave(&nfp_mgr_lock, flags);
for (curr_rule = mgr_arp_rule_db.rule_chain; curr_rule != NULL; curr_rule = curr_rule->next) {
if (curr_rule->ip == ip) {
/* We've found a match with an existing entry. Let's update it */
mac_addr_copy(curr_rule->mac, mac);
curr_rule->confirmed = 1;
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return fp_rule_db_arp_update(ip, mac);
}
}
/* We haven't found a matching existing entry. Let's add a new one */
new_rule = kmalloc(sizeof(MV_FP_ARP_RULE), GFP_ATOMIC);
if (new_rule == NULL) {
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return -ENOMEM;
}
new_rule->ip = ip;
mac_addr_copy(new_rule->mac, mac);
new_rule->confirmed = 1;
new_rule->if_index = if_index;
new_rule->next = NULL;
if (mgr_arp_rule_db.rule_chain == NULL) {
/* There is no rule in this table yet */
mgr_arp_rule_db.rule_chain = new_rule;
}
else {
/* Let's add this rule at the tail of the list */
curr_rule = mgr_arp_rule_db.rule_chain;
while (curr_rule->next != NULL)
curr_rule = curr_rule->next;
curr_rule->next = new_rule;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return fp_rule_db_arp_update(ip, mac);
}
/* Delete ARP information from the ARP database, and update the Routing + ARP database if necessary */
/* If a rule became incomplete, update the NFP database */
int fp_arp_info_delete(u32 ip)
{
unsigned long flags;
MV_FP_ARP_RULE *curr_rule;
MV_FP_ARP_RULE *prev_rule;
u8 mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
spin_lock_irqsave(&nfp_mgr_lock, flags);
prev_rule = NULL;
for ( curr_rule = mgr_arp_rule_db.rule_chain;
curr_rule != NULL;
prev_rule = curr_rule, curr_rule = curr_rule->next) {
if (curr_rule->ip == ip) {
/* Note there should only be one matching entry for this IP address */
if (prev_rule == NULL)
mgr_arp_rule_db.rule_chain = curr_rule->next;
else
prev_rule->next = curr_rule->next;
kfree(curr_rule);
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
FP_MGR_DBG(FP_MGR_DBG_ARP,
("nfp_mgr: ARP delete: IP=%u.%u.%u.%u, MAC=%02X:%02X:%02X:%02X:%02X:%02X\n",
NIPQUAD(ip), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]));
return fp_rule_db_arp_update(ip, mac);
}
}
/* We haven't found a matching entry, so nothing to do */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Set a new rule or update an existing one */
/* When looking for an existing rule, search */
/* a match for SIP, DIP, and default gateway IP */
/* Rule type is also taken into account: */
/* a new static rule can overrode an existing rule of any type, */
/* while a new dynamic rule can only override an existing dynamic rule */
int fp_rule_set(MV_FP_RULE *rule)
{
unsigned long flags;
MV_FP_RULE *curr_rule;
MV_FP_RULE *new_rule;
int status = 0;
/* Static rules are supplied with GbE port numbers, while */
/* dynamic rules are supplied with Linux interface indices. */
if (rule->mgmtInfo.ruleType == MV_FP_DYNAMIC_RULE) {
struct net_device *in_dev, *out_dev;
/* We are not interested in this rule if the input interface or */
/* the output interface is not one of our GbE interfaces. */
if ( !is_valid_index(rule->routingInfo.inIfIndex) ||
!is_valid_index(rule->routingInfo.outIfIndex))
{
return 0;
}
in_dev = mv_dev_get_by_index(rule->routingInfo.inIfIndex);
out_dev = mv_dev_get_by_index(rule->routingInfo.outIfIndex);
/* check MTU compatability */
if (in_dev && out_dev) {
#ifdef CONFIG_MV_ETH_NFP_PPP
if ((in_dev->type == ARPHRD_PPP) || (out_dev->type == ARPHRD_PPP))
;
else
#endif
if ((in_dev->mtu != out_dev->mtu) ||
(out_dev->mtu > ETH_CSUM_MAX_BYTE_COUNT))
{
/* printk("%s: invalid mtu %d, %d\n", __FUNCTION__,
in_dev->mtu, out_dev->mtu); */
return 0;
}
}
}
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = fp_rule_find(rule->routingInfo.srcIp, rule->routingInfo.dstIp);
if(curr_rule != NULL)
{
if( (curr_rule->mgmtInfo.ruleType == MV_FP_DYNAMIC_RULE) ||
(rule->mgmtInfo.ruleType == MV_FP_STATIC_RULE) )
{
/* Update existing rule, but only if the current rule is a dynamic rule, */
/* or the new rule is a static rule which overrides the current rule. */
mvFpRuleCopy(curr_rule, rule);
if (!is_zero_ether_addr(curr_rule->routingInfo.dstMac)) {
/* Now we have a full rule - we can update the NFP database */
fp_new_rule_awareness(curr_rule);
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_RULE_SET_OPCODE, curr_rule, sizeof(MV_FP_RULE));
#else
status = mvFpRuleSet(curr_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
}
}
/* else, we have found a matching entry, but the current rule is static and */
/* the new rule is dynamic, so static rule remains unchanged, and we can exit here. */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
/* We haven't found a matching existing entry. Let's add a new one */
new_rule = kmalloc(sizeof(MV_FP_RULE), GFP_ATOMIC);
if (new_rule == NULL) {
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return -ENOMEM;
}
mvFpRuleCopy(new_rule, rule);
new_rule->next = NULL;
if (mgr_rule_db.rule_chain == NULL) {
/* There is no rule in this table yet */
mgr_rule_db.rule_chain = new_rule;
}
else {
/* Let's add this rule at the tail of the list */
curr_rule = mgr_rule_db.rule_chain;
while (curr_rule->next != NULL)
curr_rule = curr_rule->next;
curr_rule->next = new_rule;
}
if (!is_zero_ether_addr(new_rule->routingInfo.dstMac)) {
/* Now we have a full rule - we can update the the NFP database */
fp_new_rule_awareness(new_rule);
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_RULE_SET_OPCODE, new_rule, sizeof(MV_FP_RULE));
#else
status = mvFpRuleSet(new_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
/* Delete an existing rule */
/* When looking for an existing rule, search */
/* a match for SIP, DIP */
int fp_rule_delete(u32 src_ip, u32 dst_ip, MV_FP_RULE_TYPE rule_type)
{
unsigned long flags;
MV_FP_RULE *curr_rule;
MV_FP_RULE *prev_rule;
int status = 0;
spin_lock_irqsave(&nfp_mgr_lock, flags);
prev_rule = NULL;
for(curr_rule = mgr_rule_db.rule_chain;
curr_rule != NULL;
prev_rule = curr_rule, curr_rule = curr_rule->next) {
if( (curr_rule->routingInfo.srcIp == src_ip) &&
(curr_rule->routingInfo.dstIp == dst_ip) &&
(curr_rule->mgmtInfo.ruleType == rule_type) ) {
/* Note there should only be one matching entry, if any */
if (prev_rule == NULL) {
mgr_rule_db.rule_chain = curr_rule->next;
}
else {
prev_rule->next = curr_rule->next;
}
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_RULE_DELETE_OPCODE, curr_rule, sizeof(MV_FP_RULE));
#else
status = mvFpRuleDelete(curr_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
kfree(curr_rule);
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
}
/* We haven't found a matching entry, so nothing to do */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/*
* Set Routing ToS for 2-tuple connection
*/
#ifdef CONFIG_MV_ETH_NFP_TOS
int fp_routing_tos_set(u32 src_ip, u32 dst_ip, u32 dscp, u32 txq)
{
MV_FP_RULE* rule;
unsigned long flags;
FP_MGR_DBG(FP_MGR_DBG_ROUTE,
("FP_MGR: Setting ToS Information: SIP = "NIPQUAD_FMT ", DIP = "NIPQUAD_FMT", TOS=0x%x TxQ=%d\n",
NIPQUAD(src_ip), NIPQUAD(dst_ip), dscp, txq));
if (txq >= CONFIG_MV_ETH_TX_Q_NUM)
return -EINVAL;
spin_lock_irqsave(&nfp_mgr_lock, flags);
rule = fp_rule_find(src_ip,dst_ip);
if (rule) {
rule->routingInfo.dscp = (u8)dscp;
rule->routingInfo.txq = (u8)txq;
/* The rule is already applied, update engine db */
if (!is_zero_ether_addr(rule->routingInfo.dstMac))
mvFpToSSet(rule);
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
#endif
/* Set Routing information received from the IP stack when a new Routing cache entry is created */
/* Look for matching ARP information to complete the rule */
/* If we have a complete rule, update the NFP database */
int fp_routing_info_set(u32 src_ip, u32 dst_ip, u32 def_gtw_ip, int ingress_if, int egress_if)
{
MV_FP_RULE new_rule;
MV_FP_ARP_RULE *arp_rule;
struct net_device *dev;
if (fp_disable_flag == 1)
return 0;
FP_MGR_DBG(FP_MGR_DBG_ROUTE, ("FP_MGR: Setting Routing Information\n"));
FP_MGR_DBG(FP_MGR_DBG_ROUTE,
("SIP = "NIPQUAD_FMT ", DIP = "NIPQUAD_FMT", GTW = "NIPQUAD_FMT", In IF = %d, Out IF = %d\n",
NIPQUAD(src_ip), NIPQUAD(dst_ip),
NIPQUAD(def_gtw_ip), ingress_if, egress_if));
if( !is_valid_index(ingress_if) || !is_valid_index(egress_if) )
return 0;
memset(&new_rule, 0, sizeof(MV_FP_RULE));
new_rule.mgmtInfo.actionType = MV_FP_ROUTE_CMD;
new_rule.mgmtInfo.ruleType = MV_FP_DYNAMIC_RULE;
new_rule.routingInfo.srcIp = src_ip;
new_rule.routingInfo.dstIp = dst_ip;
new_rule.routingInfo.defGtwIp = def_gtw_ip;
dev = mv_dev_get_by_index(egress_if);
if (dev)
mac_addr_copy(new_rule.routingInfo.srcMac, dev->dev_addr);
fp_eth_devs[egress_if].def_gtw_ip = def_gtw_ip;
/* Note: searching destination MAC according to default gateway IP */
if ((arp_rule = fp_arp_rule_find(def_gtw_ip)) != NULL)
mac_addr_copy(new_rule.routingInfo.dstMac, arp_rule->mac);
new_rule.routingInfo.inIfIndex = ingress_if;
new_rule.routingInfo.outIfIndex = egress_if;
#ifdef CONFIG_MV_ETH_NFP_PPP
/* DA is defined by pppoe session */
if (dev->type == ARPHRD_PPP)
new_rule.routingInfo.dstMac[5] = 0xAA;
#endif
return fp_rule_set(&new_rule);
}
/* Delete Routing information from the Routing + ARP database, and update the NFP database */
int fp_routing_info_delete(u32 src_ip, u32 dst_ip, int iif, int oif)
{
if (fp_disable_flag == 1)
return 0;
FP_MGR_DBG(FP_MGR_DBG_ROUTE, ("FP_MGR: Deleting Routing Information\n"));
FP_MGR_DBG(FP_MGR_DBG_ROUTE, ( "SIP = %u.%u.%u.%u, DIP = %u.%u.%u.%u\n",
NIPQUAD(src_ip), NIPQUAD(dst_ip)));
if( !is_valid_index(iif) || !is_valid_index(oif) )
return 0;
return fp_rule_delete(src_ip, dst_ip, MV_FP_DYNAMIC_RULE);
}
#ifdef CONFIG_MV_ETH_NFP_NAT
/* Initialize NFP NAT Rule Database (SNAT + DNAT table) */
int fp_nat_db_init(u32 db_size)
{
FP_MGR_DBG(FP_MGR_DBG_INIT, ("FP_MGR: Initializing NAT Rule Database\n"));
mgr_nat_rule_db.rule_chain = NULL;
mgr_nat_rule_db.max_size = db_size;
#ifdef CONFIG_MV_ETH_NFP_DUAL
return mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_NAT_DB_INIT_OPCODE,
&(mgr_nat_rule_db.max_size), sizeof(mgr_nat_rule_db.max_size));
#else
return mvFpNatDbInit(mgr_nat_rule_db.max_size);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
}
/* Clear NFP NAT Rule Database (SNAT + DNAT table) */
int fp_nat_db_clear(void)
{
int status = 0;
unsigned long flags;
MV_FP_NAT_RULE *curr_rule;
MV_FP_NAT_RULE *tmp_rule;
FP_MGR_DBG(FP_MGR_DBG_CLR, ("FP_MGR: Clearing NAT Rule Database\n"));
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = mgr_nat_rule_db.rule_chain;
while (curr_rule != NULL) {
tmp_rule = curr_rule;
curr_rule = curr_rule->next;
kfree(tmp_rule);
}
mgr_nat_rule_db.rule_chain = NULL;
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_NAT_DB_CLEAR_OPCODE, NULL, 0);
#else
status = mvFpNatDbClear();
#endif /* CONFIG_MV_ETH_NFP_DUAL */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
static int fp_nat_db_clear_and_update(void)
{
int status = 0;
unsigned long flags;
MV_FP_NAT_RULE *curr_nat_rule = mgr_nat_rule_db.rule_chain;
spin_lock_irqsave(&nfp_mgr_lock, flags);
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_NAT_DB_CLEAR_OPCODE, NULL, 0);
#else
status = mvFpNatDbClear();
#endif /* CONFIG_MV_ETH_NFP_DUAL */
while (curr_nat_rule != NULL) {
#ifdef CONFIG_MV_ETH_NFP_DUAL
status |= mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_NAT_RULE_SET_OPCODE, curr_nat_rule, sizeof(MV_FP_NAT_RULE));
#else
status |= mvFpNatRuleSet(curr_nat_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
curr_nat_rule = curr_nat_rule->next;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
/* Print NFP NAT Rule Database (SNAT + DNAT table) */
int fp_nat_db_print(MV_FP_OP_TYPE op)
{
int status = 0;
unsigned long flags;
MV_FP_NAT_RULE *curr_rule;
spin_lock_irqsave(&nfp_mgr_lock, flags);
if (op == MV_FP_MANAGER) {
printk("Printing NFP Manager NAT Rule Database: \n");
curr_rule = mgr_nat_rule_db.rule_chain;
while (curr_rule != NULL) {
mvFpNatRulePrint(curr_rule);
curr_rule = curr_rule->next;
}
}
else {
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID, MV_FP_NAT_DB_PRINT_OPCODE, NULL, 0);
#else
status = mvFpNatDbPrint();
#endif /* CONFIG_MV_ETH_NFP_DUAL */
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
static int is_relevant_nat_protocol(u8 proto)
{
/* For now we only knw how to handle NAT rules for TCP/UDP/ICMP/Zero_Hop protocols */
/* so we disregard everything else */
if ( (proto == MV_IP_PROTO_TCP) ||
(proto == MV_IP_PROTO_UDP) ||
(proto == MV_IP_PROTO_ICMP) ||
(proto == MV_IP_PROTO_ZERO_HOP))
return 1;
return 0;
}
static int is_relevant_port(u16 src_port, u16 dst_port)
{
/* We want to pass FTP control stream packets to Linux */
if ( (MV_16BIT_BE(src_port) != MV_FTP_CTRL_PORT)
&& (MV_16BIT_BE(dst_port) != MV_FTP_CTRL_PORT))
return 1;
return 0;
}
/* Set a new NAT rule, or update an existing one */
int fp_nat_info_set(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 proto,
u32 new_src_ip, u32 new_dst_ip, u16 new_src_port, u16 new_dst_port,
int if_index, enum nf_nat_manip_type maniptype)
{
unsigned long flags;
MV_FP_NAT_RULE *curr_rule;
MV_FP_NAT_RULE *new_rule;
int status = 0;
/* we are not interested in NAT rules that do not involve our interfaces */
if (!is_valid_index(if_index)) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("FP_MGR: invalid_index %d\n", if_index));
return 0;
}
if (!is_relevant_nat_protocol(proto)) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("FP_MGR: irrelevant nat protocol %d\n", proto));
return 0;
}
if (!is_relevant_port(src_port, dst_port)) {
FP_MGR_DBG(FP_MGR_DBG_NAT, ("FP_MGR: irrelevant port %d %d\n", src_port, dst_port));
return 0;
}
if ((src_ip == new_src_ip) && (dst_ip == new_dst_ip) &&
(src_port == new_src_port) && (dst_port == new_dst_port) )
{
return 0;
}
FP_MGR_DBG(FP_MGR_DBG_NAT, ("FP_MGR: Setting NAT Information\n"));
FP_MGR_DBG(FP_MGR_DBG_NAT,
("SIP=%u.%u.%u.%u, DIP=%u.%u.%u.%u, Proto=%u, SPort=%u, DPort=%u, if_index=%d\n",
NIPQUAD(src_ip), NIPQUAD(dst_ip), proto, MV_16BIT_BE(src_port),
MV_16BIT_BE(dst_port), if_index));
FP_MGR_DBG(FP_MGR_DBG_NAT,
("\tNewSIP=%u.%u.%u.%u, NewDIP=%u.%u.%u.%u, NewSPort=%u, NewDPort=%u\n\n",
NIPQUAD(new_src_ip), NIPQUAD(new_dst_ip),
MV_16BIT_BE(new_src_port), MV_16BIT_BE(new_dst_port)));
spin_lock_irqsave(&nfp_mgr_lock, flags);
for (curr_rule = mgr_nat_rule_db.rule_chain; curr_rule != NULL; curr_rule = curr_rule->next) {
if( curr_rule->srcIp == src_ip &&
curr_rule->dstIp == dst_ip &&
curr_rule->srcPort == src_port &&
curr_rule->dstPort == dst_port &&
curr_rule->proto == proto)
{
/* Updating existing rule */
curr_rule->new_count = 0;
curr_rule->old_count = 0;
if (maniptype == IP_NAT_MANIP_DST) {
curr_rule->newIp = new_dst_ip;
curr_rule->newPort = new_dst_port;
}
else {
curr_rule->newIp = new_src_ip;
curr_rule->newPort = new_src_port;
}
if (maniptype == IP_NAT_MANIP_DST) {
if ((dst_port != 0) && (dst_port != new_dst_port))
curr_rule->flags |= MV_FP_DNAT_CMD_MAP;
else
curr_rule->flags |= MV_FP_DIP_CMD_MAP;
}
else {
if ((src_port != 0) && (src_port != new_src_port))
curr_rule->flags |= MV_FP_SNAT_CMD_MAP;
else
curr_rule->flags |= MV_FP_SIP_CMD_MAP;
}
/* Now we have a full rule - we can update the NFP database */
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_NAT_RULE_SET_OPCODE, curr_rule, sizeof(MV_FP_NAT_RULE));
#else
status = mvFpNatRuleSet(curr_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
}
/* We haven't found a matching existing entry. Let's add a new one */
new_rule = kmalloc(sizeof(MV_FP_NAT_RULE), GFP_ATOMIC);
if (new_rule == NULL) {
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return -ENOMEM;
}
new_rule->srcIp = src_ip;
new_rule->dstIp = dst_ip;
new_rule->srcPort = src_port;
new_rule->dstPort = dst_port;
new_rule->proto = proto;
new_rule->old_count = 0;
new_rule->new_count = 0;
if (maniptype == IP_NAT_MANIP_DST) {
new_rule->newIp = new_dst_ip;
new_rule->newPort = new_dst_port;
}
else {
new_rule->newIp = new_src_ip;
new_rule->newPort = new_src_port;
}
if (maniptype == IP_NAT_MANIP_DST) {
if ((dst_port != 0) && (dst_port != new_dst_port))
new_rule->flags = MV_FP_DNAT_CMD_MAP;
else
new_rule->flags = MV_FP_DIP_CMD_MAP;
}
else {
if ((src_port != 0) && (src_port != new_src_port))
new_rule->flags = MV_FP_SNAT_CMD_MAP;
else
new_rule->flags = MV_FP_SIP_CMD_MAP;
}
new_rule->next = NULL;
if (mgr_nat_rule_db.rule_chain == NULL) {
/* There is no rule in this table yet */
mgr_nat_rule_db.rule_chain = new_rule;
}
else {
/* Let's add this rule at the tail of the list */
curr_rule = mgr_nat_rule_db.rule_chain;
while (curr_rule->next != NULL)
curr_rule = curr_rule->next;
curr_rule->next = new_rule;
}
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_NAT_RULE_SET_OPCODE, new_rule, sizeof(MV_FP_NAT_RULE));
#else
status = mvFpNatRuleSet(new_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
/* Delete a NAT rule */
int fp_nat_info_delete(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 proto)
{
unsigned long flags;
MV_FP_NAT_RULE *curr_rule;
MV_FP_NAT_RULE *prev_rule;
int status = 0;
spin_lock_irqsave(&nfp_mgr_lock, flags);
prev_rule = NULL;
for ( curr_rule = mgr_nat_rule_db.rule_chain;
curr_rule != NULL;
prev_rule = curr_rule, curr_rule = curr_rule->next) {
if ( curr_rule->srcIp == src_ip &&
curr_rule->dstIp == dst_ip &&
curr_rule->srcPort == src_port &&
curr_rule->dstPort == dst_port &&
curr_rule->proto == proto) {
if (prev_rule == NULL)
mgr_nat_rule_db.rule_chain = curr_rule->next;
else
prev_rule->next = curr_rule->next;
#ifdef CONFIG_MV_ETH_NFP_DUAL
status = mvIpcChanMsgSend(mgr_to_fp_chan, MV_SERVICE_NFP_ID,
MV_FP_NAT_RULE_DELETE_OPCODE, curr_rule, sizeof(MV_FP_NAT_RULE));
#else
status = mvFpNatRuleDelete(curr_rule);
#endif /* CONFIG_MV_ETH_NFP_DUAL */
kfree(curr_rule);
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return status;
}
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Return NAT rule confirmation status */
int fp_is_nat_confirmed(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 proto)
{
unsigned long flags;
MV_FP_NAT_RULE *curr_rule;
int confirmed = 0;
if (fp_disable_flag == 1)
return 0;
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = mgr_nat_rule_db.rule_chain;
while (curr_rule != NULL) {
if( (curr_rule->srcIp == src_ip) &&
(curr_rule->dstIp == dst_ip) &&
(curr_rule->srcPort == src_port) &&
(curr_rule->dstPort == dst_port) &&
(curr_rule->proto == proto))
{
if(curr_rule->new_count != curr_rule->old_count)
{
curr_rule->old_count = curr_rule->new_count;
confirmed = 1;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return confirmed;
}
curr_rule = curr_rule->next;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Set or clear the "NAT Aware" flags according to user's newly added or deleted iptables rule */
static void fp_handle_user_nat_rule(FP_IPTABLES_NAT_RULE *nat_rule, int add_del_flag)
{
/* This NAT rule that the user added can affect routing entries based on */
/* input interface, output interface, SIP, DIP */
/* For now, protocol and source/dest port are not taken into consideration */
MV_FP_RULE *curr_rule;
unsigned long flags;
spin_lock_irqsave(&nfp_mgr_lock, flags);
curr_rule = mgr_rule_db.rule_chain;
while (curr_rule != NULL) {
update_awareness(nat_rule, curr_rule, add_del_flag);
mvFpRuleAwareSet(curr_rule);
curr_rule = curr_rule->next;
}
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
}
/* Initialize our user NAT tables, mirroring iptables NAT rules table */
void init_fp_user_nat_tables(void)
{
fp_user_nat_table[0].rule_chain = NULL;
fp_user_nat_table[1].rule_chain = NULL;
curr_table = 0;
old_table = 1 - curr_table;
}
/* Add a user NAT rule to our mirror table */
int add_fp_user_nat_rule(struct ipt_entry *e, int table)
{
struct ipt_entry_target *t = NULL;
FP_IPTABLES_NAT_RULE *curr_rule = NULL, *new_rule = NULL;
t = ipt_get_target(e);
/* Disregard targets that do not interest us */
if ( strcmp(t->u.kernel.target->name, MASQ_TARGET_NAME) &&
strcmp(t->u.kernel.target->name, REDIRECT_TARGET_NAME) &&
strcmp(t->u.kernel.target->name, SNAT_TARGET_NAME) &&
strcmp(t->u.kernel.target->name, DNAT_TARGET_NAME))
return 0;
new_rule = kmalloc(sizeof(FP_IPTABLES_NAT_RULE), GFP_ATOMIC);
if (new_rule == NULL)
return -ENOMEM;
memset(new_rule, 0, sizeof(FP_IPTABLES_NAT_RULE));
strcpy(new_rule->target_name, t->u.kernel.target->name);
strcpy(new_rule->iniface, e->ip.iniface);
strcpy(new_rule->outiface, e->ip.outiface);
new_rule->sip = e->ip.src.s_addr;
new_rule->dip = e->ip.dst.s_addr;
new_rule->smsk = e->ip.smsk.s_addr;
new_rule->dmsk = e->ip.dmsk.s_addr;
new_rule->proto = e->ip.proto;
new_rule->next = NULL;
if (fp_user_nat_table[table].rule_chain == NULL) {
/* There is no rule in this table yet */
fp_user_nat_table[table].rule_chain = new_rule;
}
else {
/* Let's add this rule at the tail of the list */
curr_rule = fp_user_nat_table[table].rule_chain;
while (curr_rule->next != NULL)
curr_rule = curr_rule->next;
curr_rule->next = new_rule;
}
return 0;
}
/* Clear our user NAT rule table */
void clear_fp_user_nat_table(int table)
{
FP_IPTABLES_NAT_RULE *curr_rule;
FP_IPTABLES_NAT_RULE *tmp_rule;
curr_rule = fp_user_nat_table[table].rule_chain;
while (curr_rule != NULL) {
tmp_rule = curr_rule;
curr_rule = curr_rule->next;
kfree(tmp_rule);
}
fp_user_nat_table[table].rule_chain = NULL;
}
/* Check if two user NAT rules are equal */
static int rules_are_equal(FP_IPTABLES_NAT_RULE *r1, FP_IPTABLES_NAT_RULE *r2)
{
if (strcmp(r1->target_name, r2->target_name))
return 0;
if ( (r1->sip != r2->sip) || (r1->dip != r2->dip) ||
(r1->smsk != r2->smsk) || (r2->dmsk != r2->dmsk) ||
(r1->proto != r2->proto))
return 0;
if (strcmp(r1->iniface, r2->iniface) || strcmp(r1->outiface, r2->outiface))
return 0;
return 1;
}
/* Check if a rule exists in our user NAT table */
static int rule_exists_in_table(FP_IPTABLES_NAT_RULE *rule, int table)
{
FP_IPTABLES_NAT_RULE *curr_rule;
curr_rule = fp_user_nat_table[table].rule_chain;
while (curr_rule != NULL) {
if (rules_are_equal(rule, curr_rule))
return 1;
curr_rule = curr_rule->next;
}
return 0;
}
/* Compare the two mirror tables to discover which new rules were added, and which rules were deleted */
void compare_fp_user_nat_tables(void)
{
FP_IPTABLES_NAT_RULE *curr_rule;
/* Stage 1: */
/* Compare current table to previous one to detect newly added rules */
/* For each rule in curr_table: */
/* If it exists also in old_table, we don't care */
/* If not, it is new */
curr_rule = fp_user_nat_table[curr_table].rule_chain;
while (curr_rule != NULL) {
if (!rule_exists_in_table(curr_rule, old_table)) {
fp_handle_user_nat_rule(curr_rule, 1); /* pass 1 for adding a new rule */
}
curr_rule = curr_rule->next;
}
/* Stage 2: */
/* Compare previous table to current one to detect newly deleted rules */
/* For each rule in old_table: */
/* If it exists also in curr_table, we don't care */
/* If not, it was deleted */
curr_rule = fp_user_nat_table[old_table].rule_chain;
while (curr_rule != NULL) {
if (!rule_exists_in_table(curr_rule, curr_table)) {
fp_handle_user_nat_rule(curr_rule, 0); /* pass 0 for deleting a rule */
}
curr_rule = curr_rule->next;
}
}
#endif /* CONFIG_MV_ETH_NFP_NAT */
module_exit(fp_exit_module);
#ifdef CONFIG_MV_ETH_NFP_FDB
/* Initialize Fast Bridge Rule Database */
int fp_fdb_db_init(u32 db_size)
{
FP_MGR_DBG(FP_MGR_DBG_INIT, ("FP_MGR: Initializing Bridge Rule Database\n"));
mgr_fdb_rule_db.rule_chain = NULL;
mgr_fdb_rule_db.max_size = db_size;
return mvFpFdbInit(db_size);
}
/* Set Bridging information in the FDB database */
int fp_fdb_info_set(u32 if_bridge, u32 if_index, const u8 *mac, int is_local)
{
unsigned long flags;
MV_FP_FDB_RULE rule;
if (is_local && fp_mgr_if_register(if_bridge, MV_FP_IF_BRG, mv_dev_get_by_index(if_bridge))) {
FP_MGR_DBG(FP_MGR_DBG_FDB, ("FDB_MNG: failed to register bridge=%d\n", if_bridge));
}
memset(&rule, 0, sizeof(MV_FP_FDB_RULE));
rule.mgmtInfo.actionType = MV_FP_BRIDGE_CMD;
rule.mgmtInfo.ruleType = MV_FP_DYNAMIC_RULE;
mac_addr_copy(rule.fdbInfo.mac, mac);
rule.fdbInfo.ifIndex = if_index;
rule.fdbInfo.bridge = if_bridge;
rule.fdbInfo.flags = (unsigned short)is_local;
spin_lock_irqsave(&nfp_mgr_lock, flags);
mvFpFdbRuleSet(&rule);
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Delete bridging information in the FDB database */
int fp_fdb_info_del(u32 if_bridge, u32 if_index, const u8 *mac, int is_local)
{
unsigned long flags;
MV_FP_FDB_RULE rule;
if (is_local && !fp_mgr_if_unregister(if_bridge))
FP_MGR_DBG(FP_MGR_DBG_FDB, ("FDB_MNG: unregister bridge=%d\n", if_bridge));
memset(&rule, 0, sizeof(MV_FP_FDB_RULE));
mac_addr_copy(rule.fdbInfo.mac, mac);
rule.fdbInfo.ifIndex = if_index;
rule.fdbInfo.bridge = if_bridge;
rule.fdbInfo.flags = (unsigned short)is_local;
/* has expired */
spin_lock_irqsave(&nfp_mgr_lock, flags);
if (!is_local && (mvFpFdbRuleAge(&rule) > 0)) {
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 1;
}
mvFpFdbRuleDel(&rule);
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Clear Fast Path FDB Rule Database */
int fp_fdb_db_clear(void)
{
unsigned long flags;
spin_lock_irqsave(&nfp_mgr_lock, flags);
mvFpFdbClear();
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
/* Print Fast Path FDB Rule Database */
int fp_fdb_db_print(MV_FP_OP_TYPE op)
{
unsigned long flags;
spin_lock_irqsave(&nfp_mgr_lock, flags);
mvFpFdbPrint();
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
#endif /* CONFIG_MV_ETH_NFP_FDB */
#ifdef CONFIG_MV_ETH_NFP_PPP
/* Initialize Rule Database */
int fp_ppp_db_init(void)
{
FP_MGR_DBG(FP_MGR_DBG_INIT, ("FP_MGR: Initializing PPPoE Rule Database\n"));
return mvFpPppInit();
}
EXPORT_SYMBOL(fp_ppp_db_init);
/* Set information in the PPP database:
* @ifindex - virtual interface, e.g. ppp0
* @egress - physical interface, e.g. eth0
* @sid - session id
* @mac - destination mac
*/
int fp_ppp_info_set(u32 if_ppp, u32 if_eth, u16 sid, u8 *mac, u32 channel)
{
unsigned long flags;
MV_FP_PPP_RULE rule;
/* the physical interface is not NFP capable */
if (if_eth && fp_mgr_get_iftype(if_eth) != MV_FP_IF_INT)
goto out;
if (if_ppp && fp_mgr_if_register(if_ppp, MV_FP_IF_PPP, mv_dev_get_by_index(if_ppp)))
goto out;
memset(&rule, 0, sizeof(MV_FP_PPP_RULE));
rule.mgmtInfo.actionType = MV_FP_PPP_CMD;
rule.mgmtInfo.ruleType = MV_FP_DYNAMIC_RULE;
if (mac)
mac_addr_copy(rule.pppInfo.u.ppp.da, mac);
if (if_eth) {
struct net_device* dev = mv_dev_get_by_index(if_eth);
mac_addr_copy(rule.pppInfo.u.ppp.sa, dev->dev_addr);
rule.pppInfo.u.ppp.tag = 0; /* mvHeader */;
}
rule.pppInfo.u.ppp.ethertype = 0x6488;
rule.pppInfo.u.ppp.version = 0x0011;
rule.pppInfo.u.ppp.session = sid;
rule.pppInfo.u.ppp.proto = 0x2100;
rule.pppInfo.if_ppp = if_ppp;
rule.pppInfo.if_eth = if_eth;
rule.pppInfo.channel = channel;
spin_lock_irqsave(&nfp_mgr_lock, flags);
mvFpPppRuleSet(&rule);
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
out:
if (if_ppp >= ETH_FP_IFINDEX_MAX)
printk("ppp device ifindex (%d) is out of range, please reboot your system\n", if_ppp);
FP_MGR_DBG(FP_MGR_DBG_PPP, ("PPP_MNG: failed to register ppp=%d over eth=%d\n", if_ppp, if_eth));
return 1;
}
EXPORT_SYMBOL(fp_ppp_info_set);
/* Delete bridging information in the PPP database */
int fp_ppp_info_del(u32 channel)
{
unsigned long flags;
MV_FP_PPP_RULE rule;
memset(&rule, 0, sizeof(MV_FP_PPP_RULE));
rule.pppInfo.channel = channel;
spin_lock_irqsave(&nfp_mgr_lock, flags);
mvFpPppRuleDel(&rule);
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
EXPORT_SYMBOL(fp_ppp_info_del);
/* Clear Fast Path PPP Rule Database */
int fp_ppp_db_clear(void)
{
unsigned long flags;
spin_lock_irqsave(&nfp_mgr_lock, flags);
mvFpPppClear();
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
EXPORT_SYMBOL(fp_ppp_db_clear);
/* Print Fast Path PPP Rule Database */
int fp_ppp_db_print(MV_FP_OP_TYPE op)
{
unsigned long flags;
spin_lock_irqsave(&nfp_mgr_lock, flags);
mvFpPppPrint();
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
#endif /* CONFIG_MV_ETH_NFP_PPP */
#ifdef CONFIG_MV_ETH_NFP_SEC
/* Print Fast Path SEC Rule Database */
int fp_sec_db_print(MV_FP_OP_TYPE op)
{
unsigned long flags;
spin_lock_irqsave(&nfp_mgr_lock, flags);
mvNfpSecDbPrint();
spin_unlock_irqrestore(&nfp_mgr_lock, flags);
return 0;
}
#endif /* CONFIG_MV_ETH_NFP_SEC */