From eda98bb4942dcf2038205faaaaf256a765b279b3 Mon Sep 17 00:00:00 2001 From: Mike Wang Date: Wed, 25 Jun 2025 02:14:16 +0800 Subject: [PATCH] feat: Send payload early for incoming TCP connection & Remove TFO Cookie --- include/rawsend.h | 3 +- include/srcinfo.h | 34 +++++ src/ipv4ipt.c | 148 ++++++++++++------- src/ipv4nft.c | 103 ++++++++----- src/ipv6ipt.c | 143 +++++++++++------- src/ipv6nft.c | 102 ++++++++----- src/mainfun.c | 12 +- src/nfqueue.c | 37 +++-- src/rawsend.c | 366 ++++++++++++++++++++++++++++++++++++++-------- src/srcinfo.c | 130 ++++++++++++++++ 10 files changed, 824 insertions(+), 254 deletions(-) create mode 100644 include/srcinfo.h create mode 100644 src/srcinfo.c diff --git a/include/rawsend.h b/include/rawsend.h index f118a22..71a6bd8 100644 --- a/include/rawsend.h +++ b/include/rawsend.h @@ -27,6 +27,7 @@ int fh_rawsend_setup(void); void fh_rawsend_cleanup(void); -int fh_rawsend_handle(struct sockaddr_ll *sll, uint8_t *pkt_data, int pkt_len); +int fh_rawsend_handle(struct sockaddr_ll *sll, uint8_t *pkt_data, int pkt_len, + int *modified); #endif /* FH_RAWSEND_H */ diff --git a/include/srcinfo.h b/include/srcinfo.h new file mode 100644 index 0000000..e34b0dd --- /dev/null +++ b/include/srcinfo.h @@ -0,0 +1,34 @@ +/* + * srcinfo.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef FH_SRCINFO_H +#define FH_SRCINFO_H + +#include +#include + +int fh_srcinfo_setup(void); + +void fh_srcinfo_cleanup(void); + +int fh_srcinfo_put(struct sockaddr *addr, uint8_t ttl, uint8_t hwaddr[8]); + +int fh_srcinfo_get(struct sockaddr *addr, uint8_t *ttl, uint8_t hwaddr[8]); + +#endif /* FH_SRCINFO_H */ diff --git a/src/ipv4ipt.c b/src/ipv4ipt.c index f8a3518..b726c3c 100644 --- a/src/ipv4ipt.c +++ b/src/ipv4ipt.c @@ -33,15 +33,29 @@ static int ipt4_iface_setup(void) char iface_str[IFNAMSIZ]; size_t i; int res; - char *ipt_alliface_cmd[] = {"iptables", "-w", "-t", "mangle", "-A", - "FAKEHTTP", "-j", "FAKEHTTP_R", NULL}; + char *ipt_alliface_src_cmd[] = {"iptables", "-w", "-t", + "mangle", "-A", "FAKEHTTP_S", + "-j", "FAKEHTTP_R", NULL}; - char *ipt_iface_cmd[] = {"iptables", "-w", "-t", "mangle", - "-A", "FAKEHTTP", "-i", iface_str, - "-j", "FAKEHTTP_R", NULL}; + char *ipt_alliface_dst_cmd[] = {"iptables", "-w", "-t", + "mangle", "-A", "FAKEHTTP_D", + "-j", "FAKEHTTP_R", NULL}; + + char *ipt_iface_src_cmd[] = {"iptables", "-w", "-t", "mangle", + "-A", "FAKEHTTP_S", "-i", iface_str, + "-j", "FAKEHTTP_R", NULL}; + + char *ipt_iface_dst_cmd[] = {"iptables", "-w", "-t", "mangle", + "-A", "FAKEHTTP_D", "-o", iface_str, + "-j", "FAKEHTTP_R", NULL}; if (g_ctx.alliface) { - res = fh_execute_command(ipt_alliface_cmd, 0, NULL); + res = fh_execute_command(ipt_alliface_src_cmd, 0, NULL); + if (res < 0) { + E(T(fh_execute_command)); + return -1; + } + res = fh_execute_command(ipt_alliface_dst_cmd, 0, NULL); if (res < 0) { E(T(fh_execute_command)); return -1; @@ -56,7 +70,13 @@ static int ipt4_iface_setup(void) return -1; } - res = fh_execute_command(ipt_iface_cmd, 0, NULL); + res = fh_execute_command(ipt_iface_src_cmd, 0, NULL); + if (res < 0) { + E(T(fh_execute_command)); + return -1; + } + + res = fh_execute_command(ipt_iface_dst_cmd, 0, NULL); if (res < 0) { E(T(fh_execute_command)); return -1; @@ -72,74 +92,105 @@ int fh_ipt4_setup(void) size_t i, ipt_cmds_cnt, ipt_opt_cmds_cnt; int res; char *ipt_cmds[][32] = { - {"iptables", "-w", "-t", "mangle", "-N", "FAKEHTTP", NULL}, + {"iptables", "-w", "-t", "mangle", "-N", "FAKEHTTP_S", NULL}, + + {"iptables", "-w", "-t", "mangle", "-N", "FAKEHTTP_D", NULL}, {"iptables", "-w", "-t", "mangle", "-I", "PREROUTING", "-j", - "FAKEHTTP", NULL}, + "FAKEHTTP_S", NULL}, + + {"iptables", "-w", "-t", "mangle", "-I", "POSTROUTING", "-j", + "FAKEHTTP_D", NULL}, {"iptables", "-w", "-t", "mangle", "-N", "FAKEHTTP_R", NULL}, /* - exclude marked packets + exclude local IPs (from source) */ - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-m", "mark", - "--mark", xmark_str, "-j", "CONNMARK", "--set-xmark", xmark_str, - NULL}, + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "0.0.0.0/8", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-m", - "connmark", "--mark", xmark_str, "-j", "MARK", "--set-xmark", - xmark_str, NULL}, + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "10.0.0.0/8", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-m", "mark", - "--mark", xmark_str, "-j", "RETURN", NULL}, + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "100.64.0.0/10", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "127.0.0.0/8", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "169.254.0.0/16", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "172.16.0.0/12", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "192.168.0.0/16", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "224.0.0.0/3", "-j", "RETURN", NULL}, /* - exclude local IPs + exclude local IPs (to destination) */ - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "0.0.0.0/8", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "10.0.0.0/8", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "100.64.0.0/10", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "127.0.0.0/8", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "169.254.0.0/16", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "172.16.0.0/12", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "192.168.0.0/16", "-j", "RETURN", NULL}, - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "224.0.0.0/3", "-j", "RETURN", NULL}, + /* + exclude marked packets + */ + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-m", "mark", + "--mark", xmark_str, "-j", "RETURN", NULL}, + /* send to nfqueue */ {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-p", "tcp", - "--tcp-flags", "ACK,FIN,RST", "ACK", "-j", "NFQUEUE", + "--tcp-flags", "SYN,FIN,RST", "SYN", "-j", "NFQUEUE", "--queue-bypass", "--queue-num", nfqnum_str, NULL}}; char *ipt_opt_cmds[][32] = { /* - exclude packets from connections with more than 32 packets - */ - {"iptables", "-w", "-t", "mangle", "-I", "FAKEHTTP_R", "-m", - "connbytes", "!", "--connbytes", "0:32", "--connbytes-dir", "both", - "--connbytes-mode", "packets", "-j", "RETURN", NULL}, - - /* - exclude big packets + Also enqueue some of the early ACK packets to ensure the packet + order. This rule is optional. We do not verify its execution + result. */ - {"iptables", "-w", "-t", "mangle", "-I", "FAKEHTTP_R", "-m", "length", - "!", "--length", "0:120", "-j", "RETURN", NULL}}; + {"iptables", "-w", + "-t", "mangle", + "-A", "FAKEHTTP_R", + "-p", "tcp", + "--tcp-flags", "SYN,ACK,FIN,RST", + "ACK", "-m", + "connbytes", "--connbytes", + "2:4", "--connbytes-dir", + "both", "--connbytes-mode", + "packets", "-j", + "NFQUEUE", "--queue-bypass", + "--queue-num", nfqnum_str, + NULL}}; ipt_cmds_cnt = sizeof(ipt_cmds) / sizeof(*ipt_cmds); ipt_opt_cmds_cnt = sizeof(ipt_opt_cmds) / sizeof(*ipt_opt_cmds); @@ -187,26 +238,21 @@ void fh_ipt4_cleanup(void) char *ipt_cmds[][32] = { {"iptables", "-w", "-t", "mangle", "-F", "FAKEHTTP_R", NULL}, - {"iptables", "-w", "-t", "mangle", "-F", "FAKEHTTP", NULL}, + {"iptables", "-w", "-t", "mangle", "-F", "FAKEHTTP_S", NULL}, - {"iptables", "-w", "-t", "mangle", "-D", "PREROUTING", "-j", - "FAKEHTTP", NULL}, - - {"iptables", "-w", "-t", "mangle", "-D", "INPUT", "-j", "FAKEHTTP", - NULL}, - - {"iptables", "-w", "-t", "mangle", "-D", "FORWARD", "-j", "FAKEHTTP", - NULL}, + {"iptables", "-w", "-t", "mangle", "-F", "FAKEHTTP_D", NULL}, - {"iptables", "-w", "-t", "mangle", "-D", "OUTPUT", "-j", "FAKEHTTP", - NULL}, + {"iptables", "-w", "-t", "mangle", "-D", "PREROUTING", "-j", + "FAKEHTTP_S", NULL}, {"iptables", "-w", "-t", "mangle", "-D", "POSTROUTING", "-j", - "FAKEHTTP", NULL}, + "FAKEHTTP_D", NULL}, {"iptables", "-w", "-t", "mangle", "-X", "FAKEHTTP_R", NULL}, - {"iptables", "-w", "-t", "mangle", "-X", "FAKEHTTP", NULL}}; + {"iptables", "-w", "-t", "mangle", "-X", "FAKEHTTP_S", NULL}, + + {"iptables", "-w", "-t", "mangle", "-X", "FAKEHTTP_D", NULL}}; cnt = sizeof(ipt_cmds) / sizeof(*ipt_cmds); for (i = 0; i < cnt; i++) { diff --git a/src/ipv4nft.c b/src/ipv4nft.c index fdb4cbb..8621857 100644 --- a/src/ipv4nft.c +++ b/src/ipv4nft.c @@ -41,12 +41,24 @@ static int nft4_iface_setup(void) E("ERROR: snprintf(): %s", "failure"); return -1; } + res = fh_execute_command(nft_iface_cmd, 0, NULL); + if (res < 0) { + E(T(fh_execute_command)); + return -1; + } + res = snprintf(nftstr, sizeof(nftstr), + "add rule ip fakehttp fh_postrouting jump fh_rules"); + if (res < 0 || (size_t) res >= sizeof(nftstr)) { + E("ERROR: snprintf(): %s", "failure"); + return -1; + } res = fh_execute_command(nft_iface_cmd, 0, NULL); if (res < 0) { E(T(fh_execute_command)); return -1; } + return 0; } @@ -59,7 +71,20 @@ static int nft4_iface_setup(void) E("ERROR: snprintf(): %s", "failure"); return -1; } + res = fh_execute_command(nft_iface_cmd, 0, NULL); + if (res < 0) { + E(T(fh_execute_command)); + return -1; + } + res = snprintf( + nftstr, sizeof(nftstr), + "add rule ip fakehttp fh_postrouting oifname \"%s\" jump fh_rules", + g_ctx.iface[i]); + if (res < 0 || (size_t) res >= sizeof(nftstr)) { + E("ERROR: snprintf(): %s", "failure"); + return -1; + } res = fh_execute_command(nft_iface_cmd, 0, NULL); if (res < 0) { E(T(fh_execute_command)); @@ -72,7 +97,6 @@ static int nft4_iface_setup(void) int fh_nft4_setup(void) { - size_t i, nft_opt_cmds_cnt; int res; char *nft_cmd[] = {"nft", "-f", "-", NULL}; char nft_conf_buff[2048]; @@ -81,23 +105,8 @@ int fh_nft4_setup(void) " chain fh_prerouting {\n" " type filter hook prerouting priority mangle - 5;\n" " policy accept;\n" - " }\n" - "\n" - " chain fh_rules {\n" - /* - exclude marked packets - */ - " meta mark and %" PRIu32 " == %" PRIu32 - " ct mark set ct mark and %" PRIu32 " xor %" PRIu32 ";\n" - - " ct mark and %" PRIu32 " == %" PRIu32 - " meta mark set mark and %" PRIu32 " xor %" PRIu32 ";\n" - - " meta mark and %" PRIu32 " == %" PRIu32 " return;\n" - - /* - exclude local IPs + exclude local IPs (from source) */ " ip saddr 0.0.0.0/8 return;\n" " ip saddr 10.0.0.0/8 return;\n" @@ -107,52 +116,70 @@ int fh_nft4_setup(void) " ip saddr 172.16.0.0/12 return;\n" " ip saddr 192.168.0.0/16 return;\n" " ip saddr 224.0.0.0/3 return;\n" - + " }\n" + "\n" + " chain fh_postrouting {\n" + " type filter hook postrouting priority srcnat + 5;\n" + " policy accept;\n" + /* + exclude local IPs (to destination) + */ + " ip daddr 0.0.0.0/8 return;\n" + " ip daddr 10.0.0.0/8 return;\n" + " ip daddr 100.64.0.0/10 return;\n" + " ip daddr 127.0.0.0/8 return;\n" + " ip daddr 169.254.0.0/16 return;\n" + " ip daddr 172.16.0.0/12 return;\n" + " ip daddr 192.168.0.0/16 return;\n" + " ip daddr 224.0.0.0/3 return;\n" + " }\n" + "\n" + " chain fh_rules {\n" + /* + exclude marked packets + */ + " meta mark and %" PRIu32 " == %" PRIu32 " return;\n" /* send to nfqueue */ - " tcp flags & (fin | rst | ack) == ack queue num %" PRIu32 + " tcp flags & (syn | fin | rst) == syn queue num %" PRIu32 " bypass;\n" " }\n" "}\n"; - char *nft_opt_cmds[][32] = { - /* - exclude packets from connections with more than 32 packets - */ - {"nft", "insert rule ip fakehttp fh_rules ct packets > 32 return", - NULL}, - - /* - exclude big packets - */ - {"nft", "insert rule ip fakehttp fh_rules meta length > 120 return", - NULL}}; + char *nft_conf_opt_fmt = + "add rule ip fakehttp fh_rules tcp flags & (syn | ack | fin | rst) " + "== ack ct packets 2-4 queue num %" PRIu32 " bypass;\n"; - nft_opt_cmds_cnt = sizeof(nft_opt_cmds) / sizeof(*nft_opt_cmds); + fh_nft4_cleanup(); res = snprintf(nft_conf_buff, sizeof(nft_conf_buff), nft_conf_fmt, - g_ctx.fwmask, g_ctx.fwmark, ~g_ctx.fwmask, g_ctx.fwmark, - g_ctx.fwmask, g_ctx.fwmark, ~g_ctx.fwmask, g_ctx.fwmark, g_ctx.fwmask, g_ctx.fwmark, g_ctx.nfqnum); if (res < 0 || (size_t) res >= sizeof(nft_conf_buff)) { E("ERROR: snprintf(): %s", "failure"); return -1; } - fh_nft4_cleanup(); - res = fh_execute_command(nft_cmd, 0, nft_conf_buff); if (res < 0) { E(T(fh_execute_command)); return -1; } - for (i = 0; i < nft_opt_cmds_cnt; i++) { - fh_execute_command(nft_opt_cmds[i], 1, NULL); + /* + Also enqueue some of the early ACK packets to ensure the packet order. + This rule is optional. We do not verify its execution result. + */ + res = snprintf(nft_conf_buff, sizeof(nft_conf_buff), nft_conf_opt_fmt, + g_ctx.nfqnum); + if (res < 0 || (size_t) res >= sizeof(nft_conf_buff)) { + E("ERROR: snprintf(): %s", "failure"); + return -1; } + fh_execute_command(nft_cmd, 0, nft_conf_buff); + res = nft4_iface_setup(); if (res < 0) { E(T(nft4_iface_setup)); diff --git a/src/ipv6ipt.c b/src/ipv6ipt.c index 7154c0a..4718627 100644 --- a/src/ipv6ipt.c +++ b/src/ipv6ipt.c @@ -33,16 +33,29 @@ static int ipt6_iface_setup(void) char iface_str[IFNAMSIZ]; size_t i; int res; - char *ipt_alliface_cmd[] = {"ip6tables", "-w", "-t", - "mangle", "-A", "FAKEHTTP", - "-j", "FAKEHTTP_R", NULL}; + char *ipt_alliface_src_cmd[] = {"ip6tables", "-w", "-t", + "mangle", "-A", "FAKEHTTP_S", + "-j", "FAKEHTTP_R", NULL}; - char *ipt_iface_cmd[] = {"ip6tables", "-w", "-t", "mangle", - "-A", "FAKEHTTP", "-i", iface_str, - "-j", "FAKEHTTP_R", NULL}; + char *ipt_alliface_dst_cmd[] = {"ip6tables", "-w", "-t", + "mangle", "-A", "FAKEHTTP_D", + "-j", "FAKEHTTP_R", NULL}; + + char *ipt_iface_src_cmd[] = {"ip6tables", "-w", "-t", "mangle", + "-A", "FAKEHTTP_S", "-i", iface_str, + "-j", "FAKEHTTP_R", NULL}; + + char *ipt_iface_dst_cmd[] = {"ip6tables", "-w", "-t", "mangle", + "-A", "FAKEHTTP_D", "-o", iface_str, + "-j", "FAKEHTTP_R", NULL}; if (g_ctx.alliface) { - res = fh_execute_command(ipt_alliface_cmd, 0, NULL); + res = fh_execute_command(ipt_alliface_src_cmd, 0, NULL); + if (res < 0) { + E(T(fh_execute_command)); + return -1; + } + res = fh_execute_command(ipt_alliface_dst_cmd, 0, NULL); if (res < 0) { E(T(fh_execute_command)); return -1; @@ -57,7 +70,13 @@ static int ipt6_iface_setup(void) return -1; } - res = fh_execute_command(ipt_iface_cmd, 0, NULL); + res = fh_execute_command(ipt_iface_src_cmd, 0, NULL); + if (res < 0) { + E(T(fh_execute_command)); + return -1; + } + + res = fh_execute_command(ipt_iface_dst_cmd, 0, NULL); if (res < 0) { E(T(fh_execute_command)); return -1; @@ -73,71 +92,98 @@ int fh_ipt6_setup(void) size_t i, ipt_cmds_cnt, ipt_opt_cmds_cnt; int res; char *ipt_cmds[][32] = { - {"ip6tables", "-w", "-t", "mangle", "-N", "FAKEHTTP", NULL}, + {"ip6tables", "-w", "-t", "mangle", "-N", "FAKEHTTP_S", NULL}, + + {"ip6tables", "-w", "-t", "mangle", "-N", "FAKEHTTP_D", NULL}, {"ip6tables", "-w", "-t", "mangle", "-I", "PREROUTING", "-j", - "FAKEHTTP", NULL}, + "FAKEHTTP_S", NULL}, + + {"ip6tables", "-w", "-t", "mangle", "-I", "POSTROUTING", "-j", + "FAKEHTTP_D", NULL}, {"ip6tables", "-w", "-t", "mangle", "-N", "FAKEHTTP_R", NULL}, /* - exclude marked packets + exclude special IPv6 addresses (from source) */ - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-m", "mark", - "--mark", xmark_str, "-j", "CONNMARK", "--set-xmark", xmark_str, - NULL}, + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", "::/127", + "-j", "RETURN", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-m", - "connmark", "--mark", xmark_str, "-j", "MARK", "--set-xmark", - xmark_str, NULL}, + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "::ffff:0:0/96", "-j", "RETURN", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-m", "mark", - "--mark", xmark_str, "-j", "RETURN", NULL}, + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "64:ff9b::/96", "-j", "RETURN", NULL}, + + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "64:ff9b:1::/48", "-j", "RETURN", NULL}, + + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "2002::/16", "-j", "RETURN", NULL}, + + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "fc00::/7", "-j", "RETURN", NULL}, + + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_S", "-s", + "fe80::/10", "-j", "RETURN", NULL}, /* - exclude special IPv6 addresses + exclude special IPv6 addresses (to destination) */ - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", "::/127", + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "::/127", "-j", "RETURN", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "::ffff:0:0/96", "-j", "RETURN", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "64:ff9b::/96", "-j", "RETURN", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "64:ff9b:1::/48", "-j", "RETURN", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "2002::/16", "-j", "RETURN", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "fc00::/7", "-j", "RETURN", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-s", + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_D", "-d", "fe80::/10", "-j", "RETURN", NULL}, + /* + exclude marked packets + */ + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-m", "mark", + "--mark", xmark_str, "-j", "RETURN", NULL}, + /* send to nfqueue */ {"ip6tables", "-w", "-t", "mangle", "-A", "FAKEHTTP_R", "-p", "tcp", - "--tcp-flags", "ACK,FIN,RST", "ACK", "-j", "NFQUEUE", + "--tcp-flags", "SYN,FIN,RST", "SYN", "-j", "NFQUEUE", "--queue-bypass", "--queue-num", nfqnum_str, NULL}}; char *ipt_opt_cmds[][32] = { /* - exclude packets from connections with more than 32 packets - */ - {"ip6tables", "-w", "-t", "mangle", "-I", "FAKEHTTP_R", "-m", - "connbytes", "!", "--connbytes", "0:32", "--connbytes-dir", "both", - "--connbytes-mode", "packets", "-j", "RETURN", NULL}, - - /* - exclude big packets + Also enqueue some of the early ACK packets to ensure the packet + order. This rule is optional. We do not verify its execution + result. */ - {"ip6tables", "-w", "-t", "mangle", "-I", "FAKEHTTP_R", "-m", "length", - "!", "--length", "0:120", "-j", "RETURN", NULL}}; + {"ip6tables", "-w", + "-t", "mangle", + "-A", "FAKEHTTP_R", + "-p", "tcp", + "--tcp-flags", "SYN,ACK,FIN,RST", + "ACK", "-m", + "connbytes", "--connbytes", + "2:4", "--connbytes-dir", + "both", "--connbytes-mode", + "packets", "-j", + "NFQUEUE", "--queue-bypass", + "--queue-num", nfqnum_str, + NULL}}; ipt_cmds_cnt = sizeof(ipt_cmds) / sizeof(*ipt_cmds); ipt_opt_cmds_cnt = sizeof(ipt_opt_cmds) / sizeof(*ipt_opt_cmds); @@ -185,26 +231,21 @@ void fh_ipt6_cleanup(void) char *ipt_cmds[][32] = { {"ip6tables", "-w", "-t", "mangle", "-F", "FAKEHTTP_R", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-F", "FAKEHTTP", NULL}, + {"ip6tables", "-w", "-t", "mangle", "-F", "FAKEHTTP_S", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-D", "PREROUTING", "-j", - "FAKEHTTP", NULL}, - - {"ip6tables", "-w", "-t", "mangle", "-D", "INPUT", "-j", "FAKEHTTP", - NULL}, - - {"ip6tables", "-w", "-t", "mangle", "-D", "FORWARD", "-j", "FAKEHTTP", - NULL}, + {"ip6tables", "-w", "-t", "mangle", "-F", "FAKEHTTP_D", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-D", "OUTPUT", "-j", "FAKEHTTP", - NULL}, + {"ip6tables", "-w", "-t", "mangle", "-D", "PREROUTING", "-j", + "FAKEHTTP_S", NULL}, {"ip6tables", "-w", "-t", "mangle", "-D", "POSTROUTING", "-j", - "FAKEHTTP", NULL}, + "FAKEHTTP_D", NULL}, {"ip6tables", "-w", "-t", "mangle", "-X", "FAKEHTTP_R", NULL}, - {"ip6tables", "-w", "-t", "mangle", "-X", "FAKEHTTP", NULL}}; + {"ip6tables", "-w", "-t", "mangle", "-X", "FAKEHTTP_S", NULL}, + + {"ip6tables", "-w", "-t", "mangle", "-X", "FAKEHTTP_D", NULL}}; cnt = sizeof(ipt_cmds) / sizeof(*ipt_cmds); for (i = 0; i < cnt; i++) { diff --git a/src/ipv6nft.c b/src/ipv6nft.c index 24fc188..98e9748 100644 --- a/src/ipv6nft.c +++ b/src/ipv6nft.c @@ -41,12 +41,24 @@ static int nft6_iface_setup(void) E("ERROR: snprintf(): %s", "failure"); return -1; } + res = fh_execute_command(nft_iface_cmd, 0, NULL); + if (res < 0) { + E(T(fh_execute_command)); + return -1; + } + res = snprintf(nftstr, sizeof(nftstr), + "add rule ip6 fakehttp fh_postrouting jump fh_rules"); + if (res < 0 || (size_t) res >= sizeof(nftstr)) { + E("ERROR: snprintf(): %s", "failure"); + return -1; + } res = fh_execute_command(nft_iface_cmd, 0, NULL); if (res < 0) { E(T(fh_execute_command)); return -1; } + return 0; } @@ -59,7 +71,20 @@ static int nft6_iface_setup(void) E("ERROR: snprintf(): %s", "failure"); return -1; } + res = fh_execute_command(nft_iface_cmd, 0, NULL); + if (res < 0) { + E(T(fh_execute_command)); + return -1; + } + res = snprintf(nftstr, sizeof(nftstr), + "add rule ip6 fakehttp fh_postrouting oifname \"%s\" " + "jump fh_rules", + g_ctx.iface[i]); + if (res < 0 || (size_t) res >= sizeof(nftstr)) { + E("ERROR: snprintf(): %s", "failure"); + return -1; + } res = fh_execute_command(nft_iface_cmd, 0, NULL); if (res < 0) { E(T(fh_execute_command)); @@ -72,7 +97,6 @@ static int nft6_iface_setup(void) int fh_nft6_setup(void) { - size_t i, nft_opt_cmds_cnt; int res; char *nft_cmd[] = {"nft", "-f", "-", NULL}; char nft_conf_buff[2048]; @@ -81,23 +105,8 @@ int fh_nft6_setup(void) " chain fh_prerouting {\n" " type filter hook prerouting priority mangle - 5;\n" " policy accept;\n" - " }\n" - "\n" - " chain fh_rules {\n" - /* - exclude marked packets - */ - " meta mark and %" PRIu32 " == %" PRIu32 - " ct mark set ct mark and %" PRIu32 " xor %" PRIu32 ";\n" - - " ct mark and %" PRIu32 " == %" PRIu32 - " meta mark set mark and %" PRIu32 " xor %" PRIu32 ";\n" - - " meta mark and %" PRIu32 " == %" PRIu32 " return;\n" - - /* - exclude special IPv6 addresses + exclude special IPv6 addresses (from source) */ " ip6 saddr ::/127 return;\n" " ip6 saddr ::ffff:0:0/96 return;\n" @@ -106,52 +115,71 @@ int fh_nft6_setup(void) " ip6 saddr 2002::/16 return;\n" " ip6 saddr fc00::/7 return;\n" " ip6 saddr fe80::/10 return;\n" - + " }\n" + "\n" + " chain fh_postrouting {\n" + " type filter hook postrouting priority srcnat + 5;\n" + " policy accept;\n" /* - send to nfqueue + exclude special IPv6 addresses (to destination) */ - " tcp flags & (fin | rst | ack) == ack queue num %" PRIu32 - " bypass;\n" - + " ip6 daddr ::/127 return;\n" + " ip6 daddr ::ffff:0:0/96 return;\n" + " ip6 daddr 64:ff9b::/96 return;\n" + " ip6 daddr 64:ff9b:1::/48 return;\n" + " ip6 daddr 2002::/16 return;\n" + " ip6 daddr fc00::/7 return;\n" + " ip6 daddr fe80::/10 return;\n" " }\n" - "}\n"; + "\n" + " chain fh_rules {\n" - char *nft_opt_cmds[][32] = { /* - exclude packets from connections with more than 32 packets + exclude marked packets */ - {"nft", "insert rule ip6 fakehttp fh_rules ct packets > 32 return", - NULL}, + " meta mark and %" PRIu32 " == %" PRIu32 " return;\n" /* - exclude big packets + send to nfqueue */ - {"nft", "insert rule ip6 fakehttp fh_rules meta length > 120 return", - NULL}}; + " tcp flags & (syn | fin | rst) == syn queue num %" PRIu32 + " bypass;\n" + + " }\n" + "}\n"; + + char *nft_conf_opt_fmt = + "add rule ip6 fakehttp fh_rules tcp flags & (syn | ack | fin | rst) " + "== ack ct packets 2-4 queue num %" PRIu32 " bypass;\n"; - nft_opt_cmds_cnt = sizeof(nft_opt_cmds) / sizeof(*nft_opt_cmds); + fh_nft6_cleanup(); res = snprintf(nft_conf_buff, sizeof(nft_conf_buff), nft_conf_fmt, - g_ctx.fwmask, g_ctx.fwmark, ~g_ctx.fwmask, g_ctx.fwmark, - g_ctx.fwmask, g_ctx.fwmark, ~g_ctx.fwmask, g_ctx.fwmark, g_ctx.fwmask, g_ctx.fwmark, g_ctx.nfqnum); if (res < 0 || (size_t) res >= sizeof(nft_conf_buff)) { E("ERROR: snprintf(): %s", "failure"); return -1; } - fh_nft6_cleanup(); - res = fh_execute_command(nft_cmd, 0, nft_conf_buff); if (res < 0) { E(T(fh_execute_command)); return -1; } - for (i = 0; i < nft_opt_cmds_cnt; i++) { - fh_execute_command(nft_opt_cmds[i], 1, NULL); + /* + Also enqueue some of the early ACK packets to ensure the packet order. + This rule is optional. We do not verify its execution result. + */ + res = snprintf(nft_conf_buff, sizeof(nft_conf_buff), nft_conf_opt_fmt, + g_ctx.nfqnum); + if (res < 0 || (size_t) res >= sizeof(nft_conf_buff)) { + E("ERROR: snprintf(): %s", "failure"); + return -1; } + fh_execute_command(nft_cmd, 0, nft_conf_buff); + res = nft6_iface_setup(); if (res < 0) { E(T(nft6_iface_setup)); diff --git a/src/mainfun.c b/src/mainfun.c index 6e656fc..78c60a7 100644 --- a/src/mainfun.c +++ b/src/mainfun.c @@ -40,6 +40,7 @@ #include "process.h" #include "rawsend.h" #include "signals.h" +#include "srcinfo.h" #ifndef PROGNAME #define PROGNAME "fakehttp" @@ -392,10 +393,16 @@ int main(int argc, char *argv[]) goto cleanup_logger; } + res = fh_srcinfo_setup(); + if (res < 0) { + EE(T(fh_srcinfo_setup)); + goto cleanup_payload; + } + res = fh_rawsend_setup(); if (res < 0) { EE(T(fh_rawsend_setup)); - goto cleanup_payload; + goto cleanup_srcinfo; } res = fh_nfq_setup(); @@ -469,6 +476,9 @@ int main(int argc, char *argv[]) cleanup_rawsend: fh_rawsend_cleanup(); +cleanup_srcinfo: + fh_srcinfo_cleanup(); + cleanup_payload: fh_payload_cleanup(); diff --git a/src/nfqueue.c b/src/nfqueue.c index e5025fb..37d955e 100644 --- a/src/nfqueue.c +++ b/src/nfqueue.c @@ -46,8 +46,8 @@ static struct nfq_q_handle *qh = NULL; static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { - uint32_t pkt_id, ifindex; - int res, pkt_len; + uint32_t pkt_id, iifindex, oifindex; + int verdict, pkt_len, modified; struct nfqnl_msg_packet_hdr *ph; unsigned char *pkt_data; struct nfqnl_msg_packet_hw *hwph; @@ -64,11 +64,8 @@ static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, pkt_id = ntohl(ph->packet_id); - ifindex = nfq_get_indev(nfa); - if (!ifindex) { - EE("ERROR: nfq_get_indev(): %s", "failure"); - goto ret_accept; - } + iifindex = nfq_get_indev(nfa); + oifindex = nfq_get_outdev(nfa); pkt_data = NULL; pkt_len = nfq_get_payload(nfa, &pkt_data); @@ -80,9 +77,18 @@ static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_protocol = ph->hw_protocol; - sll.sll_ifindex = ifindex; + if (oifindex) { + sll.sll_pkttype = PACKET_OUTGOING; + sll.sll_ifindex = oifindex; + } else if (iifindex) { + sll.sll_pkttype = PACKET_HOST; + sll.sll_ifindex = iifindex; + } else { + EE("ERROR: Failed to get interface index"); + goto ret_accept; + } - /* hwph can be null on PPP interfaces */ + /* hwph can be null on PPP interfaces or POSTROUTING packets */ hwph = nfq_get_packet_hw(nfa); if (hwph) { sll.sll_halen = sizeof(hwph->hw_addr); @@ -92,14 +98,17 @@ static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, memset(sll.sll_addr, 0, sizeof(sll.sll_addr)); } - res = fh_rawsend_handle(&sll, pkt_data, pkt_len); - if (res < 0) { + verdict = fh_rawsend_handle(&sll, pkt_data, pkt_len, &modified); + if (verdict < 0) { EE(T(fh_rawsend_handle)); goto ret_accept; - } else if (res) { - goto ret_accept; } - return nfq_set_verdict2(qh, pkt_id, NF_REPEAT, g_ctx.fwmark, 0, NULL); + + if (modified && verdict != NF_DROP) { + return nfq_set_verdict(qh, pkt_id, verdict, pkt_len, pkt_data); + } + + return nfq_set_verdict(qh, pkt_id, verdict, 0, NULL); ret_accept: return nfq_set_verdict(qh, pkt_id, NF_ACCEPT, 0, NULL); diff --git a/src/rawsend.c b/src/rawsend.c index b493361..c2e44b2 100644 --- a/src/rawsend.c +++ b/src/rawsend.c @@ -26,15 +26,19 @@ #include #include #include +#include #include #include #include +#include +#include #include "globvar.h" #include "ipv4pkt.h" #include "ipv6pkt.h" #include "logging.h" #include "payload.h" +#include "srcinfo.h" static uint8_t *payload = NULL; static size_t payload_len = 0; @@ -51,6 +55,25 @@ static int hop_estimate(uint8_t ttl) } } + +static uint8_t calc_snd_ttl(int hops) +{ + int snd_ttl; + + if (!g_ctx.dynamic_pct) { + return g_ctx.ttl; + } + + snd_ttl = hops * g_ctx.dynamic_pct / 100; + + if (snd_ttl > g_ctx.ttl) { + return snd_ttl; + } + + return g_ctx.ttl; +} + + static void ipaddr_to_str(struct sockaddr *addr, char ipstr[INET6_ADDRSTRLEN]) { static const char invalid[] = "INVALID"; @@ -78,9 +101,116 @@ static void ipaddr_to_str(struct sockaddr *addr, char ipstr[INET6_ADDRSTRLEN]) } +static int remove_tfo_cookie(uint16_t ethertype, uint8_t *pkt, + struct tcphdr *tcph) +{ + int not_found = 0; + size_t i = 0, tcpopt_len; + uint8_t *tcpopt_data, kind, len; + + not_found = 1; + tcpopt_len = tcph->doff * 4 - sizeof(*tcph); + tcpopt_data = (uint8_t *) tcph + sizeof(*tcph); + + while (i < tcpopt_len) { + kind = tcpopt_data[i]; + if (kind == 0 || kind == 1) { + i++; + continue; + } + + if (i + 1 >= tcpopt_len) { + break; + } + + len = tcpopt_data[i + 1]; + + if (len < 2 || i + len > tcpopt_len) { + break; + } + + if (kind == 34 /* TCP Fast Open Cookie */) { + not_found = 0; + memset(&tcpopt_data[i], 0x01 /* NOP */, len); + } + i += len; + } + + if (!not_found) { + if (ethertype == ETHERTYPE_IP) { + nfq_tcp_compute_checksum_ipv4(tcph, (struct iphdr *) pkt); + } else if (ethertype == ETHERTYPE_IPV6) { + nfq_tcp_compute_checksum_ipv6(tcph, (struct ip6_hdr *) pkt); + } + } + + return not_found; +} + + +/* + This is a workaround for iptables since it does not allow us to intercept + packets after POSTROUTING SNAT, which means the SNATed source address is + unknown. + Instead of using an AF_PACKET socket, we create a temporary AF_INET or + AF_INET6 raw socket, so that the packet gets SNATed correctly. +*/ +static int sendto_snat(struct sockaddr_ll *sll, struct sockaddr *daddr, + uint8_t *pkt_buff, int pkt_len) +{ + int res, ret, sock_fd; + ssize_t nbytes; + char *iface, iface_buf[IF_NAMESIZE]; + + ret = -1; + + iface = if_indextoname(sll->sll_ifindex, iface_buf); + if (!iface) { + E("ERROR: if_indextoname(): %s", strerror(errno)); + return -1; + } + + sock_fd = socket(daddr->sa_family, SOCK_RAW, IPPROTO_RAW); + if (sock_fd < 0) { + E("ERROR: socket(): %s", strerror(errno)); + return -1; + } + + res = setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, iface, + strlen(iface)); + if (res < 0) { + E("ERROR: setsockopt(): SO_BINDTODEVICE: %s", strerror(errno)); + goto close_socket; + } + + res = setsockopt(sock_fd, SOL_SOCKET, SO_MARK, &g_ctx.fwmark, + sizeof(g_ctx.fwmark)); + if (res < 0) { + E("ERROR: setsockopt(): SO_MARK: %s", strerror(errno)); + goto close_socket; + } + + nbytes = sendto(sock_fd, pkt_buff, pkt_len, 0, daddr, + daddr->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) + : sizeof(struct sockaddr_in)); + if (nbytes < 0) { + E("ERROR: sendto(): %s", strerror(errno)); + goto close_socket; + } + + ret = nbytes; + +close_socket: + close(sock_fd); + + return ret; +} + + static int send_payload(struct sockaddr_ll *sll, struct sockaddr *saddr, struct sockaddr *daddr, uint8_t ttl, uint16_t sport_be, - uint16_t dport_be, uint32_t seq_be, uint32_t ackseq_be) + uint16_t dport_be, uint32_t seq_be, uint32_t ackseq_be, + int need_snat) { int pkt_len; ssize_t nbytes; @@ -107,11 +237,19 @@ static int send_payload(struct sockaddr_ll *sll, struct sockaddr *saddr, return -1; } - nbytes = sendto(sockfd, pkt_buff, pkt_len, 0, (struct sockaddr *) sll, - sizeof(*sll)); - if (nbytes < 0) { - E("ERROR: sendto(): %s", strerror(errno)); - return -1; + if (need_snat) { + nbytes = sendto_snat(sll, daddr, pkt_buff, pkt_len); + if (nbytes < 0) { + E(T(sendto_snat)); + return -1; + } + } else { + nbytes = sendto(sockfd, pkt_buff, pkt_len, 0, (struct sockaddr *) sll, + sizeof(*sll)); + if (nbytes < 0) { + E("ERROR: sendto(): %s", strerror(errno)); + return -1; + } } return 0; @@ -179,16 +317,20 @@ void fh_rawsend_cleanup(void) } -int fh_rawsend_handle(struct sockaddr_ll *sll, uint8_t *pkt_data, int pkt_len) +int fh_rawsend_handle(struct sockaddr_ll *sll, uint8_t *pkt_data, int pkt_len, + int *modified) { - uint32_t ack_new; + uint32_t seq_new, ack_new; uint16_t ethertype; - int res, i, tcp_payload_len, hop; + int res, i, src_payload_len, hop, srcinfo_unavail; uint8_t src_ttl, snd_ttl; struct tcphdr *tcph; - char src_ip[INET6_ADDRSTRLEN], dst_ip[INET6_ADDRSTRLEN]; + char src_ip_str[INET6_ADDRSTRLEN], dst_ip_str[INET6_ADDRSTRLEN]; struct sockaddr_storage saddr_store, daddr_store; struct sockaddr *saddr, *daddr; + ssize_t nbytes; + + *modified = 0; saddr = (struct sockaddr *) &saddr_store; daddr = (struct sockaddr *) &daddr_store; @@ -196,14 +338,14 @@ int fh_rawsend_handle(struct sockaddr_ll *sll, uint8_t *pkt_data, int pkt_len) ethertype = ntohs(sll->sll_protocol); if (g_ctx.use_ipv4 && ethertype == ETHERTYPE_IP) { res = fh_pkt4_parse(pkt_data, pkt_len, saddr, daddr, &src_ttl, &tcph, - &tcp_payload_len); + &src_payload_len); if (res < 0) { E(T(fh_pkt4_parse)); return -1; } } else if (g_ctx.use_ipv6 && ethertype == ETHERTYPE_IPV6) { res = fh_pkt6_parse(pkt_data, pkt_len, saddr, daddr, &src_ttl, &tcph, - &tcp_payload_len); + &src_payload_len); if (res < 0) { E(T(fh_pkt6_parse)); return -1; @@ -214,85 +356,187 @@ int fh_rawsend_handle(struct sockaddr_ll *sll, uint8_t *pkt_data, int pkt_len) } if (!g_ctx.silent) { - ipaddr_to_str(saddr, src_ip); - ipaddr_to_str(daddr, dst_ip); + ipaddr_to_str(saddr, src_ip_str); + ipaddr_to_str(daddr, dst_ip_str); } - snd_ttl = 0; + if (sll->sll_pkttype == PACKET_HOST && tcph->syn && tcph->ack) { + /* + Outbound TCP connection. SYN-ACK received from peer. + */ + sll->sll_pkttype = 0; - if (!g_ctx.nohopest) { - hop = hop_estimate(src_ttl); - if (hop <= g_ctx.ttl) { - E_INFO("%s:%u ===LOCAL(?)===> %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - return 0; - } - if (g_ctx.dynamic_pct) { - snd_ttl = hop * g_ctx.dynamic_pct / 100; - } - } - - if (snd_ttl < g_ctx.ttl) { - snd_ttl = g_ctx.ttl; - } - - if (tcp_payload_len > 0) { - E_INFO("%s:%u ===PAYLOAD(?)===> %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - return 0; - } else if (tcph->syn && tcph->ack) { if (!g_ctx.outbound) { - E_INFO("%s:%u ===SYN-ACK(?)===> %s:%u", src_ip, - ntohs(tcph->source), dst_ip, ntohs(tcph->dest)); - return 0; + E_INFO("%s:%u ===SYN-ACK(~)===> %s:%u", src_ip_str, + ntohs(tcph->source), dst_ip_str, ntohs(tcph->dest)); + return NF_ACCEPT; } - E_INFO("%s:%u ===SYN-ACK===> %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); + E_INFO("%s:%u ===SYN-ACK===> %s:%u", src_ip_str, ntohs(tcph->source), + dst_ip_str, ntohs(tcph->dest)); ack_new = ntohl(tcph->seq); ack_new++; ack_new = htonl(ack_new); + snd_ttl = g_ctx.ttl; + + if (!g_ctx.nohopest) { + hop = hop_estimate(src_ttl); + if (hop <= g_ctx.ttl) { + E_INFO("%s:%u ===LOCAL(~)===> %s:%u", src_ip_str, + ntohs(tcph->source), dst_ip_str, ntohs(tcph->dest)); + return NF_ACCEPT; + } + snd_ttl = calc_snd_ttl(hop); + } + th_payload_get(&payload, &payload_len); + for (i = 0; i < g_ctx.repeat; i++) { res = send_payload(sll, daddr, saddr, snd_ttl, tcph->dest, - tcph->source, tcph->ack_seq, ack_new); + tcph->source, tcph->ack_seq, ack_new, 0); if (res < 0) { E(T(send_payload)); return -1; } } - E_INFO("%s:%u <===FAKE(*)=== %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - - return 0; - } else if (tcph->ack) { - if (!g_ctx.inbound) { - E_INFO("%s:%u ===ACK(?)===> %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - return 0; + E_INFO("%s:%u <===FAKE(*)=== %s:%u", src_ip_str, ntohs(tcph->source), + dst_ip_str, ntohs(tcph->dest)); + + return NF_ACCEPT; + } else if (sll->sll_pkttype == PACKET_OUTGOING && tcph->syn && tcph->ack) { + /* + Inbound TCP connection. SYN-ACK to be sent from local. + */ + sll->sll_pkttype = 0; + + srcinfo_unavail = fh_srcinfo_get(daddr, &src_ttl, sll->sll_addr); + + if (!g_ctx.inbound || srcinfo_unavail) { + E_INFO("%s:%u <===SYN-ACK(~)=== %s:%u", dst_ip_str, + ntohs(tcph->dest), src_ip_str, ntohs(tcph->source)); + return NF_ACCEPT; } - E_INFO("%s:%u ===ACK===> %s:%u", src_ip, ntohs(tcph->source), dst_ip, - ntohs(tcph->dest)); + seq_new = ntohl(tcph->seq); + seq_new++; + seq_new = htonl(seq_new); + + snd_ttl = g_ctx.ttl; + + if (!g_ctx.nohopest) { + hop = hop_estimate(src_ttl); + if (hop <= g_ctx.ttl) { + E_INFO("%s:%u <===LOCAL(~)=== %s:%u", src_ip_str, + ntohs(tcph->source), dst_ip_str, ntohs(tcph->dest)); + return NF_ACCEPT; + } + snd_ttl = calc_snd_ttl(hop); + } th_payload_get(&payload, &payload_len); + for (i = 0; i < g_ctx.repeat; i++) { - res = send_payload(sll, daddr, saddr, snd_ttl, tcph->dest, - tcph->source, tcph->ack_seq, tcph->seq); + res = send_payload(sll, saddr, daddr, snd_ttl, tcph->source, + tcph->dest, seq_new, tcph->ack_seq, + g_ctx.use_iptables /* needs SNAT */); if (res < 0) { E(T(send_payload)); return -1; } } - E_INFO("%s:%u <===FAKE(*)=== %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); + E_INFO("%s:%u <===FAKE(*)=== %s:%u", dst_ip_str, ntohs(tcph->dest), + src_ip_str, ntohs(tcph->source)); + + /* + We send the original packet using a raw socket and discard the + current processing flow. + This ensures that the SYN-ACK is transmitted after the payload. + Although this deliberately causes a TCP out-of-order situation, + it guarantees that our payload is always sent before the client's + packet. + */ + if (g_ctx.use_iptables) { + nbytes = sendto_snat(sll, daddr, pkt_data, pkt_len); + if (nbytes < 0) { + E(T(sendto_snat)); + return -1; + } + } else { + nbytes = sendto(sockfd, pkt_data, pkt_len, 0, + (struct sockaddr *) sll, sizeof(*sll)); + if (nbytes < 0) { + E("ERROR: sendto(): %s", strerror(errno)); + return -1; + } + } + + E_INFO("%s:%u <===SYN-ACK=== %s:%u", dst_ip_str, ntohs(tcph->dest), + src_ip_str, ntohs(tcph->source)); + + return NF_DROP; /* Drop it! */ + } else if (sll->sll_pkttype == PACKET_HOST && tcph->syn) { + /* + Inbound TCP connection. SYN received from peer. + */ + sll->sll_pkttype = 0; + + if (!g_ctx.inbound) { + E_INFO("%s:%u ===SYN(~)===> %s:%u", src_ip_str, + ntohs(tcph->source), dst_ip_str, ntohs(tcph->dest)); + return NF_ACCEPT; + } + + *modified = !remove_tfo_cookie(ethertype, pkt_data, tcph); + if (*modified) { + E_INFO("%s:%u ===SYN(#)===> %s:%u", src_ip_str, + ntohs(tcph->source), dst_ip_str, ntohs(tcph->dest)); + } else { + E_INFO("%s:%u ===SYN===> %s:%u", src_ip_str, ntohs(tcph->source), + dst_ip_str, ntohs(tcph->dest)); + } + + res = fh_srcinfo_put(saddr, src_ttl, sll->sll_addr); + if (res < 0) { + E(T(fh_srcinfo_put)); + return -1; + } + + return NF_ACCEPT; + } else if (sll->sll_pkttype == PACKET_OUTGOING && tcph->syn) { + /* + Outbound TCP connection. SYN to be sent from local. + */ + sll->sll_pkttype = 0; + + if (!g_ctx.outbound) { + E_INFO("%s:%u <===SYN(~)=== %s:%u", dst_ip_str, ntohs(tcph->dest), + src_ip_str, ntohs(tcph->source)); + return NF_ACCEPT; + } + + *modified = !remove_tfo_cookie(ethertype, pkt_data, tcph); + if (*modified) { + E_INFO("%s:%u <===SYN(#)=== %s:%u", dst_ip_str, ntohs(tcph->dest), + src_ip_str, ntohs(tcph->source)); + } else { + E_INFO("%s:%u <===SYN=== %s:%u", dst_ip_str, ntohs(tcph->dest), + src_ip_str, ntohs(tcph->source)); + } - return 0; + return NF_ACCEPT; + } else if (sll->sll_pkttype == PACKET_HOST) { + E_INFO("%s:%u ===(~)===> %s:%u", src_ip_str, ntohs(tcph->source), + dst_ip_str, ntohs(tcph->dest)); + return NF_ACCEPT; + } else if (sll->sll_pkttype == PACKET_OUTGOING) { + E_INFO("%s:%u <===(~)=== %s:%u", dst_ip_str, ntohs(tcph->dest), + src_ip_str, ntohs(tcph->source)); + return NF_ACCEPT; } else { - E_INFO("%s:%u ===(?)===> %s:%u", src_ip, ntohs(tcph->source), dst_ip, - ntohs(tcph->dest)); - return 1; + E_INFO("%s:%u ===(~)=== %s:%u", src_ip_str, ntohs(tcph->source), + dst_ip_str, ntohs(tcph->dest)); + return NF_ACCEPT; } } diff --git a/src/srcinfo.c b/src/srcinfo.c new file mode 100644 index 0000000..b847197 --- /dev/null +++ b/src/srcinfo.c @@ -0,0 +1,130 @@ +/* + * srcinfo.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE +#include "srcinfo.h" + +#include +#include +#include +#include +#include +#include + +#include "logging.h" + +#define CAPACITY 500 + +struct srcinfo { + int initialized; + uint8_t ttl; + uint8_t hwaddr[8]; + struct sockaddr_storage addr; +}; + +static struct srcinfo *srci = NULL; +static size_t srci_end = 0; + +static int sameip(struct sockaddr *addr1, struct sockaddr *addr2) +{ + struct sockaddr_in *addr_in_1, *addr_in_2; + struct sockaddr_in6 *addr_in6_1, *addr_in6_2; + + if (addr1->sa_family != addr2->sa_family) { + return 0; + } + + if (addr1->sa_family == AF_INET) { + addr_in_1 = (struct sockaddr_in *) addr1; + addr_in_2 = (struct sockaddr_in *) addr2; + + return addr_in_1->sin_addr.s_addr == addr_in_2->sin_addr.s_addr; + } else if (addr1->sa_family == AF_INET6) { + addr_in6_1 = (struct sockaddr_in6 *) addr1; + addr_in6_2 = (struct sockaddr_in6 *) addr2; + + return memcmp(&addr_in6_1->sin6_addr, &addr_in6_2->sin6_addr, + sizeof(addr_in6_1->sin6_addr)) == 0; + } + return 0; +} + + +int fh_srcinfo_setup(void) +{ + srci = calloc(CAPACITY, sizeof(*srci)); + if (!srci) { + E("ERROR: calloc(): %s", strerror(errno)); + return -1; + } + srci_end = 0; + + return 0; +} + + +void fh_srcinfo_cleanup(void) +{ + free(srci); +} + + +int fh_srcinfo_put(struct sockaddr *addr, uint8_t ttl, uint8_t hwaddr[8]) +{ + struct srcinfo *info; + + info = &srci[srci_end]; + + if (addr->sa_family == AF_INET) { + memcpy(&info->addr, addr, sizeof(struct sockaddr_in)); + } else if (addr->sa_family == AF_INET6) { + memcpy(&info->addr, addr, sizeof(struct sockaddr_in6)); + } else { + E("ERROR: Unknown sa_family: %d", (int) addr->sa_family); + return -1; + } + + info->ttl = ttl; + memcpy(info->hwaddr, hwaddr, sizeof(info->hwaddr)); + info->initialized = 1; + + srci_end = (srci_end + 1) % CAPACITY; + + return 0; +} + + +int fh_srcinfo_get(struct sockaddr *addr, uint8_t *ttl, uint8_t hwaddr[8]) +{ + size_t i; + struct srcinfo *info; + + for (i = 0; i < CAPACITY; i++) { + info = &srci[(srci_end - i - 1) % CAPACITY]; + if (!info->initialized) { + return 1; + } + if (sameip(addr, (struct sockaddr *) &info->addr)) { + *ttl = info->ttl; + memcpy(hwaddr, info->hwaddr, sizeof(info->hwaddr)); + return 0; + } + } + return 1; +}