packages/net/igmpproxy/patches/900-qca-mcs-support.patch
2024-04-23 23:09:56 +08:00

621 lines
17 KiB
Diff

--- 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 <linux/netlink.h>
+#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
+ * "<bridge dev>|<bridge port 1>|<bridge port 2>|..."
+ */
+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