--- /dev/null
+#
+# Copyright (C) 2019 EWSI
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=macremapper
+PKG_VERSION:=1.1.0
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
+PKG_HASH:=f9580427803123d13d50f3422623a37212034a5d72a485f9c04904f19509e4bb
+
+PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
+PKG_LICENSE:=GPL-2.0-only
+PKG_LICENSE_FILES:=kernelmod/COPYING
+
+include $(INCLUDE_DIR)/package.mk
+
+define KernelPackage/macremapper
+ SUBMENU:=Network Support
+ URL:=https://www.edgewaterwireless.com
+ VERSION:=$(LINUX_VERSION)-$(BOARD)-$(PKG_RELEASE)
+ TITLE:=Dual Channel Wi-Fi macremapper Module
+ DEPENDS:= +kmod-cfg80211 +kmod-br-netfilter
+ FILES:=$(PKG_BUILD_DIR)/kernelmod/$(PKG_NAME).$(LINUX_KMOD_SUFFIX)
+ AUTOLOAD:=$(call AutoProbe,macremapper)
+endef
+
+define KernelPackage/macremapper/description
+ Linux kernel module for implementation the DCW filtering mechanism
+endef
+
+MAKE_FLAGS += KERNEL_SRC=$(LINUX_DIR)
+MAKE_PATH:=kernelmod
+
+$(eval $(call KernelPackage,macremapper))
--- /dev/null
+--- a/kernelmod/main.c
++++ b/kernelmod/main.c
+@@ -98,8 +98,11 @@ modinit( void ) {
+
+ rv = mrm_rcdb_init();
+ if (rv != 0) return rv;
+-
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0)
+ nf_register_hook(&_hops);
++#else
++ nf_register_net_hook(&init_net, &_hops);
++#endif
+ mrm_init_ctlfile(); /* XXX not checking for failure! */
+
+ printk(KERN_INFO "MRM The MAC Address Re-Mapper is now in the kernel\n");
+@@ -110,7 +113,11 @@ modinit( void ) {
+ static void __exit
+ modexit( void ) {
+ mrm_destroy_ctlfile();
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0)
+ nf_unregister_hook(&_hops);
++#else
++ nf_unregister_net_hook(&init_net, &_hops);
++#endif
+ mrm_rcdb_destroy(); /* imperative that this happens last */
+ printk(KERN_INFO "MRM The MAC Address Re-Mapper gone bye-bye\n");
+ }
include $(TOPDIR)/rules.mk
PKG_NAME:=pillow
-PKG_VERSION:=6.1.0
+PKG_VERSION:=6.2.0
PKG_RELEASE:=1
PKG_SOURCE:=Pillow-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://files.pythonhosted.org/packages/source/P/Pillow
-PKG_HASH:=0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de
+PKG_HASH:=4548236844327a718ce3bb182ab32a16fa2050c61e334e959f554cac052fb0df
PKG_BUILD_DIR:=$(BUILD_DIR)/$(BUILD_VARIANT)-Pillow-$(PKG_VERSION)
PKG_MAINTAINER:=Alexandru Ardelean <ardeleanalex@gmail.com>
include $(TOPDIR)/rules.mk
PKG_NAME:=python-asn1crypto
-PKG_VERSION:=0.24.0
+PKG_VERSION:=1.0.1
PKG_RELEASE:=1
PKG_SOURCE:=asn1crypto-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://pypi.io/packages/source/a/asn1crypto
-PKG_HASH:=9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49
+PKG_HASH:=0b199f211ae690df3db4fd6c1c4ff976497fb1da689193e368eedbadc53d9292
PKG_LICENSE:=MIT
PKG_MAINTAINER:=Alexandru Ardelean <ardeleanalex@gmail.com>
MAJOR_VERSION:=65
MINOR_VERSION:=1
PKG_VERSION:=$(MAJOR_VERSION).$(MINOR_VERSION)
-PKG_RELEASE:=2
+PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(MAJOR_VERSION)_$(MINOR_VERSION)-src.tgz
PKG_SOURCE_URL:=https://github.com/unicode-org/icu/releases/download/release-$(MAJOR_VERSION)-$(MINOR_VERSION)
CATEGORY:=Libraries
TITLE:=International Components for Unicode
URL:=http://icu-project.org
- DEPENDS:=+libstdcpp +libpthread @!USE_UCLIBC
+ DEPENDS:=+libstdcpp +libpthread
endef
define Package/icu/description
--- /dev/null
+#
+# Copyright (C) 2019 EWSI
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=libdcwproto
+PKG_VERSION:=1.1.0
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
+PKG_HASH:=b3d12f2533eafbb293bbf27608ff39520508d955a084f33894c594f39d2f7c8e
+
+PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
+PKG_LICENSE:=Apache-2.0
+PKG_LICENSE_FILES:=COPYING
+
+PKG_INSTALL:=1
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/libdcwproto
+ SECTION:=libs
+ CATEGORY:=Libraries
+ SUBMENU:=Networking
+ TITLE:=Dual-Channel WiFi messaging library
+ URL:=https://www.edgewaterwireless.com
+ DEPENDS:=+kmod-macremapper
+endef
+
+define Package/libdcwproto/description
+ Platform-independent C library for marshaling and serializing DCW messages
+endef
+
+TARGET_CFLAGS += -ffunction-sections -fdata-sections -flto
+TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed
+
+define Build/InstallDev
+ $(INSTALL_DIR) $(1)/usr/include
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/include/*.h $(1)/usr/include/
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/$(PKG_NAME)*.so* $(1)/usr/lib/
+endef
+
+define Package/libdcwproto/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ # Note: $(INSTALL_BIN) does not keep symlinks, so use $(CP)
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/$(PKG_NAME)*.so* $(1)/usr/lib/
+endef
+
+$(eval $(call BuildPackage,libdcwproto))
--- /dev/null
+#
+# Copyright (C) 2019 EWSI
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=libdcwsocket
+PKG_VERSION:=1.1.0
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
+PKG_HASH:=71383c4d8c5f58c1299a3717d7de9a8b5dabfd51a2dcf9993248f2709908d23a
+
+PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
+PKG_LICENSE:=Apache-2.0
+PKG_LICENSE_FILES:=COPYING
+
+PKG_INSTALL:=1
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/libdcwsocket
+ SECTION:=libs
+ CATEGORY:=Libraries
+ SUBMENU:=Networking
+ TITLE:=Dual-Channel socket library
+ URL:=https://www.edgewaterwireless.com
+endef
+
+define Package/libdcwsocket/description
+ User-land C library for sending and receiving DCW "EtherType"d messages
+endef
+
+TARGET_CFLAGS += -std=c89 -ffunction-sections -fdata-sections -flto
+TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed
+
+define Build/InstallDev
+ $(INSTALL_DIR) $(1)/usr/include
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/include/*.h $(1)/usr/include/
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/$(PKG_NAME)*.so* $(1)/usr/lib/
+endef
+
+define Package/libdcwsocket/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ # Note: $(INSTALL_BIN) does not keep symlinks, so use $(CP)
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/$(PKG_NAME)*.so* $(1)/usr/lib/
+endef
+
+$(eval $(call BuildPackage,libdcwsocket))
--- /dev/null
+--- a/src/dcwsocket.c.linux
++++ b/src/dcwsocket.c.linux
+@@ -31,6 +31,7 @@
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <net/if.h>
++#include <linux/if.h>
+ #include <linux/if_packet.h>
+ #include <linux/if_ether.h>
+ #include <linux/filter.h>
include $(TOPDIR)/rules.mk
PKG_NAME:=banip
-PKG_VERSION:=0.3.0
+PKG_VERSION:=0.3.1
PKG_RELEASE:=1
PKG_LICENSE:=GPL-3.0-or-later
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
option ban_src_on '0'
option ban_src_on_6 '0'
+config source 'DoH'
+ option ban_src 'https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv4.txt'
+ option ban_src_6 'https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv6.txt'
+ option ban_src_desc 'List of public DoH providers (DNS over HTTPS) (IPv4/IPv6)'
+ option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add DoH \"\$1}'
+ option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add DoH_6 \"\$1}'
+ option ban_src_settype 'net'
+ option ban_src_ruletype 'src+dst'
+ option ban_src_on '0'
+ option ban_src_on_6 '0'
+
config source 'tor'
option ban_src 'https://check.torproject.org/exit-addresses'
option ban_src_desc 'List of Tor Exit Nodes (IPv4)'
#
LC_ALL=C
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
-ban_ver="0.3.0"
+ban_ver="0.3.1"
ban_basever=""
ban_enabled=0
ban_automatic="1"
#
f_envcheck()
{
- local util utils packages tmp cnt=0
+ local util utils packages iface tmp cnt=0 cnt_max=0
# check backup directory
#
then
while [ "${cnt}" -le 30 ]
do
- network_find_wan ban_iface
- if [ -z "${ban_iface}" ]
+ network_find_wan iface
+ if [ -n "${iface}" ] && [ -z "$(printf "%s\\n" "${ban_iface}" | grep -F "${iface}")" ]
then
- network_find_wan6 ban_iface
+ ban_iface="${ban_iface} ${iface}"
+ if [ "${cnt_max}" -eq 0 ]
+ then
+ cnt_max=$((cnt+5))
+ fi
+ fi
+ network_find_wan6 iface
+ if [ -n "${iface}" ] && [ -z "$(printf "%s\\n" "${ban_iface}" | grep -F "${iface}")" ]
+ then
+ ban_iface="${ban_iface} ${iface}"
+ if [ "${cnt_max}" -eq 0 ]
+ then
+ cnt_max=$((cnt+5))
+ fi
fi
- if [ -z "${ban_iface}" ]
+ if [ -z "${ban_iface}" ] || [ "${cnt}" -le "${cnt_max}" ]
then
network_flush_cache
cnt=$((cnt+1))
for iface in ${ban_iface}
do
network_get_device tmp "${iface}"
- if [ -n "${tmp}" ]
+ if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_dev}" | grep -F "${tmp}")" ]
then
ban_dev="${ban_dev} ${tmp}"
else
network_get_physdev tmp "${iface}"
- if [ -n "${tmp}" ]
+ if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_dev}" | grep -F "${tmp}")" ]
then
ban_dev="${ban_dev} ${tmp}"
fi
fi
network_get_subnets tmp "${iface}"
- if [ -n "${tmp}" ]
+ if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_subnets}" | grep -F "${tmp}")" ]
then
ban_subnets="${ban_subnets} ${tmp}"
fi
network_get_subnets6 tmp "${iface}"
- if [ -n "${tmp}" ]
+ if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_subnets6}" | grep -F "${tmp}")" ]
then
ban_subnets6="${ban_subnets6} ${tmp}"
fi
then
f_log "err" "wan interface(s)/device(s) (${ban_iface:-"-"}/${ban_dev:-"-"}) not found, please please check your configuration"
else
- ban_dev_all="$(${ban_ip} link show | awk 'BEGIN{FS="[@: ]"}/^[0-9:]/{if(($3!="lo")&&($3!="br-lan")){print $3}}')"
+ ban_dev_all="$(${ban_ip} link show | awk 'BEGIN{FS="[@: ]"}/^[0-9:]/{if($3!="lo"){print $3}}')"
f_jsnup "running"
f_log "info" "start banIP processing (${ban_action})"
fi
--- /dev/null
+#
+# Copyright (C) 2019 EWSI
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=dcstad
+PKG_VERSION:=1.1.0
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
+PKG_HASH:=3bed8a5051c92cd41ba3477d2db211df8f10fd6e49946f0b74cf643464c1c201
+
+PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
+PKG_LICENSE:=Apache-2.0
+PKG_LICENSE_FILES:=COPYING
+
+PKG_FIXUP:=autoreconf
+PKG_INSTALL:=1
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/dcstad
+ SECTION:=net
+ CATEGORY:=Network
+ SUBMENU:=Routing and Redirection
+ TITLE:=Dual-Channel WiFi client daemon
+ URL:=https://www.edgewaterwireless.com
+ DEPENDS:=+libdcwsocket +libdcwproto
+endef
+
+define Package/dcstad/description
+Implementation of the Dual-Channel WiFi client daemon
+endef
+
+TARGET_CFLAGS += -ffunction-sections -fdata-sections -flto
+TARGET_LDFLAGS += -Wl,--gc-sections
+
+define Package/dcstad/install
+ $(INSTALL_DIR) $(1)/bin
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/dcstad $(1)/bin/
+endef
+
+$(eval $(call BuildPackage,dcstad))
--- /dev/null
+#
+# Copyright (C) 2019 EWSI
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=dcwapd
+PKG_VERSION:=1.1.0
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
+PKG_HASH:=58e52bf4e7526b2f26319740549dbcc6f6ab505f587815ee8731e40f7fecb625
+
+PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
+PKG_LICENSE:=Apache-2.0
+PKG_LICENSE_FILES:=COPYING
+
+PKG_FIXUP:=autoreconf
+PKG_INSTALL:=1
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/uclibc++.mk
+include $(INCLUDE_DIR)/package.mk
+
+define Package/dcwapd
+ SECTION:=net
+ CATEGORY:=Network
+ SUBMENU:=Routing and Redirection
+ TITLE:=Dual-Channel WiFi AP daemon
+ URL:=https://www.edgewaterwireless.com
+ DEPENDS:=$(CXX_DEPENDS) +kmod-macremapper +libdcwsocket +libdcwproto +mrmctl +libuci
+endef
+
+define Package/dcwapd/description
+Implementation of the Dual-Channel WiFi AP daemon
+endef
+
+CONFIGURE_ARGS += \
+ --enable-platform=linuxjsonstatic \
+ --enable-shared
+
+TARGET_CXXFLAGS += -std=c++11 -DRAPIDJSON_HAS_CXX11_RVALUE_REFS=0 -ffunction-sections -fdata-sections -flto
+TARGET_LDFLAGS += -ldcwproto -ldcwsocket -lmrmfilterparser -luci -Wl,--gc-sections,--as-needed
+
+define Build/InstallDev
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/* $(1)/usr/lib/
+endef
+
+define Package/dcwapd/install
+ $(INSTALL_DIR) $(1)/bin
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/$(PKG_NAME) $(1)/bin/
+ $(INSTALL_DIR) $(1)/usr/lib
+ # Note: $(INSTALL_BIN) does not keep symlinks, so use $(CP)
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
+
+# Utility files
+ $(INSTALL_DIR) $(1)/etc/$(PKG_NAME)
+ $(INSTALL_DATA) ./files/*.inc $(1)/etc/$(PKG_NAME)/
+ $(INSTALL_BIN) ./files/*.sh $(1)/etc/$(PKG_NAME)/
+# UCI config file copy - this is here for convenience and reference only
+ $(INSTALL_DATA) ./files/dcwapd.uci $(1)/etc/$(PKG_NAME)/
+
+# UCI config file
+ $(INSTALL_DIR) $(1)/etc/config
+ $(INSTALL_DATA) ./files/dcwapd.uci $(1)/etc/config/dcwapd
+
+# Init script
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_BIN) ./files/dcwapd.init.d $(1)/etc/init.d/dcwapd
+endef
+$(eval $(call BuildPackage,dcwapd))
--- /dev/null
+#!/bin/sh
+
+#
+# Dual Channel Wi-Fi Startup Script
+#
+# This script creates the proper network bridge configuration
+# necessary for Dual Channel Wi-Fi, and starts the dcwapd daemon
+#
+
+verbose=1
+
+uciconfig=dcwapd
+
+result=
+
+# NOTE: all functions write the result to the $result variable
+
+get_channelsets()
+{
+ # default to empty
+ result=
+ channelsets=$(uci show $uciconfig | grep "=channel-set$")
+ for channelset in $channelsets; do
+ channelset=$(echo "$channelset" | sed -rn "s/$uciconfig\.(.*)=.*/\1/p")
+ result="$result $channelset"
+ done
+ if [ $verbose -eq 1 ]; then
+ echo "Channel Sets: $result" 2>&1 | logger
+ fi
+}
+
+# $1 : the channel set name
+get_channelset_enabled()
+{
+ # default to disabled
+ result=0
+ if [ -n "$1" ]; then
+ result=$(uci get $uciconfig."$1".enabled)
+ fi
+ if [ $verbose -eq 1 ]; then
+ echo "Channel Set \"$1\" Enabled: $result" 2>&1 | logger
+ fi
+}
+
+# $1 : the channel set name
+get_primary_bridge()
+{
+ result=
+ if [ -n "$1" ]; then
+ result=$(uci get $uciconfig."$1".bridge)
+ fi
+ if [ $verbose -eq 1 ]; then
+ echo "Channel Set \"$1\" Primary Bridge: $result" 2>&1 | logger
+ fi
+}
+
+# $1 : the channel set name
+get_datachannels()
+{
+ # default to empty
+ result=
+ if [ -n "$1" ]; then
+ result=$(uci get $uciconfig."$1".data_channels)
+ fi
+ if [ $verbose -eq 1 ]; then
+ echo "Channel Set \"$1\" Data Channels: $result" 2>&1 | logger
+ fi
+}
+
+# $1 : the wlan interface name
+get_wifi_iface_num()
+{
+ result=
+ if [ -n "$1" ];then
+ #result=$(echo "$1" | sed -n "s/wlan//p")
+ result=$(echo "$1" | sed -rn "s/wlan([0-9]*).*/\1/p")
+ fi
+}
+
+# $1 : the bridge name
+get_bridge_network_name()
+{
+ result=
+ if [ -n "$1" ];then
+ result=$(echo "$1" | sed -n "s/br-//p")
+ fi
+}
+
+# $1 : the wlan interface name
+set_iface_init_state()
+{
+ result=
+ if [ -n "$1" ]; then
+ iface=$1
+ # need to extract the "X" from wlanX
+ get_wifi_iface_num "$iface"
+ iface_num=$result
+ if [ -n "$iface_num" ]; then
+ # get the iface network
+ init_net=$(uci get wireless.@wifi-iface[$iface_num].network)
+ if [ -n "$init_net" ]; then
+ # if the iface network is a bridge, but doesn't start with "br-"
+ # I think we need to prepend it?
+ net_type=$(uci get network."$init_net".type)
+ if [ -n "$net_type" ] && [ "$net_type" = "bridge" ]; then
+ prefix_ok=$(echo "$init_net" | grep "^br-")
+ if [ -z "$prefix_ok" ]; then
+ init_net="br-$init_net"
+ fi
+ fi
+ fi
+
+ # make sure that the init_net section exists
+ init_net_section=$(uci get dcwapd.init_net)
+ if [ "$init_net_section" != "init_net" ]; then
+ # the section did not exist
+ uci set dcwapd.init_net=init_net
+ fi
+
+ # save the initial network
+ if [ $verbose -eq 1 ]; then
+ echo "Saving '$iface' initial network '$init_net'" 2>&1 | logger
+ fi
+ uci set $uciconfig.init_net."$iface"="$init_net"
+ uci commit
+
+ # save the initial network in the result variable
+ result=$init_net
+ fi
+ fi
+}
+
+# $1 : the wlan interface name
+get_iface_init_state()
+{
+ result=
+ if [ -n "$1" ];then
+ init_net=$(uci get $uciconfig.init_net."$iface")
+
+ # if the response starts with "uci: ", it was an error not the real result
+ err=$(echo "$init_net" | grep "^uci: ")
+ if [ -z "$err" ]; then
+ # no error, set the result
+ result=$init_net
+
+ if [ $verbose -eq 1 ]; then
+ echo "Got '$iface' initial network '$init_net'" 2>&1 | logger
+ fi
+ fi
+ fi
+}
+
+# $1 : the name of the data channel name to bring up
+datachannel_up()
+{
+ if [ -n "$1" ]; then
+ bridge=$(uci get $uciconfig."$1".bridge)
+ interfaces=$(uci get $uciconfig."$1".interfaces)
+ if [ $verbose -eq 1 ]; then
+ echo "Creating Data Channel Bridge: $bridge" 2>&1 | logger
+ fi
+
+ get_bridge_network_name "$bridge"
+ netname=$result
+ if [ -n "$netname" ]; then
+ uci set network."$netname"=interface
+ uci set network."$netname".type=bridge
+ uci set network."$netname".proto=static
+ uci set network."$netname".bridge_empty='1'
+ fi
+
+ # create the bridge
+ uci commit
+ /etc/init.d/network reload
+
+ for iface in $interfaces; do
+ # if iface is in a bridge, the bridge name should be stored in result
+ set_iface_init_state "$iface"
+ init_bridge=$result
+
+ # update uci with the new bridge info
+ get_wifi_iface_num "$iface"
+ iface_num=$result
+ if [ -n "$iface_num" ]; then
+ uci set wireless.@wifi-iface[$iface_num].network="$netname"
+ fi
+
+ # manually put the interface into the data bridge
+ # if iface is in a bridge, remove it before adding it to the data bridge
+ if [ -n "$init_bridge" ]; then
+ brctl delif "$init_bridge" "$iface" 2>&1 | logger
+ fi
+ brctl addif "$bridge" "$iface" 2>&1 | logger
+ done
+
+ # commit uci changes and reload the network
+ uci commit
+ /etc/init.d/network reload
+ #/etc/init.d/network restart
+ # while [ 1 ]; do
+ # ifconfig "$bridge" > /dev/null 2>&1
+ # if [ $? == 0 ]; then
+ # break;
+ # fi
+ # sleep 1
+ # done
+ fi
+}
+
+# $1 : the name of the data channel to bring down
+datachannel_down()
+{
+ if [ -n "$1" ]; then
+ bridge=$(uci get $uciconfig."$1".bridge)
+ interfaces=$(uci get $uciconfig."$1".interfaces)
+ for iface in $interfaces; do
+ if [ $verbose -eq 1 ]; then
+ echo "Deconfiguring Data Channel Interface: $iface" 2>&1 | logger
+ fi
+
+ # manually remove the interface from the data bridge
+ brctl delif "$bridge" "$iface" 2>&1 | logger
+
+ get_iface_init_state "$iface"
+ init_bridge=$result
+ if [ -n "$init_bridge" ]; then
+ # manually move the interface back to the original bridge
+ brctl addif "$init_bridge" "$iface" 2>&1 | logger
+
+ # update uci with the new bridge and interface configuration
+ get_wifi_iface_num "$iface"
+ iface_num=$result
+ get_bridge_network_name "$init_bridge"
+ netname=$result
+ if [ -n "$iface_num" ] && [ -n "$netname" ]; then
+ uci set wireless.@wifi-iface[$iface_num].network="$netname"
+ fi
+ fi
+ done
+ if [ $verbose -eq 1 ]; then
+ echo "Deconfiguring Data Channel Bridge: $bridge" 2>&1 | logger
+ fi
+
+ # delete the bridge from uci
+ get_bridge_network_name "$bridge"
+ netname=$result
+ if [ -n "$netname" ]; then
+ uci delete network."$netname"
+ fi
+
+ # commit uci changes and reload the network
+ uci commit
+ /etc/init.d/network reload
+ #`/etc/init.d/network restart`
+ fi
+}
--- /dev/null
+#!/bin/sh /etc/rc.common
+
+START=99
+# Setting the stop value makes the restart script unreliable when invoked by LuCI
+#STOP=0
+
+scriptdir=/etc/dcwapd
+
+#validate_section_dcwapd() {
+# uci_validate_section dcwapd general "${1}" \
+# 'enabled:bool:1'
+#}
+
+start() {
+# validate_section_dcwapd dcwapd
+
+ # only run the start script if the enabled uci option is set properly
+ enabled=$(uci get dcwapd.general.enabled)
+ if [ "${enabled}" = "1" ]; then
+ ${scriptdir}/start_dcwapd.sh
+ else
+ echo "dcwapd is disabled in UCI"
+ return 1
+ fi
+}
+
+stop() {
+ ${scriptdir}/stop_dcwapd.sh
+ # Add a sleep after stopping because an immediate restat will fail otherwise
+ sleep 1
+}
--- /dev/null
+######################################################
+# Copyright 2018 EWSI
+#
+# Licensed to the public under the Apache License 2.0.
+######################################################
+# Dual Channel Wi-Fi AP Daemon configuration
+
+###################
+# General Options #
+###################
+# The "enabled" option controls the run state of the Dual Channel Wi-Fi AP Daemon
+# 0 - disabled, 1 - enabled
+# The "tmpdir" option MUST be specified
+# option tmpdir '<path_of_temp_dir>'
+
+config general 'general'
+ option enabled 0
+ option tmpdir '/tmp/dcwapd'
+
+################
+# Channel Sets #
+################
+# Sections of type "channel-set" define a Dual Channel Wi-Fi primary channel,
+# along with it's associated data channels
+#
+# The "data_channels" option is a space-delimited list of "datachannel"-typed instance names
+
+config channel-set 'channelset0'
+ option enabled 0
+# option enabled 1
+ option ssid 'OpenWrt'
+ option bridge 'br-lan'
+ option data_channels 'datachannel0'
+
+#config channel-set 'channelset1'
+# option enabled 0
+# option ssid 'OpenWrt2'
+# option bridge 'br-lan'
+# option data_channels 'datachannel1'
+
+#################
+# Data Channels #
+#################
+# Sections of type "datachannel" define a Dual Channel Wi-Fi data channel,
+# along with it's associated bridge and wireless interfaces
+#
+# The "interfaces" option is a space-delimited list of wireless interface names
+
+config datachannel 'datachannel0'
+ option ssid 'DCW0'
+ option bridge 'br-dc0'
+ option interfaces 'wlan2 wlan5'
+
+#config datachannel 'datachannel1'
+# option ssid 'DCW1'
+# option bridge 'br-dc1'
+# option interfaces 'wlan4'
+
+####################
+# Init Net Options #
+####################
+# The "init_net" section MUST be specified
+# This section will be used to save and restore the state of the data interfaces
+config init_net 'init_net'
+
+###############
+# Filter Sets #
+###############
+# Sections of type "filter-set" define a Dual Channel Wi-Fi group of filters,
+# along with it's associated MAC address and filter rules
+#
+# The "TFP_Default" filter set MUST be defined, although it is not required
+# to have any associated filter rules
+# The "TFP_Default" filter mac option can have the value of '*', meaning match
+# all MAC addresses
+#
+# The "filters" option is a space-delimited list of "filter"-typed instance names
+
+config filter-set 'TFP_Default'
+ option mac '*'
+ option filters 'filter0 filter1'
+
+#config filter-set 'filterset0'
+# option mac '00:00:BE:EF:F0:0D'
+# option filters 'filter2'
+
+
+################
+# Filter Rules #
+################
+# Sections of type "filter" define a Dual Channel Wi-Fi filter,
+# along with it's associated filter parameters
+#
+# Any or all of the filter options may be set to '*' to match
+# all values
+
+config filter 'filter0'
+ option packet_size '*'
+ option source_ip '*'
+ option source_port '80'
+ option protocol 'tcp'
+ option dest_port '*'
+
+config filter 'filter1'
+ option packet_size '*'
+ option source_ip '*'
+ option source_port '443'
+ option protocol 'tcp'
+ option dest_port '*'
+
+#config filter 'filter2'
+# option packet_size '*'
+# option source_ip '*'
+# option source_port '22'
+# option protocol 'tcp'
+# option dest_port '*'
--- /dev/null
+#!/bin/sh
+
+#
+# Dual Channel Wi-Fi Startup Script
+#
+# This script creates the proper network bridge configuration
+# necessary for Dual Channel Wi-Fi, and starts the dcwapd daemon
+#
+
+# Note - shellcheck cannot deal with the dynamic sourcing
+# shellcheck disable=SC1090
+# which also messes with variables defined in the sourced file
+# shellcheck disable=SC2154
+scriptdir=$(dirname -- "$(readlink -f -- "$0")")
+. "$scriptdir"/dcwapd.inc
+
+get_channelsets
+# get the list of channel sets
+channelsets=$result
+
+for channelset in $channelsets; do
+ if [ -n "$channelset" ]; then
+ get_channelset_enabled "$channelset"
+ enabled=$result
+ if [ "$enabled" = "1" ]; then
+ # the channel set is enabled
+
+ # get the list of data channels used by the channel set
+ get_datachannels "$channelset"
+ datachannels=$result
+ for datachannel in $datachannels; do
+ datachannel_up "$datachannel"
+ done
+ fi
+ fi
+done
+
+# start dcwapd, sending stdout and stderr to the system log
+dcwapd 2>&1 | logger &
--- /dev/null
+#!/bin/sh
+
+#
+# Dual Channel Wi-Fi Startup Script
+#
+# This script creates the proper network bridge configuration
+# necessary for Dual Channel Wi-Fi, and starts the dcwapd daemon
+#
+
+# Note - shellcheck cannot deal with the dynamic sourcing
+# shellcheck disable=SC1090
+# which also messes with variables defined in the sourced file
+# shellcheck disable=SC2154
+scriptdir=$(dirname -- "$(readlink -f -- "$0")")
+. "$scriptdir"/dcwapd.inc
+
+pid=$(pidof dcwapd)
+if [ -n "$pid" ]; then
+ if [ "$verbose" -eq "1" ]; then
+ echo "Stopping dcwapd..." 2>&1 | logger
+ fi
+ kill "$pid"
+fi
+
+get_channelsets
+# get the list of channel sets
+channelsets=$result
+
+for channelset in $channelsets; do
+ if [ -n "$channelset" ]; then
+# we don't care if it is enabled, tear it down
+# get_channelset_enabled $channelset
+# enabled=$result
+# if [ $enabled = "1" ]; then
+# # the channel set is enabled
+
+ # get the list of data channels used by the channel set
+ get_datachannels "$channelset"
+ datachannels=$result
+ for datachannel in $datachannels; do
+ datachannel_down "$datachannel"
+ done
+# fi
+ fi
+done
--- /dev/null
+--- a/dev/null
++++ b/dcwlinux/uci_configuration_provider.h
+@@ -0,0 +1,104 @@
++#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
++#define UCI_CONFIGURATION_PROVIDER_H_INCLUDED
++
++#include "./ap_configuration.h"
++
++namespace dcwlinux {
++
++class UciConfigurationProvider : public APConfigurationProvider {
++
++ static const char *SECTION_TYPE_GENERAL;
++ static const char *SECTION_TYPE_CHANNEL_SET;
++ static const char *SECTION_TYPE_DATA_CHANNEL;
++ static const char *SECTION_TYPE_FILTER_SET;
++ static const char *SECTION_TYPE_FILTER;
++ static const char *DEFAULT_FILTER_SET_NAME;
++
++ static const char *OPTION_TMPDIR;
++ static const char *OPTION_ENABLED;
++ static const char *OPTION_SSID;
++ static const char *OPTION_BRIDGE;
++ static const char *OPTION_DATA_CHANNELS;
++ static const char *OPTION_INTERFACES;
++ static const char *OPTION_MAC_ADDRESS;
++ static const char *OPTION_FILTERS;
++ static const char *OPTION_PACKET_SIZE;
++ static const char *OPTION_SOURCE_IP;
++ static const char *OPTION_SOURCE_PORT;
++ static const char *OPTION_PROTOCOL;
++ static const char *OPTION_DEST_PORT;
++
++ static const char *FILTER_FILE_EXTENSION;
++
++ UciConfigurationProvider(const UciConfigurationProvider&); //no copy
++
++ typedef std::map<std::string, std::string> DataChannelBridgeMap;
++ struct PrimaryChannel {
++ std::string bridgeName;
++ DataChannelBridgeMap dataChannels;
++ };
++ typedef std::map<std::string, PrimaryChannel> PrimaryChannelMap;
++ typedef std::map<dcw::MacAddress, std::string> StationFilterMap;
++
++ struct uci_context *_uciContext;
++ struct uci_package *_uciPackage;
++ const char *_uciConfig;
++
++ std::string _filterDirectory;
++ PrimaryChannelMap _primaryChannels;
++ StationFilterMap _stationFilters;
++
++public:
++ UciConfigurationProvider(const char * const uciConfig); // the "config" part of UCI commands
++ virtual ~UciConfigurationProvider();
++
++ virtual void InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const;
++ virtual void GetPrimarySsids(SsidSet& output) const;
++ virtual void GetDataSsids(SsidSet& output, const char * const primarySsid) const;
++ virtual const char *GetSsidIfname(const char * const ssid) const;
++ virtual void GetStationTrafficFilterProfiles(StationTFPMap& output) const;
++};
++
++}; //namespace dcwlinux {
++
++#endif //#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
++#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
++#define UCI_CONFIGURATION_PROVIDER_H_INCLUDED
++
++#include "./ap_configuration.h"
++
++namespace dcwlinux {
++
++class UciConfigurationProvider : public APConfigurationProvider {
++ UciConfigurationProvider(const UciConfigurationProvider&); //no copy
++
++ typedef std::map<std::string, std::string> DataChannelBridgeMap;
++ struct PrimaryChannel {
++ std::string bridgeName;
++ DataChannelBridgeMap dataChannels;
++ };
++ typedef std::map<std::string, PrimaryChannel> PrimaryChannelMap;
++ typedef std::map<dcw::MacAddress, std::string> StationFilterMap;
++
++ struct uci_context *_uciContext;
++ struct uci_package *_uciPackage;
++ const char *_uciConfig;
++
++ PrimaryChannelMap _primaryChannels;
++ StationFilterMap _stationFilters;
++ CFTFPList _defaultFilters;
++
++public:
++ UciConfigurationProvider(const char * const uciConfig); // the "config" part of UCI commands
++ virtual ~UciConfigurationProvider();
++
++ virtual void InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const;
++ virtual void GetPrimarySsids(SsidSet& output) const;
++ virtual void GetDataSsids(SsidSet& output, const char * const primarySsid) const;
++ virtual const char *GetSsidIfname(const char * const ssid) const;
++ virtual void GetStationTrafficFilterProfiles(StationTFPMap& output) const;
++};
++
++}; //namespace dcwlinux {
++
++#endif //#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
+--- a/dev/null
++++ b/dcwlinux/uci_configuration_provider.cxx
+@@ -0,0 +1,365 @@
++
++#include <uci.h>
++#include <string.h>
++
++#include <stdlib.h>
++#include <stdexcept>
++#include <sys/stat.h>
++#include <cerrno>
++#include <iostream>
++#include <fstream>
++
++#include "./uci_configuration_provider.h"
++
++#include "dcwposix/filterdirscanner.h"
++#include "dcw/macaddress.h"
++#include "dcw/dcwlog.h"
++
++using namespace dcwlinux;
++
++ const char *UciConfigurationProvider::SECTION_TYPE_GENERAL = "general";
++ const char *UciConfigurationProvider::SECTION_TYPE_CHANNEL_SET = "channel-set";
++ const char *UciConfigurationProvider::SECTION_TYPE_DATA_CHANNEL = "datachannel";
++ const char *UciConfigurationProvider::SECTION_TYPE_FILTER_SET = "filter-set";
++ const char *UciConfigurationProvider::SECTION_TYPE_FILTER = "filter";
++ const char *UciConfigurationProvider::DEFAULT_FILTER_SET_NAME = "TFP_Default";
++
++ const char *UciConfigurationProvider::OPTION_TMPDIR = "tmpdir";
++ const char *UciConfigurationProvider::OPTION_ENABLED = "enabled";
++ const char *UciConfigurationProvider::OPTION_SSID = "ssid";
++ const char *UciConfigurationProvider::OPTION_BRIDGE = "bridge";
++ const char *UciConfigurationProvider::OPTION_DATA_CHANNELS = "data_channels";
++ const char *UciConfigurationProvider::OPTION_INTERFACES = "interfaces";
++ const char *UciConfigurationProvider::OPTION_MAC_ADDRESS = "mac";
++ const char *UciConfigurationProvider::OPTION_FILTERS = "filters";
++ const char *UciConfigurationProvider::OPTION_PACKET_SIZE = "packet_size";
++ const char *UciConfigurationProvider::OPTION_SOURCE_IP = "source_ip";
++ const char *UciConfigurationProvider::OPTION_SOURCE_PORT = "source_port";
++ const char *UciConfigurationProvider::OPTION_PROTOCOL = "protocol";
++ const char *UciConfigurationProvider::OPTION_DEST_PORT = "dest_port";
++
++ const char *UciConfigurationProvider::FILTER_FILE_EXTENSION = ".tfp";
++
++ UciConfigurationProvider::UciConfigurationProvider(const char * const uciConfig) : _uciConfig(uciConfig) {
++
++ //printf("*** Start UciConfigurationProvider(%s)\n", _uciConfig);
++ //printf("*** About to uci_alloc_context()\n");
++
++ _uciContext = uci_alloc_context();
++
++ //printf("*** uci_alloc_context() complete\n");
++ //printf("*** About to uci_load()\n");
++
++ if (_uciContext == NULL)
++ {
++ std::string err = "Error creating UCI context ";
++ throw std::runtime_error(err);
++ }
++
++ uci_load(_uciContext, _uciConfig, &_uciPackage);
++
++ //printf("*** uci_load complete()\n");
++
++ if (_uciPackage == NULL)
++ {
++ std::string err = "Error loading UCI package " + std::string(_uciConfig);
++ throw std::runtime_error(err);
++ }
++
++ uci_section *generalSection = uci_lookup_section(_uciContext, _uciPackage, UciConfigurationProvider::SECTION_TYPE_GENERAL);
++ if (generalSection == NULL)
++ {
++ std::string err = "Error: A general section (" + std::string(UciConfigurationProvider::SECTION_TYPE_GENERAL) + ") must be specified!";
++ throw std::runtime_error(err);
++ }
++
++ uci_option *opt_tmpdir = uci_lookup_option(_uciContext, generalSection, UciConfigurationProvider::OPTION_TMPDIR);
++ if (opt_tmpdir == NULL)
++ {
++ std::string err = "Error: A temporary directory (" + std::string(UciConfigurationProvider::OPTION_TMPDIR) + ") must be specified!";
++ throw std::runtime_error(err);
++ }
++ char *tmpdir = opt_tmpdir->v.string;
++ //printf(" *** Set tmpdir: %s\n", tmpdir);
++
++ // make sure that tmpdir exists
++ int status = mkdir(tmpdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
++ if ((status != 0) && // failure
++ (errno != EEXIST)) // the failure was not that the directory already existed
++ {
++ std::string err = "Error: Unable to create the temporary directory (tmpdir), error # " + errno;
++ throw std::runtime_error(err);
++ }
++ _filterDirectory = std::string(tmpdir);
++
++ if (uci_lookup_section(_uciContext, _uciPackage, UciConfigurationProvider::DEFAULT_FILTER_SET_NAME) == NULL)
++ {
++ std::string err = "Error: A default traffic filter profile named " + std::string(UciConfigurationProvider::DEFAULT_FILTER_SET_NAME) + " MUST exist!";
++ throw std::runtime_error(err);
++ }
++
++ // iterate over all of the sections in the package
++ uci_element *elem;
++ uci_foreach_element(&_uciPackage->sections, elem)
++ {
++ //printf("--==-- element.type: %d\n", elem->type);
++ //printf("--==-- element.name: %s\n", elem->name);
++
++ if (elem->type == UCI_TYPE_SECTION)
++ {
++ // look up the section and get it's type
++
++ uci_section *section = NULL;
++ //printf("*** Looking up section: %s\n", elem->name);
++
++ section = uci_lookup_section(_uciContext, _uciPackage, elem->name);
++
++ if ((section != NULL) && (section->type != NULL))
++ {
++ //printf(" *** Section type: %s\n", section->type);
++ if (strcmp(elem->name, UciConfigurationProvider::SECTION_TYPE_GENERAL) == 0)
++ {
++ // we already processed the general section for the tmpdir
++ }
++ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_CHANNEL_SET) == 0)
++ {
++ // the section is a channel set, populate it with the specified values
++
++ uci_option *enabled = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_ENABLED);
++ if ((enabled == NULL) || (strcmp(enabled->v.string, "1") != 0))
++ {
++ // found a disabled channel set, ignore it
++ continue;
++ }
++
++ uci_option *ssid = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_SSID);
++ uci_option *bridge = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_BRIDGE);
++ uci_option *dataChannels = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_DATA_CHANNELS);
++
++ if ((ssid != NULL) && (bridge != NULL) && (dataChannels != NULL))
++ {
++ PrimaryChannel &pc = _primaryChannels[ssid->v.string];
++ pc.bridgeName = bridge->v.string;
++
++ char dataChannels_list[255];
++ // The dataChannels option is not a list
++ //if (dataChannels->type == UCI_TYPE_LIST)
++ if (dataChannels->v.string != NULL)
++ {
++ strcpy(dataChannels_list, dataChannels->v.string);
++ std::string str_dataChannels = dataChannels->v.string;
++ size_t start_pos = 0;
++ size_t pos = 0;
++ while(start_pos != std::string::npos)
++ {
++ pos = str_dataChannels.find(" ", start_pos);
++ //printf("****** start_pos: %u, pos: %u\n", start_pos, pos);
++ std::string str_dataChannel = str_dataChannels.substr(start_pos,
++ pos == std::string::npos ? pos : pos-start_pos);
++ //printf("*** dataChannel: %s\n", str_dataChannel.c_str());
++
++ // update the start position for next loop
++ start_pos = (pos == std::string::npos ? pos : pos+1);
++
++ uci_section *dcSection = uci_lookup_section(_uciContext, _uciPackage, str_dataChannel.c_str());
++ if (dcSection != NULL)
++ {
++ uci_option *dcSsid = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_SSID);
++ uci_option *dcBridge = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_BRIDGE);
++
++ // TODO: configure dcBridge and dcInterfaces
++ //uci_option *dcInterfaces = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_INTERFACES);
++
++ if ((dcSsid != NULL) && (dcBridge != NULL))
++ {
++ pc.dataChannels[dcSsid->v.string];
++ pc.dataChannels[dcSsid->v.string] = dcBridge->v.string;
++ }
++ }
++ }
++ }
++
++ //printf("Section: %s, SSID: %s, Bridge: %s, Data Channels: %s\n", section->e.name, ssid->v.string, bridge->v.string, dataChannels_list);
++ }
++ }
++ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_DATA_CHANNEL) == 0)
++ {
++ // data channels are processed by the channel set
++ }
++ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_FILTER_SET) == 0)
++ {
++ // the section is a filter set, populate it with the specified values
++ //printf("*** filter set: %s\n", elem->name);
++
++ // create a tfp file for the sectionName
++ std::ofstream tfpFile;
++ std::string tfpFilePath =
++ tmpdir + std::string("/") +
++ std::string(elem->name) +
++ std::string(UciConfigurationProvider::FILTER_FILE_EXTENSION);
++ tfpFile.open(tfpFilePath.c_str(), std::ios::out | std::ios::trunc);
++ if (!tfpFile.is_open())
++ {
++ std::string err = "Error: Unable to open the filter file: " + tfpFilePath;
++ throw std::runtime_error(err);
++ }
++
++ const char *filterDelimiter = "\n";
++ char sFilterContents[2048];
++ sFilterContents[0] = '\0';
++
++ uci_option *filters = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_FILTERS);
++ // The filters option is not a list
++ //if ((filters != NULL) && (filters->type == UCI_TYPE_LIST))
++ if (filters != NULL)
++ {
++ //printf("*** %s.filters is a list.\n", elem->name);
++ //struct uci_element *e;
++ //uci_foreach_element(&filters->v.list, e)
++
++ std::string str_filters = filters->v.string;
++ //printf("*** STR_FILTERS: %s\n", str_filters.c_str());
++ size_t start_pos = 0;
++ size_t pos = 0;
++ while(start_pos != std::string::npos)
++ {
++ pos = str_filters.find(" ", start_pos);
++ //printf("****** start_pos: %u, pos: %u\n", start_pos, pos);
++ std::string str_filter = str_filters.substr(start_pos,
++ pos == std::string::npos ? pos : pos-start_pos);
++ //printf("*** Looking for filter section named: %s ...\n", str_filter.c_str());
++
++ // update the start position for next loop
++ start_pos = (pos == std::string::npos ? pos : pos+1);
++
++ uci_section *fSection = uci_lookup_section(_uciContext, _uciPackage, str_filter.c_str());
++ if (fSection != NULL)
++ {
++ uci_option *fPacketSize = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_PACKET_SIZE);
++ uci_option *fSourceIp = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_SOURCE_IP);
++ uci_option *fSourcePort = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_SOURCE_PORT);
++ uci_option *fProtocol = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_PROTOCOL);
++ uci_option *fDestPort = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_DEST_PORT);
++
++ if ((fPacketSize != NULL) &&
++ (fSourceIp != NULL) &&
++ (fSourcePort != NULL) &&
++ (fProtocol != NULL) &&
++ (fDestPort != NULL))
++ {
++ //printf("*** filter: %s %s:%s:%s:%s:%s\n", e->name,
++ // fPacketSize->v.string, fSourceIp->v.string, fSourcePort->v.string,
++ // fProtocol->v.string, fDestPort->v.string);
++
++ strcpy(sFilterContents, fPacketSize->v.string);
++ strcat(sFilterContents, ":");
++ strcat(sFilterContents, fSourceIp->v.string);
++ strcat(sFilterContents, ":");
++ strcat(sFilterContents, fSourcePort->v.string);
++ strcat(sFilterContents, ":");
++ strcat(sFilterContents, fProtocol->v.string);
++ strcat(sFilterContents, ":");
++ strcat(sFilterContents, fDestPort->v.string);
++ strcat(sFilterContents, filterDelimiter);
++
++ //printf("*** Writing filter contents to file: %s\n", sFilterContents);
++ tfpFile << sFilterContents;
++ }
++ else
++ {
++ std::string err = "Error parsing filter: " + str_filter;
++ throw std::runtime_error(err);
++ }
++ }
++ }
++ }
++ tfpFile.close();
++
++ // if there is a MAC address for the filter set, we need to add it to the station filters list
++ uci_option *mac = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_MAC_ADDRESS);
++ if (mac != NULL)
++ {
++ // ignore wildcard MAC address
++ if (strcmp(mac->v.string,"*") != 0)
++ {
++ //printf(" *** MAC Address: %s\n", mac->v.string);
++ _stationFilters[::dcw::MacAddress(mac->v.string)] = elem->name;
++ }
++ }
++ }
++ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_FILTER) == 0)
++ {
++ // filters are processed by the filter set
++ }
++ else
++ {
++ //std::string err = "Error: Unknown UCI section type: " + std::string(section->type);
++ //throw std::runtime_error(err);
++
++ // Don't throw an exception. It is fine for UCI to contain things that we do not know about
++ // that it may use for other purposes, like UI or internal state
++ dcwlogdbgf("Ignoring UCI section type: %s\n", section->type);
++ }
++ }
++ }
++ }
++ }
++
++ UciConfigurationProvider::~UciConfigurationProvider() {
++ uci_free_context(_uciContext);
++ }
++
++ void UciConfigurationProvider::InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const {
++ ::dcwposix::FilterdirScanner::FileFilterProfileList ffpl;
++ ::dcwposix::FilterdirScanner dirScanner(_filterDirectory.c_str());
++ dirScanner.Scan(ffpl);
++
++ for (::dcwposix::FilterdirScanner::FileFilterProfileList::const_iterator i = ffpl.begin(); i != ffpl.end(); i++) {
++ output.push_back(new ::dcw::FileTrafficFilterProfile(*i));
++ }
++ }
++
++
++ void UciConfigurationProvider::GetPrimarySsids(SsidSet& output) const {
++ for (PrimaryChannelMap::const_iterator i = _primaryChannels.begin(); i != _primaryChannels.end(); i++) {
++ output.insert(i->first);
++ }
++ }
++
++ void UciConfigurationProvider::GetDataSsids(SsidSet& output, const char * const primarySsid) const {
++ const PrimaryChannelMap::const_iterator pssid = _primaryChannels.find(primarySsid);
++ if (pssid == _primaryChannels.end()) return;
++
++ for (DataChannelBridgeMap::const_iterator i = pssid->second.dataChannels.begin(); i != pssid->second.dataChannels.end(); i++) {
++ output.insert(i->first);
++ }
++ }
++
++ const char *UciConfigurationProvider::GetSsidIfname(const char * const ssid) const {
++ PrimaryChannelMap::const_iterator pssid = _primaryChannels.find(ssid);
++ if (pssid != _primaryChannels.end()) {
++ if (pssid->second.bridgeName.empty()) {
++ return NULL;
++ }
++ return pssid->second.bridgeName.c_str();
++ }
++
++ for (pssid = _primaryChannels.begin(); pssid != _primaryChannels.end(); pssid++) {
++ const DataChannelBridgeMap& dataChannels = pssid->second.dataChannels;
++ const DataChannelBridgeMap::const_iterator dc = dataChannels.find(ssid);
++ if (dc == dataChannels.end()) continue;
++ if (dc->second.empty()) {
++ return NULL;
++ }
++ return dc->second.c_str();
++ }
++
++ return NULL;
++ }
++
++ void UciConfigurationProvider::GetStationTrafficFilterProfiles(StationTFPMap& output) const {
++ for (StationFilterMap::const_iterator i = _stationFilters.begin(); i != _stationFilters.end(); i++) {
++ output[i->first] = i->second;
++ }
++
++ }
--- /dev/null
+--- a/dcwapd.linuxjsonstatic/main.cxx
++++ b/dcwapd.linuxjsonstatic/main.cxx
+@@ -10,6 +10,7 @@
+ #include "dcwlinux/ap_configuration.h"
+ #include "dcwlinux/vap_manager.h"
+ #include "dcwlinux/json_configuration_provider.h"
++#include "dcwlinux/uci_configuration_provider.h"
+
+ #include "dcw/dcwlog.h"
+
+@@ -19,7 +20,8 @@ int
+ main( void ) {
+
+ try {
+- dcwlinux::JsonConfigurationProvider configProvider("./dcwapdconf.json");
++ //dcwlinux::JsonConfigurationProvider configProvider("./dcwapdconf.json");
++ dcwlinux::UciConfigurationProvider configProvider("dcwapd");
+
+ dcwposix::ProcessSignalManager sigman;
+ dcwposix::SelectEventReactor eventReactor;
--- /dev/null
+--- a/dcwlinux/Makefile.am
++++ b/dcwlinux/Makefile.am
+@@ -6,6 +6,7 @@ libdcwlinux_la_SOURCES =
+ ap_configuration.cxx \
+ brctlnetwork.cxx \
+ json_configuration_provider.cxx \
++ uci_configuration_provider.cxx \
+ macremapper_driver.cxx \
+ vap_manager.cxx \
+ virtual_ap.cxx
+++ /dev/null
-# Description
-
-This directory contains package files for including Dual Channel Wi-Fi (dcwifi) components in an OpenWrt build.
-
-# dcwifi Packages
-
-The dcwifi packages can be found in the menuconfig in the following locations:
-
- * dcstad: `Network -> Routing and Redirection`
- * dcwapd: `Network -> Routing and Redirection`
- * libdcwproto: `Libraries -> Networking`
- * libdcwsocket: `Libraries -> Networking`
- * macremapper: `Kernel modules -> Network Support` (listed as `kmod-macremapper`)
- * mrmctl: `Utilities`
+++ /dev/null
-#
-# Copyright (C) 2019 EWSI
-#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=dcstad
-PKG_VERSION:=1.0.0
-PKG_RELEASE:=1
-
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=3b146ea22bc5480d8264c5ea269831d25993673aa90a9e82dc2dc601a111da55
-
-PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
-PKG_LICENSE:=Apache-2.0
-PKG_LICENSE_FILES:=COPYING
-
-PKG_FIXUP:=autoreconf
-PKG_INSTALL:=1
-PKG_BUILD_PARALLEL:=1
-
-include $(INCLUDE_DIR)/package.mk
-
-define Package/dcstad
- SECTION:=net
- CATEGORY:=Network
- SUBMENU:=Routing and Redirection
- TITLE:=Dual-Channel WiFi client daemon
- URL:=https://www.edgewaterwireless.com
- DEPENDS:=+libdcwsocket +libdcwproto
-endef
-
-define Package/dcstad/description
-Implementation of the Dual-Channel WiFi client daemon
-endef
-
-TARGET_CFLAGS += -ffunction-sections -fdata-sections -flto
-TARGET_LDFLAGS += -Wl,--gc-sections
-
-define Package/dcstad/install
- $(INSTALL_DIR) $(1)/bin
- $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/dcstad $(1)/bin/
-endef
-
-$(eval $(call BuildPackage,dcstad))
+++ /dev/null
---- a/src/main.c
-+++ b/src/main.c
-@@ -190,7 +190,7 @@ main( int argc, char *argv[] ) {
- rv = 1; /* failure unless proven otherwise */
-
- /* first initialize and parse the command line */
-- bzero(&cfg, sizeof(cfg));
-+ memset(&cfg, 0, sizeof(cfg));
- parse_cmdline(&cfg, argc, argv);
-
- dcwloginfof("%s\n", "DCW Station Daemon Starting Up...");
+++ /dev/null
-#
-# Copyright (C) 2019 EWSI
-#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=dcwapd
-PKG_VERSION:=1.0.0
-PKG_RELEASE:=1
-
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=750a08abccd88d9aeda942307f76ce5711181c06f9f3e8fded5cb5ce42bac323
-
-PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
-PKG_LICENSE:=Apache-2.0
-PKG_LICENSE_FILES:=COPYING
-
-PKG_FIXUP:=autoreconf
-PKG_INSTALL:=1
-PKG_BUILD_PARALLEL:=1
-
-include $(INCLUDE_DIR)/uclibc++.mk
-include $(INCLUDE_DIR)/package.mk
-
-define Package/dcwapd
- SECTION:=net
- CATEGORY:=Network
- SUBMENU:=Routing and Redirection
- TITLE:=Dual-Channel WiFi AP daemon
- URL:=https://www.edgewaterwireless.com
- DEPENDS:=$(CXX_DEPENDS) +kmod-macremapper +libdcwsocket +libdcwproto +mrmctl +libuci
-endef
-
-define Package/dcwapd/description
-Implementation of the Dual-Channel WiFi AP daemon
-endef
-
-CONFIGURE_ARGS += \
- --enable-platform=linuxjsonstatic \
- --enable-shared
-
-TARGET_CXXFLAGS += -std=c++11 -DRAPIDJSON_HAS_CXX11_RVALUE_REFS=0 -ffunction-sections -fdata-sections -flto
-TARGET_LDFLAGS += -ldcwproto -ldcwsocket -lmrmfilterparser -luci -Wl,--gc-sections,--as-needed
-
-define Build/InstallDev
- $(INSTALL_DIR) $(1)/usr/lib
- $(CP) $(PKG_INSTALL_DIR)/usr/lib/* $(1)/usr/lib/
-endef
-
-define Package/dcwapd/install
- $(INSTALL_DIR) $(1)/bin
- $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/$(PKG_NAME) $(1)/bin/
- $(INSTALL_DIR) $(1)/usr/lib
- # Note: $(INSTALL_BIN) does not keep symlinks, so use $(CP)
- $(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
-
-# Utility files
- $(INSTALL_DIR) $(1)/etc/$(PKG_NAME)
- $(INSTALL_DATA) ./files/*.inc $(1)/etc/$(PKG_NAME)/
- $(INSTALL_BIN) ./files/*.sh $(1)/etc/$(PKG_NAME)/
-# UCI config file copy - this is here for convenience and reference only
- $(INSTALL_DATA) ./files/dcwapd.uci $(1)/etc/$(PKG_NAME)/
-
-# UCI config file
- $(INSTALL_DIR) $(1)/etc/config
- $(INSTALL_DATA) ./files/dcwapd.uci $(1)/etc/config/dcwapd
-
-# Init script
- $(INSTALL_DIR) $(1)/etc/init.d
- $(INSTALL_BIN) ./files/dcwapd.init.d $(1)/etc/init.d/dcwapd
-endef
-$(eval $(call BuildPackage,dcwapd))
+++ /dev/null
-#!/bin/sh
-
-#
-# Dual Channel Wi-Fi Startup Script
-#
-# This script creates the proper network bridge configuration
-# necessary for Dual Channel Wi-Fi, and starts the dcwapd daemon
-#
-
-verbose=1
-
-uciconfig=dcwapd
-
-result=
-
-# NOTE: all functions write the result to the $result variable
-
-get_channelsets()
-{
- # default to empty
- result=
- channelsets=$(uci show $uciconfig | grep "=channel-set$")
- for channelset in $channelsets; do
- channelset=$(echo "$channelset" | sed -rn "s/$uciconfig\.(.*)=.*/\1/p")
- result="$result $channelset"
- done
- if [ $verbose -eq 1 ]; then
- echo "Channel Sets: $result" 2>&1 | logger
- fi
-}
-
-# $1 : the channel set name
-get_channelset_enabled()
-{
- # default to disabled
- result=0
- if [ -n "$1" ]; then
- result=$(uci get $uciconfig."$1".enabled)
- fi
- if [ $verbose -eq 1 ]; then
- echo "Channel Set \"$1\" Enabled: $result" 2>&1 | logger
- fi
-}
-
-# $1 : the channel set name
-get_primary_bridge()
-{
- result=
- if [ -n "$1" ]; then
- result=$(uci get $uciconfig."$1".bridge)
- fi
- if [ $verbose -eq 1 ]; then
- echo "Channel Set \"$1\" Primary Bridge: $result" 2>&1 | logger
- fi
-}
-
-# $1 : the channel set name
-get_datachannels()
-{
- # default to empty
- result=
- if [ -n "$1" ]; then
- result=$(uci get $uciconfig."$1".data_channels)
- fi
- if [ $verbose -eq 1 ]; then
- echo "Channel Set \"$1\" Data Channels: $result" 2>&1 | logger
- fi
-}
-
-# $1 : the wlan interface name
-get_wifi_iface_num()
-{
- result=
- if [ -n "$1" ];then
- #result=$(echo "$1" | sed -n "s/wlan//p")
- result=$(echo "$1" | sed -rn "s/wlan([0-9]*).*/\1/p")
- fi
-}
-
-# $1 : the bridge name
-get_bridge_network_name()
-{
- result=
- if [ -n "$1" ];then
- result=$(echo "$1" | sed -n "s/br-//p")
- fi
-}
-
-# $1 : the wlan interface name
-set_iface_init_state()
-{
- result=
- if [ -n "$1" ]; then
- iface=$1
- # need to extract the "X" from wlanX
- get_wifi_iface_num "$iface"
- iface_num=$result
- if [ -n "$iface_num" ]; then
- # get the iface network
- init_net=$(uci get wireless.@wifi-iface[$iface_num].network)
- if [ -n "$init_net" ]; then
- # if the iface network is a bridge, but doesn't start with "br-"
- # I think we need to prepend it?
- net_type=$(uci get network."$init_net".type)
- if [ -n "$net_type" ] && [ "$net_type" = "bridge" ]; then
- prefix_ok=$(echo "$init_net" | grep "^br-")
- if [ -z "$prefix_ok" ]; then
- init_net="br-$init_net"
- fi
- fi
- fi
-
- # make sure that the init_net section exists
- init_net_section=$(uci get dcwapd.init_net)
- if [ "$init_net_section" != "init_net" ]; then
- # the section did not exist
- uci set dcwapd.init_net=init_net
- fi
-
- # save the initial network
- if [ $verbose -eq 1 ]; then
- echo "Saving '$iface' initial network '$init_net'" 2>&1 | logger
- fi
- uci set $uciconfig.init_net."$iface"="$init_net"
- uci commit
-
- # save the initial network in the result variable
- result=$init_net
- fi
- fi
-}
-
-# $1 : the wlan interface name
-get_iface_init_state()
-{
- result=
- if [ -n "$1" ];then
- init_net=$(uci get $uciconfig.init_net."$iface")
-
- # if the response starts with "uci: ", it was an error not the real result
- err=$(echo "$init_net" | grep "^uci: ")
- if [ -z "$err" ]; then
- # no error, set the result
- result=$init_net
-
- if [ $verbose -eq 1 ]; then
- echo "Got '$iface' initial network '$init_net'" 2>&1 | logger
- fi
- fi
- fi
-}
-
-# $1 : the name of the data channel name to bring up
-datachannel_up()
-{
- if [ -n "$1" ]; then
- bridge=$(uci get $uciconfig."$1".bridge)
- interfaces=$(uci get $uciconfig."$1".interfaces)
- if [ $verbose -eq 1 ]; then
- echo "Creating Data Channel Bridge: $bridge" 2>&1 | logger
- fi
-
- get_bridge_network_name "$bridge"
- netname=$result
- if [ -n "$netname" ]; then
- uci set network."$netname"=interface
- uci set network."$netname".type=bridge
- uci set network."$netname".proto=static
- uci set network."$netname".bridge_empty='1'
- fi
-
- # create the bridge
- uci commit
- /etc/init.d/network reload
-
- for iface in $interfaces; do
- # if iface is in a bridge, the bridge name should be stored in result
- set_iface_init_state "$iface"
- init_bridge=$result
-
- # update uci with the new bridge info
- get_wifi_iface_num "$iface"
- iface_num=$result
- if [ -n "$iface_num" ]; then
- uci set wireless.@wifi-iface[$iface_num].network="$netname"
- fi
-
- # manually put the interface into the data bridge
- # if iface is in a bridge, remove it before adding it to the data bridge
- if [ -n "$init_bridge" ]; then
- brctl delif "$init_bridge" "$iface" 2>&1 | logger
- fi
- brctl addif "$bridge" "$iface" 2>&1 | logger
- done
-
- # commit uci changes and reload the network
- uci commit
- /etc/init.d/network reload
- #/etc/init.d/network restart
- # while [ 1 ]; do
- # ifconfig "$bridge" > /dev/null 2>&1
- # if [ $? == 0 ]; then
- # break;
- # fi
- # sleep 1
- # done
- fi
-}
-
-# $1 : the name of the data channel to bring down
-datachannel_down()
-{
- if [ -n "$1" ]; then
- bridge=$(uci get $uciconfig."$1".bridge)
- interfaces=$(uci get $uciconfig."$1".interfaces)
- for iface in $interfaces; do
- if [ $verbose -eq 1 ]; then
- echo "Deconfiguring Data Channel Interface: $iface" 2>&1 | logger
- fi
-
- # manually remove the interface from the data bridge
- brctl delif "$bridge" "$iface" 2>&1 | logger
-
- get_iface_init_state "$iface"
- init_bridge=$result
- if [ -n "$init_bridge" ]; then
- # manually move the interface back to the original bridge
- brctl addif "$init_bridge" "$iface" 2>&1 | logger
-
- # update uci with the new bridge and interface configuration
- get_wifi_iface_num "$iface"
- iface_num=$result
- get_bridge_network_name "$init_bridge"
- netname=$result
- if [ -n "$iface_num" ] && [ -n "$netname" ]; then
- uci set wireless.@wifi-iface[$iface_num].network="$netname"
- fi
- fi
- done
- if [ $verbose -eq 1 ]; then
- echo "Deconfiguring Data Channel Bridge: $bridge" 2>&1 | logger
- fi
-
- # delete the bridge from uci
- get_bridge_network_name "$bridge"
- netname=$result
- if [ -n "$netname" ]; then
- uci delete network."$netname"
- fi
-
- # commit uci changes and reload the network
- uci commit
- /etc/init.d/network reload
- #`/etc/init.d/network restart`
- fi
-}
+++ /dev/null
-#!/bin/sh /etc/rc.common
-
-START=99
-# Setting the stop value makes the restart script unreliable when invoked by LuCI
-#STOP=0
-
-scriptdir=/etc/dcwapd
-
-#validate_section_dcwapd() {
-# uci_validate_section dcwapd general "${1}" \
-# 'enabled:bool:1'
-#}
-
-start() {
-# validate_section_dcwapd dcwapd
-
- # only run the start script if the enabled uci option is set properly
- enabled=$(uci get dcwapd.general.enabled)
- if [ "${enabled}" = "1" ]; then
- ${scriptdir}/start_dcwapd.sh
- else
- echo "dcwapd is disabled in UCI"
- return 1
- fi
-}
-
-stop() {
- ${scriptdir}/stop_dcwapd.sh
- # Add a sleep after stopping because an immediate restat will fail otherwise
- sleep 1
-}
+++ /dev/null
-######################################################
-# Copyright 2018 EWSI
-#
-# Licensed to the public under the Apache License 2.0.
-######################################################
-# Dual Channel Wi-Fi AP Daemon configuration
-
-###################
-# General Options #
-###################
-# The "enabled" option controls the run state of the Dual Channel Wi-Fi AP Daemon
-# 0 - disabled, 1 - enabled
-# The "tmpdir" option MUST be specified
-# option tmpdir '<path_of_temp_dir>'
-
-config general 'general'
- option enabled 0
- option tmpdir '/tmp/dcwapd'
-
-################
-# Channel Sets #
-################
-# Sections of type "channel-set" define a Dual Channel Wi-Fi primary channel,
-# along with it's associated data channels
-#
-# The "data_channels" option is a space-delimited list of "datachannel"-typed instance names
-
-config channel-set 'channelset0'
- option enabled 0
-# option enabled 1
- option ssid 'OpenWrt'
- option bridge 'br-lan'
- option data_channels 'datachannel0'
-
-#config channel-set 'channelset1'
-# option enabled 0
-# option ssid 'OpenWrt2'
-# option bridge 'br-lan'
-# option data_channels 'datachannel1'
-
-#################
-# Data Channels #
-#################
-# Sections of type "datachannel" define a Dual Channel Wi-Fi data channel,
-# along with it's associated bridge and wireless interfaces
-#
-# The "interfaces" option is a space-delimited list of wireless interface names
-
-config datachannel 'datachannel0'
- option ssid 'DCW0'
- option bridge 'br-dc0'
- option interfaces 'wlan2 wlan5'
-
-#config datachannel 'datachannel1'
-# option ssid 'DCW1'
-# option bridge 'br-dc1'
-# option interfaces 'wlan4'
-
-####################
-# Init Net Options #
-####################
-# The "init_net" section MUST be specified
-# This section will be used to save and restore the state of the data interfaces
-config init_net 'init_net'
-
-###############
-# Filter Sets #
-###############
-# Sections of type "filter-set" define a Dual Channel Wi-Fi group of filters,
-# along with it's associated MAC address and filter rules
-#
-# The "TFP_Default" filter set MUST be defined, although it is not required
-# to have any associated filter rules
-# The "TFP_Default" filter mac option can have the value of '*', meaning match
-# all MAC addresses
-#
-# The "filters" option is a space-delimited list of "filter"-typed instance names
-
-config filter-set 'TFP_Default'
- option mac '*'
- option filters 'filter0 filter1'
-
-#config filter-set 'filterset0'
-# option mac '00:00:BE:EF:F0:0D'
-# option filters 'filter2'
-
-
-################
-# Filter Rules #
-################
-# Sections of type "filter" define a Dual Channel Wi-Fi filter,
-# along with it's associated filter parameters
-#
-# Any or all of the filter options may be set to '*' to match
-# all values
-
-config filter 'filter0'
- option packet_size '*'
- option source_ip '*'
- option source_port '80'
- option protocol 'tcp'
- option dest_port '*'
-
-config filter 'filter1'
- option packet_size '*'
- option source_ip '*'
- option source_port '443'
- option protocol 'tcp'
- option dest_port '*'
-
-#config filter 'filter2'
-# option packet_size '*'
-# option source_ip '*'
-# option source_port '22'
-# option protocol 'tcp'
-# option dest_port '*'
+++ /dev/null
-#!/bin/sh
-
-#
-# Dual Channel Wi-Fi Startup Script
-#
-# This script creates the proper network bridge configuration
-# necessary for Dual Channel Wi-Fi, and starts the dcwapd daemon
-#
-
-# Note - shellcheck cannot deal with the dynamic sourcing
-# shellcheck disable=SC1090
-# which also messes with variables defined in the sourced file
-# shellcheck disable=SC2154
-scriptdir=$(dirname -- "$(readlink -f -- "$0")")
-. "$scriptdir"/dcwapd.inc
-
-get_channelsets
-# get the list of channel sets
-channelsets=$result
-
-for channelset in $channelsets; do
- if [ -n "$channelset" ]; then
- get_channelset_enabled "$channelset"
- enabled=$result
- if [ "$enabled" = "1" ]; then
- # the channel set is enabled
-
- # get the list of data channels used by the channel set
- get_datachannels "$channelset"
- datachannels=$result
- for datachannel in $datachannels; do
- datachannel_up "$datachannel"
- done
- fi
- fi
-done
-
-# start dcwapd, sending stdout and stderr to the system log
-dcwapd 2>&1 | logger &
+++ /dev/null
-#!/bin/sh
-
-#
-# Dual Channel Wi-Fi Startup Script
-#
-# This script creates the proper network bridge configuration
-# necessary for Dual Channel Wi-Fi, and starts the dcwapd daemon
-#
-
-# Note - shellcheck cannot deal with the dynamic sourcing
-# shellcheck disable=SC1090
-# which also messes with variables defined in the sourced file
-# shellcheck disable=SC2154
-scriptdir=$(dirname -- "$(readlink -f -- "$0")")
-. "$scriptdir"/dcwapd.inc
-
-pid=$(pidof dcwapd)
-if [ -n "$pid" ]; then
- if [ "$verbose" -eq "1" ]; then
- echo "Stopping dcwapd..." 2>&1 | logger
- fi
- kill "$pid"
-fi
-
-get_channelsets
-# get the list of channel sets
-channelsets=$result
-
-for channelset in $channelsets; do
- if [ -n "$channelset" ]; then
-# we don't care if it is enabled, tear it down
-# get_channelset_enabled $channelset
-# enabled=$result
-# if [ $enabled = "1" ]; then
-# # the channel set is enabled
-
- # get the list of data channels used by the channel set
- get_datachannels "$channelset"
- datachannels=$result
- for datachannel in $datachannels; do
- datachannel_down "$datachannel"
- done
-# fi
- fi
-done
+++ /dev/null
---- a/dev/null
-+++ b/dcwlinux/uci_configuration_provider.h
-@@ -0,0 +1,104 @@
-+#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
-+#define UCI_CONFIGURATION_PROVIDER_H_INCLUDED
-+
-+#include "./ap_configuration.h"
-+
-+namespace dcwlinux {
-+
-+class UciConfigurationProvider : public APConfigurationProvider {
-+
-+ static const char *SECTION_TYPE_GENERAL;
-+ static const char *SECTION_TYPE_CHANNEL_SET;
-+ static const char *SECTION_TYPE_DATA_CHANNEL;
-+ static const char *SECTION_TYPE_FILTER_SET;
-+ static const char *SECTION_TYPE_FILTER;
-+ static const char *DEFAULT_FILTER_SET_NAME;
-+
-+ static const char *OPTION_TMPDIR;
-+ static const char *OPTION_ENABLED;
-+ static const char *OPTION_SSID;
-+ static const char *OPTION_BRIDGE;
-+ static const char *OPTION_DATA_CHANNELS;
-+ static const char *OPTION_INTERFACES;
-+ static const char *OPTION_MAC_ADDRESS;
-+ static const char *OPTION_FILTERS;
-+ static const char *OPTION_PACKET_SIZE;
-+ static const char *OPTION_SOURCE_IP;
-+ static const char *OPTION_SOURCE_PORT;
-+ static const char *OPTION_PROTOCOL;
-+ static const char *OPTION_DEST_PORT;
-+
-+ static const char *FILTER_FILE_EXTENSION;
-+
-+ UciConfigurationProvider(const UciConfigurationProvider&); //no copy
-+
-+ typedef std::map<std::string, std::string> DataChannelBridgeMap;
-+ struct PrimaryChannel {
-+ std::string bridgeName;
-+ DataChannelBridgeMap dataChannels;
-+ };
-+ typedef std::map<std::string, PrimaryChannel> PrimaryChannelMap;
-+ typedef std::map<dcw::MacAddress, std::string> StationFilterMap;
-+
-+ struct uci_context *_uciContext;
-+ struct uci_package *_uciPackage;
-+ const char *_uciConfig;
-+
-+ std::string _filterDirectory;
-+ PrimaryChannelMap _primaryChannels;
-+ StationFilterMap _stationFilters;
-+
-+public:
-+ UciConfigurationProvider(const char * const uciConfig); // the "config" part of UCI commands
-+ virtual ~UciConfigurationProvider();
-+
-+ virtual void InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const;
-+ virtual void GetPrimarySsids(SsidSet& output) const;
-+ virtual void GetDataSsids(SsidSet& output, const char * const primarySsid) const;
-+ virtual const char *GetSsidIfname(const char * const ssid) const;
-+ virtual void GetStationTrafficFilterProfiles(StationTFPMap& output) const;
-+};
-+
-+}; //namespace dcwlinux {
-+
-+#endif //#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
-+#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
-+#define UCI_CONFIGURATION_PROVIDER_H_INCLUDED
-+
-+#include "./ap_configuration.h"
-+
-+namespace dcwlinux {
-+
-+class UciConfigurationProvider : public APConfigurationProvider {
-+ UciConfigurationProvider(const UciConfigurationProvider&); //no copy
-+
-+ typedef std::map<std::string, std::string> DataChannelBridgeMap;
-+ struct PrimaryChannel {
-+ std::string bridgeName;
-+ DataChannelBridgeMap dataChannels;
-+ };
-+ typedef std::map<std::string, PrimaryChannel> PrimaryChannelMap;
-+ typedef std::map<dcw::MacAddress, std::string> StationFilterMap;
-+
-+ struct uci_context *_uciContext;
-+ struct uci_package *_uciPackage;
-+ const char *_uciConfig;
-+
-+ PrimaryChannelMap _primaryChannels;
-+ StationFilterMap _stationFilters;
-+ CFTFPList _defaultFilters;
-+
-+public:
-+ UciConfigurationProvider(const char * const uciConfig); // the "config" part of UCI commands
-+ virtual ~UciConfigurationProvider();
-+
-+ virtual void InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const;
-+ virtual void GetPrimarySsids(SsidSet& output) const;
-+ virtual void GetDataSsids(SsidSet& output, const char * const primarySsid) const;
-+ virtual const char *GetSsidIfname(const char * const ssid) const;
-+ virtual void GetStationTrafficFilterProfiles(StationTFPMap& output) const;
-+};
-+
-+}; //namespace dcwlinux {
-+
-+#endif //#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
---- a/dev/null
-+++ b/dcwlinux/uci_configuration_provider.cxx
-@@ -0,0 +1,365 @@
-+
-+#include <uci.h>
-+#include <string.h>
-+
-+#include <stdlib.h>
-+#include <stdexcept>
-+#include <sys/stat.h>
-+#include <cerrno>
-+#include <iostream>
-+#include <fstream>
-+
-+#include "./uci_configuration_provider.h"
-+
-+#include "dcwposix/filterdirscanner.h"
-+#include "dcw/macaddress.h"
-+#include "dcw/dcwlog.h"
-+
-+using namespace dcwlinux;
-+
-+ const char *UciConfigurationProvider::SECTION_TYPE_GENERAL = "general";
-+ const char *UciConfigurationProvider::SECTION_TYPE_CHANNEL_SET = "channel-set";
-+ const char *UciConfigurationProvider::SECTION_TYPE_DATA_CHANNEL = "datachannel";
-+ const char *UciConfigurationProvider::SECTION_TYPE_FILTER_SET = "filter-set";
-+ const char *UciConfigurationProvider::SECTION_TYPE_FILTER = "filter";
-+ const char *UciConfigurationProvider::DEFAULT_FILTER_SET_NAME = "TFP_Default";
-+
-+ const char *UciConfigurationProvider::OPTION_TMPDIR = "tmpdir";
-+ const char *UciConfigurationProvider::OPTION_ENABLED = "enabled";
-+ const char *UciConfigurationProvider::OPTION_SSID = "ssid";
-+ const char *UciConfigurationProvider::OPTION_BRIDGE = "bridge";
-+ const char *UciConfigurationProvider::OPTION_DATA_CHANNELS = "data_channels";
-+ const char *UciConfigurationProvider::OPTION_INTERFACES = "interfaces";
-+ const char *UciConfigurationProvider::OPTION_MAC_ADDRESS = "mac";
-+ const char *UciConfigurationProvider::OPTION_FILTERS = "filters";
-+ const char *UciConfigurationProvider::OPTION_PACKET_SIZE = "packet_size";
-+ const char *UciConfigurationProvider::OPTION_SOURCE_IP = "source_ip";
-+ const char *UciConfigurationProvider::OPTION_SOURCE_PORT = "source_port";
-+ const char *UciConfigurationProvider::OPTION_PROTOCOL = "protocol";
-+ const char *UciConfigurationProvider::OPTION_DEST_PORT = "dest_port";
-+
-+ const char *UciConfigurationProvider::FILTER_FILE_EXTENSION = ".tfp";
-+
-+ UciConfigurationProvider::UciConfigurationProvider(const char * const uciConfig) : _uciConfig(uciConfig) {
-+
-+ //printf("*** Start UciConfigurationProvider(%s)\n", _uciConfig);
-+ //printf("*** About to uci_alloc_context()\n");
-+
-+ _uciContext = uci_alloc_context();
-+
-+ //printf("*** uci_alloc_context() complete\n");
-+ //printf("*** About to uci_load()\n");
-+
-+ if (_uciContext == NULL)
-+ {
-+ std::string err = "Error creating UCI context ";
-+ throw std::runtime_error(err);
-+ }
-+
-+ uci_load(_uciContext, _uciConfig, &_uciPackage);
-+
-+ //printf("*** uci_load complete()\n");
-+
-+ if (_uciPackage == NULL)
-+ {
-+ std::string err = "Error loading UCI package " + std::string(_uciConfig);
-+ throw std::runtime_error(err);
-+ }
-+
-+ uci_section *generalSection = uci_lookup_section(_uciContext, _uciPackage, UciConfigurationProvider::SECTION_TYPE_GENERAL);
-+ if (generalSection == NULL)
-+ {
-+ std::string err = "Error: A general section (" + std::string(UciConfigurationProvider::SECTION_TYPE_GENERAL) + ") must be specified!";
-+ throw std::runtime_error(err);
-+ }
-+
-+ uci_option *opt_tmpdir = uci_lookup_option(_uciContext, generalSection, UciConfigurationProvider::OPTION_TMPDIR);
-+ if (opt_tmpdir == NULL)
-+ {
-+ std::string err = "Error: A temporary directory (" + std::string(UciConfigurationProvider::OPTION_TMPDIR) + ") must be specified!";
-+ throw std::runtime_error(err);
-+ }
-+ char *tmpdir = opt_tmpdir->v.string;
-+ //printf(" *** Set tmpdir: %s\n", tmpdir);
-+
-+ // make sure that tmpdir exists
-+ int status = mkdir(tmpdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-+ if ((status != 0) && // failure
-+ (errno != EEXIST)) // the failure was not that the directory already existed
-+ {
-+ std::string err = "Error: Unable to create the temporary directory (tmpdir), error # " + errno;
-+ throw std::runtime_error(err);
-+ }
-+ _filterDirectory = std::string(tmpdir);
-+
-+ if (uci_lookup_section(_uciContext, _uciPackage, UciConfigurationProvider::DEFAULT_FILTER_SET_NAME) == NULL)
-+ {
-+ std::string err = "Error: A default traffic filter profile named " + std::string(UciConfigurationProvider::DEFAULT_FILTER_SET_NAME) + " MUST exist!";
-+ throw std::runtime_error(err);
-+ }
-+
-+ // iterate over all of the sections in the package
-+ uci_element *elem;
-+ uci_foreach_element(&_uciPackage->sections, elem)
-+ {
-+ //printf("--==-- element.type: %d\n", elem->type);
-+ //printf("--==-- element.name: %s\n", elem->name);
-+
-+ if (elem->type == UCI_TYPE_SECTION)
-+ {
-+ // look up the section and get it's type
-+
-+ uci_section *section = NULL;
-+ //printf("*** Looking up section: %s\n", elem->name);
-+
-+ section = uci_lookup_section(_uciContext, _uciPackage, elem->name);
-+
-+ if ((section != NULL) && (section->type != NULL))
-+ {
-+ //printf(" *** Section type: %s\n", section->type);
-+ if (strcmp(elem->name, UciConfigurationProvider::SECTION_TYPE_GENERAL) == 0)
-+ {
-+ // we already processed the general section for the tmpdir
-+ }
-+ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_CHANNEL_SET) == 0)
-+ {
-+ // the section is a channel set, populate it with the specified values
-+
-+ uci_option *enabled = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_ENABLED);
-+ if ((enabled == NULL) || (strcmp(enabled->v.string, "1") != 0))
-+ {
-+ // found a disabled channel set, ignore it
-+ continue;
-+ }
-+
-+ uci_option *ssid = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_SSID);
-+ uci_option *bridge = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_BRIDGE);
-+ uci_option *dataChannels = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_DATA_CHANNELS);
-+
-+ if ((ssid != NULL) && (bridge != NULL) && (dataChannels != NULL))
-+ {
-+ PrimaryChannel &pc = _primaryChannels[ssid->v.string];
-+ pc.bridgeName = bridge->v.string;
-+
-+ char dataChannels_list[255];
-+ // The dataChannels option is not a list
-+ //if (dataChannels->type == UCI_TYPE_LIST)
-+ if (dataChannels->v.string != NULL)
-+ {
-+ strcpy(dataChannels_list, dataChannels->v.string);
-+ std::string str_dataChannels = dataChannels->v.string;
-+ size_t start_pos = 0;
-+ size_t pos = 0;
-+ while(start_pos != std::string::npos)
-+ {
-+ pos = str_dataChannels.find(" ", start_pos);
-+ //printf("****** start_pos: %u, pos: %u\n", start_pos, pos);
-+ std::string str_dataChannel = str_dataChannels.substr(start_pos,
-+ pos == std::string::npos ? pos : pos-start_pos);
-+ //printf("*** dataChannel: %s\n", str_dataChannel.c_str());
-+
-+ // update the start position for next loop
-+ start_pos = (pos == std::string::npos ? pos : pos+1);
-+
-+ uci_section *dcSection = uci_lookup_section(_uciContext, _uciPackage, str_dataChannel.c_str());
-+ if (dcSection != NULL)
-+ {
-+ uci_option *dcSsid = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_SSID);
-+ uci_option *dcBridge = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_BRIDGE);
-+
-+ // TODO: configure dcBridge and dcInterfaces
-+ //uci_option *dcInterfaces = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_INTERFACES);
-+
-+ if ((dcSsid != NULL) && (dcBridge != NULL))
-+ {
-+ pc.dataChannels[dcSsid->v.string];
-+ pc.dataChannels[dcSsid->v.string] = dcBridge->v.string;
-+ }
-+ }
-+ }
-+ }
-+
-+ //printf("Section: %s, SSID: %s, Bridge: %s, Data Channels: %s\n", section->e.name, ssid->v.string, bridge->v.string, dataChannels_list);
-+ }
-+ }
-+ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_DATA_CHANNEL) == 0)
-+ {
-+ // data channels are processed by the channel set
-+ }
-+ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_FILTER_SET) == 0)
-+ {
-+ // the section is a filter set, populate it with the specified values
-+ //printf("*** filter set: %s\n", elem->name);
-+
-+ // create a tfp file for the sectionName
-+ std::ofstream tfpFile;
-+ std::string tfpFilePath =
-+ tmpdir + std::string("/") +
-+ std::string(elem->name) +
-+ std::string(UciConfigurationProvider::FILTER_FILE_EXTENSION);
-+ tfpFile.open(tfpFilePath.c_str(), std::ios::out | std::ios::trunc);
-+ if (!tfpFile.is_open())
-+ {
-+ std::string err = "Error: Unable to open the filter file: " + tfpFilePath;
-+ throw std::runtime_error(err);
-+ }
-+
-+ const char *filterDelimiter = "\n";
-+ char sFilterContents[2048];
-+ sFilterContents[0] = '\0';
-+
-+ uci_option *filters = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_FILTERS);
-+ // The filters option is not a list
-+ //if ((filters != NULL) && (filters->type == UCI_TYPE_LIST))
-+ if (filters != NULL)
-+ {
-+ //printf("*** %s.filters is a list.\n", elem->name);
-+ //struct uci_element *e;
-+ //uci_foreach_element(&filters->v.list, e)
-+
-+ std::string str_filters = filters->v.string;
-+ //printf("*** STR_FILTERS: %s\n", str_filters.c_str());
-+ size_t start_pos = 0;
-+ size_t pos = 0;
-+ while(start_pos != std::string::npos)
-+ {
-+ pos = str_filters.find(" ", start_pos);
-+ //printf("****** start_pos: %u, pos: %u\n", start_pos, pos);
-+ std::string str_filter = str_filters.substr(start_pos,
-+ pos == std::string::npos ? pos : pos-start_pos);
-+ //printf("*** Looking for filter section named: %s ...\n", str_filter.c_str());
-+
-+ // update the start position for next loop
-+ start_pos = (pos == std::string::npos ? pos : pos+1);
-+
-+ uci_section *fSection = uci_lookup_section(_uciContext, _uciPackage, str_filter.c_str());
-+ if (fSection != NULL)
-+ {
-+ uci_option *fPacketSize = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_PACKET_SIZE);
-+ uci_option *fSourceIp = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_SOURCE_IP);
-+ uci_option *fSourcePort = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_SOURCE_PORT);
-+ uci_option *fProtocol = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_PROTOCOL);
-+ uci_option *fDestPort = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_DEST_PORT);
-+
-+ if ((fPacketSize != NULL) &&
-+ (fSourceIp != NULL) &&
-+ (fSourcePort != NULL) &&
-+ (fProtocol != NULL) &&
-+ (fDestPort != NULL))
-+ {
-+ //printf("*** filter: %s %s:%s:%s:%s:%s\n", e->name,
-+ // fPacketSize->v.string, fSourceIp->v.string, fSourcePort->v.string,
-+ // fProtocol->v.string, fDestPort->v.string);
-+
-+ strcpy(sFilterContents, fPacketSize->v.string);
-+ strcat(sFilterContents, ":");
-+ strcat(sFilterContents, fSourceIp->v.string);
-+ strcat(sFilterContents, ":");
-+ strcat(sFilterContents, fSourcePort->v.string);
-+ strcat(sFilterContents, ":");
-+ strcat(sFilterContents, fProtocol->v.string);
-+ strcat(sFilterContents, ":");
-+ strcat(sFilterContents, fDestPort->v.string);
-+ strcat(sFilterContents, filterDelimiter);
-+
-+ //printf("*** Writing filter contents to file: %s\n", sFilterContents);
-+ tfpFile << sFilterContents;
-+ }
-+ else
-+ {
-+ std::string err = "Error parsing filter: " + str_filter;
-+ throw std::runtime_error(err);
-+ }
-+ }
-+ }
-+ }
-+ tfpFile.close();
-+
-+ // if there is a MAC address for the filter set, we need to add it to the station filters list
-+ uci_option *mac = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_MAC_ADDRESS);
-+ if (mac != NULL)
-+ {
-+ // ignore wildcard MAC address
-+ if (strcmp(mac->v.string,"*") != 0)
-+ {
-+ //printf(" *** MAC Address: %s\n", mac->v.string);
-+ _stationFilters[::dcw::MacAddress(mac->v.string)] = elem->name;
-+ }
-+ }
-+ }
-+ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_FILTER) == 0)
-+ {
-+ // filters are processed by the filter set
-+ }
-+ else
-+ {
-+ //std::string err = "Error: Unknown UCI section type: " + std::string(section->type);
-+ //throw std::runtime_error(err);
-+
-+ // Don't throw an exception. It is fine for UCI to contain things that we do not know about
-+ // that it may use for other purposes, like UI or internal state
-+ dcwlogdbgf("Ignoring UCI section type: %s\n", section->type);
-+ }
-+ }
-+ }
-+ }
-+ }
-+
-+ UciConfigurationProvider::~UciConfigurationProvider() {
-+ uci_free_context(_uciContext);
-+ }
-+
-+ void UciConfigurationProvider::InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const {
-+ ::dcwposix::FilterdirScanner::FileFilterProfileList ffpl;
-+ ::dcwposix::FilterdirScanner dirScanner(_filterDirectory.c_str());
-+ dirScanner.Scan(ffpl);
-+
-+ for (::dcwposix::FilterdirScanner::FileFilterProfileList::const_iterator i = ffpl.begin(); i != ffpl.end(); i++) {
-+ output.push_back(new ::dcw::FileTrafficFilterProfile(*i));
-+ }
-+ }
-+
-+
-+ void UciConfigurationProvider::GetPrimarySsids(SsidSet& output) const {
-+ for (PrimaryChannelMap::const_iterator i = _primaryChannels.begin(); i != _primaryChannels.end(); i++) {
-+ output.insert(i->first);
-+ }
-+ }
-+
-+ void UciConfigurationProvider::GetDataSsids(SsidSet& output, const char * const primarySsid) const {
-+ const PrimaryChannelMap::const_iterator pssid = _primaryChannels.find(primarySsid);
-+ if (pssid == _primaryChannels.end()) return;
-+
-+ for (DataChannelBridgeMap::const_iterator i = pssid->second.dataChannels.begin(); i != pssid->second.dataChannels.end(); i++) {
-+ output.insert(i->first);
-+ }
-+ }
-+
-+ const char *UciConfigurationProvider::GetSsidIfname(const char * const ssid) const {
-+ PrimaryChannelMap::const_iterator pssid = _primaryChannels.find(ssid);
-+ if (pssid != _primaryChannels.end()) {
-+ if (pssid->second.bridgeName.empty()) {
-+ return NULL;
-+ }
-+ return pssid->second.bridgeName.c_str();
-+ }
-+
-+ for (pssid = _primaryChannels.begin(); pssid != _primaryChannels.end(); pssid++) {
-+ const DataChannelBridgeMap& dataChannels = pssid->second.dataChannels;
-+ const DataChannelBridgeMap::const_iterator dc = dataChannels.find(ssid);
-+ if (dc == dataChannels.end()) continue;
-+ if (dc->second.empty()) {
-+ return NULL;
-+ }
-+ return dc->second.c_str();
-+ }
-+
-+ return NULL;
-+ }
-+
-+ void UciConfigurationProvider::GetStationTrafficFilterProfiles(StationTFPMap& output) const {
-+ for (StationFilterMap::const_iterator i = _stationFilters.begin(); i != _stationFilters.end(); i++) {
-+ output[i->first] = i->second;
-+ }
-+
-+ }
+++ /dev/null
---- a/dcwapd.linuxjsonstatic/main.cxx
-+++ b/dcwapd.linuxjsonstatic/main.cxx
-@@ -10,6 +10,7 @@
- #include "dcwlinux/ap_configuration.h"
- #include "dcwlinux/vap_manager.h"
- #include "dcwlinux/json_configuration_provider.h"
-+#include "dcwlinux/uci_configuration_provider.h"
-
- #include "dcw/dcwlog.h"
-
-@@ -19,7 +20,8 @@ int
- main( void ) {
-
- try {
-- dcwlinux::JsonConfigurationProvider configProvider("./dcwapdconf.json");
-+ //dcwlinux::JsonConfigurationProvider configProvider("./dcwapdconf.json");
-+ dcwlinux::UciConfigurationProvider configProvider("dcwapd");
-
- dcwposix::ProcessSignalManager sigman;
- dcwposix::SelectEventReactor eventReactor;
+++ /dev/null
---- a/dcwlinux/Makefile.am
-+++ b/dcwlinux/Makefile.am
-@@ -6,6 +6,7 @@ libdcwlinux_la_SOURCES =
- ap_configuration.cxx \
- brctlnetwork.cxx \
- json_configuration_provider.cxx \
-+ uci_configuration_provider.cxx \
- macremapper_driver.cxx \
- vap_manager.cxx \
- virtual_ap.cxx
+++ /dev/null
---- a/dcw/controller.cxx
-+++ b/dcw/controller.cxx
-@@ -195,7 +195,7 @@ void Controller::OnStationUnjoin(const MacAddress& primaryMacAddr, const Message
- //remove any channel bondings matching the provided data channel mac addresses
- for (unsigned i = 0; i < m.data_macaddr_count; i++) {
- const ::dcw::MacAddress dcaddr(m.data_macaddrs[i]);
-- const ::dcw::TrafficPolicy::DataChannelMap::iterator dcmEntry = state.policy.dataChannels.find(dcaddr);
-+ ::dcw::TrafficPolicy::DataChannelMap::iterator dcmEntry = state.policy.dataChannels.find(dcaddr);
- if (dcmEntry == state.policy.dataChannels.end()) continue;
- if (dcmEntry->second == NULL) {
- dcwlogwarnf("Data channel MAC address %s on client %s is not currently bonded\n", dcaddr.ToString().c_str(), primaryMacAddr.ToString().c_str());
-@@ -238,7 +238,7 @@ void Controller::OnStationAck(const MacAddress& primaryMacAddr, const Message& m
- dcwlogdbgf("Got a station ACK from %s\n", primaryMacAddr.ToString().c_str());
-
- // first make sure this client has actually sent a join first...
-- const ClientStateMap::iterator client = _clients.find(primaryMacAddr);
-+ ClientStateMap::iterator client = _clients.find(primaryMacAddr);
- if (client == _clients.end()) {
- dcwlogerrf("Got a client ACK without a station join from %s\n", primaryMacAddr.ToString().c_str());
- Message reply(DCWMSG_AP_REJECT_STA);
---- a/dcwposix/processsignalmanager.cxx
-+++ b/dcwposix/processsignalmanager.cxx
-@@ -40,7 +40,7 @@ ProcessSignalManager::~ProcessSignalManager() {
- }
-
- void ProcessSignalManager::RegisterEventHandler(const int signum, ::dcwposix::ProcessSignalManager::EventHandler& eventHandler) {
-- const SignalMap::iterator i = _sigmap.find(signum);
-+ SignalMap::iterator i = _sigmap.find(signum);
- if (i == _sigmap.end()) {
- //be sure to preseve the old signal when inserting a new "unseen" signal
- _sigmap[signum].insert(&eventHandler);
-@@ -53,7 +53,7 @@ void ProcessSignalManager::RegisterEventHandler(const int signum, ::dcwposix::Pr
- }
-
- void ProcessSignalManager::UnRegisterEventHandler(const int signum, ::dcwposix::ProcessSignalManager::EventHandler& eventHandler) {
-- const SignalMap::iterator i = _sigmap.find(signum);
-+ SignalMap::iterator i = _sigmap.find(signum);
-
- if (i == _sigmap.end()) {
- dcwlogwarnf("Attempting to unregister handler %p non-registered process signal #%d\n", &eventHandler, signum);
\ No newline at end of file
+++ /dev/null
---- a/dcwlinux/macremapper_driver.cxx
-+++ b/dcwlinux/macremapper_driver.cxx
-@@ -174,7 +174,7 @@ void MacRemapperDriver::ApplyClientTrafficPolicy(const dcw::MacAddress& primaryA
- }
-
- //populate our remap ioctl()
-- bzero(&re, sizeof(re));
-+ memset(&re, 0, sizeof(re));
- strncpy(re.filter_name, policy.trafficFilterProfile->GetName(), sizeof(re.filter_name));
- memcpy(re.match_macaddr, primaryAddr.Value, sizeof(re.match_macaddr));
-
+++ /dev/null
-#
-# Copyright (C) 2019 EWSI
-#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=libdcwproto
-PKG_VERSION:=1.0.0
-PKG_RELEASE:=1
-
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=5bda395c648aa12eb90515c29024029738fde1a8f73a2cbc553be1c6962c2629
-
-PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
-PKG_LICENSE:=Apache-2.0
-PKG_LICENSE_FILES:=COPYING
-
-PKG_INSTALL:=1
-PKG_BUILD_PARALLEL:=1
-
-include $(INCLUDE_DIR)/package.mk
-
-define Package/libdcwproto
- SECTION:=libs
- CATEGORY:=Libraries
- SUBMENU:=Networking
- TITLE:=Dual-Channel WiFi messaging library
- URL:=https://www.edgewaterwireless.com
- DEPENDS:=+kmod-macremapper
-endef
-
-define Package/libdcwproto/description
- Platform-independent C library for marshaling and serializing DCW messages
-endef
-
-TARGET_CFLAGS += -ffunction-sections -fdata-sections -flto
-TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed
-
-define Build/InstallDev
- $(INSTALL_DIR) $(1)/usr/include
- $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/include/*.h $(1)/usr/include/
- $(INSTALL_DIR) $(1)/usr/lib
- $(CP) $(PKG_INSTALL_DIR)/usr/lib/$(PKG_NAME)*.so* $(1)/usr/lib/
-endef
-
-define Package/libdcwproto/install
- $(INSTALL_DIR) $(1)/usr/lib
- # Note: $(INSTALL_BIN) does not keep symlinks, so use $(CP)
- $(CP) $(PKG_INSTALL_DIR)/usr/lib/$(PKG_NAME)*.so* $(1)/usr/lib/
-endef
-
-$(eval $(call BuildPackage,libdcwproto))
+++ /dev/null
---- a/src/dcwproto.c
-+++ b/src/dcwproto.c
-@@ -20,12 +20,8 @@
-
-
-
--#ifdef WIN32
--#define bzero(ptr, size) memset(ptr, 0, size)
--#else
- #include <config.h>
- #include <strings.h>
--#endif
- #include <dcwproto.h>
-
- #include <string.h>
-@@ -95,7 +91,7 @@ dcwmsg_marshal_sta_ack(struct dcwmsg_sta_ack * const output, const unsigned char
-
- /* copy in the data ssid string bytes */
- if (buf_len < copy_size) return 0;
-- bzero(output->bonded_data_channels[i].ssid, sizeof(output->bonded_data_channels[i].ssid));
-+ memset(output->bonded_data_channels[i].ssid, 0, sizeof(output->bonded_data_channels[i].ssid));
- memcpy(output->bonded_data_channels[i].ssid, buf, copy_size);
- buf_len -= copy_size;
- buf += copy_size;
-@@ -134,7 +130,7 @@ dcwmsg_marshal_ap_accept_sta(struct dcwmsg_ap_accept_sta * const output, const u
-
- /* copy in the data ssid string bytes */
- if (buf_len < copy_size) return 0;
-- bzero(output->data_ssids[i], sizeof(output->data_ssids[i]));
-+ memset(output->data_ssids[i], 0, sizeof(output->data_ssids[i]));
- memcpy(output->data_ssids[i], buf, copy_size);
- buf_len -= copy_size;
- buf += copy_size;
+++ /dev/null
-#
-# Copyright (C) 2019 EWSI
-#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=libdcwsocket
-PKG_VERSION:=1.0.0
-PKG_RELEASE:=1
-
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
-PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
-PKG_HASH:=c7f6c69a5246fe1f184c21585f0805ceaca09c3c087ae439ded7ed4d25c7a3fa
-
-PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
-PKG_LICENSE:=Apache-2.0
-PKG_LICENSE_FILES:=COPYING
-
-PKG_INSTALL:=1
-PKG_BUILD_PARALLEL:=1
-
-include $(INCLUDE_DIR)/package.mk
-
-define Package/libdcwsocket
- SECTION:=libs
- CATEGORY:=Libraries
- SUBMENU:=Networking
- TITLE:=Dual-Channel socket library
- URL:=https://www.edgewaterwireless.com
-endef
-
-define Package/libdcwsocket/description
- User-land C library for sending and receiving DCW "EtherType"d messages
-endef
-
-TARGET_CFLAGS += -std=c89 -ffunction-sections -fdata-sections -flto
-TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed
-
-define Build/InstallDev
- $(INSTALL_DIR) $(1)/usr/include
- $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/include/*.h $(1)/usr/include/
- $(INSTALL_DIR) $(1)/usr/lib
- $(CP) $(PKG_INSTALL_DIR)/usr/lib/$(PKG_NAME)*.so* $(1)/usr/lib/
-endef
-
-define Package/libdcwsocket/install
- $(INSTALL_DIR) $(1)/usr/lib
- # Note: $(INSTALL_BIN) does not keep symlinks, so use $(CP)
- $(CP) $(PKG_INSTALL_DIR)/usr/lib/$(PKG_NAME)*.so* $(1)/usr/lib/
-endef
-
-$(eval $(call BuildPackage,libdcwsocket))
+++ /dev/null
---- a/src/dcwsocket.c.linux
-+++ b/src/dcwsocket.c.linux
-@@ -100,10 +100,10 @@ dcwsock_open(const char * const ifname) {
- }
-
- /* sanitize our data structs... defensive */
-- bzero(rv, sizeof(*rv));
-- bzero(&ifr, sizeof(ifr));
-- bzero(&sall, sizeof(sall));
-- bzero(&sfp, sizeof(sfp));
-+ memset(rv, 0, sizeof(*rv));
-+ memset(&ifr, 0, sizeof(ifr));
-+ memset(&sall, 0, sizeof(sall));
-+ memset(&sfp, 0, sizeof(sfp));
-
- /* open a raw socket... "ETH_P_ALL" says take EVERYTHING
- (this means that it is IMPERATIVE to apply a filter)
-diff --git a/src/dcwsocket.c.osx b/src/dcwsocket.c.osx
-index abead10..75cda2f 100644
---- a/src/dcwsocket.c.osx
-+++ b/src/dcwsocket.c.osx
-@@ -90,10 +90,10 @@ dcwsock_open(const char * const ifname) {
- }
-
- /* sanitize our data structs... defensive */
-- bzero(rv, sizeof(*rv));
-- bzero(&dmx_desc, sizeof(dmx_desc));
-- bzero(&proto_desc, sizeof(proto_desc));
-- bzero(&snd, sizeof(snd));
-+ memset(rv, 0, sizeof(*rv));
-+ memset(&dmx_desc, 0, sizeof(dmx_desc));
-+ memset(&proto_desc, 0, sizeof(proto_desc));
-+ memset(&snd, 0, sizeof(snd));
-
- /* open a "NDRV" socket... */
- rv->fd = socket(PF_NDRV, SOCK_RAW, 0);
-@@ -201,7 +201,7 @@ dcwsock_send( dcw_socket_t s, const void * const buf, const unsigned buf_size, c
- fill out a link-level sockaddr cause we can only
- use sendto() with PF_NDRV...
- */
-- bzero(&sdl, sizeof(sdl));
-+ memset(&sdl, 0, sizeof(sdl));
- sdl.sdl_len = sizeof(sdl);
- sdl.sdl_index = 0;
- sdl.sdl_type = IFT_ETHER;
+++ /dev/null
---- a/src/dcwsocket.c.linux
-+++ b/src/dcwsocket.c.linux
-@@ -31,6 +31,7 @@
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <net/if.h>
-+#include <linux/if.h>
- #include <linux/if_packet.h>
- #include <linux/if_ether.h>
- #include <linux/filter.h>
+++ /dev/null
-#
-# Copyright (C) 2019 EWSI
-#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-include $(TOPDIR)/rules.mk
-include $(INCLUDE_DIR)/kernel.mk
-
-PKG_NAME:=macremapper
-PKG_VERSION:=1.0.0
-PKG_RELEASE:=1
-
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=f054201dd805ce005b89606a507b58a5717d383a4339c69dfdc02f0202935437
-
-PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
-PKG_LICENSE:=GPL-2.0-only
-PKG_LICENSE_FILES:=kernelmod/COPYING
-
-include $(INCLUDE_DIR)/package.mk
-
-define KernelPackage/macremapper
- SUBMENU:=Network Support
- URL:=https://www.edgewaterwireless.com
- VERSION:=$(LINUX_VERSION)-$(BOARD)-$(PKG_RELEASE)
- TITLE:=Dual Channel Wi-Fi macremapper Module
- DEPENDS:= +kmod-cfg80211 +kmod-br-netfilter
- FILES:=$(PKG_BUILD_DIR)/kernelmod/$(PKG_NAME).$(LINUX_KMOD_SUFFIX)
- AUTOLOAD:=$(call AutoProbe,macremapper)
-endef
-
-define KernelPackage/macremapper/description
- Linux kernel module for implementation the DCW filtering mechanism
-endef
-
-MAKE_FLAGS += KERNEL_SRC=$(LINUX_DIR)
-MAKE_PATH:=kernelmod
-
-$(eval $(call KernelPackage,macremapper))
+++ /dev/null
---- a/kernelmod/main.c
-+++ b/kernelmod/main.c
-@@ -91,8 +91,11 @@ modinit( void ) {
-
- rv = mrm_rcdb_init();
- if (rv != 0) return rv;
--
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0)
- nf_register_hook(&_hops);
-+#else
-+ nf_register_net_hook(&init_net, &_hops);
-+#endif
- mrm_init_ctlfile(); /* XXX not checking for failure! */
-
- printk(KERN_INFO "MRM The MAC Address Re-Mapper is now in the kernel\n");
-@@ -103,7 +106,11 @@ modinit( void ) {
- static void __exit
- modexit( void ) {
- mrm_destroy_ctlfile();
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0)
- nf_unregister_hook(&_hops);
-+#else
-+ nf_unregister_net_hook(&init_net, &_hops);
-+#endif
- mrm_rcdb_destroy(); /* imperative that this happens last */
- printk(KERN_INFO "MRM The MAC Address Re-Mapper gone bye-bye\n");
- }
+++ /dev/null
-#
-# Copyright (C) 2019 EWSI
-#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=mrmctl
-PKG_VERSION:=1.0.0
-PKG_RELEASE:=1
-
-PKG_SOURCE:=macremapper-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://codeload.github.com/ewsi/macremapper/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=f054201dd805ce005b89606a507b58a5717d383a4339c69dfdc02f0202935437
-PKG_BUILD_DIR:=$(BUILD_DIR)/macremapper-$(PKG_VERSION)
-
-PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
-PKG_LICENSE:=Apache-2.0
-PKG_LICENSE_FILES:=userland/COPYING
-
-PKG_INSTALL:=1
-PKG_BUILD_PARALLEL:=1
-
-include $(INCLUDE_DIR)/package.mk
-
-define Package/mrmctl
- SECTION:=utils
- CATEGORY:=Utilities
- TITLE:=mrmctl utility (macremapper kernel module)
- URL:=https://www.edgewaterwireless.com
- DEPENDS:= +kmod-macremapper
-endef
-
-define Package/mrmctl/description
- Command-line utility to manually manipulate the macremapper kernel module
-endef
-
-MAKE_PATH:=userland
-CONFIGURE_PATH:=userland
-CONFIGURE_ARGS += \
- --enable-shared
-
-TARGET_CFLAGS += -std=c89 -ffunction-sections -fdata-sections -flto
-TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed
-
-define Build/InstallDev
- $(INSTALL_DIR) $(1)/usr/include
- $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/include/*.h $(1)/usr/include/
- $(INSTALL_DIR) $(1)/usr/lib
- $(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
-endef
-
-define Package/mrmctl/install
- $(INSTALL_DIR) $(1)/bin
- $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/$(PKG_NAME) $(1)/bin/
-
- $(INSTALL_DIR) $(1)/usr/lib
- $(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
-endef
-
-$(eval $(call BuildPackage,mrmctl))
+++ /dev/null
---- a/userland/mrmctl/mrmctl.c
-+++ b/userland/mrmctl/mrmctl.c
-@@ -139,7 +139,7 @@ remap(int argc, char **argv) {
- if (argc < 5) return 1; /* defensive */
-
- /* initialize variables and put things into human-readable variable names */
-- bzero(&re, sizeof(re));
-+ memset(&re, 0, sizeof(re));
- filter_name = argv[2];
- match_macaddr = argv[3];
-
-diff --git a/userland/mrmfilterparser/mrm_filter_conf_parser.c b/userland/mrmfilterparser/mrm_filter_conf_parser.c
-index 926fa76..f5c54c1 100644
---- a/userland/mrmfilterparser/mrm_filter_conf_parser.c
-+++ b/userland/mrmfilterparser/mrm_filter_conf_parser.c
-@@ -319,7 +319,7 @@ filter_file_loadf(struct mrm_filter_config * const output, FILE * const f) {
- return -1;
-
- output->rules_active = 0;
-- bzero(output->rules, sizeof(output->rules)); /* defensive */
-+ memset(output->rules, 0, sizeof(output->rules)); /* defensive */
-
- for (linenum = 1; fgets(buf, sizeof(buf), f) != NULL; linenum++) {
-
TITLE:=mosquitto - an MQTT message broker
URL:=http://www.mosquitto.org/
MAINTAINER:=Karl Palsson <karlp@etactica.com>
- DEPENDS:= +librt +libuuid
+ DEPENDS:= +librt
USERID:=mosquitto=200:mosquitto=200
endef
--- /dev/null
+#
+# Copyright (C) 2019 EWSI
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=mrmctl
+PKG_VERSION:=1.1.0
+PKG_RELEASE:=1
+
+PKG_SOURCE:=macremapper-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/ewsi/macremapper/tar.gz/v$(PKG_VERSION)?
+PKG_HASH:=f9580427803123d13d50f3422623a37212034a5d72a485f9c04904f19509e4bb
+PKG_BUILD_DIR:=$(BUILD_DIR)/macremapper-$(PKG_VERSION)
+
+PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
+PKG_LICENSE:=Apache-2.0
+PKG_LICENSE_FILES:=userland/COPYING
+
+PKG_INSTALL:=1
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/mrmctl
+ SECTION:=net
+ CATEGORY:=Network
+ SUBMENU:=Routing and Redirection
+ TITLE:=Dual-Channel WiFi macremapper utility
+ URL:=https://www.edgewaterwireless.com
+ DEPENDS:= +kmod-macremapper
+endef
+
+define Package/mrmctl/description
+ Command-line utility to manually manipulate the macremapper kernel module
+endef
+
+MAKE_PATH:=userland
+CONFIGURE_PATH:=userland
+CONFIGURE_ARGS += \
+ --enable-shared
+
+TARGET_CFLAGS += -std=c89 -ffunction-sections -fdata-sections -flto
+TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed
+
+define Build/InstallDev
+ $(INSTALL_DIR) $(1)/usr/include
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/include/*.h $(1)/usr/include/
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
+endef
+
+define Package/mrmctl/install
+ $(INSTALL_DIR) $(1)/bin
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/$(PKG_NAME) $(1)/bin/
+
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
+endef
+
+$(eval $(call BuildPackage,mrmctl))