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