pbr: update to 1.1.7-21

* switch to dstnat chain from dstnat_lan chain for dns & tor policies (thanks @egc112)
* re-introduce procd_lan_interface for better LAN detection
* improve is_domain function
* introduce health-check for requried fw4 chains
* bugfix: avoid double counters for dns policies
* bugfix: remove faulty counters for tor policies
* rename interface_process to process_interface for better code readability
* overhaul pbr.user.aws script for a much better performance and more compact
  (gzipped) storage of the ranges json locally (thanks @bigsmile74)

Signed-off-by: Stan Grishin <stangri@melmac.ca>
This commit is contained in:
Stan Grishin
2024-10-06 22:59:17 +00:00
parent caa09e5377
commit 34c68be148
6 changed files with 60 additions and 62 deletions

View File

@@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=pbr PKG_NAME:=pbr
PKG_VERSION:=1.1.7 PKG_VERSION:=1.1.7
PKG_RELEASE:=15 PKG_RELEASE:=21
PKG_LICENSE:=AGPL-3.0-or-later PKG_LICENSE:=AGPL-3.0-or-later
PKG_MAINTAINER:=Stan Grishin <stangri@melmac.ca> PKG_MAINTAINER:=Stan Grishin <stangri@melmac.ca>

View File

@@ -57,6 +57,7 @@ nft_user_set_policy=
nft_user_set_counter= nft_user_set_counter=
procd_boot_delay= procd_boot_delay=
procd_reload_delay= procd_reload_delay=
procd_lan_interface=
procd_wan_ignore_status= procd_wan_ignore_status=
procd_wan_interface= procd_wan_interface=
procd_wan6_interface= procd_wan6_interface=
@@ -305,7 +306,7 @@ uci_get_device() { uci_get 'network' "$1" 'device' || uci_get 'network' "$1" 'de
uci_get_protocol() { uci_get 'network' "$1" 'proto'; } uci_get_protocol() { uci_get 'network' "$1" 'proto'; }
is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; } is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
is_disabled_interface() { [ "$(uci_get 'network' "$1" 'disabled')" = '1' ]; } is_disabled_interface() { [ "$(uci_get 'network' "$1" 'disabled')" = '1' ]; }
is_domain() { ! is_ipv6 "$1" && str_contains "$1" '[a-zA-Z]'; } is_domain() { ! is_ipv6 "$1" && ! is_mac_address "$1" && ! is_phys_dev "$1" && str_contains "$1" '[a-zA-Z]'; }
is_dslite() { local p; network_get_protocol p "$1"; [ "${p:0:6}" = "dslite" ]; } is_dslite() { local p; network_get_protocol p "$1"; [ "${p:0:6}" = "dslite" ]; }
is_family_mismatch() { ( is_ipv4_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_ipv4_netmask "${2//!}" ); } is_family_mismatch() { ( is_ipv4_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_ipv4_netmask "${2//!}" ); }
is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; } is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
@@ -326,7 +327,7 @@ is_ipv6_link_local() { [ "${1:0:4}" = 'fe80' ]; }
is_ipv6_unique_local() { [ "${1:0:2}" = 'fc' ] || [ "${1:0:2}" = 'fd' ]; } is_ipv6_unique_local() { [ "${1:0:2}" = 'fc' ] || [ "${1:0:2}" = 'fd' ]; }
is_list() { str_contains "$1" ',' || str_contains "$1" ' '; } is_list() { str_contains "$1" ',' || str_contains "$1" ' '; }
is_ipv4_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; } is_ipv4_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; }
is_lan() { local d; network_get_device d "$1"; str_contains "$d" 'br-lan'; } is_lan() { local d; network_get_device d "$1"; str_contains "$procd_lan_interface" "$d"; }
is_l2tp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "l2tp" ]; } is_l2tp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "l2tp" ]; }
is_mac_address() { expr "$1" : '[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]$' >/dev/null; } is_mac_address() { expr "$1" : '[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]$' >/dev/null; }
is_negation() { [ "${1:0:1}" = '!' ]; } is_negation() { [ "${1:0:1}" = '!' ]; }
@@ -575,6 +576,7 @@ load_package_config() {
config_get verbosity 'config' 'verbosity' '2' config_get verbosity 'config' 'verbosity' '2'
config_get procd_boot_delay 'config' 'procd_boot_delay' '0' config_get procd_boot_delay 'config' 'procd_boot_delay' '0'
config_get procd_boot_timeout 'config' 'procd_boot_timeout' '30' config_get procd_boot_timeout 'config' 'procd_boot_timeout' '30'
config_get procd_lan_interface 'config' 'procd_lan_interface' 'br-lan'
config_get procd_wan_ignore_status 'config' 'procd_wan_ignore_status' '0' config_get procd_wan_ignore_status 'config' 'procd_wan_ignore_status' '0'
config_get procd_wan_interface 'config' 'procd_wan_interface' 'wan' config_get procd_wan_interface 'config' 'procd_wan_interface' 'wan'
config_get procd_wan6_interface 'config' 'procd_wan6_interface' 'wan6' config_get procd_wan6_interface 'config' 'procd_wan6_interface' 'wan6'
@@ -630,9 +632,9 @@ load_environment() {
state add 'errorSummary' 'errorDefaultFw4TableMissing' 'fw4' state add 'errorSummary' 'errorDefaultFw4TableMissing' 'fw4'
return 1 return 1
fi fi
if is_config_enabled 'dns_policy'; then if is_config_enabled 'dns_policy' || is_tor_running; then
if ! nft_call list chain inet fw4 dstnat_lan; then if ! nft_call list chain inet fw4 dstnat; then
state add 'errorSummary' 'errorDefaultFw4ChainMissing' 'dstnat_lan' state add 'errorSummary' 'errorDefaultFw4ChainMissing' 'dstnat'
return 1 return 1
fi fi
fi fi
@@ -910,7 +912,7 @@ cleanup_rt_tables() {
cleanup_main_chains() { cleanup_main_chains() {
local i j local i j
for i in $chainsList dstnat_lan; do for i in $chainsList dstnat; do
i="$(str_to_lower "$i")" i="$(str_to_lower "$i")"
nft_call flush chain inet "$nftTable" "${nftPrefix}_${i}" nft_call flush chain inet "$nftTable" "${nftPrefix}_${i}"
done done
@@ -1172,7 +1174,7 @@ dns_policy_routing() {
local negation value dest4 dest6 first_value local negation value dest4 dest6 first_value
local inline_set_ipv4_empty_flag inline_set_ipv6_empty_flag local inline_set_ipv4_empty_flag inline_set_ipv6_empty_flag
local name="$1" src_addr="$2" dest_dns="$3" uid="$4" local name="$1" src_addr="$2" dest_dns="$3" uid="$4"
local chain='dstnat_lan' iface='dns' local chain='dstnat' iface='dns'
if [ -z "${dest_dns_ipv4}${dest_dns_ipv6}" ]; then if [ -z "${dest_dns_ipv4}${dest_dns_ipv6}" ]; then
processPolicyError='true' processPolicyError='true'
@@ -1197,8 +1199,8 @@ dns_policy_routing() {
unset param4 unset param4
unset param6 unset param6
dest4="dport 53 counter dnat ip to ${dest_dns_ipv4}:53" dest4="dport 53 dnat ip to ${dest_dns_ipv4}:53"
dest6="dport 53 counter dnat ip6 to ${dest_dns_ipv6}:53" dest6="dport 53 dnat ip6 to ${dest_dns_ipv6}:53"
if [ -n "$src_addr" ]; then if [ -n "$src_addr" ]; then
if [ "${src_addr:0:1}" = "!" ]; then if [ "${src_addr:0:1}" = "!" ]; then
@@ -1237,8 +1239,8 @@ dns_policy_routing() {
fi fi
fi fi
param4="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param4} ${proto_i} ${nft_rule_params} ${dest4} comment \"$name\"" param4="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param4} ${nft_rule_params} meta nfproto ipv4 ${proto_i} ${dest4} comment \"$name\""
param6="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param6} ${proto_i} ${nft_rule_params} ${dest6} comment \"$name\"" param6="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param6} ${nft_rule_params} meta nfproto ipv6 ${proto_i} ${dest6} comment \"$name\""
local ipv4_error='0' ipv6_error='0' local ipv4_error='0' ipv6_error='0'
if [ "$policy_routing_nft_prev_param4" != "$param4" ] && \ if [ "$policy_routing_nft_prev_param4" != "$param4" ] && \
@@ -1439,13 +1441,14 @@ policy_routing() {
local dest_udp_53 dest_tcp_80 dest_udp_80 dest_tcp_443 dest_udp_443 local dest_udp_53 dest_tcp_80 dest_udp_80 dest_tcp_443 dest_udp_443
local ipv4_error='0' ipv6_error='0' local ipv4_error='0' ipv6_error='0'
local dest_i dest4 dest6 local dest_i dest4 dest6
param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} dstnat meta nfproto ipv4 $param4" chain='dstnat'
param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} dstnat meta nfproto ipv6 $param6" param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${nft_rule_params} meta nfproto ipv4 $param4"
dest_udp_53="udp dport 53 counter redirect to :${torDnsPort} comment 'Tor-DNS-UDP'" param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${nft_rule_params} meta nfproto ipv6 $param6"
dest_tcp_80="tcp dport 80 counter redirect to :${torTrafficPort} comment 'Tor-HTTP-TCP'" dest_udp_53="udp dport 53 redirect to :${torDnsPort} comment 'Tor-DNS-UDP'"
dest_udp_80="udp dport 80 counter redirect to :${torTrafficPort} comment 'Tor-HTTP-UDP'" dest_tcp_80="tcp dport 80 redirect to :${torTrafficPort} comment 'Tor-HTTP-TCP'"
dest_tcp_443="tcp dport 443 counter redirect to :${torTrafficPort} comment 'Tor-HTTPS-TCP'" dest_udp_80="udp dport 80 redirect to :${torTrafficPort} comment 'Tor-HTTP-UDP'"
dest_udp_443="udp dport 443 counter redirect to :${torTrafficPort} comment 'Tor-HTTPS-UDP'" dest_tcp_443="tcp dport 443 redirect to :${torTrafficPort} comment 'Tor-HTTPS-TCP'"
dest_udp_443="udp dport 443 redirect to :${torTrafficPort} comment 'Tor-HTTPS-UDP'"
for dest_i in dest_udp_53 dest_tcp_80 dest_udp_80 dest_tcp_443 dest_udp_443; do for dest_i in dest_udp_53 dest_tcp_80 dest_udp_80 dest_tcp_443 dest_udp_443; do
eval "dest4=\$$dest_i" eval "dest4=\$$dest_i"
eval "dest6=\$$dest_i" eval "dest6=\$$dest_i"
@@ -1483,7 +1486,6 @@ policy_routing() {
nft6 "$param6" || ipv6_error='1' nft6 "$param6" || ipv6_error='1'
policy_routing_nft_prev_param6="$param6" policy_routing_nft_prev_param6="$param6"
fi fi
if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
processPolicyError='true' processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name" state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
@@ -1816,7 +1818,7 @@ json_add_gateway() {
json_close_object json_close_object
} }
interface_process() { process_interface() {
local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" reloadedIface="$3" local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" reloadedIface="$3"
local displayText dispDev dispGw4 dispGw6 dispStatus local displayText dispDev dispGw4 dispGw6 dispStatus
@@ -2080,8 +2082,8 @@ start_service() {
load_environment "${param:-on_start}" "$(load_validate_config)" || return 1 load_environment "${param:-on_start}" "$(load_validate_config)" || return 1
is_wan_up "$param" || return 1 is_wan_up "$param" || return 1
interface_process 'all' 'prepare' process_interface 'all' 'prepare'
config_foreach interface_process 'interface' 'pre_init' config_foreach process_interface 'interface' 'pre_init'
case "$param" in case "$param" in
on_boot) on_boot)
@@ -2137,8 +2139,8 @@ start_service() {
on_interface_reload) on_interface_reload)
output 1 "Reloading Interface: $reloadedIface " output 1 "Reloading Interface: $reloadedIface "
json_add_array 'gateways' json_add_array 'gateways'
interface_process 'all' 'prepare' process_interface 'all' 'prepare'
config_foreach interface_process 'interface' 'reload_interface' "$reloadedIface" config_foreach process_interface 'interface' 'reload_interface' "$reloadedIface"
json_close_array json_close_array
output 1 '\n' output 1 '\n'
;; ;;
@@ -2154,10 +2156,10 @@ start_service() {
nft_file 'create' nft_file 'create'
output 1 'Processing interfaces ' output 1 'Processing interfaces '
json_add_array 'gateways' json_add_array 'gateways'
interface_process 'all' 'prepare' process_interface 'all' 'prepare'
config_foreach interface_process 'interface' 'create' config_foreach process_interface 'interface' 'create'
interface_process 'tor' 'destroy' process_interface 'tor' 'destroy'
is_tor_running && interface_process 'tor' 'create' is_tor_running && process_interface 'tor' 'create'
json_close_array json_close_array
ip route flush cache ip route flush cache
output 1 '\n' output 1 '\n'
@@ -2174,8 +2176,8 @@ start_service() {
output 1 '\n' output 1 '\n'
fi fi
if is_config_enabled 'include' || [ -d "/etc/${packageName}.d/" ]; then if is_config_enabled 'include' || [ -d "/etc/${packageName}.d/" ]; then
interface_process 'all' 'prepare' process_interface 'all' 'prepare'
config_foreach interface_process 'interface' 'create_user_set' config_foreach process_interface 'interface' 'create_user_set'
output 1 'Processing user file(s) ' output 1 'Processing user file(s) '
config_load "$packageName" config_load "$packageName"
config_foreach load_validate_include 'include' user_file_process config_foreach load_validate_include 'include' user_file_process
@@ -2270,8 +2272,8 @@ stop_service() {
cleanup_marking_chains cleanup_marking_chains
output 1 'Resetting interfaces ' output 1 'Resetting interfaces '
config_load 'network' config_load 'network'
config_foreach interface_process 'interface' 'destroy' config_foreach process_interface 'interface' 'destroy'
interface_process 'tor' 'destroy' process_interface 'tor' 'destroy'
cleanup_rt_tables cleanup_rt_tables
output 1 "\\n" output 1 "\\n"
ip route flush cache ip route flush cache
@@ -2324,7 +2326,7 @@ status_service() {
fi fi
echo "$_SEPARATOR_" echo "$_SEPARATOR_"
echo "$packageName chains - policies" echo "$packageName chains - policies"
for i in $chainsList dstnat_lan; do for i in $chainsList dstnat; do
"$nft" -a list table inet "$nftTable" | sed -n "/chain ${nftPrefix}_${i} {/,/\t}/p" "$nft" -a list table inet "$nftTable" | sed -n "/chain ${nftPrefix}_${i} {/,/\t}/p"
done done
echo "$_SEPARATOR_" echo "$_SEPARATOR_"
@@ -2377,6 +2379,7 @@ load_validate_config() {
'procd_boot_delay:integer:0' \ 'procd_boot_delay:integer:0' \
'procd_boot_timeout:integer:30' \ 'procd_boot_timeout:integer:30' \
'procd_reload_delay:integer:0' \ 'procd_reload_delay:integer:0' \
'procd_lan_interface:list(or(network)):br-lan' \
'procd_wan_ignore_status:bool:0' \ 'procd_wan_ignore_status:bool:0' \
'procd_wan_interface:network:wan' \ 'procd_wan_interface:network:wan' \
'procd_wan6_interface:network:wan6' \ 'procd_wan6_interface:network:wan6' \

View File

@@ -0,0 +1 @@
jump pbr_dstnat comment "Jump into pbr dstnat chain";

View File

@@ -1 +0,0 @@
jump pbr_dstnat_lan comment "Jump into pbr dstnat_lan chain";

View File

@@ -1,4 +1,4 @@
chain pbr_dstnat_lan {} chain pbr_dstnat {}
chain pbr_forward {} chain pbr_forward {}
chain pbr_input {} chain pbr_input {}
chain pbr_output {} chain pbr_output {}

View File

@@ -1,34 +1,29 @@
#!/bin/sh #!/bin/sh
# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh # shellcheck disable=SC2015,SC3003,SC3060
TARGET_URL='https://ip-ranges.amazonaws.com/ip-ranges.json'
TARGET_INTERFACE='wan' TARGET_DL_FILE='/var/pbr_tmp_aws_ip_ranges.gz'
TARGET_NFTSET_4="pbr_${TARGET_INTERFACE}_4_dst_ip_user"
TARGET_NFTSET_6="pbr_${TARGET_INTERFACE}_6_dst_ip_user"
TARGET_TABLE='inet fw4' TARGET_TABLE='inet fw4'
TARGET_URL="https://ip-ranges.amazonaws.com/ip-ranges.json" TARGET_INTERFACE='wan'
TARGET_DL_FILE_4="/var/pbr_tmp_aws_ip_ranges.ipv4"
# Uncomment the following line if you enabled ipv6 for pbr and want IPv6 entries added to the IPv6 set
# TARGET_DL_FILE_6="/var/pbr_tmp_aws_ip_ranges.ipv6"
_ret=0
if [ ! -s "$TARGET_DL_FILE_4" ]; then _ret=1
uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | grep "ip_prefix" | sed 's/^.*\"ip_prefix\": \"//; s/\",//' > "$TARGET_DL_FILE_4"
fi
if [ -s "$TARGET_DL_FILE_4" ]; then mkdir -p "${TARGET_DL_FILE%/*}"
params=
while read -r p; do params="${params:+$params, }${p}"; done < "$TARGET_DL_FILE_4"
[ -n "$params" ] && nft "add element $TARGET_TABLE $TARGET_NFTSET_4 { $params }" || _ret=1
fi
if [ -n "$TARGET_DL_FILE_6" ] && [ ! -s "$TARGET_DL_FILE_6" ]; then [ -s "$TARGET_DL_FILE" ] || \
uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | grep "ipv6_prefix" | sed 's/^.*\"ipv6_prefix\": \"//; s/\",//' > "$TARGET_DL_FILE_6" uclient-fetch --no-check-certificate -qO- "$TARGET_URL" | \
fi gzip > "$TARGET_DL_FILE"
if [ -s "$TARGET_DL_FILE_6" ]; then [ "$(uci get pbr.config.ipv6_enabled)" = "1" ] && vers="4 6" || vers="4"
params=
while read -r p; do params="${params:+$params, }${p}"; done < "$TARGET_DL_FILE_6" for ver in $vers;do
[ -n "$params" ] && nft "add element $TARGET_TABLE $TARGET_NFTSET_6 { $params }" || _ret=1 case "$ver" in
fi 4) search='@.prefixes[*].ip_prefix';;
6) search='@.ipv6_prefixes[*].ipv6_prefix';;
esac
params="$(zcat "$TARGET_DL_FILE" | jsonfilter -e "$search")"
[ -n "$params" ] && _ret=0 || continue
nftset="pbr_${TARGET_INTERFACE}_${ver}_dst_ip_user"
nft "add element $TARGET_TABLE $nftset { ${params//$'\n'/, } }" || _ret=1
done
return $_ret return $_ret