shadowsocks-libev: ss-rules: eliminiate not needed local var
[feed/packages.git] / net / shadowsocks-libev / files / ss-rules
old mode 100644 (file)
new mode 100755 (executable)
index 8ce1000..b0a3060
-#!/bin/sh
-
-usage() {
-       cat <<-EOF
-               Usage: ss-rules [options]
-
-               Valid options are:
-
-                   -s <server_host>        hostname or ip of shadowsocks remote server
-                   -l <local_port>         port number of shadowsocks local server
-                   -i <ip_list_file>       a file content is bypassed ip list
-                   -a <lan_ips>            lan ip of access control, need a prefix to
-                                           define access control mode
-                   -b <wan_ips>            wan ip of will be bypassed
-                   -w <wan_ips>            wan ip of will be forwarded
-                   -e <extra_options>      extra options for iptables
-                   -o                      apply the rules to the OUTPUT chain
-                   -u                      enable udprelay mode, TPROXY is required
-                   -U                      enable udprelay mode, using different IP
-                                           and ports for TCP and UDP
-                   -f                      flush the rules
-EOF
-}
+#!/bin/sh -e
+#
+# Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
+#
+# The design idea was derived from ss-rules by Jian Chang <aa65535@live.com>
+#
+# This is free software, licensed under the GNU General Public License v3.
+# See /LICENSE for more information.
+#
 
-loger() {
-       # 1.alert 2.crit 3.err 4.warn 5.notice 6.info 7.debug
-       logger -st ss-rules[$$] -p$1 $2
-}
+ss_rules_usage() {
+       cat >&2 <<EOF
+Usage: ss-rules [options]
+
+       -h, --help      Show this help message then exit
+       -f, --flush     Flush rules, ipset then exit
+       -l <port>       Local port number of ss-redir with TCP mode
+       -L <port>       Local port number of ss-redir with UDP mode
+       -s <ips>        List of ip addresses of remote shadowsocks server
+       --ifnames       Only apply rules on packets from these ifnames
+       --src-bypass <ips|cidr>
+       --src-forward <ips|cidr>
+       --src-checkdst <ips|cidr>
+       --src-default <bypass|forward|checkdst>
+                       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 <ips|cidr>
+       --dst-forward <ips|cidr>
+       --dst-bypass-file <file>
+       --dst-forward-file <file>
+       --dst-default <bypass|forward>
+                       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 <bypass|forward|checkdst>
+                       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
 
-ipt_n="iptables -t nat"
-ipt_m="iptables -t mangle"
+       ss_rules_src_bypass
+       ss_rules_src_forward
+       ss_rules_src_checkdst
+       ss_rules_dst_bypass
+       ss_rules_dst_forward
+EOF
+}
 
-flush_r() {
-       local IPT
+o_dst_bypass_="
+       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
+       255.255.255.255
+"
+o_src_default=bypass
+o_dst_default=bypass
+o_local_default=bypass
 
-       IPT=$(iptables-save -t nat)
-       eval $(echo "$IPT" | grep "_SS_SPEC_RULE_" | \
-               sed -e 's/^-A/$ipt_n -D/' -e 's/$/;/')
+__errmsg() {
+       echo "ss-rules: $*" >&2
+}
 
-       for chain in $(echo "$IPT" | awk '/^:SS_SPEC/{print $1}'); do
-               $ipt_n -F ${chain:1} 2>/dev/null && $ipt_n -X ${chain:1}
+ss_rules_parse_args() {
+       while [ "$#" -gt 0 ]; do
+               case "$1" in
+                       -h|--help) ss_rules_usage; exit 0;;
+                       -f|--flush) ss_rules_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-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;;
+                       *) __errmsg "unknown option $1"; return 1;;
+               esac
        done
 
-       IPT=$(iptables-save -t mangle)
-       eval $(echo "$IPT" | grep "_SS_SPEC_RULE_" | \
-               sed -e 's/^-A/$ipt_m -D/' -e 's/$/;/')
+       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" ] && ! iptables -m recent -h >/dev/null; then
+               __errmsg "Please install iptables-mod-conntrack-extra with opkg"
+               return 1
+       fi
+       o_remote_servers="$(for s in $o_remote_servers; do resolveip -4 "$s"; done)"
+}
 
-       for chain in $(echo "$IPT" | awk '/^:SS_SPEC/{print $1}'); do
-               $ipt_m -F ${chain:1} 2>/dev/null && $ipt_m -X ${chain:1}
+ss_rules_flush() {
+       local setname
+
+       iptables-save --counters | grep -v ss_rules_ | iptables-restore --counters
+       while ip rule del fwmark 1 lookup 100 2>/dev/null; do true; done
+       ip route flush table 100
+       for setname in $(ipset -n list | grep "ss_rules_"); do
+               ipset destroy "$setname" 2>/dev/null || true
        done
+}
 
-       ip rule del fwmark 0x01/0x01 table 100 2>/dev/null
-       ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
-       ipset -X ss_spec_lan_ac 2>/dev/null
-       ipset -X ss_spec_wan_ac 2>/dev/null
-       return 0
+ss_rules_ipset_init() {
+       ipset --exist restore <<-EOF
+               create ss_rules_src_bypass hash:net hashsize 64
+               create ss_rules_src_forward hash:net hashsize 64
+               create ss_rules_src_checkdst hash:net hashsize 64
+               create ss_rules_dst_bypass hash:net hashsize 64
+               create ss_rules_dst_bypass_ hash:net hashsize 64
+               create ss_rules_dst_forward hash:net hashsize 64
+               create ss_rules_dst_forward_recentrst_ hash:ip hashsize 64 timeout 3600
+               $(ss_rules_ipset_mkadd ss_rules_dst_bypass_ "$o_dst_bypass_ $o_remote_servers")
+               $(ss_rules_ipset_mkadd ss_rules_src_bypass "$o_src_bypass")
+               $(ss_rules_ipset_mkadd ss_rules_src_forward "$o_src_forward")
+               $(ss_rules_ipset_mkadd ss_rules_src_checkdst "$o_src_checkdst")
+               $(ss_rules_ipset_mkadd ss_rules_dst_bypass "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null)")
+               $(ss_rules_ipset_mkadd ss_rules_dst_forward "$o_dst_forward $(cat "$o_dst_forward_file" 2>/dev/null)")
+       EOF
 }
 
-ipset_r() {
-       ipset -! -R <<-EOF || return 1
-               create ss_spec_wan_ac hash:net
-               $(echo -e "$IPLIST" | sed -e "s/^/add ss_spec_wan_ac /")
-               $(for ip in $WAN_FW_IP; do echo "add ss_spec_wan_ac $ip nomatch"; done)
-EOF
-       $ipt_n -N SS_SPEC_WAN_AC && \
-       $ipt_n -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN && \
-       $ipt_n -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
-       return $?
+ss_rules_ipset_mkadd() {
+       local setname="$1"; shift
+       local i
+
+       for i in $*; do
+               echo "add $setname $i"
+       done
 }
 
-fw_rule() {
-       $ipt_n -N SS_SPEC_WAN_FW && \
-       $ipt_n -A SS_SPEC_WAN_FW -p tcp \
-               -j REDIRECT --to-ports $local_port 2>/dev/null || {
-               loger 3 "Can't redirect, please check the iptables."
-               exit 1
-       }
-       return $?
+ss_rules_iptchains_init() {
+       ss_rules_iptchains_init_tcp
+       ss_rules_iptchains_init_udp
 }
 
-ac_rule() {
-       local TAG ROUTECHAIN
+ss_rules_iptchains_init_tcp() {
+       local local_target
 
-       if [ -n "$LAN_AC_IP" ]; then
-               if [ "${LAN_AC_IP:0:1}" = "w" ]; then
-                       TAG="nomatch"
-               else
-                       if [ "${LAN_AC_IP:0:1}" != "b" ]; then
-                               loger 3 "Bad argument \`-a $LAN_AC_IP\`."
-                               return 2
-                       fi
-               fi
-       fi
+       [ -n "$o_redir_tcp_port" ] || return 0
 
-       ROUTECHAIN=PREROUTING
-       if iptables-save -t nat | grep -q "^:zone_lan_prerouting"; then
-               ROUTECHAIN=zone_lan_prerouting
-       fi
+       ss_rules_iptchains_init_ nat tcp
 
-       ipset -! -R <<-EOF || return 1
-               create ss_spec_lan_ac hash:net
-               $(for ip in ${LAN_AC_IP:1}; do echo "add ss_spec_lan_ac $ip $TAG"; done)
-EOF
-       $ipt_n -A $ROUTECHAIN -p tcp $EXT_ARGS \
-               -m set ! --match-set ss_spec_lan_ac src \
-               -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_WAN_AC
+       case "$o_local_default" in
+               checkdst) local_target=ss_rules_dst ;;
+               forward) local_target=ss_rules_forward ;;
+               bypass|*) return 0;;
+       esac
 
-       if [ "$OUTPUT" = 1 ]; then
-               $ipt_n -A OUTPUT -p tcp $EXT_ARGS \
-                       -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_WAN_AC
-       fi
-       return $?
+       iptables-restore --noflush <<-EOF
+               *nat
+               :ss_rules_local_out -
+               -I OUTPUT 1 -p tcp -j ss_rules_local_out
+               -A ss_rules_local_out -m set --match-set ss_rules_dst_bypass_ dst -j RETURN
+               -A ss_rules_local_out -p tcp $o_ipt_extra -j $local_target -m comment --comment "local_default: $o_local_default"
+               COMMIT
+       EOF
 }
 
-tp_rule() {
-       [ -n "$TPROXY" ] || return 0
-       ip rule add fwmark 0x01/0x01 table 100
-       ip route add local 0.0.0.0/0 dev lo table 100
-       $ipt_m -N SS_SPEC_TPROXY
-       $ipt_m -A SS_SPEC_TPROXY -p udp -m set ! --match-set ss_spec_wan_ac dst \
-               -j TPROXY --on-port $LOCAL_PORT --tproxy-mark 0x01/0x01
-       $ipt_m -A PREROUTING -p udp $EXT_ARGS \
-               -m set ! --match-set ss_spec_lan_ac src \
-               -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_TPROXY
-       return $?
+ss_rules_iptchains_init_udp() {
+       [ -n "$o_redir_udp_port" ] || return 0
+       ss_rules_iptchains_init_ mangle udp
 }
 
-while getopts ":s:l:S:L:i:e:a:b:w:ouUf" arg; do
-       case $arg in
-               s)
-                       server=$OPTARG
-                       ;;
-               l)
-                       local_port=$OPTARG
-                       ;;
-               S)
-                       SERVER=$OPTARG
-                       ;;
-               L)
-                       LOCAL_PORT=$OPTARG
-                       ;;
-               i)
-                       IGNORE=$OPTARG
-                       ;;
-               e)
-                       EXT_ARGS=$OPTARG
-                       ;;
-               a)
-                       LAN_AC_IP=$OPTARG
-                       ;;
-               b)
-                       WAN_BP_IP=$(for ip in $OPTARG; do echo $ip; done)
-                       ;;
-               w)
-                       WAN_FW_IP=$OPTARG
-                       ;;
-               o)
-                       OUTPUT=1
-                       ;;
-               u)
-                       TPROXY=1
-                       ;;
-               U)
-                       TPROXY=2
+ss_rules_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 ss_rules_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_rules_recentrst --set --rsource
+                                       COMMIT
+                               "
+                               recentrst_addset_rules="
+                                       -A ss_rules_dst -m recent --name ss_rules_recentrst --rcheck --rdest --seconds 3 --hitcount 3 -j SET --add-set ss_rules_dst_forward_recentrst_ dst --exist
+                                       -A ss_rules_dst -m set --match-set ss_rules_dst_forward_recentrst_ dst -j ss_rules_forward
+                               "
+                       fi
                        ;;
-               f)
-                       flush_r
-                       exit 0
+               udp)
+                       ip rule add fwmark 1 lookup 100
+                       ip route add local default dev lo table 100
+                       forward_rules="-A ss_rules_forward -p udp -j TPROXY --on-port "$o_redir_udp_port" --tproxy-mark 0x01/0x01"
                        ;;
        esac
-done
-
-if [ -z "$server" -o -z "$local_port" ]; then
-       usage
-       exit 2
-fi
+       case "$o_src_default" in
+               forward) src_default_target=ss_rules_forward ;;
+               checkdst) src_default_target=ss_rules_dst ;;
+               bypass|*) src_default_target=RETURN ;;
+       esac
+       case "$o_dst_default" in
+               forward) dst_default_target=ss_rules_forward ;;
+               bypass|*) dst_default_target=RETURN ;;
+       esac
+       sed -e '/^\s*$/d' -e 's/^\s\+//' <<-EOF | iptables-restore --noflush
+               *$table
+               :ss_rules_pre_src -
+               :ss_rules_src -
+               :ss_rules_dst -
+               :ss_rules_forward -
+               $(ss_rules_iptchains_mkprerules "$proto")
+               -A ss_rules_pre_src -m set --match-set ss_rules_dst_bypass_ dst -j RETURN
+               -A ss_rules_pre_src -p $proto $o_ipt_extra -j ss_rules_src
+               -A ss_rules_src -m set --match-set ss_rules_src_bypass src -j RETURN
+               -A ss_rules_src -m set --match-set ss_rules_src_forward src -j ss_rules_forward
+               -A ss_rules_src -m set --match-set ss_rules_src_checkdst src -j ss_rules_dst
+               -A ss_rules_src -j $src_default_target -m comment --comment "src_default: $o_src_default"
+               -A ss_rules_dst -m set --match-set ss_rules_dst_bypass dst -j RETURN
+               -A ss_rules_dst -m set --match-set ss_rules_dst_forward dst -j ss_rules_forward
+               $recentrst_addset_rules
+               -A ss_rules_dst -j $dst_default_target -m comment --comment "dst_default: $o_dst_default"
+               $forward_rules
+               COMMIT
+               $recentrst_mangle_rules
+       EOF
+}
 
-if [ "$TPROXY" = 1 ]; then
-       SERVER=$server
-       LOCAL_PORT=$local_port
-fi
+ss_rules_iptchains_mkprerules() {
+       local proto="$1"
 
-if [ "$TPROXY" = 2 ]; then
-       if [ -z "$SERVER" -o -z "$LOCAL_PORT" ]; then
-               loger 3 "Please use -S and -L specifies IP and port for UDP."
+       if [ -z "$o_ifnames" ]; then
+               echo "-I PREROUTING 1 -p $proto -j ss_rules_pre_src"
+       else
+               echo $o_ifnames \
+                       | tr ' ' '\n' \
+                       | sed "s/.*/-I PREROUTING 1 -i \\0 -p $proto -j ss_rules_pre_src/"
        fi
-fi
-
-if [ -f "$IGNORE" ]; then
-       IGNORE_IP=$(cat $IGNORE 2>/dev/null)
-fi
-
-IPLIST=$(cat <<-EOF | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}"
-       $server
-       $SERVER
-       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.88.99.0/24
-       192.168.0.0/16
-       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
-       255.255.255.255
-       $WAN_BP_IP
-       $IGNORE_IP
-EOF
-)
-
-flush_r && fw_rule && ipset_r && ac_rule && tp_rule
+}
 
-exit $?
+ss_rules_parse_args "$@"
+ss_rules_flush
+ss_rules_ipset_init
+ss_rules_iptchains_init