diff --git a/README.md b/README.md
index 56ac7fe..7f9a757 100644
--- a/README.md
+++ b/README.md
@@ -17,27 +17,30 @@ Usage: goodbyedpi.exe [OPTION...]
  -r          replace Host with hoSt
  -s          remove space between host header and its value
  -m          mix Host header case (test.com -> tEsT.cOm)
- -f [value]  set HTTP fragmentation to value
- -k [value]  enable HTTP persistent (keep-alive) fragmentation and set it to value
+ -f <value>  set HTTP fragmentation to value
+ -k <value>  enable HTTP persistent (keep-alive) fragmentation and set it to value
  -n          do not wait for first segment ACK when -k is enabled
- -e [value]  set HTTPS fragmentation to value
+ -e <value>  set HTTPS fragmentation to value
  -a          additional space between Method and Request-URI (enables -s, may break sites)
  -w          try to find and parse HTTP traffic on all processed ports (not only on port 80)
- --port        [value]    additional TCP port to perform fragmentation on (and HTTP tricks with -w)
- --ip-id       [value]    handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).
+ --port        <value>    additional TCP port to perform fragmentation on (and HTTP tricks with -w)
+ --ip-id       <value>    handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).
                           This option can be supplied multiple times.
- --dns-addr    [value]    redirect UDP DNS requests to the supplied IP address (experimental)
- --dns-port    [value]    redirect UDP DNS requests to the supplied port (53 by default)
- --dnsv6-addr  [value]    redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)
- --dnsv6-port  [value]    redirect UDPv6 DNS requests to the supplied port (53 by default)
+ --dns-addr    <value>    redirect UDP DNS requests to the supplied IP address (experimental)
+ --dns-port    <value>    redirect UDP DNS requests to the supplied port (53 by default)
+ --dnsv6-addr  <value>    redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)
+ --dnsv6-port  <value>    redirect UDPv6 DNS requests to the supplied port (53 by default)
  --dns-verb               print verbose DNS redirection messages
- --blacklist   [txtfile]  perform circumvention tricks only to host names and subdomains from
+ --blacklist   <txtfile>  perform circumvention tricks only to host names and subdomains from
                           supplied text file (HTTP Host/TLS SNI).
                           This option can be supplied multiple times.
- --set-ttl     [value]    activate Fake Request Mode and send it with supplied TTL value.
+ --set-ttl     <value>    activate Fake Request Mode and send it with supplied TTL value.
                           DANGEROUS! May break websites in unexpected ways. Use with care.
+ --auto-ttl    [decttl]   activate Fake Request Mode, automatically detect TTL and decrease
+                          it from standard 64 or 128 by decttl (128/64 - TTL - 2 by default).
  --wrong-chksum           activate Fake Request Mode and send it with incorrect TCP checksum.
                           May not work in a VM or with some routers, but is safer than set-ttl.
+ --wrong-seq              activate Fake Request Mode and send it with TCP SEQ/ACK in the past.
  --native-frag            fragment (split) the packets by sending them in smaller packets, without
                           shrinking the Window Size. Works faster (does not slow down the connection)
                           and better.
diff --git a/src/dnsredir.h b/src/dnsredir.h
index 106c6fb..d5fed65 100644
--- a/src/dnsredir.h
+++ b/src/dnsredir.h
@@ -1,3 +1,5 @@
+#ifndef _DNSREDIR_H
+#define _DNSREDIR_H
 #include <stdint.h>
 
 typedef struct conntrack_info {
@@ -34,3 +36,4 @@ int dns_handle_outgoing(const uint32_t srcip[4], const uint16_t srcport,
 
 void flush_dns_cache();
 int dns_is_dns_packet(const char *packet_data, const UINT packet_dataLen, const int outgoing);
+#endif
diff --git a/src/fakepackets.c b/src/fakepackets.c
index 5c9dd74..7fa7750 100644
--- a/src/fakepackets.c
+++ b/src/fakepackets.c
@@ -53,7 +53,8 @@ static int send_fake_data(const HANDLE w_filter,
                           const BOOL is_ipv6,
                           const BOOL is_https,
                           const BYTE set_ttl,
-                          const BYTE set_checksum
+                          const BYTE set_checksum,
+                          const BYTE set_seq
                          ) {
     char packet_fake[MAX_PACKET_SIZE];
     WINDIVERT_ADDRESS addr_new;
@@ -69,6 +70,9 @@ static int send_fake_data(const HANDLE w_filter,
     memcpy(&addr_new, addr, sizeof(WINDIVERT_ADDRESS));
     memcpy(packet_fake, pkt, packetLen);
 
+    addr_new.PseudoTCPChecksum = 0;
+    addr_new.PseudoIPChecksum = 0;
+
     if (!is_ipv6) {
         // IPv4 TCP Data packet
         if (!WinDivertHelperParsePacket(packet_fake, packetLen, &ppIpHdr,
@@ -107,8 +111,15 @@ static int send_fake_data(const HANDLE w_filter,
             ppIpV6Hdr->HopLimit = set_ttl;
     }
 
+    if (set_seq) {
+        // This is the smallest ACK drift Linux can't handle already, since at least v2.6.18.
+        // https://github.com/torvalds/linux/blob/v2.6.18/net/netfilter/nf_conntrack_proto_tcp.c#L395
+        ppTcpHdr->AckNum = htonl(ntohl(ppTcpHdr->AckNum) - 66000);
+        // This is just random, no specifics about this value.
+        ppTcpHdr->SeqNum = htonl(ntohl(ppTcpHdr->SeqNum) - 10000);
+    }
+
     // Recalculate the checksum
-    addr_new.PseudoTCPChecksum = 0;
     WinDivertHelperCalcChecksums(packet_fake, packetLen_new, &addr_new, NULL);
 
     if (set_checksum) {
@@ -127,23 +138,46 @@ static int send_fake_data(const HANDLE w_filter,
     return 0;
 }
 
+static int send_fake_request(const HANDLE w_filter,
+                                  const PWINDIVERT_ADDRESS addr,
+                                  const char *pkt,
+                                  const UINT packetLen,
+                                  const BOOL is_ipv6,
+                                  const BOOL is_https,
+                                  const BYTE set_ttl,
+                                  const BYTE set_checksum,
+                                  const BYTE set_seq
+                                 ) {
+    if (set_ttl) {
+        send_fake_data(w_filter, addr, pkt, packetLen,
+                          is_ipv6, is_https,
+                          set_ttl, FALSE, FALSE);
+    }
+    if (set_checksum) {
+        send_fake_data(w_filter, addr, pkt, packetLen,
+                          is_ipv6, is_https,
+                          FALSE, set_checksum, FALSE);
+    }
+    if (set_seq) {
+        send_fake_data(w_filter, addr, pkt, packetLen,
+                          is_ipv6, is_https,
+                          FALSE, FALSE, set_seq);
+    }
+    return 0;
+}
+
 int send_fake_http_request(const HANDLE w_filter,
                                   const PWINDIVERT_ADDRESS addr,
                                   const char *pkt,
                                   const UINT packetLen,
                                   const BOOL is_ipv6,
                                   const BYTE set_ttl,
-                                  const BYTE set_checksum
+                                  const BYTE set_checksum,
+                                  const BYTE set_seq
                                  ) {
-    return send_fake_data(w_filter,
-                          addr,
-                          pkt,
-                          packetLen,
-                          is_ipv6,
-                          FALSE,
-                          set_ttl,
-                          set_checksum
-           );
+    return send_fake_request(w_filter, addr, pkt, packetLen,
+                          is_ipv6, FALSE,
+                          set_ttl, set_checksum, set_seq);
 }
 
 int send_fake_https_request(const HANDLE w_filter,
@@ -152,15 +186,10 @@ int send_fake_https_request(const HANDLE w_filter,
                                    const UINT packetLen,
                                    const BOOL is_ipv6,
                                    const BYTE set_ttl,
-                                   const BYTE set_checksum
+                                   const BYTE set_checksum,
+                                   const BYTE set_seq
                                  ) {
-    return send_fake_data(w_filter,
-                          addr,
-                          pkt,
-                          packetLen,
-                          is_ipv6,
-                          TRUE,
-                          set_ttl,
-                          set_checksum
-           );
+    return send_fake_request(w_filter, addr, pkt, packetLen,
+                          is_ipv6, TRUE,
+                          set_ttl, set_checksum, set_seq);
 }
diff --git a/src/fakepackets.h b/src/fakepackets.h
index de20c7e..9bb44d4 100644
--- a/src/fakepackets.h
+++ b/src/fakepackets.h
@@ -4,7 +4,8 @@ int send_fake_http_request(const HANDLE w_filter,
                                   const UINT packetLen,
                                   const BOOL is_ipv6,
                                   const BYTE set_ttl,
-                                  const BYTE set_checksum
+                                  const BYTE set_checksum,
+                                  const BYTE set_seq
                                  );
 int send_fake_https_request(const HANDLE w_filter,
                                    const PWINDIVERT_ADDRESS addr,
@@ -12,5 +13,6 @@ int send_fake_https_request(const HANDLE w_filter,
                                    const UINT packetLen,
                                    const BOOL is_ipv6,
                                    const BYTE set_ttl,
-                                   const BYTE set_checksum
+                                   const BYTE set_checksum,
+                                   const BYTE set_seq
                                  );
diff --git a/src/goodbyedpi.c b/src/goodbyedpi.c
index e9c60af..a17e3f0 100644
--- a/src/goodbyedpi.c
+++ b/src/goodbyedpi.c
@@ -16,6 +16,7 @@
 #include "utils/repl_str.h"
 #include "service.h"
 #include "dnsredir.h"
+#include "ttltrack.h"
 #include "blackwhitelist.h"
 #include "fakepackets.h"
 
@@ -94,6 +95,22 @@ WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pA
     } \
 } while (0)
 
+#define TCP_HANDLE_OUTGOING_ADJUST_TTL() do { \
+    if ((packet_v4 && tcp_handle_outgoing(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr, \
+                        ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \
+                        &tcp_conn_info, 0)) \
+        || \
+        (packet_v6 && tcp_handle_outgoing(ppIpV6Hdr->SrcAddr, ppIpV6Hdr->DstAddr, \
+                        ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \
+                        &tcp_conn_info, 1))) \
+    { \
+        ttl_of_fake_packet = tcp_get_auto_ttl(tcp_conn_info.ttl, do_auto_ttl); \
+        if (do_tcp_verb) { \
+            printf("Connection TTL = %d, Fake TTL = %d\n", tcp_conn_info.ttl, ttl_of_fake_packet); \
+        } \
+    } \
+} while (0)
+
 static int running_from_service = 0;
 static HANDLE filters[MAX_FILTERS];
 static int filter_num = 0;
@@ -124,7 +141,9 @@ static struct option long_options[] = {
     {"blacklist",   required_argument, 0,  'b' },
     {"ip-id",       required_argument, 0,  'i' },
     {"set-ttl",     required_argument, 0,  '$' },
+    {"auto-ttl",    optional_argument, 0,  '+' },
     {"wrong-chksum",no_argument,       0,  '%' },
+    {"wrong-seq",   no_argument,       0,  ')' },
     {"native-frag", no_argument,       0,  '*' },
     {"reverse-frag",no_argument,       0,  '(' },
     {0,             0,                 0,   0  }
@@ -487,6 +506,7 @@ int main(int argc, char *argv[]) {
     PWINDIVERT_TCPHDR ppTcpHdr;
     PWINDIVERT_UDPHDR ppUdpHdr;
     conntrack_info_t dns_conn_info;
+    tcp_conntrack_info_t tcp_conn_info;
 
     int do_passivedpi = 0, do_fragment_http = 0,
         do_fragment_http_persistent = 0,
@@ -496,9 +516,11 @@ int main(int argc, char *argv[]) {
         do_http_allports = 0,
         do_host_mixedcase = 0,
         do_dnsv4_redirect = 0, do_dnsv6_redirect = 0,
-        do_dns_verb = 0, do_blacklist = 0,
+        do_dns_verb = 0, do_tcp_verb = 0, do_blacklist = 0,
         do_fake_packet = 0,
+        do_auto_ttl = 0,
         do_wrong_chksum = 0,
+        do_wrong_seq = 0,
         do_native_frag = 0, do_reverse_frag = 0;
     unsigned int http_fragment_size = 0;
     unsigned int https_fragment_size = 0;
@@ -695,6 +717,7 @@ int main(int argc, char *argv[]) {
                 break;
             case 'v':
                 do_dns_verb = 1;
+                do_tcp_verb = 1;
                 break;
             case 'b':
                 do_blacklist = 1;
@@ -707,10 +730,23 @@ int main(int argc, char *argv[]) {
                 do_fake_packet = 1;
                 ttl_of_fake_packet = atoub(optarg, "Set TTL parameter error!");
                 break;
+            case '+':
+                do_fake_packet = 1;
+                do_auto_ttl = 2;
+                if (optarg) {
+                    do_auto_ttl = atoub(optarg, "Set Auto TTL parameter error!");
+                } else if (argv[optind] && argv[optind][0] != '-') {
+                    do_auto_ttl = atoub(argv[optind], "Set Auto TTL parameter error!");
+                }
+                break;
             case '%':
                 do_fake_packet = 1;
                 do_wrong_chksum = 1;
                 break;
+            case ')':
+                do_fake_packet = 1;
+                do_wrong_seq = 1;
+                break;
             case '*':
                 do_native_frag = 1;
                 do_fragment_http_persistent = 1;
@@ -729,27 +765,29 @@ int main(int argc, char *argv[]) {
                 " -s          remove space between host header and its value\n"
                 " -a          additional space between Method and Request-URI (enables -s, may break sites)\n"
                 " -m          mix Host header case (test.com -> tEsT.cOm)\n"
-                " -f [value]  set HTTP fragmentation to value\n"
-                " -k [value]  enable HTTP persistent (keep-alive) fragmentation and set it to value\n"
+                " -f <value>  set HTTP fragmentation to value\n"
+                " -k <value>  enable HTTP persistent (keep-alive) fragmentation and set it to value\n"
                 " -n          do not wait for first segment ACK when -k is enabled\n"
-                " -e [value]  set HTTPS fragmentation to value\n"
+                " -e <value>  set HTTPS fragmentation to value\n"
                 " -w          try to find and parse HTTP traffic on all processed ports (not only on port 80)\n"
-                " --port        [value]    additional TCP port to perform fragmentation on (and HTTP tricks with -w)\n"
-                " --ip-id       [value]    handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\n"
-                " --dns-addr    [value]    redirect UDPv4 DNS requests to the supplied IPv4 address (experimental)\n"
-                " --dns-port    [value]    redirect UDPv4 DNS requests to the supplied port (53 by default)\n"
-                " --dnsv6-addr  [value]    redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n"
-                " --dnsv6-port  [value]    redirect UDPv6 DNS requests to the supplied port (53 by default)\n"
+                " --port        <value>    additional TCP port to perform fragmentation on (and HTTP tricks with -w)\n"
+                " --ip-id       <value>    handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\n"
+                " --dns-addr    <value>    redirect UDPv4 DNS requests to the supplied IPv4 address (experimental)\n"
+                " --dns-port    <value>    redirect UDPv4 DNS requests to the supplied port (53 by default)\n"
+                " --dnsv6-addr  <value>    redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n"
+                " --dnsv6-port  <value>    redirect UDPv6 DNS requests to the supplied port (53 by default)\n"
                 " --dns-verb               print verbose DNS redirection messages\n"
-                " --blacklist   [txtfile]  perform circumvention tricks only to host names and subdomains from\n"
+                " --blacklist   <txtfile>  perform circumvention tricks only to host names and subdomains from\n"
                 "                          supplied text file (HTTP Host/TLS SNI).\n"
                 "                          This option can be supplied multiple times.\n"
-                " --set-ttl     [value]    activate Fake Request Mode and send it with supplied TTL value.\n"
+                " --set-ttl     <value>    activate Fake Request Mode and send it with supplied TTL value.\n"
                 "                          DANGEROUS! May break websites in unexpected ways. Use with care.\n"
-                "                          Could be combined with --wrong-chksum.\n"
+                " --auto-ttl    [decttl]   activate Fake Request Mode, automatically detect TTL and decrease\n"
+                "                          it from standard 64 or 128 by decttl (128/64 - TTL - 2 by default).\n"
                 " --wrong-chksum           activate Fake Request Mode and send it with incorrect TCP checksum.\n"
                 "                          May not work in a VM or with some routers, but is safer than set-ttl.\n"
                 "                          Could be combined with --set-ttl\n"
+                " --wrong-seq              activate Fake Request Mode and send it with TCP SEQ/ACK in the past.\n"
                 " --native-frag            fragment (split) the packets by sending them in smaller packets, without\n"
                 "                          shrinking the Window Size. Works faster (does not slow down the connection)\n"
                 "                          and better.\n"
@@ -776,14 +814,16 @@ int main(int argc, char *argv[]) {
            "hoSt: %d\nHost no space: %d\nAdditional space: %d\n"
            "Mix Host: %d\nHTTP AllPorts: %d\nHTTP Persistent Nowait: %d\n"
            "DNS redirect: %d\nDNSv6 redirect: %d\n"
-           "Fake requests, TTL: %hu\nFake requests, wrong checksum: %d\n",
+           "Fake requests, TTL: %hu (auto: %hu)\nFake requests, wrong checksum: %d\n"
+           "Fake requests, wrong SEQ/ACK: %d\n",
            do_passivedpi, (do_fragment_http ? http_fragment_size : 0),
            (do_fragment_http_persistent ? http_fragment_size : 0),
            (do_fragment_https ? https_fragment_size : 0),
            do_native_frag, do_reverse_frag,
            do_host, do_host_removespace, do_additional_space, do_host_mixedcase,
            do_http_allports, do_fragment_http_persistent_nowait, do_dnsv4_redirect,
-           do_dnsv6_redirect, ttl_of_fake_packet, do_wrong_chksum
+           do_dnsv6_redirect, ttl_of_fake_packet, do_auto_ttl,
+           do_wrong_chksum, do_wrong_seq
           );
 
     if (do_fragment_http && http_fragment_size > 2) {
@@ -922,8 +962,11 @@ int main(int argc, char *argv[]) {
                             printf("Blocked HTTPS website SNI: %s\n", lsni);
 #endif
                             if (do_fake_packet) {
+                                if (do_auto_ttl) {
+                                    TCP_HANDLE_OUTGOING_ADJUST_TTL();
+                                }
                                 send_fake_https_request(w_filter, &addr, packet, packetLen, packet_v6,
-                                                        ttl_of_fake_packet, do_wrong_chksum);
+                                                        ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq);
                             }
                             if (do_native_frag) {
                                 // Signal for native fragmentation code handler
@@ -963,9 +1006,13 @@ int main(int argc, char *argv[]) {
                             should_recalc_checksum = 1;
                         }
 
-                        if (do_fake_packet)
+                        if (do_fake_packet) {
+                            if (do_auto_ttl) {
+                                TCP_HANDLE_OUTGOING_ADJUST_TTL();
+                            }
                             send_fake_http_request(w_filter, &addr, packet, packetLen, packet_v6,
-                                                   ttl_of_fake_packet, do_wrong_chksum);
+                                                   ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq);
+                        }
 
                         if (do_host_mixedcase) {
                             mix_case(host_addr, host_len);
@@ -1078,20 +1125,36 @@ int main(int argc, char *argv[]) {
             else if (packet_type == ipv4_tcp || packet_type == ipv6_tcp) {
                 /* If we got INBOUND SYN+ACK packet */
                 if (addr.Direction == WINDIVERT_DIRECTION_INBOUND &&
-                    ppTcpHdr->Syn == 1 && ppTcpHdr->Ack == 1 &&
-                    !do_native_frag) {
+                    ppTcpHdr->Syn == 1 && ppTcpHdr->Ack == 1) {
                     //printf("Changing Window Size!\n");
                     /*
                      * Window Size is changed even if do_fragment_http_persistent
                      * is enabled as there could be non-HTTP data on port 80
                      */
-                    if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) {
-                        change_window_size(ppTcpHdr, http_fragment_size);
-                        should_recalc_checksum = 1;
+
+                    if (do_fake_packet && do_auto_ttl) {
+                        if (!((packet_v4 && tcp_handle_incoming(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr,
+                                        ppTcpHdr->SrcPort, ppTcpHdr->DstPort,
+                                        0, ppIpHdr->TTL))
+                            ||
+                            (packet_v6 && tcp_handle_incoming(&ppIpV6Hdr->SrcAddr, &ppIpV6Hdr->DstAddr,
+                                        ppTcpHdr->SrcPort, ppTcpHdr->DstPort,
+                                        1, ppIpV6Hdr->HopLimit))))
+                        {
+                            if (do_tcp_verb)
+                                puts("[TCP WARN] Can't add TCP connection record.");
+                        }
                     }
-                    else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) {
-                        change_window_size(ppTcpHdr, https_fragment_size);
-                        should_recalc_checksum = 1;
+
+                    if (!do_native_frag) {
+                        if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) {
+                            change_window_size(ppTcpHdr, http_fragment_size);
+                            should_recalc_checksum = 1;
+                        }
+                        else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) {
+                            change_window_size(ppTcpHdr, https_fragment_size);
+                            should_recalc_checksum = 1;
+                        }
                     }
                 }
             }
diff --git a/src/ttltrack.c b/src/ttltrack.c
new file mode 100644
index 0000000..97aeb82
--- /dev/null
+++ b/src/ttltrack.c
@@ -0,0 +1,235 @@
+/**
+ * TCP (TTL) Connection Tracker for GoodbyeDPI
+ *
+ * Monitors SYN/ACK only, to extract the TTL value of the remote server.
+ *
+ */
+
+#include <windows.h>
+#include <time.h>
+#include <stdio.h>
+#include "goodbyedpi.h"
+#include "ttltrack.h"
+#include "utils/uthash.h"
+
+
+/* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + dstip[16] + srcport[2] + dstport[2]) */
+#define TCP_CONNRECORD_KEY_LEN 37
+
+#define TCP_CLEANUP_INTERVAL_SEC 30
+
+/* HACK!
+ * uthash uses strlen() for HASH_FIND_STR.
+ * We have null bytes in our key, so we can't use strlen()
+ * And since it's always TCP_CONNRECORD_KEY_LEN bytes long,
+ * we don't need to use any string function to determine length.
+ */
+#undef uthash_strlen
+#define uthash_strlen(s) TCP_CONNRECORD_KEY_LEN
+
+typedef struct tcp_connrecord {
+    /* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + dstip[16] + srcport[2] + dstport[2]) */
+    char key[TCP_CONNRECORD_KEY_LEN];
+    time_t time;         /* time when this record was added */
+    uint16_t ttl;
+    UT_hash_handle hh;   /* makes this structure hashable */
+} tcp_connrecord_t;
+
+static time_t last_cleanup = 0;
+static tcp_connrecord_t *conntrack = NULL;
+
+inline static void fill_key_data(char *key, const uint8_t is_ipv6, const uint32_t srcip[4],
+                    const uint32_t dstip[4], const uint16_t srcport, const uint16_t dstport)
+{
+    int offset = 0;
+
+    if (is_ipv6) {
+        *(uint8_t*)(key) = '6';
+        offset += sizeof(uint8_t);
+        ipv6_copy_addr((uint32_t*)(key + offset), srcip);
+        offset += sizeof(uint32_t) * 4;
+        ipv6_copy_addr((uint32_t*)(key + offset), dstip);
+        offset += sizeof(uint32_t) * 4;
+    }
+    else {
+        *(uint8_t*)(key) = '4';
+        offset += sizeof(uint8_t);
+        ipv4_copy_addr((uint32_t*)(key + offset), srcip);
+        offset += sizeof(uint32_t) * 4;
+        ipv4_copy_addr((uint32_t*)(key + offset), dstip);
+        offset += sizeof(uint32_t) * 4;
+    }
+
+    *(uint16_t*)(key + offset) = srcport;
+    offset += sizeof(srcport);
+    *(uint16_t*)(key + offset) = dstport;
+    offset += sizeof(dstport);
+}
+
+inline static void fill_data_from_key(uint8_t *is_ipv6, uint32_t srcip[4], uint32_t dstip[4],
+                                     uint16_t *srcport, uint16_t *dstport, const char *key)
+{
+    int offset = 0;
+
+    if (key[0] == '6') {
+        *is_ipv6 = 1;
+        offset += sizeof(uint8_t);
+        ipv6_copy_addr(srcip, (uint32_t*)(key + offset));
+        offset += sizeof(uint32_t) * 4;
+        ipv6_copy_addr(dstip, (uint32_t*)(key + offset));
+        offset += sizeof(uint32_t) * 4;
+    }
+    else {
+        *is_ipv6 = 0;
+        offset += sizeof(uint8_t);
+        ipv4_copy_addr(srcip, (uint32_t*)(key + offset));
+        offset += sizeof(uint32_t) * 4;
+        ipv4_copy_addr(dstip, (uint32_t*)(key + offset));
+        offset += sizeof(uint32_t) * 4;
+    }
+    *srcport = *(uint16_t*)(key + offset);
+    offset += sizeof(*srcport);
+    *dstport = *(uint16_t*)(key + offset);
+    offset += sizeof(*dstport);
+}
+
+inline static void construct_key(const uint32_t srcip[4], const uint32_t dstip[4],
+                                 const uint16_t srcport, const uint16_t dstport,
+                                 char *key, const uint8_t is_ipv6)
+{
+    debug("Construct key enter\n");
+    if (key) {
+        debug("Constructing key\n");
+        fill_key_data(key, is_ipv6, srcip, dstip, srcport, dstport);
+    }
+    debug("Construct key end\n");
+}
+
+inline static void deconstruct_key(const char *key, const tcp_connrecord_t *connrecord,
+                                   tcp_conntrack_info_t *conn_info)
+{
+    debug("Deconstruct key enter\n");
+    if (key && conn_info) {
+        debug("Deconstructing key\n");
+        fill_data_from_key(&conn_info->is_ipv6,
+                           conn_info->srcip, conn_info->dstip,
+                           &conn_info->srcport, &conn_info->dstport,
+                           key);
+
+        conn_info->ttl = connrecord->ttl;
+    }
+    debug("Deconstruct key end\n");
+}
+
+static int check_get_tcp_conntrack_key(const char *key, tcp_connrecord_t **connrecord) {
+    tcp_connrecord_t *tmp_connrecord = NULL;
+    if (!conntrack) return FALSE;
+
+    HASH_FIND_STR(conntrack, key, tmp_connrecord);
+    if (tmp_connrecord) {
+        if (connrecord)
+            *connrecord = tmp_connrecord;
+        debug("check_get_tcp_conntrack_key found key\n");
+        return TRUE;
+    }
+    debug("check_get_tcp_conntrack_key key not found\n");
+    return FALSE;
+}
+
+static int add_tcp_conntrack(const uint32_t srcip[4], const uint32_t dstip[4],
+                             const uint16_t srcport, const uint16_t dstport,
+                             const uint8_t is_ipv6, const uint8_t ttl
+                            )
+{
+    if (!(srcip && srcport && dstip && dstport))
+        return FALSE;
+
+    tcp_connrecord_t *tmp_connrecord = malloc(sizeof(tcp_connrecord_t));
+    construct_key(srcip, dstip, srcport, dstport, tmp_connrecord->key, is_ipv6);
+
+    if (!check_get_tcp_conntrack_key(tmp_connrecord->key, NULL)) {
+        tmp_connrecord->time = time(NULL);
+        tmp_connrecord->ttl = ttl;
+        HASH_ADD_STR(conntrack, key, tmp_connrecord);
+        debug("Added TCP conntrack %u:%hu - %u:%hu\n", srcip[0], ntohs(srcport), dstip[0], ntohs(dstport));
+        return TRUE;
+    }
+    debug("Not added TCP conntrack %u:%hu - %u:%hu\n", srcip[0], ntohs(srcport), dstip[0], ntohs(dstport));
+    free(tmp_connrecord);
+    return FALSE;
+}
+
+static void tcp_cleanup() {
+    tcp_connrecord_t *tmp_connrecord, *tmp_connrecord2 = NULL;
+
+    if (last_cleanup == 0) {
+        last_cleanup = time(NULL);
+        return;
+    }
+
+    if (difftime(time(NULL), last_cleanup) >= TCP_CLEANUP_INTERVAL_SEC) {
+        last_cleanup = time(NULL);
+
+        HASH_ITER(hh, conntrack, tmp_connrecord, tmp_connrecord2) {
+            if (difftime(last_cleanup, tmp_connrecord->time) >= TCP_CLEANUP_INTERVAL_SEC) {
+                HASH_DEL(conntrack, tmp_connrecord);
+                free(tmp_connrecord);
+            }
+        }
+    }
+}
+
+int tcp_handle_incoming(const uint32_t srcip[4], const uint32_t dstip[4],
+                        const uint16_t srcport, const uint16_t dstport,
+                        const uint8_t is_ipv6, const uint8_t ttl)
+{
+    tcp_cleanup();
+
+    debug("trying to add TCP srcport = %hu, dstport = %hu\n", ntohs(srcport), ntohs(dstport));
+    return add_tcp_conntrack(srcip, dstip, srcport, dstport, is_ipv6, ttl);
+
+    debug("____tcp_handle_incoming FALSE: srcport = %hu, dstport = %hu\n", ntohs(srcport), ntohs(dstport));
+    return FALSE;
+}
+
+int tcp_handle_outgoing(const uint32_t srcip[4], const uint32_t dstip[4],
+                        const uint16_t srcport, const uint16_t dstport,
+                        tcp_conntrack_info_t *conn_info,
+                        const uint8_t is_ipv6)
+{
+    char key[TCP_CONNRECORD_KEY_LEN];
+    tcp_connrecord_t *tmp_connrecord = NULL;
+
+    if (!conn_info)
+        return FALSE;
+
+    tcp_cleanup();
+    construct_key(dstip, srcip, dstport, srcport, key, is_ipv6);
+    if (check_get_tcp_conntrack_key(key, &tmp_connrecord) && tmp_connrecord) {
+        /* Connection exists in conntrack, moving on */
+        deconstruct_key(key, tmp_connrecord, conn_info);
+        HASH_DEL(conntrack, tmp_connrecord);
+        free(tmp_connrecord);
+        debug("____tcp_handle_outgoing TRUE: srcport = %hu\n", ntohs(srcport));
+        return TRUE;
+    }
+
+    debug("____tcp_handle_outgoing FALSE: srcport = %hu\n", ntohs(srcport));
+    return FALSE;
+}
+
+int tcp_get_auto_ttl(const uint8_t ttl, const uint8_t decrease_for) {
+    uint8_t ttl_of_fake_packet = 0;
+
+    if (ttl > 64 && ttl < 128) {
+        ttl_of_fake_packet = 128 - ttl - decrease_for;
+    }
+    else if (ttl > 34 && ttl < 64) {
+        ttl_of_fake_packet = 64 - ttl - decrease_for;
+    }
+    else {
+        ttl_of_fake_packet = 0;
+    }
+
+    return ttl_of_fake_packet;
+}
\ No newline at end of file
diff --git a/src/ttltrack.h b/src/ttltrack.h
new file mode 100644
index 0000000..2447588
--- /dev/null
+++ b/src/ttltrack.h
@@ -0,0 +1,25 @@
+#ifndef _TTLTRACK_H
+#define _TTLTRACK_H
+#include <stdint.h>
+#include "dnsredir.h"
+
+typedef struct tcp_conntrack_info {
+    uint8_t  is_ipv6;
+    uint8_t  ttl;
+    uint32_t srcip[4];
+    uint16_t srcport;
+    uint32_t dstip[4];
+    uint16_t dstport;
+} tcp_conntrack_info_t;
+
+int tcp_handle_incoming(const uint32_t srcip[4], const uint32_t dstip[4],
+                        const uint16_t srcport, const uint16_t dstport,
+                        const uint8_t is_ipv6, const uint8_t ttl);
+
+int tcp_handle_outgoing(const uint32_t srcip[4], const uint32_t dstip[4],
+                        const uint16_t srcport, const uint16_t dstport,
+                        tcp_conntrack_info_t *conn_info,
+                        const uint8_t is_ipv6);
+
+int tcp_get_auto_ttl(const uint8_t ttl, const uint8_t decrease_for);
+#endif