3 # Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
5 # The design idea was derived from ss-rules by Jian Chang <aa65535@live.com>
7 # This is free software, licensed under the GNU General Public License v3.
8 # See /LICENSE for more information.
12 echo "ss-rules: $*" >&2
15 if [ "$1" = "-6" ]; then
16 if ! ip6tables
-t nat
-L -n &>/dev
/null
; then
17 __errmsg
"Skipping ipv6. Requires ip6tables-mod-nat"
25 Usage: ss-rules [options]
27 -6 Operate on address family IPv6
28 When present, must be the first argument
29 -h, --help Show this help message then exit
30 -f, --flush Flush rules, ipset then exit
31 -l <port> Local port number of ss-redir with TCP mode
32 -L <port> Local port number of ss-redir with UDP mode
33 -s <ips> List of ip addresses of remote shadowsocks server
34 --ifnames Only apply rules on packets from these ifnames
35 --src-bypass <ips|cidr>
36 --src-forward <ips|cidr>
37 --src-checkdst <ips|cidr>
38 --src-default <bypass|forward|checkdst>
39 Packets will have their src ip checked in order against
40 bypass, forward, checkdst list and will bypass, forward
41 through, or continue to have their dst ip checked
42 respectively on the first match. Otherwise, --src-default
43 decide the default action
44 --dst-bypass <ips|cidr>
45 --dst-forward <ips|cidr>
46 --dst-bypass-file <file>
47 --dst-forward-file <file>
48 --dst-default <bypass|forward>
49 Same as with their --src-xx equivalent
50 --dst-forward-recentrst
51 Forward those packets whose destinations have recently
52 sent to us multiple tcp-rst packets
53 --local-default <bypass|forward|checkdst>
54 Default action for local out TCP traffic
56 The following ipsets will be created by ss-rules. They are also intended to be
57 populated by other programs like dnsmasq with ipset support
102 o_local_default
=bypass
104 alias grep_af
="sed -ne '/:/!p'"
105 o_dst_bypass_
="$o_dst_bypass4_"
106 if [ -n "$o_use_ipv6" ]; then
107 alias grep_af
="sed -ne /:/p"
108 alias iptables
=ip6tables
109 alias iptables-save
=ip6tables-save
110 alias iptables-restore
=ip6tables-restore
113 o_dst_bypass_
="$o_dst_bypass6_"
116 ss_rules_parse_args
() {
117 while [ "$#" -gt 0 ]; do
119 -h|
--help) ss_rules_usage
; exit 0;;
120 -f|
--flush) ss_rules_flush
; exit 0;;
121 -l) o_redir_tcp_port
="$2"; shift 2;;
122 -L) o_redir_udp_port
="$2"; shift 2;;
123 -s) o_remote_servers
="$2"; shift 2;;
124 --ifnames) o_ifnames
="$2"; shift 2;;
125 --ipt-extra) o_ipt_extra
="$2"; shift 2;;
126 --src-default) o_src_default
="$2"; shift 2;;
127 --dst-default) o_dst_default
="$2"; shift 2;;
128 --local-default) o_local_default
="$2"; shift 2;;
129 --src-bypass) o_src_bypass
="$2"; shift 2;;
130 --src-forward) o_src_forward
="$2"; shift 2;;
131 --src-checkdst) o_src_checkdst
="$2"; shift 2;;
132 --dst-bypass) o_dst_bypass
="$2"; shift 2;;
133 --dst-forward) o_dst_forward
="$2"; shift 2;;
134 --dst-forward-recentrst) o_dst_forward_recentrst
=1; shift 1;;
135 --dst-bypass-file) o_dst_bypass_file
="$2"; shift 2;;
136 --dst-forward-file) o_dst_forward_file
="$2"; shift 2;;
137 *) __errmsg
"unknown option $1"; return 1;;
141 if [ -z "$o_redir_tcp_port" -a -z "$o_redir_udp_port" ]; then
142 __errmsg
"Requires at least -l or -L option"
145 if [ -n "$o_dst_forward_recentrst" ] && ! iptables
-m recent
-h >/dev
/null
; then
146 __errmsg
"Please install iptables-mod-conntrack-extra"
149 o_remote_servers
="$(for s in $o_remote_servers; do resolveip "$s" | grep_af; done)"
155 iptables-save
--counters |
grep -v ss_rules_ | iptables-restore
--counters
156 while ip rule del fwmark
1 lookup
100 2>/dev
/null
; do true
; done
157 ip route flush table
100 2>/dev
/null || true
158 for setname
in $
(ipset
-n list |
grep "ss_rules${o_af}_"); do
159 ipset destroy
"$setname" 2>/dev
/null || true
163 ss_rules_ipset_init
() {
164 ipset
--exist restore
<<-EOF
165 create ss_rules${o_af}_src_bypass hash:net family inet$o_af hashsize 64
166 create ss_rules${o_af}_src_forward hash:net family inet$o_af hashsize 64
167 create ss_rules${o_af}_src_checkdst hash:net family inet$o_af hashsize 64
168 create ss_rules${o_af}_dst_bypass hash:net family inet$o_af hashsize 64
169 create ss_rules${o_af}_dst_bypass_ hash:net family inet$o_af hashsize 64
170 create ss_rules${o_af}_dst_forward hash:net family inet$o_af hashsize 64
171 create ss_rules${o_af}_dst_forward_rrst_ hash:ip family inet$o_af hashsize 8 timeout 3600
172 $(ss_rules_ipset_mkadd ss_rules${o_af}_dst_bypass_ "$o_dst_bypass_ $o_remote_servers")
173 $(ss_rules_ipset_mkadd ss_rules${o_af}_src_bypass "$o_src_bypass")
174 $(ss_rules_ipset_mkadd ss_rules${o_af}_src_forward "$o_src_forward")
175 $(ss_rules_ipset_mkadd ss_rules${o_af}_src_checkdst "$o_src_checkdst")
176 $(ss_rules_ipset_mkadd ss_rules${o_af}_dst_bypass "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null)")
177 $(ss_rules_ipset_mkadd ss_rules${o_af}_dst_forward "$o_dst_forward $(cat "$o_dst_forward_file" 2>/dev/null)")
181 ss_rules_ipset_mkadd
() {
182 local setname
="$1"; shift
186 echo "add $setname $i"
190 ss_rules_iptchains_init
() {
191 ss_rules_iptchains_init_tcp
192 ss_rules_iptchains_init_udp
195 ss_rules_iptchains_init_tcp
() {
198 [ -n "$o_redir_tcp_port" ] ||
return 0
200 ss_rules_iptchains_init_ nat tcp
202 case "$o_local_default" in
203 checkdst
) local_target
=ss_rules_dst
;;
204 forward
) local_target
=ss_rules_forward
;;
208 iptables-restore
--noflush <<-EOF
210 :ss_rules_local_out -
211 -I OUTPUT 1 -p tcp -j ss_rules_local_out
212 -A ss_rules_local_out -m set --match-set ss_rules${o_af}_dst_bypass_ dst -j RETURN
213 -A ss_rules_local_out $o_ipt_extra -j $local_target
218 ss_rules_iptchains_init_udp
() {
219 [ -n "$o_redir_udp_port" ] ||
return 0
220 ss_rules_iptchains_init_ mangle udp
223 ss_rules_iptchains_init_
() {
227 local src_default_target dst_default_target
228 local recentrst_mangle_rules recentrst_addset_rules
232 forward_rules
="-A ss_rules_forward -p tcp -j REDIRECT --to-ports $o_redir_tcp_port"
233 if [ -n "$o_dst_forward_recentrst" ]; then
234 recentrst_mangle_rules
="
236 -I PREROUTING 1 -p tcp -m tcp --tcp-flags RST RST -m recent --name ss_rules_recentrst --set --rsource
239 recentrst_addset_rules
="
240 -A ss_rules_dst -m recent --name ss_rules_recentrst --rcheck --rdest --seconds 3 --hitcount 3 -j SET --add-set ss_rules${o_af}_dst_forward_rrst_ dst --exist
241 -A ss_rules_dst -m set --match-set ss_rules${o_af}_dst_forward_rrst_ dst -j ss_rules_forward
246 ip rule add fwmark
1 lookup
100
247 ip route add
local default dev lo table
100
248 forward_rules
="-A ss_rules_forward -p udp -j TPROXY --on-port "$o_redir_udp_port" --tproxy-mark 0x01/0x01"
251 case "$o_src_default" in
252 forward
) src_default_target
=ss_rules_forward
;;
253 checkdst
) src_default_target
=ss_rules_dst
;;
254 bypass|
*) src_default_target
=RETURN
;;
256 case "$o_dst_default" in
257 forward
) dst_default_target
=ss_rules_forward
;;
258 bypass|
*) dst_default_target
=RETURN
;;
260 sed -e '/^\s*$/d' -e 's/^\s\+//' <<-EOF | iptables-restore --noflush
266 $(ss_rules_iptchains_mkprerules "$proto")
267 -A ss_rules_pre_src -m set --match-set ss_rules${o_af}_dst_bypass_ dst -j RETURN
268 -A ss_rules_pre_src $o_ipt_extra -j ss_rules_src
269 -A ss_rules_src -m set --match-set ss_rules${o_af}_src_bypass src -j RETURN
270 -A ss_rules_src -m set --match-set ss_rules${o_af}_src_forward src -j ss_rules_forward
271 -A ss_rules_src -m set --match-set ss_rules${o_af}_src_checkdst src -j ss_rules_dst
272 -A ss_rules_src -j $src_default_target
273 -A ss_rules_dst -m set --match-set ss_rules${o_af}_dst_bypass dst -j RETURN
274 -A ss_rules_dst -m set --match-set ss_rules${o_af}_dst_forward dst -j ss_rules_forward
275 $recentrst_addset_rules
276 -A ss_rules_dst -j $dst_default_target
279 $recentrst_mangle_rules
283 ss_rules_iptchains_mkprerules
() {
286 if [ -z "$o_ifnames" ]; then
287 echo "-I PREROUTING 1 -p $proto -j ss_rules_pre_src"
291 |
sed "s/.*/-I PREROUTING 1 -i \\0 -p $proto -j ss_rules_pre_src/"
295 ss_rules_parse_args
"$@"
298 ss_rules_iptchains_init