🗽 Sync 2026-08-09 22:41:21

This commit is contained in:
github-actions[bot] committed 2026-08-09 22:41:21 +08:00
1 parent 0daa789835
commit 8d3eaa96b2
66 files changed
+4182 -3339

No files matched your search

+21 -21
View File
@@ -20,39 +20,39 @@ iptables_prepend_rule() {
local chain="$3"
local target="$4"
$iptables "$IPTARGS" -t "$table" -I "$chain" $($iptables "$IPTARGS" -t "$table" --line-numbers -nL "$chain" | \
$iptables "$IPTARGS" -t "$table" -I "$chain" $($iptables "$IPTARGS" -t "$table" --line-numbers -nL "$chain" |
sed -ne '$s/[^0-9].*//p') -j "$target"
}
ADDED=0
add_extzone_rules() {
local ext_zone="$1"
local ext_zone="$1"
[ -z "$ext_zone" ] && return
[ -z "$ext_zone" ] && return
# IPv4 - due to NAT, need to add both to nat and filter table
# need to insert as penultimate rule for input & forward & postrouting since final rule might be a fw3 REJECT
iptables_prepend_rule "$IPTABLES" filter "zone_${ext_zone}_input" MINIUPNPD
iptables_prepend_rule "$IPTABLES" filter "zone_${ext_zone}_forward" MINIUPNPD
$IPTABLES -t nat -A "zone_${ext_zone}_prerouting" -j MINIUPNPD
iptables_prepend_rule "$IPTABLES" nat "zone_${ext_zone}_postrouting" MINIUPNPD-POSTROUTING
# IPv4 - due to NAT, need to add both to nat and filter table
# need to insert as penultimate rule for input & forward & postrouting since final rule might be a fw3 REJECT
iptables_prepend_rule "$IPTABLES" filter "zone_${ext_zone}_input" MINIUPNPD
iptables_prepend_rule "$IPTABLES" filter "zone_${ext_zone}_forward" MINIUPNPD
$IPTABLES -t nat -A "zone_${ext_zone}_prerouting" -j MINIUPNPD
iptables_prepend_rule "$IPTABLES" nat "zone_${ext_zone}_postrouting" MINIUPNPD-POSTROUTING
# IPv6 if available - filter only
[ -x $IP6TABLES ] && {
iptables_prepend_rule "$IP6TABLES" filter "zone_${ext_zone}_input" MINIUPNPD
iptables_prepend_rule "$IP6TABLES" filter "zone_${ext_zone}_forward" MINIUPNPD
}
ADDED=$(($ADDED + 1))
# IPv6 if available - filter only
[ -x $IP6TABLES ] && {
iptables_prepend_rule "$IP6TABLES" filter "zone_${ext_zone}_input" MINIUPNPD
iptables_prepend_rule "$IP6TABLES" filter "zone_${ext_zone}_forward" MINIUPNPD
}
ADDED=$(($ADDED + 1))
}
# By default, user configuration is king.
for ext_iface in $(uci -q get upnpd.config.external_iface); do
add_extzone_rules $(fw3 -q network "$ext_iface")
for ext_iface in $(uci -q get upnpd.settings.external_iface); do
add_extzone_rules $(fw3 -q network "$ext_iface")
done
add_extzone_rules $(uci -q get upnpd.config.external_zone)
add_extzone_rules $(uci -q get upnpd.settings.external_zone)
[ "$ADDED" -ne 0 ] && exit 0
@@ -66,7 +66,7 @@ network_find_wan wan_iface
network_find_wan6 wan6_iface
for ext_iface in $wan_iface $wan6_iface; do
# fw3 -q network fails on sub-interfaces => map to device first
network_get_device ext_device $ext_iface
add_extzone_rules $(fw3 -q device "$ext_device")
# fw3 -q network fails on sub-interfaces => map to device first
network_get_device ext_device $ext_iface
add_extzone_rules $(fw3 -q device "$ext_device")
done
@@ -1,8 +0,0 @@
#!/bin/sh
if uci -q get upnpd.config.force_forwarding; then
uci -q rename upnpd.config.force_forwarding=ext_allow_private_ipv4
uci -q commit upnpd
fi
exit 0
+16 -25
View File
@@ -1,45 +1,36 @@
#!/bin/sh
/etc/init.d/miniupnpd enabled || exit 0
# If miniupnpd is not running:
# - check on _any_ event (event updates may contribute to network_find_wan*)
# If miniupnpd _is_ running:
# - check only on ifup (otherwise lease updates etc would cause
# miniupnpd state loss)
# If daemon is:
# - not running: check on any event (event updates may contribute to network_find_wan*)
# - running: check only on ifup (otherwise lease updates etc. would cause daemon state loss)
[ "$ACTION" != "ifup" ] && /etc/init.d/miniupnpd running && exit 0
tmpconf="/var/etc/miniupnpd.conf"
external_iface=$(uci -q get upnpd.config.external_iface)
external_iface6=$(uci -q get upnpd.config.external_iface6)
external_zone=$(uci -q get upnpd.config.external_zone)
external_iface=$(uci -q get upnpd.settings.external_iface)
external_iface6=$(uci -q get upnpd.settings.external_iface6)
external_zone=$(uci -q get upnpd.settings.external_zone)
[ -x "$(command -v nft)" ] && FW="fw4" || FW="fw3"
. /lib/functions/network.sh
if [ -n "$external_iface" ] ; then
if [ -n "$external_iface" ]; then
network_get_device ifname "$external_iface"
elif [ -n "$external_zone" ]; then
ifname=$($FW -q zone "$external_zone" 2>/dev/null | head -1)
else
if [ -n "$external_zone" ] ; then
ifname=$($FW -q zone "$external_zone" 2>/dev/null | head -1)
else
network_find_wan external_iface && \
network_get_device ifname "$external_iface"
fi
network_find_wan external_iface && network_get_device ifname "$external_iface"
fi
if [ -n "$external_iface6" ] ; then
if [ -n "$external_iface6" ]; then
network_get_device ifname6 "$external_iface6"
elif [ -n "$external_zone" ]; then
ifname6=$($FW -q zone "$external_zone" 2>/dev/null | head -1)
else
if [ -n "$external_zone" ] ; then
ifname6=$($FW -q zone "$external_zone" 2>/dev/null | head -1)
else
network_find_wan6 external_iface6 && \
network_get_device ifname6 "$external_iface6"
fi
network_find_wan6 external_iface6 && network_get_device ifname6 "$external_iface6"
fi
[ "$DEVICE" != "$ifname" ] && [ "$DEVICE" != "$ifname6" ] && exit 0
grep -qs "^ext_ifname=$ifname" "$tmpconf" && grep -qs "^ext_ifname6=$ifname6" "$tmpconf" && exit 0
grep -qs "^ext_ifname=$ifname" "$tmpconf" && grep -qs "^ext_ifname6=${ifname6:-$ifname}" "$tmpconf" && exit 0
/etc/init.d/miniupnpd restart
+233 -183
View File
@@ -7,112 +7,15 @@ USE_PROCD=1
PROG=/usr/sbin/miniupnpd
[ -x "$(command -v nft)" ] && FW="fw4" || FW="fw3"
upnpd_get_port_range() {
local var="$1"; shift
local val
config_get val "$@"
case "$val" in
[0-9]*[:-][0-9]*)
export -n -- "${var}_start=${val%%[:-]*}"
export -n -- "${var}_end=${val##*[:-]}"
;;
[0-9]*)
export -n -- "${var}_start=$val"
export -n -- "${var}_end="
;;
esac
}
conf_rule_add() {
local cfg="$1"
local action int_addr
local ext_start ext_end int_start int_end comment
config_get action "$cfg" action "deny" # allow or deny
upnpd_get_port_range "ext" "$cfg" ext_ports "0-65535" # external ports: x, x-y, x:y
config_get int_addr "$cfg" int_addr "0.0.0.0/0" # ip or network and subnet mask (internal)
upnpd_get_port_range "int" "$cfg" int_ports "0-65535" # internal ports: x, x-y, x:y or range
config_get comment "$cfg" comment "ACL" # comment
# Make a single IP IP/32 so that miniupnpd.conf can use it.
[ "${int_addr%/*}" = "$int_addr" ] && int_addr="$int_addr/32"
echo "$action $ext_start${ext_end:+-}$ext_end $int_addr $int_start${int_end:+-}$int_end #$comment"
}
upnpd_write_bool() {
local opt="$1"
local def="${2:-0}"
local alt="${3:-$opt}"
local val
config_get_bool val config "$opt" "$def"
if [ "$val" -eq 0 ]; then
echo "$alt=no"
else
echo "$alt=yes"
fi
}
upnpd() {
start_service() {
config_load "upnpd"
local external_iface external_iface6 external_zone external_ip internal_iface
local upload download log_output port config_file serial_number model_number
local use_stun stun_host stun_port uuid notify_interval presentation_url
local upnp_lease_file upnp_lease_file6 ipv6_disable ext_allow_private_ipv4
local enabled
config_get_bool enabled config enabled 1
[ "$enabled" -eq 0 ] && return 1
config_get external_iface config external_iface
config_get external_iface6 config external_iface6
config_get external_zone config external_zone
config_get external_ip config external_ip
config_get internal_iface config internal_iface
config_get port config port 5000
config_get upload config upload
config_get download config download
config_get_bool log_output config log_output 0
config_get config_file config config_file
config_get serial_number config serial_number
config_get model_number config model_number
config_get uuid config uuid
config_get use_stun config use_stun 0
config_get stun_host config stun_host
config_get stun_port config stun_port
config_get notify_interval config notify_interval
config_get presentation_url config presentation_url
config_get upnp_lease_file config upnp_lease_file
config_get upnp_lease_file6 config upnp_lease_file6
config_get ipv6_disable config ipv6_disable 0
config_get ext_allow_private_ipv4 config ext_allow_private_ipv4 0
local conf ifname ifname6
. /lib/functions/network.sh
if [ -n "$external_iface" ] ; then
network_get_device ifname "$external_iface"
else
if [ -n "$external_zone" ] ; then
ifname=$($FW -q zone "$external_zone" 2>/dev/null | head -1)
else
network_find_wan external_iface && \
network_get_device ifname "$external_iface"
fi
fi
if [ -n "$external_iface6" ] ; then
network_get_device ifname6 "$external_iface6"
else
if [ -n "$external_zone" ] ; then
ifname6=$($FW -q zone "$external_zone" 2>/dev/null | head -1)
else
network_find_wan6 external_iface6 && \
network_get_device ifname6 "$external_iface6"
fi
local enabled config_file log_output conf
config_get enabled settings enabled 0
config_get config_file settings config_file
config_get log_output settings log_output
if [ "$enabled" != "1" ]; then
log "Service disabled, enabled UCI option not set"
return 1
fi
if [ -n "$config_file" ]; then
@@ -121,107 +24,254 @@ upnpd() {
local tmpconf="/var/etc/miniupnpd.conf"
conf="$tmpconf"
mkdir -p /var/etc
{
echo "ext_ifname=$ifname"
echo "ext_ifname6=$ifname6"
[ -n "$external_ip" ] && echo "ext_ip=$external_ip"
local iface
for iface in ${internal_iface:-lan}; do
local device
network_get_device device "$iface" && echo "listening_ip=$device"
done
config_load "upnpd"
upnpd_write_bool enable_natpmp 1 enable_pcp_pmp
upnpd_write_bool enable_upnp 1
upnpd_write_bool secure_mode 1
upnpd_write_bool system_uptime 1
upnpd_write_bool igdv1 0 force_igd_desc_v1
upnpd_write_bool use_stun 0 ext_perform_stun
upnpd_write_bool ipv6_disable $ipv6_disable
upnpd_write_bool ext_allow_private_ipv4 $ext_allow_private_ipv4
[ "$use_stun" -eq 0 ] || {
[ -n "$stun_host" ] && echo "ext_stun_host=$stun_host"
[ -n "$stun_port" ] && echo "ext_stun_port=$stun_port"
}
[ -n "$upload" ] && [ -n "$download" ] && {
echo "bitrate_down=$((download * 1024 * 8))"
echo "bitrate_up=$((upload * 1024 * 8))"
}
[ -n "$upnp_lease_file" ] && touch "$upnp_lease_file" && echo "lease_file=$upnp_lease_file"
[ -n "$upnp_lease_file6" ] && touch "$upnp_lease_file6" && echo "lease_file6=$upnp_lease_file6"
[ -n "$presentation_url" ] && echo "presentation_url=$presentation_url"
[ -n "$notify_interval" ] && echo "notify_interval=$notify_interval"
[ -n "$serial_number" ] && echo "serial=$serial_number"
[ -n "$model_number" ] && echo "model_number=$model_number"
[ -n "$port" ] && echo "port=$port"
[ -z "$uuid" ] && {
uuid="$(cat /proc/sys/kernel/random/uuid)"
uci set upnpd.config.uuid="$uuid"
uci commit upnpd
}
[ "$uuid" = "nocli" ] || echo "uuid=$uuid"
config_foreach conf_rule_add perm_rule
if [ "$FW" = "fw4" ]; then
#When using nftables configure miniupnpd to use its own table and chains
echo "upnp_table_name=fw4"
echo "upnp_nat_table_name=fw4"
echo "upnp_forward_chain=upnp_forward"
echo "upnp_nat_chain=upnp_prerouting"
echo "upnp_nat_postrouting_chain=upnp_postrouting"
fi
} > "$tmpconf"
upnpd_generate_config "$tmpconf" || return 1
fi
if [ -n "$ifname" ]; then
# start firewall
if [ "$FW" = "fw4" ]; then
nft -s -t -n list chain inet fw4 upnp_forward >/dev/null 2>&1 || fw4 reload
else
iptables -L MINIUPNPD >/dev/null 2>&1 || fw3 reload
fi
if [ "$FW" = "fw4" ]; then
nft -s -t -n list chain inet fw4 upnp_forward >/dev/null 2>&1 || fw4 reload
else
logger -t "upnp daemon" "external interface not found, not starting"
iptables -L MINIUPNPD >/dev/null 2>&1 || fw3 reload
fi
procd_open_instance
procd_set_param file "$conf" "/etc/config/firewall"
procd_set_param file "$conf"
procd_set_param command "$PROG"
procd_append_param command -f "$conf"
[ "$log_output" = "1" ] && procd_append_param command -d
[ "$log_output" = "info" ] && procd_append_param command -v
[ "$log_output" = "debug" ] && procd_append_param command -v -v
procd_close_instance
}
stop_service() {
if [ "$FW" = "fw3" ]; then
iptables -t nat -F MINIUPNPD 2>/dev/null
iptables -t nat -F MINIUPNPD-POSTROUTING 2>/dev/null
iptables -t filter -F MINIUPNPD 2>/dev/null
[ -x /usr/sbin/ip6tables ] && ip6tables -t filter -F MINIUPNPD 2>/dev/null
iptables -t nat -F MINIUPNPD 2>/dev/null
iptables -t nat -F MINIUPNPD-POSTROUTING 2>/dev/null
else
nft flush chain inet fw4 upnp_forward 2>/dev/null
nft flush chain inet fw4 upnp_prerouting 2>/dev/null
nft flush chain inet fw4 upnp_postrouting 2>/dev/null
#nft flush chain inet fw4 upnp_postrouting 2>/dev/null
fi
}
start_service() {
config_load "upnpd"
config_foreach upnpd "upnpd"
service_triggers() {
procd_add_reload_trigger "upnpd" "firewall"
}
service_triggers() {
procd_add_reload_trigger "upnpd"
upnpd_generate_config() {
# Daemon
local enable_protocols allow_cgnat stun_host allow_third_party_mapping system_uptime lease_file
config_get enable_protocols settings enable_protocols all
config_get allow_cgnat settings allow_cgnat 0
config_get stun_host settings stun_host stun.nextcloud.com
config_get allow_third_party_mapping settings allow_third_party_mapping 0
config_get system_uptime settings system_uptime 1
config_get lease_file settings lease_file /var/run/miniupnpd.leases
# Access control
local access_defaults accept_ports reject_ports check_acl ipv6_disable
config_get access_defaults settings access_defaults none
config_get accept_ports settings accept_ports
config_get reject_ports settings reject_ports "21 23 135 137-139 445 3389"
config_get check_acl settings check_acl 1
config_get ipv6_disable settings ipv6_disable 0
# UPnP IGD
local upnp_igd_compat download_kbps upload_kbps friendly_name model_number serial_number presentation_url uuid http_port notify_interval
config_get upnp_igd_compat settings upnp_igd_compat igdv1
config_get download_kbps settings download_kbps
config_get upload_kbps settings upload_kbps
config_get friendly_name settings friendly_name "OpenWrt UPnP IGD & PCP"
config_get model_number settings model_number
config_get serial_number settings serial_number
config_get presentation_url settings presentation_url
config_get uuid settings uuid
config_get http_port settings http_port 5000
config_get notify_interval settings notify_interval
# Network interfaces
local external_iface external_iface6 external_zone external_ip internal_iface
config_get external_iface settings external_iface
config_get external_iface6 settings external_iface6
config_get external_zone settings external_zone
config_get external_ip settings external_ip
config_get internal_iface settings internal_iface lan
local ifname ifname6
. /lib/functions/network.sh
if [ -n "$external_iface" ]; then
network_get_device ifname "$external_iface"
elif [ -n "$external_zone" ]; then
ifname=$($FW -q zone "$external_zone" 2>/dev/null | head -1)
else
network_find_wan external_iface && network_get_device ifname "$external_iface"
fi
if [ -n "$external_iface6" ]; then
network_get_device ifname6 "$external_iface6"
elif [ -n "$external_zone" ]; then
ifname6=$($FW -q zone "$external_zone" 2>/dev/null | head -1)
else
network_find_wan6 external_iface6 && network_get_device ifname6 "$external_iface6"
fi
if [ "$ifname" = "" ]; then
log "No external network interface found, not starting" daemon.err
return 1
fi
# Workaround for daemon bug with UPnP IGDv2 if IPv6 is not ready at start
if [ "$ipv6_disable" = "0" ] && [ "$(uci -q get firewall.@defaults[0].disable_ipv6)" != "1" ] &&
[ "$(uci -q get network.wan6.disabled)" != "1" ]; then
local pass=0
while ! ip -6 addr show dev "${ifname6:-$ifname}" | grep -q "inet6 [23]"; do
log "IPv6 not ready yet; delay start"
sleep 5
pass=$((pass + 1))
[ "$pass" = "4" ] && log "IPv6 GUA not yet available, UPnP IGD mapping not possible" && break
done
fi
# Only perform an STUN CGNAT test if necessary, with a private/CGNAT-reserved external IPv4
local extipv4 extipv4private
extipv4="$(ip -4 addr show dev "$ifname" | grep inet | head -1 | sed -E "s/.*inet ([0-9.]+).*/\1/")"
case "$extipv4" in
10.* | 100.6[4-9].* | 100.[7-9][0-9].* | 100.1[0-1][0-9].* | 100.12[0-7].* | 172.1[6-9].* | \
172.2[0-9].* | 172.3[0-1].* | 192.0.0.[1-6] | 192.168.* | 198.1[89].*) extipv4private=1 ;;
esac
[ "$extipv4private" = "1" ] && log "Private/CGNAT-reserved external IPv4 detected ($extipv4)"
{
echo "# Daemon"
[ "$enable_protocols" = "all" ] && echo "enable_upnp=yes" && echo "enable_pcp_pmp=yes"
[ "$enable_protocols" = "upnp-igd" ] && echo "enable_upnp=yes" && echo "enable_pcp_pmp=no"
[ "$enable_protocols" = "pcp+nat-pmp" ] && echo "enable_upnp=no" && echo "enable_pcp_pmp=yes"
if [ "$extipv4private" = "1" ] && [ "$allow_cgnat" != "0" ]; then
[ "$allow_cgnat" = "1" ] && echo "ext_perform_stun=yes"
[ "$allow_cgnat" = "allow-filtered" ] && echo "ext_perform_stun=allow-filtered"
# Avoid next option, as no STUN public IPv4 detection, required by clients (PCP/NAT-PMP among others)
[ "$allow_cgnat" = "report-private-ipv4" ] && echo "ext_allow_private_ipv4=yes"
echo "ext_stun_host=${stun_host%%:*}"
[ "${stun_host%%:*}" != "${stun_host##*:}" ] && echo "ext_stun_port=${stun_host##*:}"
fi
[ "$allow_third_party_mapping" = "0" ] && echo "secure_mode=yes" && echo "pcp_allow_thirdparty=no"
[ "$allow_third_party_mapping" = "1" ] && echo "secure_mode=no" && echo "pcp_allow_thirdparty=yes"
[ "$allow_third_party_mapping" = "upnp-igd" ] && echo "secure_mode=no" && echo "pcp_allow_thirdparty=no"
[ "$allow_third_party_mapping" = "pcp" ] && echo "secure_mode=yes" && echo "pcp_allow_thirdparty=yes"
[ "$system_uptime" = "0" ] && echo "system_uptime=no" || echo "system_uptime=yes"
touch "$lease_file" && echo "lease_file=$lease_file"
[ "$ipv6_disable" = "0" ] && touch "${lease_file}-ipv6" && echo "lease_file6=${lease_file}-ipv6"
if [ "$enable_protocols" = "upnp-igd" ] || [ "$enable_protocols" = "all" ]; then
echo "# UPnP IGD"
[ "$upnp_igd_compat" = "igdv1" ] && echo "force_igd_desc_v1=yes" || echo "force_igd_desc_v1=no"
[ -n "$download_kbps" ] && echo "bitrate_down=$((download_kbps * 1000))"
[ -n "$upload_kbps" ] && echo "bitrate_up=$((upload_kbps * 1000))"
[ -n "$friendly_name" ] && echo "friendly_name=$(xml_encode "$friendly_name")"
[ -n "$model_number" ] && echo "model_number=$(xml_encode "$model_number")" || echo "model_number="
[ -n "$serial_number" ] && echo "serial=$(xml_encode "$serial_number")" || echo "serial="
[ -n "$presentation_url" ] && echo "presentation_url=$presentation_url"
[ -z "$uuid" ] && {
log "Generate UPnP IGD UUID"
uuid="$(cat /proc/sys/kernel/random/uuid)"
uci set upnpd.settings.uuid="$uuid"
uci commit upnpd
}
[ "$uuid" != "nocli" ] && echo "uuid=$uuid" || log "uuid=nocli deprecated, set to 00000000-0000-0000-0000-000000000000 instead"
echo "http_port=$http_port"
[ -n "$notify_interval" ] && echo "notify_interval=$notify_interval"
fi
if [ "$FW" = "fw4" ]; then
echo "# Firewall backend"
echo "upnp_table_name=fw4"
echo "upnp_nat_table_name=fw4"
echo "upnp_forward_chain=upnp_forward"
echo "upnp_nat_chain=upnp_prerouting"
#echo "upnp_nat_postrouting_chain=upnp_postrouting"
fi
echo "# External network interface"
echo "ext_ifname=$ifname"
echo "ext_ifname6=${ifname6:-$ifname}"
[ -n "$external_ip" ] && echo "ext_ip=$external_ip"
echo "# Enable internal networks / access control"
[ "$ipv6_disable" = "0" ] && echo "ipv6_disable=no" || echo "ipv6_disable=yes"
local rejectport
for rejectport in $reject_ports; do
is_port_or_range "$rejectport" allowport0 && echo "deny $rejectport 0.0.0.0/0 $rejectport # Reject port $rejectport" ||
log "Invalid port or port range ($rejectport) in reject_ports ignored" daemon.warn
done
[ "$check_acl" = "1" ] && echo "# Access control list" && config_foreach upnpd_add_acl_entry acl_entry
local iface
for iface in $internal_iface; do
local device subnet accessdefaultsports acceptport
network_get_device device "$iface"
network_get_subnet subnet "$iface"
[ "$subnet" = "" ] && log "Cannot get IPv4 subnet for network $iface, network ignored" daemon.warn && continue
echo "# Enable internal network $iface ($device) with access defaults $access_defaults and check ACL $check_acl"
echo "listening_ip=$device"
if [ "$access_defaults" = "accept-high-ports" ]; then
accessdefaultsports="1024-65535"
elif [ "$access_defaults" = "accept-web+high-ports" ]; then
accessdefaultsports="80 443 1024-65535"
elif [ "$access_defaults" = "accept-web-ports" ]; then
accessdefaultsports="80 443"
elif [ "$access_defaults" = "accept-all-ports" ]; then
accessdefaultsports="1-65535"
elif [ "$access_defaults" != "none" ]; then
log "Invalid access_defaults ($access_defaults) ignored" daemon.warn
fi
for acceptport in $accessdefaultsports $accept_ports; do
is_port_or_range "$acceptport" && echo "allow $acceptport $subnet $acceptport # Accept port $acceptport on $iface" ||
log "Invalid port or port range ($acceptport) in accept_ports ignored" daemon.warn
done
done
echo "deny 1-65535 0.0.0.0/0 1-65535 # Reject ACL by default"
} >"$1"
}
log() {
logger -s -p "${2:-daemon.notice}" -t "miniupnpd-init" "$1" || echo "miniupnpd-init: $1" >&2
}
xml_encode() {
# Encode required XML entities of text UPnP IGD config options until the daemon does so
echo "$1" | sed "s/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g"
}
is_port_or_range() {
[ "$1" = "0" ] && [ "$2" != "allowport0" ] && return 1
[ "$1" -ge "1" ] 2>/dev/null && [ "$1" -le "65535" ] 2>/dev/null && return 0
[ "$2" = "allowport0" ] && local minport=0 || local minport=1
[ "${1%%-*}" -ge "$minport" ] 2>/dev/null && [ "${1%%-*}" -le "65535" ] 2>/dev/null &&
[ "${1##*-}" -ge "$minport" ] 2>/dev/null && [ "${1##*-}" -le "65535" ] 2>/dev/null &&
[ "${1##*-}" -ge "${1%%-*}" ] 2>/dev/null && return 0 || return 1
}
upnpd_add_acl_entry() {
local cfg="$1"
local comment int_addr int_port ext_port descr_filter action
config_get comment "$cfg" comment "unspecified" # comment
config_get int_addr "$cfg" int_addr "0.0.0.0/0" # IPv4 or network and subnet mask (internal)
config_get int_port "$cfg" int_port "1-65535" # internal port/range: x or x-y
config_get ext_port "$cfg" ext_port "1-65535" # external port/range: x or x-y
config_get descr_filter "$cfg" descr_filter # description regex filter (must be built in)
config_get action "$cfg" action # accept/reject/disabled
! is_port_or_range "$int_port" allowport0 &&
log "ACL entry: Invalid port or port range ($int_port) in int_port ignored" daemon.warn && int_port=1-65535
! is_port_or_range "$ext_port" allowport0 &&
log "ACL entry: Invalid port or port range ($ext_port) in ext_port ignored" daemon.warn && ext_port=1-65535
[ "$descr_filter" != "" ] && descr_filter=" \"$descr_filter\""
if [ "$action" = "accept" ]; then
action=allow
elif [ "$action" = "reject" ]; then
action=deny
elif [ "$action" != "disabled" ]; then
log "ACL entry: Entry with invalid action ($action) ignored" daemon.warn
action=disabled
fi
[ "$action" = "disabled" ] && return 0
echo "$action $ext_port $int_addr $int_port${descr_filter} # $comment"
}
reload_service() {
@@ -1 +0,0 @@
jump upnp_postrouting comment "Hook into miniupnpd postrouting chain";
@@ -1,3 +1,2 @@
chain upnp_forward {}
chain upnp_prerouting {}
chain upnp_postrouting {}
@@ -0,0 +1,246 @@
#!/bin/sh
log() {
logger -s -p "${2:-daemon.notice}" -t "upnpd" "$1" || echo "upnpd: $1" >&2
}
# Skip migration with existing settings (v2.0) or with no config (v1.0) UCI section
# Enables the creation of a merged v1.0/v2.0 config file
{ uci -q get upnpd.settings >/dev/null || ! uci -q get upnpd.config >/dev/null; } && exit 0
log "Check UCI options in /etc/config/upnpd to be migrated to v2.0"
cp /etc/config/upnpd /tmp
# Set missing enabled option to fix previously different defaults in LuCI/config (0) and init UCI (1)
if ! uci -q get upnpd.config.enabled >/dev/null; then
uci -q set upnpd.config.enabled="1"
fi
# Migrate boolean options to only use 0/1 for LuCI support
for option in enabled ipv6_disable system_uptime; do
if uci -q get upnpd.config.$option >/dev/null; then
uci get upnpd.config.$option | grep -q -E -x "0|off|false|no|disabled" && uci set upnpd.config.$option="0"
uci get upnpd.config.$option | grep -q -E -x "1|on|true|yes|enabled" && uci set upnpd.config.$option="1"
fi
done
# Migrate enable_upnp/enable_natpmp -> enable_protocols: Combined option
if uci -q get upnpd.config.enable_upnp >/dev/null || uci -q get upnpd.config.enable_natpmp >/dev/null; then
log "enable_upnp/enable_natpmp -> enable_protocols: Combined option"
if ! uci -q get upnpd.config.enable_upnp | grep -q -E -x "0|off|false|no|disabled"; then
uci -q get upnpd.config.enable_natpmp | grep -q -E -x "0|off|false|no|disabled" &&
uci set upnpd.config.enable_protocols="upnp-igd" ||
uci set upnpd.config.enable_protocols="all"
elif ! uci -q get upnpd.config.enable_natpmp | grep -q -E -x "0|off|false|no|disabled"; then
uci set upnpd.config.enable_protocols="pcp+nat-pmp"
else
uci set upnpd.config.enable_protocols="all"
uci set upnpd.config.enabled="0"
fi
uci -q delete upnpd.config.enable_upnp
uci -q delete upnpd.config.enable_natpmp
fi
# Rename use_stun -> allow_cgnat
if uci -q get upnpd.config.use_stun >/dev/null; then
log "use_stun -> allow_cgnat"
uci rename upnpd.config.use_stun="allow_cgnat"
fi
# Migrate force_forwarding=1 (in X-Wrt since 2021) to new similar option allow_cgnat=allow-filtered for cross-upgrades
if uci -q get upnpd.config.force_forwarding >/dev/null; then
log "force_forwarding=1 -> allow_cgnat=allow-filtered: New option"
uci get upnpd.config.force_forwarding | grep -q -E -x "1|on|true|yes|enabled" &&
uci set upnpd.config.allow_cgnat="allow-filtered"
uci delete upnpd.config.force_forwarding
fi
# Remove known unsupported (not CGNAT filtering test capable) STUN servers and include stun_port in stun_host
if uci -q get upnpd.config.stun_host | grep -q -E "stun[0-9]?.l.google.com|stun.cloudflare.com"; then
log "stun_host: Unsupported STUN server ($(uci -q get upnpd.config.stun_host)) set, remove to set default"
uci delete upnpd.config.stun_host
# Workaround for false negative filtered STUN CGNAT test results for unsupported servers before daemon 2.3.10
uci set upnpd.config.allow_cgnat="allow-filtered"
uci -q delete upnpd.config.stun_port
elif uci -q get upnpd.config.stun_port >/dev/null; then
uci -q get upnpd.config.stun_host >/dev/null && [ "$(uci -q get upnpd.config.stun_port)" != "3478" ] &&
log "stun_port: Include stun_port in stun_host, and remove option" &&
uci set upnpd.config.stun_host="$(uci -q get upnpd.config.stun_host | cut -d ":" -f 1):$(uci -q get upnpd.config.stun_port)"
uci delete upnpd.config.stun_port
fi
# Migrate secure_mode=1/0 -> allow_third_party_mapping=0/upnp-igd: Invert and extend to PCP
if uci -q get upnpd.config.secure_mode >/dev/null; then
log "secure_mode=1/0 -> allow_third_party_mapping=0/upnp-igd: Invert and extend to PCP"
uci get upnpd.config.secure_mode | grep -q -E -x "0|off|false|no|disabled" &&
uci set upnpd.config.allow_third_party_mapping="upnp-igd" ||
uci set upnpd.config.allow_third_party_mapping="0"
uci delete upnpd.config.secure_mode
fi
# Migrate log_output=0/1 -> log_output=default/debug: Now info also allowed
if uci -q get upnpd.config.log_output >/dev/null; then
log "log_output=0/1 -> log_output=default/debug: Now info also allowed"
uci get upnpd.config.log_output | grep -q -E -x "1|on|true|yes|enabled" &&
uci set upnpd.config.log_output="debug"
uci get upnpd.config.log_output | grep -q -E -x "0|off|false|no|disabled" &&
uci set upnpd.config.log_output="default"
fi
# Rename upnp_lease_file -> lease_file: To original daemon name, and remove if UCI default set
if uci -q get upnpd.config.upnp_lease_file >/dev/null; then
if [ "$(uci -q get upnpd.config.upnp_lease_file)" = "/var/run/miniupnpd.leases" ]; then
log "upnp_lease_file -> lease_file: Remove option as UCI default set"
uci delete upnpd.config.upnp_lease_file
else
log "upnp_lease_file -> lease_file"
uci rename upnpd.config.upnp_lease_file="lease_file"
fi
fi
if uci -q get upnpd.config.upnp_lease_file6 >/dev/null; then
uci delete upnpd.config.upnp_lease_file6
fi
# Migrate igdv1=1/0 -> upnp_igd_compat=igdv1/igdv2: Extensible/clearer
if uci -q get upnpd.config.igdv1 >/dev/null; then
log "igdv1=1/0 -> upnp_igd_compat=igdv1/igdv2"
uci get upnpd.config.igdv1 | grep -q -E -x "1|on|true|yes|enabled" &&
uci set upnpd.config.upnp_igd_compat="igdv1" ||
uci set upnpd.config.upnp_igd_compat="igdv2"
uci delete upnpd.config.igdv1
fi
# Migrate download/upload -> download_kbps/upload_kbps: Convert to kbit/s
if uci -q get upnpd.config.download >/dev/null; then
download="$(uci -q get upnpd.config.download)"
if [ "$download" != "1024" ] && [ "$download" -ge "1" ] 2>/dev/null; then
log "download -> download_kbps: Convert to kbit/s"
download_kbps="$((download * 8 * 1000 / 1024))"
uci set upnpd.config.download_kbps="$download_kbps"
fi
uci delete upnpd.config.download
fi
if uci -q get upnpd.config.upload >/dev/null; then
upload="$(uci -q get upnpd.config.upload)"
if [ "$upload" != "512" ] && [ "$upload" -ge "1" ] 2>/dev/null; then
log "upload -> upload_kbps: Convert to kbit/s"
upload_kbps="$((upload * 8 * 1000 / 1024))"
uci set upnpd.config.upload_kbps="$upload_kbps"
fi
uci delete upnpd.config.upload
fi
# Rename port -> http_port: Remove if UCI default set
if uci -q get upnpd.config.port >/dev/null; then
if [ "$(uci -q get upnpd.config.port)" = "5000" ]; then
log "port -> http_port: Remove option as UCI default set"
uci delete upnpd.config.port
else
log "port -> http_port"
uci rename upnpd.config.port="http_port"
fi
fi
# Migrate notify_interval <= 900 s: Remove to set minimum of 900 (default)
if [ "$(uci -q get upnpd.config.notify_interval)" -le "900" ] 2>/dev/null; then
log "notify_interval <= 900 s: Remove to set minimum of 900 (default)"
uci delete upnpd.config.notify_interval
fi
# Migrate ACL to new section, note that an empty ACL is now rejected alone
# a) Empty/unmodified ACL: Set access defaults, add/update template entries
# b) Modified ACL:
# - Add missing entry action to avoid adding inverted actions when changing via LuCI
# - Update entry action allow/deny -> accept/reject
# - Update entry port options to only use the LuCI (and daemon) supported hyphen (-) as port range separator
# - Not using access defaults, add template entries
if uci -q get upnpd.@acl_entry[0] >/dev/null; then
log "Error migrating ACL, as the new UCI section already exists" daemon.err
elif ! uci -q get upnpd.@perm_rule[0] >/dev/null; then
log "Empty ACL: Set access defaults, add templates, empty ACL rejected alone"
access_defaults=accept-all-ports
check_acl=0
addtemplateentries=1
elif ! uci -q get upnpd.@perm_rule[2] >/dev/null &&
[ "$(uci -q get upnpd.@perm_rule[0].int_addr)" = "0.0.0.0/0" ] &&
[ "$(uci -q get upnpd.@perm_rule[0].int_ports)" = "1024-65535" ] &&
[ "$(uci -q get upnpd.@perm_rule[0].ext_ports)" = "1024-65535" ] &&
[ "$(uci -q get upnpd.@perm_rule[0].action)" = "allow" ] &&
[ "$(uci -q get upnpd.@perm_rule[1].int_addr)" = "0.0.0.0/0" ] &&
[ "$(uci -q get upnpd.@perm_rule[1].int_ports)" = "0-65535" ] &&
[ "$(uci -q get upnpd.@perm_rule[1].ext_ports)" = "0-65535" ] &&
[ "$(uci -q get upnpd.@perm_rule[1].action)" = "deny" ]; then
log "Unmodified ACL: Set access defaults, empty ACL rejected alone"
access_defaults=accept-high-ports
check_acl=0
addtemplateentries=1
uci delete upnpd.@perm_rule[-1]
uci delete upnpd.@perm_rule[-1]
else
log "Modified ACL: Migrate entries/section, empty ACL rejected alone"
addtemplateentries=1
entrynr=0
while uci -q get upnpd.@perm_rule[$entrynr] >/dev/null; do
comment="$(uci -q get upnpd.@perm_rule[$entrynr].comment)"
int_addr="$(uci -q get upnpd.@perm_rule[$entrynr].int_addr)"
int_port="$(uci -q get upnpd.@perm_rule[$entrynr].int_ports)"
ext_port="$(uci -q get upnpd.@perm_rule[$entrynr].ext_ports)"
action="$(uci -q get upnpd.@perm_rule[$entrynr].action)"
echo "$int_port" | grep -q ":" &&
log "ACL entry: Update int_port to use hyphen (-) as port range separator" &&
int_port="$(echo "$int_port" | tr ":" "-")"
echo "$ext_port" | grep -q ":" &&
log "ACL entry: Update ext_port to use hyphen (-) as port range separator" &&
ext_port="$(echo "$ext_port" | tr ":" "-")"
[ "$action" = "" ] && log "ACL entry: Add missing action option" && action=reject
[ "$action" = "allow" ] && action=accept
[ "$action" = "deny" ] && action=reject
uci batch >/dev/null <<-EOF
add upnpd acl_entry
set upnpd.@acl_entry[-1].comment="${comment:-unspecified}"
set upnpd.@acl_entry[-1].int_addr="${int_addr:-0.0.0.0/0}"
set upnpd.@acl_entry[-1].int_port="$int_port"
set upnpd.@acl_entry[-1].ext_port="$ext_port"
set upnpd.@acl_entry[-1].action="$action"
EOF
entrynr=$((entrynr + 1))
done
if [ "${int_addr:-0.0.0.0/0}" = "0.0.0.0/0" ] && [ "${int_port:-0-65535}" = "0-65535" ] &&
[ "${ext_port:-0-65535}" = "0-65535" ] && [ "$action" = "reject" ]; then
log "ACL entry: Remove no longer useful reject by default entry"
uci delete upnpd.@acl_entry[-1]
fi
while uci -q delete upnpd.@perm_rule[-1]; do :; done
fi
[ "$access_defaults" != "" ] && uci set upnpd.config.access_defaults="$access_defaults"
[ "$check_acl" = "0" ] && uci set upnpd.config.check_acl="0"
if [ "$addtemplateentries" = "1" ]; then
uci batch >/dev/null <<-EOF
add upnpd acl_entry
add upnpd acl_entry
set upnpd.@acl_entry[-2].comment="High ports"
set upnpd.@acl_entry[-2].int_addr="0.0.0.0/0"
set upnpd.@acl_entry[-2].int_port="1024-65535"
set upnpd.@acl_entry[-2].ext_port="1024-65535"
set upnpd.@acl_entry[-2].action="disabled"
set upnpd.@acl_entry[-1].comment="Low/system ports"
set upnpd.@acl_entry[-1].int_addr="0.0.0.0/0"
set upnpd.@acl_entry[-1].int_port="1-1023"
set upnpd.@acl_entry[-1].ext_port="1-1023"
set upnpd.@acl_entry[-1].action="disabled"
EOF
uci -q get upnpd.@acl_entry[-3] >/dev/null &&
uci reorder upnpd.@acl_entry[-2]=0 && uci reorder upnpd.@acl_entry[-1]=1
fi
# Rename UCI section config -> settings (v2.0)
if uci -q get upnpd.config >/dev/null; then
log "Rename UCI section config -> settings (v2.0)" && uci rename upnpd.config="settings" ||
log "Error renaming the UCI section" daemon.err
fi
uci commit upnpd >/dev/null
log "Previous v1.0 config file copied to /tmp/upnpd (kept until reboot)"
exit 0
+30 -32
View File
@@ -1,34 +1,32 @@
config upnpd config
option enabled 1
option enable_natpmp 1
option enable_upnp 1
option secure_mode 1
option log_output 0
option download 20480
option upload 2048
#by default, looked up dynamically from ubus
# option external_iface wan
option internal_iface lan
option port 5000
option upnp_lease_file /var/run/miniupnpd.leases
option upnp_lease_file6 /var/run/miniupnpd.leases6
option igdv1 1
option ipv6_disable 1
option use_stun 0
option stun_host stun.miwifi.com
option stun_port 3478
option ext_allow_private_ipv4 1
# UPnP IGD & PCP/NAT-PMP Service Settings (v2.0)
config perm_rule
option action allow
option ext_ports 1024-65535
option int_addr 0.0.0.0/0 # Does not override secure_mode
option int_ports 1024-65535
option comment "Allow high ports"
config upnpd 'settings'
option enabled '1'
# Can be set to all/upnp-igd/pcp+nat-pmp
option enable_protocols 'all'
# Enable networks, multiple can be specified, separated by a space
option internal_iface 'lan'
option access_defaults 'accept-high-ports' # default none
option check_acl '0' # default 1
#option ipv6_disable '0'
#option allow_third_party_mapping '0'
# Extra logging by setting to info or debug (previously 1)
option log_output 'default'
option upnp_igd_compat 'igdv1'
config perm_rule
option action deny
option ext_ports 0-65535
option int_addr 0.0.0.0/0
option int_ports 0-65535
option comment "Default deny"
# Access Control List
# Empty ACL rejected alone, IPv6 accepted unless disabled, action: accept/reject
config acl_entry
option comment 'High ports'
option int_addr '0.0.0.0/0'
option int_port '1024-65535'
option ext_port '1024-65535'
option action 'disabled'
config acl_entry
option comment 'Low/system ports'
option int_addr '0.0.0.0/0'
option int_port '1-1023'
option ext_port '1-1023'
option action 'disabled'