You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
9.0 KiB

  1. #!/bin/sh -e
  2. #
  3. # Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
  4. #
  5. # The design idea was derived from ss-rules by Jian Chang <aa65535@live.com>
  6. #
  7. # This is free software, licensed under the GNU General Public License v3.
  8. # See /LICENSE for more information.
  9. #
  10. __errmsg() {
  11. echo "ss-rules: $*" >&2
  12. }
  13. if [ "$1" = "-6" ]; then
  14. if ! ip6tables -t nat -L -n &>/dev/null; then
  15. __errmsg "Skipping ipv6. Requires ip6tables-mod-nat"
  16. exit 1
  17. fi
  18. o_use_ipv6=1; shift
  19. fi
  20. ss_rules_usage() {
  21. cat >&2 <<EOF
  22. Usage: ss-rules [options]
  23. -6 Operate on address family IPv6
  24. When present, must be the first argument
  25. -h, --help Show this help message then exit
  26. -f, --flush Flush rules, ipset then exit
  27. -l <port> Local port number of ss-redir with TCP mode
  28. -L <port> Local port number of ss-redir with UDP mode
  29. -s <ips> List of ip addresses of remote shadowsocks server
  30. --ifnames Only apply rules on packets from these ifnames
  31. --src-bypass <ips|cidr>
  32. --src-forward <ips|cidr>
  33. --src-checkdst <ips|cidr>
  34. --src-default <bypass|forward|checkdst>
  35. Packets will have their src ip checked in order against
  36. bypass, forward, checkdst list and will bypass, forward
  37. through, or continue to have their dst ip checked
  38. respectively on the first match. Otherwise, --src-default
  39. decide the default action
  40. --dst-bypass <ips|cidr>
  41. --dst-forward <ips|cidr>
  42. --dst-bypass-file <file>
  43. --dst-forward-file <file>
  44. --dst-default <bypass|forward>
  45. Same as with their --src-xx equivalent
  46. --dst-forward-recentrst
  47. Forward those packets whose destinations have recently
  48. sent to us multiple tcp-rst packets
  49. --local-default <bypass|forward|checkdst>
  50. Default action for local out TCP traffic
  51. The following ipsets will be created by ss-rules. They are also intended to be
  52. populated by other programs like dnsmasq with ipset support
  53. ss_rules_src_bypass
  54. ss_rules_src_forward
  55. ss_rules_src_checkdst
  56. ss_rules_dst_bypass
  57. ss_rules_dst_forward
  58. EOF
  59. }
  60. o_dst_bypass4_="
  61. 0.0.0.0/8
  62. 10.0.0.0/8
  63. 100.64.0.0/10
  64. 127.0.0.0/8
  65. 169.254.0.0/16
  66. 172.16.0.0/12
  67. 192.0.0.0/24
  68. 192.0.2.0/24
  69. 192.31.196.0/24
  70. 192.52.193.0/24
  71. 192.88.99.0/24
  72. 192.168.0.0/16
  73. 192.175.48.0/24
  74. 198.18.0.0/15
  75. 198.51.100.0/24
  76. 203.0.113.0/24
  77. 224.0.0.0/4
  78. 240.0.0.0/4
  79. 255.255.255.255
  80. "
  81. o_dst_bypass6_="
  82. ::1/128
  83. ::/128
  84. ::ffff:0:0/96
  85. 64:ff9b:1::/48
  86. 100::/64
  87. 2001:2::/48
  88. 2001:db8::/32
  89. fe80::/10
  90. 2001::/23
  91. fc00::/7
  92. "
  93. o_src_default=bypass
  94. o_dst_default=bypass
  95. o_local_default=bypass
  96. alias grep_af="sed -ne '/:/!p'"
  97. o_dst_bypass_="$o_dst_bypass4_"
  98. if [ -n "$o_use_ipv6" ]; then
  99. alias grep_af="sed -ne /:/p"
  100. alias iptables=ip6tables
  101. alias iptables-save=ip6tables-save
  102. alias iptables-restore=ip6tables-restore
  103. alias ip="ip -6"
  104. o_af=6
  105. o_dst_bypass_="$o_dst_bypass6_"
  106. fi
  107. ss_rules_parse_args() {
  108. while [ "$#" -gt 0 ]; do
  109. case "$1" in
  110. -h|--help) ss_rules_usage; exit 0;;
  111. -f|--flush) ss_rules_flush; exit 0;;
  112. -l) o_redir_tcp_port="$2"; shift 2;;
  113. -L) o_redir_udp_port="$2"; shift 2;;
  114. -s) o_remote_servers="$2"; shift 2;;
  115. --ifnames) o_ifnames="$2"; shift 2;;
  116. --ipt-extra) o_ipt_extra="$2"; shift 2;;
  117. --src-default) o_src_default="$2"; shift 2;;
  118. --dst-default) o_dst_default="$2"; shift 2;;
  119. --local-default) o_local_default="$2"; shift 2;;
  120. --src-bypass) o_src_bypass="$2"; shift 2;;
  121. --src-forward) o_src_forward="$2"; shift 2;;
  122. --src-checkdst) o_src_checkdst="$2"; shift 2;;
  123. --dst-bypass) o_dst_bypass="$2"; shift 2;;
  124. --dst-forward) o_dst_forward="$2"; shift 2;;
  125. --dst-forward-recentrst) o_dst_forward_recentrst=1; shift 1;;
  126. --dst-bypass-file) o_dst_bypass_file="$2"; shift 2;;
  127. --dst-forward-file) o_dst_forward_file="$2"; shift 2;;
  128. *) __errmsg "unknown option $1"; return 1;;
  129. esac
  130. done
  131. if [ -z "$o_redir_tcp_port" -a -z "$o_redir_udp_port" ]; then
  132. __errmsg "Requires at least -l or -L option"
  133. return 1
  134. fi
  135. if [ -n "$o_dst_forward_recentrst" ] && ! iptables -m recent -h >/dev/null; then
  136. __errmsg "Please install iptables-mod-conntrack-extra"
  137. return 1
  138. fi
  139. o_remote_servers="$(for s in $o_remote_servers; do resolveip "$s" | grep_af; done)"
  140. }
  141. ss_rules_flush() {
  142. local setname
  143. iptables-save --counters | grep -v ss_rules_ | iptables-restore --counters
  144. while ip rule del fwmark 1 lookup 100 2>/dev/null; do true; done
  145. ip route flush table 100 2>/dev/null || true
  146. for setname in $(ipset -n list | grep "ss_rules${o_af}_"); do
  147. ipset destroy "$setname" 2>/dev/null || true
  148. done
  149. }
  150. ss_rules_ipset_init() {
  151. ipset --exist restore <<-EOF
  152. create ss_rules${o_af}_src_bypass hash:net family inet$o_af hashsize 64
  153. create ss_rules${o_af}_src_forward hash:net family inet$o_af hashsize 64
  154. create ss_rules${o_af}_src_checkdst hash:net family inet$o_af hashsize 64
  155. create ss_rules${o_af}_dst_bypass hash:net family inet$o_af hashsize 64
  156. create ss_rules${o_af}_dst_bypass_ hash:net family inet$o_af hashsize 64
  157. create ss_rules${o_af}_dst_forward hash:net family inet$o_af hashsize 64
  158. create ss_rules${o_af}_dst_forward_rrst_ hash:ip family inet$o_af hashsize 8 timeout 3600
  159. $(ss_rules_ipset_mkadd ss_rules${o_af}_dst_bypass_ "$o_dst_bypass_ $o_remote_servers")
  160. $(ss_rules_ipset_mkadd ss_rules${o_af}_src_bypass "$o_src_bypass")
  161. $(ss_rules_ipset_mkadd ss_rules${o_af}_src_forward "$o_src_forward")
  162. $(ss_rules_ipset_mkadd ss_rules${o_af}_src_checkdst "$o_src_checkdst")
  163. $(ss_rules_ipset_mkadd ss_rules${o_af}_dst_bypass "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null)")
  164. $(ss_rules_ipset_mkadd ss_rules${o_af}_dst_forward "$o_dst_forward $(cat "$o_dst_forward_file" 2>/dev/null)")
  165. EOF
  166. }
  167. ss_rules_ipset_mkadd() {
  168. local setname="$1"; shift
  169. local i
  170. for i in $*; do
  171. echo "add $setname $i"
  172. done | grep_af
  173. }
  174. ss_rules_iptchains_init() {
  175. ss_rules_iptchains_init_tcp
  176. ss_rules_iptchains_init_udp
  177. }
  178. ss_rules_iptchains_init_tcp() {
  179. local local_target
  180. [ -n "$o_redir_tcp_port" ] || return 0
  181. ss_rules_iptchains_init_ nat tcp
  182. case "$o_local_default" in
  183. checkdst) local_target=ss_rules_dst ;;
  184. forward) local_target=ss_rules_forward ;;
  185. bypass|*) return 0;;
  186. esac
  187. iptables-restore --noflush <<-EOF
  188. *nat
  189. :ss_rules_local_out -
  190. -I OUTPUT 1 -p tcp -j ss_rules_local_out
  191. -A ss_rules_local_out -m set --match-set ss_rules${o_af}_dst_bypass_ dst -j RETURN
  192. -A ss_rules_local_out $o_ipt_extra -j $local_target
  193. COMMIT
  194. EOF
  195. }
  196. ss_rules_iptchains_init_udp() {
  197. [ -n "$o_redir_udp_port" ] || return 0
  198. ss_rules_iptchains_init_ mangle udp
  199. }
  200. ss_rules_iptchains_init_() {
  201. local table="$1"
  202. local proto="$2"
  203. local forward_rules
  204. local src_default_target dst_default_target
  205. local recentrst_mangle_rules recentrst_addset_rules
  206. case "$proto" in
  207. tcp)
  208. forward_rules="-A ss_rules_forward -p tcp -j REDIRECT --to-ports $o_redir_tcp_port"
  209. if [ -n "$o_dst_forward_recentrst" ]; then
  210. recentrst_mangle_rules="
  211. *mangle
  212. -I PREROUTING 1 -p tcp -m tcp --tcp-flags RST RST -m recent --name ss_rules_recentrst --set --rsource
  213. COMMIT
  214. "
  215. recentrst_addset_rules="
  216. -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
  217. -A ss_rules_dst -m set --match-set ss_rules${o_af}_dst_forward_rrst_ dst -j ss_rules_forward
  218. "
  219. fi
  220. ;;
  221. udp)
  222. ip rule add fwmark 1 lookup 100
  223. ip route add local default dev lo table 100
  224. forward_rules="-A ss_rules_forward -p udp -j TPROXY --on-port "$o_redir_udp_port" --tproxy-mark 0x01/0x01"
  225. ;;
  226. esac
  227. case "$o_src_default" in
  228. forward) src_default_target=ss_rules_forward ;;
  229. checkdst) src_default_target=ss_rules_dst ;;
  230. bypass|*) src_default_target=RETURN ;;
  231. esac
  232. case "$o_dst_default" in
  233. forward) dst_default_target=ss_rules_forward ;;
  234. bypass|*) dst_default_target=RETURN ;;
  235. esac
  236. sed -e '/^\s*$/d' -e 's/^\s\+//' <<-EOF | iptables-restore --noflush
  237. *$table
  238. :ss_rules_pre_src -
  239. :ss_rules_src -
  240. :ss_rules_dst -
  241. :ss_rules_forward -
  242. $(ss_rules_iptchains_mkprerules "$proto")
  243. -A ss_rules_pre_src -m set --match-set ss_rules${o_af}_dst_bypass_ dst -j RETURN
  244. -A ss_rules_pre_src $o_ipt_extra -j ss_rules_src
  245. -A ss_rules_src -m set --match-set ss_rules${o_af}_src_bypass src -j RETURN
  246. -A ss_rules_src -m set --match-set ss_rules${o_af}_src_forward src -j ss_rules_forward
  247. -A ss_rules_src -m set --match-set ss_rules${o_af}_src_checkdst src -j ss_rules_dst
  248. -A ss_rules_src -j $src_default_target
  249. -A ss_rules_dst -m set --match-set ss_rules${o_af}_dst_bypass dst -j RETURN
  250. -A ss_rules_dst -m set --match-set ss_rules${o_af}_dst_forward dst -j ss_rules_forward
  251. $recentrst_addset_rules
  252. -A ss_rules_dst -j $dst_default_target
  253. $forward_rules
  254. COMMIT
  255. $recentrst_mangle_rules
  256. EOF
  257. }
  258. ss_rules_iptchains_mkprerules() {
  259. local proto="$1"
  260. if [ -z "$o_ifnames" ]; then
  261. echo "-I PREROUTING 1 -p $proto -j ss_rules_pre_src"
  262. else
  263. echo $o_ifnames \
  264. | tr ' ' '\n' \
  265. | sed "s/.*/-I PREROUTING 1 -i \\0 -p $proto -j ss_rules_pre_src/"
  266. fi
  267. }
  268. ss_rules_parse_args "$@"
  269. ss_rules_flush
  270. ss_rules_ipset_init
  271. ss_rules_iptchains_init