blob: 8a81ec7558f7cd134328eb734ff7cefe38d5df40 [file] [log] [blame]
/*
*
* 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;
}