diff --git a/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js b/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js index bacbbd704..3c0cdc296 100755 --- a/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js +++ b/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js @@ -183,7 +183,8 @@ return view.extend({ s.tab('timed', _('Time Restrictions')); s.filter = function(section_id) { - return (uci.get('firewall', section_id, 'target') != 'SNAT'); + //return (uci.get('firewall', section_id, 'target') != 'SNAT'); + return (uci.get('firewall', section_id, 'target') != 'SNAT' && section_id.startsWith('omr_dst') == false && section_id.startsWith('omr_dscp') == false); }; s.sectiontitle = function(section_id) { diff --git a/luci-app-omr-bypass/Makefile b/luci-app-omr-bypass/Makefile index 9dcd62369..1ceb86669 100755 --- a/luci-app-omr-bypass/Makefile +++ b/luci-app-omr-bypass/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Interface to bypass domains #LUCI_DEPENDS:=+dnsmasq-full +shadowsocks-libev-ss-rules +(LINUX_5_4||LINUX_5_15||TARGET_x86_64):iptables-mod-ndpi +iptables-mod-extra +(LINUX_5_4||LINUX_5_15||TARGET_x86_64):kmod-ipt-ndpi +iptables -LUCI_DEPENDS:=+omr-bypass +LUCI_DEPENDS:=+LINUX_5_4:omr-bypass +!LINUX_5_4:omr-bypass-nft #ifneq ($(CONFIG_TARGET_ramips),y) # #LUCI_DEPENDS+=+(LINUX_5_4||LINUX_5_15):iptables-mod-ndpi +(LINUX_5_4||LINUX_5_15):kmod-ipt-ndpi # LUCI_DEPENDS+=+iptables-mod-ndpi +kmod-ipt-ndpi diff --git a/luci-app-omr-bypass/htdocs/luci-static/resources/view/services/omr-bypass.js b/luci-app-omr-bypass/htdocs/luci-static/resources/view/services/omr-bypass.js index 62cbc973e..5cdcaf7d9 100755 --- a/luci-app-omr-bypass/htdocs/luci-static/resources/view/services/omr-bypass.js +++ b/luci-app-omr-bypass/htdocs/luci-static/resources/view/services/omr-bypass.js @@ -49,6 +49,7 @@ return L.view.extend({ o.rmempty = false; o = s.option(form.Flag, 'vpn', _('VPN on server'),_('Bypass using VPN configured on server.')); + o.modalonly = true o = s.option(widgets.DeviceSelect, 'interface', _('Interface'),_('When none selected, MPTCP master interface is used.')); o.noaliases = true; @@ -82,6 +83,7 @@ return L.view.extend({ o.rmempty = false; o = s.option(form.Flag, 'vpn', _('VPN on server'),_('Bypass using VPN configured on server.')); + o.modalonly = true o = s.option(widgets.DeviceSelect, 'interface', _('Interface'),_('When none selected, MPTCP master interface is used.')); o.noaliases = true; @@ -206,7 +208,7 @@ return L.view.extend({ o.rmempty = false; o = s.option(form.Flag, 'vpn', _('VPN on server'),_('Bypass using VPN configured on server.')); - + o.modalonly = true o = s.option(widgets.DeviceSelect, 'interface', _('Interface'),_('When none selected, MPTCP master interface is used.')); o.noaliases = true; @@ -266,6 +268,7 @@ return L.view.extend({ }; o = s.option(form.Flag, 'vpn', _('VPN on server'),_('Bypass using VPN configured on server.')); + o.modalonly = true o = s.option(widgets.DeviceSelect, 'interface', _('Interface'),_('When none selected, MPTCP master interface is used (or an other interface if master is down).')); o.noaliases = true; diff --git a/luci-app-omr-dscp/Makefile b/luci-app-omr-dscp/Makefile index 152e91cf0..b099678d6 100755 --- a/luci-app-omr-dscp/Makefile +++ b/luci-app-omr-dscp/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Interface to DSCP - +LUCI_DEPENDS:=+LINUX_5_4:omr-dscp +!LINUX_5_4:omr-dscp-nft PKG_LICENSE:=GPLv3 #include ../luci/luci.mk diff --git a/luci-app-shadowsocks-rust/htdocs/luci-static/resources/shadowsocks-rust.js b/luci-app-shadowsocks-rust/htdocs/luci-static/resources/shadowsocks-rust.js index 86c6ab0f8..0e4c4e071 100644 --- a/luci-app-shadowsocks-rust/htdocs/luci-static/resources/shadowsocks-rust.js +++ b/luci-app-shadowsocks-rust/htdocs/luci-static/resources/shadowsocks-rust.js @@ -41,13 +41,13 @@ var modes = [ var methods = [ 'none', // aead - 'aes-128-gcm', - 'aes-256-gcm', - 'chacha20-ietf-poly1305', - '2022-blake3-aes-128-gcm', +// 'aes-128-gcm', +// 'aes-256-gcm', +// 'chacha20-ietf-poly1305', +// '2022-blake3-aes-128-gcm', '2022-blake3-aes-256-gcm', - '2022-blake3-chacha8-poly1305', - '2022-blake3-chacha20-poly1305', +// '2022-blake3-chacha8-poly1305', +// '2022-blake3-chacha20-poly1305', ]; function ucival_to_bool(val) { @@ -72,7 +72,7 @@ return L.Class.extend({ } }); o.value('', ''); - o.value('all', 'all'); + //o.value('all', 'all'); o.default = ''; }, values_serverlist: function(o) { @@ -133,7 +133,7 @@ return L.Class.extend({ o.datatype = 'port'; o.size = 5; - o = optfunc(form.ListValue, 'method', _('Method'),_('Only 2022-blake3-aes-256-gcm is supported by OpenMPTCProuter Shadowsocks-GO')); + o = optfunc(form.ListValue, 'method', _('Method')); methods.forEach(function(m) { o.value(m); }); diff --git a/omr-bypass/Makefile b/omr-bypass/Makefile index b75dcf46e..59de108fb 100644 --- a/omr-bypass/Makefile +++ b/omr-bypass/Makefile @@ -20,15 +20,33 @@ DEPENDS:=+curl +dnsmasq-full +sqlite3-cli +iptables +iptables-mod-extra +ipset TITLE:=OMR-ByPass endef +define Package/$(PKG_NAME)-nft +SECTION:=net +CATEGORY:=Network +DEPENDS:=+curl +dnsmasq-full +sqlite3-cli +firewall4 +TITLE:=OMR-ByPass NFT +endef + define Package/$(PKG_NAME)/description OMR-ByPass endef +define Package/$(PKG_NAME)/description +OMR-ByPass nft support +endef + define Build/Compile endef define Package/$(PKG_NAME)/install - $(CP) ./files/* $(1)/ + $(CP) ./files/* $(1)/ + rm -f $(1)/etc/init.d/omr-bypass-nft endef -$(eval $(call BuildPackage,$(PKG_NAME))) \ No newline at end of file +define Package/$(PKG_NAME)-nft/install + $(CP) ./files/* $(1)/ + mv $(1)/etc/init.d/omr-bypass-nft $(1)/etc/init.d/omr-bypass +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) +$(eval $(call BuildPackage,$(PKG_NAME)-nft)) \ No newline at end of file diff --git a/omr-bypass/files/etc/init.d/omr-bypass b/omr-bypass/files/etc/init.d/omr-bypass index 1d292c55a..8d2ef20c6 100755 --- a/omr-bypass/files/etc/init.d/omr-bypass +++ b/omr-bypass/files/etc/init.d/omr-bypass @@ -1,5 +1,5 @@ #!/bin/sh /etc/rc.common -# Copyright (C) 2018-2020 Ycarus (Yannick Chabanois) +# Copyright (C) 2018-2023 Ycarus (Yannick Chabanois) for OpenMPTCProuter START=98 STOP=10 @@ -8,13 +8,14 @@ EXTRA_COMMANDS="reload_rules bypass_asn" . /usr/lib/unbound/iptools.sh -if [ -f /usr/sbin/iptables-legacy ]; then - IPTABLES="/usr/sbin/iptables-legacy" - IPTABLESRESTORE="/usr/sbin/iptables-legacy-restore" - IPTABLESSAVE="/usr/sbin/iptables-legacy-save" - IP6TABLES="/usr/sbin/ip6tables-legacy" - IP6TABLESRESTORE="/usr/sbin/ip6tables-legacy-restore" - IP6TABLESSAVE="/usr/sbin/ip6tables-legacy-save" +# Still used by ndpi +if [ -e /usr/sbin/iptables-nft ]; then + IPTABLES="/usr/sbin/iptables-nft" + IPTABLESRESTORE="/usr/sbin/iptables-nft-restore" + IPTABLESSAVE="/usr/sbin/iptables-nft-save" + IP6TABLES="/usr/sbin/ip6tables-nft" + IP6TABLESRESTORE="/usr/sbin/ip6tables-nft-restore" + IP6TABLESSAVE="/usr/sbin/ip6tables-nft-save" else IPTABLES="/usr/sbin/iptables" IPTABLESRESTORE="/usr/sbin/iptables-restore" @@ -58,9 +59,13 @@ _bypass_ip() { valid_ip4=$( valid_subnet4 $ip) valid_ip6=$( valid_subnet6 $ip) if [ "$valid_ip4" = "ok" ]; then - ipset -q add omr_dst_bypass_$type $ip + uci -q add_list firewall.omr_dst_bypass_${type}_4.entry=$ip + uci -q set firewall.omr_dst_bypass_${type}_4.enabled='1' + uci -q set firewall.omr_dst_bypass_${type}_dstip_4.enabled='1' elif [ "$valid_ip6" = "ok" ]; then - ipset -q add omr6_dst_bypass_$type $ip + uci -q add_list firewall.omr_dst_bypass_${type}_6.entry=$ip + uci -q set firewall.omr_dst_bypass_${type}_6.enabled='1' + uci -q set firewall.omr_dst_bypass_${type}_dstip_6.enabled='1' fi } @@ -76,6 +81,7 @@ _bypass_domains() { [ -z "$intf" ] && intf="all" config_get vpn $1 vpn [ "$vpn" = "1" ] && intf="srv_vpn1" + #echo "bypass $domain $enabled $family $intf $vpn" [ "$enabled" = "0" ] && return [ -z "$domain" ] && return [ -z "$family" ] && family="ipv4ipv6" @@ -107,6 +113,7 @@ _bypass_domains() { _bypass_domain $validdomain $intf $family $noipv6 done else + #echo "_bypass_domain $domain $intf $family $noipv6" _bypass_domain $domain $intf $family $noipv6 fi } @@ -117,7 +124,6 @@ _bypass_domain() { local family=$3 local noipv6=$4 intf=$(echo $intf | sed -e 's/\./_/') - [ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return [ -z "$intf" ] && intf="all" if [ -n "$domain" ]; then domain=$(echo $domain | sed 's:^\.::') @@ -134,35 +140,13 @@ _bypass_domain() { done fi fi - if [ "$(uci -q get dhcp.@dnsmasq[0].ipset | grep /$domain/)" = "" ]; then - if [ "$family" = "ipv4ipv6" ]; then - uci -q add_list dhcp.@dnsmasq[0].ipset="/$domain/omr_dst_bypass_$intf,omr6_dst_bypass_$intf" - elif [ "$family" = "ipv4" ]; then - uci -q add_list dhcp.@dnsmasq[0].ipset="/$domain/omr_dst_bypass_$intf" - elif [ "$family" = "ipv6" ]; then - uci -q add_list dhcp.@dnsmasq[0].ipset="/$domain/omr6_dst_bypass_$intf" - fi + if [ "$(uci -q get dhcp.omr_dst_bypass_$intf | grep /$domain/)" = "" ]; then + uci -q add_list dhcp.omr_dst_bypass_$intf.domain=$domain add_domains="true" - else - dnsmasqipset=$(uci -q get dhcp.@dnsmasq[0].ipset | sed 's/ /\n/g') - for dnsipset in $dnsmasqipset; do - if [ "$(echo $dnsipset | cut -d/ -f2)" = "$domain" ]; then - uci -q del_list dhcp.@dnsmasq[0].ipset=$dnsipset - if [ "$family" = "ipv4ipv6" ]; then - uci -q add_list dhcp.@dnsmasq[0].ipset="$dnsipset,omr_dst_bypass_$intf,omr6_dst_bypass_$intf" - elif [ "$family" = "ipv4" ]; then - uci -q add_list dhcp.@dnsmasq[0].ipset="$dnsipset,omr_dst_bypass_$intf" - elif [ "$family" = "ipv6" ]; then - uci -q add_list dhcp.@dnsmasq[0].ipset="$dnsipset,omr6_dst_bypass_$intf" - fi - add_domains="true" - fi - done fi if [ "$(uci -q get dhcp.@dnsmasq[0].noipv6 | grep /$domain/)" = "" ] && [ "$noipv6" = "1" ]; then uci -q add_list dhcp.@dnsmasq[0].noipv6="$domain" fi - #logger -t "omr-bypass" "Get IPs of $domain... Done" fi } @@ -176,38 +160,13 @@ _bypass_mac() { config_get enabled $1 enabled [ "$enabled" = "0" ] && return intf=$(echo $intf | sed -e 's/\./_/') - [ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return local intfid="$(uci -q get omr-bypass.$intf.id)" [ -z "$intf" ] && intf="all" [ -z "$mac" ] && return - if [ "$intf" = "all" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass -m mac --mac-source $mac -j MARK --set-mark 0x539 - COMMIT - EOF - if [ "$disableipv6" = "0" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 -m mac --mac-source $mac -j MARK --set-mark 0x6539 - COMMIT - EOF - fi - else - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass -m mac --mac-source $mac -j MARK --set-mark 0x539$intfid - COMMIT - EOF - if [ "$disableipv6" = "0" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 -m mac --mac-source $mac -j MARK --set-mark 0x6539$intfid - COMMIT - EOF - fi - fi + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_$intf_mac.src_mac="$mac" + EOF } _bypass_lan_ip() { @@ -226,44 +185,16 @@ _bypass_lan_ip() { [ -z "$ip" ] && return valid_ip4=$(valid_subnet4 $ip) valid_ip6=$(valid_subnet6 $ip) - if [ "$intf" = "all" ]; then - if [ "$valid_ip4" = "ok" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass -s $ip -j MARK --set-mark 0x539 - COMMIT - EOF - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-local -s $ip -j MARK --set-mark 0x539 - COMMIT - EOF - elif [ "$valid_ip6" = "ok" ] && [ "$disableipv6" = "0" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 -s $ip -j MARK --set-mark 0x6539 - COMMIT - EOF - fi - else - if [ "$valid_ip4" = "ok" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass -s $ip -j MARK --set-mark 0x539$intfid - COMMIT - EOF - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-local -s $ip -j MARK --set-mark 0x539$intfid - COMMIT - EOF - elif [ "$valid_ip6" = "ok" ] && [ "$disableipv6" = "0" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 -s $ip -j MARK --set-mark 0x6539$intfid - COMMIT - EOF - fi + if [ "$valid_ip4" = "ok" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_srcip_4.src_ip="$ip" + set firewall.omr_dst_bypass_${intf}_srcip_4.enabled='1' + EOF + elif [ "$valid_ip6" = "ok" ] && [ "$disableipv6" = "0" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_srcip_6.src_ip="$ip" + set firewall.omr_dst_bypass_${intf}_srcip_6.enabled='1' + EOF fi } @@ -278,49 +209,24 @@ _bypass_dest_port() { config_get enabled $1 enabled [ "$enabled" = "0" ] && return intf=$(echo $intf | sed -e 's/\./_/') - [ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return local intfid="$(uci -q get omr-bypass.$intf.id)" [ -z "$intf" ] && intf="all" [ -z "$dport" ] && return dport="$(echo $dport | sed 's/-/:/')" [ -z "$proto" ] && return - if [ "$intf" = "all" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass --protocol $proto --destination-port $dport -j MARK --set-mark 0x539 - COMMIT + if [ "$proto" = "tcp" ] || [ "$proto" = "tcp udp" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_dstport_tcp.dst_port="$dport" + set firewall.omr_dst_bypass_${intf}_dstport_tcp.enabled='1' EOF - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-local --protocol $proto --destination-port $dport -j MARK --set-mark 0x539 - COMMIT + fi + if [ "$proto" = "udp" ] || [ "$proto" = "tcp udp" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_dstport_udp.dst_port="$dport" + set firewall.omr_dst_bypass_${intf}_dstport_udp.enabled='1' EOF - if [ "$disableipv6" = "0" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 --protocol $proto --destination-port $dport -j MARK --set-mark 0x6539 - COMMIT - EOF - fi - else - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass --protocol $proto --destination-port $dport -j MARK --set-mark 0x539$intfid - COMMIT - EOF - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-local --protocol $proto --destination-port $dport -j MARK --set-mark 0x539$intfid - COMMIT - EOF - if [ "$disableipv6" = "0" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 --protocol $proto --destination-port $dport -j MARK --set-mark 0x6539$intfid - COMMIT - EOF - fi fi } @@ -335,49 +241,24 @@ _bypass_src_port() { config_get enabled $1 enabled [ "$enabled" = "0" ] && return intf=$(echo $intf | sed -e 's/\./_/') - [ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return local intfid="$(uci -q get omr-bypass.$intf.id)" [ -z "$intf" ] && intf="all" [ -z "$sport" ] && return sport="$(echo $sport | sed 's/-/:/')" [ -z "$proto" ] && return - if [ "$intf" = "all" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass --protocol $proto --source-port $sport -j MARK --set-mark 0x539 - COMMIT + if [ "$proto" = "tcp" ] || [ "$proto" = "tcp udp" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_dstport_tcp.dst_port="$dport" + set firewall.omr_dst_bypass_${intf}_dstport_tcp.enabled='1' EOF - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-local --protocol $proto --source-port $sport -j MARK --set-mark 0x539 - COMMIT + fi + if [ "$proto" = "udp" ] || [ "$proto" = "tcp udp" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_dstport_udp.dst_port="$dport" + set firewall.omr_dst_bypass_${intf}_dstport_udp.enabled='1' EOF - if [ "$disableipv6" = "0" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 --protocol $proto --source-port $sport -j MARK --set-mark 0x6539 - COMMIT - EOF - fi - else - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass --protocol $proto --source-port $sport -j MARK --set-mark 0x539$intfid - COMMIT - EOF - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-local --protocol $proto --source-port $sport -j MARK --set-mark 0x539$intfid - COMMIT - EOF - if [ "$disableipv6" = "0" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 --protocol $proto --source-port $sport -j MARK --set-mark 0x6539$intfid - COMMIT - EOF - fi fi } @@ -398,7 +279,7 @@ _bypass_proto() { [ -z "$noipv6" ] && noipv6="0" [ -z "$family" ] && family="ipv4ipv6" intf=$(echo $intf | sed -e 's/\./_/') - [ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return local intfid="$(uci -q get omr-bypass.$intf.id)" [ -z "$intf" ] && intf="all" @@ -408,8 +289,8 @@ _bypass_proto() { if [ "$family" = "ipv4" ] || [ "$family" = "ipv4ipv6" ]; then $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF *mangle - -A omr-bypass-dpi -m ndpi --proto $proto -j MARK --set-mark 0x539 - -A omr-bypass-dpi -m mark --mark 0x539 -j RETURN + -A omr-bypass-dpi -m ndpi --proto $proto -j MARK --set-mark 0x4539 + -A omr-bypass-dpi -m mark --mark 0x4539 -j RETURN COMMIT EOF fi @@ -425,8 +306,8 @@ _bypass_proto() { if [ "$family" = "ipv4" ] || [ "$family" = "ipv4ipv6" ]; then $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF *mangle - -A omr-bypass-dpi -m ndpi --proto $proto -j MARK --set-mark 0x539$intfid - -A omr-bypass-dpi -m mark --mark 0x539$intfid -j RETURN + -A omr-bypass-dpi -m ndpi --proto $proto -j MARK --set-mark 0x4539$intfid + -A omr-bypass-dpi -m mark --mark 0x4539$intfid -j RETURN COMMIT EOF fi @@ -497,74 +378,84 @@ _bypass_proto_without_ndpi() { [ -z "$noipv6" ] && noipv6="0" [ -z "$family" ] && family="ipv4ipv6" intf=$(echo $intf | sed -e 's/\./_/') - [ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return local intfid="$(uci -q get omr-bypass.$intf.id)" [ -z "$intf" ] && intf="all" + [ "$intf" = "all" ] && intfid="" [ -z "$proto" ] && return if [ "$(uci -q get openmptcprouter.settings.ndpi)" == "0" ] || [ "$ndpi" == "0" ] || [ "$vpn" = "1" ]; then ALLIPS=$(sqlite3 /usr/share/omr-bypass/omr-bypass.db "select ip from ipproto where proto=\"$proto\";" ".exit") if [ -n "$ALLIPS" ]; then if [ "$vpn" != "1" ]; then - ipset -q flush bypass_$proto > /dev/null 2>&1 - ipset -q flush bypass6_$proto > /dev/null 2>&1 - ipset -q --exist restore <<-EOF - create bypass_$proto hash:net hashsize 64 - create bypass6_$proto hash:net family inet6 hashsize 64 + uci -q batch <<-EOF >/dev/null + set firewall.bypass_$proto=ipset + set firewall.bypass_$proto.name="bypass_$proto" + set firewall.bypass_$proto.match='dest_ip' + set firewall.bypass_$proto_rule=rule + set firewall.bypass_$proto_rule.name="bypass_$proto" + set firewall.bypass_$proto_rule.src='lan' + set firewall.bypass_$proto_rule.dest='*' + set firewall.bypass_$proto_rule.target='MARK' + set firewall.bypass_$proto_rule.set_xmark="4539${intfid}" + commit firewall EOF + uci -q batch <<-EOF >/dev/null + set firewall.bypass6_$proto=ipset + set firewall.bypass6_$proto.name="bypas6s_$proto" + set firewall.bypass6_$proto.match='dest_ip' + set firewall.bypass6_$proto_rule=rule + set firewall.bypass6_$proto_rule.name="bypass6_$proto" + set firewall.bypass6_$proto_rule.src='lan' + set firewall.bypass6_$proto_rule.dest='*' + set firewall.bypass6_$proto_rule.target='MARK' + set firewall.bypass6_$proto_rule.set_xmark="6539${intfid}" + commit firewall + EOF + #if [ "$intfid" != "" ]; then + # uci -q batch <<-EOF >/dev/null + # delete network.${1}_fw_rule=rule + # set network.${1}_fw_rule=rule + # set network.${1}_fw_rule.priority=1 + # set network.${1}_fw_rule.mark=0x539${intfid} + # set network.${1}_fw_rule.lookup=${intfid} + # delete network.${1}_fw_rule6=rule6 + # set network.${1}_fw_rule6=rule6 + # set network.${1}_fw_rule6.priority=1 + # set network.${1}_fw_rule6.mark=0x6539${intfid} + # set network.${1}_fw_rule6.lookup=${intfid} + # commit network + # EOF + #fi + + #ipset -q flush bypass_$proto > /dev/null 2>&1 + #ipset -q flush bypass6_$proto > /dev/null 2>&1 + #ipset -q --exist restore <<-EOF + #create bypass_$proto hash:net hashsize 64 + #create bypass6_$proto hash:net family inet6 hashsize 64 + #EOF fi for ip in $ALLIPS; do valid_ip4=$( valid_subnet4 $ip) valid_ip6=$( valid_subnet6 $ip) if [ "$valid_ip4" = "ok" ]; then if [ "$vpn" != "1" ]; then - ipset -q add bypass_$proto $ip + #ipset -q add bypass_$proto $ip + uci -q add_list firewall.bypass_$proto.entry=$ip else - ipset -q add omr_dst_bypass_$intf $ip + #ipset -q add omr_dst_bypass_$intf $ip + uci -q add_list firewall.omr_dst_bypass_$intf_4.entry=$ip fi elif [ "$valid_ip6" = "ok" ]; then if [ "$vpn" != "1" ]; then - ipset -q add bypass6_$proto $ip + #ipset -q add bypass6_$proto $ip + uci -q add_list firewall.bypass6_$proto.entry=$ip else - ipset -q add omr6_dst_bypass_$intf $ip + #ipset -q add omr6_dst_bypass_$intf $ip + uci -q add_list firewall.omr6_dst_bypass_$intf_4.entry=$ip fi fi done - if [ "$intf" = "all" ]; then - if [ "$family" = "ipv4" ] || [ "$family" = "ipv4ipv6" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-dpi -m set --match-set bypass_$proto dst -j MARK --set-mark 0x539 - -A omr-bypass-dpi -m mark --mark 0x539 -j RETURN - COMMIT - EOF - fi - if [ "$disableipv6" = "0" ] && ([ "$family" = "ipv6" ] || [ "$family" = "ipv4ipv6" ]); then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6-dpi -m set --match-set bypass6_$proto dst -j MARK --set-mark 0x6539 - -A omr-bypass6-dpi -m mark --mark 0x6539 -j RETURN - COMMIT - EOF - fi - elif [ "$vpn" != "1" ]; then - if [ "$family" = "ipv4" ] || [ "$family" = "ipv4ipv6" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-dpi -m set --match-set bypass_$proto dst -j MARK --set-mark 0x539$intfid - -A omr-bypass-dpi -m mark --mark 0x539$intfid -j RETURN - COMMIT - EOF - fi - if [ "$disableipv6" = "0" ] && ([ "$family" = "ipv6" ] || [ "$family" = "ipv4ipv6" ]); then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6-dpi -m set --match-set bypass6_$proto dst -j MARK --set-mark 0x6539$intfid - -A omr-bypass6-dpi -m mark --mark 0x6539$intfid -j RETURN - COMMIT - EOF - fi - fi fi fi # Use dnsmasq ipset to bypass domains of the proto @@ -609,52 +500,15 @@ _bypass_proto_without_ndpi() { } _intf_rule_ss_rules() { - rule_name=$1 - [ "$rule_name" = "ss_rules" ] && rule_name="def" - if [ "$($IPTABLES --wait=40 -t nat -L -n | grep ssr_${rule_name}_dst)" != "" ] && [ "$($IPTABLESSAVE 2>/dev/null | grep ssr_${rule_name}_dst | grep omr_dst_bypass_$intf)" = "" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *nat - -I ssr_${rule_name}_dst 1 -m set --match-set omr_dst_bypass_$intf dst -j MARK --set-mark 0x539$count - -I ssr_${rule_name}_dst 2 -m mark --mark 0x539$count -j RETURN - COMMIT - EOF - fi - if [ "$($IPTABLES --wait=40 -t nat -L -n | grep ssr_${rule_name}_local_out)" != "" ] && [ "$($IPTABLESSAVE 2>/dev/null | grep ssr_${rule_name}_local_out | grep omr_dst_bypass_$intf)" = "" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *nat - -I ssr_${rule_name}_local_out 1 -m set --match-set omr_dst_bypass_$intf dst -j MARK --set-mark 0x539$count - -I ssr_${rule_name}_local_out 2 -m mark --mark 0x539$count -j RETURN - COMMIT - EOF - fi - if [ "$($IPTABLES --wait=40 -t nat -L -n | grep ssr_${rule_name}_pre_src)" != "" ] && [ "$($IPTABLESSAVE 2>/dev/null | grep ssr_${rule_name}_pre_src | grep omr_dst_bypass_$intf)" = "" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *nat - -I ssr_${rule_name}_pre_src 1 -m set --match-set omr_dst_bypass_$intf dst -j MARK --set-mark 0x539$count - -I ssr_${rule_name}_pre_src 2 -m mark --mark 0x539$count -j RETURN - COMMIT - EOF - fi + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 ss_rules_dst_tcp ip daddr @omr_dst_bypass_${intf}_4 accept + nft insert rule inet fw4 ss_rules_local_out ip daddr @omr_dst_bypass_${intf}_4 accept + EOF if [ "$disableipv6" = "0" ]; then - if [ "$($IP6TABLES --wait=40 -t mangle -L -n | grep omr6_dst_bypass_$intf)" = "" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -I omr-bypass6 1 -m set --match-set omr6_dst_bypass_$intf dst -j MARK --set-mark 0x6539$count - COMMIT - EOF - fi - if [ "$($IP6TABLES --wait=40 -t nat -L -n | grep ssr6_${rule_name}_pre_src)" != "" ] && [ "$($IP6TABLESSAVE 2>/dev/null | grep ssr6 | grep omr6_dst_bypass_$intf)" = "" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *nat - -I ssr6_${rule_name}_dst 1 -m set --match-set omr6_dst_bypass_$intf dst -j MARK --set-mark 0x6539$count - -I ssr6_${rule_name}_dst 2 -m mark --mark 0x6539$count -j RETURN - -I ssr6_${rule_name}_local_out 1 -m set --match-set omr6_dst_bypass_$intf dst -j MARK --set-mark 0x6539$count - -I ssr6_${rule_name}_local_out 2 -m mark --mark 0x6539$count -j RETURN - -I ssr6_${rule_name}_pre_src 1 -m set --match-set omr6_dst_bypass_$intf dst -j MARK --set-mark 0x6539$count - -I ssr6_${rule_name}_pre_src 2 -m mark --mark 0x6539$count -j RETURN - COMMIT - EOF - fi + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 ss_rules_dst_tcp ip6 daddr @omr_dst_bypass_${intf}_6 accept + nft insert rule inet fw4 ss_rules_local_out ip6 daddr @omr_dst_bypass_${intf}_6 accept + EOF fi } @@ -766,56 +620,133 @@ _intf_rule_xray_rules() { _intf_rule() { local intf - intf=$(ifstatus "$1" | jsonfilter -q -e '@["l3_device"]') + [ "$1" = "all" ] && intf="all" + [ -z "$intf" ] && intf=$(ifstatus "$1" | jsonfilter -q -e '@["l3_device"]') [ -n "$(echo $intf | grep '@')" ] && intf=$(ifstatus "$1" | jsonfilter -q -e '@["device"]') [ -z "$intf" ] && config_get intf $1 device [ -n "$(echo $intf | grep '/')" ] && return #count=$((count+1)) - config_get count $1 metric + [ "$intf" != "all" ] && config_get count $1 metric + [ "$intf" = "all" ] && count="" local mode #config_get mode $1 multipath "off" #[ "$mode" = "off" ] && return - [ -z "$count" ] && return + [ "$intf" != "all" ] && [ -z "$count" ] && return [ -z "$intf" ] && return intf=$(echo $intf | sed -e 's/\./_/') + intf=$(echo $intf | sed -e 's/-/_/') [ "$(echo $1 | grep _dev)" != "" ] && return - [ -z "$RELOAD" ] || [ "$(ipset --list | grep omr_dst_bypass_$intf)" = "" ] && { - unset RELOAD - ipset -q flush omr_dst_bypass_$intf > /dev/null 2>&1 - ipset -q flush omr6_dst_bypass_$intf > /dev/null 2>&1 - ipset -q --exist restore <<-EOF - create omr_dst_bypass_$intf hash:net hashsize 64 - create omr6_dst_bypass_$intf hash:net family inet6 hashsize 64 + [ "$intf" = "lo" ] && return + [ -z "$intf" ] && return +# [ -z "$RELOAD" ] || [ "$(uci show firewall.omr_dst_bypass_$intf_4)" = "" ] && { + #unset RELOAD + #echo "$intf ip set dhcp" + uci batch <<-EOF + set dhcp.omr_dst_bypass_$intf=ipset + set dhcp.omr_dst_bypass_$intf.name="omr_dst_bypass_${intf}_4,omr_dst_bypass_${intf}_6" + commit dhcp EOF - if [ "$(uci -q get openmptcprouter.settings.uci_rules)" = "1" ]; then + #echo "firewall omr_dst_bypass ipset" + uci -q batch <<-EOF + set firewall.omr_dst_bypass_${intf}_4=ipset + set firewall.omr_dst_bypass_${intf}_4.name="omr_dst_bypass_${intf}_4" + set firewall.omr_dst_bypass_${intf}_4.match='dest_ip' + EOF + #echo "firewall omr_dst_bypass rules" + if [ "$disableipv6" = "0" ]; then + protocol="4 6" + else + protocol="4" + fi + for ipv46 in $protocol; do + echo "ipv46: $ipv46 for $intf" + uci batch <<-EOF + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.name="omr_dst_bypass_${intf}_rule" + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.ipset="omr_dst_bypass_${intf}_4" + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.name="omr_dst_bypass_${intf}_srcip" + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.ipset="omr_dst_bypass_${intf}_4" + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.name='omr_dst_bypass_${intf}_mac' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.name="omr_dst_bypass_${intf}_srcport" + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.proto='tcp' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.name="omr_dst_bypass_${intf}_srcport" + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.proto='udp' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.name="omr_dst_bypass_${intf}_dstport" + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.name="omr_dst_bypass_${intf}_dstport" + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.set_xmark="${ipv46}539${count}" + commit firewall + EOF + done + if [ "$intf" = "all" ]; then uci -q batch <<-EOF >/dev/null - delete network.${1}_fw_rule=rule - set network.${1}_fw_rule=rule - set network.${1}_fw_rule.priority=1 - set network.${1}_fw_rule.mark=0x539${count} - set network.${1}_fw_rule.lookup=${count} - delete network.${1}_fw_rule6=rule6 - set network.${1}_fw_rule6=rule6 - set network.${1}_fw_rule6.priority=1 - set network.${1}_fw_rule6.mark=0x6539${count} - set network.${1}_fw_rule6.lookup=${count} + delete network.${intf}_fw_rule=rule + set network.${intf}_fw_rule=rule + set network.${intf}_fw_rule.priority=1 + set network.${intf}_fw_rule.mark=0x4539 + set network.${intf}_fw_rule.lookup=991337 + delete network.${intf}_fw_rule6=rule6 + set network.${intf}_fw_rule6=rule6 + set network.${intf}_fw_rule6.priority=1 + set network.${intf}_fw_rule6.mark=0x6539 + set network.${intf}_fw_rule6.lookup=6991337 commit network EOF else - ip rule add prio 1 fwmark 0x539$count lookup $count pref 1 > /dev/null 2>&1 - ip -6 rule add prio 1 fwmark 0x6539$count lookup 6$count pref 1 > /dev/null 2>&1 + uci -q batch <<-EOF >/dev/null + delete network.${intf}_fw_rule=rule + set network.${intf}_fw_rule=rule + set network.${intf}_fw_rule.priority=1 + set network.${intf}_fw_rule.mark=0x4539${count} + set network.${intf}_fw_rule.lookup=${count} + delete network.${intf}_fw_rule6=rule6 + set network.${intf}_fw_rule6=rule6 + set network.${intf}_fw_rule6.priority=1 + set network.${intf}_fw_rule6.mark=0x6539${count} + set network.${intf}_fw_rule6.lookup=${count} + commit network + EOF fi - } - if [ "$($IPTABLESSAVE 2>/dev/null | grep omr-bypass | grep omr_dst_bypass_$intf)" = "" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -I omr-bypass 1 -m set --match-set omr_dst_bypass_$intf dst -j MARK --set-mark 0x539$count - -I omr-bypass 2 -m mark --mark 0x539$count -j RETURN - -I omr-bypass-local 1 -m set --match-set omr_dst_bypass_$intf dst -j MARK --set-mark 0x539$count - -I omr-bypass-local 2 -m mark --mark 0x539$count -j RETURN - COMMIT - EOF - fi + if [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks" ]; then config_load shadowsocks-libev config_foreach _intf_rule_ss_rules ss_rules @@ -856,7 +787,6 @@ _bypass_asn() { for ip in $asnips; do _bypass_ip $ip $interface done - } bypass_asn() { @@ -872,40 +802,15 @@ _bypass_omr_server() { _ss_rules_config() { - rule_name=$1 - [ "$rule_name" = "ss_rules" ] && rule_name="def" - if [ "$($IPTABLES --wait=40 -t nat -L -n | grep ssr_${rule_name}_pre_src)" != "" ] && [ "$($IPTABLES --wait=40 -t nat -L -n | grep omr_dst_bypass_all)" = "" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *nat - -I ssr_${rule_name}_dst 1 -m set --match-set omr_dst_bypass_all dst -j MARK --set-mark 0x539 - -I ssr_${rule_name}_dst 2 -m mark --mark 0x539 -j RETURN - -I ssr_${rule_name}_local_out 1 -m set --match-set omr_dst_bypass_all dst -j MARK --set-mark 0x539 - -I ssr_${rule_name}_local_out 2 -m mark --mark 0x539 -j RETURN - -I ssr_${rule_name}_pre_src 1 -m set --match-set omr_dst_bypass_all dst -j MARK --set-mark 0x539 - -I ssr_${rule_name}_pre_src 2 -m mark --mark 0x539 -j RETURN - COMMIT - EOF - fi + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 ss_rules_dst_tcp ip daddr @omr_dst_bypass_all_4 accept + nft insert rule inet fw4 ss_rules_local_out ip daddr @omr_dst_bypass_all_4 accept + EOF if [ "$disableipv6" = "0" ]; then - if [ "$($IP6TABLES --wait=40 -t mangle -L -n | grep 'match-set omr6_dst_bypass_all dst MARK set')" = "" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 -m set --match-set omr6_dst_bypass_all dst -j MARK --set-mark 0x6539 - COMMIT - EOF - fi - if [ "$($IP6TABLES --wait=40 -t nat -L -n | grep ssr6_${rule_name}_pre_src)" != "" ] && [ "$($IP6TABLES --wait=40 -t nat -L -n | grep omr6_dst_bypass_all)" = "" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *nat - -I ssr6_${rule_name}_dst 1 -m set --match-set omr6_dst_bypass_all dst -j MARK --set-mark 0x6539 - -I ssr6_${rule_name}_dst 1 -m mark --mark 0x6539 -j RETURN - -I ssr6_${rule_name}_local_out 1 -m set --match-set omr6_dst_bypass_all dst -j MARK --set-mark 0x6539 - -I ssr6_${rule_name}_local_out 2 -m mark --mark 0x6539 -j RETURN - -I ssr6_${rule_name}_pre_src 1 -m set --match-set omr6_dst_bypass_all dst -j MARK --set-mark 0x6539 - -I ssr6_${rule_name}_pre_src 2 -m mark --mark 0x6539 -j RETURN - COMMIT - EOF - fi + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 ss_rules_dst_tcp ip6 daddr @omr_dst_bypass_all_6 accept + nft insert rule inet fw4 ss_rules_local_out ip6 daddr @omr_dst_bypass_all_6 accept + EOF fi } @@ -987,6 +892,18 @@ _xray_rules_config() { fi } +_delete_dhcp_ipset() { + [ -n "$(echo $1 | grep omr_dst_bypass)" ] && { + uci -q delete dhcp.$1 + } +} + +_delete_firewall_rules() { + [ -n "$(echo $1 | grep omr_dst_bypass)" ] && { + uci -q delete firewall.$1 + } +} + boot() { BOOT=1 start "$@" @@ -995,6 +912,16 @@ boot() { start_service() { #local count logger -t "omr-bypass" "Starting OMR-ByPass..." + + config_load dhcp + config_foreach _delete_dhcp_ipset ipset + #uci -q commit dhcp + config_load firewall + config_foreach _delete_firewall_rules rule + config_foreach _delete_firewall_rules ipset + #uci -q commit firewall + + add_domains="false" [ -d /proc/net/xt_ndpi ] && { config_load omr-bypass @@ -1003,128 +930,58 @@ start_service() { disableipv6="$(uci -q get openmptcprouter.settings.disable_ipv6)" #noipv6="$(uci -q get omr-bypass.global.noipv6)" - [ -n "$RELOAD" ] && [ "$(ipset --list | grep omr_dst_bypass_all)" = "" ] && { - unset RELOAD - } - [ -z "$RELOAD" ] && { - ipset -q flush omr_dst_bypass_all > /dev/null 2>&1 - ipset -q flush omr6_dst_bypass_all > /dev/null 2>&1 - ipset -q --exist restore <<-EOF - create omr_dst_bypass_all hash:net hashsize 64 - create omr6_dst_bypass_all hash:net family inet6 hashsize 64 - EOF - ipset -q flush omr_dst_bypass_srv_vpn1 > /dev/null 2>&1 - ipset -q flush omr6_dst_bypass_srv_vpn1 > /dev/null 2>&1 - ipset -q --exist restore <<-EOF - create omr_dst_bypass_srv_vpn1 hash:net hashsize 64 - create omr6_dst_bypass_srv_vpn1 hash:net family inet6 hashsize 64 - EOF - } - $IPTABLESSAVE --counters 2>/dev/null | grep -v omr-bypass | $IPTABLESRESTORE -w --counters 2>/dev/null - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - :omr-bypass - - -A PREROUTING -j omr-bypass - COMMIT + cat > /etc/firewall.omr-bypass <<-EOF + #!/bin/sh + #nft insert rule inet fw4 ss_rules_dst_tcp ip daddr @omr_dst_bypass_all accept + #nft insert rule inet fw4 ss_rules_local_out ip daddr @omr_dst_bypass_all accept EOF - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - :omr-bypass-local - - -A OUTPUT -m addrtype ! --dst-type LOCAL -j omr-bypass-local - COMMIT + uci batch <<-EOF + set firewall.omr_bypass=include + set firewall.omr_bypass.enabled='1' + set firewall.omr_bypass.type='script' + set firewall.omr_bypass.path='/etc/firewall.omr-bypass' + set firewall.omr_bypass.fw4_compatible='1' + commit firewall EOF - if [ "$disableipv6" = "0" ]; then - $IP6TABLESSAVE --counters 2>/dev/null | grep -v omr-bypass6 | $IP6TABLESRESTORE -w --counters 2>/dev/null - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - :omr-bypass6 - - -A PREROUTING -j omr-bypass6 - COMMIT - EOF - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - :omr-bypass6-local - - -A OUTPUT -m addrtype ! --dst-type LOCAL -j omr-bypass6-local - COMMIT - EOF - fi - + echo "intf_rule" config_load network config_foreach _intf_rule interface + _intf_rule all local ndpi_rules="" + echo "bypass server" if [ "$(uci -q get openmptcprouter.settings.bypass_servers)" = "1" ]; then config_load openmptcprouter config_foreach _bypass_omr_server server fi config_load omr-bypass + echo "bypass ip" config_foreach _bypass_ip_set ips + echo "bypass mac" config_foreach _bypass_mac macs + echo "bypass lan ip" config_foreach _bypass_lan_ip lan_ip + echo "bypass dest port" config_foreach _bypass_dest_port dest_port + echo "bypass src port" config_foreach _bypass_src_port src_port + echo "bypass asn" config_foreach _bypass_asn asns - dnsmasqipset=$(uci -q get dhcp.@dnsmasq[0].ipset | sed 's/ /\n/g' | grep -v dst_bypass) - uci -q delete dhcp.@dnsmasq[0].ipset - uci -q delete dhcp.@dnsmasq[0].noipv6 - if [ -n "$dnsmasqipset" ]; then - for dnsipset in $dnsmasqipset; do - ipsets="" - allipsets=$(echo $dnsipset | cut -d/ -f3 | sed 's/,/\n/g') - for ipset in $allipsets; do - [ "$(echo $ipset | grep -v dst_bypass)" != "" ] && { - [ "$ipsets" != "" ] && ipsets="$ipsets,$ipset" - [ "$ipsets" = "" ] && ipsets="$ipset" - } - done - if [ "$ipsets" != "" ]; then - resultipset="/$(echo $dnsipset | cut -d/ -f2)/$ipsets" - [ -n "$resultipset" ] && uci -q add_list dhcp.@dnsmasq[0].ipset=$resultipset - fi - done - fi + echo "bypass domains" config_foreach _bypass_domains domains uci -q commit dhcp - ip rule add prio 1 fwmark 0x539 lookup 991337 > /dev/null 2>&1 - ip -6 rule add prio 1 fwmark 0x6539 lookup 6991337 > /dev/null 2>&1 +# ip rule add prio 1 fwmark 0x4539 lookup 991337 > /dev/null 2>&1 +# ip -6 rule add prio 1 fwmark 0x6539 lookup 6991337 > /dev/null 2>&1 - if [ "$($IPTABLES --wait=40 -t mangle -L -n | grep 'match-set omr_dst_bypass_all dst MARK set')" = "" ]; then - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass -m set --match-set omr_dst_bypass_all dst -j MARK --set-mark 0x539 - -A omr-bypass -m mark --mark 0x539 -j RETURN - COMMIT - EOF - $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass-local -m set --match-set omr_dst_bypass_all dst -j MARK --set-mark 0x539 - -A omr-bypass-local -m mark --mark 0x539 -j RETURN - COMMIT - EOF - fi - if [ "$disableipv6" = "0" ]; then - if [ "$($IP6TABLES --wait=40 -t mangle -L -n | grep 'match-set omr6_dst_bypass_all dst MARK set')" = "" ]; then - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6 -m set --match-set omr6_dst_bypass_all dst -j MARK --set-mark 0x539 - -A omr-bypass6 -m mark --mark 0x539 -j RETURN - COMMIT - EOF - $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF - *mangle - -A omr-bypass6-local -m set --match-set omr6_dst_bypass_all dst -j MARK --set-mark 0x539 - -A omr-bypass6-local -m mark --mark 0x539 -j RETURN - COMMIT - EOF - fi - fi - config_load shadowsocks-libev - config_foreach _ss_rules_config - config_load shadowsocks-rust - config_foreach _ss_rules_config + #config_load shadowsocks-libev + #config_foreach _ss_rules_config ss_rules + ([ "$(uci -q get shadowsocks-libev.sss0.disabled)" != "1" ] || [ "$(uci -q get shadowsocks-rust.sss0.disabled)" != "1" ]) && _ss_rules_config + #config_load shadowsocks-rust + #config_foreach _ss_rules_config ss_rules _v2ray_rules_config _xray_rules_config + # NDPI Netfilter is not available for nftables $IPTABLESSAVE --counters 2>/dev/null | grep -v omr-bypass-dpi | $IPTABLESRESTORE -w --counters 2>/dev/null $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF *mangle @@ -1156,7 +1013,7 @@ start_service() { logger -t "omr-bypass" "Reload dnsmasq..." /etc/init.d/dnsmasq reload } - + fw4 restart # Create a protocol list for UI from a sqlite DB when NDPI is not available sqlite3 /usr/share/omr-bypass/omr-bypass.db "select distinct(proto) from (select proto from hostproto union all select proto from ipproto) a order by proto;" ".exit" > /usr/share/omr-bypass/omr-bypass-proto.lst config_load omr-bypass @@ -1168,15 +1025,25 @@ start_service() { stop_service() { $IPTABLESSAVE --counters 2>/dev/null | grep -v omr-bypass | $IPTABLESRESTORE -w --counters 2>/dev/null - $IPTABLESSAVE --counters 2>/dev/null | grep -v omr_dst | $IPTABLESRESTORE -w --counters 2>/dev/null +# $IPTABLESSAVE --counters 2>/dev/null | grep -v omr_dst | $IPTABLESRESTORE -w --counters 2>/dev/null $IP6TABLESSAVE --counters 2>/dev/null | grep -v omr-bypass6 | $IP6TABLESRESTORE -w --counters 2>/dev/null - $IP6TABLESSAVE --counters 2>/dev/null | grep -v omr6_dst | $IP6TABLESRESTORE -w --counters 2>/dev/null - for setname in $(ipset -n list | grep "omr_"); do - ipset -q destroy "$setname" 2>/dev/null || true - done - for setname in $(ipset list | awk '/Name: bypass_/ {print $2}'); do - ipset -q destroy "$setname" 2>/dev/null || true - done +# $IP6TABLESSAVE --counters 2>/dev/null | grep -v omr6_dst | $IP6TABLESRESTORE -w --counters 2>/dev/null + #for setname in $(ipset -n list | grep "omr_"); do + # ipset -q destroy "$setname" 2>/dev/null || true + #done + #for setname in $(ipset list | awk '/Name: bypass_/ {print $2}'); do + # ipset -q destroy "$setname" 2>/dev/null || true + #done + # disable all rules ? + uci -q set firewall.omr-bypass.enabled='0' + config_load dhcp + config_foreach _delete_dhcp_ipset ipset + uci -q commit dhcp + config_load firewall + config_foreach _delete_firewall_rules rule + config_foreach _delete_firewall_rules ipset + uci -q commit firewall + exit 0 } service_triggers() { diff --git a/omr-bypass/files/etc/init.d/omr-bypass-nft b/omr-bypass/files/etc/init.d/omr-bypass-nft new file mode 100755 index 000000000..3552b3c33 --- /dev/null +++ b/omr-bypass/files/etc/init.d/omr-bypass-nft @@ -0,0 +1,926 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2018-2023 Ycarus (Yannick Chabanois) for OpenMPTCProuter + +START=98 +STOP=10 +USE_PROCD=1 +EXTRA_COMMANDS="reload_rules bypass_asn" + +. /usr/lib/unbound/iptools.sh + +# Still used by ndpi +if [ -e /usr/sbin/iptables-nft ]; then + IPTABLES="/usr/sbin/iptables-nft" + IPTABLESRESTORE="/usr/sbin/iptables-nft-restore" + IPTABLESSAVE="/usr/sbin/iptables-nft-save" + IP6TABLES="/usr/sbin/ip6tables-nft" + IP6TABLESRESTORE="/usr/sbin/ip6tables-nft-restore" + IP6TABLESSAVE="/usr/sbin/ip6tables-nft-save" +else + IPTABLES="/usr/sbin/iptables" + IPTABLESRESTORE="/usr/sbin/iptables-restore" + IPTABLESSAVE="/usr/sbin/iptables-save" + IP6TABLES="/usr/sbin/ip6tables" + IP6TABLESRESTORE="/usr/sbin/ip6tables-restore" + IP6TABLESSAVE="/usr/sbin/ip6tables-save" +fi + +_add_proto() { + protoname=$1 + [ -z "$protoname" ] && return + if [ "$(dd if=/proc/net/xt_ndpi/proto bs=4096 2> /dev/null | grep $protoname)" = "" ]; then + echo "add_custom $protoname" >/proc/net/xt_ndpi/proto + fi + allurls="$(dd if=/proc/net/xt_ndpi/host_proto bs=4096 2> /dev/null)" + hosts="$( uci -q get omr-bypass.$protoname.url )" + for url in $hosts; do + if [ "$(echo "$allurls" | grep -i ^${protoname}: | grep $url)" = "" ]; then + echo "$protoname:$url" >/proc/net/xt_ndpi/host_proto + fi + done + ip="$( uci -q get omr-bypass.$protoname.ip )" + for ip in $ips; do + if [ "$(echo "$allurls" | grep -i ^${protoname}: | grep $ip)" = "" ]; then + echo "$protoname:$ip" >/proc/net/xt_ndpi/ip_proto + fi + done +} + +_add_proto_without_ndpi() { + protoname=$1 + [ -z "$protoname" ] && return + echo "$protoname" >> /usr/share/omr-bypass/omr-bypass-proto.lst +} + +_bypass_ip() { + local ip=$1 + local type=$2 + [ -z "$type" ] && type="all" + valid_ip4=$( valid_subnet4 $ip) + valid_ip6=$( valid_subnet6 $ip) + if [ "$valid_ip4" = "ok" ]; then + uci -q add_list firewall.omr_dst_bypass_${type}_4.entry=$ip + uci -q set firewall.omr_dst_bypass_${type}_4.enabled='1' + uci -q set firewall.omr_dst_bypass_${type}_dstip_4.enabled='1' + elif [ "$valid_ip6" = "ok" ]; then + uci -q add_list firewall.omr_dst_bypass_${type}_6.entry=$ip + uci -q set firewall.omr_dst_bypass_${type}_6.enabled='1' + uci -q set firewall.omr_dst_bypass_${type}_dstip_6.enabled='1' + fi +} + +_bypass_domains() { + local domain + local intf + local enabled + config_get domain $1 name + config_get intf $1 interface + config_get enabled $1 enabled + config_get noipv6 $1 noipv6 + config_get family $1 family + [ -z "$intf" ] && intf="all" + config_get vpn $1 vpn + [ "$vpn" = "1" ] && intf="srv_vpn1" + #echo "bypass $domain $enabled $family $intf $vpn" + [ "$enabled" = "0" ] && return + [ -z "$domain" ] && return + [ -z "$family" ] && family="ipv4ipv6" + [ -z "$noipv6" ] && noipv6="0" + if [ "$(echo $domain | grep '\.$')" != "" ] || [ "$(echo $domain | grep '\.\*$')" != "" ]; then + tlds=`curl --max-time 4 -s -k https://data.iana.org/TLD/tlds-alpha-by-domain.txt` + domain="$(echo '"$domain"' | sed 's:*::')" + domainlist="" + # construct list of domains to query + i=0 + for tld in $tlds; do + i=$((i+1)) + # trim off header + if [ "$i" -lt "12" ] || [ "$i" -gt "50" ]; then + continue + fi + # add to command + domainlist="${domainlist} ${domain}${tld}" + done + domainlist="$(echo $domainlist `# Get the list of valid domains, pass it to awk` \ + | awk '{print tolower($0)}' `# awk lowercases the whole string and passes it to ` \ + | xargs -n8 -P12 `# xargs sends 8 arguments at a time to` \ + dig a +timeout=1 +tries=1 +retry=1 +nocmd +noall +answer `# dig, which passes results (if any) to` \ + | awk '{print $1}' `# awk, which outputs queried domain to` \ + | sed -e 's/.$//' `# sed, which trims off the trailing dot (google.com. -> google.com)` to \ + | grep $domain `# grep, only keep wanted domain` \ + | awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf("%s%s",$i,FS)}{printf("\n")}')" # deduplicate + for validdomain in $domainlist; do + _bypass_domain $validdomain $intf $family $noipv6 + done + else + #echo "_bypass_domain $domain $intf $family $noipv6" + _bypass_domain $domain $intf $family $noipv6 + fi +} + +_bypass_domain() { + local domain=$1 + local intf=$2 + local family=$3 + local noipv6=$4 + intf=$(echo $intf | sed -e 's/\./_/') + [ -z "$intf" ] && intf="all" + if [ -n "$domain" ]; then + domain=$(echo $domain | sed 's:^\.::') + #logger -t "omr-bypass" "Get IPs of $domain..." + if [ -z $RELOAD ]; then + resolve=$(dig a +timeout=1 +tries=1 +nocmd +noall +answer $domain | grep -v CNAME | awk '{print $5}') + for ip in $resolve; do + _bypass_ip $ip $intf + done + if [ "$disableipv6" = "0" ]; then + resolve=$(dig aaaa +timeout=1 +tries=1 +nocmd +noall +answer $domain | grep AAAA | awk '{print $5}') + for ip in $resolve; do + _bypass_ip $ip $intf + done + fi + fi + if [ "$(uci -q get dhcp.omr_dst_bypass_$intf | grep /$domain/)" = "" ]; then + uci -q add_list dhcp.omr_dst_bypass_$intf.domain=$domain + add_domains="true" + fi + if [ "$(uci -q get dhcp.@dnsmasq[0].noipv6 | grep /$domain/)" = "" ] && [ "$noipv6" = "1" ]; then + uci -q add_list dhcp.@dnsmasq[0].noipv6="$domain" + fi + #logger -t "omr-bypass" "Get IPs of $domain... Done" + fi +} + +_bypass_mac() { + local mac + local intf + local enabled + config_get mac $1 mac + config_get intf $1 interface + config_get enabled $1 enabled + [ "$enabled" = "0" ] && return + intf=$(echo $intf | sed -e 's/\./_/') + local intfid="$(uci -q get omr-bypass.$intf.id)" + + [ -z "$intf" ] && intf="all" + [ -z "$mac" ] && return + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_$intf_mac.src_mac="$mac" + EOF +} + +_bypass_lan_ip() { + local ip + local intf + local enabled + config_get ip $1 ip + config_get intf $1 interface + config_get enabled $1 enabled + [ "$enabled" = "0" ] && return + intf=$(echo $intf | sed -e 's/\./_/') + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + local intfid="$(uci -q get omr-bypass.$intf.id)" + + [ -z "$intf" ] && intf="all" + [ -z "$ip" ] && return + valid_ip4=$(valid_subnet4 $ip) + valid_ip6=$(valid_subnet6 $ip) + if [ "$valid_ip4" = "ok" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_srcip_4.src_ip="$ip" + set firewall.omr_dst_bypass_${intf}_srcip_4.enabled='1' + EOF + elif [ "$valid_ip6" = "ok" ] && [ "$disableipv6" = "0" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_srcip_6.src_ip="$ip" + set firewall.omr_dst_bypass_${intf}_srcip_6.enabled='1' + EOF + fi +} + +_bypass_dest_port() { + local intf + local enabled + local dport + local proto + config_get dport $1 dport + config_get proto $1 proto + config_get intf $1 interface + config_get enabled $1 enabled + [ "$enabled" = "0" ] && return + intf=$(echo $intf | sed -e 's/\./_/') + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + local intfid="$(uci -q get omr-bypass.$intf.id)" + + [ -z "$intf" ] && intf="all" + [ -z "$dport" ] && return + dport="$(echo $dport | sed 's/:/-/')" + [ -z "$proto" ] && return + if [ "$proto" = "tcp" ] || [ "$proto" = "tcp udp" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_dstport_tcp.dest_port="$dport" + set firewall.omr_dst_bypass_${intf}_dstport_tcp.enabled='1' + EOF + fi + if [ "$proto" = "udp" ] || [ "$proto" = "tcp udp" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_dstport_udp.dest_port="$dport" + set firewall.omr_dst_bypass_${intf}_dstport_udp.enabled='1' + EOF + fi +} + +_bypass_src_port() { + local intf + local enabled + local sport + local proto + config_get sport $1 sport + config_get proto $1 proto + config_get intf $1 interface + config_get enabled $1 enabled + [ "$enabled" = "0" ] && return + intf=$(echo $intf | sed -e 's/\./_/') + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + local intfid="$(uci -q get omr-bypass.$intf.id)" + + [ -z "$intf" ] && intf="all" + [ -z "$sport" ] && return + sport="$(echo $sport | sed 's/:/-/')" + [ -z "$proto" ] && return + if [ "$proto" = "tcp" ] || [ "$proto" = "tcp udp" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_dstport_tcp.src_port="$sport" + set firewall.omr_dst_bypass_${intf}_dstport_tcp.enabled='1' + EOF + fi + if [ "$proto" = "udp" ] || [ "$proto" = "tcp udp" ]; then + uci -q batch <<-EOF + add_list firewall.omr_dst_bypass_${intf}_dstport_udp.src_port="$sport" + set firewall.omr_dst_bypass_${intf}_dstport_udp.enabled='1' + EOF + fi +} + +_bypass_proto() { + local proto + local intf + local enabled + config_get proto $1 proto + config_get intf $1 interface + config_get enabled $1 enabled + config_get ndpi $1 ndpi + config_get noipv6 $1 noipv6 + config_get family $1 family + config_get vpn $1 vpn + [ "$vpn" = "1" ] && intf="srv_vpn1" + + [ "$enabled" = "0" ] && return + [ -z "$noipv6" ] && noipv6="0" + [ -z "$family" ] && family="ipv4ipv6" + intf=$(echo $intf | sed -e 's/\./_/') + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + local intfid="$(uci -q get omr-bypass.$intf.id)" + + [ -z "$intf" ] && intf="all" + [ -z "$proto" ] && return + if [ "$(uci -q get openmptcprouter.settings.ndpi)" != "0" ] && [ "$ndpi" != "0" ] && [ "$vpn" != "1" ]; then + if [ "$intf" = "all" ]; then + if [ "$family" = "ipv4" ] || [ "$family" = "ipv4ipv6" ]; then + $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF + *mangle + -A omr-bypass-dpi -m ndpi --proto $proto -j MARK --set-mark 0x4539 + -A omr-bypass-dpi -m mark --mark 0x4539 -j RETURN + COMMIT + EOF + fi + if [ "$disableipv6" = "0" ] && ([ "$family" = "ipv6" ] || [ "$family" = "ipv4ipv6" ]); then + $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF + *mangle + -A omr-bypass6-dpi -m ndpi --proto $proto -j MARK --set-mark 0x6539 + -A omr-bypass6-dpi -m mark --mark 0x6539 -j RETURN + COMMIT + EOF + fi + else + if [ "$family" = "ipv4" ] || [ "$family" = "ipv4ipv6" ]; then + $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF + *mangle + -A omr-bypass-dpi -m ndpi --proto $proto -j MARK --set-mark 0x4539$intfid + -A omr-bypass-dpi -m mark --mark 0x4539$intfid -j RETURN + COMMIT + EOF + fi + if [ "$disableipv6" = "0" ] && ([ "$family" = "ipv6" ] || [ "$family" = "ipv4ipv6" ]); then + $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF + *mangle + -A omr-bypass6-dpi -m ndpi --proto $proto -j MARK --set-mark 0x6539$intfid + -A omr-bypass6-dpi -m mark --mark 0x6539$intfid -j RETURN + COMMIT + EOF + fi + fi + fi + # Use dnsmasq ipset to bypass domains of the proto + local domains + domains="$(cat /proc/net/xt_ndpi/host_proto | grep -i $proto: | sed -e "s/$proto://i" -e 's/*//' -e 's/,/ /g')" + if [ -n "$domains" ]; then + tlds=`curl --max-time 4 -s -k https://data.iana.org/TLD/tlds-alpha-by-domain.txt` + for domain in $domains; do + if [ -n "$domain" ]; then + domain="$(echo $domain | sed 's/^\.//')" + if [ "$(echo $domain | grep '\.$')" != "" ]; then + domainlist="" + # construct list of domains to query + i=0 + for tld in $tlds; do + i=$((i+1)) + # trim off header + if [ "$i" -lt "12" ] || [ "$i" -gt "50" ]; then + continue + fi + # add to command + domainlist="${domainlist} ${domain}${tld}" + done + domainlist="$(echo $domainlist `# Get the list of valid domains, pass it to awk` \ + | awk '{print tolower($0)}' `# awk lowercases the whole string and passes it to ` \ + | xargs -n8 -P12 `# xargs sends 8 arguments at a time to` \ + dig a +timeout=1 +tries=1 +retry=1 +nocmd +noall +answer `# dig, which passes results (if any) to` \ + | awk '{print $1}' `# awk, which outputs queried domain to` \ + | sed -e 's/.$//' `# sed, which trims off the trailing dot (google.com. -> google.com)` to \ + | grep $domain `# grep, only keep wanted domain` \ + | awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf("%s%s",$i,FS)}{printf("\n")}')" # deduplicate + for validdomain in $domainlist; do + _bypass_domain $validdomain $intf $family $noipv6 + done + else + _bypass_domain $domain $intf $family $noipv6 + fi + fi + done + fi +} + +_bypass_proto_without_ndpi() { + local proto + local intf + local enabled + config_get proto $1 proto + config_get intf $1 interface + config_get enabled $1 enabled + config_get ndpi $1 ndpi "0" + config_get noipv6 $1 noipv6 + config_get family $1 family + config_get vpn $1 vpn + [ "$vpn" = "1" ] && intf="srv_vpn1" + + [ "$enabled" = "0" ] && return + [ -z "$noipv6" ] && noipv6="0" + [ -z "$family" ] && family="ipv4ipv6" + intf=$(echo $intf | sed -e 's/\./_/') + #[ -n "$intf" ] && [ -z "$(ipset --list | grep omr_dst_bypass_$intf)" ] && return + local intfid="$(uci -q get omr-bypass.$intf.id)" + + [ -z "$intf" ] && intf="all" + [ "$intf" = "all" ] && intfid="" + [ -z "$proto" ] && return + if [ "$(uci -q get openmptcprouter.settings.ndpi)" == "0" ] || [ "$ndpi" == "0" ] || [ "$vpn" = "1" ]; then + ALLIPS=$(sqlite3 /usr/share/omr-bypass/omr-bypass.db "select ip from ipproto where proto=\"$proto\";" ".exit") + if [ -n "$ALLIPS" ]; then + if [ "$vpn" != "1" ]; then + uci -q batch <<-EOF >/dev/null + set firewall.bypass_$proto=ipset + set firewall.bypass_$proto.name="bypass_$proto" + set firewall.bypass_$proto.match='dest_ip' + set firewall.bypass_$proto_rule=rule + set firewall.bypass_$proto_rule.name="bypass_$proto" + set firewall.bypass_$proto_rule.src='lan' + set firewall.bypass_$proto_rule.dest='*' + set firewall.bypass_$proto_rule.target='MARK' + set firewall.bypass_$proto_rule.set_xmark="4539${intfid}" + commit firewall + EOF + uci -q batch <<-EOF >/dev/null + set firewall.bypass6_$proto=ipset + set firewall.bypass6_$proto.name="bypas6s_$proto" + set firewall.bypass6_$proto.match='dest_ip' + set firewall.bypass6_$proto_rule=rule + set firewall.bypass6_$proto_rule.name="bypass6_$proto" + set firewall.bypass6_$proto_rule.src='lan' + set firewall.bypass6_$proto_rule.dest='*' + set firewall.bypass6_$proto_rule.target='MARK' + set firewall.bypass6_$proto_rule.set_xmark="6539${intfid}" + commit firewall + EOF + #if [ "$intfid" != "" ]; then + # uci -q batch <<-EOF >/dev/null + # delete network.${1}_fw_rule=rule + # set network.${1}_fw_rule=rule + # set network.${1}_fw_rule.priority=1 + # set network.${1}_fw_rule.mark=0x539${intfid} + # set network.${1}_fw_rule.lookup=${intfid} + # delete network.${1}_fw_rule6=rule6 + # set network.${1}_fw_rule6=rule6 + # set network.${1}_fw_rule6.priority=1 + # set network.${1}_fw_rule6.mark=0x6539${intfid} + # set network.${1}_fw_rule6.lookup=${intfid} + # commit network + # EOF + #fi + + #ipset -q flush bypass_$proto > /dev/null 2>&1 + #ipset -q flush bypass6_$proto > /dev/null 2>&1 + #ipset -q --exist restore <<-EOF + #create bypass_$proto hash:net hashsize 64 + #create bypass6_$proto hash:net family inet6 hashsize 64 + #EOF + fi + for ip in $ALLIPS; do + valid_ip4=$( valid_subnet4 $ip) + valid_ip6=$( valid_subnet6 $ip) + if [ "$valid_ip4" = "ok" ]; then + if [ "$vpn" != "1" ]; then + #ipset -q add bypass_$proto $ip + uci -q add_list firewall.bypass_$proto.entry=$ip + else + #ipset -q add omr_dst_bypass_$intf $ip + uci -q add_list firewall.omr_dst_bypass_$intf_4.entry=$ip + fi + elif [ "$valid_ip6" = "ok" ]; then + if [ "$vpn" != "1" ]; then + #ipset -q add bypass6_$proto $ip + uci -q add_list firewall.bypass6_$proto.entry=$ip + else + #ipset -q add omr6_dst_bypass_$intf $ip + uci -q add_list firewall.omr6_dst_bypass_$intf_4.entry=$ip + fi + fi + done + fi + fi + # Use dnsmasq ipset to bypass domains of the proto + local domains + #domains="$(cat /proc/net/xt_ndpi/host_proto | grep -i $proto: | sed -e "s/$proto://i" -e 's/*//' -e 's/,/ /g')" + domains=$(sqlite3 /usr/share/omr-bypass/omr-bypass.db "select host from hostproto where proto='"$proto"';" ".exit") + if [ -n "$domains" ]; then + tlds=`curl --max-time 4 -s -k https://data.iana.org/TLD/tlds-alpha-by-domain.txt` + for domain in $domains; do + if [ -n "$domain" ]; then + domain="$(echo $domain | sed 's/^\.//')" + if [ "$(echo $domain | grep '\.$')" != "" ]; then + domainlist="" + # construct list of domains to query + i=0 + for tld in $tlds; do + i=$((i+1)) + # trim off header + if [ "$i" -lt "2" ] || [ "${#tld}" -gt "3" ]; then + continue + fi + # add to command + domainlist="${domainlist} ${domain}${tld}" + done + domainlist="$(echo $domainlist `# Get the list of valid domains, pass it to awk` \ + | awk '{print tolower($0)}' `# awk lowercases the whole string and passes it to ` \ + | xargs -n8 -P12 `# xargs sends 8 arguments at a time to` \ + dig a +timeout=1 +tries=1 +retry=1 +nocmd +noall +answer `# dig, which passes results (if any) to` \ + | awk '{print $1}' `# awk, which outputs queried domain to` \ + | sed 's/.$//' `# sed, which trims off the trailing dot (google.com. -> google.com) to` \ + | grep $domain `# grep, only keep wanted domain` \ + | awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf("%s%s",$i,FS)}{printf("\n")}')" # deduplicate + for validdomain in $domainlist; do + _bypass_domain $validdomain $intf $family $noipv6 + done + else + _bypass_domain $domain $intf $family $noipv6 + fi + fi + done + fi +} + +_intf_rule_ss_rules() { + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 ss_rules_dst_tcp ip daddr @omr_dst_bypass_${intf}_4 accept + nft insert rule inet fw4 ss_rules_local_out ip daddr @omr_dst_bypass_${intf}_4 accept + EOF + if [ "$disableipv6" = "0" ]; then + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 ss_rules_dst_tcp ip6 daddr @omr_dst_bypass_${intf}_6 accept + nft insert rule inet fw4 ss_rules_local_out ip6 daddr @omr_dst_bypass_${intf}_6 accept + EOF + fi +} + +_intf_rule_v2ray_rules() { + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 v2r_rules_dst_tcp ip daddr @omr_dst_bypass_${intf}_4 accept + nft insert rule inet fw4 v2r_rules_local_out ip daddr @omr_dst_bypass_${intf}_4 accept + EOF + if [ "$disableipv6" = "0" ]; then + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 v2r_rules_dst_tcp ip6 daddr @omr_dst_bypass_${intf}_6 accept + nft insert rule inet fw4 v2r_rules_local_out ip6 daddr @omr_dst_bypass_${intf}_6 accept + EOF + fi +} + +_intf_rule_xray_rules() { + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 xr_rules_dst_tcp ip daddr @omr_dst_bypass_${intf}_4 accept + nft insert rule inet fw4 xr_rules_local_out ip daddr @omr_dst_bypass_${intf}_4 accept + EOF + if [ "$disableipv6" = "0" ]; then + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 xr_rules_dst_tcp ip6 daddr @omr_dst_bypass_${intf}_6 accept + nft insert rule inet fw4 xr_rules_local_out ip6 daddr @omr_dst_bypass_${intf}_6 accept + EOF + fi +} + +_intf_rule() { + local intf + [ "$1" = "all" ] && intf="all" + [ -z "$intf" ] && intf=$(ifstatus "$1" | jsonfilter -q -e '@["l3_device"]') + [ -n "$(echo $intf | grep '@')" ] && intf=$(ifstatus "$1" | jsonfilter -q -e '@["device"]') + [ -z "$intf" ] && config_get intf $1 device + [ -n "$(echo $intf | grep '/')" ] && return + #count=$((count+1)) + [ "$intf" != "all" ] && config_get count $1 metric + [ "$intf" = "all" ] && count="" + local mode + #config_get mode $1 multipath "off" + #[ "$mode" = "off" ] && return + [ "$intf" != "all" ] && [ -z "$count" ] && return + [ -z "$intf" ] && return + intf=$(echo $intf | sed -e 's/\./_/') + intf=$(echo $intf | sed -e 's/-/_/') + [ "$(echo $1 | grep _dev)" != "" ] && return + [ "$intf" = "lo" ] && return + [ -z "$intf" ] && return +# [ -z "$RELOAD" ] || [ "$(uci show firewall.omr_dst_bypass_$intf_4)" = "" ] && { + #unset RELOAD + #echo "$intf ip set dhcp" + uci batch <<-EOF + set dhcp.omr_dst_bypass_$intf=ipset + add_list dhcp.omr_dst_bypass_$intf.name="omr_dst_bypass_${intf}_4" + add_list dhcp.omr_dst_bypass_$intf.name="omr_dst_bypass_${intf}_6" + commit dhcp + EOF + #echo "firewall omr_dst_bypass ipset" + uci -q batch <<-EOF + set firewall.omr_dst_bypass_${intf}_4=ipset + set firewall.omr_dst_bypass_${intf}_4.name="omr_dst_bypass_${intf}_4" + set firewall.omr_dst_bypass_${intf}_4.match='dest_ip' + EOF + #echo "firewall omr_dst_bypass rules" + if [ "$disableipv6" = "0" ]; then + protocol="4 6" + else + protocol="4" + fi + for ipv46 in $protocol; do + echo "ipv46: $ipv46 for $intf" + uci batch <<-EOF + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.name="omr_dst_bypass_${intf}_rule" + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.ipset="omr_dst_bypass_${intf}_4" + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_dstip_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.name="omr_dst_bypass_${intf}_srcip" + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.ipset="omr_dst_bypass_${intf}_4" + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_srcip_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.name='omr_dst_bypass_${intf}_mac' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_mac_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.name="omr_dst_bypass_${intf}_srcport" + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.proto='tcp' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_srcport_tcp_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.name="omr_dst_bypass_${intf}_srcport" + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.proto='udp' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_srcport_udp_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.name="omr_dst_bypass_${intf}_dstport" + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_dstport_tcp_${ipv46}.set_xmark="${ipv46}539${count}" + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}=rule + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.name="omr_dst_bypass_${intf}_dstport" + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.src='lan' + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.dest='*' + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.target='MARK' + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.enabled='0' + set firewall.omr_dst_bypass_${intf}_dstport_udp_${ipv46}.set_xmark="${ipv46}539${count}" + commit firewall + EOF + done + if [ "$intf" = "all" ]; then + uci -q batch <<-EOF >/dev/null + delete network.${intf}_fw_rule=rule + set network.${intf}_fw_rule=rule + set network.${intf}_fw_rule.priority=1 + set network.${intf}_fw_rule.mark=0x4539 + set network.${intf}_fw_rule.lookup=991337 + delete network.${intf}_fw_rule6=rule6 + set network.${intf}_fw_rule6=rule6 + set network.${intf}_fw_rule6.priority=1 + set network.${intf}_fw_rule6.mark=0x6539 + set network.${intf}_fw_rule6.lookup=6991337 + commit network + EOF + else + uci -q batch <<-EOF >/dev/null + delete network.${intf}_fw_rule=rule + set network.${intf}_fw_rule=rule + set network.${intf}_fw_rule.priority=1 + set network.${intf}_fw_rule.mark=0x4539${count} + set network.${intf}_fw_rule.lookup=${count} + delete network.${intf}_fw_rule6=rule6 + set network.${intf}_fw_rule6=rule6 + set network.${intf}_fw_rule6.priority=1 + set network.${intf}_fw_rule6.mark=0x6539${count} + set network.${intf}_fw_rule6.lookup=${count} + commit network + EOF + fi + + if [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks" ]; then + config_load shadowsocks-libev + config_foreach _intf_rule_ss_rules ss_rules + elif [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks-rust" ]; then + config_load shadowsocks-rust + config_foreach _intf_rule_ss_rules ss_rules + elif [ "$(uci -q get openmptcprouter.settings.proxy)" = "v2ray" ]; then + _intf_rule_v2ray_rules + elif [ "$(uci -q get openmptcprouter.settings.proxy)" = "xray" ]; then + _intf_rule_xray_rules + fi + + uci -q set omr-bypass.$intf=interface + uci -q set omr-bypass.$intf.id=$count +} + +_bypass_ip_set() { + local ip + local interface + local enabled + config_get ip $1 ip + config_get interface $1 interface + config_get enabled $1 enabled + [ "$enabled" = "0" ] && return + _bypass_ip $ip $interface +} + +_bypass_asn() { + local asn + local interface + local enabled + config_get asn $1 asn + config_get interface $1 interface + config_get enabled $1 enabled + [ "$enabled" = "0" ] && return + local asnips + asnips=`curl --max-time 4 -s -k https://stat.ripe.net/data/announced-prefixes/data.json?resource=${asn} | jsonfilter -q -e '@.data.prefixes.*.prefix'` + for ip in $asnips; do + _bypass_ip $ip $interface + done +} + +bypass_asn() { + config_load omr-bypass + config_foreach _bypass_asn asns +} + +_bypass_omr_server() { + local ip + config_get ip $1 ip + _bypass_ip $ip +} + + +_ss_rules_config() { + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 ss_rules_dst_tcp ip daddr @omr_dst_bypass_all_4 accept + nft insert rule inet fw4 ss_rules_local_out ip daddr @omr_dst_bypass_all_4 accept + EOF + if [ "$disableipv6" = "0" ]; then + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 ss_rules_dst_tcp ip6 daddr @omr_dst_bypass_all_6 accept + nft insert rule inet fw4 ss_rules_local_out ip6 daddr @omr_dst_bypass_all_6 accept + EOF + fi +} + +_v2ray_rules_config() { + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 v2r_rules_dst_tcp ip daddr @omr_dst_bypass_all_4 accept + nft insert rule inet fw4 v2r_rules_local_out ip daddr @omr_dst_bypass_all_4 accept + EOF + if [ "$disableipv6" = "0" ]; then + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 v2r_rules_dst_tcp ip6 daddr @omr_dst_bypass_all_6 accept + nft insert rule inet fw4 v2r_rules_local_out ip6 daddr @omr_dst_bypass_all_6 accept + EOF + fi +} + +_xray_rules_config() { + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 xr_rules_dst_tcp ip daddr @omr_dst_bypass_all_4 accept + nft insert rule inet fw4 xr_rules_local_out ip daddr @omr_dst_bypass_all_4 accept + EOF + if [ "$disableipv6" = "0" ]; then + cat >> /etc/firewall.omr-bypass <<-EOF + nft insert rule inet fw4 xr_rules_dst_tcp ip6 daddr @omr_dst_bypass_all_6 accept + nft insert rule inet fw4 xr_rules_local_out ip6 daddr @omr_dst_bypass_all_6 accept + EOF + fi +} + +_delete_dhcp_ipset() { + [ -n "$(echo $1 | grep omr_dst_bypass)" ] && { + uci -q delete dhcp.$1 + } +} + +_delete_firewall_rules() { + ([ -n "$(echo $1 | grep omr_dst_bypass)" ] || [ -n "$(echo $1 | grep omr6_dst_bypass)" ]) && { + uci -q delete firewall.$1 + } +} + +boot() { + BOOT=1 + start "$@" +} + +start_service() { + #local count + logger -t "omr-bypass" "Starting OMR-ByPass..." + + config_load dhcp + config_foreach _delete_dhcp_ipset ipset + #uci -q commit dhcp + config_load firewall + config_foreach _delete_firewall_rules rule + config_foreach _delete_firewall_rules ipset + #uci -q commit firewall + + + add_domains="false" + [ -d /proc/net/xt_ndpi ] && { + config_load omr-bypass + config_foreach _add_proto proto + } + disableipv6="$(uci -q get openmptcprouter.settings.disable_ipv6)" + #noipv6="$(uci -q get omr-bypass.global.noipv6)" + + cat > /etc/firewall.omr-bypass <<-EOF + #!/bin/sh + #nft insert rule inet fw4 ss_rules_dst_tcp ip daddr @omr_dst_bypass_all accept + #nft insert rule inet fw4 ss_rules_local_out ip daddr @omr_dst_bypass_all accept + EOF + uci batch <<-EOF + set firewall.omr_bypass=include + set firewall.omr_bypass.enabled='1' + set firewall.omr_bypass.type='script' + set firewall.omr_bypass.path='/etc/firewall.omr-bypass' + set firewall.omr_bypass.fw4_compatible='1' + commit firewall + EOF + echo "intf_rule" + config_load network + config_foreach _intf_rule interface + _intf_rule all + _intf_rule srv_vpn1 + local ndpi_rules="" + echo "bypass server" + if [ "$(uci -q get openmptcprouter.settings.bypass_servers)" = "1" ]; then + config_load openmptcprouter + config_foreach _bypass_omr_server server + fi + config_load omr-bypass + echo "bypass ip" + config_foreach _bypass_ip_set ips + echo "bypass mac" + config_foreach _bypass_mac macs + echo "bypass lan ip" + config_foreach _bypass_lan_ip lan_ip + echo "bypass dest port" + config_foreach _bypass_dest_port dest_port + echo "bypass src port" + config_foreach _bypass_src_port src_port + echo "bypass asn" + config_foreach _bypass_asn asns + echo "bypass domains" + config_foreach _bypass_domains domains + uci -q commit dhcp + +# ip rule add prio 1 fwmark 0x4539 lookup 991337 > /dev/null 2>&1 +# ip -6 rule add prio 1 fwmark 0x6539 lookup 6991337 > /dev/null 2>&1 + + #config_load shadowsocks-libev + #config_foreach _ss_rules_config ss_rules + ([ "$(uci -q get shadowsocks-libev.sss0.disabled)" != "1" ] || [ "$(uci -q get shadowsocks-rust.sss0.disabled)" != "1" ]) && _ss_rules_config + #config_load shadowsocks-rust + #config_foreach _ss_rules_config ss_rules + [ "$(uci -q get v2ray.main.enabled)" = "1" ] && _v2ray_rules_config + [ "$(uci -q get xray.main.enabled)" = "1" ] && _xray_rules_config + + # NDPI Netfilter is not available for nftables + $IPTABLESSAVE --counters 2>/dev/null | grep -v omr-bypass-dpi | $IPTABLESRESTORE -w --counters 2>/dev/null + $IPTABLESRESTORE -w --wait=60 --noflush <<-EOF + *mangle + :omr-bypass-dpi - + -A INPUT -j omr-bypass-dpi + -A FORWARD -j omr-bypass-dpi + COMMIT + EOF + if [ "$disableipv6" = "0" ]; then + $IP6TABLESSAVE --counters | grep -v omr-bypass6-dpi | $IP6TABLESRESTORE -w --counters 2>/dev/null + $IP6TABLESRESTORE -w --wait=60 --noflush <<-EOF + *mangle + :omr-bypass6-dpi - + -A INPUT -j omr-bypass6-dpi + -A FORWARD -j omr-bypass6-dpi + COMMIT + EOF + fi + config_load omr-bypass + [ -d /proc/net/xt_ndpi/proto ] && config_foreach _bypass_proto dpis + config_foreach _bypass_proto_without_ndpi dpis + uci -q commit omr-bypass + + [ -z "$RELOAD" ] && [ "$add_domains" = "true" ] && { + logger -t "omr-bypass" "Restart dnsmasq..." + /etc/init.d/dnsmasq restart + } + [ -n "$RELOAD" ] && [ "$add_domains" = "true" ] && { + logger -t "omr-bypass" "Reload dnsmasq..." + /etc/init.d/dnsmasq reload + } + fw4 restart + # Create a protocol list for UI from a sqlite DB when NDPI is not available + sqlite3 /usr/share/omr-bypass/omr-bypass.db "select distinct(proto) from (select proto from hostproto union all select proto from ipproto) a order by proto;" ".exit" > /usr/share/omr-bypass/omr-bypass-proto.lst + config_load omr-bypass + config_foreach _add_proto_without_ndpi proto + sort < /usr/share/omr-bypass/omr-bypass-proto.lst > /usr/share/omr-bypass/omr-bypass-proto.lst.new + mv /usr/share/omr-bypass/omr-bypass-proto.lst.new /usr/share/omr-bypass/omr-bypass-proto.lst + logger -t "omr-bypass" "OMR-ByPass is running" +} + +stop_service() { + # Rules for ndpi + $IPTABLESSAVE --counters 2>/dev/null | grep -v omr-bypass | $IPTABLESRESTORE -w --counters 2>/dev/null + $IP6TABLESSAVE --counters 2>/dev/null | grep -v omr-bypass6 | $IP6TABLESRESTORE -w --counters 2>/dev/null + # disable all rules + uci -q set firewall.omr_bypass.enabled='0' + config_load dhcp + config_foreach _delete_dhcp_ipset ipset + uci -q commit dhcp + config_load firewall + config_foreach _delete_firewall_rules rule + config_foreach _delete_firewall_rules ipset + uci -q commit firewall + fw4 restart + exit 0 +} + +service_triggers() { + procd_add_reload_trigger omr-bypass network firewall +} + +reload_service() { + RELOAD=1 + start +} + +reload_rules() { + #[ "$( ipset -n list | grep omr_ )" = "" ] && return 0 + RELOAD=1 + start +} diff --git a/omr-dscp/Makefile b/omr-dscp/Makefile new file mode 100644 index 000000000..58d60117f --- /dev/null +++ b/omr-dscp/Makefile @@ -0,0 +1,52 @@ +# +# Copyright (C) 2018-2023 Ycarus (Yannick Chabanois) for OpenMPTCProuter +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=omr-dscp +PKG_VERSION:=0.1 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) +SECTION:=net +CATEGORY:=Network +DEPENDS:= +dnsmasq-full +LINUX_5_4:iptables +LINUX_5_4:iptables-mod-extra +LINUX_5_4:ipset +TITLE:=OMR-DSCP +endef + +define Package/$(PKG_NAME)-nft +SECTION:=net +CATEGORY:=Network +DEPENDS:= +dnsmasq-full +TITLE:=OMR-DSCP nft +endef + +define Package/$(PKG_NAME)/description +OMR-ByPass +endef + +define Package/$(PKG_NAME)-nft/description +OMR-ByPass nft support +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(CP) ./files/* $(1)/ + rm -f $(1)/etc/init.d/omr-dscp-nft +endef + +define Package/$(PKG_NAME)-nft/install + $(CP) ./files/* $(1)/ + mv $(1)/etc/init.d/omr-dscp-nft $(1)/etc/init.d/omr-dscp +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) +$(eval $(call BuildPackage,$(PKG_NAME)-nft)) diff --git a/luci-app-omr-dscp/root/etc/config/dscp b/omr-dscp/files/etc/config/dscp similarity index 100% rename from luci-app-omr-dscp/root/etc/config/dscp rename to omr-dscp/files/etc/config/dscp diff --git a/luci-app-omr-dscp/root/etc/init.d/omr-dscp b/omr-dscp/files/etc/init.d/omr-dscp similarity index 98% rename from luci-app-omr-dscp/root/etc/init.d/omr-dscp rename to omr-dscp/files/etc/init.d/omr-dscp index bed7c3bad..d5b7467c0 100755 --- a/luci-app-omr-dscp/root/etc/init.d/omr-dscp +++ b/omr-dscp/files/etc/init.d/omr-dscp @@ -11,9 +11,9 @@ USE_PROCD=1 # shellcheck disable=SC1091 . /lib/functions.sh -if [ -f /usr/sbin/iptables-legacy ]; then - IPTABLES="/usr/sbin/iptables-legacy" - IP6TABLES="/usr/sbin/ip6tables-legacy" +if [ -e /usr/sbin/iptables-nft ]; then + IPTABLES="/usr/sbin/iptables-nft" + IP6TABLES="/usr/sbin/ip6tables-nft" else IPTABLES="/usr/sbin/iptables" IP6TABLES="/usr/sbin/ip6tables" diff --git a/omr-dscp/files/etc/init.d/omr-dscp-nft b/omr-dscp/files/etc/init.d/omr-dscp-nft new file mode 100755 index 000000000..69081ac7e --- /dev/null +++ b/omr-dscp/files/etc/init.d/omr-dscp-nft @@ -0,0 +1,187 @@ +#!/bin/sh /etc/rc.common +# vim: set noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 : + +# shellcheck disable=SC2034 +START=90 +# shellcheck disable=SC2034 +STOP=10 +# shellcheck disable=SC2034 +USE_PROCD=1 + +# shellcheck disable=SC1091 +. /lib/functions.sh + +# Get the lan interface name +lan_device= +config_load network +config_get lan_device lan ifname + +#config_load dscp + +_add_dscp_domain() { + domain=""; config_get domain "$1" name "" + class=""; config_get class "$1" class "" + [ -n "$domain" ] && [ -n "$class" ] && [ -z "$(uci -q get dhcp.omr_dscp-$class.domain | grep $domain)" ] && { + uci -q batch <<-EOF + add_list dhcp.omr_dscp_$class.domain="$domain" + commit dhcp + EOF + } +} + +_add_dscp_domains_rules() { + for class in cs0 cs1 cs2 cs3 cs4 cs5 cs6 cs7 ef; do + uci -q batch <<-EOF + set dhcp.omr_dscp_${class}=ipset + add_list dhcp.omr_dscp_${class}.name="omr_dscp_${class}_4" + add_list dhcp.omr_dscp_${class}.name="omr_dscp_${class}_6" + commit dhcp + EOF + uci batch <<-EOF + set firewall.omr_dscp_${class}_4=ipset + set firewall.omr_dscp_${class}_4.name="omr_dscp_${class}_4" + set firewall.omr_dscp_${class}_4.match='dest_ip' + set firewall.omr_dscp_${class}_6=ipset + set firewall.omr_dscp_${class}_6.name="omr_dscp_${class}_6" + set firewall.omr_dscp_${class}_6.match='dest_ip' + EOF + uci -q batch <<-EOF + set firewall.omr_dscp_${class}=rule + set firewall.omr_dscp_${class}.ipset="omr_dscp_${class}_4" + set firewall.omr_dscp_${class}.set_dscp="$(echo ${class} | tr '[a-z'] '[A-Z]')" + set firewall.omr_dscp_${class}.target='DSCP' + set firewall.omr_dscp_${class}.src='lan' + set firewall.omr_dscp_${class}.dest='*' + set firewall.omr6_dscp_${class}=rule + set firewall.omr6_dscp_${class}.ipset="omr_dscp_${class}_6" + set firewall.omr6_dscp_${class}.target='DSCP' + set firewall.omr6_dscp_${class}.set_dscp="$(echo ${class} | tr '[a-z'] '[A-Z]')" + set firewall.omr6_dscp_${class}.src='lan' + set firewall.omr6_dscp_${class}.dest='*' + commit firewall + EOF + done +} + +_add_dscp_rules() { + proto="" ; config_get proto "$1" proto all + src_ip="" ; config_get src_ip "$1" src_ip 0.0.0.0/0 + src_port="" ; config_get src_port "$1" src_port 0:65535 + dest_ip="" ; config_get dest_ip "$1" dest_ip 0.0.0.0/0 + dest_port="" ; config_get dest_port "$1" dest_port 0:65535 + class="" ; config_get class "$1" class + direction="" ; config_get direction "$1" direction "upload" + comment="" ; config_get comment "$1" comment "-" + + src_port="$(echo $src_port | sed 's/:/-/g')" + dest_port="$(echo $dest_port | sed 's/:/-/g')" + count=$((count + 1)) + [ "$proto" = "all" ] && proto="tcp udp" + case "$direction" in + upload|both) + # Apply the rule locally + uci -q batch <<-EOF + set firewall.omr_dscp_rule$count=rule + set firewall.omr_dscp_rule$count.name="omr_dscp_rule$count" + set firewall.omr_dscp_rule$count.target="DSCP" + set firewall.omr_dscp_rule$count.set_dscp="$(echo ${class} | tr '[a-z'] '[A-Z]')" + set firewall.omr_dscp_rule$count.src="lan" + set firewall.omr_dscp_rule$count.src_ip="$src_ip" + set firewall.omr_dscp_rule$count.dest_ip="$dest_ip" + set firewall.omr_dscp_rule$count.proto="$proto" + EOF + src_port="$(echo $src_port | sed 's/,/ /g')" + dest_port="$(echo $dest_port | sed 's/,/ /g')" + for port in $src_port; do + uci -q set firewall.omr_dscp_rule$count.src_port="$src_port" + done + for port in $src_port; do + uci -q set firewall.omr_dscp_rule$count.dest_port="$dest_port" + done + #_add_dscp_rule -m multiport --sports "$src_port" -m multiport --dports "$dest_port" + ;; + download|both) + ;; + esac + uci -q commit firewall + +} + + +_add_fwmark_chain() { + _ipt4 -N dscp_mark + _ipt4 -A PREROUTING -i "$lan_device" -j dscp_mark + _ipt4 -A POSTROUTING -j dscp_mark + _ipt6 -N dscp_mark + _ipt6 -A PREROUTING -i "$lan_device" -j dscp_mark + _ipt6 -A POSTROUTING -j dscp_mark + for class in cs4 cs5 cs6 cs7; do + # xtun (hex) -> 0x7874756e + _ipt4 -A dscp_mark \ + -m comment --comment "$class" \ + -m dscp --dscp-class "$class" \ + -j MARK --set-mark 0x7874756e + _ipt6 -A dscp_mark \ + -m comment --comment "$class" \ + -m dscp --dscp-class "$class" \ + -j MARK --set-mark 0x7874756e + done +} + +_remove_ipset_dnsmasq() { + [ -n "$(echo $1 | grep omr_dscp)" ] && uci -q delete dhcp.$1 +} + +_remove_rules() { + ([ -n "$(echo $1 | grep omr_dscp)" ] || [ -n "$(echo $1 | grep omr6_dscp)" ]) && uci -q delete firewall.$1 +} + +_setup_tunnel() { + # Mark the packets to route through xtun0 + _add_fwmark_chain + # tun0: cs0 (default) + # xtun0: cs6 + _ipt4 -A dscp_output -o "tun0" -j DSCP --set-dscp-class cs6 + _ipt6 -A dscp_output -o "tun0" -j DSCP --set-dscp-class cs6 +} + +_cleanup() { + config_load dhcp + config_foreach _remove_ipset_dnsmasq + config_load firewall + config_foreach _remove_rules + uci -q commit dhcp + uci -q commit firewall + fw4 restart +} + +start_service() { + # Cleanup + _cleanup + + config_load dscp + # Add chains + _add_dscp_domains_rules + + # Setup the tunnels dscp / marks + #_setup_tunnel + + # Add rules base on the user configuration + count=0 + config_foreach _add_dscp_rules classify + config_foreach _add_dscp_domain domains + uci -q commit dhcp + fw4 restart +} + +stop_service() { + _cleanup +} + +reload_service() { + start +} + +service_triggers() { + procd_add_reload_trigger dscp +} diff --git a/omr-schedule/Makefile b/omr-schedule/Makefile new file mode 100644 index 000000000..80dbded52 --- /dev/null +++ b/omr-schedule/Makefile @@ -0,0 +1,38 @@ +# +# Copyright (C) 2018-2023 Ycarus (Yannick Chabanois) +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=omr-schedule +PKG_VERSION:=0.1 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) +SECTION:=OMR +CATEGORY:=OMR-Schedule +DEPENDS:=$(foreach p,$(MY_DEPENDS),+$(p)) +TITLE:=OpenMPTCProuter schedule scripts +endef + +define Package/$(PKG_NAME)/description +OpenMPTCProuter schedule scripts +endef + +define Package/$(PKG_NAME)/conffiles +/etc/contabs/root +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/omr-schedule/files/bin/omr-schedule b/omr-schedule/files/bin/omr-schedule new file mode 100755 index 000000000..846c92703 --- /dev/null +++ b/omr-schedule/files/bin/omr-schedule @@ -0,0 +1,12 @@ +#!/bin/sh +. /lib/functions.sh +. /lib/functions/network.sh + +for schedule_bin in /usr/share/omr/schedule.d/*; do + [ -x "$schedule_bin" ] && ( + _log() { + logger -t "omr-schedule-${schedule_bin##*/}" "$*" + } + . "$schedule_bin" 2>&1 + ) +done diff --git a/omr-schedule/files/etc/uci-defaults/3000-omr-schedule b/omr-schedule/files/etc/uci-defaults/3000-omr-schedule new file mode 100755 index 000000000..bb5556c7a --- /dev/null +++ b/omr-schedule/files/etc/uci-defaults/3000-omr-schedule @@ -0,0 +1,4 @@ +#!/bin/sh +if [ ! -f /etc/crontabs/root ] || [ "$(cat /etc/crontabs/root | grep omr-schedule)" = "" ]; then + echo "*/5 * * * * /bin/omr-schedule" >> /etc/crontabs/root +fi diff --git a/mptcp/files/usr/share/omr/post-tracking.d/010-services b/omr-schedule/files/usr/share/omr/schedule.d/010-services similarity index 84% rename from mptcp/files/usr/share/omr/post-tracking.d/010-services rename to omr-schedule/files/usr/share/omr/schedule.d/010-services index 050ad3bc4..d1cb77c22 100755 --- a/mptcp/files/usr/share/omr/post-tracking.d/010-services +++ b/omr-schedule/files/usr/share/omr/schedule.d/010-services @@ -33,15 +33,15 @@ if [ "$(pgrep -f dnsmasq)" = "" ] && [ -f /etc/init.d/dnsmasq ]; then /etc/init.d/dnsmasq restart 2>&1 >/dev/null sleep 5 fi -if [ -f /etc/init.d/unbound ] && [ "$(uci -q get unbound.@unbound[0].enabled)" = "1" ] && [ "$OMR_TRACKER_STATUS" != "ERROR" ]; then +if [ -f /etc/init.d/unbound ] && [ "$(uci -q get unbound.@unbound[0].enabled)" = "1" ]; then if [ "$(pgrep -f unbound)" = "" ]; then _log "Can't find unbound, restart it..." /etc/init.d/unbound restart 2>&1 >/dev/null sleep 5 - elif [ "$(uci -q get openmptcprouter.settings.external_check)" != "0" ] && [ "$(uci -q get unbound.ub_main.listen_port)" = "5353" ] && [ -n "$(dig +timeout=4 +tries=1 openmptcprouter.com -p 5353 | grep 'ANSWER: 0')" ]; then - _log "Can't resolve via unbound, restart it..." - /etc/init.d/unbound restart 2>&1 >/dev/null - sleep 20 +# elif [ "$(uci -q get openmptcprouter.settings.external_check)" != "0" ] && [ "$(uci -q get unbound.ub_main.listen_port)" = "5353" ] && [ -n "$(dig +timeout=4 +tries=1 openmptcprouter.com -p 5353 | grep 'ANSWER: 0')" ]; then +# _log "Can't resolve via unbound, restart it..." +# /etc/init.d/unbound restart 2>&1 >/dev/null +# sleep 5 fi fi @@ -101,10 +101,10 @@ if [ -z "$(pgrep ModemManager)" ] && [ -f /etc/init.d/modemmanager ] && [ -n "$( _log "Can't find ModemManager, restart it..." /etc/init.d/modemmanager restart 2>&1 >/dev/null sleep 5 -#elif [ -n "$(pgrep ModemManager)" ] && [ -f /etc/init.d/modemmanager ] && [ -z "$(uci -q show network | grep modemmanager)" ]; then -# _log "ModemManager not used, stop it..." -# /etc/init.d/modemmanager stop 2>&1 >/dev/null -# sleep 5 +elif [ -n "$(pgrep ModemManager)" ] && [ -f /etc/init.d/modemmanager ] && [ -z "$(uci -q show network | grep modemmanager)" ]; then + _log "ModemManager not used, stop it..." + /etc/init.d/modemmanager stop 2>&1 >/dev/null + sleep 5 fi if [ "$(uci -q get v2ray.main.enabled)" = "1" ] && [ -f /etc/init.d/v2ray ] && [ "$(pgrep -f omr-tracker-v2ray)" = "" ] && [ "$(pgrep -f '/etc/init.d/omr-tracker')" = "" ]; then @@ -138,34 +138,33 @@ restart_omrtracker() { } set_lan_ips() { - config_get ip4table "$1" ip4table - config_get device "$1" device - config_get proto "$1" proto - if [ "$ip4table" = "lan" ] && [ -n "$device" ] && ([ "$proto" = "dhcp" ] || [ "$proto" = "static" ]); then - [ -z "$(uci -q get shadowsocks-libev.ss_rules.ifnames | grep $device)" ] && { - uci -q add_list shadowsocks-libev.ss_rules.ifnames="$device" - uci -q add_list shadowsocks-rust.ss_rules.ifnames="$device" - } - elif [ -n "$device" ] && [ -n "$(uci -q get shadowsocks-libev.ss_rules.ifnames | grep $device)" ]; then - uci -q del_list shadowsocks-libev.ss_rules.ifnames="$device" - uci -q del_list shadowsocks-rust.ss_rules.ifnames="$device" - fi + config_get ip4table "$1" ip4table + config_get device "$1" device + config_get proto "$1" proto + if [ "$ip4table" = "lan" ] && [ -n "$device" ] && ([ "$proto" = "dhcp" ] || [ "$proto" = "static" ]); then + [ -z "$(uci -q get shadowsocks-libev.ss_rules.ifnames | grep $device)" ] && { + uci -q add_list shadowsocks-libev.ss_rules.ifnames="$device" + uci -q add_list shadowsocks-rust.ss_rules.ifnames="$device" + } + elif [ -n "$device" ] && [ -n "$(uci -q get shadowsocks-libev.ss_rules.ifnames | grep $device)" ]; then + uci -q del_list shadowsocks-libev.ss_rules.ifnames="$device" + uci -q del_list shadowsocks-rust.ss_rules.ifnames="$device" + fi } - config_load network config_foreach restart_omrtracker interface -#config_foreach set_lan_ips interface - -mutlipath_fix() { +config_foreach set_lan_ips interface +uci -q commit shadowsocks-libev.ss_rules +uci -q commit shadowsocks-rust.ss_rules +multipath_fix() { config_get multipath "$1" multipath [ "$multipath" != "off" ] && return interface="$(ifstatus $1 | jsonfilter -q -e '@.l3_device' | tr -d '\n')" - [ -n "$interface"] && [ -z "$(multipath $interface | grep deactivated)" ] && /etc/init.d/mptcp reload $interface 2>&1 >/dev/null + [ -n "$interface" ] && [ -z "$(multipath $interface | grep deactivated)" ] && /etc/init.d/mptcp reload $interface 2>&1 >/dev/null } config_load network config_foreach multipath_fix interface - if [ -f /etc/init.d/shadowsocks-libev ] && [ "$(uci -q get shadowsocks-libev.sss0.disabled)" != "1" ] && [ "$(uci -q get shadowsocks-libev.sss0.key)" != "" ] && [ "$(uci -q get shadowsocks-libev.sss0.server)" != "" ] && [ "$(uci -q get shadowsocks-libev.sss0.server)" != "192.18.1.3" ] && [ "$(pgrep -f omr-tracker-ss)" = "" ] && [ "$(pgrep -f '/etc/init.d/omr-tracker')" = "" ]; then _log "Can't find omr-tracker-ss for Shadowsocks libev, restart omr-tracker..." /etc/init.d/omr-tracker restart 2>&1 >/dev/null @@ -201,7 +200,7 @@ if [ -n "$(logread | tail -n 2 | grep 'Ring expansion failed')" ]; then echo 1 > /sys/bus/pci/rescan fi -if [ -f /etc/init.d/omr-bypass ] && [ "$(iptables-save 2>/dev/null | grep omr-bypass)" = "" ] && [ "$(pgrep -f omr-bypass)" = "" ]; then +if [ -f /etc/init.d/omr-bypass ] && (([ -e /usr/sbin/iptables-nft-save ] && [ "$(iptables-nft-save 2>/dev/null | grep omr-bypass)" = "" ]) || [ "$(iptables-save 2>/dev/null | grep omr-bypass)" = "" ]) && [ "$(pgrep -f omr-bypass)" = "" ]; then _log "Can't find omr-bypass rules, restart omr-bypass..." /etc/init.d/omr-bypass 2>&1 >/dev/null sleep 5 @@ -231,7 +230,7 @@ if [ -n "$OMR_TRACKER_INTERFACE" ] && [ "$(uci -q get sqm.${OMR_TRACKER_INTERFAC fi fi -#if [ "$(uci -q show openmptcprouter | grep server)" != "" ] && [ "$(uci -q show openmptcprouter | grep password)" != "" ] && [ "$(pgrep -f openmptcprouter-vps)" = "" ] && [ "$(uci -q show openmptcprouter | grep admin_error=\'1\')" = "" ] && ([ "$(uci -q show openmptcprouter | grep set_firewall=\'1\')" != "" ] || [ -z "$(iptables-save 2>/dev/null | grep omr_dst_bypass_${OMR_TRACKER_DEVICE})" ]); then +#if [ "$(uci -q show openmptcprouter | grep server)" != "" ] && [ "$(uci -q show openmptcprouter | grep password)" != "" ] && [ "$(pgrep -f openmptcprouter-vps)" = "" ] && [ "$(uci -q show openmptcprouter | grep admin_error=\'1\')" = "" ] && ([ "$(uci -q show openmptcprouter | grep set_firewall=\'1\')" != "" ] || (([ -e /usr/sbin/iptables-nft-save ] && [ -z "$(iptables-save 2>/dev/null | grep omr_dst_bypass_${OMR_TRACKER_DEVICE})" ]) || [ -z "$(iptables-save 2>/dev/null | grep omr_dst_bypass_${OMR_TRACKER_DEVICE})" ])); then if [ "$(pgrep -f set_vps_firewall)" = "" ] && [ "$(uci -q show openmptcprouter | grep server)" != "" ] && [ "$(uci -q show openmptcprouter | grep password)" != "" ] && [ "$(pgrep -f openmptcprouter-vps)" = "" ] && [ "$(uci -q show openmptcprouter | grep admin_error=\'1\')" = "" ] && [ "$(uci -q show openmptcprouter | grep set_firewall=\'1\')" != "" ]; then check_server_fw() { [ "$(uci -q get openmptcprouter.$1.set_firewall)" = "1" ] && { diff --git a/omr-schedule/files/usr/share/omr/schedule.d/030-bypass b/omr-schedule/files/usr/share/omr/schedule.d/030-bypass new file mode 100755 index 000000000..e00f41fc0 --- /dev/null +++ b/omr-schedule/files/usr/share/omr/schedule.d/030-bypass @@ -0,0 +1,25 @@ +#!/bin/sh + +bypassipvs4s=$(ipset -o save list omr_dst_bypass_srv_vpn1_4 2>/dev/null) +[ -z "$bypassipvs4s" ] && { + ipv4set=$(nft -j list set inet fw4 "omr_dst_bypass_srv_vpn1_4" 2>/dev/null) + [ -n "$ipv4set" ] && bypassipv4s=$(echo "$ipv4set" | jsonfilter -e @.nftables[1].set.elem[*].prefix | awk '{gsub(/"/,"",$3);gsub(/,/,"/",$3); print $3 $5}') +} +bypassipvs6s=$(ipset -o save list omr_dst_bypass_srv_vpn1_6 2>/dev/null) +[ -z "$bypassipvs6s" ] && { + ipv6set=$(nft -j list set inet fw4 "omr_dst_bypass_srv_vpn1_6" 2>/dev/null) + [ -n "$ipv6set" ] && bypassipv6s=$(echo "$ipv6set" | jsonfilter -e @.nftables[1].set.elem[*].prefix | awk '{gsub(/"/,"",$3);gsub(/,/,"/",$3); print $3 $5}') +} +#" +vpnipv4md5=$(echo "${bypassipv4s}" | md5sum | awk '{print $1}' | tr -d "\n") +vpnipv6md5=$(echo "${bypassipv6s}" | md5sum | awk '{print $1}' | tr -d "\n") +if [ "$vpnipv4md5" != "$(uci -q get omr-bypass.global.vpn_ipv4_md5)" ] || [ "$vpnipv6md5" != "$(uci -q get omr-bypass.global.vpn_ipv6_md5)" ]; then + _log "Set bypass ip on servers" + /etc/init.d/openmptcprouter-vps set_bypass_ips + uci -q batch <<-EOF >/dev/null + set omr-bypass.global=global + set omr-bypass.global.vpn_ipv4_md5=${vpnipv4md5} + set omr-bypass.global.vpn_ipv6_md5=${vpnipv6md5} + commit omr-bypass + EOF +fi diff --git a/mptcp/files/usr/share/omr/post-tracking.d/050-rutx b/omr-schedule/files/usr/share/omr/schedule.d/050-rutx similarity index 100% rename from mptcp/files/usr/share/omr/post-tracking.d/050-rutx rename to omr-schedule/files/usr/share/omr/schedule.d/050-rutx diff --git a/omr-tracker/files/bin/omr-tracker-server b/omr-tracker/files/bin/omr-tracker-server index 7d39f48ea..8760427eb 100755 --- a/omr-tracker/files/bin/omr-tracker-server +++ b/omr-tracker/files/bin/omr-tracker-server @@ -37,6 +37,29 @@ _check_server() { done } +_check_server_intf() { + local host=$1 + local port=$2 + local k=0 + for intf in $(multipath | awk '/default/ {print $1}'); do + while [ "$server_ping" = false ] && [ "$k" -le "$retry" ]; do + ret=$(curl \ + --max-time "$OMR_TRACKER_TIMEOUT" \ + -s \ + -k \ + --interface $intf + "https://${host}:${port}/" + ) + [ -n "$ret" ] && { + server_ping=true + return + } + k=$((k+1)) + sleep "${intervaltries}" + done + done +} + _disable_current() { local serv=$1 config_set $serv current "0" @@ -80,7 +103,8 @@ _check_master() { fi [ -z "$ip" ] && return #_ping_server $ip - _check_server $ip $port + #_check_server $ip $port + _check_server_intf $ip $port if [ "$server_ping" = true ]; then if [ "$(uci -q get shadowsocks-libev.sss${count}.server | tr -d '\n')" != "$ip" ]; then logger -t "OMR-Tracker-Server" "Master server ${name} up ($ip), set it back" @@ -221,7 +245,8 @@ _check_backup() { [ -n "$ip6resolve" ] && ip="$ip6resolve" fi #_ping_server $ip - _check_server $ip $port + #_check_server $ip $port + _check_server_intf $ip $port #[ "$server_ping" = true ] && [ "$(uci -q get shadowsocks-libev.sss${count}.server | tr -d '\n')" = "$ip" ] && break if [ "$server_ping" = true ]; then if [ "$(uci -q get shadowsocks-libev.sss${count}.server | tr -d '\n')" != "$ip" ]; then diff --git a/omr-tracker/files/bin/omr-tracker-ss b/omr-tracker/files/bin/omr-tracker-ss index 82b7ad3ce..829cf4dc5 100755 --- a/omr-tracker/files/bin/omr-tracker-ss +++ b/omr-tracker/files/bin/omr-tracker-ss @@ -4,9 +4,9 @@ name=$0 basename="$(basename $0)" -if [ -f /usr/sbin/iptables-legacy ]; then - IPTABLES="/usr/sbin/iptables-legacy" - IPTABLESSAVE="/usr/sbin/iptables-legacy-save" +if [ -e /usr/sbin/iptables-nft ]; then + IPTABLES="/usr/sbin/iptables-nft" + IPTABLESSAVE="/usr/sbin/iptables-nft-save" else IPTABLES="/usr/sbin/iptables" IPTABLESSAVE="/usr/sbin/iptables-save" @@ -103,7 +103,7 @@ while true; do script_alert_up="$(uci -q get omr-tracker.proxy.script_alert_up)" [ -n "$script_alert_up" ] && eval $script_alert_up } - if [ -z "$($IPTABLESSAVE 2>/dev/null | grep :ssr)" ]; then + if [ -z "$($IPTABLESSAVE 2>/dev/null | grep :ssr)" ] && [ -z "$(nft list ruleset 2>/dev/null | grep ss_r)" ] && [ -z "$(nft list ruleset 2>/dev/null | grep ssr_r)" ]; then if [ "$type" = "libev" ] && [ "$(uci -q get shadowsocks-libev.ss_rules.disabled)" != "1" ]; then _log "Reload Shadowsocks rules" /etc/init.d/shadowsocks-libev rules_up 2> /dev/null @@ -120,7 +120,7 @@ while true; do last=$((last + 1 )) [ -z "$nocontact" ] && nocontact="$host" || nocontact="$nocontact, $host" [ "${last}" -ge "${retry}" ] && { - if [ -n "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ssr)" ]; then + if [ -n "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ssr)" ] || [ -n "$(nft list ruleset 2>/dev/null | grep ss_r)" ] || [ -n "$(nft list ruleset 2>/dev/null | grep ssr_r)" ]; then _log "Shadowsocks ${server} is down (can't contact via http ${nocontact})" OMR_TRACKER_STATUS_MSG="Shadowsocks ${server} is down (can't contact via http ${nocontact})" uci -q set openmptcprouter.omr.ss_${server}="down" diff --git a/omr-tracker/files/bin/omr-tracker-v2ray b/omr-tracker/files/bin/omr-tracker-v2ray index 6ef18c429..519399ce8 100755 --- a/omr-tracker/files/bin/omr-tracker-v2ray +++ b/omr-tracker/files/bin/omr-tracker-v2ray @@ -4,8 +4,8 @@ name=$0 basename="$(basename $0)" -if [ -f /usr/sbin/iptables-legacy ]; then - IPTABLES="/usr/sbin/iptables-legacy" +if [ -e /usr/sbin/iptables-nft ]; then + IPTABLES="/usr/sbin/iptables-nft" else IPTABLES="/usr/sbin/iptables" fi @@ -100,7 +100,7 @@ while true; do script_alert_up="$(uci -q get omr-tracker.proxy.script_alert_up)" [ -n "$script_alert_up" ] && eval $script_alert_up } - if [ -z "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ^v2r)" ]; then + if [ -z "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ^v2r)" ] && [ -z "$(nft list ruleset 2>/dev/null | grep 'chain v2r')" ]; then _log "Reload V2Ray rules" /etc/init.d/v2ray rules_up 2> /dev/null _get_ip @@ -111,7 +111,7 @@ while true; do last=$((last + 1 )) [ -z "$nocontact" ] && nocontact="$host" || nocontact="$nocontact, $host" [ "${last}" -ge "${retry}" ] && { - if [ -n "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ^v2r)" ]; then + if [ -n "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ^v2r)" ] || [ -n "$(nft list ruleset 2>/dev/null | grep 'chain v2r')" ]; then _log "V2Ray is down (can't contact via http ${nocontact})" OMR_TRACKER_STATUS_MSG="V2Ray is down (can't contact via http ${nocontact})" uci -q set openmptcprouter.omr.v2ray="down" diff --git a/omr-tracker/files/bin/omr-tracker-xray b/omr-tracker/files/bin/omr-tracker-xray index 21272b1e6..4aae08e76 100755 --- a/omr-tracker/files/bin/omr-tracker-xray +++ b/omr-tracker/files/bin/omr-tracker-xray @@ -4,8 +4,8 @@ name=$0 basename="$(basename $0)" -if [ -f /usr/sbin/iptables-legacy ]; then - IPTABLES="/usr/sbin/iptables-legacy" +if [ -e /usr/sbin/iptables-nft ]; then + IPTABLES="/usr/sbin/iptables-nft" else IPTABLES="/usr/sbin/iptables" fi @@ -100,7 +100,7 @@ while true; do script_alert_up="$(uci -q get omr-tracker.proxy.script_alert_up)" [ -n "$script_alert_up" ] && eval $script_alert_up } - if [ -z "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ^xr)" ]; then + if [ -z "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ^xr)" ] && [ -z "$(nft list ruleset 2>/dev/null | grep 'chain xr')" ]; then _log "Reload xray rules" /etc/init.d/xray rules_up 2> /dev/null _get_ip @@ -111,7 +111,7 @@ while true; do last=$((last + 1 )) [ -z "$nocontact" ] && nocontact="$host" || nocontact="$nocontact, $host" [ "${last}" -ge "${retry}" ] && { - if [ -n "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ^xr)" ]; then + if [ -n "$($IPTABLES -w -t nat -L -n 2>/dev/null | grep ^xr)" ] || [ -n "$(nft list ruleset 2>/dev/null | grep 'chain xr')" ]; then _log "xray is down (can't contact via http ${nocontact})" OMR_TRACKER_STATUS_MSG="xray is down (can't contact via http ${nocontact})" uci -q set openmptcprouter.omr.xray="down" diff --git a/openmptcprouter-full/Makefile b/openmptcprouter-full/Makefile index d598ef975..2db1335c8 100755 --- a/openmptcprouter-full/Makefile +++ b/openmptcprouter-full/Makefile @@ -21,7 +21,7 @@ MY_DEPENDS := \ mc \ f2fs-tools \ openmptcprouter \ - dnsmasq-full dnsmasq_full_ipset \ + dnsmasq-full LINUX_5_4:dnsmasq_full_ipset !LINUX_5_4:dnsmasq_full_nftset \ uhttpd \ uhttpd-mod-ubus \ curl \ @@ -29,9 +29,10 @@ MY_DEPENDS := \ arptables \ bind-dig \ libnetfilter-conntrack ip-full nstat \ - iptables-mod-iface iptables-mod-ipmark iptables-mod-hashlimit iptables-mod-condition iptables-mod-trace iptables-mod-conntrack-extra iptables-mod-account \ - kmod-nf-nat kmod-nf-nathelper kmod-nf-nathelper-extra iptables-mod-extra conntrack kmod-ipt-offload \ - iptables-mod-ipsec kmod-crypto-authenc kmod-ipsec kmod-ipsec4 kmod-ipsec6 kmod-ipt-ipsec \ + LINUX_5_4:iptables-mod-iface LINUX_5_4:iptables-mod-ipmark LINUX_5_4:iptables-mod-hashlimit LINUX_5_4:iptables-mod-condition LINUX_5_4:iptables-mod-trace LINUX_5_4:iptables-mod-conntrack-extra LINUX_5_4:iptables-mod-account \ + kmod-nf-nat kmod-nf-nathelper kmod-nf-nathelper-extra LINUX_5_4:iptables-mod-extra conntrack LINUX_5_4:kmod-ipt-offload \ + LINUX_5_4:iptables-mod-ipsec kmod-crypto-authenc kmod-ipsec kmod-ipsec4 kmod-ipsec6 LINUX_5_4:kmod-ipt-ipsec \ + !LINUX_5_4:nftables-json !LINUX_5_4:iptables-nft !LINUX_5_4:kmod-nft-connlimit !LINUX_5_4:kmod-nft-offload wireless-tools \ libiwinfo-lua \ ca-bundle ca-certificates \ @@ -44,7 +45,7 @@ MY_DEPENDS := \ luci-app-uhttpd \ luci-mod-rpc rpcd-mod-rpcsys rpcd-mod-file rpcd-mod-iwinfo \ luci-app-openvpn \ - shadowsocks-libev-ss-server shadowsocks-libev-ss-redir shadowsocks-libev-ss-rules shadowsocks-libev-ss-tunnel \ + shadowsocks-libev-ss-server shadowsocks-libev-ss-redir LINUX_5_4:shadowsocks-libev-ss-rules !LINUX_5_4:shadowsocks-libev-ss-rules-nft shadowsocks-libev-ss-tunnel \ omr-6in4 ip6tables-mod-nat luci-proto-ipv6 6to4 6in4 6rd ip6tables \ !TARGET_mvebu:speedtestcpp \ iftop \ @@ -82,10 +83,10 @@ MY_DEPENDS := \ luci-app-acl block-mount blockd fstools luci-app-shutdown libwebp luci-proto-gre tcptraceroute luci-proto-mbim kmod-rtl8xxxu kmod-ath9k-htc luci-app-ttyd luci-mod-dashboard (TARGET_x86||TARGET_x86_64):rtl8192eu-firmware kmod-usb2 libustream-openssl (TARGET_x86||TARGET_x86_64):kmod-ixgbevf (TARGET_x86||TARGET_x86_64):kmod-igbvf \ hwinfo (TARGET_x86||TARGET_x86_64):dmidecode luci-app-packet-capture kmod-bonding luci-proto-bonding luci-app-sysupgrade \ luci-theme-openwrt-2020 luci-proto-wireguard luci-app-wireguard kmod-crypto-lib-blake2s (TARGET_x86||TARGET_x86_64):kmod-r8125 \ - !(LINUX_5_4):mptcpd (TARGET_x86||TARGET_x86_64):kmod-igc !TARGET_mvebu:kmod-mmc-spi kmod-macsec usbutils v2ray-core syslogd \ + !(LINUX_5_4):mptcpd (TARGET_x86||TARGET_x86_64):kmod-igc !TARGET_mvebu:kmod-mmc-spi kmod-macsec usbutils v2ray-core LINUX_5_4:v2ray-config !LINUX_5_4:v2ray-config-nft syslogd \ (TARGET_x86||TARGET_x86_64):kmod-mlx4-core \ !(TARGET_ips40xx||TARGET_ramips):iptables-mod-ndpi !(TARGET_ips40xx||TARGET_ramips):kmod-ipt-ndpi libip4tc libip6tc \ - xray-core shadowsocks-rust-sslocal shadowsocks-rust-ssservice shadowsocks-rust-config luci-app-shadowsocks-rust (LINUX_5_4&&(TARGET_x86_64||aarch64)):kmod-tcp-bbr2 kmod-ovpn-dco-v2 + xray-core LINUX_5_4:xray-config !LINUX_5_4:xray-config-nft shadowsocks-rust-sslocal shadowsocks-rust-ssservice LINUX_5_4:shadowsocks-rust-config !LINUX_5_4:shadowsocks-rust-config-nft luci-app-shadowsocks-rust (LINUX_5_4&&(TARGET_x86_64||aarch64)):kmod-tcp-bbr2 kmod-ovpn-dco-v2 keepalived # !TARGET_mvebu:kmod-usb-net-smsc75xx # libnetfilter-conntrack ebtables ebtables-utils ip-full nstat \ diff --git a/openmptcprouter/files/etc/init.d/openmptcprouter-vps b/openmptcprouter/files/etc/init.d/openmptcprouter-vps index 6b8c127c2..82fb17d08 100755 --- a/openmptcprouter/files/etc/init.d/openmptcprouter-vps +++ b/openmptcprouter/files/etc/init.d/openmptcprouter-vps @@ -10,13 +10,13 @@ EXTRA_COMMANDS="set_pihole backup_send backup_get backup_list set_vps_firewall g . /usr/lib/unbound/iptools.sh -if [ -f /usr/sbin/iptables-legacy ]; then - IPTABLES="/usr/sbin/iptables-legacy" - IPTABLESRESTORE="/usr/sbin/iptables-legacy-restore" - IPTABLESSAVE="/usr/sbin/iptables-legacy-save" - IP6TABLES="/usr/sbin/ip6tables-legacy" - IP6TABLESRESTORE="/usr/sbin/ip6tables-legacy-restore" - IP6TABLESSAVE="/usr/sbin/ip6tables-legacy-save" +if [ -e /usr/sbin/iptables-nft ]; then + IPTABLES="/usr/sbin/iptables-nft" + IPTABLESRESTORE="/usr/sbin/iptables-nft-restore" + IPTABLESSAVE="/usr/sbin/iptables-nft-save" + IP6TABLES="/usr/sbin/ip6tables-nft" + IP6TABLESRESTORE="/usr/sbin/ip6tables-nft-restore" + IP6TABLESSAVE="/usr/sbin/ip6tables-nft-save" else IPTABLES="/usr/sbin/iptables" IPTABLESRESTORE="/usr/sbin/iptables-restore" @@ -947,8 +947,11 @@ _set_lan_ip() { _set_bypass_ips() { local settings [ -z "$servername" ] && servername=$1 - bypassipv4s=$(ipset -q -o save list omr_dst_bypass_srv_vpn1 | awk '/add/ NF {print "\""$3"\""}' | tr '\n' ',' | sed 's/,$//') - bypassipv6s=$(ipset -q -o save list omr6_dst_bypass_srv_vpn1 | awk '/add/ NF {print "\""$3"\""}' | tr '\n' ',' | sed 's/,$//') + bypassipv4s=$(ipset -q -o save list omr_dst_bypass_srv_vpn1_4 | awk '/add/ NF {print "\""$3"\""}' | tr '\n' ',' | sed 's/,$//') + [ -z "$bypassipvs4" ] && bypassipv4s=$(nft -j list set inet fw4 "omr_dst_bypass_srv_vpn1_4" | jsonfilter -e @.nftables[1].set.elem[*].prefix | awk '{gsub(/"/,"",$3);gsub(/,/,"/",$3); print $3 $5}') + bypassipv6s=$(ipset -q -o save list omr6_dst_bypass_srv_vpn1_6 | awk '/add/ NF {print "\""$3"\""}' | tr '\n' ',' | sed 's/,$//') + [ -z "$bypassipvs6" ] && bypassipv4s=$(nft -j list set inet fw4 "omr_dst_bypass_srv_vpn1_6" | jsonfilter -e @.nftables[1].set.elem[*].prefix | awk '{gsub(/"/,"",$3);gsub(/,/,"/",$3); print $3 $5}') + # " if [ "$bypassipv4s" != "" ] || [ "$bypassipv6s" != "" ]; then settings='{"ipv4s" : ['$bypassipv4s'],"ipv6s" : ['$bypassipv6s'],"intf" : "vpn1"}' result=$(_set_json "bypass" "$settings") @@ -1041,6 +1044,8 @@ _vps_firewall_redirect_port() { config_get name $1 name config_get dmz $1 dmz "0" config_get target $1 target "REDIRECT" + [ "$target" = "MARK" ] && return + [ "$target" = "DSCP" ] && return if [ -z "$src_dport" ] && [ -n "$dest_port" ]; then src_dport=$dest_port fi diff --git a/shadowsocks-libev/files/shadowsocks.conf b/openmptcprouter/files/etc/sysctl.d/openmptcprouter.conf similarity index 96% rename from shadowsocks-libev/files/shadowsocks.conf rename to openmptcprouter/files/etc/sysctl.d/openmptcprouter.conf index f02474433..584db2854 100755 --- a/shadowsocks-libev/files/shadowsocks.conf +++ b/openmptcprouter/files/etc/sysctl.d/openmptcprouter.conf @@ -59,4 +59,5 @@ net.ipv4.tcp_ecn = 2 #net.ipv4.tcp_sack = 0 #net.ipv4.tcp_dsack = 0 #net.ipv4.tcp_fack = 0 -net.ipv4.tcp_slow_start_after_idle = 0 \ No newline at end of file +net.ipv4.tcp_slow_start_after_idle = 0 +net.ipv4.route.gc_timeout = 150 \ No newline at end of file diff --git a/shadowsocks-libev/Makefile b/shadowsocks-libev/Makefile index 6eea8b25c..b352558a7 100755 --- a/shadowsocks-libev/Makefile +++ b/shadowsocks-libev/Makefile @@ -44,8 +44,6 @@ endef define Package/shadowsocks-libev-config/install $(INSTALL_DIR) $(1)/etc/config $(INSTALL_DATA) ./files/shadowsocks-libev.config $(1)/etc/config/shadowsocks-libev - $(INSTALL_DIR) $(1)/etc/init.d - $(INSTALL_BIN) ./files/shadowsocks-libev.init $(1)/etc/init.d/shadowsocks-libev endef @@ -92,8 +90,11 @@ define Package/shadowsocks-libev-ss-rules/install $(INSTALL_DATA) ./files/firewall.ss-rules $(1)/etc $(INSTALL_DIR) $(1)/etc/uci-defaults $(INSTALL_BIN) ./files/ss-rules.defaults $(1)/etc/uci-defaults - $(INSTALL_DIR) $(1)/etc/sysctl.d - $(INSTALL_DATA) ./files/shadowsocks.conf $(1)/etc/sysctl.d + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/shadowsocks-libev.init $(1)/etc/init.d/shadowsocks-libev + +# $(INSTALL_DIR) $(1)/etc/sysctl.d +# $(INSTALL_DATA) ./files/shadowsocks.conf $(1)/etc/sysctl.d # $(INSTALL_DIR) $(1)/usr/lib/shadowsocks-libev # $(INSTALL_DATA) $(PKG_BUILD_DIR)/src/*.ebpf $(1)/usr/lib/shadowsocks-libev endef @@ -108,6 +109,33 @@ uci batch <<-EOF EOF endef +define Package/shadowsocks-libev-ss-rules-nft + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocks-libev ss-rules NFT + URL:=https://github.com/shadowsocks/shadowsocks-libev + DEPENDS:=+firewall4 \ + +ip \ + +resolveip \ + +ucode \ + +ucode-mod-fs \ + +shadowsocks-libev-ss-redir \ + +shadowsocks-libev-config \ + +kmod-nft-tproxy +endef + + +define Package/shadowsocks-libev-ss-rules-nft/install + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./files/ss-rules.defaults $(1)/etc/uci-defaults + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/shadowsocks-libev.init-nft $(1)/etc/init.d/shadowsocks-libev + $(INSTALL_DIR) $(1)/usr/share/ss-rules + $(INSTALL_DATA) ./files/nft-rules/* $(1)/usr/share/ss-rules/ +endef + + define Build/Prepare $(call Build/Prepare/Default) $(FIND) $(PKG_BUILD_DIR) \ @@ -128,6 +156,7 @@ TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include $(eval $(call BuildPackage,shadowsocks-libev-config)) $(eval $(call BuildPackage,shadowsocks-libev-ss-rules)) +$(eval $(call BuildPackage,shadowsocks-libev-ss-rules-nft)) $(foreach component,$(SHADOWSOCKS_COMPONENTS), \ $(eval $(call BuildPackage,shadowsocks-libev-$(component))) \ ) diff --git a/shadowsocks-libev/files/nft-rules/chain.uc b/shadowsocks-libev/files/nft-rules/chain.uc new file mode 100644 index 000000000..3047f1663 --- /dev/null +++ b/shadowsocks-libev/files/nft-rules/chain.uc @@ -0,0 +1,122 @@ +{% +function get_local_verdict() { + let v = o_local_default; + if (v == "checkdst") { + return "goto ss_rules_dst_" + proto; + } else if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return null; + } +} + +function get_src_default_verdict() { + let v = o_src_default; + if (v == "checkdst") { + return "goto ss_rules_dst_" + proto; + } else if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_dst_default_verdict() { + let v = o_dst_default; + if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_ifnames() { + let res = []; + for (let ifname in split(o_ifnames, /[ \t\n]/)) { + ifname = trim(ifname); + if (ifname) push(res, ifname); + } + return res; +} + +let type, hook, priority, redir_port; +if (proto == "tcp") { + type = "nat"; + hook = "prerouting"; + priority = -1; + redir_port = o_redir_tcp_port; +} else if (proto == "udp") { + type = "filter"; + hook = "prerouting"; + priority = "mangle"; + redir_port = o_redir_udp_port; + if (system(" + set -o errexit + iprr() { + while ip $1 rule del fwmark 1 lookup 100 2>/dev/null; do true; done + ip $1 rule add fwmark 1 lookup 100 + ip $1 route flush table 100 2>/dev/null || true + ip $1 route add local default dev lo table 100 + } + iprr -4 + iprr -6 + ") != 0) { + return ; + } +} else { + return; +} + +%} +{% if (redir_port): %} + +chain ss_rules_pre_{{ proto }} { + type {{ type }} hook {{ hook }} priority {{ priority }}; + meta l4proto {{ proto }}{%- let ifnames=get_ifnames(); if (length(ifnames)): %} iifname { {{join(", ", ifnames)}} }{% endif %} goto ss_rules_pre_src_{{ proto }}; +} + +chain ss_rules_pre_src_{{ proto }} { + ip daddr @ss_rules_dst_bypass_ accept; + ip6 daddr @ss_rules6_dst_bypass_ accept; + goto ss_rules_src_{{ proto }}; +} + +chain ss_rules_src_{{ proto }} { + ip saddr @ss_rules_src_bypass accept; + ip saddr @ss_rules_src_forward goto ss_rules_forward_{{ proto }}; + ip saddr @ss_rules_src_checkdst goto ss_rules_dst_{{ proto }}; + ip6 saddr @ss_rules6_src_bypass accept; + ip6 saddr @ss_rules6_src_forward goto ss_rules_forward_{{ proto }}; + ip6 saddr @ss_rules6_src_checkdst goto ss_rules_dst_{{ proto }}; + {{ get_src_default_verdict() }}; +} + +chain ss_rules_dst_{{ proto }} { + ip daddr @ss_rules_dst_bypass accept; + ip daddr @ss_rules_dst_forward goto ss_rules_forward_{{ proto }}; + ip6 daddr @ss_rules6_dst_bypass accept; + ip6 daddr @ss_rules6_dst_forward goto ss_rules_forward_{{ proto }}; + {{ get_dst_default_verdict() }}; +} + +{% if (proto == "tcp"): %} +chain ss_rules_forward_{{ proto }} { + meta l4proto tcp {{ o_nft_tcp_extra }} redirect to :{{ redir_port }}; +} +{% let local_verdict = get_local_verdict(); if (local_verdict): %} +chain ss_rules_local_out { + type {{ type }} hook output priority -1; + meta l4proto != tcp accept; + ip daddr @ss_rules_dst_bypass_ accept; + ip daddr @ss_rules_dst_bypass accept; + ip6 daddr @ss_rules6_dst_bypass_ accept; + ip6 daddr @ss_rules6_dst_bypass accept; + {{ local_verdict }}; +} +{% endif %} +{% elif (proto == "udp"): %} +chain ss_rules_forward_{{ proto }} { + meta l4proto udp {{ o_nft_udp_extra }} meta mark set 1 tproxy to :{{ redir_port }}; +} +{% endif %} +{% endif %} diff --git a/shadowsocks-libev/files/nft-rules/set.uc b/shadowsocks-libev/files/nft-rules/set.uc new file mode 100644 index 000000000..38140e7e1 --- /dev/null +++ b/shadowsocks-libev/files/nft-rules/set.uc @@ -0,0 +1,114 @@ +{% +let fs = require("fs"); + +let o_dst_bypass4_ = " + 0.0.0.0/8 + 10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.0/8 + 169.254.0.0/16 + 172.16.0.0/12 + 192.0.0.0/24 + 192.0.2.0/24 + 192.31.196.0/24 + 192.52.193.0/24 + 192.88.99.0/24 + 192.168.0.0/16 + 192.175.48.0/24 + 198.18.0.0/15 + 198.51.100.0/24 + 203.0.113.0/24 + 224.0.0.0/4 + 240.0.0.0/4 +"; +let o_dst_bypass6_ = " + ::1/128 + ::/128 + ::ffff:0:0/96 + 64:ff9b:1::/48 + 100::/64 + fe80::/10 + 2001::/23 + fc00::/7 +"; +let o_dst_bypass_ = o_dst_bypass4_ + " " + o_dst_bypass6_; + +let set_suffix = { + "src_bypass": { + str: o_src_bypass, + }, + "src_forward": { + str: o_src_forward, + }, + "src_checkdst": { + str: o_src_checkdst, + }, + "dst_bypass": { + str: o_dst_bypass, + file: o_dst_bypass_file, + }, + "dst_bypass_": { + str: o_dst_bypass_, + }, + "dst_forward": { + str: o_dst_forward, + file: o_dst_forward_file, + }, + "dst_forward_rrst_": {}, +}; + +function set_name(suf, af) { + if (af == 4) { + return "ss_rules_"+suf; + } else { + return "ss_rules6_"+suf; + } +} + +function set_elements_parse(res, str, af) { + for (let addr in split(str, /[ \t\n]/)) { + addr = trim(addr); + if (!addr) continue; + if (af == 4 && index(addr, ":") != -1) continue; + if (af == 6 && index(addr, ":") == -1) continue; + push(res, addr); + } +} + +function set_elements(suf, af) { + let obj = set_suffix[suf]; + let res = []; + let addr; + + let str = obj["str"]; + if (str) { + set_elements_parse(res, str, af); + } + + let file = obj["file"]; + if (file) { + let fd = fs.open(file); + if (fd) { + str = fd.read("all"); + set_elements_parse(res, str, af); + } + } + + return res; +} +%} + +{% for (let suf in set_suffix): for (let af in [4, 6]): %} +set {{ set_name(suf, af) }} { + type ipv{{af}}_addr; + flags interval; + auto-merge; +{% let elems = set_elements(suf, af); if (length(elems)): %} + elements = { +{% for (let i = 0; i < length(elems); i++): %} + {{ elems[i] }}{% if (i < length(elems) - 1): %},{% endif %}{% print("\n") %} +{% endfor %} + } +{% endif %} +} +{% endfor; endfor %} diff --git a/shadowsocks-libev/files/nft-rules/ss-rules.uc b/shadowsocks-libev/files/nft-rules/ss-rules.uc new file mode 100644 index 000000000..f3955b2ef --- /dev/null +++ b/shadowsocks-libev/files/nft-rules/ss-rules.uc @@ -0,0 +1,8 @@ +{% + +include("set.uc"); +include("chain.uc", {proto: "tcp"}); +include("chain.uc", {proto: "udp"}); + +%} + diff --git a/shadowsocks-libev/files/shadowsocks-libev.init-nft b/shadowsocks-libev/files/shadowsocks-libev.init-nft new file mode 100755 index 000000000..3db4bbc56 --- /dev/null +++ b/shadowsocks-libev/files/shadowsocks-libev.init-nft @@ -0,0 +1,363 @@ +#!/bin/sh /etc/rc.common +# +# Copyright (C) 2017-2019 Yousong Zhou +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +USE_PROCD=1 +START=99 + +EXTRA_COMMANDS="rules_up rules_down rules_exist" + +ss_confdir=/var/etc/shadowsocks-libev +ss_bindir=/usr/bin + +ssrules_uc="/usr/share/ss-rules/ss-rules.uc" +ssrules_nft="/etc/nftables.d/90-ss-rules.nft" + +ss_mkjson_server_conf() { + local cfgserver + + config_get cfgserver "$cfg" server + [ -n "$cfgserver" ] || return 1 + eval "$(validate_server_section "$cfg" ss_validate_mklocal)" + validate_server_section "$cfgserver" || return 1 + [ "$disabled" = 0 ] || return 1 + ss_mkjson_server_conf_ "$cfgserver" +} + +ss_mkjson_server_conf_() { + [ -n "$server_port" ] || return 1 + [ -z "$server" ] || json_add_string server "$server" + json_add_int server_port "$server_port" + [ -z "$method" ] || json_add_string method "$method" + [ -z "$key" ] || json_add_string key "$key" + [ -z "$password" ] || json_add_string password "$password" + [ -z "$plugin" ] || json_add_string plugin "$plugin" + [ -z "$plugin_opts" ] || json_add_string plugin_opts "$plugin_opts" +} + +ss_mkjson_ss_local_conf() { + ss_mkjson_server_conf +} + +ss_mkjson_ss_redir_conf() { + ss_mkjson_server_conf +} + +ss_mkjson_ss_server_conf() { + ss_mkjson_server_conf_ +} + +ss_mkjson_ss_tunnel_conf() { + ss_mkjson_server_conf || return 1 + [ -n "$tunnel_address" ] || return 1 + json_add_string tunnel_address "$tunnel_address" +} + +ss_xxx() { + local cfg="$1" + local cfgtype="$2" + local bin="$ss_bindir/${cfgtype/_/-}" + local confjson="$ss_confdir/$cfgtype.$cfg.json" + + [ -x "$bin" ] || return + eval "$("validate_${cfgtype}_section" "$cfg" ss_validate_mklocal)" + "validate_${cfgtype}_section" "$cfg" || return + [ "$disabled" = 0 ] || return + + json_init + ss_mkjson_${cfgtype}_conf || return + json_add_boolean use_syslog 1 + json_add_boolean ipv6_first "$ipv6_first" + json_add_boolean fast_open "$fast_open" + json_add_boolean reuse_port "$reuse_port" + json_add_boolean no_delay "$no_delay" + json_add_boolean mptcp "$mptcp" + [ -z "$local_address" ] || json_add_string local_address "$local_address" + [ -z "$local_port" ] || json_add_int local_port "$local_port" + [ -z "$local_ipv4_address" ] || json_add_string local_ipv4_address "$local_ipv4_address" + [ -z "$local_ipv6_address" ] || json_add_string local_ipv6_address "$local_ipv6_address" + [ -z "$mode" ] || json_add_string mode "$mode" + [ -z "$mtu" ] || json_add_int mtu "$mtu" + [ -z "$timeout" ] || json_add_int timeout "$timeout" + [ -z "$user" ] || json_add_string user "$user" + [ -z "$acl" ] || json_add_string acl "$acl" + json_dump -i >"$confjson" + + procd_open_instance "$cfgtype.$cfg" + procd_set_param command "$bin" -c "$confjson" + [ "$verbose" = 0 ] || procd_append_param command -v + if [ -n "$bind_address" ]; then + echo "$cfgtype $cfg: uci option bind_address deprecated, please switch to local_address" >&2 + procd_append_param command -b "$bind_address" + fi + procd_set_param file "$confjson" + procd_set_param respawn + procd_close_instance + ss_rules_cb +} + +ss_rules_cb() { + local cfgserver server + + if [ "$cfgtype" = ss_redir ]; then + config_get cfgserver "$cfg" server + config_get server "$cfgserver" server + ss_redir_servers="$ss_redir_servers $server" + if [ "$mode" = tcp_only -o "$mode" = "tcp_and_udp" ]; then + eval "ss_rules_redir_tcp_$cfg=$local_port" + fi + if [ "$mode" = udp_only -o "$mode" = "tcp_and_udp" ]; then + eval "ss_rules_redir_udp_$cfg=$local_port" + fi + fi +} + +ss_rules_nft_gen() { + local cfg="ss_rules" + local cfgtype + local local_port_tcp local_port_udp + local remote_servers + + [ -s "$ssrules_uc" ] || return 1 + + config_get cfgtype "$cfg" TYPE + [ "$cfgtype" = ss_rules ] || return 1 + + eval "$(validate_ss_rules_section "$cfg" ss_validate_mklocal)" + validate_ss_rules_section "$cfg" || return 1 + [ "$disabled" = 0 ] || return 2 + + if [ "$ss_rules_redir_tcp_$redir_tcp" = "all" ]; then + min_ss_redir_ports="65535" + max_ss_redir_ports="0" + config_load shadowsocks-libev + config_foreach ss_redir_ports ss_redir $cfgrulesserver + if [ "$min_ss_redir_ports" != "$max_ss_redir_ports" ]; then + all_ss_redir_ports=$min_ss_redir_ports-$max_ss_redir_ports + else + all_ss_redir_ports=$min_ss_redir_ports + fi + local_port_tcp="$all_ss_redir_ports" + if [ "$ss_rules_redir_udp_$redir_udp" = "all" ] || [ "$ss_rules_redir_udp_$redir_udp" = "hi1" ]; then + local_port_udp="$min_ss_redir_ports" + fi + else + eval local_port_tcp="\$ss_rules_redir_tcp_$redir_tcp" + eval local_port_udp="\$ss_rules_redir_udp_$redir_udp" + fi + [ -n "$local_port_tcp" -o -n "$local_port_udp" ] || return 1 + remote_servers="$(echo $ss_redir_servers \ + | tr ' ' '\n' \ + | sort -u \ + | xargs -n 1 resolveip \ + | sort -u)" + + local tmp="/tmp/ssrules" + json_init + json_add_string o_remote_servers "$remote_servers" + json_add_int o_redir_tcp_port "$local_port_tcp" + json_add_int o_redir_udp_port "$local_port_udp" + json_add_string o_ifnames "$ifnames" + json_add_string o_local_default "$local_default" + json_add_string o_src_bypass "$src_ips_bypass" + json_add_string o_src_forward "$src_ips_forward" + json_add_string o_src_checkdst "$src_ips_checkdst" + json_add_string o_src_default "$src_default" + json_add_string o_dst_bypass "$dst_ips_bypass" + json_add_string o_dst_forward "$dst_ips_forward" + json_add_string o_dst_bypass_file "$dst_ips_bypass_file" + json_add_string o_dst_forward_file "$dst_ips_forward_file" + json_add_string o_dst_default "$dst_default" + json_add_string o_nft_tcp_extra "$nft_tcp_extra" + json_add_string o_nft_udp_extra "$nft_udp_extra" + json_dump -i >"$tmp.json" + + if utpl -S -F "$tmp.json" "$ssrules_uc" >"$tmp.nft" \ + && ! cmp -s "$tmp.nft" "$ssrules_nft"; then + echo "table inet chk {include \"$tmp.nft\";}" >"$tmp.nft.chk" + if nft -f "$tmp.nft.chk" -c; then + mv "$tmp.nft" "$ssrules_nft" + fw4 restart + fi + rm -f "$tmp.nft.chk" + fi + rm -f "$tmp.json" + rm -f "$tmp.nft" +} + +ss_rules_nft_reset() { + if [ -f "$ssrules_nft" ]; then + rm -f "$ssrules_nft" + fw4 restart + fi +} + +ss_rules() { + if ! ss_rules_nft_gen; then + ss_rules_nft_reset + fi +} + +rules_up() { + if [ -f "${ssrules_nft}.down" ]; then + mv -f "${ssrules_nft}.down" "$ssrules_nft" + fw4 restart + fi +} + +rules_down() { + if [ -f "${ssrules_nft}" ]; then + mv -f "$ssrules_nft" "${ssrules_nft}.down" + fw4 restart + fi +} + +rules_exist() { + if [ -f "$ssrules_nft" ]; then + return 0 + else + return 1 + fi +} + +start_service() { + local cfgtype + + mkdir -p "$ss_confdir" + config_load shadowsocks-libev + for cfgtype in ss_local ss_redir ss_server ss_tunnel; do + config_foreach ss_xxx "$cfgtype" "$cfgtype" + done + ss_rules +} + +stop_service() { + ss_rules_nft_reset + rm -rf "$ss_confdir" +} + +service_triggers() { + procd_add_reload_interface_trigger wan + procd_add_reload_trigger shadowsocks-libev + procd_open_validate + validate_server_section + validate_ss_local_section + validate_ss_redir_section + validate_ss_rules_section + validate_ss_server_section + validate_ss_tunnel_section + procd_close_validate +} + +ss_validate_mklocal() { + local tuple opts + + shift 2 + for tuple in "$@"; do + opts="${tuple%%:*} $opts" + done + [ -z "$opts" ] || echo "local $opts" +} + +ss_validate() { + uci_validate_section shadowsocks-libev "$@" +} + +validate_common_server_options_() { + local cfgtype="$1"; shift + local cfg="$1"; shift + local func="$1"; shift + local stream_methods='"none", "plain", "chacha20-ietf-poly1305"' + local aead_methods='"aes-128-gcm", "aes-256-gcm","2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "chacha20-ietf-poly1305", "2022-blake3-chacha20-poly1305","2022-blake3-chacha8-poly1305"' + + + "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ + 'disabled:bool:0' \ + 'server:host' \ + 'server_port:port' \ + 'password:string' \ + 'key:string' \ + "method:or($stream_methods, $aead_methods)" \ + 'plugin:string' \ + 'plugin_opts:string' +} + +validate_common_client_options_() { + validate_common_options_ "$@" \ + 'server:uci("shadowsocks-libev", "@server")' \ + 'local_address:ipaddr:0.0.0.0' \ + 'local_port:port' +} + +validate_common_options_() { + local cfgtype="$1"; shift + local cfg="$1"; shift + local func="$1"; shift + + "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ + 'disabled:bool:0' \ + 'fast_open:bool:0' \ + 'ipv6_first:bool:0' \ + 'no_delay:bool:0' \ + 'reuse_port:bool:0' \ + 'mptcp:bool:0' \ + 'verbose:bool:0' \ + 'mode:or("tcp_only", "udp_only", "tcp_and_udp"):tcp_only' \ + 'mtu:uinteger' \ + 'timeout:uinteger' \ + 'user:string' +} + +validate_server_section() { + validate_common_server_options_ server "$1" "$2" +} + +validate_ss_local_section() { + validate_common_client_options_ ss_local "$1" "$2" \ + 'acl:file' +} + +validate_ss_redir_section() { + validate_common_client_options_ ss_redir "$1" "$2" +} + +validate_ss_rules_section() { + "${2:-ss_validate}" ss_rules "$1" \ + 'disabled:bool:0' \ + 'redir_tcp:uci("shadowsocks-libev", "@ss_redir","all")' \ + 'redir_udp:uci("shadowsocks-libev", "@ss_redir","all")' \ + 'src_ips_bypass:or(ipaddr,cidr)' \ + 'src_ips_forward:or(ipaddr,cidr)' \ + 'src_ips_checkdst:or(ipaddr,cidr)' \ + 'dst_ips_bypass_file:file' \ + 'dst_ips_bypass:or(ipaddr,cidr)' \ + 'dst_ips_forward_file:file' \ + 'dst_ips_forward:or(ipaddr,cidr)' \ + 'src_default:or("bypass", "forward", "checkdst"):checkdst' \ + 'dst_default:or("bypass", "forward"):bypass' \ + 'local_default:or("bypass", "forward", "checkdst"):bypass' \ + 'nft_tcp_extra:string' \ + 'nft_udp_extra:string' \ + 'ifnames:maxlength(15)' +} + +validate_ss_server_section() { + validate_common_server_options_ ss_server "$1" \ + validate_common_options_ \ + "$2" \ + 'local_address:ipaddr' \ + 'local_ipv4_address:ip4addr' \ + 'local_ipv6_address:ip6addr' \ + 'bind_address:ipaddr' \ + 'acl:file' +} + +validate_ss_tunnel_section() { + validate_common_client_options_ ss_tunnel "$1" \ + "$2" \ + 'tunnel_address:regex(".+\:[0-9]+")' +} diff --git a/shadowsocks-rust/Makefile b/shadowsocks-rust/Makefile index 74bf8ebae..11fb4694b 100644 --- a/shadowsocks-rust/Makefile +++ b/shadowsocks-rust/Makefile @@ -2,6 +2,7 @@ # # Copyright (C) 2017-2020 Yousong Zhou # Copyright (C) 2021-2023 ImmortalWrt.org +# Copyright (C) 2023 Yannick Chabanois (Ycarus) for OpenMPTCProuter include $(TOPDIR)/rules.mk @@ -64,6 +65,34 @@ define Package/shadowsocks-rust-config/install $(INSTALL_BIN) ./files/shadowsocks-rust.init $(1)/etc/init.d/shadowsocks-rust endef +define Package/shadowsocks-rust-config-nft + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocks-rust config + URL:=https://github.com/shadowsocks/shadowsocks-rust + DEPENDS:=+firewall4 \ + +ip \ + +resolveip \ + +ucode \ + +ucode-mod-fs \ + +kmod-nft-tproxy +endef + + +define Package/shadowsocks-rust-config-nft/install + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./files/ssr-rules.defaults $(1)/etc/uci-defaults + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_DATA) ./files/shadowsocks-rust.config $(1)/etc/config/shadowsocks-rust + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/shadowsocks-rust.init-nft $(1)/etc/init.d/shadowsocks-rust + $(INSTALL_DIR) $(1)/usr/share/ssr-rules + $(INSTALL_DATA) ./files/nft-rules/* $(1)/usr/share/ssr-rules/ +endef + + + SHADOWSOCKS_COMPONENTS:=sslocal ssmanager ssserver ssurl ssservice define shadowsocks-rust/templates $(foreach component,$(SHADOWSOCKS_COMPONENTS), @@ -73,6 +102,7 @@ endef $(eval $(call shadowsocks-rust/templates)) $(eval $(call BuildPackage,shadowsocks-rust-config)) +$(eval $(call BuildPackage,shadowsocks-rust-config-nft)) $(foreach component,$(SHADOWSOCKS_COMPONENTS), \ $(eval $(call BuildPackage,shadowsocks-rust-$(component))) \ ) diff --git a/shadowsocks-rust/files/nft-rules/chain.uc b/shadowsocks-rust/files/nft-rules/chain.uc new file mode 100644 index 000000000..3b2cc0813 --- /dev/null +++ b/shadowsocks-rust/files/nft-rules/chain.uc @@ -0,0 +1,126 @@ +{% +function get_local_verdict() { + let v = o_local_default; + if (v == "checkdst") { + return "goto ss_rules_dst_" + proto; + } else if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return null; + } +} + +function get_src_default_verdict() { + let v = o_src_default; + if (v == "checkdst") { + return "goto ss_rules_dst_" + proto; + } else if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_dst_default_verdict() { + let v = o_dst_default; + if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_ifnames() { + let res = []; + for (let ifname in split(o_ifnames, /[ \t\n]/)) { + ifname = trim(ifname); + if (ifname) push(res, ifname); + } + return res; +} + +let type, hook, priority, redir_port; +if (proto == "tcp") { + type = "nat"; + hook = "prerouting"; + priority = -1; + redir_port = o_redir_tcp_port; +} else if (proto == "udp") { + type = "filter"; + hook = "prerouting"; + priority = "mangle"; + redir_port = o_redir_udp_port; + if (system(" + set -o errexit + iprr() { + while ip $1 rule del fwmark 1 lookup 100 2>/dev/null; do true; done + ip $1 rule add fwmark 1 lookup 100 + ip $1 route flush table 100 2>/dev/null || true + ip $1 route add local default dev lo table 100 + } + iprr -4 + iprr -6 + ") != 0) { + return ; + } +} else { + return; +} + +%} +{% if (redir_port): %} + +chain ss_rules_pre_{{ proto }} { + type {{ type }} hook {{ hook }} priority {{ priority }}; + meta l4proto {{ proto }}{%- let ifnames=get_ifnames(); if (length(ifnames)): %} iifname { {{join(", ", ifnames)}} }{% endif %} goto ss_rules_pre_src_{{ proto }}; +} + +chain ss_rules_pre_src_{{ proto }} { + ip daddr @ss_rules_dst_bypass_ accept; + ip6 daddr @ss_rules6_dst_bypass_ accept; + goto ss_rules_src_{{ proto }}; +} + +chain ss_rules_src_{{ proto }} { + ip saddr @ss_rules_src_bypass accept; + ip saddr @ss_rules_src_forward goto ss_rules_forward_{{ proto }}; + ip saddr @ss_rules_src_checkdst goto ss_rules_dst_{{ proto }}; + ip6 saddr @ss_rules6_src_bypass accept; + ip6 saddr @ss_rules6_src_forward goto ss_rules_forward_{{ proto }}; + ip6 saddr @ss_rules6_src_checkdst goto ss_rules_dst_{{ proto }}; + {{ get_src_default_verdict() }}; +} + +chain ss_rules_dst_{{ proto }} { + ip daddr @ss_rules_dst_bypass accept; + ip daddr @ss_rules_remote_servers accept; + ip daddr @ss_rules_dst_forward goto ss_rules_forward_{{ proto }}; + ip6 daddr @ss_rules6_dst_bypass accept; + ip6 daddr @ss_rules6_remote_servers accept; + ip6 daddr @ss_rules6_dst_forward goto ss_rules_forward_{{ proto }}; + {{ get_dst_default_verdict() }}; +} + +{% if (proto == "tcp"): %} +chain ss_rules_forward_{{ proto }} { + meta l4proto tcp {{ o_nft_tcp_extra }} redirect to :{{ redir_port }}; +} +{% let local_verdict = get_local_verdict(); if (local_verdict): %} +chain ss_rules_local_out { + type {{ type }} hook output priority -1; + meta l4proto != tcp accept; + ip daddr @ss_rules_remote_servers accept; + ip daddr @ss_rules_dst_bypass_ accept; + ip daddr @ss_rules_dst_bypass accept; + ip6 daddr @ss_rules6_remote_servers accept; + ip6 daddr @ss_rules6_dst_bypass_ accept; + ip6 daddr @ss_rules6_dst_bypass accept; + {{ local_verdict }}; +} +{% endif %} +{% elif (proto == "udp"): %} +chain ss_rules_forward_{{ proto }} { + meta l4proto udp {{ o_nft_udp_extra }} meta mark set 1 tproxy to :{{ redir_port }}; +} +{% endif %} +{% endif %} diff --git a/shadowsocks-rust/files/nft-rules/set.uc b/shadowsocks-rust/files/nft-rules/set.uc new file mode 100644 index 000000000..2698d444a --- /dev/null +++ b/shadowsocks-rust/files/nft-rules/set.uc @@ -0,0 +1,117 @@ +{% +let fs = require("fs"); + +let o_dst_bypass4_ = " + 0.0.0.0/8 + 10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.0/8 + 169.254.0.0/16 + 172.16.0.0/12 + 192.0.0.0/24 + 192.0.2.0/24 + 192.168.0.0/16 + 192.31.196.0/24 + 192.52.193.0/24 + 192.88.99.0/24 + 192.175.48.0/24 + 198.18.0.0/15 + 198.51.100.0/24 + 203.0.113.0/24 + 224.0.0.0/4 + 240.0.0.0/4 +"; +let o_dst_bypass6_ = " + ::1/128 + ::/128 + ::ffff:0:0/96 + 64:ff9b:1::/48 + 100::/64 + fe80::/10 + 2001::/23 + fc00::/7 +"; +let o_dst_bypass_ = o_dst_bypass4_ + " " + o_dst_bypass6_; + +let set_suffix = { + "src_bypass": { + str: o_src_bypass, + }, + "src_forward": { + str: o_src_forward, + }, + "src_checkdst": { + str: o_src_checkdst, + }, + "remote_servers": { + str: o_remote_servers + }, + "dst_bypass": { + str: o_dst_bypass, + file: o_dst_bypass_file, + }, + "dst_bypass_": { + str: o_dst_bypass_, + }, + "dst_forward": { + str: o_dst_forward, + file: o_dst_forward_file, + }, + "dst_forward_rrst_": {}, +}; + +function set_name(suf, af) { + if (af == 4) { + return "ss_rules_"+suf; + } else { + return "ss_rules6_"+suf; + } +} + +function set_elements_parse(res, str, af) { + for (let addr in split(str, /[ \t\n]/)) { + addr = trim(addr); + if (!addr) continue; + if (af == 4 && index(addr, ":") != -1) continue; + if (af == 6 && index(addr, ":") == -1) continue; + push(res, addr); + } +} + +function set_elements(suf, af) { + let obj = set_suffix[suf]; + let res = []; + let addr; + + let str = obj["str"]; + if (str) { + set_elements_parse(res, str, af); + } + + let file = obj["file"]; + if (file) { + let fd = fs.open(file); + if (fd) { + str = fd.read("all"); + set_elements_parse(res, str, af); + } + } + + return res; +} +%} + +{% for (let suf in set_suffix): for (let af in [4, 6]): %} +set {{ set_name(suf, af) }} { + type ipv{{af}}_addr; + flags interval; + auto-merge; +{% let elems = set_elements(suf, af); if (length(elems)): %} + elements = { +{% for (let i = 0; i < length(elems); i++): %} + {{ elems[i] }}{% if (i < length(elems) - 1): %},{% endif %}{% print("\n") %} +{% endfor %} + } +{% endif %} +} +{% endfor; endfor %} diff --git a/shadowsocks-rust/files/nft-rules/ss-rules.uc b/shadowsocks-rust/files/nft-rules/ss-rules.uc new file mode 100644 index 000000000..f3955b2ef --- /dev/null +++ b/shadowsocks-rust/files/nft-rules/ss-rules.uc @@ -0,0 +1,8 @@ +{% + +include("set.uc"); +include("chain.uc", {proto: "tcp"}); +include("chain.uc", {proto: "udp"}); + +%} + diff --git a/shadowsocks-rust/files/shadowsocks-rust.init-nft b/shadowsocks-rust/files/shadowsocks-rust.init-nft new file mode 100755 index 000000000..d895c822a --- /dev/null +++ b/shadowsocks-rust/files/shadowsocks-rust.init-nft @@ -0,0 +1,347 @@ +#!/bin/sh /etc/rc.common +# +# Copyright (C) 2017-2019 Yousong Zhou +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +USE_PROCD=1 +START=99 + +EXTRA_COMMANDS="rules_up rules_down rules_exist" + +ss_confdir=/var/etc/shadowsocks-rust +ss_bindir=/usr/bin + +ssrules_uc="/usr/share/ssr-rules/ss-rules.uc" +ssrules_nft="/etc/nftables.d/90-ssr-rules.nft" + +ss_mkjson_server_conf() { + local cfgserver + + config_get cfgserver "$cfg" server + [ -n "$cfgserver" ] || return 1 + eval "$(validate_server_section "$cfg" ss_validate_mklocal)" + validate_server_section "$cfgserver" || return 1 + [ "$disabled" = 0 ] || return 1 + ss_mkjson_server_conf_ "$cfgserver" +} + +ss_mkjson_server_conf_() { + [ -n "$server_port" ] || return 1 + [ -z "$server" ] || json_add_string server "$server" + json_add_int server_port "$server_port" + [ -z "$method" ] || json_add_string method "$method" + [ -z "$key" ] || json_add_string key "$key" + [ -z "$password" ] || json_add_string password "$password" + [ -z "$plugin" ] || json_add_string plugin "$plugin" + [ -z "$plugin_opts" ] || json_add_string plugin_opts "$plugin_opts" +} + +ss_mkjson_ss_local_conf() { + ss_mkjson_server_conf +} + +ss_mkjson_ss_redir_conf() { + ss_mkjson_server_conf +} + +ss_mkjson_ss_server_conf() { + ss_mkjson_server_conf_ +} + +ss_mkjson_ss_tunnel_conf() { + ss_mkjson_server_conf || return 1 + [ -n "$tunnel_address" ] || return 1 + json_add_string tunnel_address "$tunnel_address" +} + +ss_xxx() { + local cfg="$1" + local cfgtype="$2" +# local bin="$ss_bindir/${cfgtype/_/-}" + local bin="$ss_bindir/sslocal" + local confjson="$ss_confdir/$cfgtype.$cfg.json" + + [ -x "$bin" ] || return + eval "$("validate_${cfgtype}_section" "$cfg" ss_validate_mklocal)" + "validate_${cfgtype}_section" "$cfg" || return + [ "$disabled" = 0 ] || return + + json_init + ss_mkjson_${cfgtype}_conf || return + json_add_boolean use_syslog 1 + json_add_boolean ipv6_first "$ipv6_first" + json_add_boolean fast_open "$fast_open" + json_add_boolean reuse_port "$reuse_port" + json_add_boolean no_delay "$no_delay" + json_add_boolean mptcp "$mptcp" + [ "$cfgtype" != "ss_local" ] && json_add_string protocol "${cfgtype/ss_/}" + [ -z "$local_address" ] || json_add_string local_address "$local_address" + [ -z "$local_port" ] || json_add_int local_port "$local_port" + [ -z "$local_ipv4_address" ] || json_add_string local_ipv4_address "$local_ipv4_address" + [ -z "$local_ipv6_address" ] || json_add_string local_ipv6_address "$local_ipv6_address" + [ -z "$mode" ] || json_add_string mode "$mode" + [ -z "$mtu" ] || json_add_int mtu "$mtu" + [ -z "$timeout" ] || json_add_int timeout "$timeout" + [ -z "$user" ] || json_add_string user "$user" + [ -z "$acl" ] || json_add_string acl "$acl" + json_dump -i >"$confjson" + + procd_open_instance "$cfgtype.$cfg" + procd_set_param command "$bin" -c "$confjson" + [ "$verbose" = 0 ] || procd_append_param command -v + if [ -n "$bind_address" ]; then + echo "$cfgtype $cfg: uci option bind_address deprecated, please switch to local_address" >&2 + procd_append_param command -b "$bind_address" + fi + procd_set_param file "$confjson" + procd_set_param respawn + procd_close_instance + ss_rules_cb +} + +ss_rules_cb() { + local cfgserver server + + if [ "$cfgtype" = ss_redir ]; then + config_get cfgserver "$cfg" server + config_get server "$cfgserver" server + ss_redir_servers="$ss_redir_servers $server" + if [ "$mode" = tcp_only -o "$mode" = "tcp_and_udp" ]; then + eval "ss_rules_redir_tcp_$cfg=$local_port" + fi + if [ "$mode" = udp_only -o "$mode" = "tcp_and_udp" ]; then + eval "ss_rules_redir_udp_$cfg=$local_port" + fi + fi +} + +ss_rules_nft_gen() { + local cfg="ss_rules" + local cfgtype + local local_port_tcp local_port_udp + local remote_servers + [ -s "$ssrules_uc" ] || return 1 + config_get cfgtype "$cfg" TYPE + [ "$cfgtype" = ss_rules ] || return 1 + eval "$(validate_ss_rules_section "$cfg" ss_validate_mklocal)" + validate_ss_rules_section "$cfg" || return 1 + [ "$disabled" = 0 ] || return 2 + eval local_port_tcp="\$ss_rules_redir_tcp_$redir_tcp" + eval local_port_udp="\$ss_rules_redir_udp_$redir_udp" + [ -n "$local_port_tcp" -o -n "$local_port_udp" ] || return 1 + remote_servers="$(echo $ss_redir_servers \ + | tr ' ' '\n' \ + | sort -u \ + | xargs -n 1 resolveip \ + | sort -u)" + + local tmp="/tmp/ssrrules" + json_init + json_add_string o_remote_servers "$remote_servers" + json_add_int o_redir_tcp_port "$local_port_tcp" + json_add_int o_redir_udp_port "$local_port_udp" + json_add_string o_ifnames "$ifnames" + json_add_string o_local_default "$local_default" + json_add_string o_src_bypass "$src_ips_bypass" + json_add_string o_src_forward "$src_ips_forward" + json_add_string o_src_checkdst "$src_ips_checkdst" + json_add_string o_src_default "$src_default" + json_add_string o_dst_bypass "$dst_ips_bypass" + json_add_string o_dst_forward "$dst_ips_forward" + json_add_string o_dst_bypass_file "$dst_ips_bypass_file" + json_add_string o_dst_forward_file "$dst_ips_forward_file" + json_add_string o_dst_default "$dst_default" + json_add_string o_nft_tcp_extra "$nft_tcp_extra" + json_add_string o_nft_udp_extra "$nft_udp_extra" + json_dump -i >"$tmp.json" + + if utpl -S -F "$tmp.json" "$ssrules_uc" >"$tmp.nft" \ + && ! cmp -s "$tmp.nft" "$ssrules_nft"; then + echo "table inet chk {include \"$tmp.nft\";}" >"$tmp.nft.chk" + if nft -f "$tmp.nft.chk" -c; then + mv "$tmp.nft" "$ssrules_nft" + fw4 restart + fi + rm -f "$tmp.nft.chk" + fi + rm -f "$tmp.json" + rm -f "$tmp.nft" +} + +ss_rules_nft_reset() { + if [ -f "$ssrules_nft" ]; then + rm -f "$ssrules_nft" + fw4 restart + fi +} + +ss_rules() { + if ! ss_rules_nft_gen; then + ss_rules_nft_reset + fi +} + +rules_up() { + if [ -f "${ssrules_nft}.down" ]; then + mv -f "${ssrules_nft}.down" "$ssrules_nft" + fw4 restart + fi +} + +rules_down() { + if [ -f "${ssrules_nft}" ]; then + mv -f "$ssrules_nft" "${ssrules_nft}.down" + fw4 restart + fi +} + +rules_exist() { + if [ -f "$ssrules_nft" ]; then + return 0 + else + return 1 + fi +} + + + +start_service() { + local cfgtype + + mkdir -p "$ss_confdir" + config_load shadowsocks-rust + for cfgtype in ss_local ss_redir ss_server ss_tunnel; do + config_foreach ss_xxx "$cfgtype" "$cfgtype" + done + ss_rules +} + +stop_service() { + ss_rules_nft_reset + rm -rf "$ss_confdir" +} + +service_triggers() { + procd_add_reload_interface_trigger wan + procd_add_reload_trigger shadowsocks-rust + procd_open_validate + validate_server_section + validate_ss_local_section + validate_ss_redir_section + validate_ss_rules_section + validate_ss_server_section + validate_ss_tunnel_section + procd_close_validate +} + +ss_validate_mklocal() { + local tuple opts + + shift 2 + for tuple in "$@"; do + opts="${tuple%%:*} $opts" + done + [ -z "$opts" ] || echo "local $opts" +} + +ss_validate() { + uci_validate_section shadowsocks-rust "$@" +} + +validate_common_server_options_() { + local cfgtype="$1"; shift + local cfg="$1"; shift + local func="$1"; shift + local stream_methods='"none", "plain", "chacha20-ietf-poly1305"' + local aead_methods='"aes-128-gcm", "aes-256-gcm","2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "chacha20-ietf-poly1305", "2022-blake3-chacha20-poly1305","2022-blake3-chacha8-poly1305"' + + + "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ + 'disabled:bool:0' \ + 'server:host' \ + 'server_port:port' \ + 'password:string' \ + 'key:string' \ + "method:or($stream_methods, $aead_methods)" \ + 'plugin:string' \ + 'plugin_opts:string' +} + +validate_common_client_options_() { + validate_common_options_ "$@" \ + 'server:uci("shadowsocks-rust", "@server")' \ + 'local_address:ipaddr:0.0.0.0' \ + 'local_port:port' +} + +validate_common_options_() { + local cfgtype="$1"; shift + local cfg="$1"; shift + local func="$1"; shift + + "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ + 'disabled:bool:0' \ + 'fast_open:bool:0' \ + 'ipv6_first:bool:0' \ + 'no_delay:bool:0' \ + 'reuse_port:bool:0' \ + 'mptcp:bool:0' \ + 'verbose:bool:0' \ + 'mode:or("tcp_only", "udp_only", "tcp_and_udp"):tcp_only' \ + 'mtu:uinteger' \ + 'timeout:uinteger' \ + 'user:string' +} + +validate_server_section() { + validate_common_server_options_ server "$1" "$2" +} + +validate_ss_local_section() { + validate_common_client_options_ ss_local "$1" "$2" \ + 'acl:file' +} + +validate_ss_redir_section() { + validate_common_client_options_ ss_redir "$1" "$2" +} + +validate_ss_rules_section() { + "${2:-ss_validate}" ss_rules "$1" \ + 'disabled:bool:0' \ + 'redir_tcp:uci("shadowsocks-rust", "@ss_redir")' \ + 'redir_udp:uci("shadowsocks-rust", "@ss_redir")' \ + 'src_ips_bypass:or(ipaddr,cidr)' \ + 'src_ips_forward:or(ipaddr,cidr)' \ + 'src_ips_checkdst:or(ipaddr,cidr)' \ + 'dst_ips_bypass_file:file' \ + 'dst_ips_bypass:or(ipaddr,cidr)' \ + 'dst_ips_forward_file:file' \ + 'dst_ips_forward:or(ipaddr,cidr)' \ + 'src_default:or("bypass", "forward", "checkdst"):checkdst' \ + 'dst_default:or("bypass", "forward"):bypass' \ + 'local_default:or("bypass", "forward", "checkdst"):bypass' \ + 'nft_tcp_extra:string' \ + 'nft_udp_extra:string' \ + 'ifnames:maxlength(15)' +} + +validate_ss_server_section() { + validate_common_server_options_ ss_server "$1" \ + validate_common_options_ \ + "$2" \ + 'local_address:ipaddr' \ + 'local_ipv4_address:ip4addr' \ + 'local_ipv6_address:ip6addr' \ + 'bind_address:ipaddr' \ + 'acl:file' +} + +validate_ss_tunnel_section() { + validate_common_client_options_ ss_tunnel "$1" \ + "$2" \ + 'tunnel_address:regex(".+\:[0-9]+")' +} diff --git a/v2ray-core/Makefile b/v2ray-core/Makefile index f89c9d1bd..ebae7974c 100755 --- a/v2ray-core/Makefile +++ b/v2ray-core/Makefile @@ -59,6 +59,20 @@ define Package/v2ray-extra PKGARCH:=all endef +define Package/v2ray-config + $(call Package/v2ray/template) + TITLE+= (init script) + DEPENDS:=v2ray-core + PKGARCH:=all +endef + +define Package/v2ray-config-nft + $(call Package/v2ray/template) + TITLE+= (init script with nft) + DEPENDS:=v2ray-core + PKGARCH:=all +endef + define Package/v2ray/description Project V is a set of network tools that help you to build your own computer network. It secures your network connections and thus protects your privacy. @@ -80,6 +94,18 @@ define Package/v2ray-extra/description This includes extra resources for v2ray-core. endef +define Package/v2ray-config/description + $(call Package/v2ray/description) + + This includes init script +endef + +define Package/v2ray-config-nft/description + $(call Package/v2ray/description) + + This includes init script with nftables support +endef + define Package/v2ray-core/conffiles /etc/config/v2ray /etc/v2ray/ @@ -92,12 +118,17 @@ define Package/v2ray-core/install $(INSTALL_DIR) $(1)/etc/v2ray/ $(INSTALL_DATA) $(PKG_BUILD_DIR)/release/config/config.json $(1)/etc/v2ray/ +endef + +define Package/v2ray-config/install $(CP) ./files/* $(1)/ -# $(INSTALL_DIR) $(1)/etc/config/ -# $(INSTALL_CONF) $(CURDIR)/files/v2ray.conf $(1)/etc/config/v2ray -# $(INSTALL_DIR) $(1)/etc/init.d/ -# $(INSTALL_BIN) $(CURDIR)/files/v2ray.init $(1)/etc/init.d/v2ray + rm $(1)/etc/init.d/v2ray-nft +endef + +define Package/v2ray-config-nft/install + $(CP) ./files/* $(1)/ + mv $(1)/etc/init.d/v2ray-nft $(1)/etc/init.d/v2ray endef define Package/v2ray-example/install @@ -112,5 +143,7 @@ define Package/v2ray-extra/install endef $(eval $(call BuildPackage,v2ray-core)) +$(eval $(call BuildPackage,v2ray-config)) +$(eval $(call BuildPackage,v2ray-config-nft)) $(eval $(call BuildPackage,v2ray-example)) $(eval $(call BuildPackage,v2ray-extra)) diff --git a/v2ray-core/files/etc/init.d/v2ray-nft b/v2ray-core/files/etc/init.d/v2ray-nft new file mode 100755 index 000000000..90d5732f8 --- /dev/null +++ b/v2ray-core/files/etc/init.d/v2ray-nft @@ -0,0 +1,2304 @@ +#!/bin/sh /etc/rc.common +# +# Copyright 2019-2020 Xingwang Liao +# Copyright 2020-2022 Ycarus (Yannick Chabanois) for OpenMPTCProuter +# Licensed to the public under the MIT License. +# + +START=99 +USE_PROCD=1 +EXTRA_COMMANDS="rules_up rules_down rules_exist" + +NAME=v2ray +CONFIG_FOLDER=/var/etc/$NAME + +v2rrules_uc="/usr/share/v2ray-rules/v2r-rules.uc" +v2rrules_nft="/etc/nftables.d/90-v2r-rules.nft" + +FILE_V2RAY_DNSMASQ=/tmp/dnsmasq.d/$NAME +FILE_V2RAY_DNSMASQ_CACHE=/tmp/$NAME.dnsmasq.cache + +IPSET_SRC_IGNORE_V4=v2ray_src_ignore_v4 +IPSET_SRC_IGNORE_V6=v2ray_src_ignore_v6 +IPSET_DST_PROXY_V4=v2ray_dst_proxy_v4 +IPSET_DST_PROXY_V6=v2ray_dst_proxy_v6 +IPSET_SRC_DIRECT_V4=ss_rules_src_bypass +IPSET_DST_DIRECT_V4=ss_rules_dst_bypass +IPSET_DST_DIRECT_V6=ss_rules6_dst_bypass + +OUTBOUND_SERVERS= +OUTBOUND_SERVERS_V4= +OUTBOUND_SERVERS_V6= + +TRANSPARENT_PROXY_EXPECTED=0 +TRANSPARENT_PROXY_PORT= +TRANSPARENT_PROXY_USE_TPROXY= +TRANSPARENT_PROXY_ADDITION= + +DNSMASQ_RESTART_EXPECTED=0 + +. /usr/share/libubox/jshn.sh + +_log() { + local level="$1" ; shift + local msg="$@" + logger -p "daemon.$level" -t "$NAME" "$msg" + + echo "[$level] $msg" >&2 +} + +_info() { + _log "info" $@ +} + +_err() { + _log "err" $@ +} + +version_over_5_4() { + MAJOR_VERSION=$(uname -r | awk -F '.' '{print $1}') + MINOR_VERSION=$(uname -r | awk -F '.' '{print $2}') + if [ $MAJOR_VERSION -ge 5 ] && [ $MINOR_VERSION -gt 13 ] || [ $MAJOR_VERSION -gt 5 ] ; then + return 0 + else + return 1 + fi +} + + +get_value_from_json() { + local json="$1" + local key="$2" + + test -n "$json" || return + + local value="" + + local old_ns + json_set_namespace "json_key" old_ns + json_load "$json" + json_get_var "$key" value + json_cleanup + json_set_namespace "$old_ns" + + echo "$value" +} + +get_commands_from_json() { + local json="$1" + + test -n "$json" || return + + jshn -r "$json" 2>/dev/null | grep -v "json_init" +} + +get_file_content() { + local filename="$1" + + test -n "$filename" || return + test -r "/etc/v2ray/${filename}.txt" || return + + cat "/etc/v2ray/${filename}.txt" | grep -v "^$" | grep -v "^#" +} + +append_server_ipv4() { + local addr="$1" + + test -n "$addr" || return + + if [ -z "$OUTBOUND_SERVERS_V4" ] ; then + OUTBOUND_SERVERS_V4="$addr" + else + OUTBOUND_SERVERS_V4="$(cat >&1 <<-EOF + $OUTBOUND_SERVERS_V4 + $addr + EOF + )" + fi +} + +append_server_ipv6() { + local addr="$1" + + test -n "$addr" || return + + if [ -z "$OUTBOUND_SERVERS_V6" ] ; then + OUTBOUND_SERVERS_V6="$addr" + else + OUTBOUND_SERVERS_V6="$(cat >&1 <<-EOF + $OUTBOUND_SERVERS_V6 + $addr + EOF + )" + fi +} + +append_server_address() { + local addr="$1" + + test -n "$addr" || return + + local ipv4 + for ipv4 in $(resolveip -4 -t 5 "$addr") ; do + append_server_ipv4 "$ipv4" + OUTBOUND_SERVERS="$OUTBOUND_SERVERS $ipv4" + done + + local ipv6 + for ipv6 in $(resolveip -6 -t 5 "$addr") ; do + append_server_ipv6 "$ipv6" + OUTBOUND_SERVERS="$OUTBOUND_SERVERS $ipv6" + done +} + +v2ray_section_validate() { + uci_validate_section "$NAME" "v2ray" "$1" \ + 'enabled:bool:0' \ + 'v2ray_file:string' \ + 'asset_location:directory' \ + 'mem_percentage:and(uinteger, max(100)):80' \ + 'config_file:file' \ + 'loglevel:or("debug", "info", "warning", "error", "none")' \ + 'access_log:string' \ + 'error_log:string' \ + 'stats_enabled:bool:0' \ + 'transport_enabled:bool:0' \ + 'inbounds:list(uci("v2ray", "@inbound"))' \ + 'outbounds:list(uci("v2ray", "@outbound"))' +} + +dns_section_validate() { + uci_validate_section "$NAME" "dns" "$1" \ + 'enabled:bool:0' \ + 'tag:string' \ + 'client_ip:ipaddr' \ + 'hosts:list(string)' \ + 'servers:list(uci("v2ray", "@dns_server"))' +} + +dns_server_section_validate() { + uci_validate_section "$NAME" "dns_server" "$1" \ + 'address:string' \ + 'port:port' \ + 'domains:list(string)' \ + 'expect_ips:list(string)' +} + +routing_section_validate() { + uci_validate_section "$NAME" "routing" "$1" \ + 'enabled:bool:0' \ + 'domain_strategy:or("AsIs", "IPIfNonMatch", "IPOnDemand")' \ + 'rules:list(uci("v2ray", "@routing_rule"))' \ + 'balancers:list(uci("v2ray", "@routing_balancer"))' +} + +routing_rule_section_validate() { + uci_validate_section "$NAME" "routing_rule" "$1" \ + 'type:"field"' \ + 'domain:list(string)' \ + 'ip:list(string)' \ + 'port:or(port, portrange)' \ + 'network:list(or("tcp", "udp"))' \ + 'source:list(string)' \ + 'user:list(string)' \ + 'inbound_tag:list(string)' \ + 'protocol:list(or("http", "tls", "bittorrent"))' \ + 'attrs:string' \ + 'outbound_tag:string' \ + 'balancer_tag:string' +} + +routing_balancer_section_validate() { + uci_validate_section "$NAME" "routing_balancer" "$1" \ + 'tag:string' \ + 'selector:list(string)' +} + +policy_section_validate() { + uci_validate_section "$NAME" "policy" "$1" \ + 'enabled:bool:0' \ + 'levels:list(uci("v2ray", "@policy_level"))' \ + 'system_stats_inbound_uplink:bool:0' \ + 'system_stats_inbound_downlink:bool:0' +} + +policy_level_section_validate() { + uci_validate_section "$NAME" "policy_level" "$1" \ + 'level:uinteger' \ + 'handshake:uinteger:4' \ + 'conn_idle:uinteger:300' \ + 'uplink_only:uinteger:2' \ + 'downlink_only:uinteger:5' \ + 'stats_user_uplink:bool:0' \ + 'stats_user_downlink:bool:0' \ + 'buffer_size:uinteger' +} + +reverse_section_validate() { + uci_validate_section "$NAME" "reverse" "$1" \ + 'enabled:bool:0' \ + 'bridges:list(string)' \ + 'portals:list(string)' +} + +inbound_section_validate() { + uci_validate_section "$NAME" "inbound" "$1" \ + 'port:or(port, portrange, string)' \ + 'listen:ipaddr' \ + 'protocol:string' \ + 's_dokodemo_door_address:host' \ + 's_dokodemo_door_port:port' \ + 's_dokodemo_door_network:list(or("tcp", "udp"))' \ + 's_dokodemo_door_timeout:uinteger' \ + 's_dokodemo_door_follow_redirect:bool:0' \ + 's_dokodemo_door_user_level:uiterger' \ + 's_http_account_user:string' \ + 's_http_account_pass:string' \ + 's_http_allow_transparent:bool:0' \ + 's_http_timeout:uinteger' \ + 's_http_user_level:uinteger' \ + 's_mtproto_user_email:string' \ + 's_mtproto_user_secret:string' \ + 's_mtproto_user_level:uinteger' \ + 's_shadowsocks_email:string' \ + 's_shadowsocks_method:string' \ + 's_shadowsocks_password:string' \ + 's_shadowsocks_level:uinteger' \ + 's_shadowsocks_ota:bool:0' \ + 's_shadowsocks_network:list(or("tcp", "udp")):tcp' \ + 's_socks_auth:or("noauth", "password")' \ + 's_socks_account_user:string' \ + 's_socks_account_pass:string' \ + 's_socks_udp:bool:0' \ + 's_socks_ip:host' \ + 's_socks_user_level:uinteger' \ + 's_vmess_client_id:string' \ + 's_vmess_client_alter_id:and(uinteger, max(65535))' \ + 's_vmess_client_email:string' \ + 's_vmess_client_user_level:uinteger' \ + 's_vmess_default_alter_id:and(uinteger, max(65535))' \ + 's_vmess_default_user_level:uinteger' \ + 's_vmess_detour_to:string' \ + 's_vmess_disable_insecure_encryption:bool:0' \ + 's_vless_client_id:string' \ + 's_vless_client_alter_id:and(uinteger, max(65535))' \ + 's_vless_client_email:string' \ + 's_vless_client_user_level:uinteger' \ + 's_vless_default_alter_id:and(uinteger, max(65535))' \ + 's_vless_default_user_level:uinteger' \ + 's_vless_detour_to:string' \ + 's_vless_disable_insecure_encryption:bool:0' \ + 's_trojan_client_id:string' \ + 's_trojan_client_alter_id:and(uinteger, max(65535))' \ + 's_trojan_client_email:string' \ + 's_trojan_client_user_level:uinteger' \ + 's_trojan_default_alter_id:and(uinteger, max(65535))' \ + 's_trojan_default_user_level:uinteger' \ + 's_trojan_detour_to:string' \ + 's_trojan_disable_insecure_encryption:bool:0' \ + 's_socks_client_id:string' \ + 's_socks_client_email:string:"openmptcprouter"' \ + 'ss_network:or("tcp", "kcp", "ws", "http", "domainsocket", "quic")' \ + 'ss_security:or("none", "tls")' \ + 'ss_tls_server_name:host' \ + 'ss_tls_alpn:string' \ + 'ss_tls_allow_insecure:bool:0' \ + 'ss_tls_allow_insecure_ciphers:bool:0' \ + 'ss_tls_disable_system_root:bool:0' \ + 'ss_tls_cert_usage:or("encipherment", "verify", "issue")' \ + 'ss_tls_cert_file:string' \ + 'ss_tls_key_file:string' \ + 'ss_tcp_header_type:or("none", "http")' \ + 'ss_tcp_header_request_version:string' \ + 'ss_tcp_header_request_method:string:GET' \ + 'ss_tcp_header_request_path:string' \ + 'ss_tcp_header_request_headers:list(string)' \ + 'ss_tcp_header_response_version:string' \ + 'ss_tcp_header_response_status:string' \ + 'ss_tcp_header_response_reason:string' \ + 'ss_tcp_header_response_headers:list(string)' \ + 'ss_kcp_mtu:and(min(576), max(1460))' \ + 'ss_kcp_tti:and(min(10), max(100))' \ + 'ss_kcp_uplink_capacity:uinteger' \ + 'ss_kcp_downlink_capacity:uinteger' \ + 'ss_kcp_congestion:bool:0' \ + 'ss_kcp_read_buffer_size:uinteger' \ + 'ss_kcp_write_buffer_size:uinteger' \ + 'ss_kcp_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ + 'ss_websocket_path:string' \ + 'ss_websocket_headers:list(string)' \ + 'ss_http_host:list(host)' \ + 'ss_http_path:string' \ + 'ss_domainsocket_path:string' \ + 'ss_quic_security:or("aes-128-gcm", "chacha20-poly1305", "none")' \ + 'ss_quic_key:string' \ + 'ss_quic_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ + 'ss_sockopt_tcp_fast_open:or("0", "1")' \ + 'ss_sockopt_mptcp:or("0", "1")' \ + 'ss_sockopt_tproxy:or("redirect", "tproxy", "off")' \ + 'tag:string' \ + 'sniffing_enabled:bool:0' \ + 'sniffing_dest_override:list(or("http", "tls"))' \ + 'allocate_strategy:or("always", "random")' \ + 'allocate_refresh:uinteger' \ + 'allocate_concurrency:uinteger' +} + +outbound_section_validate() { + uci_validate_section "$NAME" "outbound" "$1" \ + 'send_through:ipaddr' \ + 'protocol:string' \ + 'tag:string' \ + 's_blackhole_reponse_type:or("none", "http")' \ + 's_dns_network:or("tcp", "udp")' \ + 's_dns_address:string' \ + 's_dns_port:port' \ + 's_freedom_domain_strategy:or("AsIs", "UseIP", "UseIPv4", "UseIPv6")' \ + 's_freedom_redirect:string' \ + 's_freedom_user_level:uinteger' \ + 's_http_server_address:host' \ + 's_http_server_port:port' \ + 's_http_account_user:string' \ + 's_http_account_pass:string' \ + 's_shadowsocks_email:string' \ + 's_shadowsocks_address:host' \ + 's_shadowsocks_port:port' \ + 's_shadowsocks_method:string' \ + 's_shadowsocks_password:string' \ + 's_shadowsocks_level:uinteger' \ + 's_shadowsocks_ota:bool:0' \ + 's_socks_server_address:host' \ + 's_socks_server_port:port' \ + 's_socks_account_user:string' \ + 's_socks_account_pass:string' \ + 's_socks_user_level:uinteger' \ + 's_vmess_address:host' \ + 's_vmess_port:port' \ + 's_vmess_user_id:string' \ + 's_vmess_user_alter_id:and(uinteger, max(65535))' \ + 's_vmess_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ + 's_vmess_user_level:uinteger' \ + 's_vless_address:host' \ + 's_vless_port:port' \ + 's_vless_user_id:string' \ + 's_vless_user_alter_id:and(uinteger, max(65535))' \ + 's_vless_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ + 's_vless_user_encryption:or("auto", "none")' \ + 's_vless_user_level:uinteger' \ + 's_trojan_address:host' \ + 's_trojan_port:port' \ + 's_trojan_user_id:string' \ + 's_trojan_user_alter_id:and(uinteger, max(65535))' \ + 's_trojan_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ + 's_trojan_user_encryption:or("auto", "none")' \ + 's_trojan_user_level:uinteger' \ + 's_socks_address:host' \ + 's_socks_port:port' \ + 's_socks_user_id:string' \ + 's_socks_email:string:openmptcprouter' \ + 'ss_network:or("tcp", "kcp", "ws", "http", "domainsocket", "quic")' \ + 'ss_security:or("none", "tls")' \ + 'ss_tls_server_name:host' \ + 'ss_tls_alpn:string' \ + 'ss_tls_allow_insecure:bool:0' \ + 'ss_tls_allow_insecure_ciphers:bool:0' \ + 'ss_tls_disable_system_root:bool:0' \ + 'ss_tls_cert_usage:or("encipherment", "verify", "issue")' \ + 'ss_tls_cert_file:string' \ + 'ss_tls_key_file:string' \ + 'ss_tcp_header_type:or("none", "http")' \ + 'ss_tcp_header_request_version:string' \ + 'ss_tcp_header_request_method:string' \ + 'ss_tcp_header_request_path:string' \ + 'ss_tcp_header_request_headers:list(string)' \ + 'ss_tcp_header_response_version:string' \ + 'ss_tcp_header_response_status:string' \ + 'ss_tcp_header_response_reason:string' \ + 'ss_tcp_header_response_headers:list(string)' \ + 'ss_kcp_mtu:and(min(576), max(1460))' \ + 'ss_kcp_tti:and(min(10), max(100))' \ + 'ss_kcp_uplink_capacity:uinteger' \ + 'ss_kcp_downlink_capacity:uinteger' \ + 'ss_kcp_congestion:bool:0' \ + 'ss_kcp_read_buffer_size:uinteger' \ + 'ss_kcp_write_buffer_size:uinteger' \ + 'ss_kcp_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ + 'ss_websocket_path:string' \ + 'ss_websocket_headers:list(string)' \ + 'ss_http_host:list(host)' \ + 'ss_http_path:string' \ + 'ss_domainsocket_path:string' \ + 'ss_quic_security:or("aes-128-gcm", "chacha20-poly1305", "none")' \ + 'ss_quic_key:string' \ + 'ss_quic_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ + 'ss_sockopt_mark:uinteger' \ + 'ss_sockopt_tcp_fast_open:or("0", "1")' \ + 'ss_sockopt_mptcp:or("0", "1")' \ + 'stream_settings:string' \ + 'proxy_settings_tag:string' \ + 'mux_enabled:bool:0' \ + 'mux_concurrency:uinteger:8' +} + +add_v2ray_redirect_rules() { + local ext_args="$1" + local lan_devices="$2" + local lan_ipaddrs="$3" + + local port="$TRANSPARENT_PROXY_PORT" + local addition="$TRANSPARENT_PROXY_ADDITION" + local ipset_src_direct="$IPSET_SRC_DIRECT_V4" + local ipset_dst_direct="$IPSET_DST_DIRECT_V4" + + test -n "$port" || return + + ## This part need a rewrite + #v2ray-rules -f + ##logger -t "v2ray" "v2ray-rules -l ${port} -L ${port} -s $OUTBOUND_SERVERS_V4 --rule-name def --src-default forward --dst-default forward --local-default forward" + #commandline="-l ${port} -s $OUTBOUND_SERVERS_V4 --rule-name def --src-default forward --dst-default forward --local-default forward" + #[ "$(uci -q get v2ray.main_transparent_proxy.redirect_udp)" = "1" ] && [ "$(uci -q get v2ray.omrout.protocol)" != "socks" ] && commandline="$commandline -L ${port}" + #v2ray-rules $commandline + #[ "$(uci -q get v2ray.main.inbounds | grep omr6)" != "" ] && [ -n "$OUTBOUND_SERVERS_V6" ] && { + # v2ray-rules6 -f + # commandline="-l $((port+1)) -L $((port+1)) -s $OUTBOUND_SERVERS_V6 --rule-name def --src-default forward --dst-default forward --local-default forward" + # [ "$(uci -q get v2ray.main_transparent_proxy.redirect_udp)" = "1" ] && ([ "$(uci -q get v2ray.omrout.protocol)" = "vless" ] || [ "$(uci -q get v2ray.omrout.protocol)" = "vmess" ]) && commandline="$commandline -L ${port+1}" + # v2ray-rules6 $commandline + #} + + remote_servers="$(echo $OUTBOUND_SERVERS \ + | tr ' ' '\n' \ + | sort -u \ + | xargs -n 1 resolveip \ + | sort -u)" + + [ "$(uci -q get v2ray.main_transparent_proxy.redirect_udp)" = "1" ] && [ "$(uci -q get v2ray.omrout.protocol)" != "socks" ] && portudp="$port" + + local tmp="/tmp/v2rrules" + json_init + json_add_string o_remote_servers "$remote_servers" + json_add_int o_redir_tcp_port "$port" + json_add_int o_redir_udp_port "$portudp" + json_add_string o_ifnames "$ifnames" + json_add_string o_local_default "forward" + json_add_string o_src_bypass "$src_ips_bypass" + json_add_string o_src_forward "$src_ips_forward" + json_add_string o_src_checkdst "$src_ips_checkdst" + json_add_string o_src_default "checkdst" + json_add_string o_dst_bypass "$dst_ips_bypass" + json_add_string o_dst_forward "$dst_ips_forward" + json_add_string o_dst_bypass_file "$dst_ips_bypass_file" + json_add_string o_dst_forward_file "$dst_ips_forward_file" + json_add_string o_dst_default "forward" + json_add_string o_nft_tcp_extra "$nft_tcp_extra" + json_add_string o_nft_udp_extra "$nft_udp_extra" + json_dump -i >"$tmp.json" + + if utpl -S -F "$tmp.json" "$v2rrules_uc" >"$tmp.nft" \ + && ! cmp -s "$tmp.nft" "$v2rrules_nft"; then + echo "table inet chk {include \"$tmp.nft\";}" >"$tmp.nft.chk" + if nft -f "$tmp.nft.chk" -c; then + mv "$tmp.nft" "$v2rrules_nft" + fw4 restart + fi + rm -f "$tmp.nft.chk" + fi + rm -f "$tmp.json" + rm -f "$tmp.nft" + + [ -f /etc/init.d/omr-bypass ] && [ -z "$(pgrep -f omr-bypass)" ] && { + logger -t "v2ray" "Reload omr-bypass rules" + /etc/init.d/omr-bypass reload_rules + } +} + +init_rules_for_listfile() { + local direct_list_dns="$1" + local proxy_list_dns="$2" + + echo "# AUTO-GENERATED FILE. DO NOT MODIFY." >"$FILE_V2RAY_DNSMASQ_CACHE" + + # For direct list + local direct_content + direct_content="$(get_file_content "directlist")" + + if [ -n "$direct_content" ] ; then + echo "$direct_content" | \ + grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_DST_DIRECT_V4 & timeout 0/" | \ + ipset -! restore 2>/dev/null + + echo "$direct_content" | \ + grep -oE "([0-9a-fA-F]{0,4}:){1,7}([0-9a-fA-F]){0,4}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_DST_DIRECT_V6 & timeout 0/" | \ + ipset -! restore 2>/dev/null + + if [ -n "$direct_list_dns" ] ; then + echo "$direct_content" | \ + grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ + sed "s|.*|server=/&/$direct_list_dns\nipset=/&/$IPSET_DST_DIRECT_V4,$IPSET_DST_DIRECT_V6|" \ + >>"$FILE_V2RAY_DNSMASQ_CACHE" + else + echo "$direct_content" | \ + grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ + sed "s|.*|ipset=/&/$IPSET_DST_DIRECT_V4,$IPSET_DST_DIRECT_V6|" \ + >>"$FILE_V2RAY_DNSMASQ_CACHE" + fi + fi + + # For proxy list + local proxy_content + proxy_content="$(get_file_content "proxylist")" + + if [ -n "$proxy_content" ] ; then + echo "$proxy_content" | \ + grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_DST_PROXY_V4 & timeout 0/" | \ + ipset -! restore 2>/dev/null + + echo "$proxy_content" | \ + grep -oE "([0-9a-fA-F]{0,4}:){1,7}([0-9a-fA-F]){0,4}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_DST_PROXY_V6 & timeout 0/" | \ + ipset -! restore 2>/dev/null + + if [ -n "$proxy_list_dns" ] ; then + echo "$proxy_content" | \ + grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ + sed "s|.*|server=/&/$proxy_list_dns\nipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ + >>"$FILE_V2RAY_DNSMASQ_CACHE" + else + echo "$proxy_content" | \ + grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ + sed "s|.*|ipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ + >>"$FILE_V2RAY_DNSMASQ_CACHE" + fi + fi + + # For local devices outbound list + local src_content + src_content="$(get_file_content "srcdirectlist")" + + if [ -n "$src_content" ] ; then + echo "$src_content" | \ + grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_SRC_DIRECT_V4 & timeout 0/" | \ + ipset -! restore 2>/dev/null + fi + +} + +gracefully_restart_dnsmasq() { + if [ "x$DNSMASQ_RESTART_EXPECTED" = "x1" ] && [ -x "/etc/init.d/dnsmasq" ] ; then + _info "Restarting dnsmasq..." + /etc/init.d/dnsmasq restart >/dev/null 2>&1 + DNSMASQ_RESTART_EXPECTED=0 + fi +} + +add_dns_settings() { + local section="${1}_dns" + + if ! dns_section_validate "$section" ; then + _err "Invalid DNS config: $section, skip" + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + _info "DNS disabled: $section" + return 0 + fi + + json_add_object "dns" + + test -n "$tag" && \ + json_add_string "tag" "$tag" + test -n "$client_ip" && \ + json_add_string "clientIp" "$client_ip" + + if [ -n "$hosts" ] ; then + json_add_object "hosts" + + local h + for h in $hosts ; do + local domain="$(echo "$h" | cut -d'|' -f1)" + local ip="$(echo "$h" | cut -d'|' -f2)" + + if [ -n "$domain" ] && [ -n "$ip" ] ; then + json_add_string "$domain" "$ip" + fi + done + + json_close_object # hosts + fi + + if [ -n "$servers" ] ; then + json_add_array "servers" + + for ss in $servers ; do + if dns_server_section_validate "$ss" ; then + if [ -z "$address" ] ; then + continue + fi + + if [ -z "${port}${domains}${expect_ips}" ] ; then + json_add_string "" "$address" + else + json_add_object "" + json_add_string "address" "$address" + + if [ -n "$port" ] ; then + json_add_int "port" "$port" + else + json_add_int "port" "53" + fi + + if [ -n "$domains" ] ; then + json_add_array "domains" + + local d + for d in $domains ; do + json_add_string "" "$d" + done + + json_close_array # domains + fi + + if [ -n "$expect_ips" ] ; then + json_add_array "expectIPs" + + local e + for e in $expect_ips ; do + json_add_string "" "$e" + done + + json_close_array # expectIPs + fi + + json_close_object + fi + fi + done + + json_close_array # servers + fi + + json_close_object # dns +} + +add_routing_settings() { + local section="${1}_routing" + + if ! routing_section_validate "$section" ; then + _err "Invalid routing config: $section, skip" + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + _info "Routing disabled: $section" + return 0 + fi + + json_add_object "routing" + + test -n "$domain_strategy" && \ + json_add_string "domainStrategy" "$domain_strategy" + + if [ -n "$rules" ] ; then + json_add_array "rules" + + local rs + for rs in $rules ; do + if routing_rule_section_validate "$rs" ; then + json_add_object "" + + json_add_string "type" "$type" + + if [ -n "$domain" ] ; then + json_add_array "domain" + + local d + for d in $domain ; do + json_add_string "" "$d" + done + + json_close_array # domain + fi + + if [ -n "$ip" ] ; then + json_add_array "ip" + + local i + for i in $ip ; do + json_add_string "" "$i" + done + + json_close_array # ip + fi + + if [ -n "$port" ] ; then + json_add_string "port" "$(echo "$port" | tr -s ' ' ',')" + fi + + if [ -n "$network" ] ; then + json_add_string "network" "$(echo "$network" | tr -s ' ' ',')" + fi + + if [ -n "$source" ] ; then + json_add_array "source" + + local s + for s in $source ; do + json_add_string "" "$s" + done + + json_close_array # source + fi + + if [ -n "$user" ] ; then + json_add_array "user" + + local u + for u in $user ; do + json_add_string "" "$u" + done + + json_close_array # user + fi + + if [ -n "$inbound_tag" ] ; then + json_add_array "inboundTag" + + local it + for it in $inbound_tag ; do + json_add_string "" "$it" + done + + json_close_array # inboundTag + fi + + if [ -n "$protocol" ] ; then + json_add_array "protocol" + local p + for p in $protocol ; do + json_add_string "" "$p" + done + json_close_array # protocol + fi + + test -n "$attrs" && \ + json_add_string "attrs" "$attrs" + test -n "$outbound_tag" && \ + json_add_string "outboundTag" "$outbound_tag" + test -n "$balancer_tag" && \ + json_add_string "balancerTag" "$balancer_tag" + + json_close_object + fi + done + + json_close_array # rules + fi + + if [ -n "$balancers" ] ; then + json_add_array "balancers" + + local bs + for bs in $balancers ; do + if routing_balancer_section_validate "$bs" ; then + json_add_object "" + json_add_string "tag" "$tag" + + json_add_array "selector" + + local s + for s in $selector ; do + json_add_string "" "$s" + done + + json_close_array # selector + json_close_object + fi + done + + json_close_array # balancers + fi + + json_close_object +} + +add_policy_settings() { + local section="${1}_policy" + + if ! policy_section_validate "$section" ; then + _err "Invalid policy config: $section, skip" + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + _info "Policy disabled: $section" + return 0 + fi + + json_add_object "policy" + + if [ -n "$levels" ] ; then + json_add_object "levels" + + local l_s + for l_s in $levels ; do + if policy_level_section_validate "$l_s" ; then + json_add_object "$level" + json_add_int "handshake" "$handshake" + json_add_int "connIdle" "$conn_idle" + json_add_int "uplinkOnly" "$uplink_only" + json_add_int "downlinkOnly" "$downlink_only" + json_add_boolean "statsUserUplink" "$stats_user_uplink" + json_add_boolean "statsUserDownlink" "$stats_user_downlink" + test -n "$buffer_size" && \ + json_add_int "bufferSize" "$buffer_size" + json_close_object + fi + done + + json_close_object # levels + fi + + json_add_object "system" + json_add_boolean "statsInboundUplink" "$system_stats_inbound_uplink" + json_add_boolean "statsInboundDownlink" "$system_stats_inbound_downlink" + json_close_object # system + + json_close_object # policy +} + +add_reverse_settings() { + local section="${1}_reverse" + + if ! reverse_section_validate "$section" ; then + _err "Invalid reverse config: $section, skip" + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + _info "Reverse disabled: $section" + return 0 + fi + + json_add_object "reverse" + + if [ -n "$bridges" ] ; then + json_add_array "bridges" + + local b + for b in $bridges ; do + local tag="$(echo "$b" | cut -d'|' -f1)" + local domain="$(echo "$b" | cut -d'|' -f2)" + if [ -n "$tag" ] && [ -n "$domain" ] ; then + json_add_object "" + json_add_string "tag" "$tag" + json_add_string "domain" "$domain" + json_close_object + fi + done + + json_close_array # bridges + fi + + if [ -n "$portals" ] ; then + json_add_array "portals" + + local p + for p in $portals ; do + local tag="$(echo "$p" | cut -d'|' -f1)" + local domain="$(echo "$p" | cut -d'|' -f2)" + if [ -n "$tag" ] && [ -n "$domain" ] ; then + json_add_object "" + json_add_string "tag" "$tag" + json_add_string "domain" "$domain" + json_close_object + fi + done + + json_close_array # portals + fi + + json_close_object # reverse +} + +add_transport_settings() { + local json + json="$(get_file_content "transport")" + + if [ -z "$json" ] ; then + _err "Invalid transport config: $key" + return 1 + fi + + json_add_object "transport" + eval "$(get_commands_from_json "$json")" + json_close_object # transport +} + +add_inbound_setting() { + local section="$1" + + if ! inbound_section_validate "$section" ; then + _err "Invalid inbound section: $section" + return 1 + fi + + json_add_object "" + + test -n "$listen" && \ + json_add_string "listen" "$listen" + json_add_int "port" "$port" + json_add_string "protocol" "$protocol" + + case "${protocol:-x}" in + "dokodemo-door") + json_add_object "settings" + + if [ -n "$port" ] && [ "x$port" = "x$TRANSPARENT_PROXY_PORT" ] ; then + local settings_network="tcp" + + test -n "$TRANSPARENT_PROXY_ADDITION" && \ + settings_network="$settings_network,udp" + + json_add_boolean "followRedirect" "1" + json_add_string "network" "$settings_network" + else + test -n "$s_dokodemo_door_address" && \ + json_add_string "address" "$s_dokodemo_door_address" + + test -n "$s_dokodemo_door_port" && \ + json_add_int "port" "$s_dokodemo_door_port" + + test -n "$s_dokodemo_door_follow_redirect" && \ + json_add_boolean "followRedirect" "$s_dokodemo_door_follow_redirect" + + test -n "$s_dokodemo_door_network" && \ + json_add_string "network" "$(echo "$s_dokodemo_door_network" | tr -s ' ' ',')" + fi + + test -n "$s_dokodemo_door_timeout" && \ + json_add_int "timeout" "$s_dokodemo_door_timeout" + + test -n "$s_dokodemo_door_user_level" && \ + json_add_int "userLevel" "$s_dokodemo_door_user_level" + + json_close_object # settings + ;; + "http") + json_add_object "settings" + + if [ -n "$s_http_account_user" ] ; then + json_add_array "accounts" + + json_add_object "" + json_add_string "user" "$s_http_account_user" + json_add_string "pass" "$s_http_account_pass" + json_close_object + + json_close_array # accounts + fi + + json_add_boolean "allowTransparent" "$s_http_allow_transparent" + + test -n "$s_http_timeout" && \ + json_add_int "timeout" "$s_http_timeout" + test -n "$s_http_user_level" && \ + json_add_int "userLevel" "$s_http_user_level" + + json_close_object # settings + ;; + "mtproto") + json_add_object "settings" + + if [ -n "$s_mtproto_user_email" ] ; then + json_add_array "users" + json_add_object "" + + json_add_string "email" "$s_mtproto_user_email" + json_add_string "secret" "$s_mtproto_user_secret" + + test -n "$s_mtproto_user_level" && \ + json_add_int "level" "$s_mtproto_user_level" + + json_close_object + json_close_array # users + fi + + json_close_object # settings + ;; + "shadowsocks") + json_add_object "settings" + + json_add_string "method" "$s_shadowsocks_method" + json_add_string "password" "$s_shadowsocks_password" + + test -n "$s_shadowsocks_email" && \ + json_add_string "email" "$s_shadowsocks_email" + test -n "$s_shadowsocks_level" && \ + json_add_int "level" "$s_shadowsocks_level" + + json_add_boolean "ota" "$s_shadowsocks_ota" + json_add_string "network" "$(echo "$s_shadowsocks_network" | tr -s ' ' ',')" + + json_close_object # settings + ;; + "socks") + json_add_object "settings" + + json_add_string "auth" "$s_socks_auth" + + if [ -n "$s_socks_account_user" ] ; then + json_add_array "accounts" + json_add_object "" + json_add_string "user" "$s_socks_account_user" + json_add_string "pass" "$s_socks_account_pass" + json_close_object + json_close_array # accounts + fi + if [ -n "$s_socks_client_id" ] ; then + json_add_array "accounts" + json_add_object "" + json_add_string "user" "$s_socks_email" + json_add_string "pass" "$s_socks_client_id" + json_close_object + json_close_array # accounts + fi + + json_add_boolean "udp" "$s_socks_udp" + + test -n "$s_socks_ip" && \ + json_add_string "ip" "$s_socks_ip" + test -n "$s_socks_user_level" && \ + json_add_int "userLevel" "$s_socks_user_level" + + json_close_object # settings + ;; + "vmess") + json_add_object "settings" + + if [ -n "$s_vmess_client_id" ] ; then + json_add_array "clients" + json_add_object "" + + json_add_string "id" "$s_vmess_client_id" + + test -n "$s_vmess_client_alter_id" && \ + json_add_int "alterId" "$s_vmess_client_alter_id" + test -n "$s_vmess_client_email" && \ + json_add_string "email" "$s_vmess_client_email" + test -n "$s_vmess_client_user_level" && \ + json_add_int "level" "$s_vmess_client_user_level" + + json_close_object + json_close_array # clients + fi + + json_add_object "default" + + test -n "$s_vmess_default_alter_id" && \ + json_add_int "alterId" "$s_vmess_default_alter_id" + test -n "$s_vmess_default_user_level" && \ + json_add_int "level" "$s_vmess_default_user_level" + + json_close_object # default + + if [ -n "$s_vmess_detour_to" ] ; then + json_add_object "detour" + json_add_string "to" "$s_vmess_detour_to" + json_close_object # detour + fi + + json_add_boolean "disableInsecureEncryption" "$s_vmess_disable_insecure_encryption" + + json_close_object # settings + ;; + "vless") + json_add_object "settings" + + if [ -n "$s_vless_client_id" ] ; then + json_add_array "clients" + json_add_object "" + + json_add_string "id" "$s_vless_client_id" + + test -n "$s_vless_client_alter_id" && \ + json_add_int "alterId" "$s_vless_client_alter_id" + test -n "$s_vless_client_email" && \ + json_add_string "email" "$s_vless_client_email" + test -n "$s_vless_client_user_level" && \ + json_add_int "level" "$s_vless_client_user_level" + + json_close_object + json_close_array # clients + fi + + json_add_object "default" + + test -n "$s_vless_default_alter_id" && \ + json_add_int "alterId" "$s_vless_default_alter_id" + test -n "$s_vless_default_user_level" && \ + json_add_int "level" "$s_vless_default_user_level" + + json_close_object # default + + if [ -n "$s_vless_detour_to" ] ; then + json_add_object "detour" + json_add_string "to" "$s_vless_detour_to" + json_close_object # detour + fi + + json_add_boolean "disableInsecureEncryption" "$s_vless_disable_insecure_encryption" + + json_close_object # settings + ;; + "trojan") + json_add_object "settings" + + if [ -n "$s_trojan_client_id" ] ; then + json_add_array "clients" + json_add_object "" + + json_add_string "password" "$s_trojan_client_id" + + test -n "$s_trojan_client_alter_id" && \ + json_add_int "alterId" "$s_trojan_client_alter_id" + test -n "$s_trojan_client_email" && \ + json_add_string "email" "$s_trojan_client_email" + test -n "$s_trojan_client_user_level" && \ + json_add_int "level" "$s_trojan_client_user_level" + + json_close_object + json_close_array # clients + fi + + json_add_object "default" + + test -n "$s_trojan_default_alter_id" && \ + json_add_int "alterId" "$s_trojan_default_alter_id" + test -n "$s_trojan_default_user_level" && \ + json_add_int "level" "$s_trojan_default_user_level" + + json_close_object # default + + if [ -n "$s_trojan_detour_to" ] ; then + json_add_object "detour" + json_add_string "to" "$s_trojan_detour_to" + json_close_object # detour + fi + + json_add_boolean "disableInsecureEncryption" "$s_trojan_disable_insecure_encryption" + + json_close_object # settings + ;; + esac + + json_add_object "streamSettings" + + test -n "$ss_network" && \ + json_add_string "network" "$ss_network" + + test -n "$ss_security" && \ + json_add_string "security" "$ss_security" + + if [ "x$ss_security" = "xtls" ] ; then + json_add_object "tlsSettings" + + test -n "$ss_tls_server_name" && \ + json_add_string "serverName" "$ss_tls_server_name" + + if [ -n "$ss_tls_alpn" ] ; then + json_add_array "alpn" + json_add_string "" "$ss_tls_alpn" + json_close_array # alpn + fi + + json_add_boolean "allowInsecure" "$ss_tls_allow_insecure" + json_add_boolean "allowInsecureCiphers" "$ss_tls_allow_insecure_ciphers" + json_add_boolean "disableSystemRoot" "$ss_tls_disable_system_root" + + json_add_array "certificates" + if [ -n "$ss_tls_cert_file" ] ; then + json_add_object "" + + json_add_string "certificateFile" "$ss_tls_cert_file" + json_add_string "keyFile" "$ss_tls_key_file" + test -n "$ss_tls_cert_usage" && \ + json_add_string "usage" "$ss_tls_cert_usage" + + json_close_object + fi + json_close_array # certificates + + json_close_object # tlsSettings + fi + + case "${ss_network:-x}" in + "tcp") + json_add_object "tcpSettings" + + if [ -n "$ss_tcp_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_tcp_header_type" + + if [ "$ss_tcp_header_type" = "http" ] ; then + json_add_object "request" + test -n "$ss_tcp_header_request_version" && \ + json_add_string "version" "$ss_tcp_header_request_version" + json_add_string "method" "$ss_tcp_header_request_method" + + if [ -n "$ss_tcp_header_request_path" ] ; then + json_add_array "path" + json_add_string "" "$ss_tcp_header_request_path" + json_close_array # path + fi + + if [ -n "$ss_tcp_header_request_headers" ] ; then + json_add_object "headers" + + handle_request_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_array "$name" + json_add_string "" "$value" + json_close_array + fi + } + config_list_foreach "$section" "ss_tcp_header_request_headers" handle_request_header + + json_close_object # headers + fi + + json_close_object # request + + json_add_object "response" + + test -n "$ss_tcp_header_response_version" && \ + json_add_string "version" "$ss_tcp_header_response_version" + test -n "$ss_tcp_header_response_status" && \ + json_add_string "status" "$ss_tcp_header_response_status" + test -n "$ss_tcp_header_response_reason" && \ + json_add_string "reason" "$ss_tcp_header_response_reason" + + if [ -n "$ss_tcp_header_response_headers" ] ; then + json_add_object "headers" + + handle_response_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_array "$name" + json_add_string "" "$value" + json_close_array + fi + } + config_list_foreach "$section" "ss_tcp_header_response_headers" handle_response_header + + json_close_object # headers + fi + + json_close_object # response + fi + json_close_object # header + fi + + json_close_object # tcpSettings + ;; + "kcp") + json_add_object "kcpSettings" + + test -n "$ss_kcp_mtu" && \ + json_add_int "mtu" "$ss_kcp_mtu" + test -n "$ss_kcp_tti" && \ + json_add_int "tti" "$ss_kcp_tti" + test -n "$ss_kcp_uplink_capacity" && \ + json_add_int "uplinkCapacity" "$ss_kcp_uplink_capacity" + test -n "$ss_kcp_downlink_capacity" && \ + json_add_int "downlinkCapacity" "$ss_kcp_downlink_capacity" + json_add_boolean "congestion" "$ss_kcp_congestion" + test -n "$ss_kcp_read_buffer_size" && \ + json_add_int "readBufferSize" "$ss_kcp_read_buffer_size" + test -n "$ss_kcp_write_buffer_size" && \ + json_add_int "writeBufferSize" "$ss_kcp_write_buffer_size" + + if [ -n "$ss_kcp_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_kcp_header_type" + json_close_object # header + fi + + json_close_object # kcpSettings + ;; + "ws") + json_add_object "wsSettings" + + test -n "$ss_websocket_path" && \ + json_add_string "path" "$ss_websocket_path" + + if [ -n "$ss_websocket_headers" ] ; then + json_add_object "headers" + + handle_websocket_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_string "$name" "$value" + fi + } + config_list_foreach "$section" "ss_websocket_headers" handle_websocket_header + + json_close_object # headers + fi + + json_close_object # wsSettings + ;; + "http") + json_add_object "httpSettings" + + if [ -n "$ss_http_host" ] ; then + json_add_array "host" + + local h + for h in $ss_http_host ; do + json_add_string "" "$h" + done + + json_close_array # host + fi + + test -n "$ss_http_path" && \ + json_add_string "path" "$ss_http_path" + + json_close_object # httpSettings + ;; + "domainsocket") + json_add_object "dsSettings" + + test -n "$ss_domainsocket_path" && \ + json_add_string "path" "$ss_domainsocket_path" + + json_close_object # dsSettings + ;; + "quic") + json_add_object "quicSettings" + + test -n "$ss_quic_security" && \ + json_add_string "security" "$ss_quic_security" + test -n "$ss_quic_key" && \ + json_add_string "key" "$ss_quic_key" + + if [ -n "$ss_quic_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_quic_header_type" + json_close_object # header + fi + + json_close_object # quicSettings + ;; + esac + + json_add_object "sockopt" + +# if [ -n "$port" ] && [ "x$port" = "x$TRANSPARENT_PROXY_PORT" ] ; then +# if [ "x$TRANSPARENT_PROXY_USE_TPROXY" = "x1" ] ; then +# json_add_string "tproxy" "tproxy" +# else +# json_add_string "tproxy" "redirect" +# fi +# else + test -n "$ss_sockopt_tcp_fast_open" && \ + json_add_boolean "tcpFastOpen" "$ss_sockopt_tcp_fast_open" + if version_over_5_4; then + test -n "$ss_sockopt_mptcp" && \ + json_add_boolean "mptcp" "$ss_sockopt_mptcp" + fi + test -n "$ss_sockopt_tproxy" && \ + json_add_string "tproxy" "$ss_sockopt_tproxy" +# fi + + json_close_object # sockopt + + json_close_object # streamSettings + + test -n "$tag" && \ + json_add_string "tag" "$tag" + + json_add_object "sniffing" + + json_add_boolean "enabled" "$sniffing_enabled" + + if [ -n "$sniffing_dest_override" ] ; then + json_add_array "destOverride" + local d + for d in $sniffing_dest_override ; do + json_add_string "" "$d" + done + json_close_array # destOverride + fi + + json_close_object # sniffing + + if [ -n "$allocate_strategy" ] ; then + json_add_object "allocate" + json_add_string "strategy" "$allocate_strategy" + test -n "$allocate_refresh" && \ + json_add_int "refresh" "$allocate_refresh" + test -n "$allocate_concurrency" && \ + json_add_int "concurrency" "$allocate_concurrency" + json_close_object # allocate + fi + + json_close_object +} + +add_outbound_setting() { + local section="$1" + + if ! outbound_section_validate "$section" ; then + _err "Invalid outbound section: $section" + return 1 + fi + + json_add_object "" + + test -n "$send_through" && \ + json_add_string "sendThrough" "$send_through" + json_add_string "protocol" "$protocol" + + case "${protocol:-x}" in + "blackhole") + json_add_object "settings" + + if [ -n "$s_blackhole_reponse_type" ] ; then + json_add_object "response" + json_add_string "type" "$s_blackhole_reponse_type" + json_close_object # response + fi + + json_close_object # settings + ;; + "dns") + json_add_object "settings" + + test -n "$s_dns_network" && \ + json_add_string "network" "$s_dns_network" + + if [ -n "$s_dns_address" ] ; then + json_add_string "address" "$s_dns_address" + append_server_address "$s_dns_address" + fi + + test -n "$s_dns_port" && \ + json_add_int "port" "$s_dns_port" + + json_close_object # settings + ;; + "freedom") + json_add_object "settings" + + test -n "$s_freedom_domain_strategy" && \ + json_add_string "domainStrategy" "$s_freedom_domain_strategy" + test -n "$s_freedom_redirect" && \ + json_add_string "redirect" "$s_freedom_redirect" + test -n "$s_freedom_user_level" && \ + json_add_int "userLevel" "$s_freedom_user_level" + + json_close_object # settings + ;; + "http") + json_add_object "settings" + json_add_array "servers" + + json_add_object "" + + json_add_string "address" "$s_http_server_address" + append_server_address "$s_http_server_address" + + test -n "$s_http_server_port" && \ + json_add_int "port" "$s_http_server_port" + + if [ -n "$s_http_account_user" ] ; then + json_add_array "users" + json_add_object "" + + json_add_string "user" "$s_http_account_user" + json_add_string "pass" "$s_http_account_pass" + + json_close_object + json_close_array # users + fi + json_close_object + + json_close_array # servers + json_close_object # settings + ;; + "mtproto") + json_add_object "settings" + json_close_object + ;; + "shadowsocks") + json_add_object "settings" + json_add_array "servers" + + json_add_object "" + test -n "$s_shadowsocks_email" && \ + json_add_string "email" "$s_shadowsocks_email" + json_add_string "address" "$s_shadowsocks_address" + append_server_address "$s_shadowsocks_address" + + json_add_int "port" "$s_shadowsocks_port" + json_add_string "method" "$s_shadowsocks_method" + json_add_string "password" "$s_shadowsocks_password" + + test -n "$s_shadowsocks_level" && \ + json_add_int "level" "$s_shadowsocks_level" + json_add_boolean "ota" "$s_shadowsocks_ota" + json_close_object + + json_close_array # servers + json_close_object # settings + ;; + "vmess") + json_add_object "settings" + + json_add_array "vnext" + json_add_object "" + + json_add_string "address" "$s_vmess_address" + append_server_address "$s_vmess_address" + + json_add_int "port" "$s_vmess_port" + + json_add_array "users" + json_add_object "" + json_add_string "id" "$s_vmess_user_id" + json_add_int "alterId" "$s_vmess_user_alter_id" + test -n "$s_vmess_user_security" && \ + json_add_string "security" "$s_vmess_user_security" + test -n "$s_vmess_user_level" && \ + json_add_int "level" "$s_vmess_user_level" + json_close_object + json_close_array # users + + json_close_object + + json_close_array # vnext + json_close_object # settings + ;; + "vless") + json_add_object "settings" + + json_add_array "vnext" + json_add_object "" + + json_add_string "address" "$s_vless_address" + append_server_address "$s_vless_address" + + json_add_int "port" "$s_vless_port" + + json_add_array "users" + json_add_object "" + json_add_string "id" "$s_vless_user_id" + json_add_int "alterId" "$s_vless_user_alter_id" + test -n "$s_vless_user_security" && \ + json_add_string "security" "$s_vless_user_security" + test -n "$s_vless_user_encryption" && \ + json_add_string "encryption" "$s_vless_user_encryption" + test -n "$s_vless_user_level" && \ + json_add_int "level" "$s_vless_user_level" + json_close_object + json_close_array # users + + json_close_object + + json_close_array # vnext + json_close_object # settings + ;; + "trojan") + json_add_object "settings" + + json_add_array "servers" + json_add_object "" + + json_add_string "address" "$s_trojan_address" + append_server_address "$s_trojan_address" + + json_add_int "port" "$s_trojan_port" + + json_add_string "password" "$s_trojan_user_id" + + json_close_object + + json_close_array # trojan + json_close_object # settings + ;; + "socks") + json_add_object "settings" + + json_add_array "servers" + json_add_object "" + + json_add_string "address" "$s_socks_address" + append_server_address "$s_socks_address" + + json_add_int "port" "$s_socks_port" + + json_add_array "users" + json_add_object "" + json_add_string "pass" "$s_socks_user_id" + json_add_string "user" "$s_socks_email" + json_close_object + json_close_array # users + + json_close_object + + json_close_array # socks + json_close_object # settings + ;; + esac + + json_add_object "streamSettings" + test -n "$ss_network" && \ + json_add_string "network" "$ss_network" + + test -n "$ss_security" && \ + json_add_string "security" "$ss_security" + + if [ "x$ss_security" = "xtls" ] ; then + json_add_object "tlsSettings" + + test -n "$ss_tls_server_name" && \ + json_add_string "serverName" "$ss_tls_server_name" + + if [ -n "$ss_tls_alpn" ] ; then + json_add_array "alpn" + json_add_string "" "$ss_tls_alpn" + json_close_array + fi + + json_add_boolean "allowInsecure" "$ss_tls_allow_insecure" + json_add_boolean "allowInsecureCiphers" "$ss_tls_allow_insecure_ciphers" + json_add_boolean "disableSystemRoot" "$ss_tls_disable_system_root" + + json_add_array "certificates" + if [ -n "$ss_tls_cert_file" ] ; then + json_add_object "" + json_add_string "certificateFile" "$ss_tls_cert_file" + json_add_string "keyFile" "$ss_tls_key_file" + test -n "$ss_tls_cert_usage" && \ + json_add_string "usage" "$ss_tls_cert_usage" + json_close_object + fi + json_close_array # certificates + + json_close_object # tlsSettings + fi + + case "${ss_network:-x}" in + "tcp") + json_add_object "tcpSettings" + + if [ -n "$ss_tcp_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_tcp_header_type" + + if [ "$ss_tcp_header_type" = "http" ] ; then + json_add_object "request" + test -n "$ss_tcp_header_request_version" && \ + json_add_string "version" "$ss_tcp_header_request_version" + json_add_string "method" "$ss_tcp_header_request_method" + + if [ -n "$ss_tcp_header_request_path" ] ; then + json_add_array "path" + json_add_string "" "$ss_tcp_header_request_path" + json_close_array + fi + + if [ -n "$ss_tcp_header_request_headers" ] ; then + json_add_object "headers" + + handle_request_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_array "$name" + json_add_string "" "$value" + json_close_array + fi + } + config_list_foreach "$section" "ss_tcp_header_request_headers" handle_request_header + + json_close_object # headers + fi + + json_close_object # request + + json_add_object "response" + test -n "$ss_tcp_header_response_version" && \ + json_add_string "version" "$ss_tcp_header_response_version" + test -n "$ss_tcp_header_response_status" && \ + json_add_string "status" "$ss_tcp_header_response_status" + test -n "$ss_tcp_header_response_reason" && \ + json_add_string "reason" "$ss_tcp_header_response_reason" + + if [ -n "$ss_tcp_header_response_headers" ] ; then + json_add_object "headers" + + handle_response_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_array "$name" + json_add_string "" "$value" + json_close_array + fi + } + config_list_foreach "$section" "ss_tcp_header_response_headers" handle_response_header + + json_close_object # headers + fi + + json_close_object # response + fi + + json_close_object # header + fi + + json_close_object # tcpSettings + ;; + "kcp") + json_add_object "kcpSettings" + + test -n "$ss_kcp_mtu" && \ + json_add_int "mtu" "$ss_kcp_mtu" + test -n "$ss_kcp_tti" && \ + json_add_int "tti" "$ss_kcp_tti" + test -n "$ss_kcp_uplink_capacity" && \ + json_add_int "uplinkCapacity" "$ss_kcp_uplink_capacity" + test -n "$ss_kcp_downlink_capacity" && \ + json_add_int "downlinkCapacity" "$ss_kcp_downlink_capacity" + + json_add_boolean "congestion" "$ss_kcp_congestion" + + test -n "$ss_kcp_read_buffer_size" && \ + json_add_int "readBufferSize" "$ss_kcp_read_buffer_size" + test -n "$ss_kcp_write_buffer_size" && \ + json_add_int "writeBufferSize" "$ss_kcp_write_buffer_size" + + if [ -n "$ss_kcp_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_kcp_header_type" + json_close_object + fi + + json_close_object # kcpSettings + ;; + "ws") + json_add_object "wsSettings" + + test -n "$ss_websocket_path" && \ + json_add_string "path" "$ss_websocket_path" + + if [ -n "$ss_websocket_headers" ] ; then + json_add_object "headers" + + handle_websocket_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_string "$name" "$value" + fi + } + config_list_foreach "$section" "ss_websocket_headers" handle_websocket_header + + json_close_object # headers + fi + + json_close_object # wsSettings + ;; + "http") + json_add_object "httpSettings" + + if [ -n "$ss_http_host" ] ; then + json_add_array "host" + + local h + for h in $ss_http_host ; do + json_add_string "" "$h" + done + + json_close_array # host + fi + + test -n "$ss_http_path" && \ + json_add_string "path" "$ss_http_path" + + json_close_object # httpSettings + ;; + "domainsocket") + json_add_object "dsSettings" + + test -n "$ss_domainsocket_path" && \ + json_add_string "path" "$ss_domainsocket_path" + + json_close_object # dsSettings + ;; + "quic") + json_add_object "quicSettings" + + test -n "$ss_quic_security" && \ + json_add_string "security" "$ss_quic_security" + test -n "$ss_quic_key" && \ + json_add_string "key" "$ss_quic_key" + + if [ -n "$ss_quic_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_quic_header_type" + json_close_object # header + fi + + json_close_object # quicSettings + ;; + esac + + json_add_object "sockopt" + + if [ -n "$TRANSPARENT_PROXY_PORT" ] ; then + json_add_int "mark" "255" + else + test -n "$ss_sockopt_mark" && \ + json_add_int "mark" "$ss_sockopt_mark" + fi + + test -n "$ss_sockopt_tcp_fast_open" && \ + json_add_boolean "tcpFastOpen" "$ss_sockopt_tcp_fast_open" + if version_over_5_4; then + test -n "$ss_sockopt_mptcp" && \ + json_add_boolean "mptcp" "$ss_sockopt_mptcp" + fi + + json_close_object # sockopt + + json_close_object # streamSettings + + test -n "$tag" && \ + json_add_string "tag" "$tag" + + if [ -n "$proxy_settings_tag" ] ; then + json_add_object "proxySettings" + json_add_string "tag" "$proxy_settings_tag" + json_close_object # proxySettings + fi + + if [ "x$mux_enabled" = "x1" ] ; then + json_add_object "mux" + json_add_boolean "enabled" "1" + json_add_int "concurrency" "$mux_concurrency" + json_close_object # mux + fi + + json_close_object +} + +init_transparent_proxy() { + local tp_cfg="main_transparent_proxy" + local redirect_port use_tproxy redirect_udp redirect_dns + + config_get redirect_port "$tp_cfg" "redirect_port" + config_get_bool use_tproxy "$tp_cfg" "use_tproxy" "0" + config_get_bool redirect_udp "$tp_cfg" "redirect_udp" "0" + config_get_bool redirect_dns "$tp_cfg" "redirect_dns" "0" + + if [ -n "$redirect_port" ] && \ + ! validate_data "port" "$redirect_port" 2>/dev/null ; then + _err "Transparent proxy redirect port is invalid: $redirect_port" + return 1 + fi + + TRANSPARENT_PROXY_PORT="$redirect_port" + TRANSPARENT_PROXY_USE_TPROXY="$use_tproxy" + + if [ "x$redirect_udp" = "x1" ] && [ "$(uci -q get v2ray.omrout.protocol)" != "socks" ]; then + TRANSPARENT_PROXY_ADDITION="udp" + elif [ "x$redirect_dns" = "x1" ] ; then + TRANSPARENT_PROXY_ADDITION="dns" + else + TRANSPARENT_PROXY_ADDITION= + fi +} + +setup_transparent_proxy() { + if [ -z "$TRANSPARENT_PROXY_PORT" ] ; then + #_info "Transparent proxy disabled." + return 0 + fi + + if [ "x$TRANSPARENT_PROXY_EXPECTED" != "x1" ] ; then + #_info "No v2ray instance enabled, skip transparent proxy." + return 0 + fi + + _info "Setting transparent proxy on port: $TRANSPARENT_PROXY_PORT" + + local tp_cfg="main_transparent_proxy" + local lan_ifaces only_privileged_ports proxy_mode direct_list_dns proxy_list_dns + + config_get lan_ifaces "$tp_cfg" "lan_ifaces" + config_get_bool only_privileged_ports "$tp_cfg" "only_privileged_ports" "0" + config_get proxy_mode "$tp_cfg" "proxy_mode" + config_get direct_list_dns "$tp_cfg" "direct_list_dns" + config_get proxy_list_dns "$tp_cfg" "proxy_list_dns" + + _info "Transparent proxy mode: $proxy_mode" + + #init_rules_for_listfile "$direct_list_dns" "$proxy_list_dns" + + local ext_args + case "${proxy_mode:-default}" in + "cn_direct") + local chnroute="$(get_file_content "chnroute")" + local chnroute6="$(get_file_content "chnroute6")" + + if [ -n "$chnroute" ] ; then + ipset -! restore <<-EOF 2>/dev/null + $(echo "$chnroute" | sed "s/.*/add $IPSET_DST_DIRECT_V4 & timeout 0/") + EOF + fi + + if [ -n "$chnroute6" ] ; then + ipset -! restore <<-EOF 2>/dev/null + $(echo "$chnroute6" | sed "s/.*/add $IPSET_DST_DIRECT_V6 & timeout 0/") + EOF + fi + + ext_args= + ;; + "cn_proxy") + local chnroute="$(get_file_content "chnroute")" + local chnroute6="$(get_file_content "chnroute6")" + + if [ -n "$chnroute" ] ; then + ipset -! restore <<-EOF 2>/dev/null + $(echo "$chnroute" | sed "s/.*/add $IPSET_DST_PROXY_V4 & timeout 0/") + EOF + fi + + if [ -n "$chnroute6" ] ; then + ipset -! restore <<-EOF 2>/dev/null + $(echo "$chnroute6" | sed "s/.*/add $IPSET_DST_PROXY_V6 & timeout 0/") + EOF + fi + + ext_args="-m set --match-set $IPSET_DST_PROXY_V4 dst" + ;; + "gfwlist_proxy") + local gfwlist="$(get_file_content "gfwlist")" + + if [ -n "$gfwlist" ] ; then + if [ -n "$proxy_list_dns" ] ; then + echo "$gfwlist" | \ + sed "s|.*|server=/&/$proxy_list_dns\nipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ + >> "$FILE_V2RAY_DNSMASQ_CACHE" + else + echo "$gfwlist" | \ + sed "s|.*|ipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ + >> "$FILE_V2RAY_DNSMASQ_CACHE" + fi + fi + + ext_args="-m set --match-set $IPSET_DST_PROXY_V4 dst" + ;; + *) + ext_args= + ;; + esac + + if [ "x$only_privileged_ports" = "x1" ] ; then + ext_args="--dport 0:1023 $ext_args" + fi + + if [ -f $FILE_V2RAY_DNSMASQ_CACHE ] && [ -n "$(cat "$FILE_V2RAY_DNSMASQ_CACHE" | grep -v "^$" | grep -v "^#")" ] ; then + local dir="$(dirname "$FILE_V2RAY_DNSMASQ")" + test -d "$dir" || mkdir -p "$dir" + cat "$FILE_V2RAY_DNSMASQ_CACHE" >"$FILE_V2RAY_DNSMASQ" 2>/dev/null + DNSMASQ_RESTART_EXPECTED=1 + fi + + rm -f "$FILE_V2RAY_DNSMASQ_CACHE" + + local lan_devices lan_ipaddrs + + if [ -n "$lan_ifaces" ] ; then + . /lib/functions/network.sh + + local lan + for lan in $lan_ifaces ; do + local device ipaddrs ipaddr + network_get_device device "$lan" + network_get_ipaddrs ipaddrs "$lan" + + if [ -n "$device" ] ; then + if [ -n "$lan_devices" ] ; then + lan_devices="$lan_devices $device" + else + lan_devices="$device" + fi + fi + + if [ -n "$ipaddrs" ] ; then + for ipaddr in $ipaddrs ; do + if [ -n "$lan_ipaddrs" ] ; then + lan_ipaddrs="$ipaddr" + else + lan_ipaddrs="$lan_ipaddrs $ipaddr" + fi + done + fi + done + fi + + logger -t "v2ray" "add rules" + add_v2ray_redirect_rules "$ext_args" "$lan_devices" "$lan_ipaddrs" +} + +clear_transparent_proxy() { + if [ -f "${v2rrules_nft}" ] || [ -f "${v2rrules_nft}.down" ]; then + rm -f "$v2rrules_nft" + rm -f "${v2rrules_nft}.down" + fw4 restart + fi + + v2ray-rules -f + + if [ -s "$FILE_V2RAY_DNSMASQ" ] ; then + rm -f "$FILE_V2RAY_DNSMASQ" + DNSMASQ_RESTART_EXPECTED=1 + fi +} + +start_instance() { + local section="$1" + + if ! v2ray_section_validate "$section" ; then + _err "Invalid config." + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + #_info "Service disabled: $section" + return 0 + fi + + if [ -z "$v2ray_file" ] || [ ! -s "$v2ray_file" ] ; then + _err "Invalid V2Ray file." + return 1 + fi + + test -x "$v2ray_file" || chmod 755 "$v2ray_file" + + local temp_config + + if [ -n "$config_file" ] ; then + if [ ! -s "$config_file" ] ; then + _err "Config file not found: $config_file" + return 1 + fi + + if ! ( eval "$v2ray_file test -config \"$config_file\" >/dev/null 2>&1" ) ; then + _err "Validate config file failed: $config_file" + return 1 + fi + + local file_content="$(cat "$config_file")" + local config_commands="$(get_commands_from_json "$file_content")" + + local addr + + for addr in $(echo "$config_commands" | sed -n "s/^json.*'address'[[:space:]]'\([^']*\)'.*/\1/p") ; do + append_server_address "$addr" + done + + temp_config="$config_file" + else + test -d "$CONFIG_FOLDER" || mkdir -p "$CONFIG_FOLDER" + + temp_config="$CONFIG_FOLDER/v2ray.${section}.json" + + local old_ns + json_set_namespace "$section" old_ns + json_init + + json_add_object "log" + + test -n "$access_log" && \ + json_add_string "access" "$access_log" + + if [ -n "$loglevel" ] && [ "$loglevel" != "none" ] ; then + json_add_string "loglevel" "$loglevel" + json_add_string "error" "$error_log" + fi + + json_close_object # log + + if [ "x$stats_enabled" = "x1" ] ; then + json_add_object "stats" + json_close_object # stats + fi + + add_dns_settings "$section" + add_routing_settings "$section" + add_policy_settings "$section" + add_reverse_settings "$section" + + if [ "x$transport_enabled" = "x1" ] ; then + add_transport_settings + fi + + if [ -n "$inbounds" ] ; then + json_add_array "inbounds" + + local is + for is in $inbounds ; do + add_inbound_setting "$is" + done + + json_close_array # inbounds + fi + + if [ -n "$outbounds" ] ; then + json_add_array "outbounds" + + local os + for os in $outbounds ; do + add_outbound_setting "$os" + done + + json_close_array # outbounds + fi + + json_dump -i >"$temp_config" + + json_cleanup + json_set_namespace "$old_ns" + + if [ ! -s "$temp_config" ] ; then + _err "Error when create config file: $temp_config" + return 1 + fi + fi + + PROG="$NAME.$section" + + TRANSPARENT_PROXY_EXPECTED=1 + procd_open_instance "$PROG" + procd_set_param command "$v2ray_file" + procd_append_param command run + procd_append_param command -config "$temp_config" + procd_set_param respawn + + if [ -n "$asset_location" ] && [ -d "$asset_location" ] ; then + procd_set_param env V2RAY_LOCATION_ASSET="$asset_location" + fi + + # cat /proc/PID/limits to see if limits works + procd_set_param limits nofile="102400 102400" + procd_append_param limits core="0 0" + + if [ "$mem_percentage" -gt "0" ] ; then + local mem_total="$(awk '/MemTotal/ {print $2}' /proc/meminfo)" + if [ -n "$mem_total" ] ; then + local use_mem="$(expr $mem_total \* $mem_percentage \* 10)" + procd_append_param limits as="$use_mem $use_mem" + fi + fi + + procd_set_param file "$temp_config" + procd_set_param stderr 1 # forward stderr of the command to logd + procd_set_param stdout 1 + procd_set_param pidfile "/var/run/${NAME}.${section}.pid" + procd_close_instance +} + +rules_exist() { + #[ -n "$(iptables -w -t nat -L -n 2>/dev/null | grep v2r_)" ] && return 0 + if [ -f "$v2rrules_nft" ]; then + return 0 + else + return 1 + fi +} + +rules_up() { + if [ -f "${v2rrules_nft}.down" ]; then + mv -f "${v2rrules_nft}.down" "$v2rrules_nft" + fw4 restart + fi +} + +rules_down() { + if [ -f "${v2rrules_nft}" ]; then + mv -f "$v2rrules_nft" "${v2rrules_nft}.down" + fw4 restart + fi +} + +start_service() { + clear_transparent_proxy + + config_load "$NAME" + + if ! init_transparent_proxy ; then + gracefully_restart_dnsmasq + return 1 + fi + + config_foreach start_instance "v2ray" + + setup_transparent_proxy + gracefully_restart_dnsmasq + rules_up + + unset OUTBOUND_SERVERS_V4 \ + OUTBOUND_SERVERS_V6 \ + TRANSPARENT_PROXY_EXPECTED \ + TRANSPARENT_PROXY_PORT \ + TRANSPARENT_PROXY_ADDITION \ + DNSMASQ_RESTART_EXPECTED +} + +stop_service() { + if [ "x$action" = "xrestart" ] ; then + # skip when restarting, start_service will do this + return 0 + fi + + clear_transparent_proxy + rules_down + gracefully_restart_dnsmasq + test -d "$CONFIG_FOLDER" && rm -rf "$CONFIG_FOLDER" +} + +service_triggers() { + procd_add_reload_trigger "$NAME" +} diff --git a/v2ray-core/files/usr/share/v2ray-rules/chain.uc b/v2ray-core/files/usr/share/v2ray-rules/chain.uc new file mode 100644 index 000000000..c0855c674 --- /dev/null +++ b/v2ray-core/files/usr/share/v2ray-rules/chain.uc @@ -0,0 +1,126 @@ +{% +function get_local_verdict() { + let v = o_local_default; + if (v == "checkdst") { + return "goto v2r_rules_dst_" + proto; + } else if (v == "forward") { + return "goto v2r_rules_forward_" + proto; + } else { + return null; + } +} + +function get_src_default_verdict() { + let v = o_src_default; + if (v == "checkdst") { + return "goto v2r_rules_dst_" + proto; + } else if (v == "forward") { + return "goto v2r_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_dst_default_verdict() { + let v = o_dst_default; + if (v == "forward") { + return "goto v2r_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_ifnames() { + let res = []; + for (let ifname in split(o_ifnames, /[ \t\n]/)) { + ifname = trim(ifname); + if (ifname) push(res, ifname); + } + return res; +} + +let type, hook, priority, redir_port; +if (proto == "tcp") { + type = "nat"; + hook = "prerouting"; + priority = -1; + redir_port = o_redir_tcp_port; +} else if (proto == "udp") { + type = "filter"; + hook = "prerouting"; + priority = "mangle"; + redir_port = o_redir_udp_port; + if (system(" + set -o errexit + iprr() { + while ip $1 rule del fwmark 1 lookup 100 2>/dev/null; do true; done + ip $1 rule add fwmark 1 lookup 100 + ip $1 route flush table 100 2>/dev/null || true + ip $1 route add local default dev lo table 100 + } + iprr -4 + iprr -6 + ") != 0) { + return ; + } +} else { + return; +} + +%} +{% if (redir_port): %} + +chain v2r_rules_pre_{{ proto }} { + type {{ type }} hook {{ hook }} priority {{ priority }}; + meta l4proto {{ proto }}{%- let ifnames=get_ifnames(); if (length(ifnames)): %} iifname { {{join(", ", ifnames)}} }{% endif %} goto v2r_rules_pre_src_{{ proto }}; +} + +chain v2r_rules_pre_src_{{ proto }} { + ip daddr @v2r_rules_dst_bypass_ accept; + ip6 daddr @v2r_rules6_dst_bypass_ accept; + goto v2r_rules_src_{{ proto }}; +} + +chain v2r_rules_src_{{ proto }} { + ip saddr @v2r_rules_src_bypass accept; + ip saddr @v2r_rules_src_forward goto v2r_rules_forward_{{ proto }}; + ip saddr @v2r_rules_src_checkdst goto v2r_rules_dst_{{ proto }}; + ip6 saddr @v2r_rules6_src_bypass accept; + ip6 saddr @v2r_rules6_src_forward goto v2r_rules_forward_{{ proto }}; + ip6 saddr @v2r_rules6_src_checkdst goto v2r_rules_dst_{{ proto }}; + {{ get_src_default_verdict() }}; +} + +chain v2r_rules_dst_{{ proto }} { + ip daddr @v2r_rules_dst_bypass accept; + ip daddr @v2r_rules_remote_servers accept; + ip daddr @v2r_rules_dst_forward goto v2r_rules_forward_{{ proto }}; + ip6 daddr @v2r_rules6_dst_bypass accept; + ip6 daddr @v2r_rules6_remote_servers accept; + ip6 daddr @v2r_rules6_dst_forward goto v2r_rules_forward_{{ proto }}; + {{ get_dst_default_verdict() }}; +} + +{% if (proto == "tcp"): %} +chain v2r_rules_forward_{{ proto }} { + meta l4proto tcp {{ o_nft_tcp_extra }} redirect to :{{ redir_port }}; +} +{% let local_verdict = get_local_verdict(); if (local_verdict): %} +chain v2r_rules_local_out { + type {{ type }} hook output priority -1; + meta l4proto != tcp accept; + ip daddr @v2r_rules_remote_servers accept; + ip daddr @v2r_rules_dst_bypass_ accept; + ip daddr @v2r_rules_dst_bypass accept; + ip6 daddr @v2r_rules6_remote_servers accept; + ip6 daddr @v2r_rules6_dst_bypass_ accept; + ip6 daddr @v2r_rules6_dst_bypass accept; + {{ local_verdict }}; +} +{% endif %} +{% elif (proto == "udp"): %} +chain v2r_rules_forward_{{ proto }} { + meta l4proto udp {{ o_nft_udp_extra }} meta mark set 1 tproxy to :{{ redir_port }}; +} +{% endif %} +{% endif %} diff --git a/v2ray-core/files/usr/share/v2ray-rules/set.uc b/v2ray-core/files/usr/share/v2ray-rules/set.uc new file mode 100644 index 000000000..a686829a6 --- /dev/null +++ b/v2ray-core/files/usr/share/v2ray-rules/set.uc @@ -0,0 +1,117 @@ +{% +let fs = require("fs"); + +let o_dst_bypass4_ = " + 0.0.0.0/8 + 10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.0/8 + 169.254.0.0/16 + 172.16.0.0/12 + 192.0.0.0/24 + 192.0.2.0/24 + 192.168.0.0/16 + 192.31.196.0/24 + 192.52.193.0/24 + 192.88.99.0/24 + 192.175.48.0/24 + 198.18.0.0/15 + 198.51.100.0/24 + 203.0.113.0/24 + 224.0.0.0/4 + 240.0.0.0/4 +"; +let o_dst_bypass6_ = " + ::1/128 + ::/128 + ::ffff:0:0/96 + 64:ff9b:1::/48 + 100::/64 + fe80::/10 + 2001::/23 + fc00::/7 +"; +let o_dst_bypass_ = o_dst_bypass4_ + " " + o_dst_bypass6_; + +let set_suffix = { + "src_bypass": { + str: o_src_bypass, + }, + "src_forward": { + str: o_src_forward, + }, + "src_checkdst": { + str: o_src_checkdst, + }, + "remote_servers": { + str: o_remote_servers + }, + "dst_bypass": { + str: o_dst_bypass, + file: o_dst_bypass_file, + }, + "dst_bypass_": { + str: o_dst_bypass_, + }, + "dst_forward": { + str: o_dst_forward, + file: o_dst_forward_file, + }, + "dst_forward_rrst_": {}, +}; + +function set_name(suf, af) { + if (af == 4) { + return "v2r_rules_"+suf; + } else { + return "v2r_rules6_"+suf; + } +} + +function set_elements_parse(res, str, af) { + for (let addr in split(str, /[ \t\n]/)) { + addr = trim(addr); + if (!addr) continue; + if (af == 4 && index(addr, ":") != -1) continue; + if (af == 6 && index(addr, ":") == -1) continue; + push(res, addr); + } +} + +function set_elements(suf, af) { + let obj = set_suffix[suf]; + let res = []; + let addr; + + let str = obj["str"]; + if (str) { + set_elements_parse(res, str, af); + } + + let file = obj["file"]; + if (file) { + let fd = fs.open(file); + if (fd) { + str = fd.read("all"); + set_elements_parse(res, str, af); + } + } + + return res; +} +%} + +{% for (let suf in set_suffix): for (let af in [4, 6]): %} +set {{ set_name(suf, af) }} { + type ipv{{af}}_addr; + flags interval; + auto-merge; +{% let elems = set_elements(suf, af); if (length(elems)): %} + elements = { +{% for (let i = 0; i < length(elems); i++): %} + {{ elems[i] }}{% if (i < length(elems) - 1): %},{% endif %}{% print("\n") %} +{% endfor %} + } +{% endif %} +} +{% endfor; endfor %} diff --git a/v2ray-core/files/usr/share/v2ray-rules/v2r-rules.uc b/v2ray-core/files/usr/share/v2ray-rules/v2r-rules.uc new file mode 100644 index 000000000..f3955b2ef --- /dev/null +++ b/v2ray-core/files/usr/share/v2ray-rules/v2r-rules.uc @@ -0,0 +1,8 @@ +{% + +include("set.uc"); +include("chain.uc", {proto: "tcp"}); +include("chain.uc", {proto: "udp"}); + +%} + diff --git a/xray-core/Makefile b/xray-core/Makefile index 8952c64e7..23f6786c2 100644 --- a/xray-core/Makefile +++ b/xray-core/Makefile @@ -1,6 +1,6 @@ include $(TOPDIR)/rules.mk -PKG_NAME:=xray-core +PKG_NAME:=xray PKG_VERSION:=1.8.6 PKG_RELEASE:=1 @@ -19,7 +19,7 @@ GO_PKG:=github.com/XTLS/Xray-core include $(INCLUDE_DIR)/package.mk include $(TOPDIR)/feeds/openmptcprouter/golang/golang-package.mk -define Package/$(PKG_NAME) +define Package/$(PKG_NAME)/template SECTION:=Custom CATEGORY:=Extra packages TITLE:=Xray-core @@ -27,13 +27,35 @@ define Package/$(PKG_NAME) PROVIDES:=xray-core endef +define Package/$(PKG_NAME)-core + $(call Package/$(PKG_NAME)/template) +endef + +define Package/$(PKG_NAME)-config + $(call Package/$(PKG_NAME)/template) + TITLE+= (init script) +endef + +define Package/$(PKG_NAME)-config-nft + $(call Package/$(PKG_NAME)/template) + TITLE+= (init script with nft) +endef + define Package/$(PKG_NAME)/description Xray-core bare bones binary (compiled without cgo) endef +define Package/$(PKG_NAME)-config/description + Xray-core init script +endef + +define Package/$(PKG_NAME)-config-nft/description + Xray-core init script with nft support +endef + define Package/$(PKG_NAME)/config menu "Xray Configuration" - depends on PACKAGE_$(PKG_NAME) + depends on PACKAGE_$(PKG_NAME)-core config PACKAGE_XRAY_ENABLE_GOPROXY_IO bool "Use goproxy.io to speed up module fetching (recommended for some network situations)" @@ -50,19 +72,25 @@ endif MAKE_PATH:=$(GO_PKG_WORK_DIR_NAME)/build/src/$(GO_PKG) MAKE_VARS += $(GO_PKG_VARS) -#define Build/Patch -# $(CP) $(PKG_BUILD_DIR)/../Xray-core-$(PKG_VERSION)/* $(PKG_BUILD_DIR) -# $(Build/Patch/Default) -#endef - define Build/Compile cd $(PKG_BUILD_DIR); $(GO_PKG_VARS) $(USE_GOPROXY) CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o $(PKG_INSTALL_DIR)/bin/xray ./main; endef -define Package/$(PKG_NAME)/install +define Package/$(PKG_NAME)-core/install $(INSTALL_DIR) $(1)/usr/bin $(INSTALL_BIN) $(PKG_INSTALL_DIR)/bin/xray $(1)/usr/bin/xray - $(CP) ./files/* $(1)/ endef -$(eval $(call BuildPackage,$(PKG_NAME))) +define Package/$(PKG_NAME)-config/install + $(CP) ./files/* $(1)/ + rm -f $(1)/etc/init.d/xray-nft +endef + +define Package/$(PKG_NAME)-config-nft/install + $(CP) ./files/* $(1)/ + mv $(1)/etc/init.d/xray-nft $(1)/etc/init.d/xray +endef + +$(eval $(call BuildPackage,$(PKG_NAME)-core)) +$(eval $(call BuildPackage,$(PKG_NAME)-config)) +$(eval $(call BuildPackage,$(PKG_NAME)-config-nft)) diff --git a/xray-core/files/etc/init.d/xray-nft b/xray-core/files/etc/init.d/xray-nft new file mode 100755 index 000000000..1c0e85af3 --- /dev/null +++ b/xray-core/files/etc/init.d/xray-nft @@ -0,0 +1,2360 @@ +#!/bin/sh /etc/rc.common +# +# Copyright 2019-2020 Xingwang Liao +# Copyright 2020-2022 Ycarus (Yannick Chabanois) for OpenMPTCProuter +# Licensed to the public under the MIT License. +# + +START=99 +USE_PROCD=1 +EXTRA_COMMANDS="rules_up rules_down rules_exist" + +NAME=xray +CONFIG_FOLDER=/var/etc/$NAME + +xrrules_uc="/usr/share/xray-rules/xr-rules.uc" +xrrules_nft="/etc/nftables.d/90-xr-rules.nft" + +FILE_XRAY_DNSMASQ=/tmp/dnsmasq.d/$NAME +FILE_XRAY_DNSMASQ_CACHE=/tmp/$NAME.dnsmasq.cache + +IPSET_SRC_IGNORE_V4=xray_src_ignore_v4 +IPSET_SRC_IGNORE_V6=xray_src_ignore_v6 +IPSET_DST_PROXY_V4=xray_dst_proxy_v4 +IPSET_DST_PROXY_V6=xray_dst_proxy_v6 +IPSET_SRC_DIRECT_V4=ss_rules_src_bypass +IPSET_DST_DIRECT_V4=ss_rules_dst_bypass +IPSET_DST_DIRECT_V6=ss_rules6_dst_bypass + +OUTBOUND_SERVERS= +OUTBOUND_SERVERS_V4= +OUTBOUND_SERVERS_V6= + +TRANSPARENT_PROXY_EXPECTED=0 +TRANSPARENT_PROXY_PORT= +TRANSPARENT_PROXY_USE_TPROXY= +TRANSPARENT_PROXY_ADDITION= + +DNSMASQ_RESTART_EXPECTED=0 + +. /usr/share/libubox/jshn.sh + +_log() { + local level="$1" ; shift + local msg="$@" + logger -p "daemon.$level" -t "$NAME" "$msg" + + echo "[$level] $msg" >&2 +} + +_info() { + _log "info" $@ +} + +_err() { + _log "err" $@ +} + +version_over_5_4() { + MAJOR_VERSION=$(uname -r | awk -F '.' '{print $1}') + MINOR_VERSION=$(uname -r | awk -F '.' '{print $2}') + if [ $MAJOR_VERSION -ge 5 ] && [ $MINOR_VERSION -gt 13 ] || [ $MAJOR_VERSION -gt 5 ] ; then + return 0 + else + return 1 + fi +} + + +get_value_from_json() { + local json="$1" + local key="$2" + + test -n "$json" || return + + local value="" + + local old_ns + json_set_namespace "json_key" old_ns + json_load "$json" + json_get_var "$key" value + json_cleanup + json_set_namespace "$old_ns" + + echo "$value" +} + +get_commands_from_json() { + local json="$1" + + test -n "$json" || return + + jshn -r "$json" 2>/dev/null | grep -v "json_init" +} + +get_file_content() { + local filename="$1" + + test -n "$filename" || return + test -r "/etc/xray/${filename}.txt" || return + + cat "/etc/xray/${filename}.txt" | grep -v "^$" | grep -v "^#" +} + +append_server_ipv4() { + local addr="$1" + + test -n "$addr" || return + + if [ -z "$OUTBOUND_SERVERS_V4" ] ; then + OUTBOUND_SERVERS_V4="$addr" + else + OUTBOUND_SERVERS_V4="$(cat >&1 <<-EOF + $OUTBOUND_SERVERS_V4 + $addr + EOF + )" + fi +} + +append_server_ipv6() { + local addr="$1" + + test -n "$addr" || return + + if [ -z "$OUTBOUND_SERVERS_V6" ] ; then + OUTBOUND_SERVERS_V6="$addr" + else + OUTBOUND_SERVERS_V6="$(cat >&1 <<-EOF + $OUTBOUND_SERVERS_V6 + $addr + EOF + )" + fi +} + +append_server_address() { + local addr="$1" + + test -n "$addr" || return + + local ipv4 + for ipv4 in $(resolveip -4 -t 5 "$addr") ; do + append_server_ipv4 "$ipv4" + OUTBOUND_SERVERS="$OUTBOUND_SERVERS $ipv4" + done + + local ipv6 + for ipv6 in $(resolveip -6 -t 5 "$addr") ; do + append_server_ipv6 "$ipv6" + OUTBOUND_SERVERS="$OUTBOUND_SERVERS $ipv6" + done +} + +xray_section_validate() { + uci_validate_section "$NAME" "xray" "$1" \ + 'enabled:bool:0' \ + 'xray_file:string' \ + 'asset_location:directory' \ + 'mem_percentage:and(uinteger, max(100)):80' \ + 'config_file:file' \ + 'loglevel:or("debug", "info", "warning", "error", "none")' \ + 'access_log:string' \ + 'error_log:string' \ + 'stats_enabled:bool:0' \ + 'transport_enabled:bool:0' \ + 'inbounds:list(uci("xray", "@inbound"))' \ + 'outbounds:list(uci("xray", "@outbound"))' +} + +dns_section_validate() { + uci_validate_section "$NAME" "dns" "$1" \ + 'enabled:bool:0' \ + 'tag:string' \ + 'client_ip:ipaddr' \ + 'hosts:list(string)' \ + 'servers:list(uci("xray", "@dns_server"))' +} + +dns_server_section_validate() { + uci_validate_section "$NAME" "dns_server" "$1" \ + 'address:string' \ + 'port:port' \ + 'domains:list(string)' \ + 'expect_ips:list(string)' +} + +routing_section_validate() { + uci_validate_section "$NAME" "routing" "$1" \ + 'enabled:bool:0' \ + 'domain_strategy:or("AsIs", "IPIfNonMatch", "IPOnDemand")' \ + 'rules:list(uci("xray", "@routing_rule"))' \ + 'balancers:list(uci("xray", "@routing_balancer"))' +} + +routing_rule_section_validate() { + uci_validate_section "$NAME" "routing_rule" "$1" \ + 'type:"field"' \ + 'domain:list(string)' \ + 'ip:list(string)' \ + 'port:or(port, portrange)' \ + 'network:list(or("tcp", "udp"))' \ + 'source:list(string)' \ + 'user:list(string)' \ + 'inbound_tag:list(string)' \ + 'protocol:list(or("http", "tls", "bittorrent"))' \ + 'attrs:string' \ + 'outbound_tag:string' \ + 'balancer_tag:string' +} + +routing_balancer_section_validate() { + uci_validate_section "$NAME" "routing_balancer" "$1" \ + 'tag:string' \ + 'selector:list(string)' +} + +policy_section_validate() { + uci_validate_section "$NAME" "policy" "$1" \ + 'enabled:bool:0' \ + 'levels:list(uci("xray", "@policy_level"))' \ + 'system_stats_inbound_uplink:bool:0' \ + 'system_stats_inbound_downlink:bool:0' +} + +policy_level_section_validate() { + uci_validate_section "$NAME" "policy_level" "$1" \ + 'level:uinteger' \ + 'handshake:uinteger:4' \ + 'conn_idle:uinteger:300' \ + 'uplink_only:uinteger:2' \ + 'downlink_only:uinteger:5' \ + 'stats_user_uplink:bool:0' \ + 'stats_user_downlink:bool:0' \ + 'buffer_size:uinteger' +} + +reverse_section_validate() { + uci_validate_section "$NAME" "reverse" "$1" \ + 'enabled:bool:0' \ + 'bridges:list(string)' \ + 'portals:list(string)' +} + +inbound_section_validate() { + uci_validate_section "$NAME" "inbound" "$1" \ + 'port:or(port, portrange, string)' \ + 'listen:ipaddr' \ + 'protocol:string' \ + 's_dokodemo_door_address:host' \ + 's_dokodemo_door_port:port' \ + 's_dokodemo_door_network:list(or("tcp", "udp"))' \ + 's_dokodemo_door_timeout:uinteger' \ + 's_dokodemo_door_follow_redirect:bool:0' \ + 's_dokodemo_door_user_level:uiterger' \ + 's_http_account_user:string' \ + 's_http_account_pass:string' \ + 's_http_allow_transparent:bool:0' \ + 's_http_timeout:uinteger' \ + 's_http_user_level:uinteger' \ + 's_mtproto_user_email:string' \ + 's_mtproto_user_secret:string' \ + 's_mtproto_user_level:uinteger' \ + 's_shadowsocks_email:string' \ + 's_shadowsocks_method:string' \ + 's_shadowsocks_password:string' \ + 's_shadowsocks_level:uinteger' \ + 's_shadowsocks_ota:bool:0' \ + 's_shadowsocks_network:list(or("tcp", "udp")):tcp' \ + 's_socks_auth:or("noauth", "password")' \ + 's_socks_account_user:string' \ + 's_socks_account_pass:string' \ + 's_socks_udp:bool:0' \ + 's_socks_ip:host' \ + 's_socks_user_level:uinteger' \ + 's_vmess_client_id:string' \ + 's_vmess_client_alter_id:and(uinteger, max(65535))' \ + 's_vmess_client_email:string' \ + 's_vmess_client_user_level:uinteger' \ + 's_vmess_default_alter_id:and(uinteger, max(65535))' \ + 's_vmess_default_user_level:uinteger' \ + 's_vmess_detour_to:string' \ + 's_vmess_disable_insecure_encryption:bool:0' \ + 's_vless_client_id:string' \ + 's_vless_client_alter_id:and(uinteger, max(65535))' \ + 's_vless_client_email:string' \ + 's_vless_client_user_level:uinteger' \ + 's_vless_default_alter_id:and(uinteger, max(65535))' \ + 's_vless_default_user_level:uinteger' \ + 's_vless_detour_to:string' \ + 's_vless_disable_insecure_encryption:bool:0' \ + 's_trojan_client_id:string' \ + 's_trojan_client_alter_id:and(uinteger, max(65535))' \ + 's_trojan_client_email:string' \ + 's_trojan_client_user_level:uinteger' \ + 's_trojan_default_alter_id:and(uinteger, max(65535))' \ + 's_trojan_default_user_level:uinteger' \ + 's_trojan_detour_to:string' \ + 's_trojan_disable_insecure_encryption:bool:0' \ + 's_socks_client_id:string' \ + 's_socks_client_email:string:"openmptcprouter"' \ + 'ss_network:or("tcp", "kcp", "ws", "http", "domainsocket", "quic")' \ + 'ss_security:or("none", "tls")' \ + 'ss_tls_server_name:host' \ + 'ss_tls_alpn:string' \ + 'ss_tls_allow_insecure:bool:0' \ + 'ss_tls_allow_insecure_ciphers:bool:0' \ + 'ss_tls_disable_system_root:bool:0' \ + 'ss_tls_cert_usage:or("encipherment", "verify", "issue")' \ + 'ss_tls_cert_file:string' \ + 'ss_tls_key_file:string' \ + 'ss_tcp_header_type:or("none", "http")' \ + 'ss_tcp_header_request_version:string' \ + 'ss_tcp_header_request_method:string:GET' \ + 'ss_tcp_header_request_path:string' \ + 'ss_tcp_header_request_headers:list(string)' \ + 'ss_tcp_header_response_version:string' \ + 'ss_tcp_header_response_status:string' \ + 'ss_tcp_header_response_reason:string' \ + 'ss_tcp_header_response_headers:list(string)' \ + 'ss_kcp_mtu:and(min(576), max(1460))' \ + 'ss_kcp_tti:and(min(10), max(100))' \ + 'ss_kcp_uplink_capacity:uinteger' \ + 'ss_kcp_downlink_capacity:uinteger' \ + 'ss_kcp_congestion:bool:0' \ + 'ss_kcp_read_buffer_size:uinteger' \ + 'ss_kcp_write_buffer_size:uinteger' \ + 'ss_kcp_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ + 'ss_websocket_path:string' \ + 'ss_websocket_headers:list(string)' \ + 'ss_http_host:list(host)' \ + 'ss_http_path:string' \ + 'ss_domainsocket_path:string' \ + 'ss_quic_security:or("aes-128-gcm", "chacha20-poly1305", "none")' \ + 'ss_quic_key:string' \ + 'ss_quic_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ + 'ss_sockopt_tcp_fast_open:or("0", "1")' \ + 'ss_sockopt_mptcp:or("0", "1")' \ + 'ss_sockopt_tproxy:or("redirect", "tproxy", "off")' \ + 'tag:string' \ + 'sniffing_enabled:bool:0' \ + 'sniffing_dest_override:list(or("http", "tls"))' \ + 'allocate_strategy:or("always", "random")' \ + 'allocate_refresh:uinteger' \ + 'allocate_concurrency:uinteger' +} + +outbound_section_validate() { + uci_validate_section "$NAME" "outbound" "$1" \ + 'send_through:ipaddr' \ + 'protocol:string' \ + 'tag:string' \ + 's_blackhole_reponse_type:or("none", "http")' \ + 's_dns_network:or("tcp", "udp")' \ + 's_dns_address:string' \ + 's_dns_port:port' \ + 's_freedom_domain_strategy:or("AsIs", "UseIP", "UseIPv4", "UseIPv6")' \ + 's_freedom_redirect:string' \ + 's_freedom_user_level:uinteger' \ + 's_http_server_address:host' \ + 's_http_server_port:port' \ + 's_http_account_user:string' \ + 's_http_account_pass:string' \ + 's_shadowsocks_email:string' \ + 's_shadowsocks_address:host' \ + 's_shadowsocks_port:port' \ + 's_shadowsocks_method:string' \ + 's_shadowsocks_password:string' \ + 's_shadowsocks_level:uinteger' \ + 's_shadowsocks_ota:bool:0' \ + 's_socks_server_address:host' \ + 's_socks_server_port:port' \ + 's_socks_account_user:string' \ + 's_socks_account_pass:string' \ + 's_socks_user_level:uinteger' \ + 's_vmess_address:host' \ + 's_vmess_port:port' \ + 's_vmess_user_id:string' \ + 's_vmess_user_alter_id:and(uinteger, max(65535))' \ + 's_vmess_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ + 's_vmess_user_level:uinteger' \ + 's_vless_address:host' \ + 's_vless_port:port' \ + 's_vless_user_id:string' \ + 's_vless_user_alter_id:and(uinteger, max(65535))' \ + 's_vless_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ + 's_vless_user_encryption:or("auto", "none")' \ + 's_vless_user_level:uinteger' \ + 's_vless_reality_address:host' \ + 's_vless_reality_port:port' \ + 's_vless_reality_user_id:string' \ + 's_vless_reality_user_alter_id:and(uinteger, max(65535))' \ + 's_vless_reality_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ + 's_vless_reality_user_encryption:or("auto", "none")' \ + 's_vless_reality_flow:string' \ + 's_vless_reality_public_key:string' \ + 's_vless_reality_user_level:uinteger' \ + 's_trojan_address:host' \ + 's_trojan_port:port' \ + 's_trojan_user_id:string' \ + 's_trojan_user_alter_id:and(uinteger, max(65535))' \ + 's_trojan_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ + 's_trojan_user_encryption:or("auto", "none")' \ + 's_trojan_user_level:uinteger' \ + 's_socks_address:host' \ + 's_socks_port:port' \ + 's_socks_user_id:string' \ + 's_socks_email:string:openmptcprouter' \ + 'ss_network:or("tcp", "kcp", "ws", "http", "domainsocket", "quic")' \ + 'ss_security:or("none", "tls")' \ + 'ss_tls_server_name:host' \ + 'ss_tls_alpn:string' \ + 'ss_tls_allow_insecure:bool:0' \ + 'ss_tls_allow_insecure_ciphers:bool:0' \ + 'ss_tls_disable_system_root:bool:0' \ + 'ss_tls_cert_usage:or("encipherment", "verify", "issue")' \ + 'ss_tls_cert_file:string' \ + 'ss_tls_key_file:string' \ + 'ss_tcp_header_type:or("none", "http")' \ + 'ss_tcp_header_request_version:string' \ + 'ss_tcp_header_request_method:string' \ + 'ss_tcp_header_request_path:string' \ + 'ss_tcp_header_request_headers:list(string)' \ + 'ss_tcp_header_response_version:string' \ + 'ss_tcp_header_response_status:string' \ + 'ss_tcp_header_response_reason:string' \ + 'ss_tcp_header_response_headers:list(string)' \ + 'ss_kcp_mtu:and(min(576), max(1460))' \ + 'ss_kcp_tti:and(min(10), max(100))' \ + 'ss_kcp_uplink_capacity:uinteger' \ + 'ss_kcp_downlink_capacity:uinteger' \ + 'ss_kcp_congestion:bool:0' \ + 'ss_kcp_read_buffer_size:uinteger' \ + 'ss_kcp_write_buffer_size:uinteger' \ + 'ss_kcp_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ + 'ss_websocket_path:string' \ + 'ss_websocket_headers:list(string)' \ + 'ss_http_host:list(host)' \ + 'ss_http_path:string' \ + 'ss_domainsocket_path:string' \ + 'ss_quic_security:or("aes-128-gcm", "chacha20-poly1305", "none")' \ + 'ss_quic_key:string' \ + 'ss_quic_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ + 'ss_sockopt_mark:uinteger' \ + 'ss_sockopt_tcp_fast_open:or("0", "1")' \ + 'ss_sockopt_mptcp:or("0", "1")' \ + 'stream_settings:string' \ + 'proxy_settings_tag:string' \ + 'mux_enabled:bool:0' \ + 'mux_concurrency:uinteger:8' +} + +add_xray_redirect_rules() { + local ext_args="$1" + local lan_devices="$2" + local lan_ipaddrs="$3" + + local port="$TRANSPARENT_PROXY_PORT" + local addition="$TRANSPARENT_PROXY_ADDITION" + local ipset_src_direct="$IPSET_SRC_DIRECT_V4" + local ipset_dst_direct="$IPSET_DST_DIRECT_V4" + + test -n "$port" || return + +# # This part need a rewrite +# xray-rules -f +# #logger -t "xray" "xray-rules -l ${port} -L ${port} -s $OUTBOUND_SERVERS_V4 --rule-name def --src-default forward --dst-default forward --local-default forward" +# commandline="-l ${port} -s $OUTBOUND_SERVERS_V4 --rule-name def --src-default forward --dst-default forward --local-default forward" +# [ "$(uci -q get xray.main_transparent_proxy.redirect_udp)" = "1" ] && [ "$(uci -q get xray.omrout.protocol)" != "socks" ] && commandline="$commandline -L ${port}" +# xray-rules $commandline +# [ "$(uci -q get xray.main.inbounds | grep omr6)" != "" ] && [ -n "$OUTBOUND_SERVERS_V6" ] && { +# xray-rules6 -f +# commandline="-l $((port+1)) -L $((port+1)) -s $OUTBOUND_SERVERS_V6 --rule-name def --src-default forward --dst-default forward --local-default forward" +# [ "$(uci -q get xray.main_transparent_proxy.redirect_udp)" = "1" ] && ([ "$(uci -q get xray.omrout.protocol)" = "vless-reality" ] || [ "$(uci -q get xray.omrout.protocol)" = "vless" ] || [ "$(uci -q get xray.omrout.protocol)" = "vmess" ]) && commandline="$commandline -L ${port+1}" +# xray-rules6 $commandline +# } + + remote_servers="$(echo $OUTBOUND_SERVERS \ + | tr ' ' '\n' \ + | sort -u \ + | xargs -n 1 resolveip \ + | sort -u)" + + [ "$(uci -q get xray.main_transparent_proxy.redirect_udp)" = "1" ] && [ "$(uci -q get xray.omrout.protocol)" != "socks" ] && portudp="$port" + + local tmp="/tmp/xrrules" + json_init + json_add_string o_remote_servers "$remote_servers" + json_add_int o_redir_tcp_port "$port" + json_add_int o_redir_udp_port "$portudp" + json_add_string o_ifnames "$ifnames" + json_add_string o_local_default "forward" + json_add_string o_src_bypass "$src_ips_bypass" + json_add_string o_src_forward "$src_ips_forward" + json_add_string o_src_checkdst "$src_ips_checkdst" + json_add_string o_src_default "checkdst" + json_add_string o_dst_bypass "$dst_ips_bypass" + json_add_string o_dst_forward "$dst_ips_forward" + json_add_string o_dst_bypass_file "$dst_ips_bypass_file" + json_add_string o_dst_forward_file "$dst_ips_forward_file" + json_add_string o_dst_default "forward" + json_add_string o_nft_tcp_extra "$nft_tcp_extra" + json_add_string o_nft_udp_extra "$nft_udp_extra" + json_dump -i >"$tmp.json" + + if utpl -S -F "$tmp.json" "$xrrules_uc" >"$tmp.nft" \ + && ! cmp -s "$tmp.nft" "$xrrules_nft"; then + echo "table inet chk {include \"$tmp.nft\";}" >"$tmp.nft.chk" + if nft -f "$tmp.nft.chk" -c; then + mv "$tmp.nft" "$xrrules_nft" + fw4 restart + fi + rm -f "$tmp.nft.chk" + fi + rm -f "$tmp.json" + rm -f "$tmp.nft" + + [ -f /etc/init.d/omr-bypass ] && [ -z "$(pgrep -f omr-bypass)" ] && { + logger -t "xray" "Reload omr-bypass rules" + /etc/init.d/omr-bypass reload_rules + } +} + +init_rules_for_listfile() { + local direct_list_dns="$1" + local proxy_list_dns="$2" + + echo "# AUTO-GENERATED FILE. DO NOT MODIFY." >"$FILE_XRAY_DNSMASQ_CACHE" + + # For direct list + local direct_content + direct_content="$(get_file_content "directlist")" + + if [ -n "$direct_content" ] ; then + echo "$direct_content" | \ + grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_DST_DIRECT_V4 & timeout 0/" | \ + ipset -! restore 2>/dev/null + + echo "$direct_content" | \ + grep -oE "([0-9a-fA-F]{0,4}:){1,7}([0-9a-fA-F]){0,4}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_DST_DIRECT_V6 & timeout 0/" | \ + ipset -! restore 2>/dev/null + + if [ -n "$direct_list_dns" ] ; then + echo "$direct_content" | \ + grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ + sed "s|.*|server=/&/$direct_list_dns\nipset=/&/$IPSET_DST_DIRECT_V4,$IPSET_DST_DIRECT_V6|" \ + >>"$FILE_XRAY_DNSMASQ_CACHE" + else + echo "$direct_content" | \ + grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ + sed "s|.*|ipset=/&/$IPSET_DST_DIRECT_V4,$IPSET_DST_DIRECT_V6|" \ + >>"$FILE_XRAY_DNSMASQ_CACHE" + fi + fi + + # For proxy list + local proxy_content + proxy_content="$(get_file_content "proxylist")" + + if [ -n "$proxy_content" ] ; then + echo "$proxy_content" | \ + grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_DST_PROXY_V4 & timeout 0/" | \ + ipset -! restore 2>/dev/null + + echo "$proxy_content" | \ + grep -oE "([0-9a-fA-F]{0,4}:){1,7}([0-9a-fA-F]){0,4}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_DST_PROXY_V6 & timeout 0/" | \ + ipset -! restore 2>/dev/null + + if [ -n "$proxy_list_dns" ] ; then + echo "$proxy_content" | \ + grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ + sed "s|.*|server=/&/$proxy_list_dns\nipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ + >>"$FILE_XRAY_DNSMASQ_CACHE" + else + echo "$proxy_content" | \ + grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ + sed "s|.*|ipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ + >>"$FILE_XRAY_DNSMASQ_CACHE" + fi + fi + + # For local devices outbound list + local src_content + src_content="$(get_file_content "srcdirectlist")" + + if [ -n "$src_content" ] ; then + echo "$src_content" | \ + grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ + sed "s/.*/add $IPSET_SRC_DIRECT_V4 & timeout 0/" | \ + ipset -! restore 2>/dev/null + fi + +} + +gracefully_restart_dnsmasq() { + if [ "x$DNSMASQ_RESTART_EXPECTED" = "x1" ] && [ -x "/etc/init.d/dnsmasq" ] ; then + _info "Restarting dnsmasq..." + /etc/init.d/dnsmasq restart >/dev/null 2>&1 + DNSMASQ_RESTART_EXPECTED=0 + fi +} + +add_dns_settings() { + local section="${1}_dns" + + if ! dns_section_validate "$section" ; then + _err "Invalid DNS config: $section, skip" + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + _info "DNS disabled: $section" + return 0 + fi + + json_add_object "dns" + + test -n "$tag" && \ + json_add_string "tag" "$tag" + test -n "$client_ip" && \ + json_add_string "clientIp" "$client_ip" + + if [ -n "$hosts" ] ; then + json_add_object "hosts" + + local h + for h in $hosts ; do + local domain="$(echo "$h" | cut -d'|' -f1)" + local ip="$(echo "$h" | cut -d'|' -f2)" + + if [ -n "$domain" ] && [ -n "$ip" ] ; then + json_add_string "$domain" "$ip" + fi + done + + json_close_object # hosts + fi + + if [ -n "$servers" ] ; then + json_add_array "servers" + + for ss in $servers ; do + if dns_server_section_validate "$ss" ; then + if [ -z "$address" ] ; then + continue + fi + + if [ -z "${port}${domains}${expect_ips}" ] ; then + json_add_string "" "$address" + else + json_add_object "" + json_add_string "address" "$address" + + if [ -n "$port" ] ; then + json_add_int "port" "$port" + else + json_add_int "port" "53" + fi + + if [ -n "$domains" ] ; then + json_add_array "domains" + + local d + for d in $domains ; do + json_add_string "" "$d" + done + + json_close_array # domains + fi + + if [ -n "$expect_ips" ] ; then + json_add_array "expectIPs" + + local e + for e in $expect_ips ; do + json_add_string "" "$e" + done + + json_close_array # expectIPs + fi + + json_close_object + fi + fi + done + + json_close_array # servers + fi + + json_close_object # dns +} + +add_routing_settings() { + local section="${1}_routing" + + if ! routing_section_validate "$section" ; then + _err "Invalid routing config: $section, skip" + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + _info "Routing disabled: $section" + return 0 + fi + + json_add_object "routing" + + test -n "$domain_strategy" && \ + json_add_string "domainStrategy" "$domain_strategy" + + if [ -n "$rules" ] ; then + json_add_array "rules" + + local rs + for rs in $rules ; do + if routing_rule_section_validate "$rs" ; then + json_add_object "" + + json_add_string "type" "$type" + + if [ -n "$domain" ] ; then + json_add_array "domain" + + local d + for d in $domain ; do + json_add_string "" "$d" + done + + json_close_array # domain + fi + + if [ -n "$ip" ] ; then + json_add_array "ip" + + local i + for i in $ip ; do + json_add_string "" "$i" + done + + json_close_array # ip + fi + + if [ -n "$port" ] ; then + json_add_string "port" "$(echo "$port" | tr -s ' ' ',')" + fi + + if [ -n "$network" ] ; then + json_add_string "network" "$(echo "$network" | tr -s ' ' ',')" + fi + + if [ -n "$source" ] ; then + json_add_array "source" + + local s + for s in $source ; do + json_add_string "" "$s" + done + + json_close_array # source + fi + + if [ -n "$user" ] ; then + json_add_array "user" + + local u + for u in $user ; do + json_add_string "" "$u" + done + + json_close_array # user + fi + + if [ -n "$inbound_tag" ] ; then + json_add_array "inboundTag" + + local it + for it in $inbound_tag ; do + json_add_string "" "$it" + done + + json_close_array # inboundTag + fi + + if [ -n "$protocol" ] ; then + json_add_array "protocol" + local p + for p in $protocol ; do + json_add_string "" "$p" + done + json_close_array # protocol + fi + + test -n "$attrs" && \ + json_add_string "attrs" "$attrs" + test -n "$outbound_tag" && \ + json_add_string "outboundTag" "$outbound_tag" + test -n "$balancer_tag" && \ + json_add_string "balancerTag" "$balancer_tag" + + json_close_object + fi + done + + json_close_array # rules + fi + + if [ -n "$balancers" ] ; then + json_add_array "balancers" + + local bs + for bs in $balancers ; do + if routing_balancer_section_validate "$bs" ; then + json_add_object "" + json_add_string "tag" "$tag" + + json_add_array "selector" + + local s + for s in $selector ; do + json_add_string "" "$s" + done + + json_close_array # selector + json_close_object + fi + done + + json_close_array # balancers + fi + + json_close_object +} + +add_policy_settings() { + local section="${1}_policy" + + if ! policy_section_validate "$section" ; then + _err "Invalid policy config: $section, skip" + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + _info "Policy disabled: $section" + return 0 + fi + + json_add_object "policy" + + if [ -n "$levels" ] ; then + json_add_object "levels" + + local l_s + for l_s in $levels ; do + if policy_level_section_validate "$l_s" ; then + json_add_object "$level" + json_add_int "handshake" "$handshake" + json_add_int "connIdle" "$conn_idle" + json_add_int "uplinkOnly" "$uplink_only" + json_add_int "downlinkOnly" "$downlink_only" + json_add_boolean "statsUserUplink" "$stats_user_uplink" + json_add_boolean "statsUserDownlink" "$stats_user_downlink" + test -n "$buffer_size" && \ + json_add_int "bufferSize" "$buffer_size" + json_close_object + fi + done + + json_close_object # levels + fi + + json_add_object "system" + json_add_boolean "statsInboundUplink" "$system_stats_inbound_uplink" + json_add_boolean "statsInboundDownlink" "$system_stats_inbound_downlink" + json_close_object # system + + json_close_object # policy +} + +add_reverse_settings() { + local section="${1}_reverse" + + if ! reverse_section_validate "$section" ; then + _err "Invalid reverse config: $section, skip" + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + _info "Reverse disabled: $section" + return 0 + fi + + json_add_object "reverse" + + if [ -n "$bridges" ] ; then + json_add_array "bridges" + + local b + for b in $bridges ; do + local tag="$(echo "$b" | cut -d'|' -f1)" + local domain="$(echo "$b" | cut -d'|' -f2)" + if [ -n "$tag" ] && [ -n "$domain" ] ; then + json_add_object "" + json_add_string "tag" "$tag" + json_add_string "domain" "$domain" + json_close_object + fi + done + + json_close_array # bridges + fi + + if [ -n "$portals" ] ; then + json_add_array "portals" + + local p + for p in $portals ; do + local tag="$(echo "$p" | cut -d'|' -f1)" + local domain="$(echo "$p" | cut -d'|' -f2)" + if [ -n "$tag" ] && [ -n "$domain" ] ; then + json_add_object "" + json_add_string "tag" "$tag" + json_add_string "domain" "$domain" + json_close_object + fi + done + + json_close_array # portals + fi + + json_close_object # reverse +} + +add_transport_settings() { + local json + json="$(get_file_content "transport")" + + if [ -z "$json" ] ; then + _err "Invalid transport config: $key" + return 1 + fi + + json_add_object "transport" + eval "$(get_commands_from_json "$json")" + json_close_object # transport +} + +add_inbound_setting() { + local section="$1" + + if ! inbound_section_validate "$section" ; then + _err "Invalid inbound section: $section" + return 1 + fi + + json_add_object "" + + test -n "$listen" && \ + json_add_string "listen" "$listen" + json_add_int "port" "$port" + json_add_string "protocol" "$protocol" + + case "${protocol:-x}" in + "dokodemo-door") + json_add_object "settings" + + if [ -n "$port" ] && [ "x$port" = "x$TRANSPARENT_PROXY_PORT" ] ; then + local settings_network="tcp" + + test -n "$TRANSPARENT_PROXY_ADDITION" && \ + settings_network="$settings_network,udp" + + json_add_boolean "followRedirect" "1" + json_add_string "network" "$settings_network" + else + test -n "$s_dokodemo_door_address" && \ + json_add_string "address" "$s_dokodemo_door_address" + + test -n "$s_dokodemo_door_port" && \ + json_add_int "port" "$s_dokodemo_door_port" + + test -n "$s_dokodemo_door_follow_redirect" && \ + json_add_boolean "followRedirect" "$s_dokodemo_door_follow_redirect" + + test -n "$s_dokodemo_door_network" && \ + json_add_string "network" "$(echo "$s_dokodemo_door_network" | tr -s ' ' ',')" + fi + + test -n "$s_dokodemo_door_timeout" && \ + json_add_int "timeout" "$s_dokodemo_door_timeout" + + test -n "$s_dokodemo_door_user_level" && \ + json_add_int "userLevel" "$s_dokodemo_door_user_level" + + json_close_object # settings + ;; + "http") + json_add_object "settings" + + if [ -n "$s_http_account_user" ] ; then + json_add_array "accounts" + + json_add_object "" + json_add_string "user" "$s_http_account_user" + json_add_string "pass" "$s_http_account_pass" + json_close_object + + json_close_array # accounts + fi + + json_add_boolean "allowTransparent" "$s_http_allow_transparent" + + test -n "$s_http_timeout" && \ + json_add_int "timeout" "$s_http_timeout" + test -n "$s_http_user_level" && \ + json_add_int "userLevel" "$s_http_user_level" + + json_close_object # settings + ;; + "mtproto") + json_add_object "settings" + + if [ -n "$s_mtproto_user_email" ] ; then + json_add_array "users" + json_add_object "" + + json_add_string "email" "$s_mtproto_user_email" + json_add_string "secret" "$s_mtproto_user_secret" + + test -n "$s_mtproto_user_level" && \ + json_add_int "level" "$s_mtproto_user_level" + + json_close_object + json_close_array # users + fi + + json_close_object # settings + ;; + "shadowsocks") + json_add_object "settings" + + json_add_string "method" "$s_shadowsocks_method" + json_add_string "password" "$s_shadowsocks_password" + + test -n "$s_shadowsocks_email" && \ + json_add_string "email" "$s_shadowsocks_email" + test -n "$s_shadowsocks_level" && \ + json_add_int "level" "$s_shadowsocks_level" + + json_add_boolean "ota" "$s_shadowsocks_ota" + json_add_string "network" "$(echo "$s_shadowsocks_network" | tr -s ' ' ',')" + + json_close_object # settings + ;; + "socks") + json_add_object "settings" + + json_add_string "auth" "$s_socks_auth" + + if [ -n "$s_socks_account_user" ] ; then + json_add_array "accounts" + json_add_object "" + json_add_string "user" "$s_socks_account_user" + json_add_string "pass" "$s_socks_account_pass" + json_close_object + json_close_array # accounts + fi + if [ -n "$s_socks_client_id" ] ; then + json_add_array "accounts" + json_add_object "" + json_add_string "user" "$s_socks_email" + json_add_string "pass" "$s_socks_client_id" + json_close_object + json_close_array # accounts + fi + + json_add_boolean "udp" "$s_socks_udp" + + test -n "$s_socks_ip" && \ + json_add_string "ip" "$s_socks_ip" + test -n "$s_socks_user_level" && \ + json_add_int "userLevel" "$s_socks_user_level" + + json_close_object # settings + ;; + "vmess") + json_add_object "settings" + + if [ -n "$s_vmess_client_id" ] ; then + json_add_array "clients" + json_add_object "" + + json_add_string "id" "$s_vmess_client_id" + + test -n "$s_vmess_client_alter_id" && \ + json_add_int "alterId" "$s_vmess_client_alter_id" + test -n "$s_vmess_client_email" && \ + json_add_string "email" "$s_vmess_client_email" + test -n "$s_vmess_client_user_level" && \ + json_add_int "level" "$s_vmess_client_user_level" + + json_close_object + json_close_array # clients + fi + + json_add_object "default" + + test -n "$s_vmess_default_alter_id" && \ + json_add_int "alterId" "$s_vmess_default_alter_id" + test -n "$s_vmess_default_user_level" && \ + json_add_int "level" "$s_vmess_default_user_level" + + json_close_object # default + + if [ -n "$s_vmess_detour_to" ] ; then + json_add_object "detour" + json_add_string "to" "$s_vmess_detour_to" + json_close_object # detour + fi + + json_add_boolean "disableInsecureEncryption" "$s_vmess_disable_insecure_encryption" + + json_close_object # settings + ;; + "vless") + json_add_object "settings" + + if [ -n "$s_vless_client_id" ] ; then + json_add_array "clients" + json_add_object "" + + json_add_string "id" "$s_vless_client_id" + + test -n "$s_vless_client_alter_id" && \ + json_add_int "alterId" "$s_vless_client_alter_id" + test -n "$s_vless_client_email" && \ + json_add_string "email" "$s_vless_client_email" + test -n "$s_vless_client_user_level" && \ + json_add_int "level" "$s_vless_client_user_level" + + json_close_object + json_close_array # clients + fi + + json_add_object "default" + + test -n "$s_vless_default_alter_id" && \ + json_add_int "alterId" "$s_vless_default_alter_id" + test -n "$s_vless_default_user_level" && \ + json_add_int "level" "$s_vless_default_user_level" + + json_close_object # default + + if [ -n "$s_vless_detour_to" ] ; then + json_add_object "detour" + json_add_string "to" "$s_vless_detour_to" + json_close_object # detour + fi + + json_add_boolean "disableInsecureEncryption" "$s_vless_disable_insecure_encryption" + + json_close_object # settings + ;; + "trojan") + json_add_object "settings" + + if [ -n "$s_trojan_client_id" ] ; then + json_add_array "clients" + json_add_object "" + + json_add_string "password" "$s_trojan_client_id" + + test -n "$s_trojan_client_alter_id" && \ + json_add_int "alterId" "$s_trojan_client_alter_id" + test -n "$s_trojan_client_email" && \ + json_add_string "email" "$s_trojan_client_email" + test -n "$s_trojan_client_user_level" && \ + json_add_int "level" "$s_trojan_client_user_level" + + json_close_object + json_close_array # clients + fi + + json_add_object "default" + + test -n "$s_trojan_default_alter_id" && \ + json_add_int "alterId" "$s_trojan_default_alter_id" + test -n "$s_trojan_default_user_level" && \ + json_add_int "level" "$s_trojan_default_user_level" + + json_close_object # default + + if [ -n "$s_trojan_detour_to" ] ; then + json_add_object "detour" + json_add_string "to" "$s_trojan_detour_to" + json_close_object # detour + fi + + json_add_boolean "disableInsecureEncryption" "$s_trojan_disable_insecure_encryption" + + json_close_object # settings + ;; + esac + + json_add_object "streamSettings" + + test -n "$ss_network" && \ + json_add_string "network" "$ss_network" + + test -n "$ss_security" && \ + json_add_string "security" "$ss_security" + + if [ "x$ss_security" = "xtls" ] && [ "x$protocol" != "xshadowsocks" ]; then + json_add_object "tlsSettings" + + test -n "$ss_tls_server_name" && \ + json_add_string "serverName" "$ss_tls_server_name" + + if [ -n "$ss_tls_alpn" ] ; then + json_add_array "alpn" + json_add_string "" "$ss_tls_alpn" + json_close_array # alpn + fi + + json_add_boolean "allowInsecure" "$ss_tls_allow_insecure" + json_add_boolean "allowInsecureCiphers" "$ss_tls_allow_insecure_ciphers" + json_add_boolean "disableSystemRoot" "$ss_tls_disable_system_root" + + json_add_array "certificates" + if [ -n "$ss_tls_cert_file" ] ; then + json_add_object "" + + json_add_string "certificateFile" "$ss_tls_cert_file" + json_add_string "keyFile" "$ss_tls_key_file" + test -n "$ss_tls_cert_usage" && \ + json_add_string "usage" "$ss_tls_cert_usage" + + json_close_object + fi + json_close_array # certificates + + json_close_object # tlsSettings + fi + + case "${ss_network:-x}" in + "tcp") + json_add_object "tcpSettings" + + if [ -n "$ss_tcp_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_tcp_header_type" + + if [ "$ss_tcp_header_type" = "http" ] ; then + json_add_object "request" + test -n "$ss_tcp_header_request_version" && \ + json_add_string "version" "$ss_tcp_header_request_version" + json_add_string "method" "$ss_tcp_header_request_method" + + if [ -n "$ss_tcp_header_request_path" ] ; then + json_add_array "path" + json_add_string "" "$ss_tcp_header_request_path" + json_close_array # path + fi + + if [ -n "$ss_tcp_header_request_headers" ] ; then + json_add_object "headers" + + handle_request_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_array "$name" + json_add_string "" "$value" + json_close_array + fi + } + config_list_foreach "$section" "ss_tcp_header_request_headers" handle_request_header + + json_close_object # headers + fi + + json_close_object # request + + json_add_object "response" + + test -n "$ss_tcp_header_response_version" && \ + json_add_string "version" "$ss_tcp_header_response_version" + test -n "$ss_tcp_header_response_status" && \ + json_add_string "status" "$ss_tcp_header_response_status" + test -n "$ss_tcp_header_response_reason" && \ + json_add_string "reason" "$ss_tcp_header_response_reason" + + if [ -n "$ss_tcp_header_response_headers" ] ; then + json_add_object "headers" + + handle_response_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_array "$name" + json_add_string "" "$value" + json_close_array + fi + } + config_list_foreach "$section" "ss_tcp_header_response_headers" handle_response_header + + json_close_object # headers + fi + + json_close_object # response + fi + json_close_object # header + fi + + json_close_object # tcpSettings + ;; + "kcp") + json_add_object "kcpSettings" + + test -n "$ss_kcp_mtu" && \ + json_add_int "mtu" "$ss_kcp_mtu" + test -n "$ss_kcp_tti" && \ + json_add_int "tti" "$ss_kcp_tti" + test -n "$ss_kcp_uplink_capacity" && \ + json_add_int "uplinkCapacity" "$ss_kcp_uplink_capacity" + test -n "$ss_kcp_downlink_capacity" && \ + json_add_int "downlinkCapacity" "$ss_kcp_downlink_capacity" + json_add_boolean "congestion" "$ss_kcp_congestion" + test -n "$ss_kcp_read_buffer_size" && \ + json_add_int "readBufferSize" "$ss_kcp_read_buffer_size" + test -n "$ss_kcp_write_buffer_size" && \ + json_add_int "writeBufferSize" "$ss_kcp_write_buffer_size" + + if [ -n "$ss_kcp_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_kcp_header_type" + json_close_object # header + fi + + json_close_object # kcpSettings + ;; + "ws") + json_add_object "wsSettings" + + test -n "$ss_websocket_path" && \ + json_add_string "path" "$ss_websocket_path" + + if [ -n "$ss_websocket_headers" ] ; then + json_add_object "headers" + + handle_websocket_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_string "$name" "$value" + fi + } + config_list_foreach "$section" "ss_websocket_headers" handle_websocket_header + + json_close_object # headers + fi + + json_close_object # wsSettings + ;; + "http") + json_add_object "httpSettings" + + if [ -n "$ss_http_host" ] ; then + json_add_array "host" + + local h + for h in $ss_http_host ; do + json_add_string "" "$h" + done + + json_close_array # host + fi + + test -n "$ss_http_path" && \ + json_add_string "path" "$ss_http_path" + + json_close_object # httpSettings + ;; + "domainsocket") + json_add_object "dsSettings" + + test -n "$ss_domainsocket_path" && \ + json_add_string "path" "$ss_domainsocket_path" + + json_close_object # dsSettings + ;; + "quic") + json_add_object "quicSettings" + + test -n "$ss_quic_security" && \ + json_add_string "security" "$ss_quic_security" + test -n "$ss_quic_key" && \ + json_add_string "key" "$ss_quic_key" + + if [ -n "$ss_quic_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_quic_header_type" + json_close_object # header + fi + + json_close_object # quicSettings + ;; + esac + + json_add_object "sockopt" + +# if [ -n "$port" ] && [ "x$port" = "x$TRANSPARENT_PROXY_PORT" ] ; then +# if [ "x$TRANSPARENT_PROXY_USE_TPROXY" = "x1" ] ; then +# json_add_string "tproxy" "tproxy" +# else +# json_add_string "tproxy" "redirect" +# fi +# else + test -n "$ss_sockopt_tcp_fast_open" && \ + json_add_boolean "tcpFastOpen" "$ss_sockopt_tcp_fast_open" + if version_over_5_4; then + test -n "$ss_sockopt_mptcp" && \ + json_add_boolean "tcpMptcp" "$ss_sockopt_mptcp" + fi + test -n "$ss_sockopt_tproxy" && \ + json_add_string "tproxy" "$ss_sockopt_tproxy" +# fi + + json_close_object # sockopt + + json_close_object # streamSettings + + test -n "$tag" && \ + json_add_string "tag" "$tag" + + json_add_object "sniffing" + + json_add_boolean "enabled" "$sniffing_enabled" + + if [ -n "$sniffing_dest_override" ] ; then + json_add_array "destOverride" + local d + for d in $sniffing_dest_override ; do + json_add_string "" "$d" + done + json_close_array # destOverride + fi + + json_close_object # sniffing + + if [ -n "$allocate_strategy" ] ; then + json_add_object "allocate" + json_add_string "strategy" "$allocate_strategy" + test -n "$allocate_refresh" && \ + json_add_int "refresh" "$allocate_refresh" + test -n "$allocate_concurrency" && \ + json_add_int "concurrency" "$allocate_concurrency" + json_close_object # allocate + fi + + json_close_object +} + +add_outbound_setting() { + local section="$1" + + if ! outbound_section_validate "$section" ; then + _err "Invalid outbound section: $section" + return 1 + fi + + json_add_object "" + + test -n "$send_through" && \ + json_add_string "sendThrough" "$send_through" + if [ "$protocol" = "vless-reality" ]; then + json_add_string "protocol" "vless" + else + json_add_string "protocol" "$protocol" + fi + + case "${protocol:-x}" in + "blackhole") + json_add_object "settings" + + if [ -n "$s_blackhole_reponse_type" ] ; then + json_add_object "response" + json_add_string "type" "$s_blackhole_reponse_type" + json_close_object # response + fi + + json_close_object # settings + ;; + "dns") + json_add_object "settings" + + test -n "$s_dns_network" && \ + json_add_string "network" "$s_dns_network" + + if [ -n "$s_dns_address" ] ; then + json_add_string "address" "$s_dns_address" + append_server_address "$s_dns_address" + fi + + test -n "$s_dns_port" && \ + json_add_int "port" "$s_dns_port" + + json_close_object # settings + ;; + "freedom") + json_add_object "settings" + + test -n "$s_freedom_domain_strategy" && \ + json_add_string "domainStrategy" "$s_freedom_domain_strategy" + test -n "$s_freedom_redirect" && \ + json_add_string "redirect" "$s_freedom_redirect" + test -n "$s_freedom_user_level" && \ + json_add_int "userLevel" "$s_freedom_user_level" + + json_close_object # settings + ;; + "http") + json_add_object "settings" + json_add_array "servers" + + json_add_object "" + + json_add_string "address" "$s_http_server_address" + append_server_address "$s_http_server_address" + + test -n "$s_http_server_port" && \ + json_add_int "port" "$s_http_server_port" + + if [ -n "$s_http_account_user" ] ; then + json_add_array "users" + json_add_object "" + + json_add_string "user" "$s_http_account_user" + json_add_string "pass" "$s_http_account_pass" + + json_close_object + json_close_array # users + fi + json_close_object + + json_close_array # servers + json_close_object # settings + ;; + "mtproto") + json_add_object "settings" + json_close_object + ;; + "shadowsocks") + json_add_object "settings" + json_add_array "servers" + + json_add_object "" + test -n "$s_shadowsocks_email" && \ + json_add_string "email" "$s_shadowsocks_email" + json_add_string "address" "$s_shadowsocks_address" + append_server_address "$s_shadowsocks_address" + + json_add_int "port" "$s_shadowsocks_port" + json_add_string "method" "$s_shadowsocks_method" + json_add_string "password" "$s_shadowsocks_password" + + test -n "$s_shadowsocks_level" && \ + json_add_int "level" "$s_shadowsocks_level" + json_add_boolean "ota" "$s_shadowsocks_ota" + json_close_object + + json_close_array # servers + json_close_object # settings + ;; + "vmess") + json_add_object "settings" + + json_add_array "vnext" + json_add_object "" + + json_add_string "address" "$s_vmess_address" + append_server_address "$s_vmess_address" + + json_add_int "port" "$s_vmess_port" + + json_add_array "users" + json_add_object "" + json_add_string "id" "$s_vmess_user_id" + json_add_int "alterId" "$s_vmess_user_alter_id" + test -n "$s_vmess_user_security" && \ + json_add_string "security" "$s_vmess_user_security" + test -n "$s_vmess_user_level" && \ + json_add_int "level" "$s_vmess_user_level" + json_close_object + json_close_array # users + + json_close_object + + json_close_array # vnext + json_close_object # settings + ;; + "vless") + json_add_object "settings" + + json_add_array "vnext" + json_add_object "" + + json_add_string "address" "$s_vless_address" + append_server_address "$s_vless_address" + + json_add_int "port" "$s_vless_port" + + json_add_array "users" + json_add_object "" + json_add_string "id" "$s_vless_user_id" + json_add_int "alterId" "$s_vless_user_alter_id" + test -n "$s_vless_user_security" && \ + json_add_string "security" "$s_vless_user_security" + test -n "$s_vless_user_encryption" && \ + json_add_string "encryption" "$s_vless_user_encryption" + test -n "$s_vless_user_level" && \ + json_add_int "level" "$s_vless_user_level" + json_close_object + json_close_array # users + + json_close_object + + json_close_array # vnext + json_close_object # settings + ;; + "vless-reality") + json_add_object "settings" + + json_add_array "vnext" + json_add_object "" + + json_add_string "address" "$s_vless_reality_address" + append_server_address "$s_vless_reality_address" + + json_add_int "port" "$s_vless_reality_port" + + json_add_array "users" + json_add_object "" + json_add_string "id" "$s_vless_reality_user_id" + json_add_int "alterId" "$s_vless_reality_user_alter_id" + test -n "$s_vless_reality_user_security" && \ + json_add_string "security" "$s_vless_reality_user_security" + test -n "$s_vless_reality_user_encryption" && \ + json_add_string "encryption" "$s_vless_reality_user_encryption" + test -n "$s_vless_reality_user_level" && \ + json_add_int "level" "$s_vless_reality_user_level" + test -n "$s_vless_reality_flow" && \ + json_add_string "flow" "$s_vless_reality_flow" + json_close_object + json_close_array # users + + json_close_object + + json_close_array # vnext + json_close_object # settings + ;; + "trojan") + json_add_object "settings" + + json_add_array "servers" + json_add_object "" + + json_add_string "address" "$s_trojan_address" + append_server_address "$s_trojan_address" + + json_add_int "port" "$s_trojan_port" + + json_add_string "password" "$s_trojan_user_id" + + json_close_object + + json_close_array # trojan + json_close_object # settings + ;; + "socks") + json_add_object "settings" + + json_add_array "servers" + json_add_object "" + + json_add_string "address" "$s_socks_address" + append_server_address "$s_socks_address" + + json_add_int "port" "$s_socks_port" + + json_add_array "users" + json_add_object "" + json_add_string "pass" "$s_socks_user_id" + json_add_string "user" "$s_socks_email" + json_close_object + json_close_array # users + + json_close_object + + json_close_array # socks + json_close_object # settings + ;; + esac + + json_add_object "streamSettings" + test -n "$ss_network" && \ + json_add_string "network" "$ss_network" + + if [ "x$protocol" != "xshadowsocks" ]; then + test -n "$ss_security" && \ + json_add_string "security" "$ss_security" + + if [ "x$ss_security" = "xtls" ]; then + json_add_object "tlsSettings" + + test -n "$ss_tls_server_name" && \ + json_add_string "serverName" "$ss_tls_server_name" + + if [ -n "$ss_tls_alpn" ] ; then + json_add_array "alpn" + json_add_string "" "$ss_tls_alpn" + json_close_array + fi + + json_add_boolean "allowInsecure" "$ss_tls_allow_insecure" + json_add_boolean "allowInsecureCiphers" "$ss_tls_allow_insecure_ciphers" + json_add_boolean "disableSystemRoot" "$ss_tls_disable_system_root" + + json_add_array "certificates" + if [ -n "$ss_tls_cert_file" ] ; then + json_add_object "" + json_add_string "certificateFile" "$ss_tls_cert_file" + json_add_string "keyFile" "$ss_tls_key_file" + test -n "$ss_tls_cert_usage" && \ + json_add_string "usage" "$ss_tls_cert_usage" + json_close_object + fi + json_close_array # certificates + + json_close_object # tlsSettings + fi + fi + if [ "x$protocol" = "xvless-reality" ]; then + json_add_string "security" "reality" + json_add_object "realitySettings" + json_add_string "fingerprint" "chrome" + json_add_string "serverName" "" + json_add_string "publicKey" "$s_vless_reality_public_key" + json_add_string "spiderX" "" + json_add_string "shortId" "" + json_close_object + fi + + case "${ss_network:-x}" in + "tcp") + json_add_object "tcpSettings" + + if [ -n "$ss_tcp_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_tcp_header_type" + + if [ "$ss_tcp_header_type" = "http" ] ; then + json_add_object "request" + test -n "$ss_tcp_header_request_version" && \ + json_add_string "version" "$ss_tcp_header_request_version" + json_add_string "method" "$ss_tcp_header_request_method" + + if [ -n "$ss_tcp_header_request_path" ] ; then + json_add_array "path" + json_add_string "" "$ss_tcp_header_request_path" + json_close_array + fi + + if [ -n "$ss_tcp_header_request_headers" ] ; then + json_add_object "headers" + + handle_request_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_array "$name" + json_add_string "" "$value" + json_close_array + fi + } + config_list_foreach "$section" "ss_tcp_header_request_headers" handle_request_header + + json_close_object # headers + fi + + json_close_object # request + + json_add_object "response" + test -n "$ss_tcp_header_response_version" && \ + json_add_string "version" "$ss_tcp_header_response_version" + test -n "$ss_tcp_header_response_status" && \ + json_add_string "status" "$ss_tcp_header_response_status" + test -n "$ss_tcp_header_response_reason" && \ + json_add_string "reason" "$ss_tcp_header_response_reason" + + if [ -n "$ss_tcp_header_response_headers" ] ; then + json_add_object "headers" + + handle_response_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_array "$name" + json_add_string "" "$value" + json_close_array + fi + } + config_list_foreach "$section" "ss_tcp_header_response_headers" handle_response_header + + json_close_object # headers + fi + + json_close_object # response + fi + + json_close_object # header + fi + + json_close_object # tcpSettings + ;; + "kcp") + json_add_object "kcpSettings" + + test -n "$ss_kcp_mtu" && \ + json_add_int "mtu" "$ss_kcp_mtu" + test -n "$ss_kcp_tti" && \ + json_add_int "tti" "$ss_kcp_tti" + test -n "$ss_kcp_uplink_capacity" && \ + json_add_int "uplinkCapacity" "$ss_kcp_uplink_capacity" + test -n "$ss_kcp_downlink_capacity" && \ + json_add_int "downlinkCapacity" "$ss_kcp_downlink_capacity" + + json_add_boolean "congestion" "$ss_kcp_congestion" + + test -n "$ss_kcp_read_buffer_size" && \ + json_add_int "readBufferSize" "$ss_kcp_read_buffer_size" + test -n "$ss_kcp_write_buffer_size" && \ + json_add_int "writeBufferSize" "$ss_kcp_write_buffer_size" + + if [ -n "$ss_kcp_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_kcp_header_type" + json_close_object + fi + + json_close_object # kcpSettings + ;; + "ws") + json_add_object "wsSettings" + + test -n "$ss_websocket_path" && \ + json_add_string "path" "$ss_websocket_path" + + if [ -n "$ss_websocket_headers" ] ; then + json_add_object "headers" + + handle_websocket_header() { + local h="$1" + + local name="$(echo "$h" | cut -d'=' -f1)" + local value="$(echo "$h" | cut -d'=' -f2)" + + if [ -n "$name" ] && [ -n "$value" ] ; then + json_add_string "$name" "$value" + fi + } + config_list_foreach "$section" "ss_websocket_headers" handle_websocket_header + + json_close_object # headers + fi + + json_close_object # wsSettings + ;; + "http") + json_add_object "httpSettings" + + if [ -n "$ss_http_host" ] ; then + json_add_array "host" + + local h + for h in $ss_http_host ; do + json_add_string "" "$h" + done + + json_close_array # host + fi + + test -n "$ss_http_path" && \ + json_add_string "path" "$ss_http_path" + + json_close_object # httpSettings + ;; + "domainsocket") + json_add_object "dsSettings" + + test -n "$ss_domainsocket_path" && \ + json_add_string "path" "$ss_domainsocket_path" + + json_close_object # dsSettings + ;; + "quic") + json_add_object "quicSettings" + + test -n "$ss_quic_security" && \ + json_add_string "security" "$ss_quic_security" + test -n "$ss_quic_key" && \ + json_add_string "key" "$ss_quic_key" + + if [ -n "$ss_quic_header_type" ] ; then + json_add_object "header" + json_add_string "type" "$ss_quic_header_type" + json_close_object # header + fi + + json_close_object # quicSettings + ;; + esac + + json_add_object "sockopt" + + if [ -n "$TRANSPARENT_PROXY_PORT" ] ; then + json_add_int "mark" "255" + else + test -n "$ss_sockopt_mark" && \ + json_add_int "mark" "$ss_sockopt_mark" + fi + + test -n "$ss_sockopt_tcp_fast_open" && \ + json_add_boolean "tcpFastOpen" "$ss_sockopt_tcp_fast_open" + if version_over_5_4; then + test -n "$ss_sockopt_mptcp" && \ + json_add_boolean "tcpMptcp" "$ss_sockopt_mptcp" + fi + + json_close_object # sockopt + + json_close_object # streamSettings + + test -n "$tag" && \ + json_add_string "tag" "$tag" + + if [ -n "$proxy_settings_tag" ] ; then + json_add_object "proxySettings" + json_add_string "tag" "$proxy_settings_tag" + json_close_object # proxySettings + fi + + if [ "x$mux_enabled" = "x1" ] ; then + json_add_object "mux" + json_add_boolean "enabled" "1" + json_add_int "concurrency" "$mux_concurrency" + json_close_object # mux + fi + + json_close_object +} + +init_transparent_proxy() { + local tp_cfg="main_transparent_proxy" + local redirect_port use_tproxy redirect_udp redirect_dns + + config_get redirect_port "$tp_cfg" "redirect_port" + config_get_bool use_tproxy "$tp_cfg" "use_tproxy" "0" + config_get_bool redirect_udp "$tp_cfg" "redirect_udp" "0" + config_get_bool redirect_dns "$tp_cfg" "redirect_dns" "0" + + if [ -n "$redirect_port" ] && \ + ! validate_data "port" "$redirect_port" 2>/dev/null ; then + _err "Transparent proxy redirect port is invalid: $redirect_port" + return 1 + fi + + TRANSPARENT_PROXY_PORT="$redirect_port" + TRANSPARENT_PROXY_USE_TPROXY="$use_tproxy" + + if [ "x$redirect_udp" = "x1" ] && [ "$(uci -q get xray.omrout.protocol)" != "socks" ]; then + TRANSPARENT_PROXY_ADDITION="udp" + elif [ "x$redirect_dns" = "x1" ] ; then + TRANSPARENT_PROXY_ADDITION="dns" + else + TRANSPARENT_PROXY_ADDITION= + fi +} + +setup_transparent_proxy() { + if [ -z "$TRANSPARENT_PROXY_PORT" ] ; then + #_info "Transparent proxy disabled." + return 0 + fi + + if [ "x$TRANSPARENT_PROXY_EXPECTED" != "x1" ] ; then + #_info "No xray instance enabled, skip transparent proxy." + return 0 + fi + + _info "Setting transparent proxy on port: $TRANSPARENT_PROXY_PORT" + + local tp_cfg="main_transparent_proxy" + local lan_ifaces only_privileged_ports proxy_mode direct_list_dns proxy_list_dns + + config_get lan_ifaces "$tp_cfg" "lan_ifaces" + config_get_bool only_privileged_ports "$tp_cfg" "only_privileged_ports" "0" + config_get proxy_mode "$tp_cfg" "proxy_mode" + config_get direct_list_dns "$tp_cfg" "direct_list_dns" + config_get proxy_list_dns "$tp_cfg" "proxy_list_dns" + + _info "Transparent proxy mode: $proxy_mode" + + #init_rules_for_listfile "$direct_list_dns" "$proxy_list_dns" + + local ext_args + case "${proxy_mode:-default}" in + "cn_direct") + local chnroute="$(get_file_content "chnroute")" + local chnroute6="$(get_file_content "chnroute6")" + + if [ -n "$chnroute" ] ; then + ipset -! restore <<-EOF 2>/dev/null + $(echo "$chnroute" | sed "s/.*/add $IPSET_DST_DIRECT_V4 & timeout 0/") + EOF + fi + + if [ -n "$chnroute6" ] ; then + ipset -! restore <<-EOF 2>/dev/null + $(echo "$chnroute6" | sed "s/.*/add $IPSET_DST_DIRECT_V6 & timeout 0/") + EOF + fi + + ext_args= + ;; + "cn_proxy") + local chnroute="$(get_file_content "chnroute")" + local chnroute6="$(get_file_content "chnroute6")" + + if [ -n "$chnroute" ] ; then + ipset -! restore <<-EOF 2>/dev/null + $(echo "$chnroute" | sed "s/.*/add $IPSET_DST_PROXY_V4 & timeout 0/") + EOF + fi + + if [ -n "$chnroute6" ] ; then + ipset -! restore <<-EOF 2>/dev/null + $(echo "$chnroute6" | sed "s/.*/add $IPSET_DST_PROXY_V6 & timeout 0/") + EOF + fi + + ext_args="-m set --match-set $IPSET_DST_PROXY_V4 dst" + ;; + "gfwlist_proxy") + local gfwlist="$(get_file_content "gfwlist")" + + if [ -n "$gfwlist" ] ; then + if [ -n "$proxy_list_dns" ] ; then + echo "$gfwlist" | \ + sed "s|.*|server=/&/$proxy_list_dns\nipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ + >> "$FILE_XRAY_DNSMASQ_CACHE" + else + echo "$gfwlist" | \ + sed "s|.*|ipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ + >> "$FILE_XRAY_DNSMASQ_CACHE" + fi + fi + + ext_args="-m set --match-set $IPSET_DST_PROXY_V4 dst" + ;; + *) + ext_args= + ;; + esac + + if [ "x$only_privileged_ports" = "x1" ] ; then + ext_args="--dport 0:1023 $ext_args" + fi + + if [ -f $FILE_XRAY_DNSMASQ_CACHE ] && [ -n "$(cat "$FILE_XRAY_DNSMASQ_CACHE" | grep -v "^$" | grep -v "^#")" ] ; then + local dir="$(dirname "$FILE_XRAY_DNSMASQ")" + test -d "$dir" || mkdir -p "$dir" + cat "$FILE_XRAY_DNSMASQ_CACHE" >"$FILE_XRAY_DNSMASQ" 2>/dev/null + DNSMASQ_RESTART_EXPECTED=1 + fi + + rm -f "$FILE_XRAY_DNSMASQ_CACHE" + + local lan_devices lan_ipaddrs + + if [ -n "$lan_ifaces" ] ; then + . /lib/functions/network.sh + + local lan + for lan in $lan_ifaces ; do + local device ipaddrs ipaddr + network_get_device device "$lan" + network_get_ipaddrs ipaddrs "$lan" + + if [ -n "$device" ] ; then + if [ -n "$lan_devices" ] ; then + lan_devices="$lan_devices $device" + else + lan_devices="$device" + fi + fi + + if [ -n "$ipaddrs" ] ; then + for ipaddr in $ipaddrs ; do + if [ -n "$lan_ipaddrs" ] ; then + lan_ipaddrs="$ipaddr" + else + lan_ipaddrs="$lan_ipaddrs $ipaddr" + fi + done + fi + done + fi + + logger -t "xray" "add rules" + add_xray_redirect_rules "$ext_args" "$lan_devices" "$lan_ipaddrs" +} + +clear_transparent_proxy() { + if [ -f "${xrrules_nft}" ] || [ -f "${xrrules_nft}.down" ]; then + rm -f "$xrrules_nft" + rm -f "$xrrules_nft.down" + fw4 restart + fi + + + if [ -s "$FILE_XRAY_DNSMASQ" ] ; then + rm -f "$FILE_XRAY_DNSMASQ" + DNSMASQ_RESTART_EXPECTED=1 + fi +} + +start_instance() { + local section="$1" + + if ! xray_section_validate "$section" ; then + _err "Invalid config." + return 1 + fi + + if [ "x$enabled" != "x1" ] ; then + #_info "Service disabled: $section" + return 0 + fi + + if [ -z "$xray_file" ] || [ ! -s "$xray_file" ] ; then + _err "Invalid xRay file." + return 1 + fi + + test -x "$xray_file" || chmod 755 "$xray_file" + + local temp_config + + if [ -n "$config_file" ] ; then + if [ ! -s "$config_file" ] ; then + _err "Config file not found: $config_file" + return 1 + fi + + if ! ( eval "$xray_file test -config \"$config_file\" >/dev/null 2>&1" ) ; then + _err "Validate config file failed: $config_file" + return 1 + fi + + local file_content="$(cat "$config_file")" + local config_commands="$(get_commands_from_json "$file_content")" + + local addr + + for addr in $(echo "$config_commands" | sed -n "s/^json.*'address'[[:space:]]'\([^']*\)'.*/\1/p") ; do + append_server_address "$addr" + done + + temp_config="$config_file" + else + test -d "$CONFIG_FOLDER" || mkdir -p "$CONFIG_FOLDER" + + temp_config="$CONFIG_FOLDER/xray.${section}.json" + + local old_ns + json_set_namespace "$section" old_ns + json_init + + json_add_object "log" + + test -n "$access_log" && \ + json_add_string "access" "$access_log" + + if [ -n "$loglevel" ] && [ "$loglevel" != "none" ] ; then + json_add_string "loglevel" "$loglevel" + json_add_string "error" "$error_log" + fi + + json_close_object # log + + if [ "x$stats_enabled" = "x1" ] ; then + json_add_object "stats" + json_close_object # stats + fi + + add_dns_settings "$section" + add_routing_settings "$section" + add_policy_settings "$section" + add_reverse_settings "$section" + + if [ "x$transport_enabled" = "x1" ] ; then + add_transport_settings + fi + + if [ -n "$inbounds" ] ; then + json_add_array "inbounds" + + local is + for is in $inbounds ; do + add_inbound_setting "$is" + done + + json_close_array # inbounds + fi + + if [ -n "$outbounds" ] ; then + json_add_array "outbounds" + + local os + for os in $outbounds ; do + add_outbound_setting "$os" + done + + json_close_array # outbounds + fi + + json_dump -i >"$temp_config" + + json_cleanup + json_set_namespace "$old_ns" + + if [ ! -s "$temp_config" ] ; then + _err "Error when create config file: $temp_config" + return 1 + fi + fi + + PROG="$NAME.$section" + + TRANSPARENT_PROXY_EXPECTED=1 + procd_open_instance "$PROG" + procd_set_param command "$xray_file" + procd_append_param command run + procd_append_param command -config "$temp_config" + procd_set_param respawn + + if [ -n "$asset_location" ] && [ -d "$asset_location" ] ; then + procd_set_param env XRAY_LOCATION_ASSET="$asset_location" + fi + + # cat /proc/PID/limits to see if limits works + procd_set_param limits nofile="102400 102400" + procd_append_param limits core="0 0" + + if [ "$mem_percentage" -gt "0" ] ; then + local mem_total="$(awk '/MemTotal/ {print $2}' /proc/meminfo)" + if [ -n "$mem_total" ] ; then + local use_mem="$(expr $mem_total \* $mem_percentage \* 10)" + procd_append_param limits as="$use_mem $use_mem" + fi + fi + + procd_set_param file "$temp_config" + procd_set_param stderr 1 # forward stderr of the command to logd + procd_set_param stdout 1 + procd_set_param pidfile "/var/run/${NAME}.${section}.pid" + procd_close_instance +} + +rules_exist() { + #[ -n "$(iptables -w -t nat -L -n 2>/dev/null | grep xr_)" ] && return 0 + if [ -f "$xrrules_nft" ]; then + return 0 + else + return 1 + fi +} + +rules_up() { + if [ -f "${xrrules_nft}.down" ]; then + mv -f "${xrrules_nft}.down" "$xrrules_nft" + fw4 restart + fi +} + +rules_down() { + if [ -f "${xrrules_nft}" ]; then + mv -f "$xrrules_nft" "${xrrules_nft}.down" + fw4 restart + fi +} + + +start_service() { + clear_transparent_proxy + + config_load "$NAME" + + if ! init_transparent_proxy ; then + gracefully_restart_dnsmasq + return 1 + fi + + config_foreach start_instance "xray" + + setup_transparent_proxy + gracefully_restart_dnsmasq + rules_up + + unset OUTBOUND_SERVERS_V4 \ + OUTBOUND_SERVERS_V6 \ + TRANSPARENT_PROXY_EXPECTED \ + TRANSPARENT_PROXY_PORT \ + TRANSPARENT_PROXY_ADDITION \ + DNSMASQ_RESTART_EXPECTED +} + +stop_service() { + if [ "x$action" = "xrestart" ] ; then + # skip when restarting, start_service will do this + return 0 + fi + + clear_transparent_proxy + rules_down + gracefully_restart_dnsmasq + test -d "$CONFIG_FOLDER" && rm -rf "$CONFIG_FOLDER" +} + +service_triggers() { + procd_add_reload_trigger "$NAME" +} diff --git a/xray-core/files/usr/share/xray-rules/chain.uc b/xray-core/files/usr/share/xray-rules/chain.uc new file mode 100644 index 000000000..cb46ba5e8 --- /dev/null +++ b/xray-core/files/usr/share/xray-rules/chain.uc @@ -0,0 +1,126 @@ +{% +function get_local_verdict() { + let v = o_local_default; + if (v == "checkdst") { + return "goto xr_rules_dst_" + proto; + } else if (v == "forward") { + return "goto xr_rules_forward_" + proto; + } else { + return null; + } +} + +function get_src_default_verdict() { + let v = o_src_default; + if (v == "checkdst") { + return "goto xr_rules_dst_" + proto; + } else if (v == "forward") { + return "goto xr_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_dst_default_verdict() { + let v = o_dst_default; + if (v == "forward") { + return "goto xr_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_ifnames() { + let res = []; + for (let ifname in split(o_ifnames, /[ \t\n]/)) { + ifname = trim(ifname); + if (ifname) push(res, ifname); + } + return res; +} + +let type, hook, priority, redir_port; +if (proto == "tcp") { + type = "nat"; + hook = "prerouting"; + priority = -1; + redir_port = o_redir_tcp_port; +} else if (proto == "udp") { + type = "filter"; + hook = "prerouting"; + priority = "mangle"; + redir_port = o_redir_udp_port; + if (system(" + set -o errexit + iprr() { + while ip $1 rule del fwmark 1 lookup 100 2>/dev/null; do true; done + ip $1 rule add fwmark 1 lookup 100 + ip $1 route flush table 100 2>/dev/null || true + ip $1 route add local default dev lo table 100 + } + iprr -4 + iprr -6 + ") != 0) { + return ; + } +} else { + return; +} + +%} +{% if (redir_port): %} + +chain xr_rules_pre_{{ proto }} { + type {{ type }} hook {{ hook }} priority {{ priority }}; + meta l4proto {{ proto }}{%- let ifnames=get_ifnames(); if (length(ifnames)): %} iifname { {{join(", ", ifnames)}} }{% endif %} goto xr_rules_pre_src_{{ proto }}; +} + +chain xr_rules_pre_src_{{ proto }} { + ip daddr @xr_rules_dst_bypass_ accept; + ip6 daddr @xr_rules6_dst_bypass_ accept; + goto xr_rules_src_{{ proto }}; +} + +chain xr_rules_src_{{ proto }} { + ip saddr @xr_rules_src_bypass accept; + ip saddr @xr_rules_src_forward goto xr_rules_forward_{{ proto }}; + ip saddr @xr_rules_src_checkdst goto xr_rules_dst_{{ proto }}; + ip6 saddr @xr_rules6_src_bypass accept; + ip6 saddr @xr_rules6_src_forward goto xr_rules_forward_{{ proto }}; + ip6 saddr @xr_rules6_src_checkdst goto xr_rules_dst_{{ proto }}; + {{ get_src_default_verdict() }}; +} + +chain xr_rules_dst_{{ proto }} { + ip daddr @xr_rules_dst_bypass accept; + ip daddr @xr_rules_remote_servers accept; + ip daddr @xr_rules_dst_forward goto xr_rules_forward_{{ proto }}; + ip6 daddr @xr_rules6_dst_bypass accept; + ip6 daddr @xr_rules6_remote_servers accept; + ip6 daddr @xr_rules6_dst_forward goto xr_rules_forward_{{ proto }}; + {{ get_dst_default_verdict() }}; +} + +{% if (proto == "tcp"): %} +chain xr_rules_forward_{{ proto }} { + meta l4proto tcp {{ o_nft_tcp_extra }} redirect to :{{ redir_port }}; +} +{% let local_verdict = get_local_verdict(); if (local_verdict): %} +chain xr_rules_local_out { + type {{ type }} hook output priority -1; + meta l4proto != tcp accept; + ip daddr @xr_rules_remote_servers accept; + ip daddr @xr_rules_dst_bypass_ accept; + ip daddr @xr_rules_dst_bypass accept; + ip6 daddr @xr_rules6_remote_servers accept; + ip6 daddr @xr_rules6_dst_bypass_ accept; + ip6 daddr @xr_rules6_dst_bypass accept; + {{ local_verdict }}; +} +{% endif %} +{% elif (proto == "udp"): %} +chain xr_rules_forward_{{ proto }} { + meta l4proto udp {{ o_nft_udp_extra }} meta mark set 1 tproxy to :{{ redir_port }}; +} +{% endif %} +{% endif %} diff --git a/xray-core/files/usr/share/xray-rules/set.uc b/xray-core/files/usr/share/xray-rules/set.uc new file mode 100644 index 000000000..7e36d3d88 --- /dev/null +++ b/xray-core/files/usr/share/xray-rules/set.uc @@ -0,0 +1,117 @@ +{% +let fs = require("fs"); + +let o_dst_bypass4_ = " + 0.0.0.0/8 + 10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.0/8 + 169.254.0.0/16 + 172.16.0.0/12 + 192.0.0.0/24 + 192.0.2.0/24 + 192.168.0.0/16 + 192.31.196.0/24 + 192.52.193.0/24 + 192.88.99.0/24 + 192.175.48.0/24 + 198.18.0.0/15 + 198.51.100.0/24 + 203.0.113.0/24 + 224.0.0.0/4 + 240.0.0.0/4 +"; +let o_dst_bypass6_ = " + ::1/128 + ::/128 + ::ffff:0:0/96 + 64:ff9b:1::/48 + 100::/64 + fe80::/10 + 2001::/23 + fc00::/7 +"; +let o_dst_bypass_ = o_dst_bypass4_ + " " + o_dst_bypass6_; + +let set_suffix = { + "src_bypass": { + str: o_src_bypass, + }, + "src_forward": { + str: o_src_forward, + }, + "src_checkdst": { + str: o_src_checkdst, + }, + "remote_servers": { + str: o_remote_servers + }, + "dst_bypass": { + str: o_dst_bypass, + file: o_dst_bypass_file, + }, + "dst_bypass_": { + str: o_dst_bypass_, + }, + "dst_forward": { + str: o_dst_forward, + file: o_dst_forward_file, + }, + "dst_forward_rrst_": {}, +}; + +function set_name(suf, af) { + if (af == 4) { + return "xr_rules_"+suf; + } else { + return "xr_rules6_"+suf; + } +} + +function set_elements_parse(res, str, af) { + for (let addr in split(str, /[ \t\n]/)) { + addr = trim(addr); + if (!addr) continue; + if (af == 4 && index(addr, ":") != -1) continue; + if (af == 6 && index(addr, ":") == -1) continue; + push(res, addr); + } +} + +function set_elements(suf, af) { + let obj = set_suffix[suf]; + let res = []; + let addr; + + let str = obj["str"]; + if (str) { + set_elements_parse(res, str, af); + } + + let file = obj["file"]; + if (file) { + let fd = fs.open(file); + if (fd) { + str = fd.read("all"); + set_elements_parse(res, str, af); + } + } + + return res; +} +%} + +{% for (let suf in set_suffix): for (let af in [4, 6]): %} +set {{ set_name(suf, af) }} { + type ipv{{af}}_addr; + flags interval; + auto-merge; +{% let elems = set_elements(suf, af); if (length(elems)): %} + elements = { +{% for (let i = 0; i < length(elems); i++): %} + {{ elems[i] }}{% if (i < length(elems) - 1): %},{% endif %}{% print("\n") %} +{% endfor %} + } +{% endif %} +} +{% endfor; endfor %} diff --git a/xray-core/files/usr/share/xray-rules/xr-rules.uc b/xray-core/files/usr/share/xray-rules/xr-rules.uc new file mode 100644 index 000000000..f3955b2ef --- /dev/null +++ b/xray-core/files/usr/share/xray-rules/xr-rules.uc @@ -0,0 +1,8 @@ +{% + +include("set.uc"); +include("chain.uc", {proto: "tcp"}); +include("chain.uc", {proto: "udp"}); + +%} +