secubox-openwrt/luci-app-network-modes/root/usr/libexec/rpcd/luci.network-modes
CyberMind-FR 4406825611 feat: Implement core features for network-modes and system-hub
Network Modes (+536 lines RPCD):
- Enhanced router.js with improved proxy configuration
- Expanded RPCD backend with new methods
- Updated README with feature documentation

System Hub (+511 lines RPCD, +127 lines API):
- Implemented diagnostics collection features
- Enhanced remote management interface
- Expanded RPCD backend with new RPC methods
- Added new API methods for diagnostics and remote access

Total changes: +1,392 additions, -126 deletions

Preparation for v0.3.6 release with real feature implementations
from CODEX roadmaps.
2025-12-28 15:34:23 +01:00

1797 lines
56 KiB
Bash
Executable File

#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# Network Modes Dashboard RPCD backend
# Copyright (C) 2024 CyberMind.fr - Gandalf
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
CONFIG_FILE="/etc/config/network-modes"
BACKUP_DIR="/etc/network-modes-backup"
PCAP_DIR="/var/log/pcap"
# Get current status
get_status() {
json_init
# Current mode
local current_mode=$(uci -q get network-modes.config.current_mode || echo "router")
local last_change=$(uci -q get network-modes.config.last_change || echo "Never")
json_add_string "current_mode" "$current_mode"
json_add_string "last_change" "$last_change"
# Get mode details
local mode_name=$(uci -q get network-modes.$current_mode.name || echo "$current_mode")
local mode_desc=$(uci -q get network-modes.$current_mode.description || echo "")
json_add_string "mode_name" "$mode_name"
json_add_string "mode_description" "$mode_desc"
# System info
json_add_string "hostname" "$(uci -q get system.@system[0].hostname || hostname)"
json_add_int "uptime" "$(cat /proc/uptime | cut -d. -f1)"
# Network interfaces status
json_add_array "interfaces"
for iface in $(ip -o link show | awk -F': ' '{print $2}' | grep -v "^lo$"); do
local state="down"
ip link show $iface 2>/dev/null | grep -q "UP" && state="up"
local ip=$(ip -4 addr show $iface 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1)
local mac=$(ip link show $iface 2>/dev/null | grep -oP 'link/ether \K[a-f0-9:]+')
json_add_object
json_add_string "name" "$iface"
json_add_string "state" "$state"
json_add_string "ip" "${ip:-N/A}"
json_add_string "mac" "${mac:-N/A}"
json_close_object
done
json_close_array
# WiFi status
local wifi_enabled=0
[ -d /sys/class/net/wlan0 ] && wifi_enabled=1
json_add_boolean "wifi_available" "$wifi_enabled"
# WireGuard status
local wg_enabled=0
command -v wg >/dev/null 2>&1 && wg_enabled=1
json_add_boolean "wireguard_available" "$wg_enabled"
# Services status
json_add_object "services"
json_add_boolean "firewall" "$(pgrep -x fw4 >/dev/null && echo 1 || echo 0)"
json_add_boolean "dnsmasq" "$(pgrep -x dnsmasq >/dev/null && echo 1 || echo 0)"
json_add_boolean "netifyd" "$(pgrep -x netifyd >/dev/null && echo 1 || echo 0)"
json_add_boolean "nginx" "$(pgrep -x nginx >/dev/null && echo 1 || echo 0)"
json_add_boolean "squid" "$(pgrep -x squid >/dev/null && echo 1 || echo 0)"
json_close_object
json_dump
}
# Get all modes configuration
get_modes() {
json_init
json_add_array "modes"
local current_mode=$(uci -q get network-modes.config.current_mode || echo "router")
# Sniffer mode
json_add_object
json_add_string "id" "sniffer"
json_add_string "name" "$(uci -q get network-modes.sniffer.name || echo 'Sniffer / Passthrough')"
json_add_string "description" "$(uci -q get network-modes.sniffer.description)"
json_add_string "icon" "🔍"
json_add_boolean "active" "$([ "$current_mode" = "sniffer" ] && echo 1 || echo 0)"
json_add_string "bridge_interface" "$(uci -q get network-modes.sniffer.bridge_interface)"
json_add_boolean "netifyd_enabled" "$(uci -q get network-modes.sniffer.netifyd_enabled || echo 0)"
json_add_boolean "promiscuous" "$(uci -q get network-modes.sniffer.promiscuous || echo 0)"
json_close_object
# Access Point mode
json_add_object
json_add_string "id" "accesspoint"
json_add_string "name" "$(uci -q get network-modes.accesspoint.name || echo 'Access Point')"
json_add_string "description" "$(uci -q get network-modes.accesspoint.description)"
json_add_string "icon" "📶"
json_add_boolean "active" "$([ "$current_mode" = "accesspoint" ] && echo 1 || echo 0)"
json_add_string "wifi_channel" "$(uci -q get network-modes.accesspoint.wifi_channel)"
json_add_string "wifi_htmode" "$(uci -q get network-modes.accesspoint.wifi_htmode)"
json_add_int "wifi_txpower" "$(uci -q get network-modes.accesspoint.wifi_txpower || echo 20)"
json_add_boolean "roaming_enabled" "$(uci -q get network-modes.accesspoint.roaming_enabled || echo 0)"
json_add_boolean "band_steering" "$(uci -q get network-modes.accesspoint.band_steering || echo 0)"
json_close_object
# Relay mode
json_add_object
json_add_string "id" "relay"
json_add_string "name" "$(uci -q get network-modes.relay.name || echo 'Relay / Extender')"
json_add_string "description" "$(uci -q get network-modes.relay.description)"
json_add_string "icon" "🔄"
json_add_boolean "active" "$([ "$current_mode" = "relay" ] && echo 1 || echo 0)"
json_add_boolean "wireguard_enabled" "$(uci -q get network-modes.relay.wireguard_enabled || echo 0)"
json_add_string "wireguard_interface" "$(uci -q get network-modes.relay.wireguard_interface)"
json_add_boolean "mtu_optimization" "$(uci -q get network-modes.relay.mtu_optimization || echo 0)"
json_add_boolean "mss_clamping" "$(uci -q get network-modes.relay.mss_clamping || echo 0)"
json_close_object
# Router mode
json_add_object
json_add_string "id" "router"
json_add_string "name" "$(uci -q get network-modes.router.name || echo 'Router')"
json_add_string "description" "$(uci -q get network-modes.router.description)"
json_add_string "icon" "🌐"
json_add_boolean "active" "$([ "$current_mode" = "router" ] && echo 1 || echo 0)"
json_add_string "wan_protocol" "$(uci -q get network-modes.router.wan_protocol)"
json_add_boolean "nat_enabled" "$(uci -q get network-modes.router.nat_enabled || echo 1)"
json_add_boolean "firewall_enabled" "$(uci -q get network-modes.router.firewall_enabled || echo 1)"
json_add_boolean "proxy_enabled" "$(uci -q get network-modes.router.proxy_enabled || echo 0)"
json_add_string "proxy_type" "$(uci -q get network-modes.router.proxy_type)"
json_add_boolean "https_frontend" "$(uci -q get network-modes.router.https_frontend || echo 0)"
json_add_string "frontend_type" "$(uci -q get network-modes.router.frontend_type)"
json_close_object
json_close_array
json_dump
}
# Get sniffer mode configuration
get_sniffer_config() {
json_init
json_add_string "mode" "sniffer"
json_add_string "name" "Sniffer / Passthrough Mode"
json_add_string "description" "Transparent Ethernet bridge without IP for passive network analysis"
# Current settings
json_add_string "bridge_interface" "$(uci -q get network-modes.sniffer.bridge_interface || echo 'br-lan')"
json_add_string "capture_interface" "$(uci -q get network-modes.sniffer.capture_interface || echo 'eth0')"
json_add_boolean "netifyd_enabled" "$(uci -q get network-modes.sniffer.netifyd_enabled || echo 1)"
json_add_boolean "promiscuous" "$(uci -q get network-modes.sniffer.promiscuous || echo 1)"
# Available interfaces for bridging
json_add_array "available_interfaces"
for iface in $(ls /sys/class/net/ | grep -v "^lo$"); do
json_add_string "" "$iface"
done
json_close_array
# Netifyd status
local netifyd_running=0
pgrep -x netifyd >/dev/null && netifyd_running=1
json_add_boolean "netifyd_running" "$netifyd_running"
# Sample config preview
json_add_string "config_preview" "# /etc/config/network bridge config
config interface 'lan'
option proto 'none'
option type 'bridge'
option ifname 'eth0 eth1'
option delegate '0'"
json_dump
}
# Get access point mode configuration
get_ap_config() {
json_init
json_add_string "mode" "accesspoint"
json_add_string "name" "Access Point Mode"
json_add_string "description" "WiFi Access Point with advanced optimizations"
# Current settings
json_add_string "upstream_interface" "$(uci -q get network-modes.accesspoint.upstream_interface || echo 'eth0')"
json_add_boolean "dhcp_client" "$(uci -q get network-modes.accesspoint.dhcp_client || echo 1)"
json_add_string "wifi_channel" "$(uci -q get network-modes.accesspoint.wifi_channel || echo 'auto')"
json_add_string "wifi_htmode" "$(uci -q get network-modes.accesspoint.wifi_htmode || echo 'VHT80')"
json_add_int "wifi_txpower" "$(uci -q get network-modes.accesspoint.wifi_txpower || echo 20)"
# WiFi tweaks
json_add_object "wifi_tweaks"
json_add_boolean "roaming_80211r" "$(uci -q get network-modes.accesspoint.roaming_enabled || echo 0)"
json_add_boolean "rrm_80211k" "$(uci -q get network-modes.accesspoint.rrm_enabled || echo 0)"
json_add_boolean "wnm_80211v" "$(uci -q get network-modes.accesspoint.wnm_enabled || echo 0)"
json_add_boolean "band_steering" "$(uci -q get network-modes.accesspoint.band_steering || echo 0)"
json_add_boolean "airtime_fairness" "$(uci -q get network-modes.accesspoint.airtime_fairness || echo 0)"
json_add_boolean "beamforming" "$(uci -q get network-modes.accesspoint.beamforming || echo 1)"
json_close_object
# Available channels
json_add_array "available_channels_2g"
for ch in 1 6 11; do
json_add_int "" "$ch"
done
json_close_array
json_add_array "available_channels_5g"
for ch in 36 40 44 48 149 153 157 161; do
json_add_int "" "$ch"
done
json_close_array
# HT modes
json_add_array "available_htmodes"
json_add_string "" "HT20"
json_add_string "" "HT40"
json_add_string "" "VHT40"
json_add_string "" "VHT80"
json_add_string "" "VHT160"
json_add_string "" "HE40"
json_add_string "" "HE80"
json_add_string "" "HE160"
json_close_array
# WiFi info
local wifi_phy=""
local wifi_driver=""
if [ -d /sys/class/ieee80211/phy0 ]; then
wifi_phy="phy0"
wifi_driver=$(cat /sys/class/ieee80211/phy0/device/uevent 2>/dev/null | grep DRIVER | cut -d= -f2)
fi
json_add_string "wifi_phy" "$wifi_phy"
json_add_string "wifi_driver" "$wifi_driver"
json_dump
}
# Get relay mode configuration
get_relay_config() {
json_init
json_add_string "mode" "relay"
json_add_string "name" "Relay / Extender Mode"
json_add_string "description" "Network relay with LAN optimization and WireGuard tunneling"
# Current settings
json_add_string "relay_interface" "$(uci -q get network-modes.relay.relay_interface || echo 'wlan0')"
json_add_string "lan_interface" "$(uci -q get network-modes.relay.lan_interface || echo 'eth0')"
# WireGuard settings
json_add_object "wireguard"
json_add_boolean "enabled" "$(uci -q get network-modes.relay.wireguard_enabled || echo 0)"
json_add_string "interface" "$(uci -q get network-modes.relay.wireguard_interface || echo 'wg0')"
json_add_int "mtu" "$(uci -q get network-modes.relay.wireguard_mtu || echo 1420)"
json_add_boolean "persistent_keepalive" "$(uci -q get network-modes.relay.persistent_keepalive || echo 1)"
json_close_object
# Optimization settings
json_add_object "optimizations"
json_add_boolean "mtu_optimization" "$(uci -q get network-modes.relay.mtu_optimization || echo 1)"
json_add_boolean "mss_clamping" "$(uci -q get network-modes.relay.mss_clamping || echo 1)"
json_add_boolean "tcp_optimization" "$(uci -q get network-modes.relay.tcp_optimization || echo 1)"
json_add_int "conntrack_max" "$(uci -q get network-modes.relay.conntrack_max || echo 16384)"
json_close_object
# Relayd status
local relayd_installed=0
opkg list-installed 2>/dev/null | grep -q "^relayd" && relayd_installed=1
json_add_boolean "relayd_available" "$relayd_installed"
# WireGuard interfaces
json_add_array "wg_interfaces"
if command -v wg >/dev/null 2>&1; then
for wgiface in $(wg show interfaces 2>/dev/null); do
json_add_string "" "$wgiface"
done
fi
json_close_array
json_dump
}
# Get router mode configuration
get_router_config() {
json_init
json_add_string "mode" "router"
json_add_string "name" "Router Mode"
json_add_string "description" "Full router with WAN, proxy, and HTTPS frontends"
# WAN settings
json_add_object "wan"
json_add_string "interface" "$(uci -q get network-modes.router.wan_interface || echo 'eth1')"
json_add_string "protocol" "$(uci -q get network-modes.router.wan_protocol || echo 'dhcp')"
json_add_boolean "nat_enabled" "$(uci -q get network-modes.router.nat_enabled || echo 1)"
json_close_object
# LAN settings
json_add_object "lan"
json_add_string "interface" "$(uci -q get network-modes.router.lan_interface || echo 'br-lan')"
json_add_string "ip_address" "$(uci -q get network.lan.ipaddr || echo '192.168.1.1')"
json_add_string "netmask" "$(uci -q get network.lan.netmask || echo '255.255.255.0')"
json_close_object
# Firewall
json_add_object "firewall"
json_add_boolean "enabled" "$(uci -q get network-modes.router.firewall_enabled || echo 1)"
json_add_boolean "syn_flood" "$(uci -q get firewall.@defaults[0].syn_flood || echo 1)"
json_add_string "input" "$(uci -q get firewall.@zone[0].input || echo 'ACCEPT')"
json_add_string "output" "$(uci -q get firewall.@zone[0].output || echo 'ACCEPT')"
json_add_string "forward" "$(uci -q get firewall.@zone[0].forward || echo 'REJECT')"
json_close_object
# Proxy settings
json_add_object "proxy"
json_add_boolean "enabled" "$(uci -q get network-modes.router.proxy_enabled || echo 0)"
json_add_string "type" "$(uci -q get network-modes.router.proxy_type || echo 'squid')"
json_add_int "port" "$(uci -q get network-modes.router.proxy_port || echo 3128)"
json_add_boolean "transparent" "$(uci -q get network-modes.router.proxy_transparent || echo 0)"
json_add_boolean "dns_over_https" "$(uci -q get network-modes.router.dns_over_https || echo 0)"
json_close_object
# HTTPS Frontend settings
json_add_object "https_frontend"
json_add_boolean "enabled" "$(uci -q get network-modes.router.https_frontend || echo 0)"
json_add_string "type" "$(uci -q get network-modes.router.frontend_type || echo 'nginx')"
json_add_int "http_port" "$(uci -q get network-modes.router.frontend_http_port || echo 80)"
json_add_int "https_port" "$(uci -q get network-modes.router.frontend_https_port || echo 443)"
json_add_boolean "letsencrypt" "$(uci -q get network-modes.router.letsencrypt || echo 0)"
json_close_object
# Virtual hosts
json_add_array "virtual_hosts"
local idx=0
while true; do
local domain=$(uci -q get network-modes.@vhost[$idx].domain)
[ -z "$domain" ] && break
json_add_object
json_add_string "domain" "$domain"
json_add_string "backend" "$(uci -q get network-modes.@vhost[$idx].backend)"
json_add_int "port" "$(uci -q get network-modes.@vhost[$idx].port || echo 80)"
json_add_boolean "ssl" "$(uci -q get network-modes.@vhost[$idx].ssl || echo 0)"
json_close_object
idx=$((idx + 1))
done
json_close_array
# Available protocols
json_add_array "available_wan_protocols"
json_add_string "" "dhcp"
json_add_string "" "static"
json_add_string "" "pppoe"
json_add_string "" "pptp"
json_add_string "" "l2tp"
json_close_array
json_dump
}
# Apply mode change (with actual network reconfiguration and rollback timer)
apply_mode() {
json_init
local pending_mode=$(uci -q get network-modes.config.pending_mode || echo "")
if [ -z "$pending_mode" ]; then
json_add_boolean "success" 0
json_add_string "error" "Aucun mode en attente. Utilisez set_mode d'abord."
json_dump
return
fi
local current_mode=$(uci -q get network-modes.config.current_mode || echo "router")
# Backup current config
mkdir -p "$BACKUP_DIR"
local backup_file="$BACKUP_DIR/backup_$(date +%Y%m%d_%H%M%S).tar.gz"
tar -czf "$backup_file" /etc/config/network /etc/config/wireless /etc/config/firewall /etc/config/dhcp 2>/dev/null
# Apply network configuration based on mode
case "$pending_mode" in
router)
# Router mode: NAT, DHCP server, firewall
# WAN interface
uci delete network.wan 2>/dev/null
uci set network.wan=interface
uci set network.wan.proto='dhcp'
uci set network.wan.device='eth1'
# LAN interface
uci set network.lan=interface
uci set network.lan.proto='static'
uci set network.lan.device='eth0'
uci set network.lan.ipaddr='192.168.1.1'
uci set network.lan.netmask='255.255.255.0'
# DHCP server
uci set dhcp.lan=dhcp
uci set dhcp.lan.interface='lan'
uci set dhcp.lan.start='100'
uci set dhcp.lan.limit='150'
uci set dhcp.lan.leasetime='12h'
# Firewall zones
uci set firewall.@zone[0]=zone
uci set firewall.@zone[0].name='lan'
uci set firewall.@zone[0].input='ACCEPT'
uci set firewall.@zone[0].output='ACCEPT'
uci set firewall.@zone[0].forward='ACCEPT'
uci set firewall.@zone[1]=zone
uci set firewall.@zone[1].name='wan'
uci set firewall.@zone[1].input='REJECT'
uci set firewall.@zone[1].output='ACCEPT'
uci set firewall.@zone[1].forward='REJECT'
uci set firewall.@zone[1].masq='1'
uci set firewall.@zone[1].mtu_fix='1'
generate_squid_config
generate_tinyproxy_config
generate_privoxy_config
configure_doh_proxy
apply_transparent_proxy_rules
deploy_nginx_vhosts
;;
accesspoint)
# Access Point mode: Bridge, no NAT, DHCP client
# Delete WAN
uci delete network.wan 2>/dev/null
# Bridge LAN
uci set network.lan=interface
uci set network.lan.proto='dhcp'
uci set network.lan.type='bridge'
uci set network.lan.ifname='eth0 eth1'
# Disable DHCP server
uci set dhcp.lan.ignore='1'
# Disable firewall
uci set firewall.@zone[0].input='ACCEPT'
uci set firewall.@zone[0].forward='ACCEPT'
uci delete firewall.@zone[1] 2>/dev/null
apply_accesspoint_features
;;
relay)
# Repeater mode: STA + AP relay
# Client interface (sta)
uci set network.wwan=interface
uci set network.wwan.proto='dhcp'
# AP interface
uci set network.lan=interface
uci set network.lan.proto='static'
uci set network.lan.ipaddr='192.168.2.1'
uci set network.lan.netmask='255.255.255.0'
# Relay with relayd
uci set network.stabridge=interface
uci set network.stabridge.proto='relay'
uci set network.stabridge.network='lan wwan'
apply_wireguard_config
apply_mtu_clamping
enable_tcp_bbr
;;
bridge)
# Pure L2 bridge: all interfaces bridged, DHCP client
uci delete network.wan 2>/dev/null
# Bridge all interfaces
uci set network.lan=interface
uci set network.lan.proto='dhcp'
uci set network.lan.type='bridge'
uci set network.lan.bridge_empty='1'
# Disable DHCP server
uci set dhcp.lan.ignore='1'
# Disable firewall
uci delete firewall.@zone[1] 2>/dev/null
;;
esac
if [ "$pending_mode" = "sniffer" ]; then
start_packet_capture
else
stop_packet_capture
fi
# Commit all changes
uci commit network
uci commit dhcp
uci commit firewall
# Update current mode
uci set network-modes.config.current_mode="$pending_mode"
uci set network-modes.config.last_change="$(date '+%Y-%m-%d %H:%M:%S')"
uci set network-modes.config.rollback_timer="120"
uci commit network-modes
# Start rollback timer (2 minutes) in background
(
for i in $(seq 120 -1 0); do
echo "$i" > /tmp/network-mode-rollback.remaining
sleep 1
done
# Timer expired, rollback
logger -t network-modes "Rollback timer expired, reverting to $current_mode"
cd /
tar -xzf "$backup_file" 2>/dev/null
/etc/init.d/network reload 2>&1
/etc/init.d/firewall reload 2>&1
/etc/init.d/dnsmasq reload 2>&1
uci set network-modes.config.current_mode="$current_mode"
uci delete network-modes.config.pending_mode 2>/dev/null
uci set network-modes.config.last_change="$(date '+%Y-%m-%d %H:%M:%S') (auto-rollback)"
uci commit network-modes
rm -f /tmp/network-mode-rollback.pid
rm -f /tmp/network-mode-rollback.remaining
) &
echo $! > /tmp/network-mode-rollback.pid
# Apply network changes NOW (async to not block RPC)
(
sleep 2
/etc/init.d/network reload 2>&1
/etc/init.d/firewall reload 2>&1
/etc/init.d/dnsmasq reload 2>&1
) &
json_add_boolean "success" 1
json_add_string "mode" "$pending_mode"
json_add_string "previous_mode" "$current_mode"
json_add_string "message" "Mode $pending_mode appliqué. Confirmez dans les 2 minutes ou rollback automatique."
json_add_string "backup" "$backup_file"
json_add_int "rollback_seconds" 120
json_dump
}
# Update mode settings
update_settings() {
read input
json_load "$input"
json_get_var mode mode
json_init
case "$mode" in
sniffer)
json_get_var bridge_interface bridge_interface
json_get_var capture_interface capture_interface
json_get_var netifyd_enabled netifyd_enabled
json_get_var promiscuous promiscuous
[ -n "$bridge_interface" ] && uci set network-modes.sniffer.bridge_interface="$bridge_interface"
[ -n "$capture_interface" ] && uci set network-modes.sniffer.capture_interface="$capture_interface"
[ -n "$netifyd_enabled" ] && uci set network-modes.sniffer.netifyd_enabled="$netifyd_enabled"
[ -n "$promiscuous" ] && uci set network-modes.sniffer.promiscuous="$promiscuous"
;;
accesspoint)
json_get_var wifi_channel wifi_channel
json_get_var wifi_htmode wifi_htmode
json_get_var wifi_txpower wifi_txpower
json_get_var roaming_enabled roaming_enabled
json_get_var band_steering band_steering
json_get_var rrm_enabled rrm_enabled
json_get_var wnm_enabled wnm_enabled
json_get_var airtime_fairness airtime_fairness
json_get_var beamforming beamforming
[ -n "$wifi_channel" ] && uci set network-modes.accesspoint.wifi_channel="$wifi_channel"
[ -n "$wifi_htmode" ] && uci set network-modes.accesspoint.wifi_htmode="$wifi_htmode"
[ -n "$wifi_txpower" ] && uci set network-modes.accesspoint.wifi_txpower="$wifi_txpower"
[ -n "$roaming_enabled" ] && uci set network-modes.accesspoint.roaming_enabled="$roaming_enabled"
[ -n "$band_steering" ] && uci set network-modes.accesspoint.band_steering="$band_steering"
[ -n "$rrm_enabled" ] && uci set network-modes.accesspoint.rrm_enabled="$rrm_enabled"
[ -n "$wnm_enabled" ] && uci set network-modes.accesspoint.wnm_enabled="$wnm_enabled"
[ -n "$airtime_fairness" ] && uci set network-modes.accesspoint.airtime_fairness="$airtime_fairness"
[ -n "$beamforming" ] && uci set network-modes.accesspoint.beamforming="$beamforming"
;;
relay)
json_get_var wireguard_enabled wireguard_enabled
json_get_var wireguard_interface wireguard_interface
json_get_var wireguard_mtu wireguard_mtu
json_get_var mtu_optimization mtu_optimization
json_get_var mss_clamping mss_clamping
json_get_var relay_interface relay_interface
json_get_var lan_interface lan_interface
json_get_var tcp_optimization tcp_optimization
json_get_var conntrack_max conntrack_max
[ -n "$wireguard_enabled" ] && uci set network-modes.relay.wireguard_enabled="$wireguard_enabled"
[ -n "$wireguard_interface" ] && uci set network-modes.relay.wireguard_interface="$wireguard_interface"
[ -n "$wireguard_mtu" ] && uci set network-modes.relay.wireguard_mtu="$wireguard_mtu"
[ -n "$relay_interface" ] && uci set network-modes.relay.relay_interface="$relay_interface"
[ -n "$lan_interface" ] && uci set network-modes.relay.lan_interface="$lan_interface"
[ -n "$mtu_optimization" ] && uci set network-modes.relay.mtu_optimization="$mtu_optimization"
[ -n "$mss_clamping" ] && uci set network-modes.relay.mss_clamping="$mss_clamping"
[ -n "$tcp_optimization" ] && uci set network-modes.relay.tcp_optimization="$tcp_optimization"
[ -n "$conntrack_max" ] && uci set network-modes.relay.conntrack_max="$conntrack_max"
;;
router)
json_get_var wan_interface wan_interface
json_get_var wan_protocol wan_protocol
json_get_var nat_enabled nat_enabled
json_get_var firewall_enabled firewall_enabled
json_get_var proxy_enabled proxy_enabled
json_get_var proxy_type proxy_type
json_get_var proxy_port proxy_port
json_get_var https_frontend https_frontend
json_get_var frontend_type frontend_type
json_get_var transparent_proxy transparent_proxy
json_get_var dns_over_https dns_over_https
json_get_var letsencrypt letsencrypt
[ -n "$wan_interface" ] && uci set network-modes.router.wan_interface="$wan_interface"
[ -n "$wan_protocol" ] && uci set network-modes.router.wan_protocol="$wan_protocol"
[ -n "$nat_enabled" ] && uci set network-modes.router.nat_enabled="$nat_enabled"
[ -n "$firewall_enabled" ] && uci set network-modes.router.firewall_enabled="$firewall_enabled"
[ -n "$proxy_enabled" ] && uci set network-modes.router.proxy_enabled="$proxy_enabled"
[ -n "$proxy_type" ] && uci set network-modes.router.proxy_type="$proxy_type"
[ -n "$proxy_port" ] && uci set network-modes.router.proxy_port="$proxy_port"
[ -n "$https_frontend" ] && uci set network-modes.router.https_frontend="$https_frontend"
[ -n "$frontend_type" ] && uci set network-modes.router.frontend_type="$frontend_type"
[ -n "$transparent_proxy" ] && uci set network-modes.router.proxy_transparent="$transparent_proxy"
[ -n "$dns_over_https" ] && uci set network-modes.router.dns_over_https="$dns_over_https"
[ -n "$letsencrypt" ] && uci set network-modes.router.letsencrypt="$letsencrypt"
;;
*)
json_add_boolean "success" 0
json_add_string "error" "Invalid mode"
json_dump
return
;;
esac
uci commit network-modes
[ "$mode" = "accesspoint" ] && apply_accesspoint_features
if [ "$mode" = "sniffer" ]; then
local current_mode=$(uci -q get network-modes.config.current_mode || echo "")
[ "$current_mode" = "sniffer" ] && start_packet_capture
fi
json_add_boolean "success" 1
json_add_string "message" "Settings updated for $mode mode"
json_dump
}
# Squid proxy configuration
generate_squid_config() {
local proxy_type=$(uci -q get network-modes.router.proxy_type || echo "none")
local enabled=$(uci -q get network-modes.router.proxy_enabled || echo 0)
[ "$proxy_type" = "squid" ] || return 0
[ "$enabled" = "1" ] || return 0
if ! command -v squid >/dev/null 2>&1; then
logger -t network-modes "squid binary not found"
return 1
fi
local proxy_port=$(uci -q get network-modes.router.proxy_port || echo 3128)
local cache_size=$(uci -q get network-modes.router.proxy_cache_size || echo 256)
mkdir -p /etc/squid /var/spool/squid /var/log/squid
cat > /etc/squid/squid.conf <<EOF
# SecuBox Network Modes - Squid Configuration
# Generated: $(date)
http_port $proxy_port transparent
cache_dir ufs /var/spool/squid $cache_size 16 256
cache_mem 64 MB
maximum_object_size 4096 KB
minimum_object_size 0 KB
acl localnet src 192.168.0.0/16
acl localnet src 10.0.0.0/8
acl localnet src fc00::/7
acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 443
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localnet
http_access allow localhost
http_access deny all
access_log /var/log/squid/access.log squid
cache_log /var/log/squid/cache.log
dns_nameservers 1.1.1.1 8.8.8.8
forwarded_for on
via on
EOF
squid -z >/dev/null 2>&1 || true
/etc/init.d/squid restart >/dev/null 2>&1 || true
}
generate_tinyproxy_config() {
local proxy_type=$(uci -q get network-modes.router.proxy_type || echo "none")
local enabled=$(uci -q get network-modes.router.proxy_enabled || echo 0)
[ "$proxy_type" = "tinyproxy" ] || return 0
[ "$enabled" = "1" ] || return 0
if ! command -v tinyproxy >/dev/null 2>&1; then
logger -t network-modes "tinyproxy binary not found"
return 1
fi
local proxy_port=$(uci -q get network-modes.router.proxy_port || echo 8888)
mkdir -p /etc/tinyproxy
cat > /etc/tinyproxy/tinyproxy.conf <<EOF
User nobody
Group nogroup
Port $proxy_port
Listen 0.0.0.0
Timeout 600
DefaultErrorFile "/usr/share/tinyproxy/default.html"
Logfile "/var/log/tinyproxy/tinyproxy.log"
StatFile "/usr/share/tinyproxy/stats.html"
MaxClients 100
MinSpareServers 5
MaxSpareServers 20
StartServers 10
Allow 192.168.0.0/16
Allow 10.0.0.0/8
Allow 172.16.0.0/12
ViaProxyName "SecuBox"
EOF
/etc/init.d/tinyproxy restart >/dev/null 2>&1 || true
}
generate_privoxy_config() {
local proxy_type=$(uci -q get network-modes.router.proxy_type || echo "none")
local enabled=$(uci -q get network-modes.router.proxy_enabled || echo 0)
[ "$proxy_type" = "privoxy" ] || return 0
[ "$enabled" = "1" ] || return 0
if ! command -v privoxy >/dev/null 2>&1; then
logger -t network-modes "privoxy binary not found"
return 1
fi
local proxy_port=$(uci -q get network-modes.router.proxy_port || echo 8118)
mkdir -p /etc/privoxy
cat > /etc/privoxy/config <<EOF
listen-address 0.0.0.0:$proxy_port
toggle 1
enable-remote-toggle 0
enable-remote-http-toggle 0
accept-intercepted-requests 1
forward-socks5 / 127.0.0.1:9050 .
logfile /var/log/privoxy/logfile
EOF
/etc/init.d/privoxy restart >/dev/null 2>&1 || true
}
apply_transparent_proxy_rules() {
local proxy_enabled=$(uci -q get network-modes.router.proxy_enabled || echo 0)
local proxy_port=$(uci -q get network-modes.router.proxy_port || echo 3128)
local transparent=$(uci -q get network-modes.router.proxy_transparent || echo 0)
uci -q delete firewall.proxy_redirect
if [ "$proxy_enabled" = "1" ] && [ "$transparent" = "1" ]; then
uci set firewall.proxy_redirect=redirect
uci set firewall.proxy_redirect.name='Transparent Web Proxy'
uci set firewall.proxy_redirect.src='lan'
uci set firewall.proxy_redirect.proto='tcp'
uci set firewall.proxy_redirect.src_dport='80'
uci set firewall.proxy_redirect.dest_port="$proxy_port"
uci set firewall.proxy_redirect.target='DNAT'
fi
uci commit firewall
/etc/init.d/firewall reload >/dev/null 2>&1 || true
}
configure_doh_proxy() {
local doh_enabled=$(uci -q get network-modes.router.dns_over_https || echo 0)
[ "$doh_enabled" = "1" ] || return 0
if ! command -v https_dns_proxy >/dev/null 2>&1; then
logger -t network-modes "https_dns_proxy not installed"
return 1
fi
local provider=$(uci -q get network-modes.router.doh_provider || echo "cloudflare")
local doh_url
case "$provider" in
cloudflare) doh_url="https://1.1.1.1/dns-query" ;;
google) doh_url="https://dns.google/dns-query" ;;
quad9) doh_url="https://dns.quad9.net/dns-query" ;;
*) doh_url="https://1.1.1.1/dns-query" ;;
esac
uci set https-dns-proxy.@https-dns-proxy[0]=https-dns-proxy
uci set https-dns-proxy.@https-dns-proxy[0].resolver_url="$doh_url"
uci set https-dns-proxy.@https-dns-proxy[0].listen_addr='127.0.0.1'
uci set https-dns-proxy.@https-dns-proxy[0].listen_port='5053'
uci commit https-dns-proxy
uci set dhcp.@dnsmasq[0].noresolv='1'
uci -q delete dhcp.@dnsmasq[0].server
uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#5053"
uci commit dhcp
/etc/init.d/https-dns-proxy restart >/dev/null 2>&1 || true
/etc/init.d/dnsmasq restart >/dev/null 2>&1 || true
}
apply_accesspoint_features() {
local iface="wireless.@wifi-iface[0]"
uci -q show "$iface" >/dev/null 2>&1 || return 0
local changed=0
local ft_enabled=$(uci -q get network-modes.accesspoint.roaming_enabled || echo 0)
if [ "$ft_enabled" = "1" ]; then
local ssid=$(uci -q get $iface.ssid || echo "SecuBox")
local mobility_domain=$(echo -n "$ssid" | md5sum 2>/dev/null | cut -c1-4)
[ -z "$mobility_domain" ] && mobility_domain="a1b2"
uci set $iface.ieee80211r='1'
uci set $iface.mobility_domain="$mobility_domain"
uci set $iface.ft_over_ds='1'
uci set $iface.ft_psk_generate_local='1'
uci set $iface.reassociation_deadline='1000'
changed=1
else
uci set $iface.ieee80211r='0'
uci -q delete $iface.mobility_domain
fi
local rrm_enabled=$(uci -q get network-modes.accesspoint.rrm_enabled || echo 0)
if [ "$rrm_enabled" = "1" ]; then
uci set $iface.ieee80211k='1'
uci set $iface.rrm_neighbor_report='1'
uci set $iface.rrm_beacon_report='1'
changed=1
else
uci set $iface.ieee80211k='0'
uci -q delete $iface.rrm_neighbor_report
uci -q delete $iface.rrm_beacon_report
fi
local v_enabled=$(uci -q get network-modes.accesspoint.wnm_enabled || echo 0)
if [ "$v_enabled" = "1" ]; then
uci set $iface.ieee80211v='1'
uci set $iface.bss_transition='1'
uci set $iface.wnm_sleep_mode='1'
changed=1
else
uci set $iface.ieee80211v='0'
uci -q delete $iface.bss_transition
uci -q delete $iface.wnm_sleep_mode
fi
local band=$(uci -q get network-modes.accesspoint.band_steering || echo 0)
if [ "$band" = "1" ]; then
local radio_2g=$(uci show wireless 2>/dev/null | grep "band='2g'" | head -n1)
local radio_5g=$(uci show wireless 2>/dev/null | grep "band='5g'" | head -n1)
if [ -n "$radio_2g" ] && [ -n "$radio_5g" ]; then
uci set $iface.bss_load_update_period='60'
uci set $iface.chan_util_avg_period='600'
uci set $iface.disassoc_low_ack='1'
changed=1
else
logger -t network-modes "band steering requested but dual-band radios missing"
fi
else
uci -q delete $iface.bss_load_update_period
uci -q delete $iface.chan_util_avg_period
uci -q delete $iface.disassoc_low_ack
fi
if [ "$changed" = "1" ]; then
uci commit wireless
wifi reload >/dev/null 2>&1 || true
fi
}
start_packet_capture() {
local capture_enabled=$(uci -q get network-modes.sniffer.pcap_capture || echo 0)
if [ "$capture_enabled" != "1" ]; then
stop_packet_capture
return 0
fi
if ! command -v tcpdump >/dev/null 2>&1; then
logger -t network-modes "tcpdump not installed"
return 1
fi
local capture_interface=$(uci -q get network-modes.sniffer.capture_interface || echo "br-lan")
local capture_filter=$(uci -q get network-modes.sniffer.capture_filter || echo "")
local max_size=$(uci -q get network-modes.sniffer.pcap_max_size || echo "100")
local rotate_count=$(uci -q get network-modes.sniffer.pcap_rotate || echo "10")
mkdir -p "$PCAP_DIR"
stop_packet_capture
if [ -n "$capture_filter" ]; then
tcpdump -i "$capture_interface" -w "$PCAP_DIR/capture.pcap" -C "$max_size" -W "$rotate_count" -s 0 $capture_filter >/dev/null 2>&1 &
else
tcpdump -i "$capture_interface" -w "$PCAP_DIR/capture.pcap" -C "$max_size" -W "$rotate_count" -s 0 >/dev/null 2>&1 &
fi
local pid=$!
echo "$pid" > /var/run/tcpdump-sniffer.pid
}
stop_packet_capture() {
if [ -f /var/run/tcpdump-sniffer.pid ]; then
local pid=$(cat /var/run/tcpdump-sniffer.pid 2>/dev/null)
[ -n "$pid" ] && kill "$pid" 2>/dev/null || true
rm -f /var/run/tcpdump-sniffer.pid
fi
killall tcpdump >/dev/null 2>&1 || true
}
validate_pcap_filter() {
read input
json_load "$input"
json_get_var filter filter
if [ -z "$filter" ]; then
json_init
json_add_boolean "valid" 1
json_add_string "filter" "all"
json_dump
return
fi
if command -v tcpdump >/dev/null 2>&1 && tcpdump -i any -d "$filter" >/dev/null 2>&1; then
json_init
json_add_boolean "valid" 1
json_add_string "filter" "$filter"
json_dump
else
json_init
json_add_boolean "valid" 0
json_add_string "error" "Invalid BPF syntax"
json_dump
fi
}
cleanup_old_pcaps() {
local max_age=$(uci -q get network-modes.sniffer.pcap_retention_days || echo "7")
mkdir -p "$PCAP_DIR"
local deleted=0
find "$PCAP_DIR" -name "*.pcap*" -mtime +$max_age -type f 2>/dev/null | while read -r file; do
rm -f "$file"
deleted=$((deleted + 1))
done
local total_size=$(du -sm "$PCAP_DIR" 2>/dev/null | cut -f1 || echo "0")
json_init
json_add_boolean "success" 1
json_add_int "deleted" "$deleted"
json_add_int "total_size_mb" "$total_size"
json_dump
}
deploy_nginx_vhosts() {
local frontend_enabled=$(uci -q get network-modes.router.https_frontend || echo 0)
local frontend_type=$(uci -q get network-modes.router.frontend_type || echo nginx)
[ "$frontend_enabled" = "1" ] || return 0
[ "$frontend_type" = "nginx" ] || return 0
if ! command -v nginx >/dev/null 2>&1; then
logger -t network-modes "nginx not installed"
return 1
fi
rm -f /etc/nginx/conf.d/vhost-*.conf 2>/dev/null || true
mkdir -p /etc/nginx/conf.d /etc/acme
local idx=0
local deployed=0
while true; do
local domain=$(uci -q get network-modes.@vhost[$idx].domain)
[ -n "$domain" ] || break
local backend=$(uci -q get network-modes.@vhost[$idx].backend || echo "127.0.0.1")
local port=$(uci -q get network-modes.@vhost[$idx].port || echo 80)
local ssl=$(uci -q get network-modes.@vhost[$idx].ssl || echo 0)
cat > "/etc/nginx/conf.d/vhost-$idx.conf" <<EOF
server {
listen 80;
server_name $domain;
EOF
if [ "$ssl" = "1" ]; then
cat >> "/etc/nginx/conf.d/vhost-$idx.conf" <<EOF
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name $domain;
ssl_certificate /etc/acme/$domain/fullchain.cer;
ssl_certificate_key /etc/acme/$domain/$domain.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://$backend:$port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
else
cat >> "/etc/nginx/conf.d/vhost-$idx.conf" <<EOF
location / {
proxy_pass http://$backend:$port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
fi
deployed=$((deployed + 1))
idx=$((idx + 1))
done
/etc/init.d/nginx reload >/dev/null 2>&1 || true
logger -t network-modes "Deployed $deployed nginx vhosts"
}
issue_letsencrypt_cert() {
local domain="$1"
[ -n "$domain" ] || return 1
local acme_email=$(uci -q get network-modes.router.acme_email)
[ -n "$acme_email" ] || acme_email="admin@$domain"
if ! command -v /usr/lib/acme/acme.sh >/dev/null 2>&1; then
opkg update >/dev/null 2>&1 || true
opkg install acme acme-dnsapi >/dev/null 2>&1 || true
fi
/usr/lib/acme/acme.sh --issue -d "$domain" --webroot /www --accountemail "$acme_email" --force >/tmp/acme-issue.log 2>&1
if [ $? -eq 0 ]; then
mkdir -p /etc/acme/$domain
/usr/lib/acme/acme.sh --install-cert \
-d "$domain" \
--cert-file /etc/acme/$domain/cert.cer \
--key-file /etc/acme/$domain/$domain.key \
--fullchain-file /etc/acme/$domain/fullchain.cer \
--reloadcmd "/etc/init.d/nginx reload" >/dev/null 2>&1
return 0
else
logger -t network-modes "ACME issue failed for $domain"
return 1
fi
}
validate_ssl_cert() {
local domain="$1"
local cert_file="/etc/acme/$domain/fullchain.cer"
[ -f "$cert_file" ] || return 1
command -v openssl >/dev/null 2>&1 || return 1
local expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" | cut -d= -f2)
local expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null || echo 0)
local now_epoch=$(date +%s)
[ "$expiry_epoch" -gt 0 ] || return 1
local days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
logger -t network-modes "Cert $domain valid $days_left days"
[ "$days_left" -gt 0 ]
}
# Generate WireGuard key pair and store in UCI
generate_wireguard_keys() {
json_init
if ! command -v wg >/dev/null 2>&1; then
json_add_boolean "success" 0
json_add_string "error" "wireguard-tools not installed"
json_dump
return
fi
local privkey="$(wg genkey 2>/dev/null)"
if [ -z "$privkey" ]; then
json_add_boolean "success" 0
json_add_string "error" "failed to generate private key"
json_dump
return
fi
local pubkey
pubkey="$(printf '%s' "$privkey" | wg pubkey 2>/dev/null)"
if [ -z "$pubkey" ]; then
json_add_boolean "success" 0
json_add_string "error" "failed to derive public key"
json_dump
return
fi
uci set network-modes.relay.wg_private_key="$privkey"
uci set network-modes.relay.wg_public_key="$pubkey"
uci commit network-modes
json_add_boolean "success" 1
json_add_string "private_key" "$privkey"
json_add_string "public_key" "$pubkey"
json_dump
}
# Deploy WireGuard interface/peer config to /etc/config/network
apply_wireguard_config() {
local wg_enabled=$(uci -q get network-modes.relay.wireguard_enabled || echo "0")
[ "$wg_enabled" = "1" ] || return 0
local privkey=$(uci -q get network-modes.relay.wg_private_key)
local peer_pubkey=$(uci -q get network-modes.relay.wg_peer_pubkey)
local peer_endpoint=$(uci -q get network-modes.relay.wg_peer_endpoint)
local wg_port=$(uci -q get network-modes.relay.wg_port || echo "51820")
local wg_ip=$(uci -q get network-modes.relay.wg_ip || echo "10.200.200.2/24")
[ -n "$privkey" ] || return 1
[ -n "$peer_pubkey" ] || return 1
[ -n "$peer_endpoint" ] || return 1
uci -q delete network.wg0
uci -q delete network.wg0_peer
uci set network.wg0=interface
uci set network.wg0.proto='wireguard'
uci set network.wg0.private_key="$privkey"
uci set network.wg0.listen_port="$wg_port"
uci add_list network.wg0.addresses="$wg_ip"
uci set network.wg0_peer=wireguard_wg0
uci set network.wg0_peer.public_key="$peer_pubkey"
uci set network.wg0_peer.endpoint_host="$(echo "$peer_endpoint" | cut -d: -f1)"
uci set network.wg0_peer.endpoint_port="$(echo "$peer_endpoint" | cut -d: -f2-)"
uci set network.wg0_peer.persistent_keepalive='25'
uci add_list network.wg0_peer.allowed_ips='0.0.0.0/0'
uci commit network
/etc/init.d/network reload >/dev/null 2>&1
}
# Configure firewall MSS clamping based on configured MTU
apply_mtu_clamping() {
local mtu_optimization=$(uci -q get network-modes.relay.mtu_optimization || echo "0")
[ "$mtu_optimization" = "1" ] || return 0
local wg_mtu=$(uci -q get network-modes.relay.wg_mtu || echo "1420")
local mss_value=$((wg_mtu - 40))
if [ "$mss_value" -lt 500 ]; then
mss_value=500
fi
uci -q delete firewall.mss_clamping
uci set firewall.mss_clamping=rule
uci set firewall.mss_clamping.name='WireGuard MSS Clamping'
uci set firewall.mss_clamping.src='lan'
uci set firewall.mss_clamping.dest='wan'
uci set firewall.mss_clamping.proto='tcp'
uci set firewall.mss_clamping.tcp_flags='SYN'
uci set firewall.mss_clamping.target='TCPMSS'
uci set firewall.mss_clamping.set_mss="$mss_value"
uci commit firewall
/etc/init.d/firewall reload >/dev/null 2>&1
}
# Enable TCP BBR congestion control if requested
enable_tcp_bbr() {
local tcp_optimize=$(uci -q get network-modes.relay.tcp_optimization || echo "0")
[ "$tcp_optimize" = "1" ] || return 0
if ! modprobe tcp_bbr 2>/dev/null; then
logger -t network-modes "tcp_bbr module unavailable"
return 1
fi
cat > /etc/sysctl.d/90-tcp-bbr.conf <<EOF
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
sysctl -p /etc/sysctl.d/90-tcp-bbr.conf >/dev/null 2>&1
}
# Add virtual host
add_vhost() {
read input
json_load "$input"
json_get_var domain domain
json_get_var backend backend
json_get_var port port
json_get_var ssl ssl
json_init
if [ -z "$domain" ] || [ -z "$backend" ]; then
json_add_boolean "success" 0
json_add_string "error" "Domain and backend are required"
json_dump
return
fi
uci add network-modes vhost
uci set network-modes.@vhost[-1].domain="$domain"
uci set network-modes.@vhost[-1].backend="$backend"
uci set network-modes.@vhost[-1].port="${port:-80}"
uci set network-modes.@vhost[-1].ssl="${ssl:-0}"
uci commit network-modes
json_add_boolean "success" 1
json_add_string "message" "Virtual host $domain added"
json_dump
}
# Generate config for current mode
generate_config() {
read input
json_load "$input"
json_get_var mode mode
json_init
local config=""
case "$mode" in
sniffer)
local bridge=$(uci -q get network-modes.sniffer.bridge_interface || echo "br-lan")
config="# Sniffer Mode Configuration
# /etc/config/network
config interface 'loopback'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'
option device 'lo'
config device
option name '$bridge'
option type 'bridge'
list ports 'eth0'
list ports 'eth1'
config interface 'lan'
option device '$bridge'
option proto 'none'
# Disable DHCP
# /etc/config/dhcp
config dhcp 'lan'
option ignore '1'
# Enable promiscuous mode
# Run: ip link set eth0 promisc on
# Run: ip link set eth1 promisc on"
;;
accesspoint)
local channel=$(uci -q get network-modes.accesspoint.wifi_channel || echo "auto")
local htmode=$(uci -q get network-modes.accesspoint.wifi_htmode || echo "VHT80")
local txpower=$(uci -q get network-modes.accesspoint.wifi_txpower || echo "20")
config="# Access Point Mode Configuration
# /etc/config/network
config interface 'lan'
option proto 'dhcp'
option device 'eth0'
# /etc/config/wireless
config wifi-device 'radio0'
option type 'mac80211'
option channel '$channel'
option htmode '$htmode'
option txpower '$txpower'
option country 'FR'
config wifi-iface 'default_radio0'
option device 'radio0'
option network 'lan'
option mode 'ap'
option ssid 'OpenWrt-AP'
option encryption 'sae-mixed'
option key 'your_password'
option ieee80211r '1'
option ft_over_ds '1'
option ieee80211k '1'
option ieee80211v '1'
option bss_transition '1'"
;;
relay)
local wg_iface=$(uci -q get network-modes.relay.wireguard_interface || echo "wg0")
config="# Relay Mode Configuration
# /etc/config/network
config interface 'lan'
option proto 'static'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
option device 'br-lan'
config interface 'wwan'
option proto 'dhcp'
option device 'wlan0'
config interface '$wg_iface'
option proto 'wireguard'
option listen_port '51820'
list addresses '10.0.0.1/24'
# /etc/config/firewall - MSS clamping
config rule
option name 'MSS-Clamping'
option src '*'
option dest '*'
option proto 'tcp'
option extra '-m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu'"
;;
router)
local wan_proto=$(uci -q get network-modes.router.wan_protocol || echo "dhcp")
config="# Router Mode Configuration
# /etc/config/network
config interface 'wan'
option proto '$wan_proto'
option device 'eth1'
config interface 'lan'
option proto 'static'
option device 'br-lan'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
# /etc/config/firewall
config defaults
option syn_flood '1'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'REJECT'
config zone
option name 'wan'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option masq '1'
option mtu_fix '1'
list network 'wan'
config zone
option name 'lan'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
list network 'lan'
config forwarding
option src 'lan'
option dest 'wan'"
;;
esac
json_add_string "config" "$config"
json_add_string "mode" "$mode"
json_dump
}
# Get current mode details
get_current_mode() {
json_init
local current_mode=$(uci -q get network-modes.config.current_mode || echo "router")
local last_change=$(uci -q get network-modes.config.last_change || echo "Never")
local pending_mode=$(uci -q get network-modes.config.pending_mode || echo "")
local rollback_timer=$(uci -q get network-modes.config.rollback_timer || echo "0")
json_add_string "current_mode" "$current_mode"
json_add_string "mode_name" "$(uci -q get network-modes.$current_mode.name || echo "$current_mode")"
json_add_string "description" "$(uci -q get network-modes.$current_mode.description || echo "")"
json_add_string "last_change" "$last_change"
json_add_string "pending_mode" "$pending_mode"
json_add_int "rollback_timer" "$rollback_timer"
# Check if rollback is active
if [ -f "/tmp/network-mode-rollback.pid" ]; then
json_add_boolean "rollback_active" 1
local remaining=$(cat /tmp/network-mode-rollback.remaining 2>/dev/null || echo "0")
json_add_int "rollback_remaining" "$remaining"
else
json_add_boolean "rollback_active" 0
json_add_int "rollback_remaining" 0
fi
json_dump
}
# Get available modes
get_available_modes() {
json_init
json_add_array "modes"
local current_mode=$(uci -q get network-modes.config.current_mode || echo "router")
# Router mode
json_add_object
json_add_string "id" "router"
json_add_string "name" "Router"
json_add_string "description" "Mode routeur complet avec NAT, DHCP et firewall"
json_add_string "icon" "🌐"
json_add_boolean "current" "$([ "$current_mode" = "router" ] && echo 1 || echo 0)"
json_add_array "features"
json_add_string "" "NAT activé"
json_add_string "" "Serveur DHCP"
json_add_string "" "Firewall (zones WAN/LAN)"
json_add_string "" "Proxy optionnel"
json_close_array
json_close_object
# Access Point mode
json_add_object
json_add_string "id" "accesspoint"
json_add_string "name" "Access Point"
json_add_string "description" "Point d'accès WiFi en mode bridge, pas de NAT"
json_add_string "icon" "📶"
json_add_boolean "current" "$([ "$current_mode" = "accesspoint" ] && echo 1 || echo 0)"
json_add_array "features"
json_add_string "" "Bridge WAN+LAN"
json_add_string "" "Pas de DHCP (mode client)"
json_add_string "" "Pas de firewall"
json_add_string "" "Optimisations WiFi 802.11r/k/v"
json_close_array
json_close_object
# Repeater/Relay mode
json_add_object
json_add_string "id" "relay"
json_add_string "name" "Repeater"
json_add_string "description" "Client WiFi + Répéteur AP avec optimisations"
json_add_string "icon" "🔄"
json_add_boolean "current" "$([ "$current_mode" = "relay" ] && echo 1 || echo 0)"
json_add_array "features"
json_add_string "" "Client WiFi (sta0)"
json_add_string "" "AP répéteur (ap0)"
json_add_string "" "WireGuard optionnel"
json_add_string "" "Optimisations MTU/MSS"
json_close_array
json_close_object
# Bridge mode
json_add_object
json_add_string "id" "bridge"
json_add_string "name" "Bridge"
json_add_string "description" "Bridge Layer 2 pur, DHCP client"
json_add_string "icon" "🔗"
json_add_boolean "current" "$([ "$current_mode" = "bridge" ] && echo 1 || echo 0)"
json_add_array "features"
json_add_string "" "Bridge transparent L2"
json_add_string "" "Toutes interfaces bridgées"
json_add_string "" "DHCP client"
json_add_string "" "Pas de firewall"
json_close_array
json_close_object
json_close_array
json_dump
}
# Set mode (prepare for switch)
set_mode() {
read input
json_load "$input"
json_get_var target_mode mode
json_init
# Validate mode
case "$target_mode" in
router|accesspoint|relay|bridge)
;;
*)
json_add_boolean "success" 0
json_add_string "error" "Mode invalide: $target_mode"
json_dump
return
;;
esac
local current_mode=$(uci -q get network-modes.config.current_mode || echo "router")
if [ "$current_mode" = "$target_mode" ]; then
json_add_boolean "success" 0
json_add_string "error" "Déjà en mode $target_mode"
json_dump
return
fi
# Store pending mode
uci set network-modes.config.pending_mode="$target_mode"
uci set network-modes.config.pending_since="$(date '+%Y-%m-%d %H:%M:%S')"
uci commit network-modes
json_add_boolean "success" 1
json_add_string "current_mode" "$current_mode"
json_add_string "target_mode" "$target_mode"
json_add_string "message" "Mode $target_mode préparé. Utilisez preview_changes puis apply_mode."
json_dump
}
# Preview changes before applying
preview_changes() {
json_init
local current_mode=$(uci -q get network-modes.config.current_mode || echo "router")
local pending_mode=$(uci -q get network-modes.config.pending_mode || echo "")
if [ -z "$pending_mode" ]; then
json_add_boolean "success" 0
json_add_string "error" "Aucun changement de mode en attente"
json_dump
return
fi
json_add_boolean "success" 1
json_add_string "current_mode" "$current_mode"
json_add_string "target_mode" "$pending_mode"
# Changes array
json_add_array "changes"
case "$pending_mode" in
router)
json_add_object
json_add_string "file" "/etc/config/network"
json_add_string "change" "WAN: proto dhcp, NAT enabled"
json_close_object
json_add_object
json_add_string "file" "/etc/config/network"
json_add_string "change" "LAN: static IP, DHCP server enabled"
json_close_object
json_add_object
json_add_string "file" "/etc/config/firewall"
json_add_string "change" "Zones: WAN, LAN, forwarding rules"
json_close_object
;;
accesspoint)
json_add_object
json_add_string "file" "/etc/config/network"
json_add_string "change" "Bridge: br-lan (WAN+LAN)"
json_close_object
json_add_object
json_add_string "file" "/etc/config/network"
json_add_string "change" "DHCP: client mode"
json_close_object
json_add_object
json_add_string "file" "/etc/config/firewall"
json_add_string "change" "Firewall: disabled"
json_close_object
;;
relay)
json_add_object
json_add_string "file" "/etc/config/wireless"
json_add_string "change" "WiFi STA: client mode on wlan0"
json_close_object
json_add_object
json_add_string "file" "/etc/config/wireless"
json_add_string "change" "WiFi AP: repeater mode on wlan1"
json_close_object
json_add_object
json_add_string "file" "/etc/config/network"
json_add_string "change" "Relay: relayd between sta0 and ap0"
json_close_object
;;
bridge)
json_add_object
json_add_string "file" "/etc/config/network"
json_add_string "change" "Bridge: all interfaces to br-lan"
json_close_object
json_add_object
json_add_string "file" "/etc/config/network"
json_add_string "change" "DHCP: client only"
json_close_object
json_add_object
json_add_string "file" "/etc/config/firewall"
json_add_string "change" "Firewall: disabled"
json_close_object
;;
esac
json_close_array
# Warnings
json_add_array "warnings"
json_add_string "" "La connexion réseau sera interrompue pendant la reconfiguration"
json_add_string "" "Vous devrez peut-être reconnecter via la nouvelle IP"
json_add_string "" "Un rollback automatique de 2 minutes sera activé"
json_add_string "" "Vous devez confirmer le changement avant expiration"
json_close_array
json_dump
}
# Confirm mode change (cancel rollback timer)
confirm_mode() {
json_init
local current_mode=$(uci -q get network-modes.config.current_mode || echo "router")
# Stop rollback timer
if [ -f "/tmp/network-mode-rollback.pid" ]; then
local pid=$(cat /tmp/network-mode-rollback.pid)
kill $pid 2>/dev/null
rm -f /tmp/network-mode-rollback.pid
rm -f /tmp/network-mode-rollback.remaining
# Clear pending mode
uci delete network-modes.config.pending_mode 2>/dev/null
uci delete network-modes.config.pending_since 2>/dev/null
uci set network-modes.config.rollback_timer="0"
uci commit network-modes
json_add_boolean "success" 1
json_add_string "message" "Mode $current_mode confirmé, rollback annulé"
json_add_string "mode" "$current_mode"
else
json_add_boolean "success" 0
json_add_string "error" "Aucun rollback actif"
fi
json_dump
}
# Rollback to previous mode
rollback() {
json_init
# Stop any active rollback timer
if [ -f "/tmp/network-mode-rollback.pid" ]; then
local pid=$(cat /tmp/network-mode-rollback.pid)
kill $pid 2>/dev/null
rm -f /tmp/network-mode-rollback.pid
rm -f /tmp/network-mode-rollback.remaining
fi
# Get backup file
local latest_backup=$(ls -t "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | head -1)
if [ -z "$latest_backup" ]; then
json_add_boolean "success" 0
json_add_string "error" "Aucune sauvegarde disponible"
json_dump
return
fi
# Restore backup
cd /
tar -xzf "$latest_backup" 2>/dev/null
# Reload network services
/etc/init.d/network reload 2>&1
/etc/init.d/firewall reload 2>&1
/etc/init.d/dnsmasq reload 2>&1
# Update UCI
local previous_mode=$(uci -q get network-modes.config.current_mode || echo "router")
uci delete network-modes.config.pending_mode 2>/dev/null
uci set network-modes.config.last_change="$(date '+%Y-%m-%d %H:%M:%S') (rollback)"
uci commit network-modes
json_add_boolean "success" 1
json_add_string "message" "Configuration restaurée depuis la sauvegarde"
json_add_string "mode" "$previous_mode"
json_add_string "backup_file" "$latest_backup"
json_dump
}
# Main dispatcher
case "$1" in
list)
echo '{"status":{},"modes":{},"get_current_mode":{},"get_available_modes":{},"set_mode":{"mode":"str"},"preview_changes":{},"apply_mode":{},"confirm_mode":{},"rollback":{},"sniffer_config":{},"ap_config":{},"relay_config":{},"router_config":{},"update_settings":{"mode":"str"},"generate_wireguard_keys":{},"apply_wireguard_config":{},"apply_mtu_clamping":{},"enable_tcp_bbr":{},"add_vhost":{"domain":"str","backend":"str","port":"int","ssl":"bool"},"generate_config":{"mode":"str"},"validate_pcap_filter":{"filter":"str"},"cleanup_old_pcaps":{}}'
;;
call)
case "$2" in
status)
get_status
;;
modes)
get_modes
;;
get_current_mode)
get_current_mode
;;
get_available_modes)
get_available_modes
;;
set_mode)
set_mode
;;
preview_changes)
preview_changes
;;
apply_mode)
apply_mode
;;
confirm_mode)
confirm_mode
;;
rollback)
rollback
;;
sniffer_config)
get_sniffer_config
;;
ap_config)
get_ap_config
;;
relay_config)
get_relay_config
;;
router_config)
get_router_config
;;
update_settings)
update_settings
;;
generate_wireguard_keys)
generate_wireguard_keys
;;
apply_wireguard_config)
if apply_wireguard_config; then
json_init; json_add_boolean "success" 1; json_dump
else
json_init; json_add_boolean "success" 0; json_add_string "error" "WireGuard deployment failed"; json_dump
fi
;;
apply_mtu_clamping)
if apply_mtu_clamping; then
json_init; json_add_boolean "success" 1; json_dump
else
json_init; json_add_boolean "success" 0; json_add_string "error" "Unable to update firewall rule"; json_dump
fi
;;
enable_tcp_bbr)
if enable_tcp_bbr; then
json_init; json_add_boolean "success" 1; json_dump
else
json_init; json_add_boolean "success" 0; json_add_string "error" "TCP BBR not applied"; json_dump
fi
;;
add_vhost)
add_vhost
;;
generate_config)
generate_config
;;
validate_pcap_filter)
validate_pcap_filter
;;
cleanup_old_pcaps)
cleanup_old_pcaps
;;
*)
echo '{"error": "Unknown method"}'
;;
esac
;;
esac