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 <careys@edgewaterwireless.com> Signed-off-by: Carey Sonsino <csonsino@gmail.com> Much help from @neheblilik-openwrt-22.03
@ -0,0 +1,14 @@ | |||
# 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` |
@ -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.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)) |
@ -0,0 +1,11 @@ | |||
--- 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..."); |
@ -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.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)) |
@ -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 | |||
} |
@ -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 | |||
} |
@ -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 '<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 '*' |
@ -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 & |
@ -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 |
@ -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<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; | |||
+ } | |||
+ | |||
+ } |
@ -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; |
@ -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 |
@ -0,0 +1,40 @@ | |||
--- 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); |
@ -0,0 +1,11 @@ | |||
--- 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)); | |||
@ -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.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)) |
@ -0,0 +1,33 @@ | |||
--- 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; |
@ -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.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)) |
@ -0,0 +1,45 @@ | |||
--- 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; |
@ -0,0 +1,10 @@ | |||
--- 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> |
@ -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.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)) |
@ -0,0 +1,27 @@ | |||
--- 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"); | |||
} |
@ -0,0 +1,63 @@ | |||
# | |||
# 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)) |
@ -0,0 +1,24 @@ | |||
--- 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++) { | |||