From 78784507f12e956fbb2fb611b510fb7da66b179d Mon Sep 17 00:00:00 2001 From: zfl9 Date: Sat, 18 Jul 2020 16:23:21 +0800 Subject: [PATCH 1/5] add `tcp_tproxy` option, use tproxy instead of redirect (tcp) --- src/jconf.c | 5 +++++ src/jconf.h | 1 + src/redir.c | 50 ++++++++++++++++++++++++++++++++++++++++++++------ src/utils.c | 4 ++++ 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/jconf.c b/src/jconf.c index dd38ee96..5ad6110a 100644 --- a/src/jconf.c +++ b/src/jconf.c @@ -338,6 +338,11 @@ read_jconf(const char *file) value, json_boolean, "invalid config file: option 'no_delay' must be a boolean"); conf.no_delay = value->u.boolean; + } else if (strcmp(name, "tcp_tproxy") == 0) { + check_json_value_type( + value, json_boolean, + "invalid config file: option 'tcp_tproxy' must be a boolean"); + conf.tcp_tproxy = value->u.boolean; } else if (strcmp(name, "workdir") == 0) { conf.workdir = to_string(value); } else if (strcmp(name, "acl") == 0) { diff --git a/src/jconf.h b/src/jconf.h index ed0ecafc..4bf7f5e2 100644 --- a/src/jconf.h +++ b/src/jconf.h @@ -82,6 +82,7 @@ typedef struct { int mptcp; int ipv6_first; int no_delay; + int tcp_tproxy; char *workdir; char *acl; char *manager_address; diff --git a/src/redir.c b/src/redir.c index 4ba1c3f8..0c33d767 100644 --- a/src/redir.c +++ b/src/redir.c @@ -65,6 +65,14 @@ #define IP6T_SO_ORIGINAL_DST 80 #endif +#ifndef IP_TRANSPARENT +#define IP_TRANSPARENT 19 +#endif + +#ifndef IPV6_TRANSPARENT +#define IPV6_TRANSPARENT 75 +#endif + static void accept_cb(EV_P_ ev_io *w, int revents); static void server_recv_cb(EV_P_ ev_io *w, int revents); static void server_send_cb(EV_P_ ev_io *w, int revents); @@ -97,19 +105,26 @@ static struct ev_signal sigint_watcher; static struct ev_signal sigterm_watcher; static struct ev_signal sigchld_watcher; +static int tcp_tproxy = 0; /* use tproxy instead of redirect (for tcp) */ + static int getdestaddr(int fd, struct sockaddr_storage *destaddr) { socklen_t socklen = sizeof(*destaddr); int error = 0; - error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen); - if (error) { // Didn't find a proper way to detect IP version. - error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen); - if (error) { - return -1; + if (tcp_tproxy) { + error = getsockname(fd, (void *)destaddr, &socklen); + } else { + error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen); + if (error) { // Didn't find a proper way to detect IP version. + error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen); } } + + if (error) { + return -1; + } return 0; } @@ -165,6 +180,23 @@ create_and_bind(const char *addr, const char *port) } } + if (tcp_tproxy) { + int level = 0, optname = 0; + if (rp->ai_family == AF_INET) { + level = IPPROTO_IP; + optname = IP_TRANSPARENT; + } else { + level = IPPROTO_IPV6; + optname = IPV6_TRANSPARENT; + } + + if (setsockopt(listen_sock, level, optname, &opt, sizeof(opt)) != 0) { + ERROR("setsockopt IP_TRANSPARENT"); + exit(EXIT_FAILURE); + } + LOGI("tcp tproxy mode enabled"); + } + s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); if (s == 0) { /* We managed to bind successfully! */ @@ -882,7 +914,7 @@ main(int argc, char **argv) USE_TTY(); - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUv6A", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUTv6A", long_options, NULL)) != -1) { switch (c) { case GETOPT_VAL_FAST_OPEN: @@ -957,6 +989,9 @@ main(int argc, char **argv) case 'U': mode = UDP_ONLY; break; + case 'T': + tcp_tproxy = 1; + break; case 'v': verbose = 1; break; @@ -1029,6 +1064,9 @@ main(int argc, char **argv) if (mode == TCP_ONLY) { mode = conf->mode; } + if (tcp_tproxy == 0) { + tcp_tproxy = conf->tcp_tproxy; + } if (mtu == 0) { mtu = conf->mtu; } diff --git a/src/utils.c b/src/utils.c index 87107aa3..0e94e5d9 100644 --- a/src/utils.c +++ b/src/utils.c @@ -360,6 +360,10 @@ usage() #endif printf( " [-U] Enable UDP relay and disable TCP relay.\n"); +#ifdef MODULE_REDIR + printf( + " [-T] Use tproxy instead of redirect (for tcp).\n"); +#endif #ifdef MODULE_REMOTE printf( " [-6] Resovle hostname to IPv6 address first.\n"); From 37cdf8d45a389909173b7aab9291ad7e154f1124 Mon Sep 17 00:00:00 2001 From: zfl9 Date: Sat, 18 Jul 2020 17:00:18 +0800 Subject: [PATCH 2/5] add `tcp_tproxy` option, completions bash/zsh --- completions/bash/ss-redir | 2 +- completions/zsh/_ss-redir | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/completions/bash/ss-redir b/completions/bash/ss-redir index a45c59ef..3fbf9bb8 100644 --- a/completions/bash/ss-redir +++ b/completions/bash/ss-redir @@ -1,7 +1,7 @@ _ss_redir() { local cur prev opts ciphers - opts='-s -p -l -k -m -a -f -t -c -n -b -u -U -v -h --reuse-port --mtu --mptcp --key --plugin --plugin-opts --help' + opts='-s -p -l -k -m -a -f -t -c -n -b -u -U -T -v -h --reuse-port --mtu --mptcp --key --plugin --plugin-opts --help' ciphers='rc4-md5 aes-128-gcm aes-192-gcm aes-256-gcm aes-128-cfb aes-192-cfb aes-256-cfb aes-128-ctr aes-192-ctr aes-256-ctr camellia-128-cfb camellia-192-cfb camellia-256-cfb bf-cfb chacha20-ietf-poly1305 xchacha20-ietf-poly1305 salsa20 chacha20 chacha20-ietf' cur=${COMP_WORDS[COMP_CWORD]} prev="${COMP_WORDS[COMP_CWORD-1]}" diff --git a/completions/zsh/_ss-redir b/completions/zsh/_ss-redir index 1d49b379..9e6684a4 100644 --- a/completions/zsh/_ss-redir +++ b/completions/zsh/_ss-redir @@ -18,6 +18,7 @@ _arguments "-h::" \ "-u:enable udp:" \ "-U:udp only:" \ "-v:verbose mode:" \ + "-T:tcp tproxy mode:" \ "--reuse-port::" \ "--fast-open::" \ "--acl:acl file:_files" \ From 6d4a3ad82cae67c4eeedbf4468b3983ddc08dd94 Mon Sep 17 00:00:00 2001 From: zfl9 Date: Sat, 18 Jul 2020 17:08:56 +0800 Subject: [PATCH 3/5] add `tcp_tproxy` option, asciidoc update --- doc/shadowsocks-libev.asciidoc | 6 ++++++ doc/ss-redir.asciidoc | 3 +++ 2 files changed, 9 insertions(+) diff --git a/doc/shadowsocks-libev.asciidoc b/doc/shadowsocks-libev.asciidoc index c3d9df84..4a283dd6 100644 --- a/doc/shadowsocks-libev.asciidoc +++ b/doc/shadowsocks-libev.asciidoc @@ -122,6 +122,11 @@ Enable UDP relay and disable TCP relay. + Not available in local mode. +-T:: +Use tproxy instead of redirect (for tcp). ++ +Only available in redir mode. + -L :: Specify destination server address and port for local port forwarding. + @@ -199,6 +204,7 @@ The config file equivalent of command line options is listed as example below. | -u | "mode": "tcp_and_udp" | -U | "mode": "udp_only" | no "-u" nor "-U" options (default) | "mode": "tcp_only" +| -T | "tcp_tproxy": true | (only in ss-manager's config) | "port_password": {"1234":"PasSworD"} |============================================================================ diff --git a/doc/ss-redir.asciidoc b/doc/ss-redir.asciidoc index 56fe1839..ea15ef31 100644 --- a/doc/ss-redir.asciidoc +++ b/doc/ss-redir.asciidoc @@ -95,6 +95,9 @@ TPROXY is required in redir mode. You may need root permission. -U:: Enable UDP relay and disable TCP relay. +-T:: +Use tproxy instead of redirect. (for tcp) + -6:: Resovle hostname to IPv6 address first. From cde88d40741eaf3d7f293a7ab141b8ec2b864ff3 Mon Sep 17 00:00:00 2001 From: zfl9 Date: Sat, 18 Jul 2020 17:10:37 +0800 Subject: [PATCH 4/5] add `tcp_tproxy` option, README.md update --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 317affed..891eedef 100644 --- a/README.md +++ b/README.md @@ -398,6 +398,9 @@ you may refer to the man pages of the applications, respectively. [-U] Enable UDP relay and disable TCP relay. (not available in local mode) + [-T] Use tproxy instead of redirect. (for tcp) + (only available in redir mode) + [-L :] Destination server address and port for local port forwarding. (only available in tunnel mode) From 03df264fcda1e7ca89cd69505f4ab918d335fb4b Mon Sep 17 00:00:00 2001 From: zfl9 Date: Sat, 18 Jul 2020 21:09:08 +0800 Subject: [PATCH 5/5] add `tcp_tproxy` option, add a sample script in readme.md --- README.md | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/README.md b/README.md index 891eedef..5570198b 100644 --- a/README.md +++ b/README.md @@ -481,6 +481,157 @@ The latest shadowsocks-libev has provided a *redir* mode. You can configure your # Start the shadowsocks-redir ss-redir -u -c /etc/config/shadowsocks.json -f /var/run/shadowsocks.pid +## Transparent proxy (pure tproxy) + +Executing this script on the linux host can proxy all outgoing traffic of this machine (except the traffic sent to the reserved address). Other hosts under the same LAN can also change their default gateway to the ip of this linux host (at the same time change the dns server to 1.1.1.1 or 8.8.8.8, etc.) to proxy their outgoing traffic. + +> Of course, the ipv6 proxy is similar, just change `iptables` to `ip6tables`, `ip` to `ip -6`, `127.0.0.1` to `::1`, and other details. + +```shell +#!/bin/bash + +start_ssredir() { + # please modify MyIP, MyPort, etc. + (ss-redir -s MyIP -p MyPort -m MyMethod -k MyPasswd -b 127.0.0.1 -l 60080 --no-delay -u -T -v >/var/log/ss-redir.log &) +} + +stop_ssredir() { + kill -9 $(pidof ss-redir) &>/dev/null +} + +start_iptables() { + ##################### SSREDIR ##################### + iptables -t mangle -N SSREDIR + + # connection-mark -> packet-mark + iptables -t mangle -A SSREDIR -j CONNMARK --restore-mark + iptables -t mangle -A SSREDIR -m mark --mark 0x2333 -j RETURN + + # please modify MyIP, MyPort, etc. + # ignore traffic sent to ss-server + iptables -t mangle -A SSREDIR -p tcp -d MyIP --dport MyPort -j RETURN + iptables -t mangle -A SSREDIR -p udp -d MyIP --dport MyPort -j RETURN + + # ignore traffic sent to reserved addresses + iptables -t mangle -A SSREDIR -d 0.0.0.0/8 -j RETURN + iptables -t mangle -A SSREDIR -d 10.0.0.0/8 -j RETURN + iptables -t mangle -A SSREDIR -d 100.64.0.0/10 -j RETURN + iptables -t mangle -A SSREDIR -d 127.0.0.0/8 -j RETURN + iptables -t mangle -A SSREDIR -d 169.254.0.0/16 -j RETURN + iptables -t mangle -A SSREDIR -d 172.16.0.0/12 -j RETURN + iptables -t mangle -A SSREDIR -d 192.0.0.0/24 -j RETURN + iptables -t mangle -A SSREDIR -d 192.0.2.0/24 -j RETURN + iptables -t mangle -A SSREDIR -d 192.88.99.0/24 -j RETURN + iptables -t mangle -A SSREDIR -d 192.168.0.0/16 -j RETURN + iptables -t mangle -A SSREDIR -d 198.18.0.0/15 -j RETURN + iptables -t mangle -A SSREDIR -d 198.51.100.0/24 -j RETURN + iptables -t mangle -A SSREDIR -d 203.0.113.0/24 -j RETURN + iptables -t mangle -A SSREDIR -d 224.0.0.0/4 -j RETURN + iptables -t mangle -A SSREDIR -d 240.0.0.0/4 -j RETURN + iptables -t mangle -A SSREDIR -d 255.255.255.255/32 -j RETURN + + # mark the first packet of the connection + iptables -t mangle -A SSREDIR -p tcp --syn -j MARK --set-mark 0x2333 + iptables -t mangle -A SSREDIR -p udp -m conntrack --ctstate NEW -j MARK --set-mark 0x2333 + + # packet-mark -> connection-mark + iptables -t mangle -A SSREDIR -j CONNMARK --save-mark + + ##################### OUTPUT ##################### + # proxy the outgoing traffic from this machine + iptables -t mangle -A OUTPUT -p tcp -m addrtype --src-type LOCAL ! --dst-type LOCAL -j SSREDIR + iptables -t mangle -A OUTPUT -p udp -m addrtype --src-type LOCAL ! --dst-type LOCAL -j SSREDIR + + ##################### PREROUTING ##################### + # proxy traffic passing through this machine (other->other) + iptables -t mangle -A PREROUTING -p tcp -m addrtype ! --src-type LOCAL ! --dst-type LOCAL -j SSREDIR + iptables -t mangle -A PREROUTING -p udp -m addrtype ! --src-type LOCAL ! --dst-type LOCAL -j SSREDIR + + # hand over the marked package to TPROXY for processing + iptables -t mangle -A PREROUTING -p tcp -m mark --mark 0x2333 -j TPROXY --on-ip 127.0.0.1 --on-port 60080 + iptables -t mangle -A PREROUTING -p udp -m mark --mark 0x2333 -j TPROXY --on-ip 127.0.0.1 --on-port 60080 +} + +stop_iptables() { + ##################### PREROUTING ##################### + iptables -t mangle -D PREROUTING -p tcp -m mark --mark 0x2333 -j TPROXY --on-ip 127.0.0.1 --on-port 60080 &>/dev/null + iptables -t mangle -D PREROUTING -p udp -m mark --mark 0x2333 -j TPROXY --on-ip 127.0.0.1 --on-port 60080 &>/dev/null + + iptables -t mangle -D PREROUTING -p tcp -m addrtype ! --src-type LOCAL ! --dst-type LOCAL -j SSREDIR &>/dev/null + iptables -t mangle -D PREROUTING -p udp -m addrtype ! --src-type LOCAL ! --dst-type LOCAL -j SSREDIR &>/dev/null + + ##################### OUTPUT ##################### + iptables -t mangle -D OUTPUT -p tcp -m addrtype --src-type LOCAL ! --dst-type LOCAL -j SSREDIR &>/dev/null + iptables -t mangle -D OUTPUT -p udp -m addrtype --src-type LOCAL ! --dst-type LOCAL -j SSREDIR &>/dev/null + + ##################### SSREDIR ##################### + iptables -t mangle -F SSREDIR &>/dev/null + iptables -t mangle -X SSREDIR &>/dev/null +} + +start_iproute2() { + ip route add local default dev lo table 100 + ip rule add fwmark 0x2333 table 100 +} + +stop_iproute2() { + ip rule del table 100 &>/dev/null + ip route flush table 100 &>/dev/null +} + +start_resolvconf() { + # or nameserver 8.8.8.8, etc. + echo "nameserver 1.1.1.1" >/etc/resolv.conf +} + +stop_resolvconf() { + echo "nameserver 114.114.114.114" >/etc/resolv.conf +} + +start() { + echo "start ..." + start_ssredir + start_iptables + start_iproute2 + start_resolvconf + echo "start end" +} + +stop() { + echo "stop ..." + stop_resolvconf + stop_iproute2 + stop_iptables + stop_ssredir + echo "stop end" +} + +restart() { + stop + sleep 1 + start +} + +main() { + if [ $# -eq 0 ]; then + echo "usage: $0 start|stop|restart ..." + return 1 + fi + + for funcname in "$@"; do + if [ "$(type -t $funcname)" != 'function' ]; then + echo "'$funcname' not a shell function" + return 1 + fi + done + + for funcname in "$@"; do + $funcname + done + return 0 +} +main "$@" +``` ## Security Tips