#!/bin/sh -e # # Copyright (C) 2017 Yousong Zhou # Copyright (C) 2018-2021 Ycarus (Yannick Chabanois) # # The design idea was derived from ss-rules by Jian Chang # # This is free software, licensed under the GNU General Public License v3. # See /LICENSE for more information. # ss_rules6_usage() { cat >&2 < Local port number of ss-redir with TCP mode -L Local port number of ss-redir with UDP mode -s List of ip addresses of remote shadowsocks server --ifnames Only apply rules on packets from these ifnames --src-bypass --src-forward --src-checkdst --src-default Packets will have their src ip checked in order against bypass, forward, checkdst list and will bypass, forward through, or continue to have their dst ip checked respectively on the first match. Otherwise, --src-default decide the default action --dst-bypass --dst-forward --dst-bypass-file --dst-forward-file --dst-default Same as with their --src-xx equivalent --dst-forward-recentrst Forward those packets whose destinations have recently sent to us multiple tcp-rst packets --local-default Default action for local out TCP traffic The following ipsets will be created by ss-rules. They are also intended to be populated by other programs like dnsmasq with ipset support ss_rules6_src_bypass ss_rules6_src_forward ss_rules6_src_checkdst ss_rules6_dst_bypass ss_rules6_dst_forward EOF } o_dst_bypass_=" fe80::/10 fd00::/8 ::1 " o_src_default=bypass o_dst_default=bypass o_local_default=bypass __errmsg() { echo "ss-rules6: $*" >&2 } ss_rules6_parse_args() { while [ "$#" -gt 0 ]; do case "$1" in -h|--help) ss_rules6_usage; exit 0;; -f|--flush) ss_rules6_flush; exit 0;; -l) o_redir_tcp_port="$2"; shift 2;; -L) o_redir_udp_port="$2"; shift 2;; -s) o_remote_servers="$2"; shift 2;; --ifnames) o_ifnames="$2"; shift 2;; --ipt-extra) o_ipt_extra="$2"; shift 2;; --src-default) o_src_default="$2"; shift 2;; --dst-default) o_dst_default="$2"; shift 2;; --local-default) o_local_default="$2"; shift 2;; --src-bypass) o_src_bypass="$2"; shift 2;; --src-forward) o_src_forward="$2"; shift 2;; --src-checkdst) o_src_checkdst="$2"; shift 2;; --dst-bypass) o_dst_bypass="$2"; shift 2;; --dst-bypass_all) o_dst_bypass_all="$2"; shift 2;; --dst-forward) o_dst_forward="$2"; shift 2;; --dst-forward-recentrst) o_dst_forward_recentrst=1; shift 1;; --dst-bypass-file) o_dst_bypass_file="$2"; shift 2;; --dst-forward-file) o_dst_forward_file="$2"; shift 2;; --rule-name) rule="$2"; shift 2;; *) __errmsg "unknown option $1"; return 1;; esac done if [ -z "$o_redir_tcp_port" -a -z "$o_redir_udp_port" ]; then __errmsg "Requires at least -l or -L option" return 1 fi if [ -n "$o_dst_forward_recentrst" ] && ! ip6tables -m recent -h >/dev/null; then __errmsg "Please install ip6tables-mod-conntrack-extra with opkg" return 1 fi o_remote_servers="$(for s in $o_remote_servers; do resolveip -6 "$s"; done)" } ss_rules6_flush() { local setname ip6tables-save --counters | grep -v ssr6_ | ip6tables-restore -w --counters while ip -f inet6 rule del fwmark 1 lookup 100 2>/dev/null; do true; done ip -f inet6 route flush table 100 || true for setname in $(ipset -n list | grep "ssr6_${rule}"); do ipset destroy "$setname" 2>/dev/null || true done } ss_rules6_ipset_init() { ipset --exist restore <<-EOF create ssr6_${rule}_src_bypass hash:net family inet6 hashsize 64 create ssr6_${rule}_src_forward hash:net family inet6 hashsize 64 create ssr6_${rule}_src_checkdst hash:net family inet6 hashsize 64 create ssr6_${rule}_dst_bypass hash:net family inet6 hashsize 64 create ss_rules6_dst_bypass_all hash:net family inet6 hashsize 64 create ssr6_${rule}_dst_bypass_ hash:net family inet6 hashsize 64 create ssr6_${rule}_dst_forward hash:net family inet6 hashsize 64 create ssr6_${rule}_dst_forward_recrst_ hash:ip family inet6 hashsize 64 timeout 3600 $(ss_rules6_ipset_mkadd ssr6_${rule}_dst_bypass_ "$o_dst_bypass_ $o_remote_servers") $(ss_rules6_ipset_mkadd ss_rules6_dst_bypass_all "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null | grep -o '\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}')") $(ss_rules6_ipset_mkadd ssr6_${rule}_dst_bypass "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null | grep -o '\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}')") $(ss_rules6_ipset_mkadd ssr6_${rule}_src_bypass "$o_src_bypass") $(ss_rules6_ipset_mkadd ssr6_${rule}_src_forward "$o_src_forward") $(ss_rules6_ipset_mkadd ssr6_${rule}_src_checkdst "$o_src_checkdst") $(ss_rules6_ipset_mkadd ssr6_${rule}_dst_forward "$o_dst_forward $(cat "$o_dst_forward_file" 2>/dev/null | grep -o '\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}')") EOF } ss_rules6_ipset_mkadd() { local setname="$1"; shift local i for i in $*; do echo "add $setname $i" done } ss_rules6_iptchains_init() { ss_rules6_iptchains_init_mark ss_rules6_iptchains_init_tcp ss_rules6_iptchains_init_udp } ss_rules6_iptchains_init_mark() { ip6tables-restore -w --noflush <<-EOF *mangle -A PREROUTING -m set --match-set ss_rules6_dst_bypass_all dst -j MARK --set-mark 0x6539 COMMIT EOF } ss_rules6_iptchains_init_tcp() { local local_target [ -n "$o_redir_tcp_port" ] || return 0 ss_rules6_iptchains_init_ nat tcp case "$o_local_default" in checkdst) local_target=ssr6_${rule}_dst ;; forward) local_target=ssr6_${rule}_forward ;; bypass|*) return 0;; esac ip6tables-restore -w --noflush <<-EOF *nat :ssr6_${rule}_local_out - -I OUTPUT 1 -p tcp -j ssr6_${rule}_local_out -A ssr6_${rule}_local_out -m set --match-set ssr6_${rule}_dst_bypass dst -j RETURN -A ssr6_${rule}_local_out -m set --match-set ssr6_${rule}_dst_bypass_all dst -j RETURN -A ssr6_${rule}_local_out -m set --match-set ssr6_${rule}_dst_bypass_ dst -j RETURN -A ssr6_${rule}_local_out -m mark --mark 0x6539 -j RETURN -A ssr6_${rule}_local_out -p tcp $o_ipt_extra -j $local_target -m comment --comment "local_default: $o_local_default" COMMIT EOF } ss_rules6_iptchains_init_udp() { [ -n "$o_redir_udp_port" ] || return 0 ss_rules6_iptchains_init_ mangle udp } ss_rules6_iptchains_init_() { local table="$1" local proto="$2" local forward_rules local src_default_target dst_default_target local recentrst_mangle_rules recentrst_addset_rules case "$proto" in tcp) forward_rules="-A ssr6_${rule}_forward -p tcp -j REDIRECT --to-ports $o_redir_tcp_port" if [ -n "$o_dst_forward_recentrst" ]; then recentrst_mangle_rules=" *mangle -I PREROUTING 1 -p tcp -m tcp --tcp-flags RST RST -m recent --name ss_rules6_recentrst --set --rsource COMMIT " recentrst_addset_rules=" -A ssr6_${rule}_dst -m recent --name ss_rules6_recentrst --rcheck --rdest --seconds 3 --hitcount 3 -j SET --add-set ss_rules6_dst_forward_recrst_ dst --exist -A ssr6_${rule}_dst -m set --match-set ss_rules6_dst_forward_recrst_ dst -j ssr6_${rule}_forward " fi ;; udp) ip -f inet6 rule add fwmark 1 lookup 100 || true ip -f inet6 route add local default dev lo table 100 || true forward_rules="-A ssr6_${rule}_forward -p udp -j TPROXY --on-port "$o_redir_udp_port" --tproxy-mark 0x01/0x01" ;; esac case "$o_src_default" in forward) src_default_target=ssr6_${rule}_forward ;; checkdst) src_default_target=ssr6_${rule}_dst ;; bypass|*) src_default_target=RETURN ;; esac case "$o_dst_default" in forward) dst_default_target=ssr6_${rule}_forward ;; bypass|*) dst_default_target=RETURN ;; esac sed -e '/^\s*$/d' -e 's/^\s\+//' <<-EOF | ip6tables-restore -w --noflush *$table :ssr6_${rule}_pre_src - :ssr6_${rule}_src - :ssr6_${rule}_dst - :ssr6_${rule}_forward - $(ss_rules6_iptchains_mkprerules "$proto") -A ssr6_${rule}_pre_src -m set --match-set ssr6_${rule}_dst_bypass_ dst -j RETURN -A ssr6_${rule}_pre_src -m set --match-set ss_rules6_dst_bypass_all dst -j MARK --set-mark 0x6539 -A ssr6_${rule}_pre_src -m set --match-set ss_rules6_dst_bypass_all dst -j RETURN -A ssr6_${rule}_pre_src -m set --match-set ssr6_${rule}_dst_bypass dst -j RETURN -A ssr6_${rule}_pre_src -m mark --mark 0x6539 -j RETURN -A ssr6_${rule}_dst -m set --match-set ss_rules6_dst_bypass_all dst -j RETURN -A ssr6_${rule}_dst -m set --match-set ssr6_${rule}_dst_bypass dst -j RETURN -A ssr6_${rule}_pre_src -p $proto $o_ipt_extra -j ssr6_${rule}_src -A ssr6_${rule}_src -m set --match-set ssr6_${rule}_src_bypass src -j RETURN -A ssr6_${rule}_src -m set --match-set ssr6_${rule}_src_forward src -j ssr6_${rule}_forward -A ssr6_${rule}_src -m set --match-set ssr6_${rule}_src_checkdst src -j ssr6_${rule}_dst -A ssr6_${rule}_src -j $src_default_target -m comment --comment "src_default: $o_src_default" -A ssr6_${rule}_dst -m set --match-set ssr6_${rule}_dst_forward dst -j ssr6_${rule}_forward $recentrst_addset_rules -A ssr6_${rule}_dst -j $dst_default_target -m comment --comment "dst_default: $o_dst_default" $forward_rules COMMIT $recentrst_mangle_rules EOF } ss_rules6_iptchains_mkprerules() { local proto="$1" if [ -z "$o_ifnames" ]; then echo "-I PREROUTING 1 -p $proto -j ssr6_${rule}_pre_src" else echo $o_ifnames \ | tr ' ' '\n' \ | sed "s/.*/-I PREROUTING 1 -i \\0 -p $proto -j ssr6_${rule}_pre_src/" fi } ss_rules6_parse_args "$@" ss_rules6_flush ss_rules6_ipset_init ss_rules6_iptchains_init