| /* |
| * 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. |
| * |
| * |
| */ |
| |
| |
| #include "modules.h" |
| #include "module_ethernet.h" |
| #include "module_ipv4.h" |
| #include "module_ipv6.h" |
| #include "module_pppoe.h" |
| #include "module_hidrv.h" |
| #include "module_timer.h" |
| #include "system.h" |
| #include "gemac.h" |
| #include "fpp.h" |
| #include "layer2.h" |
| #include "module_qm.h" |
| #include "module_wifi.h" |
| |
| int PPPoE_Get_Next_SessionEntry(pPPPoECommand pSessionCmd, int reset_action); |
| |
| |
| #if defined(COMCERTO_2000) |
| |
| static pPPPoE_Info pppoe_alloc(void) |
| { |
| int i; |
| |
| for (i = 0; i < PPPOE_MAX_ITF; i++) |
| if (!pppoe_itf[i].itf.type) |
| return &pppoe_itf[i]; |
| |
| return NULL; |
| } |
| |
| |
| static void pppoe_free(pPPPoE_Info pEntry) |
| { |
| pEntry->itf.type = 0; |
| } |
| |
| |
| static void pppoe_add(pPPPoE_Info pEntry, U16 hash_key) |
| { |
| struct pfe_ctrl *ctrl = &pfe->ctrl; |
| int id; |
| PPPoE_Info pppoe; |
| |
| /* Add to our local hash */ |
| slist_add(&pppoe_cache[hash_key], &pEntry->list); |
| |
| /* Construct the hardware entry, converting virtual addresses and endianess where needed */ |
| memcpy(&pppoe, pEntry, sizeof(PPPoE_Info)); |
| pppoe.itf.phys = (void *)cpu_to_be32(virt_to_class(pEntry->itf.phys)); |
| slist_set_next(&pppoe.list, (void *)cpu_to_be32(virt_to_class_dmem(slist_next(&pEntry->list)))); |
| pppoe.relay = (void *)cpu_to_be32(virt_to_class_dmem(pEntry->relay)); |
| pppoe.ppp_flags = cpu_to_be32(pEntry->ppp_flags); |
| |
| /* Update the PE pppoe interface in DMEM, for each PE */ |
| for (id = CLASS0_ID; id <= CLASS_MAX_ID; id++) |
| pe_dmem_memcpy_to32(id, virt_to_class_dmem(pEntry), &pppoe, sizeof(PPPoE_Info)); |
| |
| /* Now add the interface to the PE pppoe hash in DMEM, for each PE and atomically */ |
| |
| pe_sync_stop(ctrl, CLASS_MASK); |
| |
| /* We use the fact that slist_add() always adds to the head of the list */ |
| for (id = CLASS0_ID; id <= CLASS_MAX_ID; id++) |
| pe_dmem_write(id, cpu_to_be32(virt_to_class_dmem(&pEntry->list)), virt_to_class_dmem(&pppoe_cache[hash_key]), sizeof(struct slist_entry *)); |
| |
| pe_start(ctrl, CLASS_MASK); |
| } |
| |
| |
| static void pppoe_remove(pPPPoE_Info pEntry, U16 hash_key) |
| { |
| struct pfe_ctrl *ctrl = &pfe->ctrl; |
| struct slist_entry *prev; |
| int id; |
| |
| /* Now remove the interface from the PE pppoe hash in DMEM, for each PE and atomically */ |
| |
| prev = slist_prev(&pppoe_cache[hash_key], &pEntry->list); |
| |
| pe_sync_stop(ctrl, CLASS_MASK); |
| |
| for (id = CLASS0_ID; id <= CLASS_MAX_ID; id++) |
| pe_dmem_write(id, cpu_to_be32(virt_to_class_dmem(slist_next(&pEntry->list))), virt_to_class_dmem(prev), sizeof(struct slist_entry *)); |
| |
| pe_start(ctrl, CLASS_MASK); |
| |
| /* Remove from our local hash */ |
| slist_remove_after(prev); |
| } |
| |
| void M_pppoe_encapsulate(PMetadata mtd, pPPPoE_Info ppp_info, U16 ethertype, U8 update) |
| { |
| |
| } |
| |
| #else |
| |
| static pPPPoE_Info pppoe_alloc(void) |
| { |
| return Heap_Alloc_ARAM(sizeof (PPPoE_Info)); |
| } |
| |
| static void pppoe_free(pPPPoE_Info pEntry) |
| { |
| Heap_Free(pEntry); |
| } |
| |
| static void pppoe_add(pPPPoE_Info pEntry, U16 hash_key) |
| { |
| slist_add(&pppoe_cache[hash_key], &pEntry->list); |
| } |
| |
| static void pppoe_remove(pPPPoE_Info pEntry, U16 hash_key) |
| { |
| slist_remove(&pppoe_cache[hash_key], &pEntry->list); |
| } |
| |
| #endif |
| |
| static int PPPoE_Handle_Get_Idle(U16* p, U16 Length) |
| { |
| pPPPoEIdleTimeCmd cmd; |
| POnifDesc ppp_if; |
| pPPPoE_Info pppoeinfo; |
| |
| cmd = (pPPPoEIdleTimeCmd)p; |
| if (Length != sizeof(PPPoEIdleTimeCmd)) |
| return ERR_WRONG_COMMAND_SIZE; |
| |
| ppp_if = get_onif_by_name(cmd->ppp_intf); |
| |
| if (!ppp_if) |
| return ERR_UNKNOWN_INTERFACE; |
| |
| if(ppp_if->itf->type & IF_TYPE_PPPOE) |
| { |
| pppoeinfo = (pPPPoE_Info)ppp_if->itf; |
| if (pppoeinfo && (pppoeinfo->ppp_flags & PPPOE_AUTO_MODE)) |
| { |
| cmd = (pPPPoEIdleTimeCmd)(p+1); |
| SFL_memcpy(cmd->ppp_intf,ppp_if->name,sizeof(ppp_if->name)); |
| #if defined(COMCERTO_2000) |
| /* FIXME, add writeback to DDR, in the data path, to signal session activity */ |
| /* for now just prevent it from expiring */ |
| cmd->recv_idle = 0; |
| cmd->xmit_idle = 0; |
| #else |
| cmd->recv_idle = (ct_timer - pppoeinfo->last_pkt_rcvd) / CT_TICKS_PER_SECOND; |
| cmd->xmit_idle= (ct_timer - pppoeinfo->last_pkt_xmit) / CT_TICKS_PER_SECOND; |
| #endif |
| return NO_ERR; |
| } |
| } |
| |
| return ERR_UNKNOWN_INTERFACE; |
| } |
| |
| |
| static int PPPoE_Handle_Relay_Entry(U16 *p, U16 Length) |
| { |
| pPPPoERelayCommand cmd; |
| pPPPoE_Info pEntry, plastEntry = NULL; |
| pPPPoE_Info pRelayEntry, plastRelayEntry = NULL; |
| POnifDesc onif,relayonif; |
| U16 hash_key,relay_hash_key; |
| struct slist_entry *entry; |
| |
| cmd = (pPPPoERelayCommand) p; |
| if (Length != sizeof(PPPoERelayCommand)) |
| return ERR_WRONG_COMMAND_SIZE; |
| |
| cmd->sesID = htons(cmd->sesID); |
| cmd->relaysesID = htons(cmd->relaysesID); |
| |
| hash_key = HASH_PPPOE(cmd->sesID, cmd->peermac1); |
| relay_hash_key = HASH_PPPOE(cmd->relaysesID, cmd->peermac2); |
| |
| switch (cmd->action) |
| { |
| case ACTION_REGISTER: |
| |
| slist_for_each(pEntry, entry, &pppoe_cache[hash_key], list) |
| { |
| if((pEntry->sessionID == cmd->sesID) && TESTEQ_MACADDR(pEntry->DstMAC, cmd->peermac1)) |
| return ERR_PPPOE_ENTRY_ALREADY_REGISTERED; //trying to add the same pppoe session |
| } |
| |
| slist_for_each(pRelayEntry, entry, &pppoe_cache[relay_hash_key], list) |
| { |
| if((pRelayEntry->sessionID == cmd->relaysesID) && TESTEQ_MACADDR(pRelayEntry->DstMAC, cmd->peermac2)) |
| return ERR_PPPOE_ENTRY_ALREADY_REGISTERED; //trying to add the same pppoe session |
| } |
| |
| if ((pEntry = pppoe_alloc()) == NULL) |
| { |
| return ERR_NOT_ENOUGH_MEMORY; |
| } |
| memset(pEntry, 0, sizeof (PPPoE_Info)); |
| pEntry->itf.type = IF_TYPE_PPPOE; |
| |
| if ((pRelayEntry = pppoe_alloc()) == NULL) |
| { |
| pppoe_free(pEntry); |
| return ERR_CREATION_FAILED; |
| } |
| memset(pRelayEntry, 0, sizeof (PPPoE_Info)); |
| pRelayEntry->itf.type = IF_TYPE_PPPOE; |
| |
| /* populate relay pairs */ |
| pEntry->sessionID = cmd->sesID; |
| COPY_MACADDR(pEntry->DstMAC,cmd->peermac1); |
| |
| pRelayEntry->sessionID = cmd->relaysesID; |
| COPY_MACADDR(pRelayEntry->DstMAC,cmd->peermac2); |
| |
| /*Check if the Physical interface is known by the Interface manager*/ |
| onif = get_onif_by_name(cmd->ipifname); |
| relayonif = get_onif_by_name(cmd->opifname); |
| |
| if ((!onif) || (!relayonif)) { |
| pppoe_free(pEntry); |
| pppoe_free(pRelayEntry); |
| return ERR_UNKNOWN_INTERFACE; |
| } |
| |
| pEntry->itf.phys = onif->itf; |
| pRelayEntry->itf.phys = relayonif->itf; |
| |
| /*Now link these two entries by relay ptr */ |
| pEntry->relay = pRelayEntry; |
| pRelayEntry->relay = pEntry; |
| |
| /* FIXME, we need to add both entries atomically */ |
| pppoe_add(pEntry, hash_key); |
| pppoe_add(pRelayEntry, relay_hash_key); |
| |
| PPPoE_entries++; |
| |
| #ifdef CFG_STATS |
| gStatPPPoEQueryStatus = STAT_PPPOE_QUERY_NOT_READY; |
| #endif |
| break; |
| |
| case ACTION_DEREGISTER: |
| slist_for_each(pEntry, entry, &pppoe_cache[hash_key], list) |
| { |
| if (pEntry->relay) |
| { |
| if((pEntry->sessionID == cmd->sesID) && TESTEQ_MACADDR(pEntry->DstMAC, cmd->peermac1) |
| && (pEntry->relay->sessionID == cmd->relaysesID) && |
| TESTEQ_MACADDR(pEntry->relay->DstMAC, cmd->peermac2)) |
| goto found; |
| |
| } |
| |
| plastEntry = pEntry; |
| } |
| |
| return ERR_PPPOE_ENTRY_NOT_FOUND; |
| |
| found: |
| /* Now relay part as we already searched for relay link, just check for peer2mac and relaysesID */ |
| |
| slist_for_each(pRelayEntry, entry, &pppoe_cache[relay_hash_key], list) |
| { |
| if((pRelayEntry->sessionID == cmd->relaysesID) && |
| (TESTEQ_MACADDR(pRelayEntry->DstMAC, cmd->peermac2)) && |
| (pRelayEntry->relay == pEntry)) |
| goto found_relay; |
| |
| plastRelayEntry = pRelayEntry; |
| } |
| |
| return ERR_PPPOE_ENTRY_NOT_FOUND; |
| |
| found_relay: |
| pppoe_remove(pEntry, hash_key); |
| pppoe_remove(pRelayEntry, relay_hash_key); |
| |
| pppoe_free(pEntry); |
| pppoe_free(pRelayEntry); |
| |
| PPPoE_entries--; |
| |
| #ifdef CFG_STATS |
| gStatPPPoEQueryStatus = STAT_PPPOE_QUERY_NOT_READY; |
| #endif |
| |
| break; |
| |
| default : |
| return ERR_UNKNOWN_COMMAND; |
| } |
| |
| return NO_ERR; |
| } |
| |
| static int PPPoE_Handle_Entry(U16 *p, U16 Length) |
| { |
| pPPPoECommand cmd; |
| pPPPoE_Info pEntry, plastEntry = NULL; |
| POnifDesc phys_onif; |
| U32 hash_key; |
| struct slist_entry *entry; |
| |
| cmd = (pPPPoECommand) p; |
| if (Length != sizeof(PPPoECommand)) |
| return ERR_WRONG_COMMAND_SIZE; |
| |
| cmd->sessionID = htons(cmd->sessionID); |
| |
| hash_key = HASH_PPPOE(cmd->sessionID, cmd->macAddr); |
| |
| switch (cmd->action) |
| { |
| case ACTION_DEREGISTER: |
| slist_for_each(pEntry, entry, &pppoe_cache[hash_key], list) |
| { |
| if ((pEntry->sessionID == cmd->sessionID) && TESTEQ_MACADDR(pEntry->DstMAC, cmd->macAddr) && |
| (pEntry->relay == NULL) && !strcmp(get_onif_name(pEntry->itf.index), (char *)cmd->log_intf) ) |
| goto found; |
| |
| plastEntry = pEntry; |
| } |
| |
| return ERR_PPPOE_ENTRY_NOT_FOUND; |
| |
| found: |
| pppoe_remove(pEntry, hash_key); |
| |
| /*Tell the Interface Manager to remove the pppoe IF*/ |
| remove_onif_by_index(pEntry->itf.index); |
| |
| pppoe_free(pEntry); |
| |
| PPPoE_entries--; |
| |
| #ifdef CFG_STATS |
| gStatPPPoEQueryStatus = STAT_PPPOE_QUERY_NOT_READY; |
| #endif |
| break; |
| |
| case ACTION_REGISTER: |
| |
| if (get_onif_by_name(cmd->log_intf)) |
| return ERR_PPPOE_ENTRY_ALREADY_REGISTERED; |
| |
| /*Check if the Physical interface is known by the Interface manager*/ |
| phys_onif = get_onif_by_name(cmd->phy_intf); |
| if (!phys_onif) |
| return ERR_UNKNOWN_INTERFACE; |
| |
| slist_for_each(pEntry, entry, &pppoe_cache[hash_key], list) |
| { |
| if ((pEntry->sessionID == cmd->sessionID) && TESTEQ_MACADDR(pEntry->DstMAC, cmd->macAddr)) |
| return ERR_PPPOE_ENTRY_ALREADY_REGISTERED; //trying to add exactly the same vlan entry |
| } |
| |
| if ((pEntry = pppoe_alloc()) == NULL) |
| { |
| return ERR_NOT_ENOUGH_MEMORY; |
| } |
| |
| memset(pEntry, 0, sizeof (PPPoE_Info)); |
| |
| /* populate pppoe_info entry */ |
| pEntry->sessionID = cmd->sessionID; |
| COPY_MACADDR(pEntry->DstMAC,cmd->macAddr); |
| |
| pEntry->last_pkt_rcvd = ct_timer; |
| pEntry->last_pkt_xmit = ct_timer; |
| |
| if (cmd->mode & PPPOE_AUTO_MODE) |
| pEntry->ppp_flags |= PPPOE_AUTO_MODE; |
| |
| /*Now create a new interface in the Interface Manager and remember the index*/ |
| if (!add_onif(cmd->log_intf, &pEntry->itf, phys_onif->itf, IF_TYPE_PPPOE)) |
| { |
| pppoe_free(pEntry); |
| return ERR_CREATION_FAILED; |
| } |
| |
| pppoe_add(pEntry, hash_key); |
| |
| PPPoE_entries++; |
| |
| #ifdef CFG_STATS |
| gStatPPPoEQueryStatus = STAT_PPPOE_QUERY_NOT_READY; |
| #endif |
| break; |
| |
| case ACTION_QUERY: |
| case ACTION_QUERY_CONT: |
| { |
| int rc; |
| |
| rc = PPPoE_Get_Next_SessionEntry(cmd, cmd->action == ACTION_QUERY); |
| return rc; |
| |
| } |
| |
| default: |
| return ERR_UNKNOWN_ACTION; |
| } |
| |
| /* return success */ |
| return NO_ERR; |
| } |
| |
| |
| static U16 M_pppoe_cmdproc(U16 cmd_code, U16 cmd_len, U16 *pcmd) |
| { |
| U16 rc = NO_ERR; |
| U16 ret_len = 2; |
| U16 action; |
| |
| switch (cmd_code) |
| { |
| case CMD_PPPOE_ENTRY: |
| action = *pcmd; |
| rc = PPPoE_Handle_Entry(pcmd, cmd_len); |
| if (rc == NO_ERR && (action == ACTION_QUERY || action == ACTION_QUERY_CONT)) |
| ret_len = sizeof(PPPoECommand); |
| break; |
| |
| case CMD_PPPOE_RELAY_ENTRY: |
| action = *pcmd; |
| rc = PPPoE_Handle_Relay_Entry(pcmd, cmd_len); |
| if (rc == NO_ERR && (action == ACTION_QUERY || action == ACTION_QUERY_CONT)) |
| ret_len = sizeof(PPPoECommand); |
| break; |
| |
| case CMD_PPPOE_GET_IDLE: |
| rc = PPPoE_Handle_Get_Idle(pcmd, cmd_len); |
| if (rc == NO_ERR) |
| ret_len = sizeof(PPPoEIdleTimeCmd) + 2; |
| break; |
| |
| default: |
| rc = ERR_UNKNOWN_COMMAND; |
| break; |
| } |
| |
| *pcmd = rc; |
| return ret_len; |
| } |
| |
| |
| int pppoe_init(void) |
| { |
| int i; |
| |
| #if !defined(COMCERTO_2000) |
| set_event_handler(EVENT_PPPOE, M_pppoe_ingress); |
| #endif |
| |
| set_cmd_handler(EVENT_PPPOE, M_pppoe_cmdproc); |
| |
| for (i = 0; i < NUM_PPPOE_ENTRIES; i++) |
| { |
| slist_head_init(&pppoe_cache[i]); |
| } |
| |
| return 0; |
| } |
| |
| void pppoe_exit(void) |
| { |
| /* FIXME remove all pppoe interfaces */ |
| } |