| /* |
| * |
| * Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "cmm.h" |
| #include "itf.h" |
| #include "fpp.h" |
| #include "cmmd.h" |
| |
| #include <net/if.h> |
| #include <linux/if_vlan.h> |
| #include <linux/sockios.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <sys/ioctl.h> |
| |
| |
| void __cmmGetVlan(int fd, struct interface *itf) |
| { |
| struct vlan_ioctl_args if_request; |
| |
| itf->itf_flags &= ~ITF_VLAN; |
| |
| if (itf->phys_ifindex == itf->ifindex) |
| goto out; |
| |
| memset(&if_request, 0, sizeof(if_request)); |
| |
| if_request.cmd = GET_VLAN_VID_CMD; |
| if (____itf_get_name(itf, if_request.device1, sizeof(if_request.device1)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex); |
| goto out; |
| } |
| |
| if (ioctl(fd, SIOCGIFVLAN, &if_request) < 0) |
| goto out; |
| |
| itf->itf_flags |= ITF_VLAN; |
| itf->vlan_id = if_request.u.VID; |
| |
| out: |
| return; |
| } |
| /***************************************************************** |
| * cmmFeVLANUpdate |
| * |
| * |
| ******************************************************************/ |
| int cmmFeVLANUpdate(FCI_CLIENT *fci_handle, int request, struct interface *itf) |
| { |
| fpp_vlan_cmd_t cmd; |
| short ret = CMMD_ERR_OK; |
| int action; |
| |
| switch (request) |
| { |
| default: |
| case ADD: |
| if ((itf->flags & (FPP_PROGRAMMED | FPP_NEEDS_UPDATE)) == FPP_PROGRAMMED) |
| goto out; |
| |
| if ((itf->flags & (FPP_PROGRAMMED | FPP_NEEDS_UPDATE)) == (FPP_PROGRAMMED | FPP_NEEDS_UPDATE)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: trying to update vlan interface(%d)\n", __func__, itf->ifindex); |
| ret = CMMD_ERR_NOT_CONFIGURED; |
| goto out; |
| } |
| |
| action = FPP_ACTION_REGISTER; |
| |
| break; |
| |
| case REMOVE: |
| if (!(itf->flags & FPP_PROGRAMMED)) |
| goto out; |
| |
| action = FPP_ACTION_DEREGISTER; |
| |
| break; |
| } |
| |
| memset(&cmd, 0, sizeof(cmd)); |
| |
| cmd.action = action; |
| |
| if (____itf_get_name(itf, cmd.vlan_ifname, sizeof(cmd.vlan_ifname)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex); |
| ret = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto out; |
| } |
| |
| cmd.vlan_id = itf->vlan_id; |
| |
| switch (action) |
| { |
| case FPP_ACTION_REGISTER: |
| cmm_print(DEBUG_COMMAND, "Send CMD_VLAN_ENTRY ACTION_REGISTER\n"); |
| |
| if (__itf_get_name(itf->phys_ifindex, cmd.vlan_phy_ifname, sizeof(cmd.vlan_phy_ifname)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: __itf_get_name(%d) failed\n", __func__, itf->phys_ifindex); |
| ret = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto out; |
| } |
| |
| ret = fci_write(fci_handle, FPP_CMD_VLAN_ENTRY, sizeof(fpp_vlan_cmd_t), (unsigned short *) &cmd); |
| if ((ret == FPP_ERR_OK) || (ret == FPP_ERR_VLAN_ENTRY_ALREADY_REGISTERED)) |
| { |
| itf->flags |= FPP_PROGRAMMED; |
| itf->flags &= ~FPP_NEEDS_UPDATE; |
| } |
| else |
| { |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_VLAN_ENTRY, ACTION_REGISTER\n", __func__, ret); |
| goto out; |
| } |
| |
| break; |
| #if 0 |
| case ACTION_UPDATE: |
| cmm_print(DEBUG_COMMAND, "Send CMD_VLAN_ENTRY ACTION_UPDATE\n"); |
| |
| ret = fci_write(fci_handle, CMD_VLAN_ENTRY, sizeof(struct VlanCmd), (unsigned short *) &cmd); |
| if (ret == NO_ERR) |
| { |
| itf->flags &= ~FPP_NEEDS_UPDATE; |
| } |
| else |
| { |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_VLAN_ENTRY, ACTION_UPDATE\n", __func__, ret); |
| goto err; |
| } |
| |
| break; |
| #endif |
| case FPP_ACTION_DEREGISTER: |
| |
| cmm_print(DEBUG_COMMAND, "Send CMD_VLAN_ENTRY ACTION_DEREGISTER\n"); |
| |
| ret = fci_write(fci_handle, FPP_CMD_VLAN_ENTRY, sizeof(fpp_vlan_cmd_t), (unsigned short *) &cmd); |
| if ((ret == FPP_ERR_OK) || (ret == FPP_ERR_VLAN_ENTRY_NOT_FOUND)) |
| { |
| itf->flags &= ~FPP_PROGRAMMED; |
| } |
| else |
| { |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_VLAN_ENTRY, ACTION_DEREGISTER\n", __func__, ret); |
| goto out; |
| } |
| |
| break; |
| |
| default: |
| cmm_print(DEBUG_ERROR, "%s: unknown CMD_VLAN_ENTRY action %x\n", __func__, action); |
| ret = CMMD_ERR_UNKNOWN_ACTION; |
| break; |
| } |
| |
| out: |
| return ret; |
| } |
| |
| /***************************************************************** |
| * cmmVlanReset |
| * |
| * |
| * |
| ******************************************************************/ |
| void cmmVlanReset(FCI_CLIENT *fci_handle) |
| { |
| struct list_head *entry; |
| struct interface *itf; |
| short ret; |
| int i; |
| |
| // Send message to forward engine |
| cmm_print(DEBUG_COMMAND, "Send CMD_VLAN_RESET\n"); |
| |
| __pthread_mutex_lock(&itf_table.lock); |
| |
| ret = fci_write(fci_handle, FPP_CMD_VLAN_RESET, 0, NULL); |
| if (ret == FPP_ERR_OK) |
| { |
| for (i = 0; i < ITF_HASH_TABLE_SIZE; i++) |
| { |
| for (entry = list_first(&itf_table.hash[i]); entry != &itf_table.hash[i]; entry = list_next(entry)) |
| { |
| itf = container_of(entry, struct interface, list); |
| |
| if (!__itf_is_vlan(itf)) |
| continue; |
| |
| itf->flags &= ~FPP_PROGRAMMED; |
| } |
| } |
| } |
| else |
| { |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_VLAN_RESET\n", __func__, ret); |
| } |
| |
| __pthread_mutex_unlock(&itf_table.lock); |
| } |
| |
| /***************************************************************** |
| * cmmVlanCheckPolicy |
| * check if it is allowable to create device with given name |
| * 0 means prohibited |
| * non-zero means allowed |
| ******************************************************************/ |
| int cmmVlanCheckPolicy(struct interface *itf) |
| { |
| // Full implementation will query allow list for prohibit policy |
| // and allow list for prohibit policy here |
| if (globalConf.vlan_policy != MANUAL) |
| return 1; |
| |
| return 0; |
| } |
| |
| /***************************************************************** |
| * cmmVlanLocalShow |
| * |
| * |
| ******************************************************************/ |
| int cmmVlanLocalShow(struct cli_def *cli, char *command, char *argv[], int argc) |
| { |
| struct list_head *entry; |
| struct interface *itf; |
| char ifname[IFNAMSIZ], phys_ifname[IFNAMSIZ]; |
| int i; |
| |
| for (i = 0; i < ITF_HASH_TABLE_SIZE; i++) |
| { |
| __pthread_mutex_lock(&itf_table.lock); |
| |
| for (entry = list_first(&itf_table.hash[i]); entry != &itf_table.hash[i]; entry = list_next(entry)) |
| { |
| itf = container_of(entry, struct interface, list); |
| |
| if (!__itf_is_vlan(itf)) |
| continue; |
| |
| cli_print(cli, "Interface: %s, VLAN Id: %4d, physical Interface: %s, Flags: %x", if_indextoname(itf->ifindex, ifname), itf->vlan_id, if_indextoname(itf->phys_ifindex, phys_ifname), itf->flags); |
| |
| } |
| |
| __pthread_mutex_unlock(&itf_table.lock); |
| } |
| |
| return CLI_OK; |
| } |
| |
| int vlanAddProcess(daemon_handle_t daemon_handle, int argc, char *argv[]) |
| { |
| unsigned char rspbuf[512]; |
| unsigned short rsplen; |
| cmmd_vlan_cmd_t cmd; |
| |
| if (argc < 1) |
| goto usage; |
| |
| cmd.action = CMMD_ACTION_REGISTER; |
| cmd.vlan_id = 0; |
| strncpy(cmd.vlan_ifname, argv[0], 12); |
| cmd.vlan_phy_ifname[0] = 0; |
| |
| if (((rsplen = cmmSendToDaemon(daemon_handle, CMMD_CMD_VLAN_ENTRY, &cmd, sizeof(cmd), rspbuf)) < sizeof(unsigned short)) || |
| cmmDaemonCmdRC(rspbuf)) |
| { |
| cmm_print(DEBUG_ERROR, "Error sending CMD_VLAN_ENTRY Register\n"); |
| /* break; */ return 0; |
| } |
| |
| return 0; |
| |
| usage: |
| cmm_print(DEBUG_ERROR, "Usage: vlan add ifname.vlanid\n"); |
| return 0; |
| } |
| |
| int vlanDeleteProcess(daemon_handle_t daemon_handle, int argc, char *argv[]) |
| { |
| unsigned char rspbuf[512]; |
| unsigned short rsplen; |
| cmmd_vlan_cmd_t cmd; |
| |
| if (argc < 1) |
| goto usage; |
| |
| cmd.action = CMMD_ACTION_DEREGISTER; |
| cmd.vlan_id = 0; |
| strncpy(cmd.vlan_ifname, argv[0], 12); |
| cmd.vlan_phy_ifname[0] = 0; |
| |
| if (((rsplen = cmmSendToDaemon(daemon_handle, CMMD_CMD_VLAN_ENTRY, &cmd, sizeof(cmd), rspbuf)) < sizeof(unsigned short)) || |
| cmmDaemonCmdRC(rspbuf)) |
| { |
| cmm_print(DEBUG_ERROR, "Error sending CMD_VLAN_ENTRY DeRegister\n"); |
| return 0; |
| } |
| |
| return 0; |
| |
| usage: |
| cmm_print(DEBUG_ERROR, "Usage: vlan delete ifname.vlanid\n"); |
| return 0; |
| } |
| |
| static int vlanShowProcess(daemon_handle_t daemon_handle, int argc, char *argv[]) |
| { |
| unsigned char rspbuf[512]; |
| int rsplen; |
| cmmd_vlan_cmd_t cmd; |
| cmmd_vlan_response_t *pqrsp; |
| int skipcount = 0; |
| |
| do { |
| memset(&cmd, 0, sizeof(cmd)); |
| cmd.action = CMMD_ACTION_QUERY_LOCAL; |
| cmd.vlan_id = skipcount; |
| |
| pqrsp = (cmmd_vlan_response_t *) (rspbuf + 4); |
| |
| if (((rsplen = cmmSendToDaemon(daemon_handle, CMMD_CMD_VLAN_ENTRY, &cmd, sizeof(cmd), rspbuf)) < sizeof(unsigned short)) || |
| cmmDaemonCmdRC(rspbuf)) |
| { |
| cmm_print(DEBUG_STDOUT, "No vlans defined.\n"); |
| break; |
| } |
| |
| rsplen -= 4; |
| |
| while (rsplen >= sizeof(*pqrsp)) |
| { |
| cmm_print(DEBUG_STDOUT, "Interface: %s, VLAN Id: %d, physical Interface: %s\n", |
| pqrsp->vlan_ifname, |
| pqrsp->vlan_id, |
| pqrsp->vlan_phy_if_name); |
| |
| rsplen -= sizeof(*pqrsp); |
| pqrsp += 1; |
| skipcount += 1; |
| } |
| |
| if ((rsplen & 1) == 0) |
| break; |
| } while(1); |
| |
| return 0; |
| } |
| |
| /* |
| ** cmmVlanClient |
| ** Client side demux - check input and find client side processor for it |
| */ |
| int cmmVlanClient(int argc, char **argv, int firstarg, daemon_handle_t daemon_handle) |
| { |
| if (argc <= firstarg) |
| goto usage; |
| |
| if (strncasecmp(argv[firstarg], "add", 1) == 0) |
| return vlanAddProcess(daemon_handle, argc - firstarg - 1, &argv[firstarg + 1]); |
| else if (strncasecmp(argv[firstarg], "delete", 1) == 0) |
| return vlanDeleteProcess(daemon_handle, argc - firstarg - 1, &argv[firstarg + 1]); |
| else if (strncasecmp(argv[firstarg], "show", 1) == 0) |
| return vlanShowProcess(daemon_handle, argc - firstarg - 1, &argv[firstarg + 1]); |
| |
| return 0; |
| |
| usage: |
| cmm_print(DEBUG_ERROR, "Usage:\n\tvlan [add|delete] ifname.vlanid\n\tvlan show\n"); |
| return 0; |
| } |
| |
| /* |
| ** cmmVlanProcessClientCmd |
| ** Daemon side demux. |
| ** receives command from client side, processes it and sends response back. |
| ** Return code is a length of a response in bytes, not including 2 bytes of command rc. |
| ** To prevent daemon issuing commands to fpp - return code must be greater then 1 |
| */ |
| int cmmVlanProcessClientCmd(FCI_CLIENT *fci_handle, int function_code, u_int8_t *cmd_buf, u_int16_t cmd_len, u_int16_t *res_buf, u_int16_t *res_len) |
| { |
| cmmd_vlan_cmd_t *pcmd = (fpp_vlan_cmd_t *) cmd_buf; |
| cmmd_vlan_response_t *pqrsp; |
| struct interface *itf; |
| struct list_head *entry; |
| int skipcount, len, i; |
| int rc = 0; |
| |
| res_buf[0] = CMMD_ERR_OK; |
| |
| __pthread_mutex_lock(&itf_table.lock); |
| |
| switch (pcmd->action) |
| { |
| case CMMD_ACTION_REGISTER: |
| *res_len = 2; |
| |
| itf = __itf_find(if_nametoindex(pcmd->vlan_ifname)); |
| if (!itf) |
| { |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| break; |
| } |
| |
| if (!__itf_is_vlan(itf) || !__itf_is_up(itf)) |
| { |
| res_buf[0] = CMMD_ERR_NOT_CONFIGURED; |
| break; |
| } |
| |
| rc = cmmFeVLANUpdate(fci_handle, ADD, itf); |
| if (rc > 0) |
| { |
| res_buf[0] = rc; |
| rc = 0; |
| } |
| |
| break; |
| |
| case CMMD_ACTION_DEREGISTER: |
| *res_len = 2; |
| |
| itf = __itf_find(if_nametoindex(pcmd->vlan_ifname)); |
| if (!itf) |
| { |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| break; |
| } |
| |
| if (!__itf_is_vlan(itf)) |
| { |
| res_buf[0] = CMMD_ERR_NOT_CONFIGURED; |
| break; |
| } |
| |
| rc = cmmFeVLANUpdate(fci_handle, REMOVE, itf); |
| if (rc > 0) |
| { |
| res_buf[0] = rc; |
| rc = 0; |
| } |
| |
| break; |
| |
| case CMMD_ACTION_QUERY_LOCAL: |
| |
| skipcount = pcmd->vlan_id; |
| len = 4; |
| pqrsp = (cmmd_vlan_response_t *)((char*)res_buf + 4); |
| |
| for (i = 0; i < ITF_HASH_TABLE_SIZE; i++) |
| { |
| for (entry = list_first(&itf_table.hash[i]); entry != &itf_table.hash[i]; entry = list_next(entry)) |
| { |
| itf = container_of(entry, struct interface, list); |
| |
| if (!__itf_is_vlan(itf)) |
| continue; |
| |
| if (skipcount <= 0) |
| { |
| if_indextoname(itf->ifindex, pqrsp->vlan_ifname); |
| pqrsp->vlan_id = itf->vlan_id; |
| if_indextoname(itf->phys_ifindex, pqrsp->vlan_phy_if_name); |
| len += sizeof(*pqrsp); |
| pqrsp += 1; |
| |
| if (len + sizeof(*pqrsp) >= *res_len) |
| { |
| |
| len += 1; // odd length means that there are more entries to report |
| |
| goto out; |
| } |
| } else { |
| skipcount--; |
| } |
| } |
| } |
| |
| out: |
| *res_len = len; |
| |
| break; |
| case CMMD_ACTION_QUERY: |
| case CMMD_ACTION_QUERY_CONT: |
| rc = fci_cmd(fci_handle, function_code, (u_int16_t*)cmd_buf, cmd_len, res_buf, res_len); |
| break; |
| |
| default: |
| res_buf[0] = CMMD_ERR_UNKNOWN_ACTION; |
| *res_len = 2; |
| break; |
| } |
| |
| __pthread_mutex_unlock(&itf_table.lock); |
| return rc; |
| } |
| |
| /***************************************************************** |
| * * cmmVlanQuery |
| * * |
| * * |
| * ******************************************************************/ |
| int cmmVlanQuery(char ** keywords, int tabStart, daemon_handle_t daemon_handle) |
| { |
| int rcvBytes = 0; |
| char rcvBuffer[256]; |
| short rc; |
| int count = 0; |
| cmmd_vlan_cmd_t* pVlanCmd = (cmmd_vlan_cmd_t *) rcvBuffer; |
| |
| pVlanCmd->action = CMMD_ACTION_QUERY; |
| rcvBytes = cmmSendToDaemon(daemon_handle, CMMD_CMD_VLAN_ENTRY, pVlanCmd, |
| sizeof(cmmd_vlan_cmd_t) , rcvBuffer); |
| |
| if (rcvBytes < sizeof(cmmd_vlan_cmd_t) + sizeof(unsigned short)) { |
| rc = (rcvBytes < sizeof(unsigned short) ) ? 0 : |
| *((unsigned short *) rcvBuffer); |
| if (rc == CMMD_ERR_UNKNOWN_ACTION) { |
| cmm_print(DEBUG_STDERR, |
| "ERROR: FPP VLANP does not support ACTION_QUERY\n"); |
| } else if (rc == CMMD_ERR_VLAN_ENTRY_NOT_FOUND) { |
| cmm_print(DEBUG_STDERR, "ERROR: FPP VLAN table empty\n"); |
| } else { |
| cmm_print(DEBUG_STDERR, |
| "ERROR: Unexpected result returned from FPP rc:%d\n", rc); |
| } |
| return CLI_OK; |
| } |
| cmm_print(DEBUG_STDOUT, "VLAN interfaces:\n"); |
| do { |
| cmm_print(DEBUG_STDOUT, |
| "Interface: %s, VLAN Id : %4d, physical Interface: %s \n", |
| pVlanCmd->vlan_ifname, pVlanCmd->vlan_id, |
| pVlanCmd->vlan_phy_ifname); |
| count++; |
| pVlanCmd->action = CMMD_ACTION_QUERY_CONT; |
| rcvBytes = cmmSendToDaemon(daemon_handle, CMMD_CMD_VLAN_ENTRY, pVlanCmd, |
| sizeof(cmmd_vlan_cmd_t) , rcvBuffer); |
| }while (rcvBytes >= sizeof(cmmd_vlan_cmd_t) + sizeof(unsigned short)); |
| cmm_print(DEBUG_STDOUT, "Total VLAN Entries:%d\n", count); |
| |
| return CLI_OK; |
| } |