2428 lines
80 KiB
Bash
Executable File
2428 lines
80 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
|
|
|
|
get_pkg_version() {
|
|
local ctrl="/usr/lib/opkg/info/luci-app-network-modes.control"
|
|
if [ -f "$ctrl" ]; then
|
|
awk -F': ' '/^Version/ { print $2; exit }' "$ctrl"
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
|
|
PKG_VERSION="$(get_pkg_version)"
|
|
|
|
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 "version" "$PKG_VERSION"
|
|
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
|
|
|
|
# Travel mode
|
|
json_add_object
|
|
json_add_string "id" "travel"
|
|
json_add_string "name" "$(uci -q get network-modes.travel.name || echo 'Travel Router')"
|
|
json_add_string "description" "$(uci -q get network-modes.travel.description || echo 'Portable router with WiFi uplink and personal hotspot')"
|
|
json_add_string "icon" "✈️"
|
|
json_add_boolean "active" "$([ "$current_mode" = "travel" ] && echo 1 || echo 0)"
|
|
json_add_string "client_interface" "$(uci -q get network-modes.travel.client_interface || echo 'wlan1')"
|
|
json_add_string "hotspot_interface" "$(uci -q get network-modes.travel.hotspot_interface || echo 'wlan0')"
|
|
json_add_boolean "mac_clone_enabled" "$([ -n "$(uci -q get network-modes.travel.clone_mac)" ] && echo 1 || echo 0)"
|
|
json_add_string "hotspot_ssid" "$(uci -q get network-modes.travel.hotspot_ssid || echo 'SecuBox-Travel')"
|
|
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
|
|
}
|
|
|
|
# Get travel router configuration
|
|
get_travel_config() {
|
|
json_init
|
|
|
|
local client_iface=$(uci -q get network-modes.travel.client_interface || echo "wlan1")
|
|
local hotspot_iface=$(uci -q get network-modes.travel.hotspot_interface || echo "wlan0")
|
|
local client_radio=$(uci -q get network-modes.travel.client_radio || echo "radio1")
|
|
local hotspot_radio=$(uci -q get network-modes.travel.hotspot_radio || echo "radio0")
|
|
local clone_mac=$(uci -q get network-modes.travel.clone_mac || echo "")
|
|
|
|
json_add_string "mode" "travel"
|
|
json_add_string "name" "$(uci -q get network-modes.travel.name || echo 'Travel Router')"
|
|
json_add_string "description" "$(uci -q get network-modes.travel.description || echo 'Portable WiFi router for hotels and coworking spaces')"
|
|
|
|
json_add_object "client"
|
|
json_add_string "interface" "$client_iface"
|
|
json_add_string "radio" "$client_radio"
|
|
json_add_string "ssid" "$(uci -q get network-modes.travel.ssid || echo '')"
|
|
json_add_string "encryption" "$(uci -q get network-modes.travel.encryption || echo 'sae-mixed')"
|
|
json_add_string "password" "$(uci -q get network-modes.travel.password || echo '')"
|
|
json_add_string "clone_mac" "$clone_mac"
|
|
json_add_boolean "mac_clone_enabled" "$([ -n "$clone_mac" ] && echo 1 || echo 0)"
|
|
json_close_object
|
|
|
|
json_add_object "hotspot"
|
|
json_add_string "interface" "$hotspot_iface"
|
|
json_add_string "radio" "$hotspot_radio"
|
|
json_add_string "ssid" "$(uci -q get network-modes.travel.hotspot_ssid || echo 'SecuBox-Travel')"
|
|
json_add_string "password" "$(uci -q get network-modes.travel.hotspot_password || echo 'TravelSafe123!')"
|
|
json_add_string "band" "$(uci -q get network-modes.travel.hotspot_band || echo 'dual')"
|
|
json_close_object
|
|
|
|
json_add_object "lan"
|
|
json_add_string "subnet" "$(uci -q get network-modes.travel.lan_subnet || echo '10.77.0.1')"
|
|
json_add_string "netmask" "$(uci -q get network-modes.travel.lan_netmask || echo '255.255.255.0')"
|
|
json_close_object
|
|
|
|
json_add_array "available_interfaces"
|
|
for iface in $(ls /sys/class/net/ 2>/dev/null | grep -E '^wl|^wlan' || true); do
|
|
json_add_string "" "$iface"
|
|
done
|
|
json_close_array
|
|
|
|
json_add_array "available_radios"
|
|
for radio in $(uci show wireless 2>/dev/null | grep "=wifi-device" | cut -d'.' -f2 | cut -d'=' -f1); do
|
|
json_add_string "" "$radio"
|
|
done
|
|
json_close_array
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Scan nearby WiFi networks for travel mode
|
|
travel_scan_networks() {
|
|
json_init
|
|
json_add_array "networks"
|
|
|
|
local iface=$(uci -q get network-modes.travel.client_interface || echo "wlan1")
|
|
local scan_output
|
|
scan_output="$(iwinfo "$iface" scan 2>/dev/null || true)"
|
|
|
|
if [ -z "$scan_output" ]; then
|
|
json_close_array
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local bssid=""
|
|
local ssid=""
|
|
local channel=""
|
|
local signal=""
|
|
local encryption=""
|
|
local quality=""
|
|
|
|
while IFS= read -r line; do
|
|
case "$line" in
|
|
Cell*)
|
|
if [ -n "$bssid" ]; then
|
|
json_add_object
|
|
json_add_string "ssid" "${ssid:-Unknown}"
|
|
json_add_string "bssid" "$bssid"
|
|
json_add_string "channel" "${channel:-?}"
|
|
json_add_string "signal" "${signal:-N/A}"
|
|
json_add_string "quality" "${quality:-N/A}"
|
|
json_add_string "encryption" "${encryption:-Unknown}"
|
|
json_close_object
|
|
fi
|
|
bssid=$(printf '%s\n' "$line" | awk '{print $5}')
|
|
ssid=""
|
|
channel=""
|
|
signal=""
|
|
encryption=""
|
|
quality=""
|
|
;;
|
|
*"ESSID:"*)
|
|
ssid=$(printf '%s\n' "$line" | sed -n 's/.*ESSID: "\(.*\)".*/\1/p')
|
|
;;
|
|
*"Channel:"*)
|
|
channel=$(printf '%s\n' "$line" | awk -F'Channel:' '{print $2}' | awk '{print $1}')
|
|
;;
|
|
*"Signal:"*)
|
|
signal=$(printf '%s\n' "$line" | awk -F'Signal:' '{print $2}' | awk '{print $1" "$2}')
|
|
if echo "$line" | grep -q 'Quality'; then
|
|
quality=$(printf '%s\n' "$line" | sed -n 's/.*Quality: \([0-9\/]*\).*/\1/p')
|
|
fi
|
|
;;
|
|
*"Encryption:"*)
|
|
encryption=$(printf '%s\n' "$line" | sed -n 's/.*Encryption: //p')
|
|
;;
|
|
esac
|
|
done <<EOF
|
|
$scan_output
|
|
EOF
|
|
|
|
if [ -n "$bssid" ]; then
|
|
json_add_object
|
|
json_add_string "ssid" "${ssid:-Unknown}"
|
|
json_add_string "bssid" "$bssid"
|
|
json_add_string "channel" "${channel:-?}"
|
|
json_add_string "signal" "${signal:-N/A}"
|
|
json_add_string "quality" "${quality:-N/A}"
|
|
json_add_string "encryption" "${encryption:-Unknown}"
|
|
json_close_object
|
|
fi
|
|
|
|
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
|
|
;;
|
|
travel)
|
|
local client_iface=$(uci -q get network-modes.travel.client_interface || echo "wlan1")
|
|
local client_radio=$(uci -q get network-modes.travel.client_radio || echo "radio1")
|
|
local hotspot_radio=$(uci -q get network-modes.travel.hotspot_radio || echo "radio0")
|
|
local travel_ssid=$(uci -q get network-modes.travel.ssid || echo "")
|
|
local travel_password=$(uci -q get network-modes.travel.password || echo "")
|
|
local travel_encryption=$(uci -q get network-modes.travel.encryption || echo "sae-mixed")
|
|
local hotspot_ssid=$(uci -q get network-modes.travel.hotspot_ssid || echo "SecuBox-Travel")
|
|
local hotspot_password=$(uci -q get network-modes.travel.hotspot_password || echo "TravelSafe123!")
|
|
local clone_mac=$(uci -q get network-modes.travel.clone_mac || echo "")
|
|
local lan_ip=$(uci -q get network-modes.travel.lan_subnet || echo "10.77.0.1")
|
|
local lan_netmask=$(uci -q get network-modes.travel.lan_netmask || echo "255.255.255.0")
|
|
|
|
uci delete network.wan 2>/dev/null
|
|
uci set network.wan=interface
|
|
uci set network.wan.proto='dhcp'
|
|
uci set network.wan.device="$client_iface"
|
|
|
|
uci set network.lan=interface
|
|
uci set network.lan.proto='static'
|
|
uci set network.lan.device='br-lan'
|
|
uci set network.lan.ipaddr="$lan_ip"
|
|
uci set network.lan.netmask="$lan_netmask"
|
|
|
|
uci set dhcp.lan=dhcp
|
|
uci set dhcp.lan.interface='lan'
|
|
uci set dhcp.lan.start='60'
|
|
uci set dhcp.lan.limit='80'
|
|
uci set dhcp.lan.leasetime='6h'
|
|
|
|
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].network='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'
|
|
|
|
uci delete wireless.travel_sta 2>/dev/null
|
|
uci set wireless.travel_sta=wifi-iface
|
|
uci set wireless.travel_sta.device="$client_radio"
|
|
uci set wireless.travel_sta.mode='sta'
|
|
uci set wireless.travel_sta.network='wan'
|
|
[ -n "$travel_ssid" ] && uci set wireless.travel_sta.ssid="$travel_ssid" || uci delete wireless.travel_sta.ssid 2>/dev/null
|
|
uci set wireless.travel_sta.encryption="$travel_encryption"
|
|
if [ -n "$travel_password" ]; then
|
|
uci set wireless.travel_sta.key="$travel_password"
|
|
else
|
|
uci delete wireless.travel_sta.key 2>/dev/null
|
|
fi
|
|
if [ -n "$clone_mac" ]; then
|
|
uci set wireless.travel_sta.macaddr="$clone_mac"
|
|
else
|
|
uci delete wireless.travel_sta.macaddr 2>/dev/null
|
|
fi
|
|
|
|
uci delete wireless.travel_ap 2>/dev/null
|
|
uci set wireless.travel_ap=wifi-iface
|
|
uci set wireless.travel_ap.device="$hotspot_radio"
|
|
uci set wireless.travel_ap.mode='ap'
|
|
uci set wireless.travel_ap.network='lan'
|
|
uci set wireless.travel_ap.ssid="$hotspot_ssid"
|
|
uci set wireless.travel_ap.encryption='sae-mixed'
|
|
uci set wireless.travel_ap.key="$hotspot_password"
|
|
uci set wireless.travel_ap.ieee80211w='1'
|
|
uci set wireless.travel_ap.hidden='0'
|
|
;;
|
|
|
|
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
|
|
;;
|
|
|
|
sniffer)
|
|
local ports=$(uci -q get network-modes.sniffer.bridge_ports || echo "eth0 eth1")
|
|
local bridge_iface=$(uci -q get network-modes.sniffer.bridge_interface || echo "br-lan")
|
|
local promisc=$(uci -q get network-modes.sniffer.promiscuous || echo 1)
|
|
|
|
uci delete network.wan 2>/dev/null
|
|
uci set network.lan=interface
|
|
uci set network.lan.proto='none'
|
|
uci set network.lan.type='bridge'
|
|
uci set network.lan.ifname="$ports"
|
|
uci set network.lan.delegate='0'
|
|
uci set network.lan.device="$bridge_iface"
|
|
|
|
uci set dhcp.lan=dhcp
|
|
uci set dhcp.lan.interface='lan'
|
|
uci set dhcp.lan.ignore='1'
|
|
|
|
uci set firewall.@zone[0].input='ACCEPT'
|
|
uci set firewall.@zone[0].output='ACCEPT'
|
|
uci set firewall.@zone[0].forward='ACCEPT'
|
|
uci delete firewall.@zone[1] 2>/dev/null
|
|
|
|
if [ "$promisc" = "1" ]; then
|
|
for port in $ports; do
|
|
ip link set "$port" promisc on 2>/dev/null || true
|
|
done
|
|
fi
|
|
;;
|
|
|
|
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"
|
|
;;
|
|
travel)
|
|
json_get_var client_interface client_interface
|
|
json_get_var client_radio client_radio
|
|
json_get_var hotspot_radio hotspot_radio
|
|
json_get_var ssid ssid
|
|
json_get_var password password
|
|
json_get_var encryption encryption
|
|
json_get_var hotspot_ssid hotspot_ssid
|
|
json_get_var hotspot_password hotspot_password
|
|
json_get_var clone_mac clone_mac
|
|
json_get_var lan_subnet lan_subnet
|
|
json_get_var lan_netmask lan_netmask
|
|
|
|
[ -n "$client_interface" ] && uci set network-modes.travel.client_interface="$client_interface"
|
|
[ -n "$client_radio" ] && uci set network-modes.travel.client_radio="$client_radio"
|
|
[ -n "$hotspot_radio" ] && uci set network-modes.travel.hotspot_radio="$hotspot_radio"
|
|
[ -n "$ssid" ] && uci set network-modes.travel.ssid="$ssid"
|
|
[ -n "$password" ] && uci set network-modes.travel.password="$password"
|
|
[ -n "$encryption" ] && uci set network-modes.travel.encryption="$encryption"
|
|
[ -n "$hotspot_ssid" ] && uci set network-modes.travel.hotspot_ssid="$hotspot_ssid"
|
|
[ -n "$hotspot_password" ] && uci set network-modes.travel.hotspot_password="$hotspot_password"
|
|
[ -n "$clone_mac" ] && uci set network-modes.travel.clone_mac="$clone_mac"
|
|
[ -n "$lan_subnet" ] && uci set network-modes.travel.lan_subnet="$lan_subnet"
|
|
[ -n "$lan_netmask" ] && uci set network-modes.travel.lan_netmask="$lan_netmask"
|
|
;;
|
|
doublenat)
|
|
json_get_var wan_interface wan_interface
|
|
json_get_var wan_protocol wan_protocol
|
|
json_get_var lan_interface lan_interface
|
|
json_get_var lan_ip lan_ip
|
|
json_get_var lan_netmask lan_netmask
|
|
json_get_var guest_network guest_network
|
|
json_get_var isolate_guest isolate_guest
|
|
json_get_var upnp_enabled upnp_enabled
|
|
json_get_var dmz_host dmz_host
|
|
|
|
[ -n "$wan_interface" ] && uci set network-modes.doublenat.wan_interface="$wan_interface"
|
|
[ -n "$wan_protocol" ] && uci set network-modes.doublenat.wan_protocol="$wan_protocol"
|
|
[ -n "$lan_interface" ] && uci set network-modes.doublenat.lan_interface="$lan_interface"
|
|
[ -n "$lan_ip" ] && uci set network-modes.doublenat.lan_ip="$lan_ip"
|
|
[ -n "$lan_netmask" ] && uci set network-modes.doublenat.lan_netmask="$lan_netmask"
|
|
[ -n "$guest_network" ] && uci set network-modes.doublenat.guest_network="$guest_network"
|
|
[ -n "$isolate_guest" ] && uci set network-modes.doublenat.isolate_guest="$isolate_guest"
|
|
[ -n "$upnp_enabled" ] && uci set network-modes.doublenat.upnp_enabled="$upnp_enabled"
|
|
[ -n "$dmz_host" ] && uci set network-modes.doublenat.dmz_host="$dmz_host"
|
|
;;
|
|
multiwan)
|
|
json_get_var wan_primary wan_primary
|
|
json_get_var wan_secondary wan_secondary
|
|
json_get_var policy policy
|
|
json_get_var tracking_host tracking_host
|
|
json_get_var tracking_interval tracking_interval
|
|
json_get_var failover_hold failover_hold
|
|
json_get_var load_balance load_balance
|
|
json_get_var use_mwan3 use_mwan3
|
|
|
|
[ -n "$wan_primary" ] && uci set network-modes.multiwan.wan_primary="$wan_primary"
|
|
[ -n "$wan_secondary" ] && uci set network-modes.multiwan.wan_secondary="$wan_secondary"
|
|
[ -n "$policy" ] && uci set network-modes.multiwan.policy="$policy"
|
|
[ -n "$tracking_host" ] && uci set network-modes.multiwan.tracking_host="$tracking_host"
|
|
[ -n "$tracking_interval" ] && uci set network-modes.multiwan.tracking_interval="$tracking_interval"
|
|
[ -n "$failover_hold" ] && uci set network-modes.multiwan.failover_hold="$failover_hold"
|
|
[ -n "$load_balance" ] && uci set network-modes.multiwan.load_balance="$load_balance"
|
|
[ -n "$use_mwan3" ] && uci set network-modes.multiwan.use_mwan3="$use_mwan3"
|
|
;;
|
|
vpnrelay)
|
|
json_get_var vpn_type vpn_type
|
|
json_get_var vpn_provider vpn_provider
|
|
json_get_var wg_interface wg_interface
|
|
json_get_var openvpn_profile openvpn_profile
|
|
json_get_var upstream_interface upstream_interface
|
|
json_get_var policy_routing policy_routing
|
|
json_get_var dns_override dns_override
|
|
json_get_var kill_switch kill_switch
|
|
json_get_var lan_bypass lan_bypass
|
|
|
|
[ -n "$vpn_type" ] && uci set network-modes.vpnrelay.vpn_type="$vpn_type"
|
|
[ -n "$vpn_provider" ] && uci set network-modes.vpnrelay.vpn_provider="$vpn_provider"
|
|
[ -n "$wg_interface" ] && uci set network-modes.vpnrelay.wg_interface="$wg_interface"
|
|
[ -n "$openvpn_profile" ] && uci set network-modes.vpnrelay.openvpn_profile="$openvpn_profile"
|
|
[ -n "$upstream_interface" ] && uci set network-modes.vpnrelay.upstream_interface="$upstream_interface"
|
|
[ -n "$policy_routing" ] && uci set network-modes.vpnrelay.policy_routing="$policy_routing"
|
|
[ -n "$dns_override" ] && uci set network-modes.vpnrelay.dns_override="$dns_override"
|
|
[ -n "$kill_switch" ] && uci set network-modes.vpnrelay.kill_switch="$kill_switch"
|
|
[ -n "$lan_bypass" ] && uci set network-modes.vpnrelay.lan_bypass="$lan_bypass"
|
|
;;
|
|
*)
|
|
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'"
|
|
;;
|
|
travel)
|
|
local travel_ssid=$(uci -q get network-modes.travel.ssid || echo "HotelWiFi")
|
|
local hotspot_ssid=$(uci -q get network-modes.travel.hotspot_ssid || echo "SecuBox-Travel")
|
|
local lan_ip=$(uci -q get network-modes.travel.lan_subnet || echo "10.77.0.1")
|
|
config="# Travel Router Mode
|
|
# /etc/config/network
|
|
|
|
config interface 'wan'
|
|
option proto 'dhcp'
|
|
option device 'wlan-sta'
|
|
|
|
config interface 'lan'
|
|
option proto 'static'
|
|
option ipaddr '$lan_ip'
|
|
option netmask '255.255.255.0'
|
|
option device 'br-lan'
|
|
|
|
# /etc/config/wireless
|
|
|
|
config wifi-iface 'travel_sta'
|
|
option device 'radio1'
|
|
option mode 'sta'
|
|
option network 'wan'
|
|
option ssid '$travel_ssid'
|
|
option encryption 'sae-mixed'
|
|
option key 'hotel-password'
|
|
|
|
config wifi-iface 'travel_ap'
|
|
option device 'radio0'
|
|
option mode 'ap'
|
|
option network 'lan'
|
|
option ssid '$hotspot_ssid'
|
|
option encryption 'sae-mixed'
|
|
option key 'TravelSafe123!'
|
|
|
|
# /etc/config/firewall
|
|
config zone
|
|
option name 'wan'
|
|
option input 'REJECT'
|
|
option output 'ACCEPT'
|
|
option forward 'REJECT'
|
|
option masq '1'"
|
|
;;
|
|
doublenat)
|
|
local lan_ip=$(uci -q get network-modes.doublenat.lan_ip || echo "10.10.0.1")
|
|
local lan_netmask=$(uci -q get network-modes.doublenat.lan_netmask || echo "255.255.255.0")
|
|
local guest=$(uci -q get network-modes.doublenat.guest_network || echo "br-guest")
|
|
config="# Double NAT mode
|
|
# /etc/config/network
|
|
|
|
config interface 'wan'
|
|
option proto 'dhcp'
|
|
option device 'eth1'
|
|
|
|
config interface 'lan'
|
|
option proto 'static'
|
|
option ipaddr '$lan_ip'
|
|
option netmask '$lan_netmask'
|
|
option device 'br-lan'
|
|
|
|
config interface '$guest'
|
|
option proto 'static'
|
|
option ipaddr '172.16.0.1'
|
|
option netmask '255.255.255.0'
|
|
|
|
# /etc/config/firewall
|
|
config zone
|
|
option name 'lan'
|
|
option input 'ACCEPT'
|
|
option output 'ACCEPT'
|
|
option forward 'REJECT'
|
|
list network 'lan'
|
|
|
|
config zone
|
|
option name 'guest'
|
|
option input 'REJECT'
|
|
option output 'ACCEPT'
|
|
option forward 'REJECT'
|
|
list network '$guest'
|
|
|
|
config forwarding
|
|
option src 'lan'
|
|
option dest 'wan'"
|
|
;;
|
|
multiwan)
|
|
local primary=$(uci -q get network-modes.multiwan.wan_primary || echo "eth1")
|
|
local secondary=$(uci -q get network-modes.multiwan.wan_secondary || echo "wwan0")
|
|
local host=$(uci -q get network-modes.multiwan.tracking_host || echo "8.8.8.8")
|
|
config="# Multi-WAN (mwan3) snippet
|
|
# /etc/config/mwan3
|
|
|
|
config interface '$primary'
|
|
option enabled '1'
|
|
option reliability '1'
|
|
option initial_state 'online'
|
|
option track_method 'ping'
|
|
list track_ip '$host'
|
|
|
|
config interface '$secondary'
|
|
option enabled '1'
|
|
option reliability '1'
|
|
option initial_state 'online'
|
|
option timeout '5'
|
|
option track_method 'ping'
|
|
list track_ip '$host'
|
|
|
|
config member 'member_primary'
|
|
option interface '$primary'
|
|
option metric '1'
|
|
option weight '3'
|
|
|
|
config member 'member_secondary'
|
|
option interface '$secondary'
|
|
option metric '2'
|
|
option weight '1'
|
|
|
|
config policy 'balanced'
|
|
list use_member 'member_primary'
|
|
list use_member 'member_secondary'
|
|
|
|
config policy 'failover'
|
|
list use_member 'member_primary'
|
|
list use_member 'member_secondary'
|
|
option last_resort 'unreachable'"
|
|
;;
|
|
vpnrelay)
|
|
local vpn_type=$(uci -q get network-modes.vpnrelay.vpn_type || echo "wireguard")
|
|
local wg_iface=$(uci -q get network-modes.vpnrelay.wg_interface || echo "wg0")
|
|
config="# VPN Relay example
|
|
# /etc/config/network
|
|
|
|
config interface '$wg_iface'
|
|
option proto 'wireguard'
|
|
option private_key '<private>'
|
|
list addresses '10.8.0.2/32'
|
|
|
|
config wireguard_$wg_iface
|
|
option public_key '<server_pub>'
|
|
option endpoint_host 'vpn.example.com'
|
|
option endpoint_port '51820'
|
|
option persistent_keepalive '25'
|
|
list allowed_ips '0.0.0.0/0'
|
|
|
|
# /etc/config/firewall
|
|
config zone
|
|
option name 'vpn'
|
|
option input 'ACCEPT'
|
|
option output 'ACCEPT'
|
|
option forward 'REJECT'
|
|
option masq '1'
|
|
list network '$wg_iface'
|
|
|
|
config forwarding
|
|
option src 'lan'
|
|
option dest 'vpn'
|
|
|
|
config rule
|
|
option name 'Kill-Switch'
|
|
option src 'lan'
|
|
option dest 'wan'
|
|
option proto 'all'
|
|
option target 'REJECT'
|
|
option enabled '1'
|
|
|
|
# Force DNS over VPN
|
|
uci set dhcp.@dnsmasq[0].server='10.8.0.1'
|
|
uci commit dhcp && /etc/init.d/dnsmasq restart"
|
|
;;
|
|
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")
|
|
|
|
add_mode() {
|
|
local id="$1"; shift
|
|
local name="$1"; shift
|
|
local icon="$1"; shift
|
|
local desc="$1"; shift
|
|
json_add_object
|
|
json_add_string "id" "$id"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "$desc"
|
|
json_add_string "icon" "$icon"
|
|
json_add_boolean "current" "$([ "$current_mode" = "$id" ] && echo 1 || echo 0)"
|
|
json_add_array "features"
|
|
for feature in "$@"; do
|
|
json_add_string "" "$feature"
|
|
done
|
|
json_close_array
|
|
json_close_object
|
|
}
|
|
|
|
add_mode "router" "Router" "🌐" "Mode routeur complet avec NAT, DHCP et firewall" \
|
|
"NAT activé" "Serveur DHCP" "Firewall (zones WAN/LAN)" "Proxy optionnel"
|
|
|
|
add_mode "doublenat" "Double NAT" "🔁" "Derrière box opérateur avec réseau isolé" \
|
|
"WAN DHCP client" "Deuxième LAN privé" "Isolation invitée" "UPnP désactivé"
|
|
|
|
add_mode "multiwan" "Multi-WAN" "⚡" "Agrégation et bascule multi-liens WAN" \
|
|
"Double WAN" "Suivi de santé" "Équilibrage de charge" "Failover automatique"
|
|
|
|
add_mode "accesspoint" "Access Point" "📶" "Point d'accès WiFi en mode bridge" \
|
|
"Bridge WAN+LAN" "Pas de DHCP" "Pas de firewall" "Optimisations WiFi"
|
|
|
|
add_mode "relay" "Repeater" "🔄" "Client WiFi + Répéteur AP" \
|
|
"Client WiFi (sta0)" "AP répéteur (ap0)" "WireGuard optionnel" "Optimisations MTU/MSS"
|
|
|
|
add_mode "vpnrelay" "VPN Relay" "🛡️" "Tunnels WireGuard/OpenVPN avec policy routing" \
|
|
"Policy routing" "Kill-switch" "DNS sécurisé" "Split tunnel"
|
|
|
|
add_mode "travel" "Travel Router" "✈️" "Hotspot personnel depuis WiFi/ethernet hôtel" \
|
|
"Client WiFi + scan" "Clone MAC WAN" "Hotspot WPA3" "NAT + DHCP isolé"
|
|
|
|
add_mode "bridge" "Bridge" "🔗" "Bridge Layer 2 pur, DHCP client" \
|
|
"Bridge transparent L2" "Toutes interfaces bridgées" "DHCP client" "Pas de firewall"
|
|
|
|
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|multiwan|doublenat|accesspoint|relay|vpnrelay|bridge|sniffer|travel)
|
|
;;
|
|
*)
|
|
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
|
|
}
|
|
|
|
# Multi-WAN configuration
|
|
get_multiwan_config() {
|
|
json_init
|
|
|
|
json_add_string "mode" "multiwan"
|
|
json_add_string "name" "$(uci -q get network-modes.multiwan.name || echo 'Multi-WAN Controller')"
|
|
json_add_string "description" "$(uci -q get network-modes.multiwan.description || echo 'Dual-WAN bonding with health tracking')"
|
|
|
|
json_add_object "policy"
|
|
json_add_string "profile" "$(uci -q get network-modes.multiwan.policy || echo 'balanced')"
|
|
json_add_boolean "load_balance" "$(uci -q get network-modes.multiwan.load_balance || echo 1)"
|
|
json_add_string "tracking_host" "$(uci -q get network-modes.multiwan.tracking_host || echo '8.8.8.8')"
|
|
json_add_int "tracking_interval" "$(uci -q get network-modes.multiwan.tracking_interval || echo 30)"
|
|
json_add_int "failover_hold" "$(uci -q get network-modes.multiwan.failover_hold || echo 45)"
|
|
json_close_object
|
|
|
|
json_add_object "links"
|
|
json_add_string "primary" "$(uci -q get network-modes.multiwan.wan_primary || echo 'eth1')"
|
|
json_add_string "secondary" "$(uci -q get network-modes.multiwan.wan_secondary || echo 'wwan0')"
|
|
json_add_boolean "mwan3_enabled" "$(uci -q get network-modes.multiwan.use_mwan3 || echo 0)"
|
|
json_close_object
|
|
|
|
json_add_array "wan_candidates"
|
|
for iface in $(ls /sys/class/net 2>/dev/null); do
|
|
[ "$iface" = "lo" ] && continue
|
|
case "$iface" in
|
|
br-*|ifb*|gre*|ip6*|mon*) continue ;;
|
|
esac
|
|
local operstate="$(cat /sys/class/net/$iface/operstate 2>/dev/null)"
|
|
local mac="$(cat /sys/class/net/$iface/address 2>/dev/null)"
|
|
json_add_object
|
|
json_add_string "name" "$iface"
|
|
json_add_boolean "up" "$([ "$operstate" = "up" ] && echo 1 || echo 0)"
|
|
json_add_string "mac" "$mac"
|
|
json_close_object
|
|
done
|
|
json_close_array
|
|
|
|
json_dump
|
|
}
|
|
|
|
# VPN relay configuration
|
|
get_vpnrelay_config() {
|
|
json_init
|
|
|
|
json_add_string "mode" "vpnrelay"
|
|
json_add_string "name" "$(uci -q get network-modes.vpnrelay.name || echo 'VPN Relay')"
|
|
json_add_string "description" "$(uci -q get network-modes.vpnrelay.description || echo 'Route LAN traffic through VPN tunnels with policy routing')"
|
|
|
|
json_add_object "vpn"
|
|
json_add_string "type" "$(uci -q get network-modes.vpnrelay.vpn_type || echo 'wireguard')"
|
|
json_add_string "provider" "$(uci -q get network-modes.vpnrelay.vpn_provider || echo 'Mullvad')"
|
|
json_add_string "wg_interface" "$(uci -q get network-modes.vpnrelay.wg_interface || echo 'wg0')"
|
|
json_add_string "openvpn_profile" "$(uci -q get network-modes.vpnrelay.openvpn_profile || echo '')"
|
|
json_add_string "upstream_interface" "$(uci -q get network-modes.vpnrelay.upstream_interface || echo 'wan')"
|
|
json_close_object
|
|
|
|
json_add_object "policy"
|
|
json_add_boolean "policy_routing" "$(uci -q get network-modes.vpnrelay.policy_routing || echo 1)"
|
|
json_add_boolean "dns_override" "$(uci -q get network-modes.vpnrelay.dns_override || echo 1)"
|
|
json_add_boolean "kill_switch" "$(uci -q get network-modes.vpnrelay.kill_switch || echo 1)"
|
|
json_add_boolean "lan_bypass" "$(uci -q get network-modes.vpnrelay.lan_bypass || echo 0)"
|
|
json_close_object
|
|
|
|
json_add_array "wireguard_interfaces"
|
|
if command -v wg >/dev/null 2>&1; then
|
|
for iface in $(wg show interfaces 2>/dev/null); do
|
|
json_add_string "" "$iface"
|
|
done
|
|
fi
|
|
json_close_array
|
|
|
|
json_add_array "openvpn_profiles"
|
|
for profile in /etc/openvpn/*.ovpn /etc/openvpn/*.conf; do
|
|
[ -f "$profile" ] || continue
|
|
json_add_string "" "$(basename "$profile")"
|
|
done
|
|
json_close_array
|
|
|
|
json_add_array "available_interfaces"
|
|
for iface in $(ls /sys/class/net 2>/dev/null); do
|
|
[ "$iface" = "lo" ] && continue
|
|
json_add_string "" "$iface"
|
|
done
|
|
json_close_array
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Double NAT configuration
|
|
get_doublenat_config() {
|
|
json_init
|
|
|
|
local wan_iface=$(uci -q get network-modes.doublenat.wan_interface || echo "eth1")
|
|
local wan_proto=$(uci -q get network-modes.doublenat.wan_protocol || echo "dhcp")
|
|
local lan_iface=$(uci -q get network-modes.doublenat.lan_interface || echo "br-lan")
|
|
local lan_ip=$(uci -q get network-modes.doublenat.lan_ip || echo "10.10.0.1")
|
|
local lan_netmask=$(uci -q get network-modes.doublenat.lan_netmask || echo "255.255.255.0")
|
|
|
|
json_add_string "mode" "doublenat"
|
|
json_add_string "name" "$(uci -q get network-modes.doublenat.name || echo 'Double NAT')"
|
|
json_add_string "description" "$(uci -q get network-modes.doublenat.description || echo 'Operate behind an ISP router with isolated LAN and guest network')"
|
|
|
|
json_add_object "wan"
|
|
json_add_string "interface" "$wan_iface"
|
|
json_add_string "protocol" "$wan_proto"
|
|
json_close_object
|
|
|
|
json_add_object "lan"
|
|
json_add_string "interface" "$lan_iface"
|
|
json_add_string "ipaddr" "$lan_ip"
|
|
json_add_string "netmask" "$lan_netmask"
|
|
json_close_object
|
|
|
|
json_add_boolean "upnp_enabled" "$(uci -q get network-modes.doublenat.upnp_enabled || echo 0)"
|
|
json_add_string "guest_network" "$(uci -q get network-modes.doublenat.guest_network || echo 'br-guest')"
|
|
json_add_boolean "isolate_guest" "$(uci -q get network-modes.doublenat.isolate_guest || echo 1)"
|
|
json_add_string "dmz_host" "$(uci -q get network-modes.doublenat.dmz_host || echo '')"
|
|
|
|
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":{},"travel_config":{},"doublenat_config":{},"multiwan_config":{},"vpnrelay_config":{},"travel_scan_networks":{},"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
|
|
;;
|
|
travel_config)
|
|
get_travel_config
|
|
;;
|
|
doublenat_config)
|
|
get_doublenat_config
|
|
;;
|
|
multiwan_config)
|
|
get_multiwan_config
|
|
;;
|
|
vpnrelay_config)
|
|
get_vpnrelay_config
|
|
;;
|
|
travel_scan_networks)
|
|
travel_scan_networks
|
|
;;
|
|
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
|