blob: ec93599592057bed65a6c6f3963ebf7e77db941a [file] [log] [blame]
/*
* Copyright (c) 2009 Mindspeed Technologies, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
*/
#ifdef CFG_MACVLAN
#include "layer2.h"
#include "module_macvlan.h"
#include "gemac.h"
#include "system.h"
#include "fe.h"
#include "module_hidrv.h"
#include "fpp.h"
#include "module_ethernet.h"
/* This function returns total macvlan entries configured in a given hash index */
static int Macvlan_Get_Hash_Entries(int macvlan_hash_index)
{
int tot_macvlan_entries = 0;
PMacvlanEntry pMacvlanEntry, pFirstEntry;
pMacvlanEntry = pFirstEntry = macvlan_cache[macvlan_hash_index];
if (pMacvlanEntry)
{
do {
tot_macvlan_entries++;
pMacvlanEntry = pMacvlanEntry->next;
}while(pMacvlanEntry != pFirstEntry);
}
return tot_macvlan_entries;
}
/* This function fills the snapshot of Macvlan entries in a given hash index */
static int Macvlan_Get_Hash_Snapshot( int macvlan_hash_index, int macvlan_tot_entries, PMacvlanCmd pMacvlanSnapshot)
{
int tot_macvlan_entries = 0;
PMacvlanEntry pMacvlanEntry, pFirstEntry;
pMacvlanEntry = pFirstEntry = macvlan_cache[macvlan_hash_index];
if((pMacvlanEntry) && (macvlan_tot_entries > 0))
{
do
{
SFL_memcpy(pMacvlanSnapshot->macaddr, pMacvlanEntry->MACaddr, ETHER_ADDR_LEN);
SFL_memcpy(pMacvlanSnapshot->macvlan_ifname, get_onif_name(pMacvlanEntry->itf.index), 12);
SFL_memcpy(pMacvlanSnapshot->phys_ifname, get_onif_name(pMacvlanEntry->itf.phys->index), 12);
pMacvlanSnapshot++;
tot_macvlan_entries++;
pMacvlanEntry = pMacvlanEntry->next;
macvlan_tot_entries--;
}while(pMacvlanEntry != pFirstEntry);
}
return tot_macvlan_entries;
}
/* This function creates the snapshot memory and returns the next macvlan entry
from the snapshot of the Macvlan entries of a single hash to the caller */
static int Macvlan_Get_Next_Hash_Entry(PMacvlanCmd pMacvlanCmd, int reset_action)
{
PMacvlanCmd pMacvlan;
int macvlan_hash_entries;
static PMacvlanCmd pMacvlanSnapshot = NULL;
static int macvlan_hash_index = 0,
macvlan_snapshot_entries = 0,
macvlan_snapshot_index = 0,
macvlan_snapshot_buf_entries = 0;
if(reset_action)
{
macvlan_hash_index = 0;
macvlan_snapshot_entries =0;
macvlan_snapshot_index=0;
if(pMacvlanSnapshot)
{
Heap_Free(pMacvlanSnapshot);
pMacvlanSnapshot = NULL;
}
macvlan_snapshot_buf_entries = 0;
}
if (macvlan_snapshot_index == 0)
{
while(macvlan_hash_index < NUM_MACVLAN_HASH_ENTRIES)
{
macvlan_hash_entries = Macvlan_Get_Hash_Entries(macvlan_hash_index);
if(macvlan_hash_entries == 0)
{
macvlan_hash_index++;
continue;
}
if(macvlan_hash_entries > macvlan_snapshot_buf_entries)
{
if(pMacvlanSnapshot)
Heap_Free(pMacvlanSnapshot);
pMacvlanSnapshot = Heap_Alloc(macvlan_hash_entries * sizeof(MacvlanCmd));
if (!pMacvlanSnapshot)
{
macvlan_hash_index = 0;
macvlan_snapshot_buf_entries = 0;
return ERR_NOT_ENOUGH_MEMORY;
}
macvlan_snapshot_buf_entries = macvlan_hash_entries;
}
macvlan_snapshot_entries = Macvlan_Get_Hash_Snapshot(macvlan_hash_index, macvlan_hash_entries, pMacvlanSnapshot);
break;
}
if (macvlan_hash_index >= NUM_MACVLAN_HASH_ENTRIES)
{
macvlan_hash_index = 0;
if(pMacvlanSnapshot)
{
Heap_Free(pMacvlanSnapshot);
pMacvlanSnapshot = NULL;
}
macvlan_snapshot_buf_entries = 0;
return ERR_MACVLAN_ENTRY_NOT_FOUND;
}
}
pMacvlan = &pMacvlanSnapshot[macvlan_snapshot_index++];
SFL_memcpy(pMacvlanCmd, pMacvlan, sizeof(MacvlanCmd));
if (macvlan_snapshot_index == macvlan_snapshot_entries)
{
macvlan_snapshot_index = 0;
macvlan_hash_index ++;
}
return NO_ERR;
}
static int Macvlan_rule_find( U8* mac_addr, struct itf *itf_phys)
{
PMacvlanEntry this_entry, first_entry;
U8 hash_key;
hash_key = HASH_MACVLAN(mac_addr);
first_entry = macvlan_cache[hash_key];
if (first_entry == NULL)
return 0;
this_entry = first_entry;
while (1) {
if (TESTEQ_MACADDR(mac_addr, this_entry->MACaddr) && (this_entry->itf.phys->index == itf_phys->index))
{
break;
}
this_entry = this_entry->next;
if(this_entry == first_entry)
return 0;
}
return 1;
}
/**
* Macvlan_add_entry()
*
*/
static int Macvlan_add_entry(PMacvlanCmd macvlan_cmd)
{
POnifDesc phys_onif, macvlan_onif;
PMacvlanEntry this_entry, first_entry;
U8 hash_key;
phys_onif = get_onif_by_name(macvlan_cmd->phys_ifname);
if (!phys_onif)
return ERR_UNKNOWN_INTERFACE;
if ((phys_onif->itf->type != IF_TYPE_ETHERNET) && (phys_onif->itf->type != IF_TYPE_VLAN))
{
return ERR_UNKNOWN_INTERFACE;
}
macvlan_onif = get_onif_by_name(macvlan_cmd->macvlan_ifname);
if (macvlan_onif)
return ERR_MACVLAN_ALREADY_REGISTERED;
hash_key = HASH_MACVLAN(macvlan_cmd->macaddr);
if (Macvlan_rule_find(macvlan_cmd->macaddr, phys_onif->itf))
return ERR_MACVLAN_ALREADY_REGISTERED;
/* If mac doesn't exist, add it to the macvlan cache */
if ((this_entry = (struct _tMacvlanEntry*)Heap_Alloc_ARAM(sizeof(MacvlanEntry))) == NULL)
return ERR_CREATION_FAILED;
memset(this_entry, 0, sizeof(MacvlanEntry));
SFL_memcpy(this_entry->MACaddr, macvlan_cmd->macaddr, ETHER_ADDR_LEN);
/* Create the macvlan interface */
if(!add_onif(macvlan_cmd->macvlan_ifname, &this_entry->itf, phys_onif->itf, IF_TYPE_MACVLAN))
{
Heap_Free((PVOID)this_entry);
return ERR_CREATION_FAILED;
}
first_entry = macvlan_cache[hash_key];
if (first_entry == NULL)
{
macvlan_cache[hash_key] = this_entry;
this_entry->next = this_entry;
this_entry->prev = this_entry;
}
else
{
this_entry->next = first_entry;
this_entry->prev = first_entry->prev;
first_entry->prev->next = this_entry;
first_entry->prev = this_entry;
}
return NO_ERR;
}
static inline void Macvlan_free_entry(U8 hash_key , PMacvlanEntry pEntry)
{
if (pEntry->next == pEntry)
macvlan_cache[hash_key] = NULL;
else
{
pEntry->next->prev = pEntry->prev;
pEntry->prev->next = pEntry->next;
if (macvlan_cache[hash_key] == pEntry)
macvlan_cache[hash_key] = pEntry->next;
}
Heap_Free((PVOID)pEntry);
return;
}
/**
* Macvlan_remove_entry()
*
*/
static int Macvlan_remove_entry(PMacvlanCmd macvlan_cmd)
{
PMacvlanEntry this_entry;
POnifDesc macvlan_if;
U8 hash_key;
macvlan_if = get_onif_by_name(macvlan_cmd->macvlan_ifname);
if(!macvlan_if)
return ERR_MACVLAN_ENTRY_NOT_FOUND;
this_entry = (PMacvlanEntry)macvlan_if->itf;
hash_key = HASH_MACVLAN(this_entry->MACaddr);
remove_onif(macvlan_if);
Macvlan_free_entry(hash_key, this_entry);
return NO_ERR;
}
static int Macvlan_handle_entry(U16 *p, U16 Length)
{
MacvlanCmd cmd;
U16 rc = NO_ERR;
int reset_action = 0;
/* Check length */
if (Length != sizeof(MacvlanCmd) )
return ERR_WRONG_COMMAND_SIZE;
memset(&cmd, 0, sizeof(MacvlanCmd));
SFL_memcpy((U8*)&cmd, (U8*)p, Length);
switch(cmd.action)
{
case ACTION_DEREGISTER:
rc = Macvlan_remove_entry(&cmd);
break;
case ACTION_REGISTER:
rc = Macvlan_add_entry(&cmd);
break;
case ACTION_QUERY:
reset_action = 1;
case ACTION_QUERY_CONT:
rc = Macvlan_Get_Next_Hash_Entry((MacvlanCmd*)p, reset_action);
break;
default :
rc = ERR_UNKNOWN_ACTION;
break;
}
return rc;
}
static int Macvlan_handle_reset(void)
{
PMacvlanEntry pEntry = NULL;
int rc = NO_ERR;
U8 i;
/* free MACVLAN entries */
for(i = 0; i < NUM_MACVLAN_HASH_ENTRIES; i++)
{
while(macvlan_cache[i] != NULL)
{
pEntry = macvlan_cache[i];
Macvlan_free_entry(i, pEntry);
}
}
return rc;
}
static U16 M_macvlan_cmdproc(U16 cmd_code, U16 cmd_len, U16 *pcmd)
{
U16 rc;
U16 retlen = 2;
U16 action;
switch (cmd_code)
{
case CMD_MACVLAN_ENTRY:
action = *pcmd;
rc = Macvlan_handle_entry(pcmd, cmd_len);
if (rc == NO_ERR && (action == ACTION_QUERY || action == ACTION_QUERY_CONT))
retlen = sizeof(MacvlanCmd);
break;
case CMD_MACVLAN_ENTRY_RESET:
rc = Macvlan_handle_reset();
break;
default:
rc = ERR_UNKNOWN_COMMAND;
break;
}
*pcmd = rc;
return retlen;
}
BOOL M_macvlan_init(PModuleDesc pModule)
{
/* Entry point and Channel registration */
pModule->entry = &M_macvlan_entry;
pModule->cmdproc = &M_macvlan_cmdproc;
return 0;
}
#endif /* CFG_MACVLAN */