qosmngr: alternative

This commit is contained in:
Sukru Senli 2026-03-13 01:42:57 +01:00
parent 76a8f98a94
commit dcba2de075
3 changed files with 190 additions and 111 deletions

View file

@ -112,10 +112,10 @@ configure_classify() {
sh /tmp/qos/classify.ip6tables
sh /tmp/qos/classify.iprule
# Sync DSCP/PCP mappings to mapcontroller for WiFi QoS
if [ -f /lib/qos/mapcontroller_sync.sh ]; then
. /lib/qos/mapcontroller_sync.sh
sync_mapcontroller_qos
# Sync DSCP/PCP mappings to WiFi QoS Map on local AP interfaces
if [ -f /lib/qos/wifi_qos.sh ]; then
. /lib/qos/wifi_qos.sh
sync_wifi_qos
fi
}

View file

@ -1,107 +0,0 @@
#!/bin/sh
# Sync DSCP/PCP mappings from qosmngr classify rules to mapcontroller
# for WiFi QoS (EasyMesh QoS Map) integration.
#
# When device priority classify rules set dscp_mark/pcp_mark, this script
# generates a mapcontroller qos_rule (type dscp_pcp) so all EasyMesh agents
# apply the correct DSCP-to-WMM AC mapping on WiFi interfaces.
MAPCNTLR_CFG="mapcontroller"
AUTO_RULE_ID="1000"
AUTO_RULE_NAME="qos_auto_dscp_pcp"
# Collect dscp_mark/pcp_mark pairs from enabled classify sections.
# Stores results in /tmp/qos/dscp_pcp_map (sorted by order, first wins).
_collect_dscp_pcp_mapping() {
local cid="$1"
local is_enable dscp_mark pcp_mark c_order
config_get is_enable "$cid" "enable" "1"
[ "$is_enable" = "0" ] && return
config_get dscp_mark "$cid" "dscp_mark"
config_get pcp_mark "$cid" "pcp_mark"
# Skip rules without both dscp_mark and pcp_mark
[ -z "$dscp_mark" ] && return
[ -z "$pcp_mark" ] && return
# Skip negative values (means "no change" in TR-181)
[ "$dscp_mark" -lt 0 ] 2>/dev/null && return
[ "$pcp_mark" -lt 0 ] 2>/dev/null && return
# Validate ranges
[ "$dscp_mark" -gt 63 ] 2>/dev/null && return
[ "$pcp_mark" -gt 7 ] 2>/dev/null && return
config_get c_order "$cid" "order" "999"
# Format: order dscp pcp (sorted later, first occurrence per dscp wins)
echo "$c_order $dscp_mark $pcp_mark" >> /tmp/qos/dscp_pcp_map
}
# Remove previously auto-generated qos_rule sections from mapcontroller config
_cleanup_auto_rules() {
local section auto_gen
# Find all qos_rule sections with auto_generated='1'
for section in $(uci -q show "$MAPCNTLR_CFG" | grep '\.auto_generated=.1.' | sed "s/\.auto_generated=.*//;s/${MAPCNTLR_CFG}\.//"); do
uci -q delete "${MAPCNTLR_CFG}.${section}"
done
}
# Main sync function - called after configure_classify()
sync_mapcontroller_qos() {
# Guard: skip if mapcontroller/EasyMesh is not installed
[ -f "/etc/config/$MAPCNTLR_CFG" ] || return 0
rm -f /tmp/qos/dscp_pcp_map
# Collect mappings from all classify rules
config_load qos
config_foreach _collect_dscp_pcp_mapping classify
# Remove old auto-generated rules
_cleanup_auto_rules
# If no mappings found, just commit cleanup and signal
if [ ! -s /tmp/qos/dscp_pcp_map ]; then
uci -q commit "$MAPCNTLR_CFG"
killall -HUP mapcontroller 2>/dev/null
return 0
fi
# Deduplicate: sort by order (ascending), keep first occurrence per DSCP
sort -n -k1 /tmp/qos/dscp_pcp_map | awk '!seen[$2]++ {print $2 "," $3}' > /tmp/qos/dscp_pcp_list
if [ ! -s /tmp/qos/dscp_pcp_list ]; then
uci -q commit "$MAPCNTLR_CFG"
killall -HUP mapcontroller 2>/dev/null
return 0
fi
# Ensure QoS is enabled in mapcontroller
uci -q set "${MAPCNTLR_CFG}.qos=qos"
uci -q set "${MAPCNTLR_CFG}.qos.enabled=1"
# Create auto-generated qos_rule
uci -q set "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}=qos_rule"
uci -q set "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}.id=${AUTO_RULE_ID}"
uci -q set "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}.type=dscp_pcp"
uci -q set "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}.precedence=200"
uci -q set "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}.output=0"
uci -q set "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}.always_match=1"
uci -q set "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}.auto_generated=1"
# Clear any existing dscp_pcp list entries and add new ones
uci -q delete "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}.dscp_pcp"
while read -r mapping; do
uci -q add_list "${MAPCNTLR_CFG}.${AUTO_RULE_NAME}.dscp_pcp=${mapping}"
done < /tmp/qos/dscp_pcp_list
# Commit and signal mapcontroller to reload
uci -q commit "$MAPCNTLR_CFG"
killall -HUP mapcontroller 2>/dev/null
rm -f /tmp/qos/dscp_pcp_map /tmp/qos/dscp_pcp_list
}

View file

@ -0,0 +1,186 @@
#!/bin/sh
# Sync DSCP/PCP mappings from qosmngr classify rules to WiFi QoS Map.
#
# When device priority or QoS profile classify rules set dscp_mark/pcp_mark,
# this script builds an IEEE 802.11 QoS Map and applies it directly to all
# local WiFi AP interfaces via ubus (wifi.ap.* set_qos_map).
#
# QoS Map format (IEEE 802.11-2020, 9.4.2.93):
# [exception pairs...] [8 UP ranges]
# Exception pair: DSCP_value, User_Priority (2 bytes each)
# UP range: DSCP_low, DSCP_high (2 bytes each, 16 bytes total for UP 0-7)
# If DSCP_low=255 and DSCP_high=255, that UP is unused.
# Collect dscp_mark/pcp_mark pairs from enabled classify sections.
_collect_dscp_pcp_mapping() {
local cid="$1"
local is_enable dscp_mark pcp_mark c_order
config_get is_enable "$cid" "enable" "1"
[ "$is_enable" = "0" ] && return
config_get dscp_mark "$cid" "dscp_mark"
config_get pcp_mark "$cid" "pcp_mark"
[ -z "$dscp_mark" ] && return
[ -z "$pcp_mark" ] && return
# Skip negative values (means "no change" in TR-181)
[ "$dscp_mark" -lt 0 ] 2>/dev/null && return
[ "$pcp_mark" -lt 0 ] 2>/dev/null && return
# Validate ranges
[ "$dscp_mark" -gt 63 ] 2>/dev/null && return
[ "$pcp_mark" -gt 7 ] 2>/dev/null && return
config_get c_order "$cid" "order" "999"
echo "$c_order $dscp_mark $pcp_mark" >> /tmp/qos/dscp_pcp_map
}
# Build IEEE 802.11 QoS Map byte array from DSCP->PCP mappings.
# Input: /tmp/qos/dscp_pcp_list (lines of "dscp,pcp")
# Output: prints space-separated byte array for ubus json
_build_qos_map() {
local dscp pcp default_up i
# Initialize 64-entry DSCP->PCP table with defaults (IP precedence: dscp/8)
# Using a file since shell arrays aren't portable
i=0
while [ $i -lt 64 ]; do
echo "$i $((i / 8))"
i=$((i + 1))
done > /tmp/qos/dscp_pcp_full
# Override with our explicit mappings
while IFS=',' read -r dscp pcp; do
sed -i "s/^${dscp} .*/${dscp} ${pcp}/" /tmp/qos/dscp_pcp_full
done < /tmp/qos/dscp_pcp_list
# For each PCP (0-7), find the largest contiguous range of DSCPs
# and collect exceptions (DSCPs outside the range)
local exc=""
local ranges=""
i=0
while [ $i -lt 8 ]; do
# Get all DSCPs mapped to this PCP, sorted numerically
local dscp_list
dscp_list=$(awk -v p="$i" '$2 == p {print $1}' /tmp/qos/dscp_pcp_full | sort -n)
if [ -z "$dscp_list" ]; then
# No DSCPs map to this PCP - mark range as unused
ranges="$ranges 255 255"
i=$((i + 1))
continue
fi
# Find largest contiguous range
local best_start="" best_end="" best_len=0
local cur_start="" cur_end="" cur_len=0
local prev=-2
for d in $dscp_list; do
if [ $((prev + 1)) -eq "$d" ]; then
cur_end=$d
cur_len=$((cur_len + 1))
else
cur_start=$d
cur_end=$d
cur_len=1
fi
if [ $cur_len -gt $best_len ]; then
best_start=$cur_start
best_end=$cur_end
best_len=$cur_len
fi
prev=$d
done
ranges="$ranges $best_start $best_end"
# Exceptions: DSCPs outside the best range for this PCP
for d in $dscp_list; do
if [ "$d" -lt "$best_start" ] || [ "$d" -gt "$best_end" ]; then
exc="$exc $d $i"
fi
done
i=$((i + 1))
done
# Output: exceptions first, then 8 UP ranges (16 bytes)
echo "$exc $ranges" | tr -s ' ' | sed 's/^ //'
rm -f /tmp/qos/dscp_pcp_full
}
# Apply QoS Map to all local WiFi AP interfaces via ubus
_apply_qos_map_to_aps() {
local qos_map_bytes="$1"
local ap json_array
# Build JSON array string from space-separated bytes
json_array=$(echo "$qos_map_bytes" | tr ' ' ',' | sed 's/^/[/;s/$/]/')
for ap in $(ubus list 2>/dev/null | grep '^wifi\.ap\.'); do
ubus call "$ap" set_qos_map "{\"set\": $json_array}" 2>/dev/null
done
}
# Send QoS Map Configuration frame to all connected STAs
_notify_stas() {
local ap sta_list sta
for ap in $(ubus list 2>/dev/null | grep '^wifi\.ap\.'); do
# Get list of connected STAs
sta_list=$(ubus call "$ap" stations 2>/dev/null | \
jsonfilter -e '@.stations[*].macaddr' 2>/dev/null)
for sta in $sta_list; do
ubus call "$ap" send_qos_map_conf "{\"sta\": \"$sta\"}" 2>/dev/null
done
done
}
# Clear QoS Map from all WiFi AP interfaces
_clear_qos_map() {
local ap
for ap in $(ubus list 2>/dev/null | grep '^wifi\.ap\.'); do
ubus call "$ap" set_qos_map '{"set": []}' 2>/dev/null
done
}
# Main sync function - called after configure_classify()
sync_wifi_qos() {
rm -f /tmp/qos/dscp_pcp_map
# Collect mappings from all classify rules
config_load qos
config_foreach _collect_dscp_pcp_mapping classify
# If no mappings found, clear QoS Map
if [ ! -s /tmp/qos/dscp_pcp_map ]; then
_clear_qos_map
rm -f /tmp/qos/dscp_pcp_map /tmp/qos/dscp_pcp_list
return 0
fi
# Deduplicate: sort by order (ascending), keep first occurrence per DSCP
sort -n -k1 /tmp/qos/dscp_pcp_map | awk '!seen[$2]++ {print $2 "," $3}' > /tmp/qos/dscp_pcp_list
if [ ! -s /tmp/qos/dscp_pcp_list ]; then
_clear_qos_map
rm -f /tmp/qos/dscp_pcp_map /tmp/qos/dscp_pcp_list
return 0
fi
# Build and apply QoS Map
local qos_map
qos_map=$(_build_qos_map)
_apply_qos_map_to_aps "$qos_map"
_notify_stas
rm -f /tmp/qos/dscp_pcp_map /tmp/qos/dscp_pcp_list
}