igmpproxy: add support for QCA MCS module

This commit is contained in:
lean 2022-02-16 16:08:55 +08:00
parent b5e83ce6d1
commit b211c4397e
4 changed files with 845 additions and 5 deletions

View File

@ -8,12 +8,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=igmpproxy
PKG_VERSION:=0.3
PKG_RELEASE:=1
PKG_VERSION:=0.2.1
PKG_RELEASE:=4
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/pali/igmpproxy/releases/download/${PKG_VERSION}/
PKG_HASH:=d1fc244cb2fbbf99f720bda3e841fe59ece9b6919073790b4b892739b1b844eb
PKG_HASH:=d351e623037390f575c1203d9cbb7ba33a8bdef85a3c5e1d2901c5a2a38449a1
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
include $(INCLUDE_DIR)/package.mk
@ -35,15 +35,43 @@ define Package/igmpproxy/description
only IGMP signalling (Internet Group Management Protocol).
endef
define Package/igmpproxy/config
if PACKAGE_igmpproxy
if PACKAGE_kmod-qca-mcs
config PACKAGE_IGMPROXY_QCA_MCS_SUPPORT
bool "Enable QCA Multicast Snooping Module Support"
default y
help
This option enables support for the QCA Multicast Snooping module.
Enabling this will enable multicast traffic to be accelerated by
the QCA NSS ECM module.
endif
endif
endef
define Package/igmpproxy/conffiles
/etc/config/igmpproxy
endef
TARGET_CFLAGS += -Dlog=igmpproxy_log
ifneq ($(CONFIG_PACKAGE_IGMPROXY_QCA_MCS_SUPPORT),)
CONFIG_SUFFIX=.qca-mcs
TARGET_CFLAGS += -DIGMPPROXY_QCA_MCS_SUPPORT
endif
define Build/Compile
cd $(PKG_BUILD_DIR) && aclocal
$(MAKE) -C $(PKG_BUILD_DIR)/src \
CC="$(TARGET_CC)" \
CFLAGS="$(TARGET_CFLAGS) -std=gnu99"
endef
define Package/igmpproxy/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/igmpproxy.config $(1)/etc/config/igmpproxy
$(INSTALL_CONF) ./files/igmpproxy.config$(CONFIG_SUFFIX) $(1)/etc/config/igmpproxy
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/igmpproxy.init $(1)/etc/init.d/igmpproxy
$(INSTALL_BIN) ./files/igmpproxy.init$(CONFIG_SUFFIX) $(1)/etc/init.d/igmpproxy
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/igmpproxy $(1)/usr/sbin/
endef

View File

@ -0,0 +1,27 @@
config igmpproxy
option quickleave 1
# option verbose [0-3](none, minimal[default], more, maximum)
config phyint
option network wan
option zone wan
option direction upstream
list altnet 192.168.1.0/24
config phyint
option network lan
option zone lan
option direction downstream
# This section configures the QCA MCS support
# Uncomment to enable QCA NSS ECM multicast acceleration.
#
# 'lan' should be the LAN bridge device
# 'brport' should be the bridge port(s)
#
# Up to 16 bridges with up to 16 bridge ports(per bridge) can be configured.
#config phyint
# option network lan
# option zone lan
# option direction qca-mcs-bridge
# list brport eth1.1

View File

@ -0,0 +1,164 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2010-2014 OpenWrt.org
START=99
USE_PROCD=1
PROG=/usr/sbin/igmpproxy
CONFIGFILE=/var/etc/igmpproxy.conf
igmp_header() {
local quickleave verbose
config_get_bool quickleave "$1" quickleave 0
config_get verbose "$1" verbose 1
[ $verbose = "0" ] && logopts="-d"
[ $verbose = "2" ] && logopts="-v"
[ $verbose = "3" ] && logopts="-v -v"
mkdir -p /var/etc
rm -f /var/etc/igmpproxy.conf
[ $quickleave -gt 0 ] && echo "quickleave" >> /var/etc/igmpproxy.conf
[ -L /etc/igmpproxy.conf ] || ln -nsf /var/etc/igmpproxy.conf /etc/igmpproxy.conf
}
igmp_add_phyint() {
local network direction altnets device up brports
config_get network $1 network
config_get direction $1 direction
config_get altnets $1 altnet
config_get brports $1 brport
local status="$(ubus -S call "network.interface.$network" status)"
[ -n "$status" ] || return
json_load "$status"
json_get_var device l3_device
json_get_var up up
[ -n "$device" -a "$up" = "1" ] || {
procd_append_param error "$network is not up"
return;
}
append netdevs "$device"
[ "$direction" = "upstream" ] && has_upstream=1
echo -e "\nphyint $device $direction ratelimit 0 threshold 1" >> /var/etc/igmpproxy.conf
if [ -n "$altnets" ]; then
local altnet
for altnet in $altnets; do
echo -e "\taltnet $altnet" >> /var/etc/igmpproxy.conf
done
fi
if [ -n "$brports" ]; then
local brport
for brport in $brports; do
echo -e "\tbrport $brport" >> /var/etc/igmpproxy.conf
done
fi
}
igmp_add_network() {
local network
config_get network $1 network
procd_add_interface_trigger "interface.*" $network /etc/init.d/igmpproxy reload
}
igmp_add_firewall_routing() {
config_get direction $1 direction
config_get zone $1 zone
if [ "$direction" != "downstream" ] || [ -z "$zone" ]; then
return 0
fi
# First drop SSDP packets then accept all other multicast
json_add_object ""
json_add_string type rule
json_add_string src "$upstream"
json_add_string dest "$zone"
json_add_string family ipv4
json_add_string proto udp
json_add_string dest_ip "239.255.255.250"
json_add_string target DROP
json_close_object
json_add_object ""
json_add_string type rule
json_add_string src "$upstream"
json_add_string dest "$zone"
json_add_string family ipv4
json_add_string proto udp
json_add_string dest_ip "224.0.0.0/4"
json_add_string target ACCEPT
json_close_object
}
igmp_add_firewall_network() {
config_get direction $1 direction
config_get zone $1 zone
[ ! -z "$zone" ] || return
json_add_object ""
json_add_string type rule
json_add_string src "$zone"
json_add_string family ipv4
json_add_string proto igmp
json_add_string target ACCEPT
json_close_object
[ "$direction" = "upstream" ] && {
upstream="$zone"
config_foreach igmp_add_firewall_routing phyint
}
}
service_triggers() {
procd_add_reload_trigger "igmpproxy"
config_foreach igmp_add_network phyint
}
start_service() {
has_upstream=
netdevs=
logopts=
config_load igmpproxy
config_foreach igmp_header igmpproxy
config_foreach igmp_add_phyint phyint
[ -n "$has_upstream" ] || return
procd_open_instance
procd_set_param command $PROG '-n'
[ -n "$logopts" ] && procd_append_param command $logopts
procd_append_param command $CONFIGFILE
procd_set_param file $CONFIGFILE
procd_set_param netdev $netdevs
procd_set_param respawn
procd_open_data
json_add_array firewall
config_foreach igmp_add_firewall_network phyint
json_close_array
procd_close_data
procd_close_instance
}
service_started() {
procd_set_config_changed firewall
}
stop_service() {
procd_set_config_changed firewall
}

View File

@ -0,0 +1,621 @@
--- 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..
*/
@@ -132,17 +136,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) {
@@ -244,6 +256,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();
@@ -273,6 +288,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;
@@ -345,6 +364,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;
@@ -359,6 +392,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
@@ -122,6 +122,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
@@ -152,9 +158,21 @@ int main( int ArgCn, char *ArgVc[] ) {
}
}
+#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
@@ -71,6 +71,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
+
// Socket for sending join or leave requests.
int mcGroupSock = 0;
@@ -159,6 +164,12 @@ static void sendJoinLeaveUpstream(struct
joinMcGroup( getMcGroupSock(), 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));
@@ -174,6 +185,12 @@ static void sendJoinLeaveUpstream(struct
leaveMcGroup( getMcGroupSock(), 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
}
}
}
--- a/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));
+
--- a/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
@@ -21,4 +21,5 @@ igmpproxy_SOURCES = \
request.c \
rttable.c \
syslog.c \
- udpsock.c
+ udpsock.c \
+ qca-mcs.c