From ad5ed8269ab0c5dfaa7928b4da58ec5f2dab29db Mon Sep 17 00:00:00 2001 From: Stan Grishin Date: Mon, 8 Feb 2021 17:52:42 +0000 Subject: [PATCH] vpn-policy-routing: update to version 0.3 Signed-off-by: Stan Grishin --- net/vpn-policy-routing/Makefile | 27 +- net/vpn-policy-routing/files/README.md | 891 +----------------- .../files/vpn-policy-routing.conf | 5 +- .../files/vpn-policy-routing.iface.hotplug | 6 - .../files/vpn-policy-routing.init | 723 ++++++++------ .../files/vpn-policy-routing.netflix.user | 33 +- net/vpn-policy-routing/test.sh | 3 + 7 files changed, 474 insertions(+), 1214 deletions(-) delete mode 100755 net/vpn-policy-routing/files/vpn-policy-routing.iface.hotplug create mode 100644 net/vpn-policy-routing/test.sh diff --git a/net/vpn-policy-routing/Makefile b/net/vpn-policy-routing/Makefile index b94c8e7b2..3fc0f1727 100644 --- a/net/vpn-policy-routing/Makefile +++ b/net/vpn-policy-routing/Makefile @@ -4,8 +4,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=vpn-policy-routing -PKG_VERSION:=0.2.1 -PKG_RELEASE:=14 +PKG_VERSION:=0.3.2 +PKG_RELEASE:=10 PKG_LICENSE:=GPL-3.0-or-later PKG_MAINTAINER:=Stan Grishin @@ -15,7 +15,8 @@ define Package/vpn-policy-routing SECTION:=net CATEGORY:=Network TITLE:=VPN Policy-Based Routing Service - DEPENDS:=+ipset +iptables +resolveip +kmod-ipt-ipset +iptables-mod-ipopt +!BUSYBOX_CONFIG_IP:ip-full + URL:=https://docs.openwrt.melmac.net/vpn-policy-routing/ + DEPENDS:=+jshn +ipset +iptables +resolveip +kmod-ipt-ipset +iptables-mod-ipopt +ip-full PKGARCH:=all endef @@ -28,12 +29,6 @@ define Package/vpn-policy-routing/conffiles /etc/config/vpn-policy-routing endef -define Build/Prepare - mkdir -p $(PKG_BUILD_DIR)/files/ - $(CP) ./files/vpn-policy-routing.init $(PKG_BUILD_DIR)/files/vpn-policy-routing.init - sed -i "s|^\(PKG_VERSION\).*|\1='$(PKG_VERSION)-$(PKG_RELEASE)'|" $(PKG_BUILD_DIR)/files/vpn-policy-routing.init -endef - define Build/Configure endef @@ -41,17 +36,12 @@ define Build/Compile endef define Package/vpn-policy-routing/install - $(INSTALL_DIR) $(1)/etc/init.d - $(INSTALL_BIN) $(PKG_BUILD_DIR)/files/vpn-policy-routing.init $(1)/etc/init.d/vpn-policy-routing - $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/etc/hotplug.d/firewall $(1)/etc/ + $(INSTALL_BIN) ./files/vpn-policy-routing.init $(1)/etc/init.d/vpn-policy-routing + $(SED) "s|^\(PKG_VERSION\).*|\1='$(PKG_VERSION)-$(PKG_RELEASE)'|" $(1)/etc/init.d/vpn-policy-routing $(INSTALL_CONF) ./files/vpn-policy-routing.conf $(1)/etc/config/vpn-policy-routing - $(INSTALL_DIR) $(1)/etc/hotplug.d/firewall $(INSTALL_DATA) ./files/vpn-policy-routing.firewall.hotplug $(1)/etc/hotplug.d/firewall/99-vpn-policy-routing - $(INSTALL_DIR) $(1)/etc/hotplug.d/iface - $(INSTALL_DATA) ./files/vpn-policy-routing.iface.hotplug $(1)/etc/hotplug.d/iface/70-vpn-policy-routing - $(INSTALL_DIR) $(1)/etc/ $(INSTALL_DATA) ./files/vpn-policy-routing.aws.user $(1)/etc/vpn-policy-routing.aws.user - $(INSTALL_DIR) $(1)/etc/ $(INSTALL_DATA) ./files/vpn-policy-routing.netflix.user $(1)/etc/vpn-policy-routing.netflix.user endef @@ -60,9 +50,6 @@ define Package/vpn-policy-routing/postinst # check if we are on real system if [ -z "$${IPKG_INSTROOT}" ]; then /etc/init.d/vpn-policy-routing enable - if ! /bin/ubus -S call system board | /bin/grep 'Turris' | /bin/grep -q '15.05' ; then - rm -rf /etc/hotplug.d/iface/70-vpn-policy-routing - fi fi exit 0 endef diff --git a/net/vpn-policy-routing/files/README.md b/net/vpn-policy-routing/files/README.md index 8bb8304cf..98d7efc62 100644 --- a/net/vpn-policy-routing/files/README.md +++ b/net/vpn-policy-routing/files/README.md @@ -1,890 +1,3 @@ -# VPN Policy-Based Routing +# README -## Description - -This service allows you to define rules (policies) for routing traffic via WAN or your L2TP, Openconnect, OpenVPN, PPTP or Wireguard tunnels. Policies can be set based on any combination of local/remote ports, local/remote IPv4 or IPv6 addresses/subnets or domains. This service supersedes the ```VPN Bypass``` available on [GitHub](https://github.com/openwrt/packages/blob/master/net/vpnbypass/files/README.md)/[jsDelivr](https://cdn.jsdelivr.net/gh/openwrt/packages@master/net/vpnbypass/files/README.md) service, by supporting IPv6 and by allowing you to set explicit rules not just for WAN interface (bypassing OpenVPN tunnel), but for L2TP, Openconnect, OpenVPN, PPTP and Wireguard tunnels as well. - -## Features - -### Gateways/Tunnels - -- Any policy can target either WAN or a VPN tunnel interface. -- L2TP tunnels supported (with protocol names l2tp\*). -- Openconnect tunnels supported (with protocol names openconnect\*). -- OpenVPN tunnels supported (with device names tun\* or tap\*).[1](#footnote1) [2](#footnote2) -- PPTP tunnels supported (with protocol names pptp\*). -- Wireguard tunnels supported (with protocol names wireguard\*). - -### IPv4/IPv6/Port-Based Policies - -- Policies based on local names, IPs or subnets. You can specify a single IP (as in ```192.168.1.70```) or a local subnet (as in ```192.168.1.81/29```) or a local device name (as in ```nexusplayer```). IPv6 addresses are also supported. -- Policies based on local ports numbers. Can be set as an individual port number (```32400```), a range (```5060-5061```), a space-separated list (```80 8080```) or a combination of the above (```80 8080 5060-5061```). Limited to 15 space-separated entries per policy. -- Policies based on remote IPs/subnets or domain names. Same format/syntax as local IPs/subnets. -- Policies based on remote ports numbers. Same format/syntax and restrictions as local ports. -- You can mix the IP addresses/subnets and device (or domain) names in one field separating them by space (like this: ```66.220.2.74 he.net tunnelbroker.net```). -- See [Policy Options](#policy-options) section for more information. - -### Domain-Based Policies - -- Policies based on (remote) domain names can be processed in different ways, please review the [Policy Options](#policy-options) section and [Footnotes/Known Issues](#footnotesknown-issues) section, specifically [#5](#footnote5) and any other information in that section relevant to domain-based routing/DNS. - -### Physical Device Policies - -- Policies based on a local physical device (like a specially created wlan), please review the [Policy Options](#policy-options) section and [Footnotes/Known Issues](#footnotesknown-issues) section, specifically [#6](#footnote6) and any other information in that section relevant to handling physical device. - -### DSCP Tag-Based Policies - -You can also set policies for traffic with specific DSCP tag. On Windows 10, for example, you can mark traffic from specific apps with DSCP tags (instructions for tagging specific app traffic in Windows 10 can be found [here](http://serverfault.com/questions/769843/cannot-set-dscp-on-windows-10-pro-via-group-policy)). - -### Custom User Files - -If the custom user file includes are set, the service will load and execute them after setting up ip tables and ipsets and processing policies. This allows, for example, to add large numbers of domains/IP addresses to ipsets without manually adding all of them to the config file. - -Two example custom user-files are provided: ```/etc/vpn-policy-routing.aws.user``` and ```/etc/vpn-policy-routing.netflix.user```. They are provided to pull the AWS and Netflix IP addresses into the ```wan``` ipset respectively. - -### Strict Enforcement - -- Supports strict policy enforcement, even if the policy interface is down -- resulting in network being unreachable for specific policy (enabled by default). - -### Use DNSMASQ ipset - -- Service can be set to utilize ```dnsmasq```'s ```ipset``` support, which requires the ```dnsmasq-full``` package to be installed (see [How to install dnsmasq-full](#how-to-install-dnsmasq-full)). This significantly improves the start up time because ```dnsmasq``` resolves the domain names and adds them to appropriate ```ipset``` in background. Another benefit of using ```dnsmasq```'s ```ipset``` is that it also automatically adds third-level domains to the ```ipset```: if ```domain.com``` is added to the policy, this policy will affect all ```*.domain.com``` subdomains. This also works for top-level domains as well, a policy targeting the ```at``` for example, will affect all the ```*.at``` domains. -- Please review the [Footnotes/Known Issues](#footnotesknown-issues) section, specifically [#5](#footnote5) and any other information in that section relevant to domain-based routing/DNS. - -### Customization - -- Can be fully configured with ```uci``` commands or by editing ```/etc/config/vpn-policy-routing``` file. -- Has a companion package (```luci-app-vpn-policy-routing```) so policies can be configured with Web UI. - -### Other Features - -- Doesn't stay in memory, creates the routing tables and ```iptables``` rules/```ipset``` entries which are automatically updated when supported/monitored interface changes. -- Proudly made in :maple_leaf: Canada :maple_leaf: , using locally-sourced electrons. - -## Screenshots (luci-app-vpn-policy-routing) - -Service Status -![screenshot](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/screenshots/vpn-policy-routing/screenshot04-status.png "Service Status") - -Configuration - Basic Configuration -![screenshot](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/screenshots/vpn-policy-routing/screenshot04-config-basic.png "Basic Configuration") - -Configuration - Advanced Configuration -![screenshot](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/screenshots/vpn-policy-routing/screenshot04-config-advanced.png "Advanced Configuration") - -Configuration - WebUI Configuration -![screenshot](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/screenshots/vpn-policy-routing/screenshot04-config-webui.png "WebUI Configuration") - -Policies -![screenshot](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/screenshots/vpn-policy-routing/screenshot04-policies.png "Policies") - -DSCP Tagging -![screenshot](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/screenshots/vpn-policy-routing/screenshot04-dscp.png "DSCP Tagging") - -Custom User File Includes -![screenshot](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/screenshots/vpn-policy-routing/screenshot04-userfiles.png "Custom User File Includes") - -## How It Works - -On start, this service creates routing tables for each supported interface (WAN/WAN6 and VPN tunnels) which are used to route specially marked packets. For the ```mangle``` table's ```PREROUTING```, ```FORWARD```, ```INPUT``` and ```OUTPUT``` chains, the service creates corresponding ```VPR_*``` chains to which policies are assigned. Evaluation and marking of packets happen in these ```VPR_*``` chains. If enabled, the service also creates the remote/local ipsets per each supported interface and the corresponding ```iptables``` rule for marking packets matching the ```ipset```. The service then processes the user-created policies. - -### Processing Policies - -Each policy can result in either a new ```iptables``` rule or, if ```src_ipset``` or ```dest_ipset``` are enabled, an ```ipset``` or a ```dnsmasq```'s ```ipset``` entry. - -- Policies with local MAC-addresses, IP addresses or local device names can be created as ```iptables``` rules or ```ipset``` entries. -- Policies with local or remote ports are always created as ```iptables``` rules. -- Policies with local or remote netmasks can be created as ```iptables``` rules or ```ipset``` entries. -- Policies with **only** remote IP address or a domain name can be created as ```iptables``` rules or ```dnsmasq```'s ```ipset``` or an ```ipset``` (if enabled). - -### Policies Priorities - -- If support for ```src_ipset``` and ```dest_ipset``` is disabled, then only ```iptables``` rules are created. The policy priority is the same as its order as listed in Web UI and ```/etc/config/vpn-policy-routing```. The higher the policy is in the Web UI and configuration file, the higher its priority is. -- If support for ```src_ipset``` and ```dest_ipset``` is enabled, then the ```ipset``` entries have the highest priority (irrelevant of their position in the policies list) and the other policies are processed in the same order as they are listed in Web UI and ```/etc/config/vpn-policy-routing```. -- If there are conflicting ```ipset``` entries for different interfaces, the priority is given to the interface which is listed first in the ```/etc/config/network``` file. -- If set, the ```DSCP``` policies trump all other policies, including the ```ipset``` ones. - -## How To Install - -Please make sure that the [requirements](#requirements) are satisfied and install ```vpn-policy-routing``` and ```luci-app-vpn-policy-routing``` from Web UI or connect to your router via ssh and run the following commands: - -```sh -opkg update -opkg install vpn-policy-routing luci-app-vpn-policy-routing -``` - -If these packages are not found in the official feed/repo for your version of OpenWrt/LEDE Project, you will need to add a custom repo to your router following instructions on [GitHub](https://github.com/stangri/openwrt_packages/blob/master/README.md#on-your-router)/[jsDelivr](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/README.md#on-your-router) first. - -These packages have been designed to be backwards compatible with OpenWrt 19.07, OpenWrt 18.06, LEDE Project 17.01 and OpenWrt 15.05. However, on systems older than OpenWrt 18.06.6 and/or a system which has deviated too far (or haven't been updated to keep in-sync) with official OpenWrt release you may get a message about missing ```luci-compat``` dependency, which (and only which) you can safely ignore and force-install the luci app using ```opkg install --force-depends``` command instead of ```opkg install```. - -### Requirements - -This service requires the following packages to be installed on your router: ```ipset```, ```resolveip```, ```ip-full``` (or a ```busybox``` built with ```ip``` support), ```kmod-ipt-ipset``` and ```iptables```. - -To satisfy the requirements, connect to your router via ssh and run the following commands: - -```sh -opkg update; opkg install ipset resolveip ip-full kmod-ipt-ipset iptables -``` - -### How to install dnsmasq-full - -If you want to use ```dnsmasq```'s ```ipset``` support, you will need to install ```dnsmasq-full``` instead of the ```dnsmasq```. To do that, connect to your router via ssh and run the following command: - -```sh -opkg update; opkg remove dnsmasq; opkg install dnsmasq-full; -``` - -### Unmet dependencies - -If you are running a development (trunk/snapshot) build of OpenWrt on your router and your build is outdated (meaning that packages of the same revision/commit hash are no longer available and when you try to satisfy the [requirements](#requirements) you get errors), please flash either current OpenWrt release image or current development/snapshot image. - -## Service Configuration Settings - -As per screenshots above, in the Web UI the ```vpn-policy-routing``` configuration is split into ```Basic```, ```Advanced``` and ```WebUI``` settings. The full list of configuration parameters of ```vpn-policy-routing.config``` section is: - -|Web UI Section|Parameter|Type|Default|Description| -| --- | --- | --- | --- | --- | -|Basic|enabled|boolean|0|Enable/disable the ```vpn-policy-routing``` service.| -|Basic|verbosity|integer|2|Can be set to 0, 1 or 2 to control the console and system log output verbosity of the ```vpn-policy-routing``` service.| -|Basic|strict_enforcement|boolean|1|Enforce policies when their interface is down. See [Strict enforcement](#strict-enforcement) for more details.| -|Basic|dest_ipset|string|none|Enable/disable use of one of the ipset options for compatible remote policies (policies with only a remote hostname and no other fields set). This speeds up service start-up and operation. Currently supported options are ```none```, ```ipset``` and ```dnsmasq.ipset``` (see [Use DNSMASQ ipset](#use-dnsmasq-ipset) for more details). Make sure the [requirements](#requirements) are met.| -|Basic|src_ipset|boolean|0|Enable/disable use of ```ipset``` entries for compatible local policies (policies with only a local IP address or MAC address and no other fields set). Using ```ipset``` for local IPs/MACs is faster than using ```iptables``` rules, however it makes it impossible to enforce policies priority/order. Make sure the [requirements](#requirements) are met.| -|Basic|ipv6_enabled|boolean|0|Enable/disable IPv6 support.| -|Advanced|supported_interface|list/string||Allows to specify the space-separated list of interface names (in lower case) to be explicitly supported by the ```vpn-policy-routing``` service. Can be useful if your OpenVPN tunnels have dev option other than tun\* or tap\*.| -|Advanced|ignored_interface|list/string||Allows to specify the space-separated list of interface names (in lower case) to be ignored by the ```vpn-policy-routing``` service. Can be useful if running both VPN server and VPN client on the router.| -|Advanced|boot_timeout|number|30|Allows to specify the time (in seconds) for ```vpn-policy-routing``` service to wait for WAN gateway discovery on boot. Can be useful on devices with ADSL modem built in.| -|Advanced|iptables_rule_option|append/insert|append|Allows to specify the iptables parameter for rules: ```-A``` for ```append``` and ```-I``` for ```insert```. Append is generally speaking more compatible with other packages/firewall rules. Recommended to change to ```insert``` only to enable compatibility with the ```mwan3``` package.| -|Advanced|iprule_enabled|boolean|0|Add an ```ip rule```, not an ```iptables``` entry for policies with just the local address. Use with caution to manipulate policies priorities.| -|Advanced|icmp_interface|string||Set the default ICMP protocol interface (interface name in lower case). Use with caution.| -|Advanced|append_src_rules|string||Append local IP Tables rules. Can be used to exclude local IP addresses from destinations for policies with local address set.| -|Advanced|append_dest_rules|string||Append local IP Tables rules. Can be used to exclude remote IP addresses from sources for policies with remote address set.| -|Advanced|wan_tid|integer|201|Starting (WAN) Table ID number for tables created by the ```vpn-policy-routing``` service.| -|Advanced|wan_mark|hexadecimal|0x010000|Starting (WAN) fw mark for marks used by the ```vpn-policy-routing``` service. High starting mark is used to avoid conflict with SQM/QoS, this can be changed by user. Change with caution together with ```fw_mask```.| -|Advanced|fw_mask|hexadecimal|0xff0000|FW Mask used by the ```vpn-policy-routing``` service. High mask is used to avoid conflict with SQM/QoS, this can be changed by user. Change with caution together with ```wan_mark```.| -|Web UI|webui_enable_column|boolean|0|Shows ```Enable``` checkbox column for policies, allowing to quickly enable/disable specific policy without deleting it.| -|Web UI|webui_protocol_column|boolean|0|Shows ```Protocol``` column for policies, allowing to specify the protocol for ```iptables``` rules for policies.| -|Web UI|webui_supported_protocol|list|0|List of protocols to display in the ```Protocol``` column for policies.| -|Web UI|webui_chain_column|boolean|0|Shows ```Chain``` column for policies, allowing to specify ```PREROUTING``` (default), ```FORWARD```, ```INPUT```, or ```OUTPUT``` chain for ```iptables``` rules for policies.| -|Web UI|webui_sorting|boolean|1|Shows the Up/Down buttons for policies, allowing you to move a policy up or down in the list/priority.| -||wan_dscp|integer||Allows use of [DSCP-tag based policies](#dscp-tag-based-policies) for WAN interface.| -||{interface_name}_dscp|integer||Allows use of [DSCP-tag based policies](#dscp-tag-based-policies) for a VPN interface.| - -### Default Settings - -Default configuration has service disabled (use Web UI to enable/start service or run ```uci set vpn-policy-routing.config.enabled=1; uci commit vpn-policy-routing;```). - -### Policy Options - -Each policy may have a combination of the options below, the ```name``` and ```interface``` options are required. - -The ```src_addr```, ```src_port```, ```dest_addr``` and ```dest_port``` options supports parameter negation, for example if you want to **exclude** remote port 80 from the policy, set ```dest_port="!80"``` (notice lack of space between ```!``` and parameter). - -|Option|Default|Description| -| --- | --- | --- | -|**name**||Policy name, it **must** be set.| -|enabled|1|Enable/disable policy. To display the ```Enable``` checkbox column for policies in the WebUI, make sure to select ```Enabled``` for ```Show Enable Column``` in the ```Web UI``` tab.| -|**interface**||Policy interface, it **must** be set.| -|src_addr||List of space-separated local/source IP addresses, CIDRs, hostnames or mac addresses (colon-separated). You can also specify a local physical device (like a specially created wlan) prepended by an ```@``` symbol.| -|src_port||List of space-separated local/source ports or port-ranges.| -|dest_addr||List of space-separated remote/target IP addresses, CIDRs or hostnames/domain names.| -|dest_port||List of space-separated remote/target ports or port-ranges.| -|proto|auto|Policy protocol, can be any valid protocol from ```/etc/protocols``` for CLI/uci or can be selected from the values set in ```webui_supported_protocol```. To display the ```Protocol``` column for policies in the WebUI, make sure to select ```Enabled``` for ```Show Protocol Column``` in the ```Web UI``` tab.
Special cases: ```auto``` will try to intelligently insert protocol-agnostic policy and fall back to TCP/UDP if the protocol must be selected for specific policy; ```all``` will always insert a protocol-agnostic policy (which may fail depending on the policy).| -|chain|PREROUTING|Policy chain, can be either ```PREROUTING```, ```FORWARDING```, ```INPUT``` or ```OUTPUT```. This setting is case-sensitive. To display the ```Chain``` column for policies in the WebUI, make sure to select ```Enabled``` for ```Show Chain Column``` in the ```Web UI``` tab.| - -### Custom User Files Include Options - -|Option|Default|Description| -| --- | --- | --- | -|**path**||Path to a custom user file (in a form of shell script), it **must** be set.| -|enabled|1|Enable/disable setting.| - -### Example Policies - -#### Single IP, IP Range, Local Machine, Local MAC Address - -The following policies route traffic from a single IP address, a range of IP addresses, a local machine (requires definition as DHCP host record in DHCP config), a MAC-address of a local device and finally all of the above via WAN. - -```text -config policy - option name 'Local IP' - option interface 'wan' - option src_addr '192.168.1.70' - -config policy - option name 'Local Subnet' - option interface 'wan' - option src_addr '192.168.1.81/29' - -config policy - option name 'Local Machine' - option interface 'wan' - option src_addr 'dell-ubuntu' - -config policy - option name 'Local MAC Address' - option interface 'wan' - option src_addr '00:0F:EA:91:04:08' - -config policy - option name 'Local Devices' - option interface 'wan' - option src_addr '192.168.1.70 192.168.1.81/29 dell-ubuntu 00:0F:EA:91:04:08' - -``` - -#### Logmein Hamachi - -The following policy routes LogMeIn Hamachi zero-setup VPN traffic via WAN. - -```text -config policy - option name 'LogmeIn Hamachi' - option interface 'wan' - option dest_addr '25.0.0.0/8 hamachi.cc hamachi.com logmein.com' -``` - -#### SIP Port - -The following policy routes standard SIP port traffic via WAN for both TCP and UDP protocols. - -```text -config policy - option name 'SIP Ports' - option interface 'wan' - option dest_port '5060' - option proto 'tcp udp' -``` - -#### Plex Media Server - -The following policies route Plex Media Server traffic via WAN. Please note, you'd still need to open the port in the firewall either manually or with the UPnP. - -```text -config policy - option name 'Plex Local Server' - option interface 'wan' - option src_port '32400' - -config policy - option name 'Plex Remote Servers' - option interface 'wan' - option dest_addr 'plex.tv my.plexapp.com' -``` - -#### Emby Media Server - -The following policy route Emby traffic via WAN. Please note, you'd still need to open the port in the firewall either manually or with the UPnP. - -```text -config policy - option name 'Emby Local Server' - option interface 'wan' - option src_port '8096 8920' - -config policy - option name 'Emby Remote Servers' - option interface 'wan' - option dest_addr 'emby.media app.emby.media tv.emby.media' -``` - -#### Local OpenVPN Server + OpenVPN Client (Scenario 1) - -If the OpenVPN client on your router is used as default routing (for the whole internet), make sure your settings are as following (three dots on the line imply other options can be listed in the section as well). - -Relevant part of ```/etc/config/vpn-policy-routing```: - -```text -config vpn-policy-routing 'config' - list ignored_interface 'vpnserver' - ... - -config policy - option name 'OpenVPN Server' - option interface 'wan' - option proto 'tcp' - option src_port '1194' - option chain 'OUTPUT' -``` - -The network/firewall/openvpn settings are below. - -Relevant part of ```/etc/config/network``` (**DO NOT** modify default OpenWrt network settings for neither ```wan``` nor ```lan```): - -```text -config interface 'vpnclient' - option proto 'none' - option ifname 'ovpnc0' - -config interface 'vpnserver' - option proto 'none' - option ifname 'ovpns0' - option auto '1' -``` - -Relevant part of ```/etc/config/firewall``` (**DO NOT** modify default OpenWrt firewall settings for neither ```wan``` nor ```lan```): - -```text -config zone - option name 'vpnclient' - option network 'vpnclient' - option input 'REJECT' - option forward 'ACCEPT' - option output 'REJECT' - option masq '1' - option mtu_fix '1' - -config forwarding - option src 'lan' - option dest 'vpnclient' - -config zone - option name 'vpnserver' - option network 'vpnserver' - option input 'ACCEPT' - option forward 'REJECT' - option output 'ACCEPT' - option masq '1' - -config forwarding - option src 'vpnserver' - option dest 'wan' - -config forwarding - option src 'vpnserver' - option dest 'lan' - -config forwarding - option src 'vpnserver' - option dest 'vpnclient' - -config rule - option name 'Allow-OpenVPN-Inbound' - option target 'ACCEPT' - option src '*' - option proto 'tcp' - option dest_port '1194' -``` - -Relevant part of ```/etc/config/openvpn```: - -```text -config openvpn 'vpnclient' - option client '1' - option dev_type 'tun' - option dev 'ovpnc0' - option proto 'udp' - option remote 'some.domain.com 1197' # DO NOT USE PORT 1194 for VPN Client - ... - -config openvpn 'vpnserver' - option port '1194' - option proto 'tcp' - option server '192.168.200.0 255.255.255.0' - ... -``` - -#### Local OpenVPN Server + OpenVPN Client (Scenario 2) - -If the OpenVPN client is **not** used as default routing and you create policies to selectively use the OpenVPN client, make sure your settings are as following (three dots on the line imply other options can be listed in the section as well). - -Relevant part of ```/etc/config/vpn-policy-routing```: - -```text -config vpn-policy-routing 'config' - list ignored_interface 'vpnserver' - option append_src_rules '! -d 192.168.200.0/24' - ... -``` - -The network/firewall/openvpn settings are below. - -Relevant part of ```/etc/config/network``` (**DO NOT** modify default OpenWrt network settings for neither ```wan``` nor ```lan```): - -```text -config interface 'vpnclient' - option proto 'none' - option ifname 'ovpnc0' - -config interface 'vpnserver' - option proto 'none' - option ifname 'ovpns0' - option auto '1' -``` - -Relevant part of ```/etc/config/firewall``` (**DO NOT** modify default OpenWrt firewall settings for neither ```wan``` nor ```lan```): - -```text -config zone - option name 'vpnclient' - option network 'vpnclient' - option input 'REJECT' - option forward 'ACCEPT' - option output 'REJECT' - option masq '1' - option mtu_fix '1' - -config forwarding - option src 'lan' - option dest 'vpnclient' - -config zone - option name 'vpnserver' - option network 'vpnserver' - option input 'ACCEPT' - option forward 'REJECT' - option output 'ACCEPT' - option masq '1' - -config forwarding - option src 'vpnserver' - option dest 'wan' - -config forwarding - option src 'vpnserver' - option dest 'lan' - -config forwarding - option src 'vpnserver' - option dest 'vpnclient' - -config rule - option name 'Allow-OpenVPN-Inbound' - option target 'ACCEPT' - option src '*' - option proto 'tcp' - option dest_port '1194' -``` - -Relevant part of ```/etc/config/openvpn```: - -```text -config openvpn 'vpnclient' - option client '1' - option dev_type 'tun' - option dev 'ovpnc0' - option proto 'udp' - option remote 'some.domain.com 1197' # DO NOT USE PORT 1194 for VPN Client - list pull_filter 'ignore "redirect-gateway"' # for OpenVPN 2.4 and later - option route_nopull '1' # for OpenVPN earlier than 2.4 - ... - -config openvpn 'vpnserver' - option port '1194' - option proto 'tcp' - option server '192.168.200.0 255.255.255.0' - ... -``` - -#### Local Wireguard Server + Wireguard Client (Scenario 1) - -Yes, I'm aware that technically there are no clients nor servers in Wireguard, it's all peers, but for the sake of README readability I will use the terminology similar to the OpenVPN Server + Client setups. - -If the Wireguard tunnel on your router is used as default routing (for the whole internet), make sure your settings are as following (three dots on the line imply other options can be listed in the section as well). - -Relevant part of ```/etc/config/vpn-policy-routing```: - -```text -config vpn-policy-routing 'config' - list ignored_interface 'wgserver' - ... - -config policy - option name 'Wireguard Server' - option interface 'wan' - option proto 'udp' - option src_port '61820' - option chain 'OUTPUT' -``` - -The recommended network/firewall settings are below. - -Relevant part of ```/etc/config/network``` (**DO NOT** modify default OpenWrt network settings for neither ```wan``` nor ```lan```): - -```text -config interface 'wgclient' - option proto 'wireguard' - ... - -config wireguard_wgclient - list allowed_ips '0.0.0.0/0' - list allowed_ips '::0/0' - option endpoint_port '51820' - option route_allowed_ips '1' - ... - -config interface 'wgserver' - option proto 'wireguard' - option listen_port '61820' - list addresses '192.168.200.1' - ... - -config wireguard_wgserver - list allowed_ips '192.168.200.2/32' - option route_allowed_ips '1' - ... -``` - -Relevant part of ```/etc/config/firewall``` (**DO NOT** modify default OpenWrt firewall settings for neither ```wan``` nor ```lan```): - -```text -config zone - option name 'wgclient' - option network 'wgclient' - option input 'REJECT' - option forward 'ACCEPT' - option output 'REJECT' - option masq '1' - option mtu_fix '1' - -config forwarding - option src 'lan' - option dest 'wgclient' - -config zone - option name 'wgserver' - option network 'wgserver' - option input 'ACCEPT' - option forward 'REJECT' - option output 'ACCEPT' - option masq '1' - -config forwarding - option src 'wgserver' - option dest 'wan' - -config forwarding - option src 'wgserver' - option dest 'lan' - -config forwarding - option src 'wgserver' - option dest 'wgclient' - -config rule - option name 'Allow-WG-Inbound' - option target 'ACCEPT' - option src '*' - option proto 'udp' - option dest_port '61820' -``` - -#### Local Wireguard Server + Wireguard Client (Scenario 2) - -Yes, I'm aware that technically there are no clients nor servers in Wireguard, it's all peers, but for the sake of README readability I will use the terminology similar to the OpenVPN Server + Client setups. - -If the Wireguard client is **not** used as default routing and you create policies to selectively use the Wireguard client, make sure your settings are as following (three dots on the line imply other options can be listed in the section as well). - -Relevant part of ```/etc/config/vpn-policy-routing```: - -```text -config vpn-policy-routing 'config' - list ignored_interface 'wgserver' - option append_src_rules '! -d 192.168.200.0/24' - ... -``` - -The recommended network/firewall settings are below. - -Relevant part of ```/etc/config/network``` (**DO NOT** modify default OpenWrt network settings for neither ```wan``` nor ```lan```): - -```text -config interface 'wgclient' - option proto 'wireguard' - ... - -config wireguard_wgclient - list allowed_ips '0.0.0.0/0' - list allowed_ips '::0/0' - option endpoint_port '51820' - ... - -config interface 'wgserver' - option proto 'wireguard' - option listen_port '61820' - list addresses '192.168.200.1/24' - ... - -config wireguard_wgserver - list allowed_ips '192.168.200.2/32' - option route_allowed_ips '1' - ... -``` - -Relevant part of ```/etc/config/firewall``` (**DO NOT** modify default OpenWrt firewall settings for neither ```wan``` nor ```lan```): - -```text -config zone - option name 'wgclient' - option network 'wgclient' - option input 'REJECT' - option forward 'ACCEPT' - option output 'REJECT' - option masq '1' - option mtu_fix '1' - -config forwarding - option src 'lan' - option dest 'wgclient' - -config zone - option name 'wgserver' - option network 'wgserver' - option input 'ACCEPT' - option forward 'REJECT' - option output 'ACCEPT' - option masq '1' - -config forwarding - option src 'wgserver' - option dest 'wan' - -config forwarding - option src 'wgserver' - option dest 'lan' - -config forwarding - option src 'wgserver' - option dest 'wgclient' - -config rule - option name 'Allow-WG-Inbound' - option target 'ACCEPT' - option src '*' - option proto 'udp' - option dest_port '61820' -``` - -#### Netflix Domains - -The following policy should route US Netflix traffic via WAN. For capturing international Netflix domain names, you can refer to the getdomainnames.sh-specific instructions on [GitHub](https://github.com/Xentrk/netflix-vpn-bypass/blob/master/README.md#ipset_netflix_domainssh)/[jsDelivr](https://cdn.jsdelivr.net/gh/Xentrk/openwrt_packages@master/netflix-vpn-bypass/README.md#ipset_netflix_domainssh) and don't forget to adjust them for OpenWrt. This may not work if Netflix changes things. For more reliable US Netflix routing you may want to consider using [custom user files](#custom-user-files). - -```text -config policy - option name 'Netflix Domains' - option interface 'wan' - option dest_addr 'amazonaws.com netflix.com nflxext.com nflximg.net nflxso.net nflxvideo.net dvd.netflix.com' -``` - -#### Example Custom User Files Includes - -```text -config include - option path '/etc/vpn-policy-routing.netflix.user' - -config include - option path '/etc/vpn-policy-routing.aws.user' -``` - -#### Basic OpenVPN Client Config - -There are multiple guides online on how to configure the OpenVPN client on OpenWrt "the easy way", and they usually result either in a kill-switch configuration or configuration where the OpenVPN tunnel cannot be properly (and separately from WAN) routed, either way, incompatible with the VPN Policy-Based Routing. - -Below is the sample OpenVPN client configuration for OpenWrt which is guaranteed to work. If you have already deviated from the instructions below (ie: made any changes to any of the ```wan``` or ```lan``` configurations in either ```/etc/config/network``` or ```/etc/config/firewall```), you will need to start from scratch with a fresh OpenWrt install. - -Relevant part of ```/etc/config/vpn-policy-routing```: - -```text -config vpn-policy-routing 'config' - list supported_interface 'vpnc' - ... -``` - -The recommended network/firewall settings are below. - -Relevant part of ```/etc/config/network``` (**DO NOT** modify default OpenWrt network settings for neither ```wan``` nor ```lan```): - -```text -config interface 'vpnc' - option proto 'none' - option ifname 'ovpnc0' -``` - -Relevant part of ```/etc/config/firewall``` (**DO NOT** modify default OpenWrt firewall settings for neither ```wan``` nor ```lan```): - -```text -config zone - option name 'vpnc' - option network 'vpnc' - option input 'REJECT' - option forward 'REJECT' - option output 'ACCEPT' - option masq '1' - option mtu_fix '1' - -config forwarding - option src 'lan' - option dest 'vpnc' -``` - -If you have a Guest Network, add the following to the ```/etc/config/firewall```: - -```text -config forwarding - option src 'guest' - option dest 'vpnc' -``` - -Relevant part of ```/etc/config/openvpn``` (configure the rest of the client connection for your specifics by either referring to an existing ```.ovpn``` file or thru the OpenWrt uci settings): - -```text -config openvpn 'vpnc' - option enabled '1' - option client '1' - option dev_type 'tun' - option dev 'ovpnc0' - ... -``` - -## Footnotes/Known Issues - -1. See [note about multiple OpenVPN clients](#multiple-openvpn-clients). - -2. If your ```OpenVPN``` interface has the device name different from tun\* or tap\*, is not up and is not explicitly listed in ```supported_interface``` option, it may not be available in the policies ```Interface``` drop-down within WebUI. - -3. If your default routing is set to the VPN tunnel, then the true WAN interface cannot be discovered using OpenWrt built-in functions, so service will assume your network interface ending with or starting with ```wan``` is the true WAN interface. - -4. The service does **NOT** support the "killswitch" router mode (where if you stop the VPN tunnel, you have no internet connection). For proper operation, leave all the default OpenWrt ```network``` and ```firewall``` settings for ```lan``` and ```wan``` intact. - -5. When using the ```dnsmasq.ipset``` option, please make sure to flush the DNS cache of the local devices, otherwise domain policies may not work until you do. If you're not sure how to flush the DNS cache (or if the device/OS doesn't offer an option to flush its DNS cache), reboot your local devices when starting to use the service and/or when connecting data-capable device to your WiFi. - -6. When using the policies targeting physical devices, make sure you have the following packages installed: ```kmod-br-netfilter```, ```kmod-ipt-physdev``` and ```iptables-mod-physdev```. Also, if your physical device is a part of the bridge, you may have to set ```net.bridge.bridge-nf-call-iptables``` to `1` in your ```/etc/sysctl.conf```. - -### First Troubleshooting Step - -If your router is set to use [default routing via VPN tunnel](#a-word-about-default-routing) and the WAN-targeting policies do not work, you need to stop your VPN tunnel first and ensure that you still have internet connection. If your router is set up to use the default routing via VPN tunnel and when you stop the VPN tunnel you have no internet connection, this package can't help you. You first need to make sure that you do have internet connection when the VPN tunnel is stopped. - -### Multiple OpenVPN Clients - -If you use multiple OpenVPN clients on your router, the order in which their devices are named (tun0, tun1, etc) is not guaranteed by OpenWrt/LEDE Project. The following settings are recommended in this case. - -For ```/etc/config/network```: - -```text -config interface 'vpnclient0' - option proto 'none' - option ifname 'ovpnc0' - -config interface 'vpnclient1' - option proto 'none' - option ifname 'ovpnc1' -``` - -For ```/etc/config/openvpn```: - -```text -config openvpn 'vpnclient0' - option client '1' - option dev_type 'tun' - option dev 'ovpnc0' - ... - -config openvpn 'vpnclient1' - option client '1' - option dev_type 'tun' - option dev 'ovpnc1' - ... -``` - -For ```/etc/config/vpn-policy-routing```: - -```text -config vpn-policy-routing 'config' - list supported_interface 'vpnclient0 vpnclient1' - ... -``` - -### A Word About Default Routing - -Service does not alter the default routing. Depending on your VPN tunnel settings (and settings of the VPN server you are connecting to), the default routing might be set to go via WAN or via VPN tunnel. This service affects only routing of the traffic matching the policies. If you want to override default routing, follow the instructions below. - -#### OpenVPN tunnel configured via uci (/etc/config/openvpn) - -Set the following to the appropriate section of your ```/etc/config/openvpn```: - -- For OpenVPN 2.4 and newer client config: - - ```text - list pull_filter 'ignore "redirect-gateway"' - ``` - -- For OpenVPN 2.3 and older client config: - - ```text - option route_nopull '1' - ``` - -- For your Wireguard (client) config: - - ```text - option route_allowed_ips '0' - ``` - -#### OpenVPN tunnel configured with .ovpn file - -Set the following to the appropriate section of your ```.ovpn``` file: - -- For OpenVPN 2.4 and newer client ```.ovpn``` file: - - ```text - pull-filter ignore "redirect-gateway" - ``` - -- For OpenVPN 2.3 and older client ```.ovpn``` file: - - ```text - route-nopull "1" - ``` - -#### Wireguard tunnel - -- For your Wireguard (client) config: - - ```text - option route_allowed_ips '0' - ``` - -- Routing Wireguard traffic may require setting `net.ipv4.conf.wg0.rp_filter = 2` in `/etc/sysctl.conf`. Please refer to [issue #41](https://github.com/stangri/openwrt_packages/issues/41) for more details. - -### A Word About HTTP/3 (QUICK) - -If you want to target traffic using HTTP/3 protocol, you can use the ```AUTO``` as the protocol (the policy will be either protocol-agnostic or ```TCP/UDP```) or explicitly use ```UDP``` as a protocol. - -### A Word About DNS-over-HTTPS - -Some browsers, like [Mozilla Firefox](https://support.mozilla.org/en-US/kb/firefox-dns-over-https#w_about-dns-over-https) or [Google Chrome/Chromium](https://blog.chromium.org/2019/09/experimenting-with-same-provider-dns.html) have [DNS-over-HTTPS proxy](https://en.wikipedia.org/wiki/DNS_over_HTTPS) built-in. Their requests to web-sites cannot be affected if the ```dnsmasq.ipset``` is set for the ```dest_ipset``` option. To fix this, you can try either of the following: - - 1. Disable the DNS-over-HTTPS support in your browser and use the OpenWrt's ```net/https-dns-proxy``` (README on [GitHub](https://github.com/openwrt/packages/tree/master/net/https-dns-proxy)/[jsDelivr](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/https-dns-proxy/files/README.md)) package with optional ```https-dns-proxy``` WebUI/luci app. You can then continue to use ```dnsmasq.ipset``` setting for the ```dest_ipset``` in VPN Policy Routing. - - 2. Continue using DNS-over-HTTPS in your browser (which, by the way, also limits your options for router-level AdBlocking as described in ```dnsmasq.ipset``` option description here of ```net/simple-adblock``` README on [GitHub](https://github.com/openwrt/packages/tree/master/net/simple-adblock/files#dns-resolution-option)/[jsDelivr](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/simple-adblock/files/README.md#dns-resolution-option)), you than would either have to disable the ```dest_ipset``` or switch it to ```ipset```. Please note, you will lose all the benefits of [```dnsmasq.ipset```](#use-dnsmasq-ipset) option. - -### A Word About Cloudflare's 1.1.1.1 App - -Cloudflare has released an app for [iOS](https://itunes.apple.com/us/app/1-1-1-1-faster-internet/id1423538627) and [Android](https://play.google.com/store/apps/details?id=com.cloudflare.onedotonedotonedotone), which can also be configured to route traffic thru their own VPN tunnel (WARP+). - -If you use Cloudlfare's VPN tunnel (WARP+), none of the policies you set up with the VPN Policy Routing will take effect on your mobile device. Disable WARP+ for your home WiFi to keep VPN Policy Routing affecting your mobile device. - -If you just use the private DNS queries (WARP), [A Word About DNS-over-HTTPS](#a-word-about-DNS-over-HTTPS) applies. You can also disable WARP for your home WiFi to keep VPN Policy Routing affecting your mobile device. - -## Discussion - -Please head to [OpenWrt Forum](https://forum.openwrt.org/t/vpn-policy-based-routing-web-ui-discussion/10389) for discussions of this service. - -## Getting Help - -If things are not working as intended, please include the following in your post: - -- content of ```/etc/config/dhcp``` -- content of ```/etc/config/firewall``` -- content of ```/etc/config/network``` -- content of ```/etc/config/vpn-policy-routing``` -- the output of ```/etc/init.d/vpn-policy-routing support``` -- the output of ```/etc/init.d/vpn-policy-routing reload``` with verbosity setting set to 2 - -If you don't want to post the ```/etc/init.d/vpn-policy-routing support``` output in a public forum, there's a way to have the support details automatically uploaded to my account at paste.ee by running: ```/etc/init.d/vpn-policy-routing support -p```. You need to have the following packages installed to enable paste.ee upload functionality: ```curl libopenssl ca-bundle```. - -WARNING: while paste.ee uploads are unlisted/not indexed at the web-site, they are still publicly available. - -## Thanks - -I'd like to thank everyone who helped create, test and troubleshoot this service. Without contributions from [@hnyman](https://github.com/hnyman), [@dibdot](https://github.com/dibdot), [@danrl](https://github.com/danrl), [@tohojo](https://github.com/tohojo), [@cybrnook](https://github.com/cybrnook), [@nidstigator](https://github.com/nidstigator), [@AndreBL](https://github.com/AndreBL), [@dz0ny](https://github.com/dz0ny), rigorous testing/bugreporting by [@dziny](https://github.com/dziny), [@bluenote73](https://github.com/bluenote73), [@buckaroo](https://github.com/pgera), [@Alexander-r](https://github.com/Alexander-r), [n8v8R](https://github.com/n8v8R), [psherman](https://forum.openwrt.org/u/psherman), [@Vale-max](https://github.com/Vale-max), multiple contributions from [dl12345](https://github.com/dl12345) and [trendy](https://forum.openwrt.org/u/trendy) and feedback from other OpenWrt users it wouldn't have been possible. Wireguard/IPv6 support is courtesy of [Mullvad](https://www.mullvad.net) and [IVPN](https://www.ivpn.net/). +README has been moved to [https://docs.openwrt.melmac.net/vpn-policy-routing/](https://docs.openwrt.melmac.net/vpn-policy-routing/). diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.conf b/net/vpn-policy-routing/files/vpn-policy-routing.conf index 7cf430877..ec0f77335 100644 --- a/net/vpn-policy-routing/files/vpn-policy-routing.conf +++ b/net/vpn-policy-routing/files/vpn-policy-routing.conf @@ -3,16 +3,17 @@ config vpn-policy-routing 'config' option verbosity '2' option strict_enforcement '1' option src_ipset '0' - option dest_ipset 'dnsmasq.ipset' + option dest_ipset '0' + option resolver_ipset 'dnsmasq.ipset' option ipv6_enabled '0' list supported_interface '' list ignored_interface 'vpnserver wgserver' option boot_timeout '30' option iptables_rule_option 'append' - option iprule_enabled '0' option webui_enable_column '0' option webui_protocol_column '0' option webui_chain_column '0' + option webui_show_ignore_target '0' option webui_sorting '1' list webui_supported_protocol 'tcp' list webui_supported_protocol 'udp' diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.iface.hotplug b/net/vpn-policy-routing/files/vpn-policy-routing.iface.hotplug deleted file mode 100755 index 156df4bac..000000000 --- a/net/vpn-policy-routing/files/vpn-policy-routing.iface.hotplug +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -if [ "$ACTION" != "ifup" ] && [ "$ACTION" != "ifupdate" ]; then exit 0; fi - -logger -t vpn-policy-routing "Reloading vpn-policy-routing due to $ACTION of $INTERFACE ($DEVICE)" -/etc/init.d/vpn-policy-routing reload diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.init b/net/vpn-policy-routing/files/vpn-policy-routing.init index 4600b580a..56e2c5323 100755 --- a/net/vpn-policy-routing/files/vpn-policy-routing.init +++ b/net/vpn-policy-routing/files/vpn-policy-routing.init @@ -3,30 +3,57 @@ # shellcheck disable=SC2039,SC1091,SC2018,SC2019 PKG_VERSION='dev-test' -export START=94 -export USE_PROCD=1 +# sysctl net.ipv4.conf.default.rp_filter=1 +# sysctl net.ipv4.conf.all.rp_filter=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" +# shellcheck disable=SC2034 +START=94 +# shellcheck disable=SC2034 +USE_PROCD=1 -extra_command "support" "Generates output required to troubleshoot routing issues +if type extra_command 1>/dev/null 2>&1; then + extra_command '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" + extra_command 'version' 'Show version information' +else +# shellcheck disable=SC2034 + EXTRA_COMMANDS='support version' +# shellcheck disable=SC2034 + 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" +fi 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" +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 _ERROR_='\033[0;31mERROR\033[0m' +readonly _WARNING_='\033[0;33mWARNING\033[0m' + +# declare gatewaySummary errorSummary warningSummary +# declare serviceEnabled verbosity strictMode +# declare wanTableID wanMark fwMask +# declare ipv6Enabled srcIpset destIpset resolverIpset +# declare wanIface4 wanIface6 ifaceMark ifaceTableID +# declare ifAll ifSupported ignoredIfaces supportedIfaces icmpIface +# declare wanGW4 wanGW6 bootTimeout insertOption +# declare webuiChainColumn webuiShowIgnore dnsmasqIpsetSupported +usedChainsList='PREROUTING' +ipsetSupported='true' +configLoaded='false' + +version() { echo "$PKG_VERSION"; } create_lock() { [ -e "$PID" ] && return 1; touch "$PID"; } remove_lock() { [ -e "$PID" ] && rm -f "$PID"; } trap remove_lock EXIT @@ -34,9 +61,10 @@ 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_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; } +str_replace() { echo "${1//$2/$3}"; } +str_contains() { [ -n "$2" ] && [ "${1//$2}" != "$1" ]; } +str_contains_word() { echo "$1" | grep -q -w "$2"; } str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; } str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; } str_extras_to_space() { echo "$1" | tr ';{}' ' '; } @@ -60,13 +88,7 @@ output() { 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 +is_variant_installed() { [ "$(echo /usr/lib/opkg/info/"${1}"*.control)" != "/usr/lib/opkg/info/${1}*.control" ]; } list_iface() { ifAll="${ifAll}${1} "; } list_supported_iface() { is_supported_interface "$1" && ifSupported="${ifSupported}${1} "; } @@ -104,28 +126,37 @@ is_l2tp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4} 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_tor() { [ "$(str_to_lower "$1")" = "tor" ]; } +is_tor_running() { + local ret=0 + if [ -s "/etc/tor/torrc" ]; then + json_load "$(ubus call service list "{ 'name': 'tor' }")" + json_select 'tor'; json_select 'instances'; json_select 'instance1'; + json_get_var ret 'running'; json_cleanup + fi + if [ "$ret" = "0" ]; then return 1; else return 0; fi +} 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_ignored_interface() { str_contains_word "$ignoredIfaces" "$1"; } +is_supported_interface() { str_contains_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() { ! is_mac_address "$1" && str_contains "$1" ":"; } +is_family_mismatch() { ( is_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_netmask "${2//!}" ); } 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_domain() { str_contains "$1" '[a-zA-Z]'; } 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; } +dnsmasq_restart() { output 3 '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 @@ -134,17 +165,35 @@ is_supported_iface_dev() { return 1 } is_supported_protocol () { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; } +append_chains_targets() { + local chain iface name + config_get name "$1" 'name' 'blank' + config_get chain "$1" 'chain' 'PREROUTING' + config_get iface "$1" 'interface' + if ! str_contains_word "$usedChainsList" "$chain"; then + usedChainsList="$usedChainsList $chain" + if [ "$chain" != 'PREROUTING' ] && [ "$webuiChainColumn" != '1' ]; then + warningSummary="${warningSummary}$_WARNING_: Chain '$chain' is used by a policy '$name', but a WebUI setting to show chains column (webui_chain_column) is disabled!\\n" + fi + fi + if [ "$iface" = 'ignore' ] && ! str_contains_word "$supportedIfaces" 'ignore'; then + supportedIfaces="$supportedIfaces ignore" + if [ "$webuiShowIgnore" != '1' ]; then + warningSummary="${warningSummary}$_WARNING_: The 'ignore' target is used by a policy '$name', but a WebUI setting to show 'ignore' target (webui_show_ignore_target) is disabled!\\n" + fi + fi +} load_package_config() { + [ "$configLoaded" = 'false' ] || return 0 + 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_bool srcIpset 'config' 'src_ipset' 0 + config_get_bool destIpset 'config' 'dest_ipset' 0 + config_get resolverIpset 'config' 'resolver_ipset' 'dnsmasq.ipset' config_get verbosity 'config' 'verbosity' '2' config_get wanTableID 'config' 'wan_tid' '201' config_get wanMark 'config' 'wan_mark' '0x010000' @@ -154,9 +203,12 @@ load_package_config() { config_get supportedIfaces 'config' 'supported_interface' config_get bootTimeout 'config' 'boot_timeout' '30' config_get insertOption 'config' 'iptables_rule_option' 'append' + config_get_bool webuiChainColumn 'config' 'webui_chain_column' '0' + config_get_bool webuiShowIgnore 'config' 'webui_show_ignore_target' '0' + config_foreach append_chains_targets 'policy' if [ -z "${verbosity##*[!0-9]*}" ] || [ "$verbosity" -lt 0 ] || [ "$verbosity" -gt 2 ]; then - verbosity=1 + verbosity=2 fi . /lib/functions/network.sh @@ -166,6 +218,42 @@ load_package_config() { [ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4" [ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6" wanGW="${wanGW4:-$wanGW6}" + + case $insertOption in + insert|-i|-I) insertOption='-I';; + append|-a|-A|*) insertOption='-A';; + esac + + [ "$resolverIpset" = 'dnsmasq.ipset' ] && dnsmasqIpsetSupported='true' + if dnsmasq -v 2>/dev/null | grep -q 'no-ipset' || ! dnsmasq -v 2>/dev/null | grep -q -w 'ipset'; then + unset dnsmasqIpsetSupported + if [ -n "$dnsmasqIpsetSupported" ]; then + errorSummary="${errorSummary}$_ERROR_: Resolver ipset support (dnsmasq.ipset) is enabled in $packageName, but DNSMASQ ipsets are not supported on this system!\\n" + fi + fi + if ! ipset help hash:net >/dev/null 2>&1; then + unset ipsetSupported + if [ -n "$dnsmasqIpsetSupported" ]; then + errorSummary="${errorSummary}$_ERROR_: DNSMASQ ipsets are supported, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n" + unset dnsmasqIpsetSupported + fi + if [ "$destIpset" -ne 0 ]; then + errorSummary="${errorSummary}$_ERROR_: Destination ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n" + destIpset=0 + fi + if [ "$srcIpset" -ne 0 ]; then + errorSummary="${errorSummary}$_ERROR_: Source ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n" + srcIpset=0 + fi + fi + if ! ipset help hash:mac >/dev/null 2>&1; then + if [ "$srcIpset" -ne 0 ]; then + errorSummary="${errorSummary}$_ERROR_: Source ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:mac' type!\\n" + srcIpset=0 + fi + fi + + configLoaded='true' } is_enabled() { @@ -179,41 +267,6 @@ is_enabled() { 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() { @@ -265,34 +318,40 @@ ipt() { # shellcheck disable=SC2086 ips() { local command="$1" ipset="${2//-/_}" param="$3" comment="$4" appendix failFlag=0 - if [ "${ipset//_ip}" != "${ipset}" ]; then + if str_contains "$ipset" '_ip'; then ipset="${ipset//_ip}"; appendix='_ip'; - elif [ "${ipset//_mac}" != "${ipset}" ]; then + elif str_contains "$ipset" '_mac'; 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 + [ "$resolverIpset" = "dnsmasq.ipset" ] || return 1 + if [ -z "$dnsmasqIpsetSupported" ]; then + warningSummary="${warningSummary}${_WARNING_}: The 'resolver_ipset' is set to 'dnsmasq.ipset', but DNSMASQ ipsets are not supported on this system!\\n" + failFlag=1 + elif [ "$ipv6Enabled" -ne 0 ]; then + echo "ipset=/${param}/${ipset},${ipset}6 # $comment" >> "$dnsmasqFile" || failFlag=1 + else + echo "ipset=/${param}/${ipset} # $comment" >> "$dnsmasqFile" || failFlag=1 + fi ;; add) + if [ -z "$appendix" ] && [ "$destIpset" -eq 0 ]; then return 1; fi + if [ -n "$appendix" ] && [ "$srcIpset" -eq 0 ]; then return 1; fi + if [ "$ipv6Enabled" -ne 0 ] && [ "$appendix" != "_mac" ]; then + ipset -q -! $command "${ipset}6${appendix}" $param comment "$comment" || failFlag=1 + fi ipset -q -! $command "${ipset}${appendix}" $param comment "$comment" || failFlag=1 ;; create) + if [ "$ipv6Enabled" -ne 0 ] && [ "$appendix" != "_mac" ]; then + ipset -q -! "$command" "${ipset}6${appendix}" $param family inet6 || failFlag=1 + fi ipset -q -! "$command" "${ipset}${appendix}" $param || failFlag=1 ;; destroy|flush) + ipset -q -! "$command" "${ipset}6${appendix}" 2>/dev/null || failFlag=1 ipset -q -! "$command" "${ipset}${appendix}" 2>/dev/null || failFlag=1 return 0 ;; @@ -300,46 +359,40 @@ ips() { 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 + local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain + proto="$(str_to_lower "$7")" + chain="${8:-PREROUTING}" + if [ -n "${laddr}${lport}${rport}" ]; then + processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n" + fi + if [ -n "$proto" ] && [ "$proto" != "all" ]; then + processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n" + fi + if [ "$chain" != "PREROUTING" ]; then + processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '$comment'\\n" + fi + ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || processPolicyError="${processPolicyError}${_ERROR_}: ipset 'add' $iface $raddr\\n" 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 + local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain + local mark param i valueNeg value dest ipInsertOption="-A" + proto="$(str_to_lower "$7")" + chain="${8:-PREROUTING}" + mark=$(eval echo "\$mark_${iface//-/_}") + if [ "$ipv6Enabled" -eq 0 ] && ( is_ipv6 "$laddr" || is_ipv6 "$raddr" ); then + processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$comment' as IPv6 support is disabled\\n" + return 1 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}##" + if [ -n "$mark" ]; then + dest="-g VPR_MARK${mark}" + elif [ "$iface" = "ignore" ]; then + dest="-j RETURN" + else + processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}\\n" return 0 fi @@ -351,14 +404,19 @@ insert_policy() { fi fi + if is_family_mismatch "$laddr" "$raddr"; then + processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$laddr' and '$raddr' in policy '$comment'\\n" + return 0 + fi + for i in $proto; do if [ "$i" = 'all' ]; then - param="-t mangle -I VPR_${chain} -j MARK --set-xmark ${mark}/${fwMask}" + param="-t mangle ${ipInsertOption} VPR_${chain} $dest" elif ! is_supported_protocol "$i"; then - processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$comment'##" + processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$comment'\\n" return 0 else - param="-t mangle -I VPR_${chain} -j MARK --set-xmark ${mark}/${fwMask} -p $i" + param="-t mangle ${ipInsertOption} VPR_${chain} $dest -p $i" fi if [ -n "$laddr" ]; then @@ -371,11 +429,8 @@ insert_policy() { 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" + param="$param $valueNeg -s $value" fi fi @@ -394,12 +449,7 @@ insert_policy() { 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 + param="$param $valueNeg -d $value" fi if [ -n "$rport" ]; then @@ -419,16 +469,16 @@ insert_policy() { 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 + if str_contains "$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 + elif str_contains "$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 + elif str_contains "$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 + elif str_contains "$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 @@ -441,47 +491,42 @@ r_process_policy(){ 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" + if [ -n "$proto" ] && [ "$proto" != 'all' ] && [ "$srcIpset" -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 + if [ -n "$proto" ] && [ "$proto" != "all" ] && [ "$srcIpset" -ne 0 ]; then + processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', source: '$laddr'\\n" fi + ips 'add' "${iface}_ip" "$laddr" "${comment}: $laddr" || ipsFailFlag=1 fi - elif [ -n "$raddr" ] && [ -z "${laddr}${lport}${rport}" ] && [ "$chain" = 'PREROUTING' ] && [ -n "$remoteIpset" ]; then + elif [ -n "$raddr" ] && [ -z "${laddr}${lport}${rport}" ] && [ "$chain" = 'PREROUTING' ]; then if [ -n "$proto" ] && [ "$proto" != 'all' ]; then - processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment', domain '$raddr'\\n" + processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', destination: '$raddr'\\n" + fi + if is_domain "$raddr"; then + ips 'add_dnsmasq' "${iface}" "$raddr" "${comment}" || ipsFailFlag=1 + else + ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || ipsFailFlag=1 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 + [ -n "$ipsFailFlag" ] || return 0; + 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" + 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 - [ -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 + insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain" fi fi } @@ -540,17 +585,20 @@ process_policy(){ table_destroy(){ local tid="$1" iface="$2" mark="$3" if [ -n "$tid" ] && [ -n "$iface" ] && [ -n "$mark" ]; then + ipt -t mangle -F "VPR_MARK${mark}" + ipt -t mangle -X "VPR_MARK${mark}" 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"; + ip -4 route flush table "$tid" >/dev/null 2>&1 + ip -6 route flush table "$tid" >/dev/null 2>&1 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 + sed -i "/$iface/d" /etc/iproute2/rt_tables return 0 else return 1 @@ -559,7 +607,7 @@ table_destroy(){ # 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 + local tid="$1" mark="$2" iface="$3" gw4="$4" dev="$5" gw6="$6" dev6="$7" dscp s=0 i ipv4_error=0 ipv6_error=1 if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then return 1 @@ -568,12 +616,14 @@ table_create(){ table_destroy "$tid" "$iface" "$mark" if [ -n "$gw4" ] || [ "$strictMode" -ne 0 ]; then + echo "$tid" "$iface" >> /etc/iproute2/rt_tables 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 +# ip -4 route list table main | grep -v 'br-lan' | while read -r i; do + ip -4 route list table main | 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 @@ -581,14 +631,18 @@ table_create(){ done ip -4 route flush cache || ipv4_error=1 ip -4 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv4_error=1 + ipt -t mangle -N "VPR_MARK${mark}" || ipv4_error=1 + ipt -t mangle -A "VPR_MARK${mark}" -j MARK --set-xmark "${mark}/${fwMask}" || ipv4_error=1 + ipt -t mangle -A "VPR_MARK${mark}" -j RETURN || ipv4_error=1 fi if [ "$ipv6Enabled" -ne 0 ]; then + ipv6_error=0 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 list 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 @@ -600,31 +654,33 @@ table_create(){ 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 + ipt -t mangle -I VPR_PREROUTING -m dscp --dscp "${dscp}" -g "VPR_MARK${mark}" || s=1 fi - if [ -n "$remoteIpset" ]; then + if [ -n "$ipsetSupported" ] && { [ -n "$dnsmasqIpsetSupported" ] || [ "$destIpset" -ne 0 ]; }; 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 + for i in $usedChainsList; do + ipt -t mangle -I VPR_${i} -m set --match-set "${iface}" dst -g "VPR_MARK${mark}" || s=1 + if [ "$ipv6Enabled" -ne 0 ]; then ipt -t mangle -I VPR_${i} -m set --match-set "${iface}6" dst -g "VPR_MARK${mark}" || s=1; fi done else - s=1 + s=1 fi fi - if [ "$localIpset" -ne 0 ]; then + if [ -n "$ipsetSupported" ] && [ "$srcIpset" -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 + ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_ip" src -g "VPR_MARK${mark}" || s=1 + if [ "$ipv6Enabled" -ne 0 ]; then ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}6_ip" src -g "VPR_MARK${mark}" || s=1; fi else - s=1 + 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 + ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_mac" src -g "VPR_MARK${mark}" || s=1 else - s=1 + s=1 fi fi if [ "$iface" = "$icmpIface" ]; then - ipt -t mangle -I VPR_OUTPUT -p icmp -j MARK --set-xmark "${mark}/${fwMask}" || s=1 + ipt -t mangle -I VPR_OUTPUT -p icmp -g "VPR_MARK${mark}" || s=1 fi else s=1 @@ -681,55 +737,104 @@ process_interface(){ return $s } +process_tor_interface(){ + local s=0 iface="$1" action="$2" displayText + case "$action" in + destroy) + for i in PREROUTING FORWARD INPUT OUTPUT; do + ipt -t nat -D "${i}" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}" + ipt -t nat -F "VPR_${i}"; ipt -t nat -X "VPR_${i}"; + done + ;; + create) + output 2 "Creating TOR redirects " + dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')" + transPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')" + dnsPort="${dnsPort:-9053}"; transPort="${transPort:-9040}"; + for i in $usedChainsList; do + ipt -t nat -N "VPR_${i}" + ipt -t nat "$insertOption" "$i" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}" + done + if ips 'create' "${iface}" 'hash:net comment' && ips 'flush' "${iface}"; then + for i in $usedChainsList; do + ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 53 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$dnsPort" -m comment --comment "TorDNS-UDP" || s=1 + ipt -t nat -I "VPR_${i}" -p tcp -m tcp --dport 80 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTP-TCP" || s=1 + ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 80 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTP-UDP" || s=1 + ipt -t nat -I "VPR_${i}" -p tcp -m tcp --dport 443 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTPS-TCP" || s=1 + ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 443 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTPS-UDP" || s=1 + done + else + s=1 + fi + displayText="${iface}/53->${dnsPort}/80,443->${transPort}" + if [ "$s" -eq "0" ]; then + gatewaySummary="${gatewaySummary}${displayText}\\n" + output_ok + else + errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n" + output_fail + fi + ;; + esac + return $s +} + convert_config(){ - local i + local i src_ipset dest_ipset resolver_ipset [ -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="" + grep -q "ignored_interfaces" "/etc/config/${packageName}" && sed -i 's/ignored_interfaces/ignored_interface/g' "/etc/config/${packageName}" + grep -q "supported_interfaces" "/etc/config/${packageName}" && sed -i 's/supported_interfaces/supported_interface/g' "/etc/config/${packageName}" + grep -q "local_addresses" "/etc/config/${packageName}" && sed -i 's/local_addresses/local_address/g' "/etc/config/${packageName}" + grep -q "local_ports" "/etc/config/${packageName}" && sed -i 's/local_ports/local_port/g' "/etc/config/${packageName}" + grep -q "remote_addresses" "/etc/config/${packageName}" && sed -i 's/remote_addresses/remote_address/g' "/etc/config/${packageName}" + grep -q "remote_ports" "/etc/config/${packageName}" && sed -i 's/remote_ports/remote_port/g' "/etc/config/${packageName}" + grep -q "ipset_enabled" "/etc/config/${packageName}" && sed -i 's/ipset_enabled/dest_ipset/g' "/etc/config/${packageName}" + grep -q "dnsmasq_enabled" "/etc/config/${packageName}" && sed -i 's/dnsmasq_enabled/resolver_ipset/g' "/etc/config/${packageName}" + grep -q "enable_control" "/etc/config/${packageName}" && sed -i 's/enable_control/webui_enable_column/g' "/etc/config/${packageName}" + grep -q "proto_control" "/etc/config/${packageName}" && sed -i 's/proto_control/webui_protocol_column/g' "/etc/config/${packageName}" + grep -q "chain_control" "/etc/config/${packageName}" && sed -i 's/chain_control/webui_chain_column/g' "/etc/config/${packageName}" + grep -q "sort_control" "/etc/config/${packageName}" && sed -i 's/sort_control/webui_sorting/g' "/etc/config/${packageName}" + grep -q "local_address" "/etc/config/${packageName}" && sed -i 's/local_address/src_addr/g' "/etc/config/${packageName}" + grep -q "local_port" "/etc/config/${packageName}" && sed -i 's/local_port/src_port/g' "/etc/config/${packageName}" + grep -q "remote_address" "/etc/config/${packageName}" && sed -i 's/remote_address/dest_addr/g' "/etc/config/${packageName}" + grep -q "remote_port" "/etc/config/${packageName}" && sed -i 's/remote_port/dest_port/g' "/etc/config/${packageName}" + grep -q "local_ipset" "/etc/config/${packageName}" && sed -i 's/local_ipset/src_ipset/g' "/etc/config/${packageName}" + grep -q "remote_ipset" "/etc/config/${packageName}" && sed -i 's/remote_ipset/dest_ipset/g' "/etc/config/${packageName}" +# sync + dest_ipset="$(uci -q get $packageName.config.dest_ipset)" + src_ipset="$(uci -q get $packageName.config.src_ipset)" + resolver_ipset="$(uci -q get $packageName.config.resolver_ipset)" + + if [ -n "$dest_ipset" ] && [ "$dest_ipset" != "0" ] && [ "$dest_ipset" != "1" ]; then + uci set "$packageName".config.dest_ipset='0' + if [ -z "$resolver_ipset" ]; then + uci set "$packageName".config.resolver_ipset='dnsmasq.ipset' + fi + uci commit "$packageName" + fi + if [ -n "$src_ipset" ] && [ "$src_ipset" != "0" ] && [ "$src_ipset" != "1" ]; then + uci set "$packageName".config.src_ipset='1' + uci commit "$packageName" fi - uci -q del "$packageName.config.dnsmasq_ipset" - uci -q set "$packageName".config.remote_ipset="$remoteIpset" -# shellcheck disable=SC2154 - if [ -z "$webuiProtocol" ]; then + if [ -z "$(uci -q get $packageName.config.webui_supported_protocol)" ]; 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' + uci commit "$packageName" 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" + for i in append_local_rules append_src_rules \ + append_remote_rules append_dest_rules; do + if [ -n "$(uci -q get $packageName.config.$i)" ]; then + warningSummary="${warningSummary}$_WARNING_: $i setting is not supported in ${serviceName}.\\n" + fi + done + for i in udp_proto_enabled forward_chain_enabled input_chain_enabled \ + output_chain_enabled iprule_enabled; do + if [ "$(uci -q get $packageName.config.$i)" = "1" ]; then + warningSummary="${warningSummary}$_WARNING_: $i setting is not supported in ${serviceName}.\\n" + fi done } @@ -770,7 +875,7 @@ process_user_file(){ } start_service() { - local gatewaySummary errorSummary warningSummary dnsmasqStoredHash dnsmasqNewHash i modprobeStatus=0 + local dnsmasqStoredHash dnsmasqNewHash i modprobeStatus=0 convert_config is_enabled 'on_start' || return 1 is_wan_up || return 0 @@ -788,13 +893,14 @@ start_service() { errorSummary="${errorSummary}${_ERROR_}: Failed to load kernel modules\\n" fi - for i in PREROUTING FORWARD INPUT OUTPUT; do + for i in $usedChainsList; 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'; + process_tor_interface 'tor' 'destroy'; is_tor_running && process_tor_interface 'tor' 'create'; output 1 '\n' if is_config_enabled 'policy'; then output 1 'Processing Policies ' @@ -829,7 +935,7 @@ start_service() { [ -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 + if [ "$strictMode" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then json_add_string mode "strict" fi json_close_object @@ -844,8 +950,15 @@ start_service() { fi } -restart() { reload; } -restart_service() { reload; } +service_started() { + if [ -n "$errorSummary" ]; then + return 2 + elif [ -n "$warningSummary" ]; then + return 1 + else + return 0 + fi +} stop_service() { local i @@ -856,7 +969,8 @@ stop_service() { 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' + config_load 'network'; config_foreach process_interface 'interface' 'destroy'; + process_tor_interface 'tor' 'destroy' unset ifaceTableID; unset ifaceMark; if [ -s "$dnsmasqFile" ]; then rm -f "$dnsmasqFile" @@ -883,20 +997,22 @@ service_triggers() { validate_include procd_close_validate - procd_add_reload_trigger 'firewall' 'openvpn' 'vpn-policy-routing' procd_open_trigger + procd_add_reload_trigger 'openvpn' + if type procd_add_service_trigger 1>/dev/null 2>&1; then + procd_add_service_trigger "service.restart" "firewall" /etc/init.d/${packageName} reload + fi + procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload 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 + output 3 "$serviceName monitoring interfaces: $ifSupported"; output_okn; } -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 + local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j + readonly _SEPARATOR_='============================================================' is_enabled json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version @@ -912,72 +1028,91 @@ support() { 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}." + [ -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 + echo "$_SEPARATOR_" + dnsmasq --version 2>/dev/null | sed '/^$/,$d' + if [ -n "$1" ]; then + echo "$_SEPARATOR_" + echo "Resolving domains" + for i in $1; do + echo "$i: $(resolveip "$i" | tr '\n' ' ')" + done + fi + + echo "$_SEPARATOR_" + 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 + i=0; while [ $i -lt $tableCount ]; do + echo "" + echo "IPv4 Table $((wanTableID + i)): $(ip -4 route show table $((wanTableID + i)))" + echo "IPv4 Table $((wanTableID + i)) Rules:" + ip -4 rule list table "$((wanTableID + i))" + i=$((i + 1)) + done + + if [ "$ipv6Enabled" -ne 0 ]; then + 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 + fi + + for j in Mangle NAT; do + if [ -z "$set_d" ]; then + for i in $usedChainsList; do + if iptables -v -t "$(str_to_lower $j)" -S "VPR_${i}" 1>/dev/null 2>&1; then + echo "$_SEPARATOR_" + echo "$j IP Table: $i" + iptables -v -t "$(str_to_lower $j)" -S "VPR_${i}" + if [ "$ipv6Enabled" -ne 0 ]; then + echo "$_SEPARATOR_" + echo "$j IPv6 Table: $i" + ip6tables -v -t "$(str_to_lower $j)" -S "VPR_${i}" + fi + fi + done + else + echo "$_SEPARATOR_" + echo "$j IP Table" + iptables -L -t "$(str_to_lower $j)" + if [ "$ipv6Enabled" -ne 0 ]; then + echo "$_SEPARATOR_" + echo "$j IPv6 Table" + ip6tables -L -t "$(str_to_lower $j)" + fi + fi + i=0; ifaceMark="$wanMark"; + while [ $i -lt $tableCount ]; do + if iptables -v -t "$(str_to_lower $j)" -S "VPR_MARK${ifaceMark}" 1>/dev/null 2>&1; then + echo "$_SEPARATOR_" + echo "$j IP Table MARK Chain: VPR_MARK${ifaceMark}" + iptables -v -t "$(str_to_lower $j)" -S "VPR_MARK${ifaceMark}" + ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))"; + fi + i=$((i + 1)) + done + done + + echo "$_SEPARATOR_" + echo "Current ipsets" + ipset save if [ -s "$dnsmasqFile" ]; then - echo "============================================================" - echo "DNSMASQ ipsets" - cat "$dnsmasqFile" + echo "$_SEPARATOR_" + echo "DNSMASQ ipsets" + cat "$dnsmasqFile" fi - echo "============================================================" - } | input + echo "$_SEPARATOR_" + } | tee -a /var/${packageName}-support if [ -n "$set_p" ]; then printf "%b" "Pasting to paste.ee... " - if is_installed 'curl' && is_installed 'libopenssl' && is_installed 'ca-bundle'; then + if is_installed 'curl' && is_variant_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)" @@ -985,7 +1120,7 @@ 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__" + [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n" [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support" else printf "%b" "$__FAIL__\\n" @@ -1003,13 +1138,13 @@ validate_config() { 'verbosity:range(0,2):1' \ 'strict_enforcement:bool:1' \ 'src_ipset:bool:0' \ - 'dest_ipset:string' \ + 'dest_ipset:bool:0' \ + 'resolver_ipset::or("", "none", "dnsmasq.ipset")' \ '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)' \ diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.netflix.user b/net/vpn-policy-routing/files/vpn-policy-routing.netflix.user index 3f317dd2d..d2fd6acc1 100644 --- a/net/vpn-policy-routing/files/vpn-policy-routing.netflix.user +++ b/net/vpn-policy-routing/files/vpn-policy-routing.netflix.user @@ -1,11 +1,38 @@ #!/bin/sh # This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh +# Credits to https://forum.openwrt.org/u/dscpl for api.hackertarget.com code. +# Credits to https://github.com/kkeker and https://github.com/tophirsch for api.bgpview.io code. TARGET_IPSET='wan' TARGET_ASN='2906' -TARGET_URL="https://ipinfo.io/AS${TARGET_ASN}" TARGET_FNAME="/var/tmp_AS${TARGET_ASN}" +#DB_SOURCE='ipinfo.io' +#DB_SOURCE='api.hackertarget.com' +DB_SOURCE='api.bgpview.io' -curl "$TARGET_URL" 2>/dev/null | grep -E "a href.*${TARGET_ASN}\/" | grep -v ":" | sed "s/^.*//" > "$TARGET_FNAME" -awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_FNAME" | ipset restore -! +_ret=1 +if [ "$DB_SOURCE" = "ipinfo.io" ]; then + TARGET_URL="https://ipinfo.io/AS${TARGET_ASN}" + curl "$TARGET_URL" 2>/dev/null | grep -E "a href.*${TARGET_ASN}\/" | grep -v ":" | sed "s/^.*//" > "$TARGET_FNAME" +fi + +if [ "$DB_SOURCE" = "api.hackertarget.com" ]; then + TARGET_URL="https://api.hackertarget.com/aslookup/?q=AS${TARGET_ASN}" + curl "$TARGET_URL" 2>/dev/null | sed '1d' > "$TARGET_FNAME" +fi + +if [ "$DB_SOURCE" = "api.bgpview.io" ]; then + TARGET_URL="https://api.bgpview.io/asn/${TARGET_ASN}/prefixes" + if command -v jq >/dev/null 2>&1; then + curl -s "$TARGET_URL" 2>/dev/null | jq -r '.data.ipv4_prefixes[].prefix' > "$TARGET_FNAME" + else + curl -s "$TARGET_URL" 2>/dev/null | sed -ne 's/.*"ipv4_prefixes":\(.*\),"ipv6_prefixes":.*/\1\n/g; s/\}\},/&\n/g; s/"parent":{"prefix":/"parent":{"parent_prefix":/gp' | sed -ne 's/.*"prefix":"\(.\{0,20\}\)",".*/\1/g; s/\\//gp' > "$TARGET_FNAME" + fi +fi + +if [ -s "$TARGET_FNAME" ]; then + awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_FNAME" | ipset restore -! && _ret=0 +fi rm -f "$TARGET_FNAME" + +return $_ret diff --git a/net/vpn-policy-routing/test.sh b/net/vpn-policy-routing/test.sh new file mode 100644 index 000000000..45469ed96 --- /dev/null +++ b/net/vpn-policy-routing/test.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/etc/init.d/"$1" version 2>&1 | grep "$2"