nfqws: conntrack

This commit is contained in:
bol-van 2021-03-18 17:21:25 +03:00
parent dccf672d8a
commit b27cfbc813
19 changed files with 564 additions and 46 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -189,3 +189,7 @@ limited MacOS support
v38
MacOS easy install
v39
nfqws: conntrack, wssize

View File

@ -135,7 +135,9 @@ nfqws takes the following parameters:
--qnum=<nfqueue_number>
--wsize=<window_size> ; set window size. 0 = do not modify (obsolete !)
--wsize=<winsize>[:<scale_factor>] ; change window size in SYN,ACK packets. default is not to change scale factor (OBSOLETE !)
--wssize=<winsize>[:<scale_factor>] ; change window size in all packets except SYN,ACK. default scale factor is 0.
--wssize=<winsize>[:<scale_factor>] ; change window size in outgoing packets. default scale factor is 0. (see CONNTRACK)
--wssize-cutoff=N ; apply server wsize only to packet numbers less than N
--ctrack-timeouts=S:E:F ; internal conntrack timeouts for SYN, ESTABLISHED and FIN stages. default 60:300:60
--hostcase ; change Host: => host:
--hostspell=HoSt ; exact spelling of the "Host" header. must be 4 chars. default is "host"
--hostnospace ; remove space after Host: and add it to User-Agent: to preserve packet size
@ -220,8 +222,8 @@ If, instead of ACK or SACK, there is an RST packet with minimal delay, DPI cuts
If the RST is after a full ACK after a delay of about ping to the server, then probably DPI acts
on the server response. The DPI may be satisfied with good ClientHello and stop monitoring the TCP session
without checking ServerHello. Then you were lucky. 'fake' option could work.
If it does not stop monitoring and persistently checks the ServerHello, also performing reconstruction of TCP segments,
doing something about it is hardly possible without the help of the server.
If it does not stop monitoring and persistently checks the ServerHello, --wssize parameter may help (see CONNTRACK).
Otherwise it is hardly possible to overcome this without the help of the server.
The best solution is to enable TLS 1.3 support on the server. TLS 1.3 sends the server certificate in encrypted form.
This is recommendation to all admins of blocked sites. Enable TLS 1.3. You will give more opportunities to overcome DPI.
@ -253,6 +255,37 @@ Hypervisor forcibly changes ttl and does not forward fake packets.
Set up bridge networking.
CONNTRACK
nfqws is equipped with minimalicstic connection tracking system (conntrack)
It's enabled if some specific DPI circumvention methods are involved. At the moment it's --wssize parameter.
Conntrack can track connection phase : SYN,ESTABLISHED,FIN , packet counts in both directions , sequence numbers.
It can be fed with unidirectional or bidirectional packets.
A SYN or SYN,ACK packet creates an entry in the conntrack table.
That's why iptables redirection must start with the first packet although can be cut later using connbytes filter.
A connection is deleted from the table as soon as it's no more required to satisfy nfqws needs or when a timeout happens.
There're 3 timeouts for each connection state. They can be changed in --ctrack-timeouts parameter.
--wssize changes tcp window size for the server to force it to send split replies.
In order for this to affect all server operating systems, it is necessary to change the window size in each outgoing packet
before sending the message, the answer to which must be split (for example, TLS ClientHello).
That's why conntrack is required to know when to stop applying low window size.
If you do not stop and set the low wssize all the time, the speed will drop catastrophically.
Linux can overcome this using connbytes filter but other OS may not include similar filter.
In http(s) case wssize stops after the first http request or TLS ClientHello.
If you deal with a non-http(s) protocol you need --wssize-cutoff. It sets the number of the outgoing packet where wssize stops.
If your protocol is prone to long inactivity, you should increase ESTABLISHED phase timeout using --ctrack-timeouts.
Default timeout is low - only 5 mins.
Don't forget that nfqws feeds with redirected packets. If you have limited redirection with connbytes
ESTABLISHED entries can remain in the table until dropped by timeout.
To diagnose conntrack state send SIGUSR1 signal to nfqws : killall -SIGUSR1 nfqws.
nfqws will dump current conntrack table to stdout.
Typically, in a SYN packet, client sends TCP extension "scaling factor" in addition to window size.
scaling factor is the power of two by which the window size is multiplied : 0=>1, 1=>2, 2=>4, ..., 8=>256, ...
The wssize parameter specifies the scaling factor after a colon.
Scaling factor can only decrease, increase is blocked to prevent the server from exceeding client's window size.
To force a TLS server to fragment ServerHello message to avoid hostname detection on DPI use --wssize=1:6
tpws
-----

View File

@ -1,4 +1,4 @@
zapret v.38
zapret v.39
English
-------
@ -179,7 +179,9 @@ nfqws
--uid=uid[:gid] ; менять uid процесса
--qnum=N ; номер очереди N
--wsize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в SYN,ACK. если не задан scale_factor, то он не меняется (устарело !)
--wssize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер во всех пакетах, кроме SYN,ACK. scale_factor по умолчанию 0.
--wssize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в исходящих пакетах. scale_factor по умолчанию 0. (см. conntrack !)
--wssize-cutoff=N ; применять изменение server window size в исходящих пакетах по номеру меньше N
--ctrack-timeouts=S:E:F ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN. по умолчанию 60:300:60
--hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:".
--hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета
--hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase
@ -265,8 +267,9 @@ disorder2 и split2 не предполагают отсылку фейк пак
тогда вероятно DPI реагирует на ответ сервера.
DPI может отстать от потока, если ClientHello его удовлетворил и не проверять ServerHello.
Тогда вам повезло. Вариант fake может сработать.
Если же он не отстает и упорно проверяет ServerHello, еще и выполняя реконструкцию сегментов TCP,
то сделать с этим что-либо вряд ли возможно без помощи со стороны сервера.
Если же он не отстает и упорно проверяет ServerHello, то можно попробовать заставить сервер высылать ServerHello частями
через параметр --wssize (см. conntrack).
Если и это не помогает, то сделать с этим что-либо вряд ли возможно без помощи со стороны сервера.
Лучшее решение - включить на сервере поддержку TLS 1.3. В нем сертификат сервера передается в зашифрованном виде.
Это рекомендация ко всем админам блокируемых сайтов. Включайте TLS 1.3. Так вы дадите больше возможностей преодолеть DPI.
@ -306,6 +309,41 @@ mark нужен, чтобы сгенерированный поддельный
Изнутри VM от virtualbox и vmware в режиме NAT не работают многие техники пакетной магии nfqws.
Принудительно заменяется ttl, не проходят фейк пакеты. Необходимо настроить сеть в режиме bridge.
CONNTRACK
nfqws оснащен ограниченной реализацией слежения за состоянием tcp соединений (conntrack).
Он включается для реализации некоторых методов противодействия DPI. На текущий момент это параметр --wssize.
conntrack способен следить за фазой соединения : SYN,ESTABLISHED,FIN , количеством пакетов в каждую сторону, sequence numbers.
conntrack способен "кормиться" пакетами в обе или только в одну сторону.
Соединение попадает в таблицу при обнаружении пакетов с выставленными флагами SYN или SYN,ACK.
Поэтому если необходим conntrack, в правилах перенаправления iptables соединение должно идти на nfqws с самого первого пакета,
хотя затем может обрываться по фильтру connbytes.
conntrack - простенький, он не писался с учетом всевозможных атак на соединение, он не проверяет
пакеты на валидность sequence numbers или чексумму. Его задача - лишь обслуживание нужд nfqws, он обычно
кормится только исходящим трафиком, потому нечувствителен к подменам со стороны внешней сети.
Соединение удаляется из таблицы, как только отпадает нужда в слежении за ним или по таймауту неактивности.
Существуют отдельные таймауты на каждую фазу соединения. Они могут быть изменены параметром --ctrack-timeouts.
--wssize позволяет изменить с клиента размер tcp window для сервера, чтобы он послал следующие ответы разбитыми на части.
Чтобы это подействовало на все серверные ОС, необходимо менять window size в каждом исходящем с клиента пакете до отсылки сообщения,
ответ на который должен быть разбит (например, TLS ClientHello). Именно поэтому и необходим conntrack, чтобы
знать когда надо остановиться. Если не остановиться и все время устанавливать низкий wssize, скорость упадет катастрофически.
В linux это может быть купировано через connbytes, но в BSD системах такой возможности нет.
В случае http(s) останавливаемся сразу после отсылки первого http запроса или TLS ClientHello.
Если вы имеете дело с не http(s), то вам потребуется параметр --wssize-cutoff. Он устанавливает номер исходящего
пакета, с которого действие wssize прекращается. Если ваш протокол склонен к долгому бездействию, следует увеличить
таймаут фазы ESTABLISHED через параметр --ctrack-timeouts. Таймаут по умолчанию низкий - всего 5 минут.
Не забывайте, что nfqws кормится приходящими на него пакетами. Если вы ограничили поступление пакетов через connbytes,
то в таблице могут остаться повисшие соединения в фазе ESTABLISHED, которые отвалятся только по таймауту.
Для диагностики состояния conntrack пошлите сигнал SIGUSR1 процессу nfqws : killall -SIGUSR1 nfqws.
Текущая таблица будет выведена nfqws в stdout.
Обычно в SYN пакете клиент отсылает кроме window size еще и TCP extension "scaling factor".
scaling factor представляет себя степень двойки, на которую умножается window size : 0=>1, 1=>2, 2=>4, ..., 8=>256, ...
В параметре wssize указывается scaling factor указывается через двоеточие.
Scaling factor может только снижаться, увеличение заблокировано, чтобы не допустить превышение размера окна со стороны сервера.
Для принуждения сервера к фрагментации ServerHello, чтобы избежать просекание имени сервера из сертификата сервера на DPI,
лучше всего использовать --wssize=1:6
tpws
-----

294
nfq/conntrack.c Normal file
View File

@ -0,0 +1,294 @@
#include "conntrack.h"
#include "darkmagic.h"
#include <arpa/inet.h>
#include <stdio.h>
#undef uthash_nonfatal_oom
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
static bool oom = false;
static void ut_oom_recover(void *elem)
{
oom = true;
}
static const char *connstate_s[]={"SYN","ESTABLISHED","FIN"};
#define _connswap \
memset(c2,0,sizeof(*c2)); \
c2->e1 = c->e2; \
c2->e2 = c->e1;
static void connswap4(const t_conn4 *c, t_conn4 *c2)
{
_connswap
}
static void connswap6(const t_conn6 *c, t_conn6 *c2)
{
_connswap
}
#define _ConntrackPoolDestroy(v) \
t_conntrack##v *elem, *tmp; \
HASH_ITER(hh, *pp, elem, tmp) { HASH_DEL(*pp, elem); free(elem); }
static void ConntrackPoolDestroy4(t_conntrack4 **pp)
{
_ConntrackPoolDestroy(4)
}
static void ConntrackPoolDestroy6(t_conntrack6 **pp)
{
_ConntrackPoolDestroy(6)
}
void ConntrackPoolDestroy(t_conntrack *p)
{
ConntrackPoolDestroy4(&p->pool4);
ConntrackPoolDestroy6(&p->pool6);
}
void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin)
{
p->timeout_syn = timeout_syn;
p->timeout_established = timeout_established;
p->timeout_fin = timeout_fin;
p->t_purge_interval = purge_interval;
time(&p->t_last_purge);
p->pool4 = NULL;
p->pool6 = NULL;
}
#define _ConntrackExtractConn(v) \
memset(c,0,sizeof(*c)); \
if (bReverse) { \
c->e1.adr = ip->ip##v##_dst; \
c->e2.adr = ip->ip##v##_src; \
c->e1.port = htons(tcphdr->th_dport); \
c->e2.port = htons(tcphdr->th_sport); \
} else { \
c->e1.adr = ip->ip##v##_src; \
c->e2.adr = ip->ip##v##_dst; \
c->e1.port = htons(tcphdr->th_sport); \
c->e2.port = htons(tcphdr->th_dport); \
}
void ConntrackExtractConn4(t_conn4 *c, bool bReverse, const struct ip *ip, const struct tcphdr *tcphdr)
{
_ConntrackExtractConn()
}
void ConntrackExtractConn6(t_conn6 *c, bool bReverse, const struct ip6_hdr *ip, const struct tcphdr *tcphdr)
{
_ConntrackExtractConn(6)
}
#define _ConntrackPoolSearch(v) \
t_conntrack##v *t; \
HASH_FIND(hh, p, c, sizeof(*c), t); \
return t;
t_conntrack4 *ConntrackPoolSearch4(t_conntrack4 *p, const t_conn4 *c)
{
_ConntrackPoolSearch(4)
}
t_conntrack6 *ConntrackPoolSearch6(t_conntrack6 *p, const t_conn6 *c)
{
_ConntrackPoolSearch(6)
}
static void ConntrackInitTrack(t_ctrack *t)
{
memset(t,0,sizeof(*t));
time(&t->t_start);
}
#define _ConntrackNew(v) \
t_conntrack##v *new; \
if (!(new = calloc(1,sizeof(*new)))) return NULL; \
new->conn = *c; \
oom = false; \
HASH_ADD(hh, *pp, conn, sizeof(*c), new); \
if (oom) { free(new); return NULL; } \
ConntrackInitTrack(&new->track); \
return new;
static t_conntrack4 *ConntrackNew4(t_conntrack4 **pp, const t_conn4 *c)
{
_ConntrackNew(4)
}
static t_conntrack6 *ConntrackNew6(t_conntrack6 **pp, const t_conn6 *c)
{
_ConntrackNew(6)
}
static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr *tcphdr, uint32_t len_payload)
{
if (tcp_syn_segment(tcphdr))
{
if (t->state==FIN) ConntrackInitTrack(t); // erase current entry
if (t->state==SYN) t->seq0 = htonl(tcphdr->th_seq);
}
else if (tcp_synack_segment(tcphdr))
{
if (t->state==FIN) ConntrackInitTrack(t); // erase current entry
if (t->state==SYN)
{
if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1;
t->ack0 = htonl(tcphdr->th_seq);
}
}
else if (tcphdr->th_flags & (TH_FIN|TH_RST))
{
t->state = FIN;
}
else
{
if (t->state==SYN)
{
t->state=ESTABLISHED;
if (!bReverse && !t->ack0) t->ack0 = htonl(tcphdr->th_ack)-1;
}
}
if (bReverse)
{
t->seq_last = htonl(tcphdr->th_ack);
t->ack_last = htonl(tcphdr->th_seq) + len_payload;
t->pcounter_reply++;
}
else
{
t->seq_last = htonl(tcphdr->th_seq) + len_payload;
t->ack_last = htonl(tcphdr->th_ack);
t->pcounter_orig++;
}
time(&t->t_last);
}
#define _ConntrackPoolFeed(v) \
t_conn##v conn, connswap; \
t_conntrack##v *ctr; \
bool b_rev; \
ConntrackExtractConn##v(&conn,false,ip,tcphdr); \
if ((ctr=ConntrackPoolSearch##v(*pp,&conn))) \
{ \
ConntrackFeedPacket(&ctr->track, (b_rev=false), tcphdr, len_payload); \
goto ok; \
} \
else \
{ \
connswap##v(&conn,&connswap); \
if ((ctr=ConntrackPoolSearch##v(*pp,&connswap))) \
{ \
ConntrackFeedPacket(&ctr->track, (b_rev=true), tcphdr, len_payload); \
goto ok; \
} \
} \
b_rev = tcp_synack_segment(tcphdr); \
if (tcp_syn_segment(tcphdr) || b_rev) \
{ \
if ((ctr=ConntrackNew##v(pp, b_rev ? &connswap : &conn))) \
{ \
ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload); \
goto ok; \
} \
} \
return false; \
ok: \
if (ctrack) *ctrack = &ctr->track; \
if (bReverse) *bReverse = b_rev; \
return true;
static bool ConntrackPoolFeed4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr, uint32_t len_payload, t_ctrack **ctrack, bool *bReverse)
{
_ConntrackPoolFeed(4)
}
static bool ConntrackPoolFeed6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr, uint32_t len_payload, t_ctrack **ctrack, bool *bReverse)
{
_ConntrackPoolFeed(6)
}
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse)
{
return ip ? ConntrackPoolFeed4(&p->pool4,ip,tcphdr,(uint32_t)len_payload,ctrack,bReverse) : ip6 ? ConntrackPoolFeed6(&p->pool6,ip6,tcphdr,(uint32_t)len_payload,ctrack,bReverse) : false;
}
#define _ConntrackPoolDrop(v) \
t_conn##v conn, connswap; \
t_conntrack##v *t; \
ConntrackExtractConn##v(&conn,false,ip,tcphdr); \
if (!(t=ConntrackPoolSearch##v(*pp,&conn))) \
{ \
connswap##v(&conn,&connswap); \
t=ConntrackPoolSearch##v(*pp,&connswap); \
} \
if (!t) return false; \
HASH_DEL(*pp, t); free(t); \
return true;
static bool ConntrackPoolDrop4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr)
{
_ConntrackPoolDrop(4)
}
static bool ConntrackPoolDrop6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr)
{
_ConntrackPoolDrop(6)
}
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr)
{
return ip ? ConntrackPoolDrop4(&p->pool4,ip,tcphdr) : ip6 ? ConntrackPoolDrop6(&p->pool6,ip6,tcphdr) : false;
}
#define _ConntrackPoolPurge(v, pp) \
{ \
t_conntrack##v *t, *tmp; \
time_t tidle; \
HASH_ITER(hh, *pp , t, tmp) { \
tidle = tnow - t->track.t_last; \
if ( t->track.b_cutoff || \
t->track.state==SYN && tidle>=p->timeout_syn || \
t->track.state==ESTABLISHED && tidle>=p->timeout_established || \
t->track.state==FIN && tidle>=p->timeout_fin) \
{ \
HASH_DEL(*pp, t); free(t); \
} \
} \
}
void ConntrackPoolPurge(t_conntrack *p)
{
time_t tnow = time(NULL); \
if ((tnow - p->t_last_purge)>=p->t_purge_interval)
{
_ConntrackPoolPurge(4, &p->pool4);
_ConntrackPoolPurge(6, &p->pool6);
p->t_last_purge = tnow;
}
}
#define _ConntrackPoolDump(v,f) \
t_conntrack##v *t, *tmp; \
char sa1[40],sa2[40]; \
time_t tnow = time(NULL); \
HASH_ITER(hh, p, t, tmp) { \
*sa1=0; inet_ntop(AF_INET##f, &t->conn.e1.adr, sa1, sizeof(sa1)); \
*sa2=0; inet_ntop(AF_INET##f, &t->conn.e2.adr, sa2, sizeof(sa2)); \
printf("[%s]:%u => [%s]:%u : %s : t0=%lld last=t0+%lld now=last+%lld cutoff=%u packets_orig=%llu packets_reply=%llu seq0=%u rseq=%u ack0=%u rack=%u\n", \
sa1, t->conn.e1.port, sa2, t->conn.e2.port, \
connstate_s[t->track.state], \
(unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last), \
t->track.b_cutoff, \
(unsigned long long)t->track.pcounter_orig, (unsigned long long)t->track.pcounter_reply, \
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.ack0, t->track.ack_last - t->track.ack0); \
};
void ConntrackPoolDump4(t_conntrack4 *p)
{
_ConntrackPoolDump(4,)
}
void ConntrackPoolDump6(t_conntrack6 *p)
{
_ConntrackPoolDump(6,6)
}
void ConntrackPoolDump(t_conntrack *p)
{
ConntrackPoolDump4(p->pool4);
ConntrackPoolDump6(p->pool6);
}

92
nfq/conntrack.h Normal file
View File

@ -0,0 +1,92 @@
#pragma once
// this conntrack is not bullet-proof
// its designed to satisfy dpi desync needs only
#include <stdbool.h>
#include <stdint.h>
#include <ctype.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
//#define HASH_BLOOM 20
#define HASH_NONFATAL_OOM 1
#undef HASH_FUNCTION
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
typedef struct
{
struct in_addr adr;
uint16_t port;
} t_endpoint4;
typedef struct
{
struct in6_addr adr;
uint16_t port;
} t_endpoint6;
// e1 - initiator. the one who have sent SYN
// e2 - acceptor
typedef struct
{
t_endpoint4 e1,e2;
} t_conn4;
typedef struct
{
t_endpoint6 e1,e2;
} t_conn6;
// SYN - SYN or SYN/ACK received
// ESTABLISHED - any except SYN or SYN/ACK received
// FIN - FIN or RST received
typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate;
typedef struct
{
t_connstate state;
time_t t_start, t_last;
uint32_t seq0, ack0; // starting seq and ack
uint32_t seq_last, ack_last; // current seq and ack
uint64_t pcounter_orig, pcounter_reply; // packet counter
bool b_cutoff; // mark for deletion
} t_ctrack;
// use separate pools for ipv4 and ipv6 to save RAM. otherwise could use union key
typedef struct
{
t_ctrack track;
UT_hash_handle hh; // makes this structure hashable
t_conn4 conn; // key
} t_conntrack4;
typedef struct
{
t_ctrack track;
UT_hash_handle hh; // makes this structure hashable
t_conn6 conn; // key
} t_conntrack6;
typedef struct
{
// inactivity time to purge an entry in each connection state
uint32_t timeout_syn,timeout_established,timeout_fin;
time_t t_purge_interval, t_last_purge;
t_conntrack4 *pool4;
t_conntrack6 *pool6;
} t_conntrack;
void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin);
void ConntrackPoolDestroy(t_conntrack *p);
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse);
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr);
void ConntrackExtractConn4(t_conn4 *c, bool bReverse, const struct ip *ip, const struct tcphdr *tcphdr);
void ConntrackExtractConn6(t_conn6 *c, bool bReverse, const struct ip6_hdr *ip, const struct tcphdr *tcphdr);
void ConntrackPoolDump4(t_conntrack4 *p);
void ConntrackPoolDump6(t_conntrack6 *p);
void ConntrackPoolDump(t_conntrack *p);
void ConntrackPoolPurge(t_conntrack *p);

View File

@ -513,7 +513,7 @@ static void str_srcdst_ip(char *s, size_t s_len, const void *saddr,const void *d
}
static void str_ip(char *s, size_t s_len, const struct ip *ip)
{
char ss[64],s_proto[16];
char ss[35],s_proto[16];
str_srcdst_ip(ss,sizeof(ss),&ip->ip_src,&ip->ip_dst);
str_proto_name(s_proto,sizeof(s_proto),ip->ip_p);
snprintf(s,s_len,"%s proto=%s",ss,s_proto);
@ -534,7 +534,7 @@ static void str_srcdst_ip6(char *s, size_t s_len, const void *saddr,const void *
}
static void str_ip6hdr(char *s, size_t s_len, const struct ip6_hdr *ip6hdr, uint8_t proto)
{
char ss[128],s_proto[16];
char ss[83],s_proto[16];
str_srcdst_ip6(ss,sizeof(ss),&ip6hdr->ip6_src,&ip6hdr->ip6_dst);
str_proto_name(s_proto,sizeof(s_proto),proto);
snprintf(s,s_len,"%s proto=%s",ss,s_proto);

View File

@ -5,6 +5,7 @@
#include "params.h"
#include "helpers.h"
#include "hostlist.h"
#include "conntrack.h"
#include <string.h>
@ -103,21 +104,30 @@ static bool rawsend_rep(const struct sockaddr* dst,uint32_t fwmark,const void *d
packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload)
{
packet_process_result res=pass;
t_ctrack *ctrack=NULL;
bool bReverse=false;
if (!!ip == !!ip6hdr) return res; // one and only one must be present
if (params.wssize)
{
ConntrackPoolPurge(&params.conntrack);
if (ConntrackPoolFeed(&params.conntrack, ip, ip6hdr, tcphdr, len_payload, &ctrack, &bReverse))
if (params.wssize_cutoff && ctrack->pcounter_orig>=params.wssize_cutoff)
ctrack->b_cutoff=true;
}
if (params.wsize && tcp_synack_segment(tcphdr))
{
tcp_rewrite_winsize(tcphdr, params.wsize, params.wscale);
res=modify;
}
if (params.wssize && !tcp_synack_segment(tcphdr))
if (params.wssize && !bReverse && (ctrack && !ctrack->b_cutoff))
{
tcp_rewrite_winsize(tcphdr, params.wssize, params.wsscale);
res=modify;
}
if (params.desync_mode==DESYNC_NONE && !params.hostcase && !params.hostnospace && !params.domcase) return res; // nothing to do. do not waste cpu
if (bReverse || !params.wssize && params.desync_mode==DESYNC_NONE && !params.hostcase && !params.hostnospace && !params.domcase) return res; // nothing to do. do not waste cpu
if (!(tcphdr->th_flags & TH_SYN) && len_payload)
{
@ -132,6 +142,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
if ((bIsHttp = IsHttp(data_payload,len_payload)))
{
DLOG("packet contains HTTP request\n")
if (ctrack && !params.wssize_cutoff) ctrack->b_cutoff = true;
fake = params.fake_http;
fake_size = params.fake_http_size;
if (params.hostlist || params.debug) bHaveHost=HttpExtractHost(data_payload,len_payload,host,sizeof(host));
@ -144,6 +155,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
else if (IsTLSClientHello(data_payload,len_payload))
{
DLOG("packet contains TLS ClientHello\n")
if (ctrack && !params.wssize_cutoff) ctrack->b_cutoff = true;
fake = params.fake_tls;
fake_size = params.fake_tls_size;
if (params.hostlist || params.desync_skip_nosni || params.debug)

View File

@ -1 +0,0 @@
../binaries/x86_64/nfqws

BIN
nfq/nfqws Executable file

Binary file not shown.

View File

@ -36,6 +36,9 @@
#define IPPROTO_DIVERT 258
#endif
#define CTRACK_T_SYN 60
#define CTRACK_T_EST 300
#define CTRACK_T_FIN 60
struct params_s params;
@ -65,6 +68,14 @@ static void dohup()
}
}
static void onusr1(int sig)
{
printf("\nCONNTRACK DUMP\n");
ConntrackPoolDump(&params.conntrack);
printf("\n");
}
#ifdef __linux__
static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt, uint32_t *mark)
@ -234,6 +245,7 @@ static int nfq_main()
print_id();
signal(SIGHUP, onhup);
signal(SIGUSR1, onusr1);
desync_init();
@ -349,6 +361,7 @@ static int dvt_main()
print_id();
signal(SIGHUP, onhup);
signal(SIGUSR1, onusr1);
desync_init();
@ -459,7 +472,9 @@ static void exithelp()
" --user=<username>\t\t\t; drop root privs\n"
" --uid=uid[:gid]\t\t\t; drop root privs\n"
" --wsize=<window_size>[:<scale_factor>]\t; set window size. 0 = do not modify. OBSOLETE !\n"
" --wssize=<window_size>[:<scale_factor>]; set window size for server. 0 = do not modify.\n"
" --wssize=<window_size>[:<scale_factor>]; set window size for server. 0 = do not modify. default scale_factor = 0.\n"
" --wssize-cutoff=N\t\t\t; apply server wsize only to packet numbers less than N\n"
" --ctrack-timeouts=S:E:F\t\t; internal conntrack timeouts for SYN, ESTABLISHED and FIN stage. default %u:%u:%u\n"
" --hostcase\t\t\t\t; change Host: => host:\n"
" --hostspell\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
" --hostnospace\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n"
@ -482,6 +497,7 @@ static void exithelp()
" --dpi-desync-fake-http=<filename>\t; file containing fake http request\n"
" --dpi-desync-fake-tls=<filename>\t; file containing fake TLS ClientHello (for https)\n"
" --hostlist=<filename>\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply)\n",
CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN,
#if defined(__linux__) || defined(SO_USER_COOKIE)
DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT,
#endif
@ -497,6 +513,7 @@ static void cleanup_params()
StrPoolDestroy(&params.hostlist);
params.hostlist = NULL;
}
ConntrackPoolDestroy(&params.conntrack);
}
static void exithelp_clean()
{
@ -531,6 +548,9 @@ int main(int argc, char **argv)
params.fake_http_size = strlen(fake_http_request_default);
memcpy(params.fake_http,fake_http_request_default,params.fake_http_size);
params.wscale=-1; // default - dont change scale factor (client)
params.ctrack_t_syn = CTRACK_T_SYN;
params.ctrack_t_est = CTRACK_T_EST;
params.ctrack_t_fin = CTRACK_T_FIN;
if (can_drop_root()) // are we root ?
{
@ -553,28 +573,30 @@ int main(int argc, char **argv)
{"uid",required_argument,0,0 }, // optidx=5
{"wsize",required_argument,0,0}, // optidx=6
{"wssize",required_argument,0,0}, // optidx=7
{"hostcase",no_argument,0,0}, // optidx=8
{"hostspell",required_argument,0,0}, // optidx=9
{"hostnospace",no_argument,0,0}, // optidx=10
{"domcase",no_argument,0,0 }, // optidx=11
{"dpi-desync",required_argument,0,0}, // optidx=12
{"wssize-cutoff",required_argument,0,0},// optidx=8
{"ctrack-timeouts",required_argument,0,0},// optidx=9
{"hostcase",no_argument,0,0}, // optidx=10
{"hostspell",required_argument,0,0}, // optidx=11
{"hostnospace",no_argument,0,0}, // optidx=12
{"domcase",no_argument,0,0 }, // optidx=13
{"dpi-desync",required_argument,0,0}, // optidx=14
#ifdef __linux__
{"dpi-desync-fwmark",required_argument,0,0}, // optidx=13
{"dpi-desync-fwmark",required_argument,0,0}, // optidx=15
#elif defined(SO_USER_COOKIE)
{"dpi-desync-sockarg",required_argument,0,0}, // optidx=13
{"dpi-desync-sockarg",required_argument,0,0}, // optidx=15
#else
{"disabled_argument_2",no_argument,0,0}, // optidx=13
{"disabled_argument_2",no_argument,0,0}, // optidx=15
#endif
{"dpi-desync-ttl",required_argument,0,0}, // optidx=14
{"dpi-desync-fooling",required_argument,0,0}, // optidx=15
{"dpi-desync-retrans",optional_argument,0,0}, // optidx=16
{"dpi-desync-repeats",required_argument,0,0}, // optidx=17
{"dpi-desync-skip-nosni",optional_argument,0,0},// optidx=18
{"dpi-desync-split-pos",required_argument,0,0},// optidx=19
{"dpi-desync-any-protocol",optional_argument,0,0},// optidx=20
{"dpi-desync-fake-http",required_argument,0,0},// optidx=21
{"dpi-desync-fake-tls",required_argument,0,0},// optidx=22
{"hostlist",required_argument,0,0}, // optidx=23
{"dpi-desync-ttl",required_argument,0,0}, // optidx=16
{"dpi-desync-fooling",required_argument,0,0}, // optidx=17
{"dpi-desync-retrans",optional_argument,0,0}, // optidx=18
{"dpi-desync-repeats",required_argument,0,0}, // optidx=19
{"dpi-desync-skip-nosni",optional_argument,0,0},// optidx=20
{"dpi-desync-split-pos",required_argument,0,0},// optidx=21
{"dpi-desync-any-protocol",optional_argument,0,0},// optidx=22
{"dpi-desync-fake-http",required_argument,0,0},// optidx=23
{"dpi-desync-fake-tls",required_argument,0,0},// optidx=24
{"hostlist",required_argument,0,0}, // optidx=25
{NULL,0,NULL,0}
};
if (argc < 2) exithelp();
@ -643,10 +665,24 @@ int main(int argc, char **argv)
if (!parse_scale_factor(optarg,&params.wssize,&params.wsscale))
exit_clean(1);
break;
case 8: /* hostcase */
case 8: /* wssize-cutoff */
if (!sscanf(optarg, "%u", &params.wssize_cutoff))
{
fprintf(stderr, "invalid wssize-cutoff value\n");
exit_clean(1);
}
break;
case 9: /* ctrack-timeouts */
if (sscanf(optarg, "%u:%u:%u", &params.ctrack_t_syn, &params.ctrack_t_est, &params.ctrack_t_fin)!=3)
{
fprintf(stderr, "invalid ctrack-timeouts value\n");
exit_clean(1);
}
break;
case 10: /* hostcase */
params.hostcase = true;
break;
case 9: /* hostspell */
case 11: /* hostspell */
if (strlen(optarg) != 4)
{
fprintf(stderr, "hostspell must be exactly 4 chars long\n");
@ -655,13 +691,13 @@ int main(int argc, char **argv)
params.hostcase = true;
memcpy(params.hostspell, optarg, 4);
break;
case 10: /* hostnospace */
case 12: /* hostnospace */
params.hostnospace = true;
break;
case 11: /* domcase */
case 13: /* domcase */
params.domcase = true;
break;
case 12: /* dpi-desync */
case 14: /* dpi-desync */
{
char *mode2;
mode2 = optarg ? strchr(optarg,',') : NULL;
@ -681,7 +717,7 @@ int main(int argc, char **argv)
}
}
break;
case 13: /* dpi-desync-fwmark/dpi-desync-sockarg */
case 15: /* dpi-desync-fwmark/dpi-desync-sockarg */
#if defined(__linux__) || defined(SO_USER_COOKIE)
params.desync_fwmark = 0;
if (!sscanf(optarg, "0x%X", &params.desync_fwmark)) sscanf(optarg, "%u", &params.desync_fwmark);
@ -695,10 +731,10 @@ int main(int argc, char **argv)
exit_clean(1);
#endif
break;
case 14: /* dpi-desync-ttl */
case 16: /* dpi-desync-ttl */
params.desync_ttl = (uint8_t)atoi(optarg);
break;
case 15: /* dpi-desync-fooling */
case 17: /* dpi-desync-fooling */
{
char *e,*p = optarg;
while (p)
@ -727,7 +763,7 @@ int main(int argc, char **argv)
}
}
break;
case 16: /* dpi-desync-retrans */
case 18: /* dpi-desync-retrans */
#ifdef __linux__
params.desync_retrans = !optarg || atoi(optarg);
#else
@ -735,7 +771,7 @@ int main(int argc, char **argv)
exit_clean(1);
#endif
break;
case 17: /* dpi-desync-repeats */
case 19: /* dpi-desync-repeats */
params.desync_repeats = atoi(optarg);
if (params.desync_repeats<=0 || params.desync_repeats>20)
{
@ -743,10 +779,10 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 18: /* dpi-desync-skip-nosni */
case 20: /* dpi-desync-skip-nosni */
params.desync_skip_nosni = !optarg || atoi(optarg);
break;
case 19: /* dpi-desync-split-pos */
case 21: /* dpi-desync-split-pos */
params.desync_split_pos = atoi(optarg);
if (params.desync_split_pos<1 || params.desync_split_pos>DPI_DESYNC_MAX_FAKE_LEN)
{
@ -754,10 +790,10 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 20: /* dpi-desync-any-protocol */
case 22: /* dpi-desync-any-protocol */
params.desync_any_proto = !optarg || atoi(optarg);
break;
case 21: /* dpi-desync-fake-http */
case 23: /* dpi-desync-fake-http */
params.fake_http_size = sizeof(params.fake_http);
if (!load_file_nonempty(optarg,params.fake_http,&params.fake_http_size))
{
@ -765,7 +801,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 22: /* dpi-desync-fake-tls */
case 24: /* dpi-desync-fake-tls */
params.fake_tls_size = sizeof(params.fake_tls);
if (!load_file_nonempty(optarg,params.fake_tls,&params.fake_tls_size))
{
@ -773,7 +809,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 23: /* hostlist */
case 25: /* hostlist */
if (!LoadHostList(&params.hostlist, optarg))
exit_clean(1);
strncpy(params.hostfile,optarg,sizeof(params.hostfile));
@ -797,6 +833,9 @@ int main(int argc, char **argv)
goto exiterr;
}
DLOG("initializing conntrack with timeouts %u:%u:%u\n", params.ctrack_t_syn, params.ctrack_t_est, params.ctrack_t_fin)
ConntrackPoolInit(&params.conntrack, 10, params.ctrack_t_syn, params.ctrack_t_est, params.ctrack_t_fin);
#ifdef __linux__
result = nfq_main();
#elif defined(BSD)

View File

@ -2,6 +2,7 @@
#include "params.h"
#include "strpool.h"
#include "conntrack.h"
#include "desync.h"
#include <sys/param.h>
@ -28,6 +29,7 @@ struct params_s
bool debug;
uint16_t wsize,wssize;
uint8_t wscale,wsscale;
unsigned int wssize_cutoff;
#ifdef __linux__
int qnum;
#elif defined(BSD)
@ -48,6 +50,9 @@ struct params_s
bool droproot;
uid_t uid;
gid_t gid;
unsigned int ctrack_t_syn, ctrack_t_est, ctrack_t_fin;
t_conntrack conntrack;
};
extern struct params_s params;

View File

@ -5,6 +5,7 @@
//#define HASH_BLOOM 20
#define HASH_NONFATAL_OOM 1
#undef HASH_FUNCTION
#define HASH_FUNCTION HASH_BER
#include "uthash.h"