| /* |
| * 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 */ |