diff --git a/ffda-wireless-rate-limiter/Makefile b/ffda-wireless-rate-limiter/Makefile new file mode 100644 index 00000000..a575a200 --- /dev/null +++ b/ffda-wireless-rate-limiter/Makefile @@ -0,0 +1,48 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=ffda-wireless-rate-limiter +PKG_RELEASE:=1 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL=https://github.com/blocktrron/wireless-rate-limiter.git +PKG_SOURCE_DATE:=2024-08-24 +PKG_SOURCE_VERSION:=1fedb08ba7ce347d5c3f7c8ddcac8a5e2f5d14b6 + +PKG_MAINTAINER:=David Bauer +PKG_LICENSE:=GPL-2.0 + +include $(TOPDIR)/../package/gluon.mk +include $(INCLUDE_DIR)/cmake.mk + +CMAKE_SOURCE_SUBDIR:=src + +define Package/ffda-wireless-rate-limiter + TITLE:=Package to provide diagnostic information using WiFi beacons + DEPENDS:=+libubox +libubus +libblobmsg-json +tc +kmod-sched-core +kmod-ifb +gluon-core +endef + +define Package/ffda-wireless-rate-limiter/description + Package to impose per-interface and per-client rate limits on a wireless interface +endef + +define Package/ffda-wireless-rate-limiter/conffiles +/etc/config/wireless-rate-limiter +endef + +define Package/ffda-wireless-rate-limiter/install + $(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d $(1)/etc/config $(1)/lib/wireless-rate-limiter $(1)/lib/gluon/upgrade + + $(INSTALL_BIN) $(PKG_BUILD_DIR)/wireless-rate-limiter $(1)/usr/bin/wireless-rate-limiter + + $(INSTALL_BIN) $(PKG_BUILD_DIR)/openwrt/wireless-rate-limiter/files/wireless-rate-limiter.init $(1)/etc/init.d/wireless-rate-limiter + + $(CP) $(PKG_BUILD_DIR)/openwrt/wireless-rate-limiter/files/wireless-rate-limiter.uci $(1)/etc/config/wireless-rate-limiter + + $(CP) $(PKG_BUILD_DIR)/openwrt/wireless-rate-limiter/files/htb-shared.sh $(1)/lib/wireless-rate-limiter/htb-shared.sh + $(INSTALL_BIN) $(PKG_BUILD_DIR)/openwrt/wireless-rate-limiter/files/htb-client.sh $(1)/lib/wireless-rate-limiter/htb-client.sh + $(INSTALL_BIN) $(PKG_BUILD_DIR)/openwrt/wireless-rate-limiter/files/htb-netdev.sh $(1)/lib/wireless-rate-limiter/htb-netdev.sh + + $(INSTALL_BIN) ./files/wireless-rate-limiter.upgrade.lua $(1)/lib/gluon/upgrade/880-wireless-rate-limiter +endef + +$(eval $(call BuildPackageGluon,ffda-wireless-rate-limiter)) diff --git a/ffda-wireless-rate-limiter/README.md b/ffda-wireless-rate-limiter/README.md new file mode 100644 index 00000000..9f4467a5 --- /dev/null +++ b/ffda-wireless-rate-limiter/README.md @@ -0,0 +1,74 @@ +# ffda-wireless-rate-limiter + +This package provides a rate-limiter which can shape traffic per client +and per interface. The limit is user-configurable and defaults can be +applied and updated via the site-config. + +## Examples +### Site + +Below you can see an example of how to configure the rate-limiter +in the site-configuration. + +Each client is shaped to a different rate. + + - On 5 GHz, only the uplink will be shaped. + - On 2.4 GHz, both uplink and downlink will be shaped. Additionally, + all traffic on the 2.4 GHz radios will each be shaped to a maximum + of 10 Mbit/s downlink and 5 Mbit/s uplink. + +When using OWE, the limits are imposed for the unencrypted and OWE network +indepedently. + +```lua +wifi24 = { + channel = 5, -- 2432 MHz + + rate_limit = { + client = { + down = 6000, -- 6 Mbit/s + up = 3000, -- 3 Mbit/s + }, + iface = { + down = 10000, -- 10 Mbit/s + up = 5000, -- 5 Mbit/s + }, + }, + + mesh = { + mcast_rate = 12000, + }, +}, +wifi5 = { + channel = 48, -- 5230 MHz + outdoor_chanlist = '96-116 132-140', + + rate_limit = { + client = { + up = 6000, -- 6 Mbit/s + }, + }, + + mesh = { + mcast_rate = 12000, + }, +}, +``` + +### UCI + +The rate-limiter can also be configured via UCI. The following example is +for a configuration which is equivalent to the site-configuration above. + +```sh +uci set gluon.rate_limit_2g=rate-limit +uci set gluon.rate_limit_2g.client_down=6000 +uci set gluon.rate_limit_2g.client_up=3000 +uci set gluon.rate_limit_2g.iface_down=10000 +uci set gluon.rate_limit_2g.iface_up=5000 + +uci set gluon.rate_limit_5g=rate-limit +uci set gluon.rate_limit_5g.client_up=6000 +uci set gluon.rate_limit_5g.iface_down=10000 +uci set gluon.rate_limit_5g.iface_up=5000 +``` diff --git a/ffda-wireless-rate-limiter/files/wireless-rate-limiter.upgrade.lua b/ffda-wireless-rate-limiter/files/wireless-rate-limiter.upgrade.lua new file mode 100644 index 00000000..ae5c08ad --- /dev/null +++ b/ffda-wireless-rate-limiter/files/wireless-rate-limiter.upgrade.lua @@ -0,0 +1,106 @@ +#!/usr/bin/lua + +local uci = require('simple-uci').cursor() +local site = require('gluon.site') +local wireless = require('gluon.wireless') + +local function wireless_limits_get() + local output_limits = {} + wireless.foreach_radio(uci, function(radio, index, site_config) + local radio_band = radio.band + local radio_index = radio['.name']:match('^radio(%d+)$') + + local uci_section = 'rate_limit_2g' + if radio_band == '5g' then + uci_section = 'rate_limit_5g' + end + + local limit_table = { + client = { + up = { + ['user'] = uci:get('gluon', uci_section, 'client_up'), + ['site'] = site_config.rate_limit.client.up(0) + }, + down = { + ['user'] = uci:get('gluon', uci_section, 'client_down'), + ['site'] = site_config.rate_limit.client.down(0) + } + }, + iface = { + up = { + ['user'] = uci:get('gluon', uci_section, 'iface_up'), + ['site'] = site_config.rate_limit.up(0) + }, + down = { + ['user'] = uci:get('gluon', uci_section, 'iface_down'), + ['site'] = site_config.rate_limit.down(0) + } + } + } + + output_limits[radio_band] = { + index = radio_index, + limits = { + client = { + up = limit_table.client.up.user or limit_table.client.up.site, + down = limit_table.client.down.user or limit_table.client.down.site + }, + iface = { + up = limit_table.iface.up.user or limit_table.iface.up.site, + down = limit_table.iface.down.user or limit_table.iface.down.site + } + }, + } + end) + + return output_limits +end + +local function wireless_limits_set(index, limits) + local limit_applied = false + for type_key, type_value in pairs(limits) do + local limit_down = type_value.down + local limit_up = type_value.up + + for _, iface_type in ipairs({'client', 'owe'}) do + local section_name = type_key .. '_limit_' .. iface_type .. index + + uci:delete('wireless-rate-limiter', section_name) + + if limit_down ~= 0 or limit_up ~= 0 then + local section_type = type_key == 'client' and 'limit-client' or 'limit-interface' + uci:section('wireless-rate-limiter', section_type, section_name, { + interface = iface_type .. index, + download = limit_down, + upload = limit_up, + disabled = 0 + }) + + limit_applied = true + end + end + end + + return limit_applied +end + +-- Delete existing config +uci:delete_all('wireless-rate-limiter', 'limit-client') +uci:delete_all('wireless-rate-limiter', 'limit-interface') + +-- Apply config +local limits = wireless_limits_get() +local limits_applied = false +for _, value in pairs(limits) do + if wireless_limits_set(value.index, value.limits) then + limits_applied = true + end +end + +-- Decide daemon necessity +uci:set('wireless-rate-limiter', 'core', 'disabled', limits_applied and 0 or 1) + +-- Save +uci:commit('wireless-rate-limiter') + +return 0