Configure OpenVPN only on some LAN ports

I am using the OpenVPN configuration in luci. I am not sure what interface name to add? I tried tun0, but it does not then show up in the policies interface drop down.

uci export network; uci export wireless

config interface 'loopback'
        option ifname 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option ula_prefix 'fdb8:dd48:fbae::/48'

config interface 'lan'
        option type 'bridge'
        option ifname 'eth0.1'
        option proto 'static'
        option ipaddr '192.168.1.1'
        option netmask '255.255.255.0'
        option ip6assign '60'
        list dns '8.8.8.8'
        list dns '8.8.4.4'
        list dns '1.1.1.1'

config device 'lan_eth0_1_dev'
        option name 'eth0.1'
        option macaddr 'ec:ad:e0:20:a5:10'

config interface 'wan'
        option ifname 'eth0.2'
        option proto 'dhcp'

config device 'wan_eth0_2_dev'
        option name 'eth0.2'
        option macaddr 'ec:ad:e0:20:a5:0f'

config interface 'wan6'
        option ifname 'eth0.2'
        option proto 'dhcpv6'

config switch
        option name 'switch0'
        option reset '1'
        option enable_vlan '1'

config switch_vlan
        option device 'switch0'
        option vlan '1'
        option ports '0 1 2 3 6t'

config switch_vlan
        option device 'switch0'
        option vlan '2'
        option ports '4 6t'

config interface 'wwan'
        option proto 'qmi'
        option device '/dev/cdc-wdm0'
        option apn 'wapaccess.co.nz'
        list dns '8.8.8.8'
        list dns '8.8.4.4'
        list dns '1.1.1.1'

package wireless

config wifi-device 'radio0'
        option type 'mac80211'
        option channel '11'
        option hwmode '11g'
        option path 'platform/10180000.wmac'
        option htmode 'HT20'

config wifi-iface 'default_radio0'
        option device 'radio0'
        option network 'lan'
        option mode 'ap'
        option ssid 'OpenWrt'
        option encryption 'none'

What is the output of: uci export openvpn ? (remove any keys/cert/passwords)

Here we go:


root@OpenWrt:~# uci export openvpn
package openvpn

config openvpn 'custom_config'
        option config '/etc/openvpn/my-vpn.conf'

config openvpn 'sample_server'
        option port '1194'
        option proto 'udp'
        option dev 'tun'
        option ca '/etc/openvpn/ca.crt'
        option cert '/etc/openvpn/server.crt'
        option key '/etc/openvpn/server.key'
        option dh '/etc/openvpn/dh1024.pem'
        option server '10.8.0.0 255.255.255.0'
        option ifconfig_pool_persist '/tmp/ipp.txt'
        option keepalive '10 120'
        option compress 'lzo'
        option persist_key '1'
        option persist_tun '1'
        option user 'nobody'
        option status '/tmp/openvpn-status.log'
        option verb '3'

config openvpn 'sample_client'
        option client '1'
        option dev 'tun'
        option proto 'udp'
        list remote 'my_server_1 1194'
        option resolv_retry 'infinite'
        option nobind '1'
        option persist_key '1'
        option persist_tun '1'
        option user 'nobody'
        option ca '/etc/openvpn/ca.crt'
        option cert '/etc/openvpn/client.crt'
        option key '/etc/openvpn/client.key'
        option compress 'lzo'
        option verb '3'

config openvpn 'StrongVPN'
        option config '/etc/openvpn/StrongVPN.ovpn'
        option enabled '1'

The configuration is stored in there, please paste the contents of the file here.

Thank you - here is the /etc/openvpn/StrongVPN.ovpn

remote xxx.xxx.xxx.x 4672 udp
remote xxx.xxx.xxx.x 123 udp
remote xxx.xxx.xxx.x 53 udp
key-direction 1
cipher BF-CBC
client
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
;http-proxy-retry
;http-proxy xxx.xxx.xxx.x xx
verb 3
reneg-sec 86400
echo xxxxxxxxxxxxxxxxx
tun-mtu 1500
route-method exe
route-delay 2
redirect-gateway def1
comp-lzo no
fragment 1390
mssfix 1390
hand-window 30
<ca>
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
</ca>
<key>
-----BEGIN PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END PRIVATE KEY-----
</key>
<cert>
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
</cert>
<tls-auth>
-----BEGIN OpenVPN Static key V1-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END OpenVPN Static key V1-----
</tls-auth>

It's a tun interface so it must be detected from VPN-PBR. Could you try to connect the VPN and paste the output of /etc/init.d/vpn-policy-routing restart ?

Hi

I stopped and then started the VPN (luci, VPN, OpenVPN.

I then looked at /etc/init.d/vpn-policy-routing and I could not really see much under restart. I cant paste the full text from vpn-policy-routing because it is over the character limit - so I will spread it over two posts.....

Part 1

#!/bin/sh /etc/rc.common
# Copyright 2017-2020 Stan Grishin (stangri@melmac.net)
# shellcheck disable=SC2039,SC1091,SC2018,SC2019
PKG_VERSION='0.2.1-13'

export START=94
export USE_PROCD=1

readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m'
readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m'
readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m'
readonly __PASS__='\033[0;33m[-]\033[0m'
readonly _ERROR_='\033[0;31mERROR\033[0m'
readonly _WARNING_='\033[0;33mWARNING\033[0m'
readonly readmeURL="https://github.com/openwrt/packages/tree/master/net/vpn-policy-routing/files/README.md"

export EXTRA_COMMANDS='support'
export EXTRA_HELP="	support	Generates output required to troubleshoot routing issues
		Use '-d' option for more detailed output
		Use '-p' option to automatically upload data under VPR paste.ee account
			WARNING: while paste.ee uploads are unlisted, they are still publicly available
		List domain names after options to include their lookup in report"

readonly packageName='vpn-policy-routing'
readonly serviceName="$packageName $PKG_VERSION"
readonly PID="/var/run/${packageName}.pid"
readonly dnsmasqFile="/var/dnsmasq.d/${packageName}"
readonly userFile="/etc/${packageName}.user"
readonly sharedMemoryOutput="/dev/shm/$packageName-output"
create_lock() { [ -e "$PID" ] && return 1; touch "$PID"; }
remove_lock() { [ -e "$PID" ] && rm -f "$PID"; }
trap remove_lock EXIT
output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; }
output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; }
output_fail() { s=1; output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; }
output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; }
# str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; }
# str_contains() { [ "$1" != "$(str_replace "$1" "$2" "")" ]; }
# shellcheck disable=SC2018,SC2019
str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
str_extras_to_space() { echo "$1" | tr ';{}' ' '; }

output() {
# Can take a single parameter (text) to be output at any verbosity
# Or target verbosity level and text to be output at specifc verbosity
	local msg memmsg logmsg
	if [ $# -ne 1 ]; then
		if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi
	fi
	[ -t 1 ] && printf "%b" "$1"
	msg="${1//$serviceName /service }";
	if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
		[ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
		logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
		logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")"
		rm -f "$sharedMemoryOutput"
	else
		printf "%b" "$msg" >> "$sharedMemoryOutput"
	fi
}
is_installed() { [ -s "/usr/lib/opkg/info/${1}.control" ]; }

export serviceEnabled verbosity strictMode wanTableID wanMark fwMask
export ipv6Enabled localIpset remoteIpset ipruleEnabled icmpIface
export ignoredIfaces="" supportedIfaces=""
export appendLocalPolicy="" appendRemotePolicy=""
export wanIface4 wanIface6 ifaceMark ifaceTableID ifAll ifSupported wanGW4 wanGW6
export bootTimeout insertOption

list_iface() { ifAll="${ifAll}${1} "; }
list_supported_iface() { is_supported_interface "$1" && ifSupported="${ifSupported}${1} "; }
vpr_find_true() {
	local iface i param="$2"
	[ "$param" = 'wan6' ] || param='wan'
	"network_find_${param}" iface
	is_tunnel "$iface" && unset iface
	if [ -z "$iface" ]; then
		unset ifAll; config_load 'network';
		config_foreach list_iface 'interface'
		for i in $ifAll; do
			if "is_${param}" "$i"; then break; else unset i; fi
		done
	fi
	export "$1=${iface:-$i}"
}
vpr_get_gateway() {
	local iface="$2" dev="$3" gw
	network_get_gateway gw "$iface"
	if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
		gw="$(ip -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')"
	fi
	export "$1=$gw"
}
vpr_get_gateway6() {
	local iface="$2" dev="$3" gw
	network_get_gateway6 gw "$iface"
	if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
		gw="$(ip -6 a list dev "$dev" 2>/dev/null | grep inet6 | awk '{print $2}')"
	fi
	export "$1=$gw"
}
is_l2tp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "l2tp" ]; }
is_oc() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:11}" = "openconnect" ]; }
is_ovpn() { local dev; dev=$(uci -q get network."$1".ifname); [ "${dev:0:3}" = "tun" ] || [ "${dev:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${dev}/tun_flags" ]; }
is_pptp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "pptp" ]; }
is_tor() { local dev; dev=$(uci -q get network."$1".ifname); [ "${dev:0:3}" = "tor" ]; }
is_wg() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:9}" = "wireguard" ]; }
is_tunnel() { is_l2tp "$1" || is_oc "$1" || is_ovpn "$1" || is_pptp "$1" || is_tor "$1" || is_wg "$1"; }
is_wan() { [ "$1" = "$wanIface4" ] || { [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; }
is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1/#wan6}" != "$1" ] || [ "${1/%wan6}" != "$1" ]; }
string_match_word() { echo "$1" | grep -q -w "$2"; }
is_ignored_interface() { string_match_word "$ignoredIfaces" "$1"; }
is_supported_interface() { string_match_word "$supportedIfaces" "$1" || { ! is_ignored_interface "$1" && { is_wan "$1" || is_wan6 "$1" || is_tunnel "$1"; }; }; }
is_mac_address() { expr "$1" : '[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]$' >/dev/null; }
is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; }
is_ipv6() { ! is_mac_address "$1" && [ "${1//:}" != "$1" ]; }
is_ipv6_link_local() { [ "${1:0:4}" = "fe80" ]; }
is_ipv6_unique_local() { [ "${1:0:2}" = "fc" ] || [ "${1:0:2}" = "fd" ]; }
is_ipv6_global() { [ "${1:0:4}" = "2001" ]; }
# is_ipv6_global() { is_ipv6 "$1" && ! is_ipv6_link_local "$1" && ! is_ipv6_link_local "$1"; }
is_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; }
is_domain() { [ "${1//[a-zA-Z-]}" != "$1" ]; }
is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; }
is_turris() { /bin/ubus -S call system board | /bin/grep 'Turris' | /bin/grep -q '15.05'; }
is_chaos_calmer() { ubus -S call system board | grep -q 'Chaos Calmer'; }
dnsmasq_kill() { killall -q -HUP dnsmasq; }
dnsmasq_restart() { output 1 'Restarting DNSMASQ '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; }
is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
is_supported_iface_dev() {
	for n in $ifSupported; do 
		if [ "$1" = "$(uci -q get "network.${n}.ifname" || echo "$n")" ] || [ "$1" = "$(uci -q get "network.${n}.proto")-${n}" ] ; then return 0; fi
	done
	return 1
}
is_supported_protocol () { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; }

load_package_config() {
	config_load "$packageName"
	config_get_bool serviceEnabled      'config' 'enabled' 0
	config_get_bool strictMode          'config' 'strict_enforcement' 1
	config_get_bool ipv6Enabled         'config' 'ipv6_enabled' 0
	config_get_bool localIpset          'config' 'src_ipset' 0
	config_get_bool ipruleEnabled       'config' 'iprule_enabled' 0
	config_get remoteIpset              'config' 'dest_ipset'
	config_get appendLocalPolicy        'config' 'append_src_rules'
	config_get appendRemotePolicy       'config' 'append_dest_rules'
	config_get verbosity                'config' 'verbosity' '2'
	config_get wanTableID               'config' 'wan_tid' '201'
	config_get wanMark                  'config' 'wan_mark' '0x010000'
	config_get fwMask                   'config' 'fw_mask' '0xff0000'
	config_get icmpIface                'config' 'icmp_interface'
	config_get ignoredIfaces            'config' 'ignored_interface'
	config_get supportedIfaces          'config' 'supported_interface'
	config_get bootTimeout              'config' 'boot_timeout' '30'
	config_get insertOption             'config' 'iptables_rule_option' 'append'

	if [ -z "${verbosity##*[!0-9]*}" ] || [ "$verbosity" -lt 0 ] || [ "$verbosity" -gt 2 ]; then
		verbosity=1
	fi

	. /lib/functions/network.sh
	. /usr/share/libubox/jshn.sh
	vpr_find_true wanIface4 'wan'
	[ "$ipv6Enabled" -ne 0 ] && vpr_find_true wanIface6 'wan6'
	[ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4"
	[ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6"
	wanGW="${wanGW4:-$wanGW6}"
}

is_enabled() {
	load_package_config
	if [ "$serviceEnabled" -eq 0 ]; then
		if [ "$1" = 'on_start' ]; then
			output "$packageName is currently disabled.\\n"
			output "Run the following commands before starting service again:\\n"
			output "uci set $packageName.config.enabled='1'; uci commit;\\n"
		fi
		return 1
	fi

	case $insertOption in
		insert|-i|-I) insertOption='-I';;
		append|-a|-A|*) insertOption='-A';;
	esac

	case $remoteIpset in
		ipset)
			if ! ipset help hash:net >/dev/null 2>&1; then
				output "$_ERROR_: ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n"
				unset remoteIpset
			fi
			;;
		dnsmasq.ipset)
			if dnsmasq -v 2>/dev/null | grep -q 'no-ipset' || ! dnsmasq -v 2>/dev/null | grep -q -w 'ipset'; then
				output "$_ERROR_: DNSMASQ ipset support is enabled in $packageName, but DNSMASQ is either not installed or installed DNSMASQ does not support ipsets!\\n"
				unset remoteIpset
			fi
			if ! ipset help hash:net >/dev/null 2>&1; then
				output "$_ERROR_: DNSMASQ ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n"
				unset remoteIpset
			fi
			;;
		*) unset remoteIpset;;
	esac
	
	if [ "$localIpset" -ne 0 ]; then
		if ! ipset help hash:net >/dev/null 2>&1; then
			output "$_ERROR_: Local ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n"
			unset localIpset
		fi
		if ! ipset help hash:mac >/dev/null 2>&1; then
			output "$_ERROR_: Local ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:mac' type!\\n"
			unset localIpset
		fi
	fi
}

is_wan_up() {
	local sleepCount=1
	while [ -z "$wanGW" ] ; do
		vpr_find_true wanIface4 'wan'
		[ "$ipv6Enabled" -ne 0 ] && vpr_find_true wanIface6 'wan6'
		[ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4"
		[ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6"
		wanGW="${wanGW4:-$wanGW6}"
		if [ $((sleepCount)) -gt $((bootTimeout)) ] || [ -n "$wanGW" ]; then break; fi
		output "$serviceName waiting for wan gateway...\\n"; sleep 1; network_flush_cache; sleepCount=$((sleepCount+1));
	done
	mkdir -p "${PID%/*}"; mkdir -p "${dnsmasqFile%/*}";
	unset ifSupported
	config_load 'network'
	config_foreach list_supported_iface 'interface'
	if [ -n "$wanGW" ]; then
		return 0	
	else	
		output "$_ERROR_: $serviceName failed to discover WAN gateway!\\n"
		return 1
	fi
}

ipt_cleanup() {
	local i
	for i in PREROUTING FORWARD INPUT OUTPUT; do
		while iptables -t mangle -D $i -m mark --mark 0x0/0xff0000 -j VPR_${i} >/dev/null 2>&1; do : ; done
	done
	for i in PREROUTING FORWARD INPUT OUTPUT; do
		while iptables -t mangle -D $i -j VPR_${i} >/dev/null 2>&1; do : ; done
	done
}

# shellcheck disable=SC2086
ipt() {
	local d failFlagIpv4=1 failFlagIpv6=1
	for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do 
		[ "$d" != "$*" ] && { iptables $d >/dev/null 2>&1; ip6tables $d >/dev/null 2>&1; }
	done

	d="$*"; iptables $d >/dev/null 2>&1 && failFlagIpv4=0;
	if [ "$ipv6Enabled" -gt 0 ]; then ip6tables $d >/dev/null 2>&1 && failFlagIpv6=0; fi

	[ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ]
}

# shellcheck disable=SC2086
ips() {
	local command="$1" ipset="${2//-/_}" param="$3" comment="$4" appendix failFlag=0
	if [ "${ipset//_ip}" != "${ipset}" ]; then
		ipset="${ipset//_ip}"; appendix='_ip';
	elif [ "${ipset//_mac}" != "${ipset}" ]; then
		ipset="${ipset//_mac}"; appendix='_mac';
	fi

	if [ "$command" = "add_dnsmasq" ]; then
		[ "$remoteIpset" != 'dnsmasq.ipset' ] && return 1
#	elif [ "$command" = "add_unbound" ]; then
#		[ "$remoteIpset" != 'unbound.ipset' ] && return 1
	else
		if [[ -z "$appendix" && -z "$remoteIpset" ]] || \
			 [[ -n "$appendix" && "$localIpset" -eq 0 ]]; then
			return 1
		fi
	fi

	case "$command" in
		add_dnsmasq)
			echo "ipset=/${param}/${ipset} # $comment" >> "$dnsmasqFile" || failFlag=1
			;;
		add)
			ipset -q -! $command "${ipset}${appendix}" $param comment "$comment" || failFlag=1
			;;
		create)
			ipset -q -! "$command" "${ipset}${appendix}" $param || failFlag=1
			;;
		destroy|flush)
			ipset -q -! "$command" "${ipset}${appendix}" 2>/dev/null || failFlag=1
			return 0
			;;
	esac
	return $failFlag
}

ipr()
{
	[ "$ipruleEnabled" -ne 0 ] || return 1
	local comment="$1" tid=$(eval echo "\$tid_${2//-/_}") laddr="$3" failFlagIpv4=0 failFlagIpv6=1
	ip -4 rule del from "$laddr" table "$tid" >/dev/null 2>&1
	ip -4 rule add from "$laddr" table "$tid" >/dev/null 2>&1 || failFlagIpv4=1
	if [ "$ipv6Enabled" -ne 0 ]; then
		ip -6 rule del from "$laddr" table "$tid" >/dev/null 2>&1
		ip -6 rule add from "$laddr" table "$tid" >/dev/null 2>&1 && failFlagIpv6=0
	fi
	if [ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ]; then return 0; else return 1; fi
}

insert_tor_policy() {
	local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto="$7" chain="${8:-PREROUTING}"
	local mark=$(eval echo "\$mark_${iface//-/_}")
	[ -z "$mark" ] && processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}##"
	param="-t mangle $insertOption VPR_${chain} -j MARK --set-xmark ${mark}/${fwMask}"
	[ -n "$laddr" ] && param="$param -s $laddr"
	[ -n "$lport" ] && param="$param -p tcp -m multiport --sport ${lport//-/:}"
	[ -n "$raddr" ] && param="$param -d $raddr"
	[ -n "$rport" ] && param="$param -p $proto -m multiport --dport ${rport//-/:}"
	[ -n "$comment" ] && param="$param -m comment --comment $(str_extras_to_underscore "$comment")"
# Here be dragons
	return 0
}

insert_policy() {
	local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto="$(str_to_lower "$7")" chain="${8:-PREROUTING}"
	local mark=$(eval echo "\$mark_${iface//-/_}") param i valueNeg value
	if [ "$ipv6Enabled" -eq 0 ]; then
		is_ipv6 "$laddr" && return 0
		is_ipv6 "$raddr" && return 0
	fi

	if is_ipv4 "$laddr" && is_ipv6 "$raddr"; then return 0; fi
	if is_ipv6 "$laddr" && is_ipv4 "$raddr"; then return 0; fi

	if [ -z "$mark" ]; then
		processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}##"
		return 0
	fi

	if [ -z "$proto" ]; then
		if [ -n "$lport" ] || [ -n "$rport" ]; then 
			proto='tcp udp'
		else
			proto='all'
		fi
	fi

	for i in $proto; do
		if [ "$i" = 'all' ]; then
			param="-t mangle -I VPR_${chain} -j MARK --set-xmark ${mark}/${fwMask}"
		elif ! is_supported_protocol "$i"; then
			processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$comment'##"
			return 0
		else
			param="-t mangle -I VPR_${chain} -j MARK --set-xmark ${mark}/${fwMask} -p $i"
		fi

		if [ -n "$laddr" ]; then
			if [ "${laddr:0:1}" = "!" ]; then
				valueNeg='!'; value="${laddr:1}"
			else
				unset valueNeg; value="$laddr";
			fi
			if is_phys_dev "$value"; then
				param="$param $valueNeg -m physdev --physdev-in ${value:1}"
			elif is_mac_address "$value"; then
				param="$param -m mac $valueNeg --mac-source $value"
			elif [ "${appendLocalPolicy//-d}" != "$appendLocalPolicy" ] && [ -n "$raddr" ]; then
				param="$param $valueNeg -s $value"
				processPolicyError="${processPolicyError}${_ERROR_}: Cannot append '$comment' policy with '$appendLocalPolicy' as destination is already set to '$raddr'##"
			else
				param="$param $valueNeg -s $value $appendLocalPolicy"
			fi
		fi

		if [ -n "$lport" ]; then
			if [ "${lport:0:1}" = "!" ]; then
				valueNeg='!'; value="${lport:1}"
			else
				unset valueNeg; value="$lport";
			fi
			param="$param -m multiport $valueNeg --sport ${value//-/:}"
		fi

		if [ -n "$raddr" ]; then 
			if [ "${raddr:0:1}" = "!" ]; then
				valueNeg='!'; value="${raddr:1}"
			else
				unset valueNeg; value="$raddr";
			fi
			if [ "${appendRemotePolicy//-s}" != "$appendRemotePolicy" ] && [ -n "$laddr" ]; then
				param="$param $valueNeg -d $value"
				processPolicyError="${processPolicyError}${_ERROR_}: Cannot append '$comment' policy with '$appendRemotePolicy' as source is already set to '$laddr'\\n"
			else
				param="$param $valueNeg -d $value $appendRemotePolicy"
			fi
		fi

		if [ -n "$rport" ]; then
			if [ "${rport:0:1}" = "!" ]; then
				valueNeg='!'; value="${rport:1}"
			else
				unset valueNeg; value="$rport";
			fi
			param="$param -m multiport $valueNeg --dport ${value//-/:}"
		fi

		[ -n "$comment" ] && param="$param -m comment --comment $(str_extras_to_underscore "$comment")"
		ipt "$param" || processPolicyError="${processPolicyError}${_ERROR_}: iptables $param\\n"
	done
	return 0
}

r_process_policy(){
	local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto="$7" chain="$8" resolved_laddr resolved_raddr i ipsFailFlag
	if [ "${laddr//[ ;\{\}]/}" != "$laddr" ]; then
		for i in $(str_extras_to_space "$laddr"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$i" "$lport" "$raddr" "$rport" "$proto" "$chain"; done
		return 0
	elif [ "${lport//[ ;\{\}]/}" != "$lport" ]; then
		for i in $(str_extras_to_space "$lport"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$i" "$raddr" "$rport" "$proto" "$chain"; done
		return 0
	elif [ "${raddr//[ ;\{\}]/}" != "$raddr" ]; then
		for i in $(str_extras_to_space "$raddr"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$lport" "$i" "$rport" "$proto" "$chain"; done
		return 0
	elif [ "${rport//[ ;\{\}]/}" != "$rport" ]; then
		for i in $(str_extras_to_space "$rport"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$i" "$proto" "$chain"; done
		return 0
	fi

	# start non-recursive processing 
	# process TOR, netmask, physical device and mac-address separately, so we don't send them to resolveip
	if is_tor "$iface"; then
		insert_tor_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
	elif is_phys_dev "$laddr"; then
		insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
	elif [ -n "$laddr" ] && [ -z "${lport}${raddr}${rport}" ] && [ "$chain" = 'PREROUTING' ]; then
		if is_mac_address "$laddr"; then
			if [ -n "$proto" ] && [ "$proto" != 'all' ] && [ "$localIpset" -ne 0 ]; then
				processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment', mac-address '$laddr'\\n"
			fi
			ips 'add' "${iface}_mac" "$laddr" "${comment}: $laddr" || ipsFailFlag=1
		else
			if [ -n "$proto" ] && [ "$proto" != "all" ] && [ "$localIpset" -ne 0 ]; then
				processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment', address '$laddr'\\n"
			fi
			if ! ips 'add' "${iface}_ip" "$laddr" "${comment}: $laddr"; then
				ipr "$comment" "$iface" "$i" || ipsFailFlag=1
			fi
		fi
	elif [ -n "$raddr" ] && [ -z "${laddr}${lport}${rport}" ] && [ "$chain" = 'PREROUTING' ] && [ -n "$remoteIpset" ]; then
		if [ -n "$proto" ] && [ "$proto" != 'all' ]; then
			processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment', domain '$raddr'\\n"
		fi
		case "$remoteIpset" in
		ipset)
			ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || ipsFailFlag=1;;
		dnsmasq.ipset)
			if is_domain "$raddr"; then ips 'add_dnsmasq' "${iface}" "$raddr" "${comment}" || ipsFailFlag=1
			else ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || ipsFailFlag=1; fi;;
		esac
	else
		ipsFailFlag=1
	fi
	if [ -n "$ipsFailFlag" ]; then
		if is_mac_address "$laddr"; then
		insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
		elif is_netmask "$laddr" || is_netmask "$raddr"; then
			insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
		else
			[ -n "$laddr" ] && resolved_laddr="$(resolveip "$laddr")"
			[ -n "$raddr" ] && resolved_raddr="$(resolveip "$raddr")"
			if [ -n "$resolved_laddr" ] && [ "$resolved_laddr" != "$laddr" ]; then
				for i in $resolved_laddr; do [ -n "$i" ] && r_process_policy "$comment $laddr" "$iface" "$i" "$lport" "$raddr" "$rport" "$proto" "$chain"; done
			elif [ -n "$resolved_raddr" ] && [ "$resolved_raddr" != "$raddr" ]; then
					for i in $resolved_raddr; do [ -n "$i" ] && r_process_policy "$comment $raddr" "$iface" "$laddr" "$lport" "$i" "$rport" "$proto" "$chain"; done
			else
				insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
			fi
		fi
	fi
}

process_policy(){
	local name comment iface laddr lport raddr rport param mark processPolicyError processPolicyWarning proto chain enabled
	config_get comment "$1" 'comment'
	config_get name    "$1" 'name' 'blank'
	config_get iface   "$1" 'interface'
	config_get laddr   "$1" 'src_addr'
	config_get lport   "$1" 'src_port'
	config_get raddr   "$1" 'dest_addr'
	config_get rport   "$1" 'dest_port'
	config_get proto   "$1" 'proto'
	config_get chain   "$1" 'chain' 'PREROUTING'
	config_get_bool enabled "$1" 'enabled' 1

	[ "$enabled" -gt 0 ] || return 0
	proto="$(str_to_lower "$proto")"
	[ "$proto" = 'auto' ] && unset proto

	comment="${comment:-$name}"
	output 2 "Routing '$comment' via $iface "

	if [ -z "$comment" ]; then
		errorSummary="${errorSummary}${_ERROR_}: Policy name is empty\\n"
		output_fail; return 1;
	fi
	if [ -z "${laddr}${lport}${raddr}${rport}" ]; then
		errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' missing all IPs/ports\\n"
		output_fail; return 1;
	fi
	if [ -z "$iface" ]; then
		errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' has no assigned interface\\n"
		output_fail; return 1;
	fi
	if ! is_supported_interface "$iface"; then
		errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' has unknown interface: '${iface}'\\n"
		output_fail; return 1;
	fi

	lport="${lport//  / }"; lport="${lport// /,}"; lport="${lport//,\!/ !}"; 
	rport="${rport//  / }"; rport="${rport// /,}"; rport="${rport//,\!/ !}";
	r_process_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
	if [ -n "$processPolicyWarning" ]; then
		warningSummary="${warningSummary}${processPolicyWarning}\\n"
	fi
	if [ -n "$processPolicyError" ]; then
		output_fail
		errorSummary="${errorSummary}${processPolicyError}\\n"
	else
		output_ok
	fi
}

table_destroy(){
	local tid="$1" iface="$2" mark="$3"
	if [ -n "$tid" ] && [ -n "$iface" ] && [ -n "$mark" ]; then
		ip -4 rule del fwmark "$mark" table "$tid" >/dev/null 2>&1
		ip -6 rule del fwmark "$mark" table "$tid" >/dev/null 2>&1
	 	ip -4 rule del table "$tid" >/dev/null 2>&1
		ip -6 rule del table "$tid" >/dev/null 2>&1
		ip -4 route flush table "$tid";
		ip -6 route flush table "$tid";
		ips 'flush' "${iface}"; ips 'destroy' "${iface}";
		ips 'flush' "${iface}_ip"; ips 'destroy' "${iface}_ip";
		ips 'flush' "${iface}_mac"; ips 'destroy' "${iface}_mac";
		ip -4 route flush cache
		ip -6 route flush cache
		return 0
	else
		return 1
	fi
}

# shellcheck disable=SC2086
table_create(){
	local tid="$1" mark="$2" iface="$3" gw4="$4" dev="$5" gw6="$6" dev6="$7" dscp s=0 i ipv4_error=0 ipv6_error=0

	if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
		return 1
	fi

	table_destroy "$tid" "$iface" "$mark"

	if [ -n "$gw4" ] || [ "$strictMode" -ne 0 ]; then
		if [ -z "$gw4" ]; then
			ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
		else
			ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
		fi
		ip -4 route ls table main | grep -v 'br-lan' | while read -r i; do
			idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')"
			if ! is_supported_iface_dev "$idev"; then
				ip -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1
			fi
		done
		ip -4 route flush cache || ipv4_error=1
		ip -4 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv4_error=1
	fi

	if [ "$ipv6Enabled" -ne 0 ]; then
		if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strictMode" -ne 0 ]; then
			if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
				ip -6 route add unreachable default table "$tid" || ipv6_error=1
			else
				ip -6 route ls table main | grep " dev $dev6 " | while read -r i; do
					ip -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
				done
			fi
			ip -6 route flush cache || ipv6_error=1
			ip -6 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv6_error=1
		fi
	fi

	if [ $ipv4_error -eq 0 ] || [ $ipv6_error -eq 0 ]; then
		dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)"
		if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
			ipt -t mangle -I VPR_PREROUTING -m dscp --dscp "${dscp}" -j MARK --set-xmark "${mark}/${fwMask}" || s=1
		fi
		if [ -n "$remoteIpset" ]; then
			if ips 'create' "${iface}" 'hash:net comment' && ips 'flush' "${iface}"; then
				for i in PREROUTING FORWARD INPUT OUTPUT; do
					ipt -t mangle -I VPR_${i} -m set --match-set "${iface}" dst -j MARK --set-xmark "${mark}/${fwMask}" || s=1
				done
			else
			s=1
			fi
		fi
		if [ "$localIpset" -ne 0 ]; then
			if ips 'create' "${iface}_ip" 'hash:net comment' && ips 'flush' "${iface}_ip"; then
				ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_ip" src -j MARK --set-mark "${mark}/${fwMask}" || s=1
			else
			s=1
			fi
			if ips 'create' "${iface}_mac" 'hash:mac comment' && ips 'flush' "${iface}_mac"; then
				ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_mac" src -j MARK --set-mark "${mark}/${fwMask}" || s=1
			else
			s=1
			fi
		fi
		if [ "$iface" = "$icmpIface" ]; then
			ipt -t mangle -I VPR_OUTPUT -p icmp -j MARK --set-xmark "${mark}/${fwMask}" || s=1
		fi
	else
		s=1
	fi

	return $s
}

process_interface(){
	local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" displayText

	is_supported_interface "$iface" || return 0
	is_wan6 "$iface" && return 0
	[ $((ifaceMark)) -gt $((fwMask)) ] && return 1

	network_get_device dev "$iface"
	[ -z "$dev" ] && config_get dev "$iface" 'ifname'
	if is_wan "$iface" && [ -n "$wanIface6" ]; then
		network_get_device dev6 "$wanIface6"
		[ -z "$dev6" ] && config_get dev6 "$wanIface6" 'ifname'
	fi
	[ -z "$dev6" ] && dev6="$dev"

	[ -z "$ifaceTableID" ] && ifaceTableID="$wanTableID"; [ -z "$ifaceMark" ] && ifaceMark="$wanMark";

	case "$action" in
		destroy)
			table_destroy "${ifaceTableID}" "${iface}" "${ifaceMark}"
			ifaceTableID="$((ifaceTableID + 1))"; ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))";
			;;
		create)
			export "mark_${iface//-/_}=$ifaceMark"; export "tid_${iface//-/_}=$ifaceTableID";
			table_destroy "${ifaceTableID}" "${iface}"
			vpr_get_gateway gw4 "$iface" "$dev"
			vpr_get_gateway6 gw6 "$iface" "$dev6"
			if [ "$iface" = "$dev" ]; then
				displayText="${iface}/${gw4:-0.0.0.0}"
			else
				displayText="${iface}/${dev}/${gw4:-0.0.0.0}"
			fi
			[ "$ipv6Enabled" -ne 0 ] && displayText="${displayText}/${gw6:-::/0}"
			output 2 "Creating table '$displayText' "
			is_default_dev "$dev" && displayText="${displayText} ${__OK__}"
			if table_create "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6"; then
				gatewaySummary="${gatewaySummary}${displayText}\\n"
				output_ok
			else
				errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n"
				output_fail
			fi
			ifaceTableID="$((ifaceTableID + 1))"; ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))";
			;;
	esac
	return $s
}

convert_config(){
	local i
	[ -s "/etc/config/${packageName}" ] || return 0
	sed -i 's/ignored_interfaces/ignored_interface/g' "/etc/config/${packageName}"
	sed -i 's/supported_interfaces/supported_interface/g' "/etc/config/${packageName}"
	sed -i 's/local_addresses/local_address/g' "/etc/config/${packageName}"
	sed -i 's/local_ports/local_port/g' "/etc/config/${packageName}"
	sed -i 's/remote_addresses/remote_address/g' "/etc/config/${packageName}"
	sed -i 's/remote_ports/remote_port/g' "/etc/config/${packageName}"
	sed -i 's/ipset_enabled/remote_ipset/g' "/etc/config/${packageName}"
	sed -i 's/dnsmasq_enabled/dnsmasq_ipset/g' "/etc/config/${packageName}"
	sed -i 's/enable_control/webui_enable_column/g' "/etc/config/${packageName}"
	sed -i 's/proto_control/webui_protocol_column/g' "/etc/config/${packageName}"
	sed -i 's/chain_control/webui_chain_column/g' "/etc/config/${packageName}"
	sed -i 's/sort_control/webui_sorting/g' "/etc/config/${packageName}"
	sed -i 's/local_address/src_addr/g' "/etc/config/${packageName}"
	sed -i 's/local_port/src_port/g' "/etc/config/${packageName}"
	sed -i 's/remote_address/dest_addr/g' "/etc/config/${packageName}"
	sed -i 's/remote_port/dest_port/g' "/etc/config/${packageName}"
	sed -i 's/append_local_rules/append_src_rules/g' "/etc/config/${packageName}"
	sed -i 's/append_remote_rules/append_dest_rules/g' "/etc/config/${packageName}"
	sync
	config_load "$packageName"
	config_get_bool dnsmasqIpset        'config' 'dnsmasq_ipset' 0
	config_get      remoteIpset         'config' 'remote_ipset'
	config_get      webuiProtocol       'config' 'webui_supported_protocol'
# shellcheck disable=SC2154
	if [ "$dnsmasqIpset" = "1" ]; then
		remoteIpset="dnsmasq.ipset";
	elif [ "$remoteIpset" = "1" ]; then
		remoteIpset="ipset";
	elif [ "$remoteIpset" = "0" ]; then
		remoteIpset=""
	fi
	uci -q del "$packageName.config.dnsmasq_ipset"
	uci -q set "$packageName".config.remote_ipset="$remoteIpset"
# shellcheck disable=SC2154
	if [ -z "$webuiProtocol" ]; then
		uci add_list "$packageName".config.webui_supported_protocol='tcp'
		uci add_list "$packageName".config.webui_supported_protocol='udp'
		uci add_list "$packageName".config.webui_supported_protocol='tcp udp'
		uci add_list "$packageName".config.webui_supported_protocol='icmp'
		uci add_list "$packageName".config.webui_supported_protocol='all'
	fi
	uci commit "$packageName"
	sed -i 's/local_ipset/src_ipset/g' "/etc/config/${packageName}"
	sed -i 's/remote_ipset/dest_ipset/g' "/etc/config/${packageName}"
	for i in udp_proto_enabled forward_chain_enabled input_chain_enabled output_chain_enabled; do
		grep -q "$i" "/etc/config/${packageName}" && output "${_WARNING_}: $i setting is not supported in ${serviceName}.\\n"
	done
}

check_config(){ local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; }
is_config_enabled(){
	local cfg="$1" _cfg_enabled=1
	[ -n "$1" ] || return 1
	config_load "$packageName"
	config_foreach check_config "$cfg"
	return "$_cfg_enabled"
}

process_user_file(){
	local path enabled shellBin="${SHELL:-/bin/ash}"
	config_get_bool enabled "$1" 'enabled' 1
	config_get      path    "$1" 'path'
	[ "$enabled" -gt 0 ] || return 0
	if [ ! -s "$path" ]; then
		errorSummary="${errorSummary}${_ERROR_}: Custom user file '$path' not found or empty\\n"
		output_fail
		return 1
	fi
	if ! $shellBin -n "$path"; then
		errorSummary="${errorSummary}${_ERROR_}: Syntax error in custom user file '$path'\\n"
		output_fail
		return 1
	fi

Part 2:

# shellcheck disable=SC1090
	if ! . "$path"; then
		errorSummary="${errorSummary}${_ERROR_}: Error running custom user file '$path'\\n"
		output_fail
		return 1
	else
		output 2 "Running $path "
		output_ok
		return 0
	fi
}

start_service() {
	local gatewaySummary errorSummary warningSummary dnsmasqStoredHash dnsmasqNewHash i modprobeStatus=0
	convert_config
	is_enabled 'on_start' || return 1
	is_wan_up || return 0
	if create_lock; then
		if [ -s "$dnsmasqFile" ]; then
			dnsmasqStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
			rm -f "$dnsmasqFile"
		fi

		for i in xt_set ip_set ip_set_hash_ip; do
			modprobe "$i" >/dev/null 2>/dev/null || modprobeStatus=$((modprobeStatus + 1))
		done

		if [ "$modprobeStatus" -gt 0 ] && ! is_chaos_calmer; then
			errorSummary="${errorSummary}${_ERROR_}: Failed to load kernel modules\\n"
		fi

		for i in PREROUTING FORWARD INPUT OUTPUT; do
			ipt -t mangle -N "VPR_${i}"
			ipt -t mangle "$insertOption" "$i" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
		done

		output 1 'Processing Interfaces '
		config_load 'network'; config_foreach process_interface 'interface' 'create';
		output 1 '\n'
		if is_config_enabled 'policy'; then
			output 1 'Processing Policies '
			config_load "$packageName"; config_foreach process_policy 'policy';
			output 1 '\n'
		fi
		if is_config_enabled 'include'; then
			output 1 'Processing User File(s) '
			config_load "$packageName"; config_foreach process_user_file 'include';
			output 1 '\n'
		fi

		if [ -s "$dnsmasqFile" ]; then
			dnsmasqNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
		fi
		[ "$dnsmasqNewHash" != "$dnsmasqStoredHash" ] && dnsmasq_restart

		if [ -z "$gatewaySummary" ]; then
			errorSummary="${errorSummary}${_ERROR_}: failed to set up any gateway\\n"
		else
			output "$serviceName started with gateways:\\n${gatewaySummary}"
			[ -n "$errorSummary" ] && output "${errorSummary}"
			[ -n "$warningSummary" ] && output "${warningSummary}"
		fi
		procd_open_instance "main"
		procd_set_param command /bin/true
		procd_set_param stdout 1
		procd_set_param stderr 1
		procd_open_data
		json_add_array 'status'
		json_add_object ''
		[ -n "$gatewaySummary" ] && json_add_string gateway "$gatewaySummary"
		[ -n "$errorSummary" ] && json_add_string error "$errorSummary"
		[ -n "$warningSummary" ] && json_add_string warning "$warningSummary"
		if [ "$strictMode" -ne 0 ] && [ "${gatewaySummary//0.0.0.0}" != "${gatewaySummary}" ]; then
			json_add_string mode "strict"
		fi
		json_close_object
		json_close_array
		procd_close_data
		procd_close_instance
		remove_lock
	else
		output "$serviceName: another instance of ${packageName} is currently running "
		output_failn
		return 1
	fi
}

restart() { reload; }
restart_service() { reload; }

stop_service() {
	local i
	iptables -t mangle -L | grep -q VPR_PREROUTING || return 0
	if create_lock; then
		load_package_config
		for i in PREROUTING FORWARD INPUT OUTPUT; do
			ipt -t mangle -D "${i}" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
			ipt -t mangle -F "VPR_${i}"; ipt -t mangle -X "VPR_${i}";
		done
		config_load 'network'; config_foreach process_interface 'interface' 'destroy'
		unset ifaceTableID; unset ifaceMark;
		if [ -s "$dnsmasqFile" ]; then
			rm -f "$dnsmasqFile"
			dnsmasq_restart
		fi
		if [ "$serviceEnabled" -ne 0 ]; then
			output "$serviceName stopped "; output_okn;
		fi
		remove_lock
	else
		output "$serviceName: another instance of ${packageName} is currently running "; output_failn;
		return 1
	fi
}

# shellcheck disable=SC2119
service_triggers() {
	local n
	is_enabled || return 1

	procd_open_validate
		validate_config
		validate_policy
		validate_include
	procd_close_validate

	procd_add_reload_trigger 'firewall' 'openvpn' 'vpn-policy-routing'
	procd_open_trigger
		for n in $ifSupported; do procd_add_reload_interface_trigger "$n"; procd_add_interface_trigger "interface.*" "$n" /etc/init.d/${packageName} reload; done;
	procd_close_trigger

	if [ "$verbosity" -eq 2 ]; then
		output "$serviceName monitoring interfaces: $ifSupported.\\n"
	fi
}

input() { local data; while read -r data; do echo "$data" | tee -a /var/${packageName}-support; done; }
status_service() { support "$@"; }
support() {
	local dist vers out id s param status set_d set_p tableCount i=0 dev dev6
	is_enabled

	json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
	if [ -n "$wanIface4" ]; then
		network_get_gateway wanGW4 "$wanIface4"
		dev="$(uci -q get network."${wanIface4}".ifname)"
	fi
	if [ -n "$wanIface6" ]; then
		dev6="$(uci -q get network."${wanIface6}".ifname)"
		wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
		[ "$wanGW6" = "default" ] && wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
	fi
	while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; export "set_$param=1"; shift; done
	[ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
	status="$serviceName running on $dist $vers."
	[ -n "$wanIface4" ] && status="$status WAN (IPv4): $wanIface4/dev/${wanGW4:-0.0.0.0}."
	[ -n "$wanIface6" ] && status="$status WAN (IPv6): $wanIface6/dev6/${wanGW6:-::/0}."
	{
		echo "$status"
		echo "============================================================"
			dnsmasq --version 2>/dev/null | sed '/^$/,$d'
		[ -n "$1" ] && {
			echo "============================================================"
				echo "Resolving domains"
				while [ -n "$1" ]; do echo "$1: $(resolveip "$1" | tr '\n' ' ')"; shift; done; }
		echo "============================================================"
			echo "Routes/IP Rules"
			tableCount=$(ip rule list | grep -c 'fwmark') || tableCount=0
			if [ -n "$set_d" ]; then route; else route | grep '^default'; fi
			if [ -n "$set_d" ]; then ip rule list; fi # || ip rule list | grep 'fwmark'
			i=0; while [ $i -lt $tableCount ]; do echo "IPv4 Table $((wanTableID + i)): $(ip route show table $((wanTableID + i)))"; echo "IPv4 Table $((wanTableID + i)) Rules:"; ip rule list | grep $((wanTableID + i)); i=$((i + 1)); done
			[ "$ipv6Enabled" -ne 0 ] && {
				i=0; while [ $i -lt $tableCount ]; do
					ip -6 route show table $((wanTableID + i)) | while read -r param; do echo "IPv6 Table $((wanTableID + i)): $param"; done
					i=$((i + 1))
				done; }
		echo "============================================================"
			if [ -z "$set_d" ]; then echo "IP Tables PREROUTING"; else echo "IP Tables"; fi
			if [ -z "$set_d" ]; then iptables -v -t mangle -S VPR_PREROUTING; else iptables -L -t mangle; fi
		[ "$ipv6Enabled" -ne 0 ] && {
		echo "============================================================"
			if [ -z "$set_d" ]; then echo "IP6 Tables PREROUTING"; else echo "IP6 Tables"; fi
			if [ -z "$set_d" ]; then ip6tables -v -t mangle -S VPR_PREROUTING; else ip6tables -L -t mangle; fi
		}
		[ -z "$set_d" ] && { echo "============================================================"
			echo "IP Tables FORWARD"
			iptables -v -t mangle -S VPR_FORWARD
		[ "$ipv6Enabled" -ne 0 ] && {
		echo "============================================================"
			echo "IPv6 Tables FORWARD"
			ip6tables -v -t mangle -S VPR_FORWARD
		};}
		[ -z "$set_d" ] && { echo "============================================================"
			echo "IP Tables INPUT"
			iptables -v -t mangle -S VPR_INPUT
		[ "$ipv6Enabled" -ne 0 ] && {
		echo "============================================================"
			echo "IPv6 Tables INPUT"
			ip6tables -v -t mangle -S VPR_INPUT
		};}
		[ -z "$set_d" ] && { echo "============================================================"
			echo "IP Tables OUTPUT"
			iptables -v -t mangle -S VPR_OUTPUT
		[ "$ipv6Enabled" -ne 0 ] && {
		echo "============================================================"
			echo "IPv6 Tables OUTPUT"
			ip6tables -v -t mangle -S VPR_OUTPUT
		};}
		echo "============================================================"
			echo "Current ipsets"
			ipset save
		if [ -s "$dnsmasqFile" ]; then
			echo "============================================================"
				echo "DNSMASQ ipsets"
				cat "$dnsmasqFile"
		fi
		echo "============================================================"
	} | input
	if [ -n "$set_p" ]; then
		printf "%b" "Pasting to paste.ee... "
		if is_installed 'curl' && is_installed 'libopenssl' && is_installed 'ca-bundle'; then
			json_init; json_add_string "description" "${packageName}-support"
			json_add_array "sections"; json_add_object '0'
			json_add_string "name" "$(uci -q get system.@system[0].hostname)"
			json_add_string "contents" "$(cat /var/${packageName}-support)"
			json_close_object; json_close_array; payload=$(json_dump)
			out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload")
			json_load "$out"; json_get_var id id; json_get_var s success
			[ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__" || printf "%b" "$__FAIL__"
			[ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
		else
			printf "%b" "$__FAIL__\\n"
			printf "%b" "$_ERROR_: curl, libopenssl or ca-bundle were not found!\\nRun 'opkg update; opkg install curl libopenssl ca-bundle' to install them.\\n"
		fi
	else
		printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n"
	fi
}

# shellcheck disable=SC2120
validate_config() {
	uci_validate_section "${packageName}" config "${1}" \
		'enabled:bool:0' \
		'verbosity:range(0,2):1' \
		'strict_enforcement:bool:1' \
		'src_ipset:bool:0' \
		'dest_ipset:string' \
		'ipv6_enabled:bool:0' \
		'supported_interface:list(string)' \
		'ignored_interface:list(string)' \
		'boot_timeout:integer:30' \
		'iptables_rule_option:or("", "append", "insert")' \
		'iprule_enabled:bool:0' \
		'webui_enable_column:bool:0' \
		'webui_protocol_column:bool:0' \
		'webui_supported_protocol:list(string)' \
		'webui_chain_column:bool:0' \
		'webui_sorting:bool:1' \
		'icmp_interface:string' \
		'wan_tid:integer:201' \
		'wan_fw_mark:hex(8)' \
		'fw_mask:hex(8)'
}

# shellcheck disable=SC2120
validate_policy() {
	uci_validate_section "${packageName}" policy "${1}" \
		'name:string' \
		'enabled:bool:0' \
		'interface:network' \
		'proto:or(string)' \
		'chain:or("", "PREROUTING", "FORWARD", "INPUT", "OUTPUT")' \
		'src_addr:list(neg(or(host,network,macaddr)))' \
		'src_port:list(neg(or(portrange, string)))' \
		'dest_addr:list(neg(host))' \
		'dest_port:list(neg(or(portrange, string)))'
}

# shellcheck disable=SC2120
validate_include() {
	uci_validate_section "${packageName}" include "${1}" \
		'path:string' \
		'enabled:bool:0'
}

Run this:
/etc/init.d/vpn-policy-routing restart
and paste here the output.

Hi. This time. The 'test' and test2' are policies I tried - Which worked when I as plugged into the router by ethernet cable, but not when I am connected by WiFi.
Thank you!

root@OpenWrt:~# /etc/init.d/vpn-policy-routing restart
Creating table 'wan/eth0.2/0.0.0.0' [✓]
Creating table 'wwan/wwan0/100.107.xxx.xxx' [✓]
Routing 'test' via wwan [✓]
Routing 'test2' via wwan [✓]
vpn-policy-routing 0.2.1-13 started with gateways:
wan/eth0.2/0.0.0.0
wwan/wwan0/100.107.xxx.xxx
vpn-policy-routing 0.2.1-13 monitoring interfaces: wan wwan .
root@OpenWrt:~#

Is the vpn interface up? I still don't see it in the list of gateways.

yes - the VPN interface is definitely up - no errors on the OpenVPN page, and IP address is from my VPN provider. If I check my IP address ('whatismyip'), I see it change from my local IP to a different country as OpenVPN starts. And it remains connected to this address, and internet working, etc.

The system log seems to show tun0 being created?

Sat Jul  4 18:44:06 2020 daemon.notice openvpn(StrongVPN)[1220]: TUN/TAP device tun0 opened
Sat Jul  4 18:44:06 2020 daemon.notice openvpn(StrongVPN)[1220]: TUN/TAP TX queue length set to 100
Sat Jul  4 18:44:06 2020 daemon.notice openvpn(StrongVPN)[1220]: /sbin/ifconfig tun0 10.8.0.14 pointopoint 10.8.0.13 mtu 1500

What is the output of:
ip -4 ro; /etc/init.d/vpn-policy-routing support

root@OpenWrt:~# ip -4 ro; /etc/init.d/vpn-policy-routing support
0.0.0.0/1 via 10.8.0.13 dev tun0
default via 100.87.xx.xxx dev wwan0 proto static src 100.87.xx.xxx
10.8.0.9 via 10.8.0.13 dev tun0 metric 1
10.8.0.13 dev tun0 proto kernel scope link src 10.8.0.14
100.87.xx.xxx/29 dev wwan0 proto kernel scope link src 100.87.xx.xxx
128.0.0.0/1 via 10.8.0.13 dev tun0
173.xxx.xxx.x via 100.87.xx.xxx dev wwan0
192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.1
vpn-policy-routing 0.2.1-13 running on OpenWrt 19.07.3. WAN (IPv4): wwan_4/dev/100.87.xx.xxx.
============================================================
Dnsmasq version 2.80  Copyright (c) 2000-2018 Simon Kelley
Compile time options: IPv6 GNU-getopt no-DBus no-i18n no-IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth DNSSEC no-ID loop-detect inotify dumpfile
============================================================
Routes/IP Rules
default         10.8.0.13       128.0.0.0       UG    0      0        0 tun0
default         100.87.xx.xxx   0.0.0.0         UG    0      0        0 wwan0
IPv4 Table 201: unreachable default
100.87.xx.xxx/29 dev wwan0 proto kernel scope link src 100.87.xx.xxx
IPv4 Table 201 Rules:
32765:  from all fwmark 0x10000/0xff0000 lookup 201
IPv4 Table 202: default via 100.87.96.155 dev wwan0
100.87.xx.xxx/29 dev wwan0 proto kernel scope link src 100.87.xx.xxx
IPv4 Table 202 Rules:
32764:  from all fwmark 0x20000/0xff0000 lookup 202
============================================================
IP Tables PREROUTING
-N VPR_PREROUTING
-A VPR_PREROUTING -s 192.168.1.98/32 -m comment --comment test2 -c 0 0 -j MARK --set-xmark 0x20000/0xff0000
-A VPR_PREROUTING -s 192.168.1.99/32 -m comment --comment test -c 0 0 -j MARK --set-xmark 0x20000/0xff0000
-A VPR_PREROUTING -m set --match-set wwan dst -c 0 0 -j MARK --set-xmark 0x20000/0xff0000
-A VPR_PREROUTING -m set --match-set wan dst -c 0 0 -j MARK --set-xmark 0x10000/0xff0000
============================================================
IP Tables FORWARD
-N VPR_FORWARD
-A VPR_FORWARD -m set --match-set wwan dst -c 0 0 -j MARK --set-xmark 0x20000/0xff0000
-A VPR_FORWARD -m set --match-set wan dst -c 0 0 -j MARK --set-xmark 0x10000/0xff0000
============================================================
IP Tables INPUT
-N VPR_INPUT
-A VPR_INPUT -m set --match-set wwan dst -c 0 0 -j MARK --set-xmark 0x20000/0xff0000
-A VPR_INPUT -m set --match-set wan dst -c 0 0 -j MARK --set-xmark 0x10000/0xff0000
============================================================
IP Tables OUTPUT
-N VPR_OUTPUT
-A VPR_OUTPUT -m set --match-set wwan dst -c 0 0 -j MARK --set-xmark 0x20000/0xff0000
-A VPR_OUTPUT -m set --match-set wan dst -c 0 0 -j MARK --set-xmark 0x10000/0xff0000
============================================================
Current ipsets
create wan hash:net family inet hashsize 1024 maxelem 65536 comment
create wwan hash:net family inet hashsize 1024 maxelem 65536 comment
============================================================
Your support details have been logged to '/var/vpn-policy-routing-support'. [✓]
root@OpenWrt:~#

Looping @stangri
Stan I am not sure why VPN-PBR is using unreachable default for the vpn table 201.
Could it be the 0.0.0.0/1 and 128.0.0.0/1 split?

@trendy, I'm afraid it's because of the WAN finding code and the fact that both wan and wwan are present on the system.

I'll have a look at the code again.

1 Like

Hi @stangri and @trendy thank you very much for looking at this.

Please let me know if there is any other data or information I can provide.

Thank you!
Andrew

@stangri and @trendy

Thank you for your help. All sorted. VPN-PBR working great!

The first bit of the solution from Stan was to renamed WWAN to sta (so as the name did not include wan).

Secondly created an interface in Luci for the VPN.

And finally the OpenVPN config I used included a kill switch (doh.....) - after turning that off, all is working as expected.

Thank you for your patient help!

1 Like

If your problem is solved, please consider marking this topic as [Solved]. See How to mark a topic as [Solved] for a short how-to.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.