Browse Source

Merge pull request #2736 from zfl9/master

Add TPROXY support for TCP (ss-redir)
pull/2741/head
Max Lv 4 years ago
committed by GitHub
parent
commit
31dd81649d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 219 additions and 7 deletions
  1. 154
      README.md
  2. 2
      completions/bash/ss-redir
  3. 1
      completions/zsh/_ss-redir
  4. 6
      doc/shadowsocks-libev.asciidoc
  5. 3
      doc/ss-redir.asciidoc
  6. 5
      src/jconf.c
  7. 1
      src/jconf.h
  8. 50
      src/redir.c
  9. 4
      src/utils.c

154
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 <addr>:<port>] Destination server address and port
for local port forwarding.
(only available in tunnel mode)
@ -478,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 </dev/null &>>/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

2
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]}"

1
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" \

6
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 <addr:port>::
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"}
|============================================================================

3
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.

5
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) {

1
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;

50
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;
}

4
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");

Loading…
Cancel
Save