blob: 9eda6362252fd6e9950410c9ea123a559106edcf [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 <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>
#include <ctype.h>
#include "libcmm.h"
#include "cmm.h"
#include "fpp.h"
#include "itf.h"
#include "module_pktcap.h"
#define MIN_SLICE_VALUE 40
#define MAX_SLICE_VALUE 1518
#define SNAP_LENGTH 96
int PktCapSliceProcess(daemon_handle_t daemon_handle, int argc, char *argv[])
{
cmm_command_t cmd;
cmm_response_t res;
fpp_pktcap_slice_cmd_t *pktcap_cmd;
unsigned char port_id;
unsigned short slice;
char buf[128];
if (argc != 2)
goto usage;
if ((char)(port_id = get_port_id(argv[0])) < 0)
goto usage;
if (! isdigit(*argv[1]))
goto usage;
slice = atoi(argv[1]);
if ( (slice < MIN_SLICE_VALUE )||(slice > MAX_SLICE_VALUE))
{
cmm_print(DEBUG_ERROR,"slice value should be between(%d-%d)", MIN_SLICE_VALUE, MAX_SLICE_VALUE);
return -1;
}
memset(&cmd, 0 , sizeof(cmd));
memset(&res, 0 , sizeof(res));
cmd.func = FPP_CMD_PKTCAP_SLICE;
cmd.length = sizeof(fpp_pktcap_slice_cmd_t);
pktcap_cmd = (fpp_pktcap_slice_cmd_t*)&cmd.buf;
pktcap_cmd->action = FPP_PKTCAP_SLICE;
pktcap_cmd->ifindex = port_id;
pktcap_cmd->slice = slice;
if (cmm_send(daemon_handle, &cmd, 0) != 0) {
cmm_print(DEBUG_ERROR,"Error sending message to CMM, error = `%s'\n", strerror(errno));
return -1;
}
if (cmm_recv(daemon_handle, &res, 0) < 0) {
cmm_print(DEBUG_ERROR,"Error receiving message from CMM, error = `%s'\n", strerror(errno));
return -1;
}
if (res.rc != FPP_ERR_OK) {
cmm_print(DEBUG_ERROR,"Error from CMM, error = `%d'\n", res.rc);
return -1;
}
return CLI_OK;
usage:
print_all_gemac_ports(buf, 128);
cmm_print(DEBUG_ERROR, "Usage: pktcapture slice <%s> <value>\n", buf);
return -1;
}
int PktCapStatProcess(daemon_handle_t daemon_handle, int argc, char *argv[])
{
cmm_command_t cmd;
cmm_response_t res;
fpp_pktcap_status_cmd_t *pktcap_cmd;
unsigned char port_id;
unsigned char status;
char buf[128];
if (argc != 2)
goto usage;
if ((char)(port_id = get_port_id(argv[0])) < 0)
goto usage;
if (strcmp(argv[1] , "enable") == 0)
status = PKTCAP_IFSTATUS_ENABLE;
else if (strcmp(argv[1], "disable") == 0)
status = PKTCAP_IFSTATUS_DISABLE;
else
goto usage;
memset(&cmd, 0 , sizeof(cmd));
memset(&res, 0 , sizeof(res));
cmd.func = FPP_CMD_PKTCAP_IFSTATUS;
cmd.length = sizeof(fpp_pktcap_status_cmd_t);
pktcap_cmd = (fpp_pktcap_status_cmd_t*)&cmd.buf;
pktcap_cmd->action = FPP_PKTCAP_STATUS;
pktcap_cmd->ifindex = port_id;
pktcap_cmd->status = status;
if (cmm_send(daemon_handle, &cmd, 0) != 0) {
cmm_print(DEBUG_ERROR,"Error sending message to CMM, error = `%s'\n", strerror(errno));
return -1;
}
if (cmm_recv(daemon_handle, &res, 0) < 0) {
cmm_print(DEBUG_ERROR,"Error receiving message from CMM, error = `%s'\n", strerror(errno));
return -1;
}
if (res.rc != FPP_ERR_OK) {
cmm_print(DEBUG_ERROR,"Error from CMM, error = `%d'\n", res.rc);
return -1;
}
return CLI_OK;
usage:
print_all_gemac_ports(buf, 128);
cmm_print(DEBUG_ERROR, "Usage: pktcapture status <%s> <enable|disable>\n", buf);
return CLI_OK;
}
int PktCapFilterProcess(daemon_handle_t daemon_handle, int argc, char *argv[])
{
cmm_command_t cmd;
cmm_response_t res;
struct bpf_program fd = {0,NULL};
fpp_pktcap_flf_cmd_t *pFlfCmd = NULL;
int port_id = 0, fgmts = 0;
int length = 0, seqno = 0;
char buf[128];
if (argc < 2)
goto usage;
if ((char)(port_id = get_port_id(argv[0])) < 0)
goto usage;
cmd.func = FPP_CMD_PKTCAP_FLF;
cmd.length = sizeof(fpp_pktcap_flf_cmd_t);
pFlfCmd = (fpp_pktcap_flf_cmd_t* )&cmd.buf;
if( strlen(argv[1]) >= 1024 )
{
cmm_print(DEBUG_ERROR,"Error Filter too long \n");
goto usage;
}
pcap_t *pd = pcap_open_dead(DLT_EN10MB, SNAP_LENGTH);
if(pd)
{
if(pcap_compile(pd, &fd, argv[1], 1, 0)<0)
{
cmm_print(DEBUG_ERROR,"Error Invalid filter string \n");
goto done;
}
if(( fd.bf_len == 1)&& ( fd.bf_insns[0].code == BPF_RET ))// Filter reset !!
goto reset_flf;
pFlfCmd->ifindex = port_id;
pFlfCmd->flen = length = fd.bf_len & 0xFFFF;
if(Check_BPFfilter(fd.bf_insns, fd.bf_len))
{
cmm_print(DEBUG_ERROR,"Error This filter combination is not supported by FLF\n");
goto done;
}
if(fd.bf_len > MAX_FLF_INSTRUCTIONS)
{
cmm_print(DEBUG_ERROR,"Warning: Filter could be too expensive");
length = MAX_FLF_INSTRUCTIONS;
fgmts = (fd.bf_len / MAX_FLF_INSTRUCTIONS);
if( fd.bf_len % MAX_FLF_INSTRUCTIONS )
++fgmts; // Account for one more fragment;
}
do
{
memcpy(pFlfCmd->filter, &fd.bf_insns[seqno * MAX_FLF_INSTRUCTIONS], length * sizeof(struct bpf_insn));
pFlfCmd->mfg = ((( fgmts - (seqno + 1)) > 0) << 3 ) | (seqno & 0x7);
/* Push to FPP */
if (cmm_send(daemon_handle, &cmd, 0) != 0) {
cmm_print(DEBUG_ERROR,"Error sending message to CMM, error = `%s'\n", strerror(errno));
goto reset_flf;
}
if (cmm_recv(daemon_handle, &res, 0) < 0) {
cmm_print(DEBUG_ERROR,"Error receiving message from CMM, error = `%s'\n", strerror(errno));
goto reset_flf;
}
if (res.rc != FPP_ERR_OK) {
cmm_print(DEBUG_ERROR,"Error from CMM, error = `%d'\n", res.rc);
goto reset_flf;
}
pFlfCmd->flen = (fd.bf_len - (++seqno * MAX_FLF_INSTRUCTIONS));
if( pFlfCmd->flen / MAX_FLF_INSTRUCTIONS)
{
length = pFlfCmd->flen = MAX_FLF_INSTRUCTIONS;
continue;
}
else
length = pFlfCmd->flen;
}while(seqno < fgmts);
}
done:
if(fd.bf_insns) free(fd.bf_insns);
if(pd) pcap_close(pd);
return CLI_OK;
usage:
print_all_gemac_ports(buf, 128);
cmm_print(DEBUG_ERROR, "Usage: pktcapture filter <%s> <string>\n", buf);
return CLI_OK;
reset_flf:
cmm_print(DEBUG_ERROR, "Resetting filter");
/* cleanup structures got from library */
if(fd.bf_insns) free(fd.bf_insns);
if(pd) pcap_close(pd);
/* reset length */
pFlfCmd->ifindex = port_id;
pFlfCmd->flen = 0;
if (cmm_send(daemon_handle, &cmd, 0) != 0) {
cmm_print(DEBUG_ERROR,"Error sending message to CMM, error = `%s'\n", strerror(errno));
return -1;
}
if (cmm_recv(daemon_handle, &res, 0) < 0) {
cmm_print(DEBUG_ERROR,"Error receiving message from CMM, error = `%s'\n", strerror(errno));
return -1;
}
if (res.rc != FPP_ERR_OK) {
cmm_print(DEBUG_ERROR,"Error from CMM, error = `%d'\n", res.rc);
return -1;
}
return CLI_OK;
}
/*
* The purpose of this function is to ensure the filter is compatible with our
* version of BPF interpretor in FPP.
* As libpcap version changes further, with changes in the filter corresponding changes
* will also have to be made in fpp.
*/
int Check_BPFfilter(struct bpf_insn *filter, int flen)
{
struct bpf_insn *ftest;
int pc;
if (flen == 0 || flen > (3*MAX_FLF_INSTRUCTIONS))
return -EINVAL;
/* check the filter code now */
for (pc = 0; pc < flen; pc++) {
ftest = &filter[pc];
/* Only allow valid instructions */
switch (ftest->code) {
case BPF_ALU|BPF_ADD|BPF_K:
case BPF_ALU|BPF_ADD|BPF_X:
case BPF_ALU|BPF_SUB|BPF_K:
case BPF_ALU|BPF_SUB|BPF_X:
case BPF_ALU|BPF_MUL|BPF_K:
case BPF_ALU|BPF_MUL|BPF_X:
case BPF_ALU|BPF_DIV|BPF_X:
case BPF_ALU|BPF_AND|BPF_K:
case BPF_ALU|BPF_AND|BPF_X:
case BPF_ALU|BPF_OR|BPF_K:
case BPF_ALU|BPF_OR|BPF_X:
case BPF_ALU|BPF_LSH|BPF_K:
case BPF_ALU|BPF_LSH|BPF_X:
case BPF_ALU|BPF_RSH|BPF_K:
case BPF_ALU|BPF_RSH|BPF_X:
case BPF_ALU|BPF_NEG:
case BPF_LD|BPF_W|BPF_ABS:
case BPF_LD|BPF_H|BPF_ABS:
case BPF_LD|BPF_B|BPF_ABS:
case BPF_LD|BPF_W|BPF_LEN:
case BPF_LD|BPF_W|BPF_IND:
case BPF_LD|BPF_H|BPF_IND:
case BPF_LD|BPF_B|BPF_IND:
case BPF_LD|BPF_IMM:
case BPF_LDX|BPF_W|BPF_LEN:
case BPF_LDX|BPF_B|BPF_MSH:
case BPF_LDX|BPF_IMM:
case BPF_MISC|BPF_TAX:
case BPF_MISC|BPF_TXA:
case BPF_RET|BPF_K:
case BPF_RET|BPF_A:
break;
/* Some instructions need special checks */
case BPF_ALU|BPF_DIV|BPF_K:
/* check for division by zero */
if (ftest->k == 0)
return -EINVAL;
break;
case BPF_LD|BPF_MEM:
case BPF_LDX|BPF_MEM:
case BPF_ST:
case BPF_STX:
/* check for invalid memory addresses */
if (ftest->k >= BPF_MEMWORDS)
return -EINVAL;
break;
case BPF_JMP|BPF_JA:
/*
* Note, the large ftest->k might cause loops.
* Compare this with conditional jumps below,
* where offsets are limited. --ANK (981016)
*/
if (ftest->k >= (unsigned)(flen-pc-1))
return -EINVAL;
break;
case BPF_JMP|BPF_JEQ|BPF_K:
case BPF_JMP|BPF_JEQ|BPF_X:
case BPF_JMP|BPF_JGE|BPF_K:
case BPF_JMP|BPF_JGE|BPF_X:
case BPF_JMP|BPF_JGT|BPF_K:
case BPF_JMP|BPF_JGT|BPF_X:
case BPF_JMP|BPF_JSET|BPF_K:
case BPF_JMP|BPF_JSET|BPF_X:
/* for conditionals both must be safe */
if (pc + ftest->jt + 1 >= flen ||
pc + ftest->jf + 1 >= flen)
return -EINVAL;
break;
default:
return -EINVAL;
}
}
return (BPF_CLASS(filter[flen - 1].code) == BPF_RET) ? 0 : -EINVAL;
}
int PktCapQueryProcess(struct cli_def *cli, daemon_handle_t daemon_handle)
{
/* Query fpp for the details */
char rcvBuffer[256];
fpp_pktcap_query_cmd_t *pktcap_cmd;
int ii;
pktcap_cmd = (fpp_pktcap_query_cmd_t*)&rcvBuffer;
if ((cmmSendToDaemon(daemon_handle, FPP_CMD_PKTCAP_QUERY, pktcap_cmd,
0, pktcap_cmd)) < sizeof(fpp_pktcap_query_cmd_t))
{
cmm_print(DEBUG_ERROR,"Error sending message to CMM, error = `%s'\n", strerror(errno));
return -1;
}
for (ii = 0; ii < GEM_PORTS; ii++)
{
cli_print(cli, "slice <%s> %d \n", port_table[ii].logical_name, pktcap_cmd[port_table[ii].port_id].slice);
}
for (ii = 0; ii < GEM_PORTS; ii++)
{
cli_print(cli, "status <%s> %d \n", port_table[ii].logical_name, pktcap_cmd[port_table[ii].port_id].status);
}
return CLI_OK;
}