diff --git a/net/vpnbypass/Makefile b/net/vpnbypass/Makefile new file mode 100644 index 000000000..a65e1a319 --- /dev/null +++ b/net/vpnbypass/Makefile @@ -0,0 +1,53 @@ +# Copyright (c) 2017 Stan Grishin (stangri@melmac.net) +# This is free software, licensed under the GNU General Public License v3. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=vpnbypass +PKG_VERSION:=1.0.0 +PKG_RELEASE:=4 +PKG_LICENSE:=GPL-3.0+ +PKG_MAINTAINER:=Stan Grishin + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=net + CATEGORY:=Network + DEPENDS:=+ip-full +ipset +iptables +ubox +dnsmasq-full + CONFLICTS:=ip dnsmasq + TITLE:=Simple VPN Bypass Service + PKGARCH:=all +endef + +define Package/$(PKG_NAME)/description +This service can be used to enable simple VPN split tunnelling. +Supports accessing domains, IP ranges outside of your VPN tunnel. +Also supports dedicating local ports/IP ranges for direct internet access (outside of your VPN tunnel). +Please see the README for further information. + +endef + +define Package/$(PKG_NAME)/conffiles +/etc/config/vpnbypass +endef + +define Build/Prepare +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/vpnbypass.init $(1)/etc/init.d/vpnbypass + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/vpnbypass.conf $(1)/etc/config/vpnbypass + $(INSTALL_DIR) $(1)/etc/hotplug.d/firewall + $(INSTALL_DATA) ./files/vpnbypass.hotplug $(1)/etc/hotplug.d/firewall/90-vpnbypass +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) \ No newline at end of file diff --git a/net/vpnbypass/files/README.md b/net/vpnbypass/files/README.md new file mode 100644 index 000000000..9dd2cc249 --- /dev/null +++ b/net/vpnbypass/files/README.md @@ -0,0 +1,47 @@ +# OpenWrt Simple VPNBypass +A simple PROCD-based vpnbypass init script for OpenWrt/LEDE Project. Useful if your router accesses internet thru VPN client/tunnel, but you want specific traffic (ports, IP ranges, domains or local IP ranges) to be routed outside of this tunnel. + +# Features +- Routes Plex Media Server traffic outside of the VPN tunnel. +- Allows you to define IPs/ranges in local network so that their traffic is routed outside of the VPN tunnel. +- Allows you to define list of domain names which are accessed outside of the VPN tunnel (useful for Netflix, Hulu, etc). +- Doesn't stay in memory -- creates the iptables rules which are automatically updated on WAN up/down. + +# Requirements +This service requires following packages to be installed on your router: ip-full ipset iptables dnsmasq-full (dnsmasq-full requires you uninstall dnsmasq first). Run the following commands to satisfy the requirements: +```sh +opkg update +opkg remove dnsmasq ip; opkg install ip-full ipset iptables dnsmasq-full +``` + +# How to install +```sh +opkg update +opkg install vpnbypass luci-app-vpnbypass +``` +Default install routes Plex Media Server traffic (port 32400) outside of the VPN tunnel, routes LogmeIn Hamachi traffic (25.0.0.0/8) outside of the VPN tunnel and also routes internet traffic from local IPs 192.168.1.80-192.168.1.88 outside of the VPN tunnel. + +# Documentation / Discussion +Please head to OpenWrt/LEDE Project Forums for discussion of this script. + +# What's New +1.0.0: +- Hotplug script created during install. + +0.1.0: +- Package built. +- Support for user-defined ports implemented. +- Support for user-defined routes implemented. +- Support for user-defined local ranges implemented. + +0.0.1: +- Initial release. + +# Known Issues +Until user-defined domains are supported within vpnbypass config, you can set domains to be accessed outside of VPN tunnel like so: +```sh +uci add_list dhcp.@dnsmasq[-1].ipset='/github.com/plex.tv/google.com/vpnbypass' +uci add_list dhcp.@dnsmasq[-1].ipset='/hulu.com/netflix.com/nhl.com/vpnbypass' +uci commit dhcp +/etc/init.d/dnsmasq restart +``` \ No newline at end of file diff --git a/net/vpnbypass/files/vpnbypass.conf b/net/vpnbypass/files/vpnbypass.conf new file mode 100644 index 000000000..8c200e934 --- /dev/null +++ b/net/vpnbypass/files/vpnbypass.conf @@ -0,0 +1,5 @@ +config vpnbypass 'config' + option enabled '1' + list localport '32400' + list remotesubnet '25.0.0.0/8' + list localsubnet '192.168.1.80/28' diff --git a/net/vpnbypass/files/vpnbypass.hotplug b/net/vpnbypass/files/vpnbypass.hotplug new file mode 100644 index 000000000..a2874f97e --- /dev/null +++ b/net/vpnbypass/files/vpnbypass.hotplug @@ -0,0 +1,2 @@ +#!/bin/sh +[ "$ACTION" = "reload" ] && /etc/init.d/vpnbypass reload diff --git a/net/vpnbypass/files/vpnbypass.init b/net/vpnbypass/files/vpnbypass.init new file mode 100644 index 000000000..2bf5aa60e --- /dev/null +++ b/net/vpnbypass/files/vpnbypass.init @@ -0,0 +1,76 @@ +#!/bin/sh /etc/rc.common + +START=90 +USE_PROCD=1 + +TID="200"; FW_MARK="0x010000"; IPSET="vpnbypass"; + +output() { + [ -z "$verbosity" ] && config_get verbosity 'config' 'verbosity' '2' + [ -n "$2" -a $((verbosity)) -ne $(($2)) ] && return 0; + [ -t 1 ] && echo -e -n "$1" + [ $(echo -e -n "$1" | wc -l) -gt 0 ] && logger -t "vpnbypass[$$]" "$(echo -e -n ${logmsg}${1})" && logmsg='' || logmsg=${logmsg}${1} +} + +vpnbypass_enabled() { + config_get_bool enabled 'config' 'enabled' 0 + [ $((enabled)) -gt 0 ] && return 0 || { output "VPNBypass is not enabled in the config file!\nTo enable, run 'uci set vpnbypass.config.enabled=1; uci commit vpnbypass'\n"; return 1; } +} + +boot() { ubus -t 30 wait_for network.interface && rc_procd start_service || output 'ERROR: Failed to settle network interface!\n'; } + +start_service() { + local ll + config_load vpnbypass + vpnbypass_enabled || return 1 + [ -d /etc/openvpn ] || return 1 + config_get lports 'config' 'localport' + config_get rports 'config' 'remoteport' + config_get routes 'config' 'remotesubnet' + config_get ranges 'config' 'localsubnet' + config_get domains 'config' 'domain' + + procd_open_instance + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance + + . /lib/functions/network.sh; network_get_ipaddr wanip wan; network_get_gateway gwip wan; network_get_ipaddr lanip lan + [ ! "$wanip" ] && output 'ERROR: Could not get wan ip\n' && exit 0 + [ ! "$gwip" ] && output 'ERROR: Could not get wan gateway\n' && exit 0 + for ll in ${routes}; do (ip route del $ll; ip route add $ll via $gwip) >/dev/null 2>&1; done + (ip rule del fwmark $FW_MARK table $TID; iptables -t mangle -F; ipset -F $IPSET; ipset -X $IPSET) >/dev/null 2>&1 + (ip route flush table $TID; ip route flush cache) >/dev/null 2>&1 + (ip route add default via $gwip table $TID; ip route flush cache) >/dev/null 2>&1 + (modprobe xt_set || modprobe ip_set; insmod ip_set_hash_ip) >/dev/null 2>&1 + (ipset -N $IPSET iphash -q; ipset -F $IPSET) >/dev/null 2>&1 + for ll in ${lports}; do iptables -t mangle -A PREROUTING -p tcp -m multiport --sport $ll -j MARK --set-mark $FW_MARK/$FW_MARK -m comment --comment "vpnbypass"; done + for ll in ${rports}; do iptables -t mangle -A PREROUTING -p tcp -m multiport --dport $ll -j MARK --set-mark $FW_MARK/$FW_MARK -m comment --comment "vpnbypass"; done + for ll in ${ranges}; do iptables -t mangle -I PREROUTING -s $ll -j MARK --set-mark $FW_MARK/$FW_MARK -m comment --comment "vpnbypass"; done + iptables -t mangle -A PREROUTING -m set --match-set $IPSET dst -j MARK --set-mark $FW_MARK/$FW_MARK -m comment --comment "vpnbypass" + ip rule add fwmark $FW_MARK table $TID + output "vpnbypass started with TID: $TID FW_MARK: $FW_MARK\n" +} + +stop_service() { + local ll + config_load vpnbypass + vpnbypass_enabled || return 1 + config_get routes 'config' 'remotesubnet' + + [ -d /etc/openvpn ] || return 1 + rm -f /etc/hotplug.d/firewall/${START}-${IPSET} + . /lib/functions/network.sh; network_get_ipaddr wanip wan; network_get_gateway gwip wan; network_get_ipaddr lanip lan + [ ! "$wanip" ] && output 'ERROR: Could not get wan ip\n' && exit 0 + [ ! "$gwip" ] && output 'ERROR: Could not get wan gateway\n' && exit 0 + for ll in ${routes}; do ip route del $ll >/dev/null 2>&1; done +# iptables-save | grep -Fv -- "vpnbypass" | iptables-restore + (ip rule del fwmark $FW_MARK table $TID; iptables -t mangle -F; ipset -F $IPSET; ipset -X $IPSET) >/dev/null 2>&1 + (ip route flush table $TID; ip route flush cache) >/dev/null 2>&1 + output "vpnbypass stopped\n" +} + +service_triggers() { + procd_add_reload_trigger 'vpnbypass' + procd_add_reload_interface_trigger 'wan' +}