nfqws: --dpi-desync-cutoff

This commit is contained in:
bol-van 2021-03-21 21:55:26 +03:00
parent 29b905c1a1
commit 96cbd2fd5e
13 changed files with 84 additions and 16 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

@ -157,6 +157,7 @@ nfqws takes the following parameters:
--dpi-desync-any-protocol=0|1 ; 0(default)=desync only http and tls 1=desync any nonempty data packet --dpi-desync-any-protocol=0|1 ; 0(default)=desync only http and tls 1=desync any nonempty data packet
--dpi-desync-fake-http=<filename> ; file containing fake http request. replacement for built-in --dpi-desync-fake-http=<filename> ; file containing fake http request. replacement for built-in
--dpi-desync-fake-tls=<filename> ; file containing fake TLS ClientHello (for https). replacement for built-in --dpi-desync-fake-tls=<filename> ; file containing fake TLS ClientHello (for https). replacement for built-in
--dpi-desync-cutoff=N ; apply dpi desync only to packet numbers less than N
--hostlist=<filename> ; apply fooling only to the listed hosts (one host per line, subdomains auto apply) --hostlist=<filename> ; apply fooling only to the listed hosts (one host per line, subdomains auto apply)
The manipulation parameters can be combined in any way. The manipulation parameters can be combined in any way.
@ -273,6 +274,8 @@ If you do not stop and set the low wssize all the time, the speed will drop cata
Linux can overcome this using connbytes filter but other OS may not include similar filter. 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. 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 you deal with a non-http(s) protocol you need --wssize-cutoff. It sets the number of the outgoing packet where wssize stops.
(numbering starts from 1).
If a http request or TLS ClientHello packet is detected wssize stops immediately ignoring wssize-cutoff option.
If your protocol is prone to long inactivity, you should increase ESTABLISHED phase timeout using --ctrack-timeouts. If your protocol is prone to long inactivity, you should increase ESTABLISHED phase timeout using --ctrack-timeouts.
Default timeout is low - only 5 mins. Default timeout is low - only 5 mins.
Don't forget that nfqws feeds with redirected packets. If you have limited redirection with connbytes Don't forget that nfqws feeds with redirected packets. If you have limited redirection with connbytes
@ -293,6 +296,11 @@ Hostlist filter does not affect --wssize because it works since the connection i
to extract the host name. to extract the host name.
--wssize may slow down sites and/or increase response time. It's desired to use another methods if possible. --wssize may slow down sites and/or increase response time. It's desired to use another methods if possible.
--dpi-desync-cutoff allows you to set the limit on the number of the outgoing packet, at which it stops
applying dpi-desync. Useful with --dpi-desync-any-protocol=1.
If the connection falls out of the conntrack and --dpi-desync-cutoff is set, dpi desync will not be applied.
Set conntrack timeouts appropriately.
tpws tpws
----- -----

View File

@ -180,7 +180,7 @@ nfqws
--qnum=N ; номер очереди N --qnum=N ; номер очереди N
--wsize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в SYN,ACK. если не задан scale_factor, то он не меняется (устарело !) --wsize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в SYN,ACK. если не задан scale_factor, то он не меняется (устарело !)
--wssize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в исходящих пакетах. scale_factor по умолчанию 0. (см. conntrack !) --wssize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в исходящих пакетах. scale_factor по умолчанию 0. (см. conntrack !)
--wssize-cutoff=N ; применять изменение server window size в исходящих пакетах по номеру меньше N --wssize-cutoff=N ; изменять server window size в исходящих пакетах по номеру меньше N
--ctrack-timeouts=S:E:F ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN. по умолчанию 60:300:60 --ctrack-timeouts=S:E:F ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN. по умолчанию 60:300:60
--hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:". --hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:".
--hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета --hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета
@ -197,6 +197,7 @@ nfqws
--dpi-desync-any-protocol=0|1 ; 0(default)=работать только по http request и tls clienthello 1=по всем непустым пакетам данных --dpi-desync-any-protocol=0|1 ; 0(default)=работать только по http request и tls clienthello 1=по всем непустым пакетам данных
--dpi-desync-fake-http=<filename> ; файл, содержащий фейковый http запрос для dpi-desync=fake, на замену стандартному w3.org --dpi-desync-fake-http=<filename> ; файл, содержащий фейковый http запрос для dpi-desync=fake, на замену стандартному w3.org
--dpi-desync-fake-tls=<filename> ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному w3.org --dpi-desync-fake-tls=<filename> ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному w3.org
--dpi-desync-cutoff=N ; применять dpi desync только к исходящим пакетам по номеру меньше N
--hostlist=<filename> ; применять дурение только к хостам из листа --hostlist=<filename> ; применять дурение только к хостам из листа
Параметры манипуляции могут сочетаться в любых комбинациях. Параметры манипуляции могут сочетаться в любых комбинациях.
@ -311,7 +312,8 @@ mark нужен, чтобы сгенерированный поддельный
CONNTRACK CONNTRACK
nfqws оснащен ограниченной реализацией слежения за состоянием tcp соединений (conntrack). nfqws оснащен ограниченной реализацией слежения за состоянием tcp соединений (conntrack).
Он включается для реализации некоторых методов противодействия DPI. На текущий момент это параметр --wssize. Он включается для реализации некоторых методов противодействия DPI.
На текущий момент это параметры --wssize и --dpi-desync-cutoff.
conntrack способен следить за фазой соединения : SYN,ESTABLISHED,FIN , количеством пакетов в каждую сторону, sequence numbers. conntrack способен следить за фазой соединения : SYN,ESTABLISHED,FIN , количеством пакетов в каждую сторону, sequence numbers.
conntrack способен "кормиться" пакетами в обе или только в одну сторону. conntrack способен "кормиться" пакетами в обе или только в одну сторону.
Соединение попадает в таблицу при обнаружении пакетов с выставленными флагами SYN или SYN,ACK. Соединение попадает в таблицу при обнаружении пакетов с выставленными флагами SYN или SYN,ACK.
@ -330,8 +332,10 @@ conntrack - простенький, он не писался с учетом в
В linux это может быть купировано через connbytes, но в BSD системах такой возможности нет. В linux это может быть купировано через connbytes, но в BSD системах такой возможности нет.
В случае http(s) останавливаемся сразу после отсылки первого http запроса или TLS ClientHello. В случае http(s) останавливаемся сразу после отсылки первого http запроса или TLS ClientHello.
Если вы имеете дело с не http(s), то вам потребуется параметр --wssize-cutoff. Он устанавливает номер исходящего Если вы имеете дело с не http(s), то вам потребуется параметр --wssize-cutoff. Он устанавливает номер исходящего
пакета, с которого действие wssize прекращается. Если ваш протокол склонен к долгому бездействию, следует увеличить пакета, с которого действие wssize прекращается (нумерация с 1 по аналогии connbytes).
таймаут фазы ESTABLISHED через параметр --ctrack-timeouts. Таймаут по умолчанию низкий - всего 5 минут. Если проскочит пакет с http request или TLS ClientHello, действие wssize прекращается сразу же, не дожидаясь wssize-cutoff.
Если ваш протокол склонен к долгому бездействию, следует увеличить таймаут фазы ESTABLISHED через параметр --ctrack-timeouts.
Таймаут по умолчанию низкий - всего 5 минут.
Не забывайте, что nfqws кормится приходящими на него пакетами. Если вы ограничили поступление пакетов через connbytes, Не забывайте, что nfqws кормится приходящими на него пакетами. Если вы ограничили поступление пакетов через connbytes,
то в таблице могут остаться повисшие соединения в фазе ESTABLISHED, которые отвалятся только по таймауту. то в таблице могут остаться повисшие соединения в фазе ESTABLISHED, которые отвалятся только по таймауту.
Для диагностики состояния conntrack пошлите сигнал SIGUSR1 процессу nfqws : killall -SIGUSR1 nfqws. Для диагностики состояния conntrack пошлите сигнал SIGUSR1 процессу nfqws : killall -SIGUSR1 nfqws.
@ -351,6 +355,11 @@ window size итоговый размер окна стал максимальн
--wssize может замедлять скорость и/или увеличивать время ответа сайтов, поэтому если есть другие работающие способы --wssize может замедлять скорость и/или увеличивать время ответа сайтов, поэтому если есть другие работающие способы
обхода DPI, лучше применять их. обхода DPI, лучше применять их.
--dpi-desync-cutoff позволяет задать предел по номеру исходящего пакета, при достижении которого прекращается
применение dpi-desync. Полезно совместно с --dpi-desync-any-protocol=1.
На склонных к бездействию соединениях следует изменить таймауты conntrack.
Если соединение выпало из conntrack и задана опция --dpi-desync-cutoff, dpi desync применяться не будет.
tpws tpws
----- -----

View File

@ -276,15 +276,15 @@ void ConntrackPoolPurge(t_conntrack *p)
HASH_ITER(hh, p, t, tmp) { \ HASH_ITER(hh, p, t, tmp) { \
*sa1=0; inet_ntop(AF_INET##f, &t->conn.e1.adr, sa1, sizeof(sa1)); \ *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)); \ *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 wsize_orig=%u:%d wsize_reply=%u:%d\n", \ printf("[%s]:%u => [%s]:%u : %s : t0=%lld last=t0+%lld now=last+%lld packets_orig=%llu packets_reply=%llu seq0=%u rseq=%u ack0=%u rack=%u wsize_orig=%u:%d wsize_reply=%u:%d cutoff=%u wss_cutoff=%u d_cutoff=%u\n", \
sa1, t->conn.e1.port, sa2, t->conn.e2.port, \ sa1, t->conn.e1.port, sa2, t->conn.e2.port, \
connstate_s[t->track.state], \ 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), \ (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, \ (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, \ t->track.seq0, t->track.seq_last - t->track.seq0, t->track.ack0, t->track.ack_last - t->track.ack0, \
t->track.winsize_orig, t->track.scale_orig==SCALE_NONE ? -1 : t->track.scale_orig, \ t->track.winsize_orig, t->track.scale_orig==SCALE_NONE ? -1 : t->track.scale_orig, \
t->track.winsize_reply, t->track.scale_reply==SCALE_NONE ? -1 : t->track.scale_reply ); \ t->track.winsize_reply, t->track.scale_reply==SCALE_NONE ? -1 : t->track.scale_reply, \
t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff); \
}; };
void ConntrackPoolDump4(t_conntrack4 *p) void ConntrackPoolDump4(t_conntrack4 *p)
{ {

View File

@ -100,6 +100,33 @@ static bool rawsend_rep(const struct sockaddr* dst,uint32_t fwmark,const void *d
} }
static void maybe_cutoff(t_ctrack *ctrack)
{
if (ctrack)
{
ctrack->b_wssize_cutoff |= params.wssize_cutoff && ctrack->pcounter_orig>=params.wssize_cutoff;
ctrack->b_desync_cutoff |= params.desync_cutoff && ctrack->pcounter_orig>=params.desync_cutoff;
// do not cut off in OpenBSD. It looks like it's not possible to divert-packet only outgoing part of the connection
// It's better to destinguish outgoings using conntrack
#ifndef __OpenBSD__
ctrack->b_cutoff |= (!params.wssize || ctrack->b_wssize_cutoff) && !params.desync_cutoff;
#endif
}
}
static void wssize_cutoff(t_ctrack *ctrack)
{
if (ctrack)
{
ctrack->b_wssize_cutoff = true;
maybe_cutoff(ctrack);
}
}
#ifdef __OpenBSD__
#define CONNTRACK_REQUIRED true
#else
#define CONNTRACK_REQUIRED (params.wssize || params.desync_cutoff)
#endif
// result : true - drop original packet, false = dont drop // result : true - drop original packet, false = dont drop
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 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)
{ {
@ -109,19 +136,18 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
if (!!ip == !!ip6hdr) return res; // one and only one must be present if (!!ip == !!ip6hdr) return res; // one and only one must be present
if (params.wssize) if (CONNTRACK_REQUIRED)
{ {
ConntrackPoolPurge(&params.conntrack); ConntrackPoolPurge(&params.conntrack);
if (ConntrackPoolFeed(&params.conntrack, ip, ip6hdr, tcphdr, len_payload, &ctrack, &bReverse)) if (ConntrackPoolFeed(&params.conntrack, ip, ip6hdr, tcphdr, len_payload, &ctrack, &bReverse))
if (params.wssize_cutoff && ctrack->pcounter_orig>=params.wssize_cutoff) maybe_cutoff(ctrack);
ctrack->b_cutoff=true;
} }
if (params.wsize && tcp_synack_segment(tcphdr)) if (params.wsize && tcp_synack_segment(tcphdr))
{ {
tcp_rewrite_winsize(tcphdr, params.wsize, params.wscale); tcp_rewrite_winsize(tcphdr, params.wsize, params.wscale);
res=modify; res=modify;
} }
if (params.wssize && !bReverse && (ctrack && !ctrack->b_cutoff)) if (params.wssize && !bReverse && (ctrack && !ctrack->b_wssize_cutoff))
{ {
tcp_rewrite_winsize(tcphdr, params.wssize, params.wsscale); tcp_rewrite_winsize(tcphdr, params.wssize, params.wsscale);
res=modify; res=modify;
@ -142,7 +168,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
if ((bIsHttp = IsHttp(data_payload,len_payload))) if ((bIsHttp = IsHttp(data_payload,len_payload)))
{ {
DLOG("packet contains HTTP request\n") DLOG("packet contains HTTP request\n")
if (ctrack && !params.wssize_cutoff) ctrack->b_cutoff = true; wssize_cutoff(ctrack);
fake = params.fake_http; fake = params.fake_http;
fake_size = params.fake_http_size; fake_size = params.fake_http_size;
if (params.hostlist || params.debug) bHaveHost=HttpExtractHost(data_payload,len_payload,host,sizeof(host)); if (params.hostlist || params.debug) bHaveHost=HttpExtractHost(data_payload,len_payload,host,sizeof(host));
@ -155,7 +181,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
else if (IsTLSClientHello(data_payload,len_payload)) else if (IsTLSClientHello(data_payload,len_payload))
{ {
DLOG("packet contains TLS ClientHello\n") DLOG("packet contains TLS ClientHello\n")
if (ctrack && !params.wssize_cutoff) ctrack->b_cutoff = true; wssize_cutoff(ctrack);
fake = params.fake_tls; fake = params.fake_tls;
fake_size = params.fake_tls_size; fake_size = params.fake_tls_size;
if (params.hostlist || params.desync_skip_nosni || params.debug) if (params.hostlist || params.desync_skip_nosni || params.debug)
@ -222,6 +248,16 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
} }
if (params.desync_mode==DESYNC_NONE) return res; if (params.desync_mode==DESYNC_NONE) return res;
if (ctrack && ctrack->b_desync_cutoff)
{
DLOG("not desyncing. cutoff reached : %llu/%u\n", (unsigned long long)ctrack->pcounter_orig, params.desync_cutoff);
return res;
}
if (!ctrack && params.desync_cutoff)
{
DLOG("not desyncing. desync_cutoff is set but conntrack entry is missing\n");
return res;
}
extract_endpoints(ip, ip6hdr, tcphdr, &src, &dst); extract_endpoints(ip, ip6hdr, tcphdr, &src, &dst);
if (params.debug) if (params.debug)

View File

@ -37,8 +37,14 @@
#endif #endif
#define CTRACK_T_SYN 60 #define CTRACK_T_SYN 60
#define CTRACK_T_EST 300
#define CTRACK_T_FIN 60 #define CTRACK_T_FIN 60
#ifdef __OpenBSD__
// It looks like it's not possible to divert-packet only outgoing part of the connection
// It's better to destinguish outgoings using conntrack. Do not purge conntrack entry too early
#define CTRACK_T_EST 7200
#else
#define CTRACK_T_EST 300
#endif
struct params_s params; struct params_s params;
@ -496,6 +502,7 @@ static void exithelp()
" --dpi-desync-any-protocol=0|1\t\t; 0(default)=desync only http and tls 1=desync any nonempty data packet\n" " --dpi-desync-any-protocol=0|1\t\t; 0(default)=desync only http and tls 1=desync any nonempty data packet\n"
" --dpi-desync-fake-http=<filename>\t; file containing fake http request\n" " --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" " --dpi-desync-fake-tls=<filename>\t; file containing fake TLS ClientHello (for https)\n"
" --dpi-desync-cutoff=N\t\t\t; apply dpi desync only to packet numbers less than N\n"
" --hostlist=<filename>\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply)\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, CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN,
#if defined(__linux__) || defined(SO_USER_COOKIE) #if defined(__linux__) || defined(SO_USER_COOKIE)
@ -596,7 +603,8 @@ int main(int argc, char **argv)
{"dpi-desync-any-protocol",optional_argument,0,0},// optidx=22 {"dpi-desync-any-protocol",optional_argument,0,0},// optidx=22
{"dpi-desync-fake-http",required_argument,0,0},// optidx=23 {"dpi-desync-fake-http",required_argument,0,0},// optidx=23
{"dpi-desync-fake-tls",required_argument,0,0},// optidx=24 {"dpi-desync-fake-tls",required_argument,0,0},// optidx=24
{"hostlist",required_argument,0,0}, // optidx=25 {"dpi-desync-cutoff",required_argument,0,0},// optidx=25
{"hostlist",required_argument,0,0}, // optidx=26
{NULL,0,NULL,0} {NULL,0,NULL,0}
}; };
if (argc < 2) exithelp(); if (argc < 2) exithelp();
@ -809,7 +817,14 @@ int main(int argc, char **argv)
exit_clean(1); exit_clean(1);
} }
break; break;
case 25: /* hostlist */ case 25: /* desync-cutoff */
if (!sscanf(optarg, "%u", &params.desync_cutoff))
{
fprintf(stderr, "invalid desync-cutoff value\n");
exit_clean(1);
}
break;
case 26: /* hostlist */
if (!LoadHostList(&params.hostlist, optarg)) if (!LoadHostList(&params.hostlist, optarg))
exit_clean(1); exit_clean(1);
strncpy(params.hostfile,optarg,sizeof(params.hostfile)); strncpy(params.hostfile,optarg,sizeof(params.hostfile));