--- a/src/config.c +++ b/src/config.c @@ -65,6 +65,10 @@ static struct Config commonConfig; struct vifconfig *parsePhyintToken(void); struct SubnetList *parseSubnetAddress(char *addrstr); +#ifdef IGMPPROXY_QCA_MCS_SUPPORT +void qca_mcs_add_brdev(char *brdevstr); +#endif + /** * Initializes common config.. */ @@ -136,17 +140,25 @@ int loadConfig(char *configFile) { my_log(LOG_WARNING, 0, "Unknown token '%s' in configfile", token); return 0; } else { - - my_log(LOG_DEBUG, 0, "IF name : %s", tmpPtr->name); - my_log(LOG_DEBUG, 0, "Next ptr : %x", tmpPtr->next); - my_log(LOG_DEBUG, 0, "Ratelimit : %d", tmpPtr->ratelimit); - my_log(LOG_DEBUG, 0, "Threshold : %d", tmpPtr->threshold); - my_log(LOG_DEBUG, 0, "State : %d", tmpPtr->state); - my_log(LOG_DEBUG, 0, "Allowednet ptr : %x", tmpPtr->allowednets); - - // Insert config, and move temppointer to next location... - *currPtr = tmpPtr; - currPtr = &tmpPtr->next; +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + if (tmpPtr->state == IF_STATE_QCA_MCS_BR) { + free(tmpPtr->name); + free(tmpPtr); + } + else +#endif + { + my_log(LOG_DEBUG, 0, "IF name : %s", tmpPtr->name); + my_log(LOG_DEBUG, 0, "Next ptr : %x", tmpPtr->next); + my_log(LOG_DEBUG, 0, "Ratelimit : %d", tmpPtr->ratelimit); + my_log(LOG_DEBUG, 0, "Threshold : %d", tmpPtr->threshold); + my_log(LOG_DEBUG, 0, "State : %d", tmpPtr->state); + my_log(LOG_DEBUG, 0, "Allowednet ptr : %x", tmpPtr->allowednets); + + // Insert config, and move temppointer to next location... + *currPtr = tmpPtr; + currPtr = &tmpPtr->next; + } } } else if(strcmp("quickleave", token)==0) { @@ -294,6 +306,9 @@ struct vifconfig *parsePhyintToken(void) struct SubnetList **anetPtr, **agrpPtr; char *token; short parseError = 0; +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + char qcaMcsIntfs[300] = { 0 }; +#endif // First token should be the interface name.... token = nextConfigToken(); @@ -323,6 +338,10 @@ struct vifconfig *parsePhyintToken(void) my_log(LOG_ERR, 0, "Out of memory."); } +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + strcpy(qcaMcsIntfs, tmpPtr->name); +#endif + // Set the altnet pointer to the allowednets pointer. anetPtr = &tmpPtr->allowednets; agrpPtr = &tmpPtr->allowedgroups; @@ -411,6 +430,20 @@ struct vifconfig *parsePhyintToken(void) break; } } +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + else if(strcmp("qca-mcs-bridge", token)==0) { + // QCA MCS bridge + my_log(LOG_DEBUG, 0, "Config: IF: Got qca-mcs module bridge token."); + tmpPtr->state = IF_STATE_QCA_MCS_BR; + } + else if(strcmp("brport", token)==0) { + // QCA MCS bridge port + token = nextConfigToken(); + my_log(LOG_DEBUG, 0, "Config: IF: Got brport token %s.",token); + strcat(&qcaMcsIntfs, "|"); + strcat(&qcaMcsIntfs, token); + } +#endif else { // Unknown token. Break... break; @@ -425,6 +458,13 @@ struct vifconfig *parsePhyintToken(void) tmpPtr = NULL; } +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + if (tmpPtr->state == IF_STATE_QCA_MCS_BR) { + // Configure QCA MCS configurations + qca_mcs_add_brdev(&qcaMcsIntfs); + } +#endif + return tmpPtr; } --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -124,6 +124,10 @@ void my_log( int Serverity, int Errno, c #define IF_STATE_LOST 3 // aimwang: Temp from downstream to hidden #define IF_STATE_HIDDEN 4 // aimwang: Interface is hidden +#ifdef IGMPPROXY_QCA_MCS_SUPPORT +#define IF_STATE_QCA_MCS_BR 5 // QCA MCA support +#endif + // Multicast default values... #define DEFAULT_ROBUSTNESS 2 #define DEFAULT_THRESHOLD 1 --- a/src/igmpproxy.c +++ b/src/igmpproxy.c @@ -61,6 +61,12 @@ int igmpProxyInit(void); void igmpProxyCleanUp(void); void igmpProxyRun(void); +#ifdef IGMPPROXY_QCA_MCS_SUPPORT +bool qca_mcs_is_configured(void); +void qca_mcs_start(void); +void qca_mcs_stop(void); +#endif + // Global vars... static int sighandled = 0; #define GOT_SIGINT 0x01 @@ -185,9 +191,21 @@ int main( int ArgCn, char *ArgVc[] ) { close(devnull); } +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + if (qca_mcs_is_configured()) { + qca_mcs_start(); + } +#endif + // Go to the main loop. igmpProxyRun(); +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + if (qca_mcs_is_configured()) { + qca_mcs_stop(); + } +#endif + // Clean up igmpProxyCleanUp(); --- a/src/rttable.c +++ b/src/rttable.c @@ -75,6 +75,11 @@ void logRouteTable(const char *header); int internAgeRoute(struct RouteTable *croute); int internUpdateKernelRoute(struct RouteTable *route, int activate); +#ifdef IGMPPROXY_QCA_MCS_SUPPORT +bool qca_mcs_is_configured(void); +void qca_mcs_flood_brport(u_int32_t mc_addr, bool is_join); +#endif + /** * Functions for downstream hosts hash table @@ -192,6 +197,12 @@ static void sendJoinLeaveUpstream(struct k_join(upstrIf, route->group); route->upstrState = ROUTESTATE_JOINED; + +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + if (qca_mcs_is_configured()) { + qca_mcs_flood_brport( route->group, true ); + } +#endif } else { my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", inetFmt(route->group, s1)); @@ -206,6 +217,12 @@ static void sendJoinLeaveUpstream(struct k_leave(upstrIf, route->group); route->upstrState = ROUTESTATE_NOTJOINED; + +#ifdef IGMPPROXY_QCA_MCS_SUPPORT + if (qca_mcs_is_configured()) { + qca_mcs_flood_brport( route->group, false ); + } +#endif } } } --- /dev/null +++ b/src/qca-mcs.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012, 2015 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Header files copied from QCA NHSS.QSDK.12.0.r8 qca-mcs sources + */ + +#define NETLINK_QCA_MC 23 + +#define MC_DEF_IF_MAX 16 +#define MC_DEF_IP6_SIZE 16 + +#define MC_ACL_RULE_MAX_COUNT 8 /* 8 for IGMP, 8 for MLD */ + +/* netlink message type */ +enum { + MC_MSG_SET_ENABLE, + MC_MSG_SET_DEBUG, + MC_MSG_SET_POLICY, + MC_MSG_SET_MEMBERSHIP_INTERVAL, + MC_MSG_SET_RETAG, + MC_MSG_SET_ROUTER_PORT, + MC_MSG_SET_ADD_ACL_RULE, + MC_MSG_SET_FLUSH_ACL_RULE, + MC_MSG_SET_CONVERT_ALL, + MC_MSG_SET_TIMEOUT, + MC_MSG_SET_M2I3_FILTER, + MC_MSG_SET_TBIT, + MC_MSG_SET_LOCAL_QUERY_INTERVAL, + MC_MSG_SET_PSW_ENCAP, + MC_MSG_SET_PSW_FLOOD, + MC_MSG_SET_EVENT_PID, + MC_MSG_GET_ACL, + MC_MSG_GET_MDB, + MC_MSG_SET_ROUTER, + MC_MSG_GET_ROUTER_PORT, + MC_MSG_MAX +}; + +struct __mcctl_msg_header { + char if_name[IFNAMSIZ]; /* bridge name: br-lan */ + u_int32_t buf_len; /* not include this header */ + u_int32_t tbl_offsite; /* how many entries to skip */ + u_int32_t status; + u_int32_t bytes_written; + u_int32_t bytes_needed; +} __attribute__ ((packed));; + +struct __mc_param_value { + u_int32_t val; +}; + +enum { + MC_RTPORT_FLOOD = 0, + MC_RTPORT_DROP, + MC_RTPORT_SPECIFY, + MC_RTPORT_DEFAULT, + MC_RTPORT_MAX +}; + +struct __mc_param_router_port { + u_int32_t type; + u_int32_t ifindex; +}; + +struct __mc_group { + u_int32_t pro; + union { + u_int32_t ip4; + u_int8_t ip6[MC_DEF_IP6_SIZE]; + } u; +}; + +struct __mc_floodtbl_entry { + struct __mc_group group; + u_int32_t ifcnt; + u_int32_t ifindex[MC_DEF_IF_MAX]; +} __attribute__ ((packed)); + --- /dev/null +++ b/src/qca-mcs.c @@ -0,0 +1,319 @@ +/* +** QCA Multicast Snooping (MCS) Module Support +** +** Enables the ipq806x NSS ECM multicast acceleration support. +**/ + +#include +#include "igmpproxy.h" +#include "qca-mcs.h" + +#define QCA_MCS_MAX_MCAST_GRP 32 + +struct qca_mcs_brdev { + char if_name[IFNAMSIZ]; /* bridge interface name */ + u_int32_t br_ifidx; /* bridge interface index */ + u_int32_t port_num; /* number of bridge port to flood */ + u_int32_t port_ifidx[MC_DEF_IF_MAX]; /* bridge port interface index */ +}; + +struct qca_mcs_all_brdev { + u_int32_t num_brdev; /* number of bridge interface to manage */ + struct qca_mcs_brdev brdev[MC_DEF_IF_MAX]; /* bridge details */ +}; + +static unsigned int mcsNlFd = -1; + +static struct qca_mcs_all_brdev all_brdev = { 0 }; + +static u_int32_t flooded_mc_addr[QCA_MCS_MAX_MCAST_GRP] = { 0 }; +static int flooded_mc_addr_cnt = 0; + +static bool qca_mcs_configured = false; + +/* + * data - netlink messages payload - includes the __mcctl_msg_header + */ +static int qca_mcs_sendmsg(void *data, int data_len, int msg_type) +{ + struct sockaddr_nl sa, da; + struct nlmsghdr *nlh; + struct iovec iov; + struct msghdr msg; + int sentcnt; + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_pid = getpid(); + sa.nl_groups = 0; + if (bind(mcsNlFd, (struct sockaddr *) &sa, sizeof(sa))) { + my_log( LOG_WARNING, errno, "%s: Unable to bind socket", __func__); + return -1; + } + + memset(&da, 0, sizeof(da)); + da.nl_family = AF_NETLINK; + da.nl_pid = 0; + da.nl_groups = 0; + + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(data_len)); + if (!nlh) { + my_log( LOG_WARNING, errno, "%s: malloc() failed", __func__); + return -1; + } + + memset(nlh, 0, NLMSG_SPACE(data_len)); + nlh->nlmsg_len = NLMSG_SPACE(data_len); + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = msg_type; + + memcpy(NLMSG_DATA(nlh), data, data_len); + + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + msg.msg_name = (void *)&da; + msg.msg_namelen = sizeof(da); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + sentcnt = sendmsg(mcsNlFd, &msg, 0); + if (sentcnt < 0) { + my_log( LOG_WARNING, 0, "%s: Failed - data_len[%d], mcsNlFd[%d], msg_type[%d]", __func__, + data_len, mcsNlFd, msg_type); + } + free(nlh); + + return sentcnt; +} + +/* + * Enable/disable the QCA MCS bridge device and bridge ports + */ +static int qca_mcs_brdev_state(bool enable) +{ + void *mcctl_br_state; + struct __mcctl_msg_header *mcctl_msghdr; + struct __mc_param_value *mc_param_val; + int br_state_msglen; + int err = 0; + + /* Prepare bridge state message structures */ + br_state_msglen = sizeof(struct __mcctl_msg_header) + sizeof(struct __mc_param_value) + 32; + mcctl_br_state = malloc(br_state_msglen); + if (!mcctl_br_state) { + my_log( LOG_ERR, errno, "Out of memory trying to allocate [%] byte(s)!", br_state_msglen ); + return -1; + } + memset(mcctl_br_state, 0, br_state_msglen); + mcctl_msghdr = (struct __mcctl_msg_header *)mcctl_br_state; + mcctl_msghdr->buf_len = sizeof(struct __mc_param_value); + mc_param_val = (struct __mc_param_value *)(mcctl_br_state + + sizeof(struct __mcctl_msg_header)); + if (enable) + mc_param_val->val = 1; + else + mc_param_val->val = 0; + + for (int i=0; i < all_brdev.num_brdev; i++) { + /* enable or disable bridge */ + strcpy(mcctl_br_state, all_brdev.brdev[i].if_name); + qca_mcs_sendmsg(mcctl_br_state, br_state_msglen, MC_MSG_SET_ENABLE); + } + + free(mcctl_br_state); + + return err; +} + +static bool qca_mcs_mcastgrp_flooded(u_int32_t mc_addr) +{ + for (int i=0; i < QCA_MCS_MAX_MCAST_GRP; i++) { + if (flooded_mc_addr[i] == 0) + break; + + if (flooded_mc_addr[i] == mc_addr) + return true; + } + + return false; +} + +static void qca_mcs_add_flood_entry(u_int32_t mc_addr) +{ + for (int i=0; i < QCA_MCS_MAX_MCAST_GRP; i++) { + if (flooded_mc_addr[i]) + continue; + + flooded_mc_addr[i] = mc_addr; + flooded_mc_addr_cnt++; + + if (flooded_mc_addr_cnt > QCA_MCS_MAX_MCAST_GRP) { + flooded_mc_addr_cnt = QCA_MCS_MAX_MCAST_GRP; + my_log( LOG_WARNING, 0, "MCastAddr count is greater than buffer size! Set to max size."); + } + + return; + } + + my_log( LOG_WARNING, 0, "Maximum[%d] multicast group flood entry reach! Unable to add more.", + QCA_MCS_MAX_MCAST_GRP ); +} + +static void qca_mcs_remove_flood_entry(u_int32_t mc_addr) +{ + bool mc_addr_found = false; + int i; + + for (i=0; i < flooded_mc_addr_cnt; i++) { + if (flooded_mc_addr[i] == mc_addr) { + mc_addr_found = true; + break; + } + } + + if (!mc_addr_found) { + my_log( LOG_WARNING, 0, "MCastAddr[%d.%d.%d.%d] not found in multcast address list", + ((char *)&mc_addr)[0], ((char *)&mc_addr)[1], + ((char *)&mc_addr)[2], ((char *)&mc_addr)[3]); + return; + } + + for (; i < flooded_mc_addr_cnt - 1; i++) { + flooded_mc_addr[i] = flooded_mc_addr[i + 1]; + } + flooded_mc_addr[i] = 0; + + flooded_mc_addr_cnt--; + if (flooded_mc_addr_cnt < 0) { + flooded_mc_addr_cnt = 0; + my_log( LOG_WARNING, 0, "MCastAddr count is negative! Reset to zero."); + } +} + +/* + * Enable/disabled a multicast group to be flooded to the bridge pors + * when the multicast router joins an upstream multicast group. + */ +void qca_mcs_flood_brport(u_int32_t mc_addr, bool is_join) +{ + void *mcctl_br_fldentry; + struct __mcctl_msg_header *mcctl_msghdr; + struct __mc_floodtbl_entry *mc_fld_entry; + int br_fldentry_msglen; + bool flooded = false; + int err = -1; + + flooded = qca_mcs_mcastgrp_flooded(mc_addr); + if (is_join && !flooded) + qca_mcs_add_flood_entry(mc_addr); + + /* Prepare bridge state message structures */ + br_fldentry_msglen = sizeof(struct __mcctl_msg_header) + + (sizeof(struct __mc_floodtbl_entry) * flooded_mc_addr_cnt); + mcctl_br_fldentry = malloc(br_fldentry_msglen); + if (!mcctl_br_fldentry) { + my_log( LOG_WARNING, errno, "%s: out of memory trying to allocate [%d] byte(s)", + __func__, br_fldentry_msglen ); + return; + } + + memset(mcctl_br_fldentry, 0, br_fldentry_msglen); + mcctl_msghdr = (struct __mcctl_msg_header *)mcctl_br_fldentry; + mcctl_msghdr->buf_len = br_fldentry_msglen - sizeof(struct __mcctl_msg_header); + mc_fld_entry = (struct __mc_floodtbl_entry *)(mcctl_br_fldentry + + sizeof(struct __mcctl_msg_header)); + + for (int i=0; i < flooded_mc_addr_cnt; i++) { + mc_fld_entry[i].group.pro = htons(0x0800); /* ETH_P_IP */ + mc_fld_entry[i].group.u.ip4 = flooded_mc_addr[i]; + } + + for (int i=0; i < all_brdev.num_brdev; i++) { + strcpy(mcctl_br_fldentry, all_brdev.brdev[i].if_name); + + for (int fei=0; fei < flooded_mc_addr_cnt; fei++) { + if (is_join || mc_fld_entry[fei].group.u.ip4 != mc_addr) { + mc_fld_entry[fei].ifcnt = all_brdev.brdev[i].port_num; + + for (int bpi=0; bpi < all_brdev.brdev[i].port_num; bpi++) { + mc_fld_entry[fei].ifindex[bpi] = all_brdev.brdev[i].port_ifidx[bpi]; + } + } + } + qca_mcs_sendmsg(mcctl_br_fldentry, br_fldentry_msglen, MC_MSG_SET_PSW_FLOOD); + } + free(mcctl_br_fldentry); + + if (!is_join && flooded) + qca_mcs_remove_flood_entry(mc_addr); +} + +bool qca_mcs_is_configured(void) +{ + return qca_mcs_configured; +} + +/* + * Setup the data structure for bridge and bridge port(s) + * + * brdevstr - bridge and bridge port delimited string + * "|||..." + */ +void qca_mcs_add_brdev(char *brdevstr) +{ + char *token; + char *delimiter = "|"; + int curIdx, i = 0; + + if (curIdx >= MC_DEF_IF_MAX) { + my_log( LOG_ERR, 0, "Max QCA MCS bridge limit[%d] reached.", MC_DEF_IF_MAX ); + } + + token = strtok(brdevstr, delimiter); + strcpy(all_brdev.brdev[curIdx].if_name, token); + all_brdev.brdev[curIdx].br_ifidx = if_nametoindex(token); + + while(token = strtok(NULL, delimiter)) { + all_brdev.brdev[curIdx].port_ifidx[i++] = if_nametoindex(token); + if (i >= MC_DEF_IF_MAX) { + my_log( LOG_ERR, 0, "Max QCA MCS bridge port limit[%d] reached.", MC_DEF_IF_MAX ); + } + } + all_brdev.brdev[curIdx].port_num = i; + all_brdev.num_brdev++; + qca_mcs_configured = true; +} + +/* + * Start QCA MCS support. + */ +void qca_mcs_start(void) +{ + if (!qca_mcs_configured) { + my_log( LOG_WARNING, 0, "QCA MCS bridges not configured. Unable to enable QCA MCS support." ); + return; + } + + mcsNlFd = socket(AF_NETLINK, SOCK_RAW, NETLINK_QCA_MC); + if ( mcsNlFd < 0 ) { + my_log( LOG_ERR, errno, "NETLINK_QCA_MC socket open" ); + } + + qca_mcs_brdev_state(true); + my_log( LOG_NOTICE, 0, "QCA MCS support enabled." ); +} + +/* + * Stop QCA MCS support. + */ +void qca_mcs_stop(void) +{ + qca_mcs_configured = false; + + qca_mcs_brdev_state(false); + my_log( LOG_NOTICE, 0, "QCA MCS support disabled." ); + + if ( mcsNlFd > 0 ) + close(mcsNlFd); +} --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,4 +19,5 @@ igmpproxy_SOURCES = \ os-qnxnto.h \ request.c \ rttable.c \ + qca-mcs.c \ syslog.c