--- a/completions/bash/ss-redir
+++ b/completions/bash/ss-redir
@@ -2,7 +2,7 @@ _ss_redir()
 {
     local cur prev opts ciphers
     ciphers='rc4-md5 table rc4 aes-128-cfb aes-192-cfb aes-256-cfb aes-128-ctr aes-192-ctr aes-256-ctr bf-cfb camellia-128-cfb camellia-192-cfb camellia-256-cfb cast5-cfb des-cfb idea-cfb rc2-cfb seed-cfb salsa20 chacha20 and chacha20-ietf'
-    opts='-s -b -p -k -f -t -m -c -a -n -u -U -v -h -A --mtu --help --mptcp -l'
+    opts='-s -b -p -k -f -t -m -c -a -n -u -U -T -v -h -A --mtu --help --mptcp -l'
     cur=${COMP_WORDS[COMP_CWORD]}
     prev="${COMP_WORDS[COMP_CWORD-1]}"
     case "$prev" in
--- a/src/jconf.c
+++ b/src/jconf.c
@@ -338,7 +338,11 @@ read_jconf(const char *file)
                     check_json_value_type(value, json_boolean,
                                           "invalid config file: option 'ipv6_first' must be a boolean");
                     conf.ipv6_first = 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 {
--- a/src/jconf.h
+++ b/src/jconf.h
@@ -105,6 +105,7 @@ typedef struct {
     int mtu;
     int mptcp;
     int ipv6_first;
+	int tcp_tproxy;
 } jconf_t;
 
 jconf_t *read_jconf(const char *file);
--- a/src/redir.c
+++ b/src/redir.c
@@ -71,6 +71,14 @@
 #define IP6T_SO_ORIGINAL_DST 80
 #endif
 
+#ifndef IP_TRANSPARENT
+#define IP_TRANSPARENT       19
+#endif
+
+#ifndef IPV6_TRANSPARENT
+#define IPV6_TRANSPARENT     75
+#endif
+
 #include "includeobfs.h" // I don't want to modify makefile
 #include "jconf.h"
 
@@ -101,18 +109,28 @@ static struct cork_dllist inactive_profi
 static listen_ctx_t *current_profile;
 static struct cork_dllist all_connections;
 
+static int tcp_tproxy = 0; /* use tproxy instead of redirect (for tcp) */
+
 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;
+			}
+		}
+	}
+	
+	if (error) {
+        return -1;
     }
     return 0;
 }
@@ -164,6 +182,23 @@ create_and_bind(const char *addr, const
         if (err == 0) {
             LOGI("tcp port reuse enabled");
         }
+		
+		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) {
@@ -1094,7 +1129,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:huUvA6"
+    while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUTvA6"
                             "O:o:G:g:",
                             long_options, &option_index)) != -1) {
         switch (c) {
@@ -1169,6 +1204,9 @@ main(int argc, char **argv)
         case 'U':
             mode = UDP_ONLY;
             break;
+		case 'T':
+            tcp_tproxy = 1;
+            break;
         case 'v':
             verbose = 1;
             break;
@@ -1255,6 +1293,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;
         }
--- a/src/utils.c
+++ b/src/utils.c
@@ -342,6 +342,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");
