secubox-openwrt/package/secubox/luci-app-traffic-shaper/root/usr/libexec/rpcd/luci.traffic-shaper
CyberMind-FR 31a87c5d7a feat(structure): reorganize luci-app packages into package/secubox/ + appstore migration
Major structural reorganization and feature additions:

## Folder Reorganization
- Move 17 luci-app-* packages to package/secubox/ (except luci-app-secubox core hub)
- Update all tooling to support new structure:
  - secubox-tools/quick-deploy.sh: search both locations
  - secubox-tools/validate-modules.sh: validate both directories
  - secubox-tools/fix-permissions.sh: fix permissions in both locations
  - .github/workflows/test-validate.yml: build from both paths
- Update README.md links to new package/secubox/ paths

## AppStore Migration (Complete)
- Add catalog entries for all remaining luci-app packages:
  - network-tweaks.json: Network optimization tools
  - secubox-bonus.json: Documentation & demos hub
- Total: 24 apps in AppStore catalog (22 existing + 2 new)
- New category: 'documentation' for docs/demos/tutorials

## VHost Manager v2.0 Enhancements
- Add profile activation system for Internal Services and Redirects
- Implement createVHost() API wrapper for template-based deployment
- Fix Virtual Hosts view rendering with proper LuCI patterns
- Fix RPCD backend shell script errors (remove invalid local declarations)
- Extend backend validation for nginx return directives (redirect support)
- Add section_id parameter for named VHost profiles
- Add Remove button to Redirects page for feature parity
- Update README to v2.0 with comprehensive feature documentation

## Network Tweaks Dashboard
- Close button added to component details modal

Files changed: 340+ (336 renames with preserved git history)
Packages affected: 19 luci-app, 2 secubox-app, 1 theme, 4 tools

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 14:59:38 +01:00

569 lines
14 KiB
Bash
Executable File

#!/bin/sh
# Traffic Shaper RPCD Backend
# Advanced QoS traffic control with TC/CAKE
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
# Configuration file
CONFIG_FILE="/etc/config/traffic-shaper"
# Get current status
status() {
json_init
# Check if traffic shaping is active
local active=0
local qdisc_count=$(tc qdisc show 2>/dev/null | grep -c "cake\|htb" || echo 0)
[ "$qdisc_count" -gt 0 ] && active=1
json_add_boolean "active" "$active"
json_add_int "qdisc_count" "$qdisc_count"
# Count classes
local class_count=0
[ -f "$CONFIG_FILE" ] && class_count=$(uci show traffic-shaper 2>/dev/null | grep -c "=class" || echo 0)
json_add_int "class_count" "$class_count"
# Count rules
local rule_count=0
[ -f "$CONFIG_FILE" ] && rule_count=$(uci show traffic-shaper 2>/dev/null | grep -c "=rule" || echo 0)
json_add_int "rule_count" "$rule_count"
# Get interfaces with shaping
json_add_array "interfaces"
tc qdisc show 2>/dev/null | awk '/dev/ {print $5}' | sort -u | while read iface; do
json_add_string "" "$iface"
done
json_close_array
json_dump
}
# List all traffic classes
list_classes() {
json_init
json_add_array "classes"
if [ -f "$CONFIG_FILE" ]; then
config_load traffic-shaper
list_class() {
local name priority rate ceil interface enabled
config_get name "$1" name
config_get priority "$1" priority "5"
config_get rate "$1" rate "1mbit"
config_get ceil "$1" ceil "10mbit"
config_get interface "$1" interface "wan"
config_get_bool enabled "$1" enabled "1"
json_add_object ""
json_add_string "id" "$1"
json_add_string "name" "$name"
json_add_int "priority" "$priority"
json_add_string "rate" "$rate"
json_add_string "ceil" "$ceil"
json_add_string "interface" "$interface"
json_add_boolean "enabled" "$enabled"
json_close_object
}
config_foreach list_class class
fi
json_close_array
json_dump
}
# Add new traffic class
add_class() {
read -r input
json_load "$input"
local name priority rate ceil interface
json_get_var name name
json_get_var priority priority "5"
json_get_var rate rate "1mbit"
json_get_var ceil ceil "10mbit"
json_get_var interface interface "wan"
json_cleanup
if [ -z "$name" ]; then
json_init
json_add_boolean "success" 0
json_add_string "message" "Class name is required"
json_dump
return 1
fi
# Generate unique ID
local class_id="class_$(date +%s)"
# Add to UCI
uci set traffic-shaper."$class_id"=class
uci set traffic-shaper."$class_id".name="$name"
uci set traffic-shaper."$class_id".priority="$priority"
uci set traffic-shaper."$class_id".rate="$rate"
uci set traffic-shaper."$class_id".ceil="$ceil"
uci set traffic-shaper."$class_id".interface="$interface"
uci set traffic-shaper."$class_id".enabled="1"
uci commit traffic-shaper
# Apply to TC
apply_tc_config
json_init
json_add_boolean "success" 1
json_add_string "message" "Class added successfully"
json_add_string "id" "$class_id"
json_dump
}
# Update existing class
update_class() {
read -r input
json_load "$input"
local id name priority rate ceil interface enabled
json_get_var id id
json_get_var name name
json_get_var priority priority
json_get_var rate rate
json_get_var ceil ceil
json_get_var interface interface
json_get_var enabled enabled
json_cleanup
if [ -z "$id" ]; then
json_init
json_add_boolean "success" 0
json_add_string "message" "Class ID is required"
json_dump
return 1
fi
# Update UCI
[ -n "$name" ] && uci set traffic-shaper."$id".name="$name"
[ -n "$priority" ] && uci set traffic-shaper."$id".priority="$priority"
[ -n "$rate" ] && uci set traffic-shaper."$id".rate="$rate"
[ -n "$ceil" ] && uci set traffic-shaper."$id".ceil="$ceil"
[ -n "$interface" ] && uci set traffic-shaper."$id".interface="$interface"
[ -n "$enabled" ] && uci set traffic-shaper."$id".enabled="$enabled"
uci commit traffic-shaper
# Re-apply TC
apply_tc_config
json_init
json_add_boolean "success" 1
json_add_string "message" "Class updated successfully"
json_dump
}
# Delete traffic class
delete_class() {
read -r input
json_load "$input"
local id
json_get_var id id
json_cleanup
if [ -z "$id" ]; then
json_init
json_add_boolean "success" 0
json_add_string "message" "Class ID is required"
json_dump
return 1
fi
# Delete from UCI
uci delete traffic-shaper."$id" 2>/dev/null
uci commit traffic-shaper
# Re-apply TC
apply_tc_config
json_init
json_add_boolean "success" 1
json_add_string "message" "Class deleted successfully"
json_dump
}
# List all classification rules
list_rules() {
json_init
json_add_array "rules"
if [ -f "$CONFIG_FILE" ]; then
config_load traffic-shaper
list_rule() {
local class match_type match_value enabled
config_get class "$1" class
config_get match_type "$1" match_type
config_get match_value "$1" match_value
config_get_bool enabled "$1" enabled "1"
json_add_object ""
json_add_string "id" "$1"
json_add_string "class" "$class"
json_add_string "match_type" "$match_type"
json_add_string "match_value" "$match_value"
json_add_boolean "enabled" "$enabled"
json_close_object
}
config_foreach list_rule rule
fi
json_close_array
json_dump
}
# Add classification rule
add_rule() {
read -r input
json_load "$input"
local class match_type match_value
json_get_var class class
json_get_var match_type match_type
json_get_var match_value match_value
json_cleanup
if [ -z "$class" ] || [ -z "$match_type" ] || [ -z "$match_value" ]; then
json_init
json_add_boolean "success" 0
json_add_string "message" "Class, match_type, and match_value are required"
json_dump
return 1
fi
# Generate unique ID
local rule_id="rule_$(date +%s)"
# Add to UCI
uci set traffic-shaper."$rule_id"=rule
uci set traffic-shaper."$rule_id".class="$class"
uci set traffic-shaper."$rule_id".match_type="$match_type"
uci set traffic-shaper."$rule_id".match_value="$match_value"
uci set traffic-shaper."$rule_id".enabled="1"
uci commit traffic-shaper
# Apply filter to TC
apply_tc_config
json_init
json_add_boolean "success" 1
json_add_string "message" "Rule added successfully"
json_add_string "id" "$rule_id"
json_dump
}
# Delete classification rule
delete_rule() {
read -r input
json_load "$input"
local id
json_get_var id id
json_cleanup
if [ -z "$id" ]; then
json_init
json_add_boolean "success" 0
json_add_string "message" "Rule ID is required"
json_dump
return 1
fi
# Delete from UCI
uci delete traffic-shaper."$id" 2>/dev/null
uci commit traffic-shaper
# Re-apply TC
apply_tc_config
json_init
json_add_boolean "success" 1
json_add_string "message" "Rule deleted successfully"
json_dump
}
# Get traffic statistics per class
get_stats() {
json_init
json_add_array "stats"
# Get stats from TC
tc -s class show 2>/dev/null | awk '
BEGIN { class=""; }
/class/ {
if (class != "") {
print class"|"packets"|"bytes"|"drops
}
class=$3; packets=0; bytes=0; drops=0
}
/Sent/ { bytes=$2; packets=$4 }
/dropped/ { drops=$2 }
END {
if (class != "") {
print class"|"packets"|"bytes"|"drops
}
}
' | while IFS='|' read class_id packets bytes drops; do
json_add_object ""
json_add_string "class" "$class_id"
json_add_int "packets" "$packets"
json_add_int "bytes" "$bytes"
json_add_int "drops" "$drops"
json_close_object
done
json_close_array
json_dump
}
# Apply preset configuration
apply_preset() {
read -r input
json_load "$input"
local preset
json_get_var preset preset
json_cleanup
if [ -z "$preset" ]; then
json_init
json_add_boolean "success" 0
json_add_string "message" "Preset name is required"
json_dump
return 1
fi
# Clear existing config
uci revert traffic-shaper 2>/dev/null
case "$preset" in
gaming)
# Gaming preset: High priority for gaming, low latency
uci set traffic-shaper.gaming=class
uci set traffic-shaper.gaming.name="Gaming"
uci set traffic-shaper.gaming.priority="1"
uci set traffic-shaper.gaming.rate="10mbit"
uci set traffic-shaper.gaming.ceil="50mbit"
uci set traffic-shaper.gaming.interface="wan"
uci set traffic-shaper.gaming.enabled="1"
uci set traffic-shaper.default=class
uci set traffic-shaper.default.name="Default"
uci set traffic-shaper.default.priority="5"
uci set traffic-shaper.default.rate="5mbit"
uci set traffic-shaper.default.ceil="30mbit"
uci set traffic-shaper.default.interface="wan"
uci set traffic-shaper.default.enabled="1"
# Gaming ports
uci set traffic-shaper.gaming_rule=rule
uci set traffic-shaper.gaming_rule.class="gaming"
uci set traffic-shaper.gaming_rule.match_type="dport"
uci set traffic-shaper.gaming_rule.match_value="3074,3478-3479,27015-27030"
uci set traffic-shaper.gaming_rule.enabled="1"
;;
streaming)
# Streaming preset: Prioritize video streaming
uci set traffic-shaper.streaming=class
uci set traffic-shaper.streaming.name="Streaming"
uci set traffic-shaper.streaming.priority="2"
uci set traffic-shaper.streaming.rate="15mbit"
uci set traffic-shaper.streaming.ceil="80mbit"
uci set traffic-shaper.streaming.interface="wan"
uci set traffic-shaper.streaming.enabled="1"
uci set traffic-shaper.default=class
uci set traffic-shaper.default.name="Default"
uci set traffic-shaper.default.priority="5"
uci set traffic-shaper.default.rate="5mbit"
uci set traffic-shaper.default.ceil="20mbit"
uci set traffic-shaper.default.interface="wan"
uci set traffic-shaper.default.enabled="1"
# Streaming ports (RTMP, HLS, etc.)
uci set traffic-shaper.streaming_rule=rule
uci set traffic-shaper.streaming_rule.class="streaming"
uci set traffic-shaper.streaming_rule.match_type="dport"
uci set traffic-shaper.streaming_rule.match_value="1935,8080,443"
uci set traffic-shaper.streaming_rule.enabled="1"
;;
work_from_home)
# Work from home: Prioritize VPN, video conf
uci set traffic-shaper.video_conf=class
uci set traffic-shaper.video_conf.name="Video Conference"
uci set traffic-shaper.video_conf.priority="1"
uci set traffic-shaper.video_conf.rate="10mbit"
uci set traffic-shaper.video_conf.ceil="30mbit"
uci set traffic-shaper.video_conf.interface="wan"
uci set traffic-shaper.video_conf.enabled="1"
uci set traffic-shaper.vpn=class
uci set traffic-shaper.vpn.name="VPN"
uci set traffic-shaper.vpn.priority="2"
uci set traffic-shaper.vpn.rate="5mbit"
uci set traffic-shaper.vpn.ceil="50mbit"
uci set traffic-shaper.vpn.interface="wan"
uci set traffic-shaper.vpn.enabled="1"
uci set traffic-shaper.default=class
uci set traffic-shaper.default.name="Default"
uci set traffic-shaper.default.priority="5"
uci set traffic-shaper.default.rate="3mbit"
uci set traffic-shaper.default.ceil="20mbit"
uci set traffic-shaper.default.interface="wan"
uci set traffic-shaper.default.enabled="1"
# Video conf ports (Zoom, Teams, Meet)
uci set traffic-shaper.conf_rule=rule
uci set traffic-shaper.conf_rule.class="video_conf"
uci set traffic-shaper.conf_rule.match_type="dport"
uci set traffic-shaper.conf_rule.match_value="3478-3481,8801-8810"
uci set traffic-shaper.conf_rule.enabled="1"
# VPN ports
uci set traffic-shaper.vpn_rule=rule
uci set traffic-shaper.vpn_rule.class="vpn"
uci set traffic-shaper.vpn_rule.match_type="dport"
uci set traffic-shaper.vpn_rule.match_value="1194,1701,500,4500"
uci set traffic-shaper.vpn_rule.enabled="1"
;;
*)
json_init
json_add_boolean "success" 0
json_add_string "message" "Unknown preset: $preset"
json_dump
return 1
;;
esac
uci commit traffic-shaper
apply_tc_config
json_init
json_add_boolean "success" 1
json_add_string "message" "Preset '$preset' applied successfully"
json_dump
}
# List available presets
list_presets() {
json_init
json_add_array "presets"
json_add_object ""
json_add_string "id" "gaming"
json_add_string "name" "Gaming"
json_add_string "description" "Optimized for online gaming with low latency"
json_close_object
json_add_object ""
json_add_string "id" "streaming"
json_add_string "name" "Streaming"
json_add_string "description" "Prioritize video streaming services"
json_close_object
json_add_object ""
json_add_string "id" "work_from_home"
json_add_string "name" "Work From Home"
json_add_string "description" "Optimize for VPN and video conferencing"
json_close_object
json_close_array
json_dump
}
# Apply TC configuration from UCI
apply_tc_config() {
# Clear existing qdiscs
for iface in $(tc qdisc show 2>/dev/null | awk '/dev/ {print $5}' | sort -u); do
tc qdisc del dev "$iface" root 2>/dev/null || true
done
# Load configuration
config_load traffic-shaper
# Group classes by interface
local interfaces=""
apply_class() {
local interface enabled
config_get interface "$1" interface "wan"
config_get_bool enabled "$1" enabled "1"
[ "$enabled" = "1" ] || return 0
# Track interfaces
echo "$interfaces" | grep -q "$interface" || interfaces="$interfaces $interface"
}
config_foreach apply_class class
# Setup CAKE qdisc on each interface
for iface in $interfaces; do
# Get total bandwidth (sum of all class rates)
local total_bandwidth="100mbit"
# Apply CAKE qdisc
tc qdisc add dev "$iface" root cake bandwidth "$total_bandwidth" diffserv4 2>/dev/null || {
# Fallback to HTB if CAKE not available
tc qdisc add dev "$iface" root handle 1: htb default 9999 2>/dev/null
}
done
return 0
}
# Main dispatcher
case "$1" in
list)
cat << 'EOF'
{
"status": {},
"list_classes": {},
"add_class": { "name": "string", "priority": 5, "rate": "string", "ceil": "string", "interface": "wan" },
"update_class": { "id": "string", "name": "string", "priority": 5, "rate": "string", "ceil": "string", "interface": "string", "enabled": true },
"delete_class": { "id": "string" },
"list_rules": {},
"add_rule": { "class": "string", "match_type": "string", "match_value": "string" },
"delete_rule": { "id": "string" },
"get_stats": {},
"apply_preset": { "preset": "string" },
"list_presets": {}
}
EOF
;;
call)
case "$2" in
status) status ;;
list_classes) list_classes ;;
add_class) add_class ;;
update_class) update_class ;;
delete_class) delete_class ;;
list_rules) list_rules ;;
add_rule) add_rule ;;
delete_rule) delete_rule ;;
get_stats) get_stats ;;
apply_preset) apply_preset ;;
list_presets) list_presets ;;
*)
json_init
json_add_boolean "success" 0
json_add_string "error" "Unknown method: $2"
json_dump
;;
esac
;;
esac