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