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