mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-11 02:44:57 +08:00
281 lines
12 KiB
Bash
281 lines
12 KiB
Bash
#!/bin/sh /etc/rc.common
|
|
# Copyright (C) 2006-2014 OpenWrt.org
|
|
|
|
START=94
|
|
STOP=15
|
|
USE_PROCD=1
|
|
PROG=/usr/sbin/miniupnpd
|
|
[ -x "$(command -v nft)" ] && FW="fw4" || FW="fw3"
|
|
|
|
start_service() {
|
|
config_load "upnpd"
|
|
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
|
|
conf="$config_file"
|
|
else
|
|
local tmpconf="/var/etc/miniupnpd.conf"
|
|
conf="$tmpconf"
|
|
mkdir -p /var/etc
|
|
upnpd_generate_config "$tmpconf" || return 1
|
|
fi
|
|
|
|
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
|
|
|
|
procd_open_instance
|
|
procd_set_param file "$conf"
|
|
procd_set_param command "$PROG"
|
|
procd_append_param command -f "$conf"
|
|
[ "$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 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 # Not used with nftables
|
|
fi
|
|
}
|
|
|
|
service_triggers() {
|
|
procd_add_reload_trigger "upnpd" "firewall"
|
|
}
|
|
|
|
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" # Not used with nftables
|
|
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 1-65535 0.0.0.0/0 $rejectport # Reject port $rejectport" &&
|
|
echo "deny $rejectport 0.0.0.0/0 1-65535 # Reject external 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/&/\&/g; s/</\</g; s/>/\>/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 address or address and netmask (internal)
|
|
config_get int_port "$cfg" int_port "1-65535" # internal port or range: x or x-y
|
|
config_get ext_port "$cfg" ext_port "1-65535" # external port or 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() {
|
|
restart
|
|
}
|