From 82a2e3f55f6d568d5d10b91206f3b494737e135d Mon Sep 17 00:00:00 2001 From: Carey Sonsino Date: Tue, 15 Oct 2019 20:54:01 +0000 Subject: [PATCH] dcwifi: Add Dual Channel Wi-Fi component packages dcstad: Dual Channel Wi-Fi Station Daemon dcwapd: Dual Channel Wi-Fi Access Point Daemon libdcwproto: Dual Channel Wi-Fi Protocol Library libdcwsocket: Dual Channel Wi-Fi Socket Library macremapper: MAC Address Remapper Linux Kernel Module mrmctl: Userland tool to get/set remap rules Signed-off-by: Carey Sonsino Signed-off-by: Carey Sonsino --- kernel/macremapper/Makefile | 42 ++ .../macremapper/patches/01_fix_nf_hooks.patch | 27 + libs/libdcwproto/Makefile | 56 +++ libs/libdcwsocket/Makefile | 56 +++ .../patches/02_fix_storage_size_error.patch | 10 + net/dcstad/Makefile | 49 ++ net/dcwapd/Makefile | 76 +++ net/dcwapd/files/dcwapd.inc | 256 ++++++++++ net/dcwapd/files/dcwapd.init.d | 31 ++ net/dcwapd/files/dcwapd.uci | 116 +++++ net/dcwapd/files/start_dcwapd.sh | 39 ++ net/dcwapd/files/stop_dcwapd.sh | 45 ++ .../patches/01_add_uci_config_provider.patch | 475 ++++++++++++++++++ .../patches/02_use_uci_config_provider.patch | 20 + ..._add_uci_config_provider_to_Makefile.patch | 10 + net/mrmctl/Makefile | 64 +++ 16 files changed, 1372 insertions(+) create mode 100644 kernel/macremapper/Makefile create mode 100644 kernel/macremapper/patches/01_fix_nf_hooks.patch create mode 100644 libs/libdcwproto/Makefile create mode 100644 libs/libdcwsocket/Makefile create mode 100644 libs/libdcwsocket/patches/02_fix_storage_size_error.patch create mode 100644 net/dcstad/Makefile create mode 100644 net/dcwapd/Makefile create mode 100644 net/dcwapd/files/dcwapd.inc create mode 100755 net/dcwapd/files/dcwapd.init.d create mode 100644 net/dcwapd/files/dcwapd.uci create mode 100755 net/dcwapd/files/start_dcwapd.sh create mode 100755 net/dcwapd/files/stop_dcwapd.sh create mode 100644 net/dcwapd/patches/01_add_uci_config_provider.patch create mode 100644 net/dcwapd/patches/02_use_uci_config_provider.patch create mode 100644 net/dcwapd/patches/03_add_uci_config_provider_to_Makefile.patch create mode 100644 net/mrmctl/Makefile diff --git a/kernel/macremapper/Makefile b/kernel/macremapper/Makefile new file mode 100644 index 0000000000..69416c260c --- /dev/null +++ b/kernel/macremapper/Makefile @@ -0,0 +1,42 @@ +# +# 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 +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) ARCH=$(LINUX_KARCH) +MAKE_PATH:=kernelmod + +$(eval $(call KernelPackage,macremapper)) diff --git a/kernel/macremapper/patches/01_fix_nf_hooks.patch b/kernel/macremapper/patches/01_fix_nf_hooks.patch new file mode 100644 index 0000000000..29cb421f80 --- /dev/null +++ b/kernel/macremapper/patches/01_fix_nf_hooks.patch @@ -0,0 +1,27 @@ +--- 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"); + } diff --git a/libs/libdcwproto/Makefile b/libs/libdcwproto/Makefile new file mode 100644 index 0000000000..37905b8545 --- /dev/null +++ b/libs/libdcwproto/Makefile @@ -0,0 +1,56 @@ +# +# 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 +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)) diff --git a/libs/libdcwsocket/Makefile b/libs/libdcwsocket/Makefile new file mode 100644 index 0000000000..73a4c3a62a --- /dev/null +++ b/libs/libdcwsocket/Makefile @@ -0,0 +1,56 @@ +# +# 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 +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)) diff --git a/libs/libdcwsocket/patches/02_fix_storage_size_error.patch b/libs/libdcwsocket/patches/02_fix_storage_size_error.patch new file mode 100644 index 0000000000..431b093df0 --- /dev/null +++ b/libs/libdcwsocket/patches/02_fix_storage_size_error.patch @@ -0,0 +1,10 @@ +--- a/src/dcwsocket.c.linux ++++ b/src/dcwsocket.c.linux +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include diff --git a/net/dcstad/Makefile b/net/dcstad/Makefile new file mode 100644 index 0000000000..3112b0c1c2 --- /dev/null +++ b/net/dcstad/Makefile @@ -0,0 +1,49 @@ +# +# 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 +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)) diff --git a/net/dcwapd/Makefile b/net/dcwapd/Makefile new file mode 100644 index 0000000000..0fe3362981 --- /dev/null +++ b/net/dcwapd/Makefile @@ -0,0 +1,76 @@ +# +# 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 +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)) diff --git a/net/dcwapd/files/dcwapd.inc b/net/dcwapd/files/dcwapd.inc new file mode 100644 index 0000000000..c4b44f1b18 --- /dev/null +++ b/net/dcwapd/files/dcwapd.inc @@ -0,0 +1,256 @@ +#!/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 +} diff --git a/net/dcwapd/files/dcwapd.init.d b/net/dcwapd/files/dcwapd.init.d new file mode 100755 index 0000000000..6f6a48fb23 --- /dev/null +++ b/net/dcwapd/files/dcwapd.init.d @@ -0,0 +1,31 @@ +#!/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 +} diff --git a/net/dcwapd/files/dcwapd.uci b/net/dcwapd/files/dcwapd.uci new file mode 100644 index 0000000000..b24ec6f0d0 --- /dev/null +++ b/net/dcwapd/files/dcwapd.uci @@ -0,0 +1,116 @@ +###################################################### +# 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 '' + +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 '*' diff --git a/net/dcwapd/files/start_dcwapd.sh b/net/dcwapd/files/start_dcwapd.sh new file mode 100755 index 0000000000..8a8c11bc6b --- /dev/null +++ b/net/dcwapd/files/start_dcwapd.sh @@ -0,0 +1,39 @@ +#!/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 & diff --git a/net/dcwapd/files/stop_dcwapd.sh b/net/dcwapd/files/stop_dcwapd.sh new file mode 100755 index 0000000000..a360a8288e --- /dev/null +++ b/net/dcwapd/files/stop_dcwapd.sh @@ -0,0 +1,45 @@ +#!/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 diff --git a/net/dcwapd/patches/01_add_uci_config_provider.patch b/net/dcwapd/patches/01_add_uci_config_provider.patch new file mode 100644 index 0000000000..afb034bdd8 --- /dev/null +++ b/net/dcwapd/patches/01_add_uci_config_provider.patch @@ -0,0 +1,475 @@ +--- 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 DataChannelBridgeMap; ++ struct PrimaryChannel { ++ std::string bridgeName; ++ DataChannelBridgeMap dataChannels; ++ }; ++ typedef std::map PrimaryChannelMap; ++ typedef std::map 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 DataChannelBridgeMap; ++ struct PrimaryChannel { ++ std::string bridgeName; ++ DataChannelBridgeMap dataChannels; ++ }; ++ typedef std::map PrimaryChannelMap; ++ typedef std::map 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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; ++ } ++ ++ } diff --git a/net/dcwapd/patches/02_use_uci_config_provider.patch b/net/dcwapd/patches/02_use_uci_config_provider.patch new file mode 100644 index 0000000000..db1037d948 --- /dev/null +++ b/net/dcwapd/patches/02_use_uci_config_provider.patch @@ -0,0 +1,20 @@ +--- 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; diff --git a/net/dcwapd/patches/03_add_uci_config_provider_to_Makefile.patch b/net/dcwapd/patches/03_add_uci_config_provider_to_Makefile.patch new file mode 100644 index 0000000000..2980c853d5 --- /dev/null +++ b/net/dcwapd/patches/03_add_uci_config_provider_to_Makefile.patch @@ -0,0 +1,10 @@ +--- 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 diff --git a/net/mrmctl/Makefile b/net/mrmctl/Makefile new file mode 100644 index 0000000000..a595cecbe1 --- /dev/null +++ b/net/mrmctl/Makefile @@ -0,0 +1,64 @@ +# +# 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 +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)) -- 2.30.2