feat: Replace SQM with per-client speed control for paid WiFi upgrade
Add parahub-speed-control script (nftables set + tc HTB) for per-IP speed shaping. Free tier 512kbps, paid tier unlimited. Heartbeat now parses paid_clients from API response and syncs nftables set. Replaced sqm-scripts/kmod-sched-cake/luci-app-sqm packages with tc-full/kmod-ifb/kmod-sched-htb. Section 8 of uci-defaults creates init.d service for speed control instead of SQM config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -525,22 +525,26 @@ fi
|
|||||||
uci commit dhcp
|
uci commit dhcp
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 8. SQM TRAFFIC SHAPING (Bumblebee only — guest 512 kbps limit)
|
# 8. SPEED CONTROL (Bumblebee only — per-client shaping, paid Lightning upgrade)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
if [ "$ROLE" != "bee" ]; then
|
if [ "$ROLE" != "bee" ]; then
|
||||||
uci batch <<-SQM_EOF
|
chmod +x /usr/bin/parahub-speed-control
|
||||||
set sqm.guest=queue
|
|
||||||
set sqm.guest.enabled='1'
|
# Create init.d service for speed control
|
||||||
set sqm.guest.interface='br-guest'
|
cat > /etc/init.d/parahub-speed <<'INITEOF'
|
||||||
set sqm.guest.download='512'
|
#!/bin/sh /etc/rc.common
|
||||||
set sqm.guest.upload='512'
|
START=99
|
||||||
set sqm.guest.qdisc='cake'
|
start() { /usr/bin/parahub-speed-control init; }
|
||||||
set sqm.guest.script='piece_of_cake.qos'
|
stop() {
|
||||||
set sqm.guest.linklayer='ethernet'
|
tc qdisc del dev br-guest root 2>/dev/null
|
||||||
set sqm.guest.overhead='44'
|
tc qdisc del dev br-guest ingress 2>/dev/null
|
||||||
SQM_EOF
|
ip link del ifb-guest 2>/dev/null
|
||||||
uci commit sqm
|
nft delete table inet parahub 2>/dev/null
|
||||||
|
}
|
||||||
|
INITEOF
|
||||||
|
chmod +x /etc/init.d/parahub-speed
|
||||||
|
/etc/init.d/parahub-speed enable
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -34,26 +34,38 @@ esac
|
|||||||
|
|
||||||
PAYLOAD="{\"mac\":\"${MAC}\",\"hostname\":\"${HOSTNAME}\",\"yggdrasil_address\":\"${YGG_ADDR}\",\"firmware_version\":\"25.12.0-rc4\",\"hardware_profile\":\"${HW}\",\"uptime\":${UPTIME},\"private_ssid\":\"${SSID}\",\"firmware_role\":\"${ROLE}\",\"mesh_ip\":\"${MESH_IP}\"}"
|
PAYLOAD="{\"mac\":\"${MAC}\",\"hostname\":\"${HOSTNAME}\",\"yggdrasil_address\":\"${YGG_ADDR}\",\"firmware_version\":\"25.12.0-rc4\",\"hardware_profile\":\"${HW}\",\"uptime\":${UPTIME},\"private_ssid\":\"${SSID}\",\"firmware_role\":\"${ROLE}\",\"mesh_ip\":\"${MESH_IP}\"}"
|
||||||
|
|
||||||
|
RESPONSE=""
|
||||||
|
|
||||||
if [ "$ROLE" = "bee" ]; then
|
if [ "$ROLE" = "bee" ]; then
|
||||||
# Bee: no yggdrasil, use public URL only
|
# Bee: no yggdrasil, use public URL only
|
||||||
curl -s -m 10 -X POST \
|
RESPONSE=$(curl -s -m 10 -X POST \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Authorization: Bearer ${HEARTBEAT_KEY}" \
|
-H "Authorization: Bearer ${HEARTBEAT_KEY}" \
|
||||||
-d "$PAYLOAD" \
|
-d "$PAYLOAD" \
|
||||||
"${PARAHUB_API_PUBLIC}" >/dev/null 2>&1
|
"${PARAHUB_API_PUBLIC}" 2>/dev/null)
|
||||||
else
|
else
|
||||||
# Bumblebee: try yggdrasil first, fallback to public
|
# Bumblebee: try yggdrasil first, fallback to public
|
||||||
if ping6 -c 1 -W 3 200:abb9:5810:37d3:8a4c:98a6:b82b:969a >/dev/null 2>&1; then
|
if ping6 -c 1 -W 3 200:abb9:5810:37d3:8a4c:98a6:b82b:969a >/dev/null 2>&1; then
|
||||||
curl -s -m 10 -X POST \
|
RESPONSE=$(curl -s -m 10 -X POST \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Authorization: Bearer ${HEARTBEAT_KEY}" \
|
-H "Authorization: Bearer ${HEARTBEAT_KEY}" \
|
||||||
-d "$PAYLOAD" \
|
-d "$PAYLOAD" \
|
||||||
"${PARAHUB_API_YGG}" >/dev/null 2>&1
|
"${PARAHUB_API_YGG}" 2>/dev/null)
|
||||||
else
|
else
|
||||||
curl -s -m 10 -X POST \
|
RESPONSE=$(curl -s -m 10 -X POST \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Authorization: Bearer ${HEARTBEAT_KEY}" \
|
-H "Authorization: Bearer ${HEARTBEAT_KEY}" \
|
||||||
-d "$PAYLOAD" \
|
-d "$PAYLOAD" \
|
||||||
"${PARAHUB_API_PUBLIC}" >/dev/null 2>&1
|
"${PARAHUB_API_PUBLIC}" 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Sync paid_clients to speed control (Bumblebee only)
|
||||||
|
if [ "$ROLE" != "bee" ] && [ -x /usr/bin/parahub-speed-control ] && [ -n "$RESPONSE" ]; then
|
||||||
|
PAID_IPS=$(echo "$RESPONSE" | jsonfilter -e '$.paid_clients[*]' 2>/dev/null)
|
||||||
|
# Flush and re-add all paid IPs
|
||||||
|
parahub-speed-control flush
|
||||||
|
for IP in $PAID_IPS; do
|
||||||
|
parahub-speed-control add "$IP"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|||||||
74
files/usr/bin/parahub-speed-control
Executable file
74
files/usr/bin/parahub-speed-control
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Parahub Mesh — Per-client speed control
|
||||||
|
# Free tier: 512kbps (default), Paid tier: full speed (via nftables set + tc HTB)
|
||||||
|
#
|
||||||
|
# Usage: parahub-speed-control init|add <ip>|remove <ip>|list|flush
|
||||||
|
|
||||||
|
IFACE="br-guest"
|
||||||
|
SLOW_RATE="512kbit"
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
init)
|
||||||
|
# Remove SQM if active
|
||||||
|
/etc/init.d/sqm stop 2>/dev/null
|
||||||
|
/etc/init.d/sqm disable 2>/dev/null
|
||||||
|
|
||||||
|
# --- Egress shaping (router → client, i.e. download for client) ---
|
||||||
|
tc qdisc del dev $IFACE root 2>/dev/null
|
||||||
|
tc qdisc add dev $IFACE root handle 1: htb default 10
|
||||||
|
tc class add dev $IFACE parent 1: classid 1:1 htb rate 1000mbit
|
||||||
|
tc class add dev $IFACE parent 1:1 classid 1:10 htb rate $SLOW_RATE ceil $SLOW_RATE # free tier
|
||||||
|
tc class add dev $IFACE parent 1:1 classid 1:20 htb rate 1000mbit # paid tier
|
||||||
|
tc qdisc add dev $IFACE parent 1:10 fq_codel
|
||||||
|
tc qdisc add dev $IFACE parent 1:20 fq_codel
|
||||||
|
|
||||||
|
# tc filter: packets with mark 0x20 → paid class
|
||||||
|
tc filter add dev $IFACE parent 1: protocol ip handle 0x20 fw flowid 1:20
|
||||||
|
|
||||||
|
# --- Ingress shaping (client → router, i.e. upload for client) via IFB ---
|
||||||
|
ip link add ifb-guest type ifb 2>/dev/null
|
||||||
|
ip link set ifb-guest up
|
||||||
|
tc qdisc del dev $IFACE ingress 2>/dev/null
|
||||||
|
tc qdisc add dev $IFACE ingress
|
||||||
|
tc filter add dev $IFACE parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb-guest
|
||||||
|
|
||||||
|
tc qdisc del dev ifb-guest root 2>/dev/null
|
||||||
|
tc qdisc add dev ifb-guest root handle 1: htb default 10
|
||||||
|
tc class add dev ifb-guest parent 1: classid 1:1 htb rate 1000mbit
|
||||||
|
tc class add dev ifb-guest parent 1:1 classid 1:10 htb rate $SLOW_RATE ceil $SLOW_RATE
|
||||||
|
tc class add dev ifb-guest parent 1:1 classid 1:20 htb rate 1000mbit
|
||||||
|
tc qdisc add dev ifb-guest parent 1:10 fq_codel
|
||||||
|
tc qdisc add dev ifb-guest parent 1:20 fq_codel
|
||||||
|
tc filter add dev ifb-guest parent 1: protocol ip handle 0x20 fw flowid 1:20
|
||||||
|
|
||||||
|
# --- nftables: paid_clients set + mark rules ---
|
||||||
|
nft add table inet parahub 2>/dev/null
|
||||||
|
nft flush table inet parahub 2>/dev/null
|
||||||
|
nft add set inet parahub paid_clients '{ type ipv4_addr; }'
|
||||||
|
nft add chain inet parahub speed_mark '{ type filter hook forward priority -150; }'
|
||||||
|
nft add rule inet parahub speed_mark ip daddr @paid_clients meta mark set 0x20
|
||||||
|
nft add rule inet parahub speed_mark ip saddr @paid_clients meta mark set 0x20
|
||||||
|
|
||||||
|
logger -t parahub-speed "Speed control initialized: free=${SLOW_RATE}, paid=unlimited"
|
||||||
|
;;
|
||||||
|
add)
|
||||||
|
[ -z "$2" ] && echo "Usage: $0 add <ip>" && exit 1
|
||||||
|
nft add element inet parahub paid_clients "{ $2 }" 2>/dev/null
|
||||||
|
logger -t parahub-speed "Added paid client: $2"
|
||||||
|
;;
|
||||||
|
remove)
|
||||||
|
[ -z "$2" ] && echo "Usage: $0 remove <ip>" && exit 1
|
||||||
|
nft delete element inet parahub paid_clients "{ $2 }" 2>/dev/null
|
||||||
|
logger -t parahub-speed "Removed paid client: $2"
|
||||||
|
;;
|
||||||
|
list)
|
||||||
|
nft list set inet parahub paid_clients 2>/dev/null
|
||||||
|
;;
|
||||||
|
flush)
|
||||||
|
nft flush set inet parahub paid_clients 2>/dev/null
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {init|add <ip>|remove <ip>|list|flush}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -86,10 +86,10 @@ PACKAGES_BUMBLEBEE=(
|
|||||||
wireguard-tools
|
wireguard-tools
|
||||||
luci-proto-wireguard
|
luci-proto-wireguard
|
||||||
https-dns-proxy
|
https-dns-proxy
|
||||||
sqm-scripts
|
tc-full
|
||||||
kmod-sched-cake
|
kmod-ifb
|
||||||
|
kmod-sched-htb
|
||||||
luci
|
luci
|
||||||
luci-app-sqm
|
|
||||||
tcpdump
|
tcpdump
|
||||||
iperf3
|
iperf3
|
||||||
iwinfo
|
iwinfo
|
||||||
|
|||||||
Reference in New Issue
Block a user