blob: 5d8182f3b6a8c9d3177cec90f0599c1b1d9d1d41 [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 "cmm.h"
#include "cmmd.h"
#define MC4_MAX_LISTENERS_IN_QUERY 5
extern int cmmMcParseListener(char ** keywords, int *cpt, cmmd_mc6_listener_t * listener, u_int8_t * mode, u_int16_t action );
extern pthread_mutex_t mc_lock;
int cmmMc4Show(struct cli_def * cli, char *command, char *argv[], int argc)
{
int count = 0, ii, jj;
struct list_head *entry;
struct mcast_entry *mc;
char buf1[INET_ADDRSTRLEN];
char buf2[INET_ADDRSTRLEN];
__pthread_mutex_lock(&mc_lock);
for( ii = 0; ii < MC_NUM_HASH_ENTRIES; ii++ )
{
for (entry = list_first(&mc_table[ii]); entry != &mc_table[ii]; entry = list_next(entry))
{
mc = container_of(entry, struct mcast_entry, list);
if( mc->family != AF_INET )
continue;
cmm_print(DEBUG_STDOUT, "%04d: Src addr: %s src_mask_len: %x Dst addr: %s Queue : %d\n",
count,
inet_ntop(AF_INET, &mc->src_addr, buf1, INET_ADDRSTRLEN),
mc->src_mask_len,
inet_ntop(AF_INET, &mc->dst_addr, buf2, INET_ADDRSTRLEN),
mc->queue);
for( jj = 0; jj < mc->num_output; jj++)
{
cmm_print(DEBUG_STDOUT," PROGRAMMED : %02d TIMER : 0x%x SHAPER : 0x%x Interface %s\n",mc->l_program[jj],
mc->listener[jj].timer,mc->listener[jj].shaper_mask , mc->listener[jj].output_device_str);
if(mc->listener[jj].q_bit)
cmm_print(DEBUG_STDOUT," QUEUE : 0x%x \n",mc->listener[jj].queue );
if(mc->listener[jj].uc_bit)
cmm_print(DEBUG_STDOUT,"UC MAC is %02x:%02x:%02x:%02x:%02x:%02x ",mc->listener[jj].uc_mac[0],mc->listener[jj].uc_mac[1],mc->listener[jj].uc_mac[2],mc->listener[jj].uc_mac[3],mc->listener[jj].uc_mac[4],mc->listener[jj].uc_mac[5]);
}
count++;
}
}
cmm_print(DEBUG_STDOUT, "Total Multicast v4 Entries: %d\n", count);
__pthread_mutex_unlock(&mc_lock);
return 0;
}
/************************************************************
*
*
*
************************************************************/
void cmmMc4ShowPrintHelp()
{
cmm_print(DEBUG_STDOUT, "show mc4 not yet supported\n");
}
/************************************************************
*
*
*
************************************************************/
int cmmMc4ShowProcess(char ** keywords, int tabStart, daemon_handle_t daemon_handle)
{
//help:
cmmMc4ShowPrintHelp();
return -1;
}
/************************************************************
*
*
*
*************************************************************/
int cmmMc4QueryProcess(char ** keywords, int tabStart, daemon_handle_t daemon_handle)
{
int rcvBytes = 0;
char rcvBuffer[256];
char buf1[INET_ADDRSTRLEN];
char buf2[INET_ADDRSTRLEN];
char output_buf[256];
short rc;
int count = 0,len=0;
cmmd_mc4_entry_t *mc4_entry = (cmmd_mc4_entry_t *) rcvBuffer;
cmmd_mc4_listener_t* mc4_listener = (cmmd_mc4_listener_t *)(rcvBuffer + sizeof (cmmd_mc4_entry_t));
mc4_entry->action = CMMD_ACTION_QUERY;
rcvBytes = cmmSendToDaemon(daemon_handle,CMMD_CMD_MC4_MULTICAST ,
mc4_entry, sizeof(cmmd_mc4_entry_t)+ MC4_MAX_LISTENERS_IN_QUERY * sizeof(cmmd_mc4_listener_t) , rcvBuffer);
if (rcvBytes < sizeof(cmmd_mc4_entry_t) + sizeof (cmmd_mc4_listener_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 CMD_MC4_MULTICAST does not support ACTION_QUERY\n");
} else if (rc == CMMD_ERR_MC_ENTRY_NOT_FOUND) {
cmm_print(DEBUG_STDERR, "ERROR: FPP Multicast IPV4 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, "IPv4 Multicast Entries: \n");
do {
int i, listener_count = 0;
if((ntohl(mc4_entry->dst_addr) & 0xE0000000) == 0xE0000000)
{
cmm_print(DEBUG_STDOUT, "%04d: Src addr: %s src_mask_len: %x Dst addr: %s Queue: 0x%x \n\n",
count,
inet_ntop(AF_INET, &mc4_entry->src_addr, buf1, INET_ADDRSTRLEN),
mc4_entry->src_mask_len,
inet_ntop(AF_INET, &mc4_entry->dst_addr, buf2, INET_ADDRSTRLEN),
mc4_entry->queue);
count++;
}
listener_count = mc4_entry->num_output;
if(listener_count > MC4_MAX_LISTENERS_IN_QUERY)
{
cmm_print(DEBUG_STDOUT,"output interfaces are more than %d",MC4_MAX_LISTENERS_IN_QUERY);
listener_count = MC4_MAX_LISTENERS_IN_QUERY;
}
for ( i = 0; i < listener_count; i++)
{
len += sprintf(output_buf + len, "output interface: ");
len += sprintf(output_buf + len, "%s ", mc4_listener[i].output_device_str);
cmm_print (DEBUG_STDOUT,output_buf);
if(mc4_listener[i].uc_bit)
cmm_print(DEBUG_STDOUT,"UC MAC is %02x:%02x:%02x:%02x:%02x:%02x ",mc4_listener[i].uc_mac[0],mc4_listener[i].uc_mac[1],mc4_listener[i].uc_mac[2],mc4_listener[i].uc_mac[3],mc4_listener[i].uc_mac[4],mc4_listener[i].uc_mac[5]);
cmm_print(DEBUG_STDOUT," TIMER : 0x%x SHAPER : 0x%x QUEUE : 0x%x\n",mc4_listener[i].timer,mc4_listener[i].shaper_mask, mc4_listener[i].queue);
len = 0;
}
mc4_entry->action = CMMD_ACTION_QUERY_CONT;
rcvBytes = cmmSendToDaemon(daemon_handle, CMMD_CMD_MC4_MULTICAST, mc4_entry,
sizeof(cmmd_mc4_entry_t)+ 4* sizeof(cmmd_mc4_listener_t), rcvBuffer);
} while (rcvBytes >= sizeof(cmmd_mc4_entry_t)+ sizeof(cmmd_mc4_listener_t) + sizeof(unsigned short));
cmm_print(DEBUG_STDOUT, "Total Multicast Entries: %d\n", count);
return CLI_OK;
}
/************************************************************
*
*
*
************************************************************/
void cmmMc4SetPrintHelp()
{
#ifdef COMCERTO_2000
cmm_print(DEBUG_STDOUT, "Usage: set mc4 interface {if_name} {add | del | update}\n"
" group {bit_mask} {ipv4_src_addr} {ipv4_multicast_dst_addr}\n"
" [timer {timer_value}]\n"
" [mode {bridged | routed}]\n"
" [queue {0..19}]\n"
" [listener [timer {timer_value1}] [shapers {0..0xFF}][mc | uc {Mac addr1} ] [queue {0..19}] [if {if_name}]] \n"
" [listener [timer {timer_value2}] [shapers {0..0xFF}][mc | uc {Mac addr2} ] [queue {0..19}] [if {if_name}]] ...\n"
" [listener [timer {timer_valuen}] [shapers {0..0xFF}][mc | uc {Mac addrn} ] [queue {0..19}] [if {if_name}]] \n");
#else
cmm_print(DEBUG_STDOUT, "Usage: set mc4 interface {if_name} {add | del | update}\n"
" group {bit_mask} {ipv4_src_addr} {ipv4_multicast_dst_addr}\n"
" [timer {timer_value}]\n"
" [mode {bridged | routed}]\n"
" [queue {0..31}]\n"
" [listener [timer {timer_value1}] [shapers {0..0xFF}][mc | uc {Mac addr1} ] [queue {0..31}] [if {if_name}]] \n"
" [listener [timer {timer_value2}] [shapers {0..0xFF}][mc | uc {Mac addr2} ] [queue {0..31}] [if {if_name}]] ...\n"
" [listener [timer {timer_valuen}] [shapers {0..0xFF}][mc | uc {Mac addrn} ] [queue {0..31}] [if {if_name}]] \n");
#endif
}
/************************************************************
*
*
*
************************************************************/
int cmmMc4SetProcess(char ** keywords, int tabStart, daemon_handle_t daemon_handle)
{
int cpt = tabStart;
unsigned int tmp;
char * endptr;
char sndBuffer[256];
char rcvBuffer[256];
cmmd_mc4_entry_t * entryCmd = (cmmd_mc4_entry_t *) sndBuffer;
cmmd_mc4_listener_t * listener = (cmmd_mc4_listener_t *) (sndBuffer + sizeof(cmmd_mc4_entry_t));
char interfaceName[11] = {};
int listenerCnt = 0;
u_int8_t mode = 0;
int rc = 0;
memset(sndBuffer, 0, sizeof(sndBuffer));
listener->timer = 0xFFFFFFFF;
listener->q_bit = 0;
listener->uc_bit = 0; // Multicast by default when a new entry is added
listener->Ifbit = 0;
if(!keywords[cpt])
goto help;
if(strcasecmp(keywords[cpt], "interface") == 0)
{
if(!keywords[++cpt])
goto help;
if (strlen(keywords[cpt]) > 10) {
cmm_print(DEBUG_STDERR, "Error : interface name %s limited to 10 characters\n", keywords[cpt]);
goto help;
}
strcpy(interfaceName, keywords[cpt]);
strcpy(listener->output_device_str,interfaceName);
}
else if(strcasecmp(keywords[cpt], "reset") == 0)
{
/*Reset, special case*/
if(cmmSendToDaemon(daemon_handle, CMMD_CMD_MC4_RESET, NULL, 0, rcvBuffer) == 2)
{
if ( ((unsigned short *)rcvBuffer)[0] != 0)
cmm_print(DEBUG_STDERR, "Error %d received from FPP for CMD_MC4_RESET\n", ((unsigned short *)rcvBuffer)[0]);
}
return ((unsigned short *)rcvBuffer)[0];
}
else
goto keyword_error;
if(!keywords[++cpt])
goto help;
if(strcasecmp(keywords[cpt], "add") == 0)
{
entryCmd->action = CMMD_MC_ACTION_ADD;
}
else if(strcasecmp(keywords[cpt], "del") == 0)
{
entryCmd->action = CMMD_MC_ACTION_REMOVE;
}
else if(strcasecmp(keywords[cpt], "update") == 0)
{
entryCmd->action = CMMD_MC_ACTION_UPDATE;
}
else
goto keyword_error;
if(!keywords[++cpt])
goto help;
if (strcasecmp(keywords[cpt], "group") == 0)
{
if(!keywords[++cpt])
goto help;
/*Get an integer from the string for the MASK*/
endptr = NULL;
tmp = strtoul(keywords[cpt], &endptr, 0);
entryCmd->src_mask_len = tmp;
if(!keywords[++cpt])
goto help;
/*Get an integer from the string for the IPsrc*/
if(inet_pton(AF_INET, keywords[cpt], &entryCmd->src_addr) != 1)
goto help;
if(!keywords[++cpt])
goto help;
/*Get an integer from the string for the IPdst*/
if(inet_pton(AF_INET, keywords[cpt], &entryCmd->dst_addr) != 1)
goto help;
}
++cpt;
while(keywords[cpt])
{
strcpy(((cmmd_mc4_listener_t *)listener + listenerCnt)->output_device_str,interfaceName);
if (strncasecmp(keywords[cpt], "listener",strlen(keywords[cpt])) == 0) //May be a case of multiple listeners, parse differently
{
++cpt;
if(!cmmMcParseListener(keywords, &cpt, listener + listenerCnt, &mode, entryCmd->action))
{
if(++listenerCnt == MC4_MAX_LISTENERS_IN_QUERY)
//Send first batch to FPP and reset listener count
{
entryCmd->num_output = listenerCnt;
if(cmmSendToDaemon(daemon_handle, CMMD_CMD_MC4_MULTICAST, sndBuffer, sizeof(cmmd_mc4_entry_t) + (listenerCnt * sizeof(cmmd_mc4_listener_t)), rcvBuffer) == 2)
{
if ( (rc = ((unsigned short *)rcvBuffer)[0]) != 0)
{
if(rc == CMMD_ERR_NOT_CONFIGURED)
cmm_print(DEBUG_STDERR, "%d Interface not yet up in FPP, will be configured when interface comes up\n", rc);
else if(rc == CMMD_ERR_MC_MAX_LISTENERS)
cmm_print(DEBUG_STDERR, "Error %d MAX listeners for the group exhausted\n", rc);
else if(rc == CMMD_ERR_MC_INTERFACE_NOT_ALLOWED)
cmm_print(DEBUG_STDERR, "Error %d Interface configured cannot support Unicast MAC\n", rc);
else if(rc == CMMD_ERR_MC_ENTRY_OVERLAP)
{
cmm_print(DEBUG_STDERR, "Error %d Overlapping entry configured\n", rc);
entryCmd->action = CMMD_MC_ACTION_REMOVE_LOCAL;
cmmSendToDaemon(daemon_handle, CMMD_CMD_MC4_MULTICAST, sndBuffer,
sizeof(cmmd_mc4_entry_t) + (listenerCnt * sizeof(cmmd_mc4_listener_t)), rcvBuffer);
}
else
cmm_print(DEBUG_STDERR, "Error %d received from FPP for CMD_MC6_MULTICAST\n", rc);
}
}
listenerCnt = 0;
memset(sndBuffer + sizeof(cmmd_mc4_entry_t), 0 , sizeof(sndBuffer) - sizeof(cmmd_mc4_entry_t));
}
}
else
goto help;
}
else
{
if(!cmmMcParseListener(keywords, &cpt, listener, &mode, entryCmd->action))
{
listener->q_bit = 0; // Default config listener q_bit is always zero.
entryCmd->queue = listener->queue;
entryCmd->mode = mode;
}
else
goto help;
}
}
if(!listenerCnt & !entryCmd->num_output)
listenerCnt = 1; //fall back to legacy design, single listener is configured.
entryCmd->num_output = listenerCnt;
if(listenerCnt && (cmmSendToDaemon(daemon_handle, CMMD_CMD_MC4_MULTICAST, sndBuffer, sizeof(cmmd_mc4_entry_t) + (listenerCnt * sizeof(cmmd_mc4_listener_t)), rcvBuffer) == 2))
{
if ( (rc = ((unsigned short *)rcvBuffer)[0]) != 0)
{
if(rc == CMMD_ERR_NOT_CONFIGURED)
cmm_print(DEBUG_STDERR, "%d Interface not yet up in FPP, will be configured when interface comes up\n", rc);
else if(rc == CMMD_ERR_MC_MAX_LISTENERS)
cmm_print(DEBUG_STDERR, "Error %d MAX listeners for the group exhausted\n", rc);
else if(rc == CMMD_ERR_MC_INTERFACE_NOT_ALLOWED)
cmm_print(DEBUG_STDERR, "Error %d Interface configured cannot support Unicast MAC\n", rc);
else if(rc == CMMD_ERR_MC_ENTRY_OVERLAP)
{
cmm_print(DEBUG_STDERR, "Error %d Overlapping entry configured\n", rc);
entryCmd->action = CMMD_MC_ACTION_REMOVE_LOCAL;
cmmSendToDaemon(daemon_handle, CMMD_CMD_MC4_MULTICAST, sndBuffer,
sizeof(cmmd_mc4_entry_t) + (listenerCnt * sizeof(cmmd_mc4_listener_t)), rcvBuffer);
}
else
cmm_print(DEBUG_STDERR, "Error %d received from FPP for CMD_MC6_MULTICAST\n", rc);
}
}
#if 1
cmm_print(DEBUG_INFO, "action:%x\n",entryCmd->action);
cmm_print(DEBUG_INFO, "src addr:%x\n",entryCmd->src_addr);
cmm_print(DEBUG_INFO, "src addr:%x\n",entryCmd->dst_addr);
cmm_print(DEBUG_INFO, "src masklen:%x\n",entryCmd->src_mask_len);
cmm_print(DEBUG_INFO, "num output:%x\n",entryCmd->num_output);
cmm_print(DEBUG_INFO, "timer:%x\n",listener->timer);
cmm_print(DEBUG_INFO, "outputstring:%s\n",listener->output_device_str);
cmm_print(DEBUG_INFO, "queue:%d\n",entryCmd->queue);
#endif
return rc;
keyword_error:
cmm_print(DEBUG_STDERR, "ERROR: Unknown keyword %s\n", keywords[cpt]);
help:
cmmMc4SetPrintHelp();
return -1;
}
/************************************************************
* cmmMc4ProcessClientCmd
* This function do needed local process before sending
* the command to FPP
*
************************************************************/
int cmmMc4ProcessClientCmd(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)
{
int ret = 0;
cmmd_mc4_entry_t * entry = (cmmd_mc4_entry_t *) cmd_buf;
cmmd_mc4_listener_t * listener;
switch(function_code)
{
case CMMD_CMD_MC4_MULTICAST:
// We need to check if we don't have to do something with the interface names
//Sanity checks
if (cmd_len < sizeof(cmmd_mc4_entry_t))
{
res_buf[0] = CMMD_ERR_WRONG_COMMAND_SIZE;
break;
}
if ((entry->action == CMMD_ACTION_QUERY) ||
(entry->action == CMMD_ACTION_QUERY_CONT) ||
(entry->action == CMMD_ACTION_KEEP_ALIVE))
goto FCI_CMD;
if (entry->num_output > MC4_MAX_LISTENERS_IN_QUERY)
{
res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM;
break;
}
if (cmd_len < (sizeof(cmmd_mc4_entry_t) + (entry->num_output * sizeof(cmmd_mc4_listener_t))))
{
res_buf[0] = CMMD_ERR_WRONG_COMMAND_SIZE;
break;
}
// Check if the listeners are on virtual interface
listener = (cmmd_mc4_listener_t *)(cmd_buf + sizeof(cmmd_mc4_entry_t) );
switch( entry->action )
{
case CMMD_MC_ACTION_ADD:
case CMMD_MC_ACTION_REMOVE:
case CMMD_MC_ACTION_UPDATE:
res_buf[0] = CMMD_ERR_OK;
ret = mc4_update_entry( entry, listener, entry->action );
if( ret != CMMD_ERR_OK )
{
res_buf[0] = ret;
goto out;
}
goto FCI_CMD;
break;
case CMMD_MC_ACTION_REMOVE_LOCAL:
mc_remove_group(entry,AF_INET);
break;
}
out:
break;
case CMMD_CMD_MC4_RESET:
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
__pthread_mutex_lock(&mc_lock);
res_buf[0] = fci_write(fci_handle, CMMD_CMD_MC4_RESET, 0, NULL);
if ( ret == CMMD_ERR_OK )
mc_reset(AF_INET);
__pthread_mutex_unlock(&mc_lock);
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&itf_table.lock);
break;
default:
res_buf[0] = CMMD_ERR_UNKNOWN_COMMAND;
break;
}
*res_len = 2;
return 0;
FCI_CMD:
return fci_cmd(fci_handle, function_code, (u_int16_t*)cmd_buf, cmd_len, res_buf, res_len);
}