mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-28 10:44:43 +08:00
Merge pull request #2736 from zfl9/master
Add TPROXY support for TCP (ss-redir)
This commit is contained in:
9 files changed
+219
-7
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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]}"
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -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"}
|
||||
|============================================================================
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -82,6 +82,7 @@ typedef struct {
|
||||
int mptcp;
|
||||
int ipv6_first;
|
||||
int no_delay;
|
||||
int tcp_tproxy;
|
||||
char *workdir;
|
||||
char *acl;
|
||||
char *manager_address;
|
||||
|
||||
+44
-6
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in new issue
Block a user