1#!/bin/bash 2# 3# Copyright (C) 2015 OpenWrt-dist 4# Copyright (C) 2015 Jian Chang <aa65535@live.com> 5# 6# This is free software, licensed under the GNU General Public License v3. 7# See /LICENSE for more information. 8# 9 10TAG="SS_SPEC" # iptables tag 11IPT="iptables -t nat" # alias of iptables 12FWI=$(uci get firewall.shadowsocks.path 2>/dev/null) # firewall include file 13IP_REGEX="^([0-9]{1,3}\.){3}[0-9]{1,3}" # used to check if input is a valid IP 14 15usage() { 16 cat <<-EOF 17 18 Copyright (C) 2015 OpenWrt-dist 19 Copyright (C) 2015 Jian Chang <aa65535@live.com> 20 21 Usage: ss-nat [options] 22 23 Valid options are: 24 25 -s <server_ip> hostname (requires dig) or ip address of shadowsocks remote server 26 -l <local_port> port number of shadowsocks local server 27 -S <server_ip> hostname (requires dig) or ip address of shadowsocks remote UDP server 28 -L <local_port> port number of shadowsocks local UDP server 29 -i <ip_list_file> a file content is bypassed ip list 30 -I <interface> lan interface of nat, default: eth0 31 -a <lan_ips> lan ip of access control, need a prefix to 32 define access control mode 33 -b <wan_ips> wan ip of will be bypassed 34 -w <wan_ips> wan ip of will be forwarded 35 -e <extra_options> extra options for iptables 36 -o apply the rules to the OUTPUT chain 37 -u enable udprelay mode, TPROXY is required 38 -U enable udprelay mode, using different IP 39 and ports for TCP and UDP 40 -f flush the rules 41 -h show this help message and exit 42 43 This is free software, licensed under the GNU General Public License v3. 44 See /LICENSE for more information. 45 46EOF 47 exit $1 48} 49 50loger() { 51 # 1.alert 2.crit 3.err 4.warn 5.notice 6.info 7.debug 52 logger -st ss-nat[$$] -p$1 $2 53} 54 55flush_r() { 56 iptables-save -c | grep -v "$TAG" | iptables-restore -c 57 ip rule del fwmark 0x01/0x01 table 100 2>/dev/null 58 ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null 59 ipset -X ss_spec_lan_ac 2>/dev/null 60 ipset -X ss_spec_wan_ac 2>/dev/null 61 [ -n "$FWI" ] && echo '#!/bin/sh' >$FWI 62 return 0 63} 64 65ipset_r() { 66 ipset -! -R <<-EOF || return 1 67 create ss_spec_wan_ac hash:net 68 $(gen_iplist | sed "/^\s*$/d" | sed -e "s/^/add ss_spec_wan_ac /") 69 $(for ip in $WAN_FW_IP; do echo "add ss_spec_wan_ac $ip nomatch"; done) 70EOF 71 $IPT -N SS_SPEC_WAN_AC && \ 72 $IPT -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN && \ 73 $IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW 74 return $? 75} 76 77fw_rule() { 78 $IPT -N SS_SPEC_WAN_FW && \ 79 $IPT -A SS_SPEC_WAN_FW -p tcp \ 80 -j REDIRECT --to-ports $local_port 2>/dev/null || { 81 loger 3 "Can't redirect, please check the iptables." 82 exit 1 83 } 84 return $? 85} 86 87ac_rule() { 88 if [ -n "$LAN_AC_IP" ]; then 89 case "${LAN_AC_IP:0:1}" in 90 w|W) 91 MATCH_SET="-m set --match-set ss_spec_lan_ac src" 92 ;; 93 b|B) 94 MATCH_SET="-m set ! --match-set ss_spec_lan_ac src" 95 ;; 96 *) 97 loger 3 "Illegal argument \`-a $LAN_AC_IP\`." 98 return 2 99 ;; 100 esac 101 fi 102 IFNAME=${IFNAME:-eth0} 103 ipset -! -R <<-EOF || return 1 104 create ss_spec_lan_ac hash:net 105 $(for ip in ${LAN_AC_IP:1}; do echo "add ss_spec_lan_ac $ip"; done) 106EOF 107 $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS $MATCH_SET \ 108 -j SS_SPEC_WAN_AC 109 if [ "$OUTPUT" = 1 ]; then 110 $IPT -I OUTPUT 1 -p tcp $EXT_ARGS -j SS_SPEC_WAN_AC 111 fi 112 return $? 113} 114 115tp_rule() { 116 lsmod | grep -q TPROXY || return 0 117 [ -n "$TPROXY" ] || return 0 118 ip rule add fwmark 0x01/0x01 table 100 119 ip route add local 0.0.0.0/0 dev lo table 100 120 local ipt="iptables -t mangle" 121 $ipt -N SS_SPEC_TPROXY 122 $ipt -A SS_SPEC_TPROXY -p udp -m set ! --match-set ss_spec_wan_ac dst \ 123 -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01 124 $ipt -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p udp $EXT_ARGS $MATCH_SET \ 125 -j SS_SPEC_TPROXY 126 return $? 127} 128 129get_wan_ip() { 130 cat <<-EOF | grep -E $IP_REGEX 131 $server 132 $SERVER 133 $WAN_BP_IP 134EOF 135} 136 137gen_iplist() { 138 cat <<-EOF 139 0.0.0.0/8 140 10.0.0.0/8 141 100.64.0.0/10 142 127.0.0.0/8 143 169.254.0.0/16 144 172.16.0.0/12 145 192.0.0.0/24 146 192.0.2.0/24 147 192.88.99.0/24 148 192.168.0.0/16 149 198.18.0.0/15 150 198.51.100.0/24 151 203.0.113.0/24 152 224.0.0.0/4 153 240.0.0.0/4 154 255.255.255.255 155 $(get_wan_ip) 156 $(cat ${IGNORE_LIST:=/dev/null} 2>/dev/null) 157EOF 158} 159 160gen_include() { 161 [ -n "$FWI" ] || return 0 162 cat <<-EOF >>$FWI 163 iptables-restore -n <<-EOT 164 $(iptables-save | grep -E "$TAG|^\*|^COMMIT" |\ 165 sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/") 166 EOT 167EOF 168 return $? 169} 170 171while getopts ":s:l:S:L:i:I:e:a:b:w:ouUfh" arg; do 172 case "$arg" in 173 s) 174 if [[ $OPTARG =~ $IP_REGEX ]] 175 then 176 server=$OPTARG 177 else 178 server=$(ping -4 -q -c 1 -s 0 -W 1 -w 1 $OPTARG| head -n 1 | sed -n 's/[^(]*(\([^)]*\)).*/\1/p') 179 fi 180 ;; 181 l) 182 local_port=$OPTARG 183 ;; 184 S) 185 if [[ $OPTARG =~ $IP_REGEX ]] 186 then 187 SERVER=$OPTARG 188 else 189 SERVER=$(ping -4 -q -c 1 -s 0 -W 1 -w 1 $OPTARG| head -n 1 | sed -n 's/[^(]*(\([^)]*\)).*/\1/p') 190 fi 191 ;; 192 L) 193 LOCAL_PORT=$OPTARG 194 ;; 195 i) 196 IGNORE_LIST=$OPTARG 197 ;; 198 I) 199 IFNAME=$OPTARG 200 ;; 201 e) 202 EXT_ARGS=$OPTARG 203 ;; 204 a) 205 LAN_AC_IP=$OPTARG 206 ;; 207 b) 208 WAN_BP_IP=$(for ip in $OPTARG; do echo $ip; done) 209 ;; 210 w) 211 WAN_FW_IP=$OPTARG 212 ;; 213 o) 214 OUTPUT=1 215 ;; 216 u) 217 TPROXY=1 218 ;; 219 U) 220 TPROXY=2 221 ;; 222 f) 223 flush_r 224 exit 0 225 ;; 226 h) 227 usage 0 228 ;; 229 esac 230done 231 232if [ -z "$server" -o -z "$local_port" ]; then 233 usage 2 234fi 235 236if [ "$TPROXY" = 1 ]; then 237 SERVER=$server 238 LOCAL_PORT=$local_port 239elif [ "$TPROXY" = 2 ]; then 240 : ${SERVER:?"You must assign an ip for the udp relay server."} 241 : ${LOCAL_PORT:?"You must assign a port for the udp relay server."} 242fi 243 244flush_r && fw_rule && ipset_r && ac_rule && tp_rule && gen_include 245[ "$?" = 0 ] || loger 3 "Start failed!" 246exit $? 247