diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index b90a6f2..bf091f5 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/arm/nfqws b/binaries/arm/nfqws index 38a4d2b..2ed7f7b 100755 Binary files a/binaries/arm/nfqws and b/binaries/arm/nfqws differ diff --git a/binaries/mips32r1-lsb/nfqws b/binaries/mips32r1-lsb/nfqws index 915a873..33e00b8 100755 Binary files a/binaries/mips32r1-lsb/nfqws and b/binaries/mips32r1-lsb/nfqws differ diff --git a/binaries/mips32r1-msb/nfqws b/binaries/mips32r1-msb/nfqws index 920cabc..fdc223c 100755 Binary files a/binaries/mips32r1-msb/nfqws and b/binaries/mips32r1-msb/nfqws differ diff --git a/binaries/mips64r2-msb/nfqws b/binaries/mips64r2-msb/nfqws index 7d04126..8327788 100755 Binary files a/binaries/mips64r2-msb/nfqws and b/binaries/mips64r2-msb/nfqws differ diff --git a/binaries/ppc/nfqws b/binaries/ppc/nfqws index 7b64639..81df7de 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index 59a010c..6acc625 100755 Binary files a/binaries/x86/nfqws and b/binaries/x86/nfqws differ diff --git a/binaries/x86_64/nfqws b/binaries/x86_64/nfqws index 31ddab1..359a217 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/docs/bsdfw.txt b/docs/bsdfw.txt index 1788141..1d1970f 100644 --- a/docs/bsdfw.txt +++ b/docs/bsdfw.txt @@ -27,6 +27,7 @@ ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1 ipfw delete 100 ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted not sockarg xmit em0 +ipfw add 100 divert 989 udp from any to any 443 out not diverted not sockarg xmit em0 ipfw delete 100 ipfw add 100 allow tcp from me to table\(nozapret\) 80,443 diff --git a/docs/changes.txt b/docs/changes.txt index a2b0d50..806cdd0 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -210,3 +210,7 @@ blockcheck.sh v43 nfqws: UDP desync with conntrack support (any-protocol only for now) + +v44 + +nfqws: ipfrag diff --git a/docs/iptables.txt b/docs/iptables.txt index 51b806c..f00f3e2 100644 --- a/docs/iptables.txt +++ b/docs/iptables.txt @@ -5,13 +5,14 @@ iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK - For outgoing data manipulation ("Host:" case changing) : -iptables -t mangle -I POSTROUTING -p tcp --dport 80 -j NFQUEUE --queue-num 200 --queue-bypass iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:5 -j NFQUEUE --queue-num 200 --queue-bypass For dpi desync attack : iptables -t mangle -I POSTROUTING -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass +iptables -t mangle -I POSTROUTING -p tcp --dport 443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass +iptables -t mangle -I POSTROUTING -p udp --dport 443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass For TPROXY : diff --git a/docs/readme.eng.md b/docs/readme.eng.md index a39b30c..949a5da 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -139,7 +139,7 @@ nfqws takes the following parameters: --pidfile= ; write pid to file --user= ; drop root privs --uid=uid[:gid] ; drop root privs - --dpi-desync=[[,] ; desync dpi state. modes : synack fake rst rstack disorder disorder2 split split2 + --dpi-desync=[[,] ; desync dpi state. modes : synack fake rst rstack disorder disorder2 split split2 ipfrag2 --dpi-desync-fwmark= ; override fwmark for desync packet. default = 0x40000000 --dpi-desync-ttl= ; set ttl for desync packet --dpi-desync-ttl6= ; set ipv6 hop limit for desync packet. by default ttl value is used @@ -148,6 +148,8 @@ nfqws takes the following parameters: --dpi-desync-repeats= ; send every desync packet N times --dpi-desync-skip-nosni=0|1 ; 1(default)=do not apply desync to requests without hostname in the SNI --dpi-desync-split-pos=<1..1500> ; (for split* and disorder* only) split TCP packet at specified position + --dpi-desync-ipfrag-pos-tcp=<8..9216> ; ip frag position starting from the second header (usually transport header). multiple of 8, default 8. + --dpi-desync-ipfrag-pos-udp=<8..9216> ; ip frag position starting from the second header (usually transport header). multiple of 8, default 32. --dpi-desync-badseq-increment= ; badseq fooling seq signed increment. default -10000 --dpi-desync-badack-increment= ; badseq fooling ackseq signed increment. default -66000 --dpi-desync-any-protocol=0|1 ; 0(default)=desync only http and tls 1=desync any nonempty data packet @@ -391,6 +393,14 @@ parameter of `--ctrack-timeouts`. Fake attack is useful only for stateful DPI and useless for stateless dealing with each packet independently. By default fake payload is 64 zeroes. Can be overriden using `--dpi-desync-fake-unknown-udp`. +### IP fragmentation + +Modern network is very hostile to IP fragmentation. Fragmented packets are often not delivered or refragmented/reassembled +on the way. Linux always reassembles forwarded fragmented ipv6 if possible and it cannot be disablled. +But Linux can send fragments. +Frag position is set independently for tcp and udp. By default 24 and 8, must be multiple of 8. +Offset starts from the header following ip header - transport header in most cases. + ## tpws diff --git a/docs/readme.txt b/docs/readme.txt index 43647d6..3ee133b 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -1,4 +1,4 @@ -zapret v.43 +zapret v.44 English ------- @@ -187,7 +187,7 @@ nfqws --hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета --hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase --domcase ; домен после Host: сделать таким : TeSt.cOm - --dpi-desync=[,][,,][, ; бит fwmark для пометки десинхронизирующих пакетов, чтобы они повторно не падали в очередь. default = 0x40000000 --dpi-desync-ttl= ; установить ttl для десинхронизирующих пакетов --dpi-desync-ttl6= ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение ttl @@ -418,13 +418,20 @@ window size итоговый размер окна стал максимальн ПОДДЕРЖКА UDP Атаки на udp более ограничены в возможностях. udp нельзя фрагментировать иначе, чем на уровне ip. -ip фрагментация на данный момент не реализована. Пока что реализован только метод десинхронизации fake в режиме --dpi-desync-any-protocol. Реализован conntrack для udp. Можно пользоваться --dpi-desync-cutoff. Таймаут conntrack для udp можно изменить 4-м параметром в --ctrack-timeouts. Атака fake полезна только для stateful DPI, она бесполезна для анализа на уровне отдельных пакетов. По умолчанию fake наполнение - 64 нуля. Можно указать файл в --dpi-desync-fake-unknown-udp. +IP ФРАГМЕНТАЦИЯ +В современной сети с этом все очень плохо. Фрагментированные пакеты застревают по пути, часто отбрасываются. +Иногда доходят. Иногда то доходят, то не доходят. Может зависеть от версии ipv4/ipv6. +Роутеры на базе linux и freebsd могут самопроизвольно собирать или перефрагментировать пакеты. +Linux всегда собирает проходящие ipv6 фрагменты, и это неотключаемо, но может отсылать фрагменты сам. +Позиция фрагментации задается отдельно для tcp и udp. По умолчанию 24 и 8 соответственно, должна быть кратна 8. +Смещение считается с заголовка, следующего за ip. В большинство случаев это транспортный заголовок. + tpws ----- diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c index b8c0671..a032993 100644 --- a/nfq/darkmagic.c +++ b/nfq/darkmagic.c @@ -307,6 +307,100 @@ bool prepare_udp_segment( +// split ipv4 packet into 2 fragments at data payload position frag_pos +bool ip_frag4( + const uint8_t *pkt, size_t pkt_size, + size_t frag_pos, uint32_t ident, + uint8_t *pkt1, size_t *pkt1_size, + uint8_t *pkt2, size_t *pkt2_size) +{ + uint16_t hdrlen, payload_len; + // frag_pos must be 8-byte aligned + if (frag_pos & 7 || pkt_size < sizeof(struct ip)) return false; + payload_len = htons(((struct ip *)pkt)->ip_len); + hdrlen = ((struct ip *)pkt)->ip_hl<<2; + if (payload_len>pkt_size || hdrlen>pkt_size || hdrlen>payload_len) return false; + payload_len -= hdrlen; + if (frag_pos>=payload_len || *pkt1_size<(hdrlen+frag_pos) || *pkt2_size<(hdrlen+payload_len-frag_pos)) return false; + + memcpy(pkt1, pkt, hdrlen+frag_pos); + ((struct ip*)pkt1)->ip_off = htons(IP_MF); + ((struct ip*)pkt1)->ip_len = htons(hdrlen+frag_pos); + if (ident!=(uint32_t)-1) ((struct ip*)pkt1)->ip_id = (uint16_t)ident; + + *pkt1_size=hdrlen+frag_pos; + ip4_fix_checksum((struct ip *)pkt1); + + memcpy(pkt2, pkt, hdrlen); + memcpy(pkt2+hdrlen, pkt+hdrlen+frag_pos, payload_len-frag_pos); + ((struct ip*)pkt2)->ip_off = htons((uint16_t)frag_pos>>3 & IP_OFFMASK); + ((struct ip*)pkt2)->ip_len = htons(hdrlen+payload_len-frag_pos); + if (ident!=(uint32_t)-1) ((struct ip*)pkt2)->ip_id = (uint16_t)ident; + *pkt2_size=hdrlen+payload_len-frag_pos; + ip4_fix_checksum((struct ip *)pkt2); + + return true; +} +bool ip_frag6( + const uint8_t *pkt, size_t pkt_size, + size_t frag_pos, uint32_t ident, + uint8_t *pkt1, size_t *pkt1_size, + uint8_t *pkt2, size_t *pkt2_size) +{ + uint16_t payload_len; + uint8_t proto; + struct ip6_frag *frag; + + if (frag_pos & 7 || pkt_size < sizeof(struct ip6_hdr)) return false; + payload_len = htons(((struct ip6_hdr*)pkt)->ip6_ctlun.ip6_un1.ip6_un1_plen); + if ((sizeof(struct ip6_hdr)+payload_len)>pkt_size || frag_pos>=payload_len || + *pkt1_size<(sizeof(struct ip6_hdr)+sizeof(struct ip6_frag)+frag_pos) || + *pkt2_size<(sizeof(struct ip6_hdr)+sizeof(struct ip6_frag)+payload_len-frag_pos)) + { + return false; + } + + proto = ((struct ip6_hdr*)pkt)->ip6_ctlun.ip6_un1.ip6_un1_nxt; + + memcpy(pkt1, pkt, sizeof(struct ip6_hdr)); + ((struct ip6_hdr*)pkt1)->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(sizeof(struct ip6_frag)+frag_pos); + ((struct ip6_hdr*)pkt1)->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_FRAGMENT; + frag = (struct ip6_frag*)(((struct ip6_hdr*)pkt1)+1); + frag->ip6f_nxt = proto; + frag->ip6f_reserved = 0; + frag->ip6f_offlg = IP6F_MORE_FRAG; + frag->ip6f_ident = ident; + memcpy(frag+1, pkt+sizeof(struct ip6_hdr), frag_pos); + *pkt1_size = sizeof(struct ip6_hdr)+sizeof(struct ip6_frag)+frag_pos; + + memcpy(pkt2, pkt, sizeof(struct ip6_hdr)); + ((struct ip6_hdr*)pkt2)->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(sizeof(struct ip6_frag)+payload_len-frag_pos); + ((struct ip6_hdr*)pkt2)->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_FRAGMENT; + frag = (struct ip6_frag*)(((struct ip6_hdr*)pkt2)+1); + frag->ip6f_nxt = proto; + frag->ip6f_reserved = 0; + frag->ip6f_offlg = htons(frag_pos); + frag->ip6f_ident = ident; + memcpy(frag+1, pkt+sizeof(struct ip6_hdr)+frag_pos, payload_len-frag_pos); + *pkt2_size = sizeof(struct ip6_hdr)+sizeof(struct ip6_frag)+payload_len-frag_pos; + + return true; +} +bool ip_frag( + const uint8_t *pkt, size_t pkt_size, + size_t frag_pos, uint32_t ident, + uint8_t *pkt1, size_t *pkt1_size, + uint8_t *pkt2, size_t *pkt2_size) +{ + if (proto_check_ipv4(pkt,pkt_size)) + return ip_frag4(pkt,pkt_size,frag_pos,ident,pkt1,pkt1_size,pkt2,pkt2_size); + else if (proto_check_ipv6(pkt,pkt_size)) + return ip_frag6(pkt,pkt_size,frag_pos,ident,pkt1,pkt1_size,pkt2,pkt2_size); + else + return false; +} + + void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport) { if (sport) *sport = htons(tcphdr ? tcphdr->th_sport : udphdr ? udphdr->uh_sport : 0); diff --git a/nfq/darkmagic.h b/nfq/darkmagic.h index 40f60d4..ebf2666 100644 --- a/nfq/darkmagic.h +++ b/nfq/darkmagic.h @@ -84,6 +84,24 @@ bool prepare_udp_segment( uint8_t *buf, size_t *buflen); +// ipv4: ident==-1 - copy ip_id from original ipv4 packet +bool ip_frag4( + const uint8_t *pkt, size_t pkt_size, + size_t frag_pos, uint32_t ident, + uint8_t *pkt1, size_t *pkt1_size, + uint8_t *pkt2, size_t *pkt2_size); +bool ip_frag6( + const uint8_t *pkt, size_t pkt_size, + size_t frag_pos, uint32_t ident, + uint8_t *pkt1, size_t *pkt1_size, + uint8_t *pkt2, size_t *pkt2_size); +bool ip_frag( + const uint8_t *pkt, size_t pkt_size, + size_t frag_pos, uint32_t ident, + uint8_t *pkt1, size_t *pkt1_size, + uint8_t *pkt2, size_t *pkt2_size); + + void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport); void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr,const struct udphdr *udphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst); uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind); diff --git a/nfq/desync.c b/nfq/desync.c index c17b91f..20a3c86 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -50,7 +50,8 @@ const uint8_t fake_tls_clienthello_default[517] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; -#define PKT_MAXDUMP 32 +#define PKTDATA_MAXDUMP 32 +#define IP_MAXDUMP 64 static uint8_t zeropkt[DPI_DESYNC_MAX_FAKE_LEN]; @@ -70,7 +71,7 @@ bool desync_valid_first_stage(enum dpi_desync_mode mode) } bool desync_valid_second_stage(enum dpi_desync_mode mode) { - return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2; + return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_IPFRAG2; } enum dpi_desync_mode desync_mode_from_string(const char *s) { @@ -92,6 +93,8 @@ enum dpi_desync_mode desync_mode_from_string(const char *s) return DESYNC_SPLIT; else if (!strcmp(s,"split2")) return DESYNC_SPLIT2; + else if (!strcmp(s,"ipfrag2")) + return DESYNC_IPFRAG2; return DESYNC_INVALID; } @@ -151,8 +154,8 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s bool bReverse=false; struct sockaddr_storage src, dst; - uint8_t newdata[DPI_DESYNC_MAX_FAKE_LEN+100]; - size_t newlen; + uint8_t pkt1[DPI_DESYNC_MAX_FAKE_LEN+100], pkt2[DPI_DESYNC_MAX_FAKE_LEN+100]; + size_t pkt1_len, pkt2_len; uint8_t ttl_orig,ttl_fake,flags_orig,scale_factor; uint32_t *timestamps; @@ -210,15 +213,15 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s if (params.desync_mode0==DESYNC_SYNACK && tcp_syn_segment(tcphdr)) { - newlen = sizeof(newdata); + pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_SYN|TH_ACK, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, - NULL, 0, newdata, &newlen)) + NULL, 0, pkt1, &pkt1_len)) { return res; } DLOG("sending fake SYNACK\n"); - if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) return res; } @@ -349,26 +352,26 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s enum dpi_desync_mode desync_mode = params.desync_mode; bool b; - newlen = sizeof(newdata); + pkt1_len = sizeof(pkt1); b = false; switch(desync_mode) { case DESYNC_FAKE: if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, - fake, fake_size, newdata, &newlen)) + fake, fake_size, pkt1, &pkt1_len)) { return res; } DLOG("sending fake request : "); - hexdump_limited_dlog(fake,fake_size,PKT_MAXDUMP); DLOG("\n") + hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n") b = true; break; case DESYNC_RST: case DESYNC_RSTACK: if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (desync_mode==DESYNC_RSTACK ? TH_ACK:0), tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, - NULL, 0, newdata, &newlen)) + NULL, 0, pkt1, &pkt1_len)) { return res; } @@ -379,7 +382,7 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s if (b) { - if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) return res; if (params.desync_mode2==DESYNC_NONE) { @@ -406,13 +409,13 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s desync_mode = params.desync_mode2; } - newlen = sizeof(newdata); + size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1; + pkt1_len = sizeof(pkt1); switch(desync_mode) { case DESYNC_DISORDER: case DESYNC_DISORDER2: { - size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1; uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100]; size_t fakeseg_len; @@ -420,11 +423,11 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s { if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(tcphdr->th_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, - data_payload+split_pos, len_payload-split_pos, newdata, &newlen)) + data_payload+split_pos, len_payload-split_pos, pkt1, &pkt1_len)) return res; DLOG("sending 2nd out-of-order tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos) - hexdump_limited_dlog(data_payload+split_pos,len_payload-split_pos,PKT_MAXDUMP); DLOG("\n") - if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + hexdump_limited_dlog(data_payload+split_pos,len_payload-split_pos,PKTDATA_MAXDUMP); DLOG("\n") + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) return res; } @@ -437,26 +440,26 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s zeropkt, split_pos, fakeseg, &fakeseg_len)) return res; DLOG("sending fake(1) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) - hexdump_limited_dlog(zeropkt,split_pos,PKT_MAXDUMP); DLOG("\n") + hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n") if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) return res; } - newlen = sizeof(newdata); + pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, - data_payload, split_pos, newdata, &newlen)) + data_payload, split_pos, pkt1, &pkt1_len)) return res; DLOG("sending 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) - hexdump_limited_dlog(data_payload,split_pos,PKT_MAXDUMP); DLOG("\n") - if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + hexdump_limited_dlog(data_payload,split_pos,PKTDATA_MAXDUMP); DLOG("\n") + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) return res; if (desync_mode==DESYNC_DISORDER) { DLOG("sending fake(2) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) - hexdump_limited_dlog(zeropkt,split_pos,PKT_MAXDUMP); DLOG("\n") + hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n") if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) return res; } @@ -467,7 +470,6 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s case DESYNC_SPLIT: case DESYNC_SPLIT2: { - size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1; uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100]; size_t fakeseg_len; @@ -479,45 +481,74 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s zeropkt, split_pos, fakeseg, &fakeseg_len)) return res; DLOG("sending fake(1) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) - hexdump_limited_dlog(zeropkt,split_pos,PKT_MAXDUMP); DLOG("\n") + hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n") if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) return res; } - newlen = sizeof(newdata); + pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, - data_payload, split_pos, newdata, &newlen)) + data_payload, split_pos, pkt1, &pkt1_len)) return res; DLOG("sending 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) - hexdump_limited_dlog(data_payload,split_pos,PKT_MAXDUMP); DLOG("\n") - if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + hexdump_limited_dlog(data_payload,split_pos,PKTDATA_MAXDUMP); DLOG("\n") + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) return res; if (desync_mode==DESYNC_SPLIT) { DLOG("sending fake(2) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) - hexdump_limited_dlog(zeropkt,split_pos,PKT_MAXDUMP); DLOG("\n") + hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n") if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len)) return res; } if (split_posth_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, - data_payload+split_pos, len_payload-split_pos, newdata, &newlen)) + data_payload+split_pos, len_payload-split_pos, pkt1, &pkt1_len)) return res; DLOG("sending 2nd tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos) - hexdump_limited_dlog(data_payload+split_pos,len_payload-split_pos,PKT_MAXDUMP); DLOG("\n") - if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + hexdump_limited_dlog(data_payload+split_pos,len_payload-split_pos,PKTDATA_MAXDUMP); DLOG("\n") + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) return res; } return drop; } break; + case DESYNC_IPFRAG2: + { + #ifdef __FreeBSD__ + // FreeBSD tend to pass ipv6 frames with wrong checksum + if (ip6hdr) + tcp_fix_checksum(tcphdr,len_tcp,ip,ip6hdr); + #endif + + size_t ipfrag_pos = (params.desync_ipfrag_pos_tcp && params.desync_ipfrag_pos_tcpip_id ? ip->ip_id : htons(1+random()%0xFFFF) : htonl(1+random()&0xFFFFFFFF); + + pkt1_len = sizeof(pkt1); + pkt2_len = sizeof(pkt2); + + if (!ip_frag(data_pkt, len_pkt, ipfrag_pos, ident, pkt1, &pkt1_len, pkt2, &pkt2_len)) + return res; + + DLOG("sending 1st ip fragment 0-%zu len=%zu : ", ipfrag_pos-1, ipfrag_pos) + hexdump_limited_dlog(pkt1,pkt1_len,IP_MAXDUMP); DLOG("\n") + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, pkt2, pkt2_len)) + return res; + + DLOG("sending 2nd ip fragment %zu-%zu len=%zu : ", ipfrag_pos, len_tcp-1, len_tcp-ipfrag_pos) + hexdump_limited_dlog(pkt2,pkt2_len,IP_MAXDUMP); DLOG("\n") + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) + return res; + + return frag; + } } } @@ -533,8 +564,8 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s bool bReverse=false; struct sockaddr_storage src, dst; - uint8_t newdata[DPI_DESYNC_MAX_FAKE_LEN+100]; - size_t newlen; + uint8_t pkt1[DPI_DESYNC_MAX_FAKE_LEN+100], pkt2[DPI_DESYNC_MAX_FAKE_LEN+100]; + size_t pkt1_len, pkt2_len; uint8_t ttl_orig,ttl_fake; if (!!ip == !!ip6hdr) return res; // one and only one must be present @@ -579,6 +610,8 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s if (!params.desync_any_proto) return res; DLOG("applying tampering to unknown protocol\n") + enum dpi_desync_mode desync_mode = params.desync_mode; + ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim; if (ip6hdr) ttl_fake = params.desync_ttl6 ? params.desync_ttl6 : ttl_orig; else ttl_fake = params.desync_ttl ? params.desync_ttl : ttl_orig; @@ -596,36 +629,77 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s printf("\n"); } - newlen = sizeof(newdata); + pkt1_len = sizeof(pkt1); b = false; - switch(params.desync_mode) + switch(desync_mode) { case DESYNC_FAKE: - if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, params.desync_fooling_mode, fake, fake_size, newdata, &newlen)) + if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, params.desync_fooling_mode, fake, fake_size, pkt1, &pkt1_len)) return res; DLOG("sending fake request : "); - hexdump_limited_dlog(fake,fake_size,PKT_MAXDUMP); DLOG("\n") + hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n") b = true; break; } if (b) { - if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) - return res; - DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", len_pkt, len_payload) - #ifdef __FreeBSD__ - // FreeBSD tend to pass ipv6 frames with wrong checksum - if (res==modify || ip6hdr) - #else - // if original packet was tampered earlier it needs checksum fixed - if (res==modify) - #endif - udp_fix_checksum(udphdr,sizeof(struct udphdr)+len_payload,ip,ip6hdr); - if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, data_pkt, len_pkt)) - return res; - return drop; + if (params.desync_mode2==DESYNC_NONE) + { + if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) + return res; + DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", len_pkt, len_payload) + #ifdef __FreeBSD__ + // FreeBSD tend to pass ipv6 frames with wrong checksum + if (res==modify || ip6hdr) + #else + // if original packet was tampered earlier it needs checksum fixed + if (res==modify) + #endif + udp_fix_checksum(udphdr,sizeof(struct udphdr)+len_payload,ip,ip6hdr); + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, data_pkt, len_pkt)) + return res; + return drop; + } + desync_mode = params.desync_mode2; } + + switch(desync_mode) + { + case DESYNC_IPFRAG2: + { + + #ifdef __FreeBSD__ + // FreeBSD tend to pass ipv6 frames with wrong checksum + if (ip6hdr) + udp_fix_checksum(udphdr,sizeof(struct udphdr)+len_payload,ip,ip6hdr); + #endif + + size_t len_transport = len_payload + sizeof(struct udphdr); + size_t ipfrag_pos = (params.desync_ipfrag_pos_udp && params.desync_ipfrag_pos_udpip_id ? ip->ip_id : htons(1+random()%0xFFFF) : htonl(1+random()&0xFFFFFFFF); + + pkt1_len = sizeof(pkt1); + pkt2_len = sizeof(pkt2); + + if (!ip_frag(data_pkt, len_pkt, ipfrag_pos, ident, pkt1, &pkt1_len, pkt2, &pkt2_len)) + return res; + + DLOG("sending 1st ip fragment 0-%zu len=%zu : ", ipfrag_pos-1, ipfrag_pos) + hexdump_limited_dlog(pkt1,pkt1_len,IP_MAXDUMP); DLOG("\n") + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, pkt2, pkt2_len)) + return res; + + DLOG("sending 2nd ip fragment %zu-%zu len=%zu : ", ipfrag_pos, len_transport-1, len_transport-ipfrag_pos) + hexdump_limited_dlog(pkt2,pkt2_len,IP_MAXDUMP); DLOG("\n") + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, pkt1, pkt1_len)) + return res; + + return frag; + } + } + } return res; diff --git a/nfq/desync.h b/nfq/desync.h index 74402d2..2a21cd6 100644 --- a/nfq/desync.h +++ b/nfq/desync.h @@ -28,7 +28,8 @@ enum dpi_desync_mode { DESYNC_DISORDER, DESYNC_DISORDER2, DESYNC_SPLIT, - DESYNC_SPLIT2 + DESYNC_SPLIT2, + DESYNC_IPFRAG2 }; extern const char *fake_http_request_default; diff --git a/nfq/nfqws.c b/nfq/nfqws.c index cbb59a3..d80bfe3 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -148,7 +148,7 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt // ipv6 packets were with incorrect checksum #ifdef __FreeBSD__ // FreeBSD tend to pass ipv6 frames with wrong checksum - if (res==modify || ip6hdr) + if (res==modify || res!=frag && ip6hdr) #else if (res==modify) #endif @@ -171,7 +171,7 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt res = dpi_desync_udp_packet(data_pkt, len_pkt, ip, ip6hdr, udphdr, data, len); #ifdef __FreeBSD__ // FreeBSD tend to pass ipv6 frames with wrong checksum - if (res==modify || ip6hdr) + if (res==modify || res!=frag && ip6hdr) #else if (res==modify) #endif @@ -209,6 +209,7 @@ static int nfq_cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, DLOG("packet: id=%d pass modified\n", id); return nfq_set_verdict2(qh, id, NF_ACCEPT, mark, len, data); case drop: + case frag: DLOG("packet: id=%d drop\n", id); return nfq_set_verdict2(qh, id, NF_DROP, mark, 0, NULL); } @@ -504,7 +505,7 @@ static void exithelp() " --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" " --domcase\t\t\t\t; mix domain case : Host: TeSt.cOm\n" - " --dpi-desync=[,][,] ; try to desync dpi state. modes : synack fake rst rstack disorder disorder2 split split2\n" + " --dpi-desync=[,][,] ; try to desync dpi state. modes : synack fake rst rstack disorder disorder2 split split2 ipfrag2\n" #ifdef __linux__ " --dpi-desync-fwmark=\t; override fwmark for desync packet. default = 0x%08X (%u)\n" #elif defined(SO_USER_COOKIE) @@ -518,7 +519,9 @@ static void exithelp() #endif " --dpi-desync-repeats=\t\t; send every desync packet N times\n" " --dpi-desync-skip-nosni=0|1\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\n" - " --dpi-desync-split-pos=<1..%u>\t; TCP packet split position\n" + " --dpi-desync-split-pos=<1..%u>\t; data payload split position\n" + " --dpi-desync-ipfrag-pos-tcp=<8..%u>\t; ip frag position starting from the second header (usually transport header). multiple of 8, default %u.\n" + " --dpi-desync-ipfrag-pos-udp=<8..%u>\t; ip frag position starting from the second header (usually transport header). multiple of 8, default %u.\n" " --dpi-desync-badseq-increment= ; badseq fooling seq signed increment. default %d\n" " --dpi-desync-badack-increment= ; badseq fooling ackseq signed increment. default %d\n" " --dpi-desync-any-protocol=0|1\t\t; 0(default)=desync only http and tls 1=desync any nonempty data packet\n" @@ -533,6 +536,8 @@ static void exithelp() DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT, #endif DPI_DESYNC_MAX_FAKE_LEN, + DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_UDP_DEFAULT, + DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_TCP_DEFAULT, BADSEQ_INCREMENT_DEFAULT, BADSEQ_ACK_INCREMENT_DEFAULT ); exit(1); @@ -602,6 +607,8 @@ int main(int argc, char **argv) params.desync_fwmark = DPI_DESYNC_FWMARK_DEFAULT; params.desync_skip_nosni = true; params.desync_split_pos = 2; + params.desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT; + params.desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT; params.desync_repeats = 1; params.fake_tls_size = sizeof(fake_tls_clienthello_default); memcpy(params.fake_tls,fake_tls_clienthello_default,params.fake_tls_size); @@ -661,15 +668,17 @@ int main(int argc, char **argv) {"dpi-desync-repeats",required_argument,0,0}, // optidx=20 {"dpi-desync-skip-nosni",optional_argument,0,0},// optidx=21 {"dpi-desync-split-pos",required_argument,0,0},// optidx=22 - {"dpi-desync-badseq-increment",required_argument,0,0},// optidx=23 - {"dpi-desync-badack-increment",required_argument,0,0},// optidx=24 - {"dpi-desync-any-protocol",optional_argument,0,0},// optidx=25 - {"dpi-desync-fake-http",required_argument,0,0},// optidx=26 - {"dpi-desync-fake-tls",required_argument,0,0},// optidx=27 - {"dpi-desync-fake-unknown",required_argument,0,0},// optidx=28 - {"dpi-desync-fake-unknown-udp",required_argument,0,0},// optidx=29 - {"dpi-desync-cutoff",required_argument,0,0},// optidx=30 - {"hostlist",required_argument,0,0}, // optidx=31 + {"dpi-desync-ipfrag-pos-tcp",required_argument,0,0},// optidx=24 + {"dpi-desync-ipfrag-pos-udp",required_argument,0,0},// optidx=25 + {"dpi-desync-badseq-increment",required_argument,0,0},// optidx=26 + {"dpi-desync-badack-increment",required_argument,0,0},// optidx=27 + {"dpi-desync-any-protocol",optional_argument,0,0},// optidx=28 + {"dpi-desync-fake-http",required_argument,0,0},// optidx=29 + {"dpi-desync-fake-tls",required_argument,0,0},// optidx=30 + {"dpi-desync-fake-unknown",required_argument,0,0},// optidx=31 + {"dpi-desync-fake-unknown-udp",required_argument,0,0},// optidx=32 + {"dpi-desync-cutoff",required_argument,0,0},// optidx=33 + {"hostlist",required_argument,0,0}, // optidx=34 {NULL,0,NULL,0} }; if (argc < 2) exithelp(); @@ -806,6 +815,13 @@ int main(int argc, char **argv) fprintf(stderr, "invalid desync combo : %s+%s\n", mode,mode2); exit_clean(1); } + #if defined(__OpenBSD__) + if (params.desync_mode==DESYNC_IPFRAG2 || params.desync_mode2==DESYNC_IPFRAG2) + { + fprintf(stderr, "OpenBSD has checksum issues with fragmented packets. ipfrag disabled.\n"); + exit_clean(1); + } + #endif } break; case 15: /* dpi-desync-fwmark/dpi-desync-sockarg */ @@ -866,8 +882,7 @@ int main(int argc, char **argv) #endif break; case 20: /* dpi-desync-repeats */ - params.desync_repeats = atoi(optarg); - if (params.desync_repeats<=0 || params.desync_repeats>20) + if (sscanf(optarg,"%u",¶ms.desync_repeats)<1 || params.desync_repeats<=0 || params.desync_repeats>20) { fprintf(stderr, "dpi-desync-repeats must be within 1..20\n"); exit_clean(1); @@ -877,54 +892,77 @@ int main(int argc, char **argv) params.desync_skip_nosni = !optarg || atoi(optarg); break; case 22: /* 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) + if (sscanf(optarg,"%u",¶ms.desync_split_pos)<1 || params.desync_split_pos<1 || params.desync_split_pos>DPI_DESYNC_MAX_FAKE_LEN) { fprintf(stderr, "dpi-desync-split-pos must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN); exit_clean(1); } break; - case 23: /* dpi-desync-badseq-increments */ + case 23: /* dpi-desync-ipfrag-pos-tcp */ + if (sscanf(optarg,"%u",¶ms.desync_ipfrag_pos_tcp)<1 || params.desync_ipfrag_pos_tcp<1 || params.desync_ipfrag_pos_tcp>DPI_DESYNC_MAX_FAKE_LEN) + { + fprintf(stderr, "dpi-desync-ipfrag-pos-tcp must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN); + exit_clean(1); + } + if (params.desync_ipfrag_pos_tcp & 7) + { + fprintf(stderr, "dpi-desync-ipfrag-pos-tcp must be multiple of 8\n"); + exit_clean(1); + } + break; + case 24: /* dpi-desync-ipfrag-pos-udp */ + if (sscanf(optarg,"%u",¶ms.desync_ipfrag_pos_udp)<1 || params.desync_ipfrag_pos_udp<1 || params.desync_ipfrag_pos_udp>DPI_DESYNC_MAX_FAKE_LEN) + { + fprintf(stderr, "dpi-desync-ipfrag-pos-udp must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN); + exit_clean(1); + } + if (params.desync_ipfrag_pos_udp & 7) + { + fprintf(stderr, "dpi-desync-ipfrag-pos-udp must be multiple of 8\n"); + exit_clean(1); + } + break; + case 25: /* dpi-desync-badseq-increments */ if (!parse_badseq_increment(optarg,¶ms.desync_badseq_increment)) { fprintf(stderr, "dpi-desync-badseq-increment should be signed decimal or signed 0xHEX\n"); exit_clean(1); } break; - case 24: /* dpi-desync-badack-increment */ + case 26: /* dpi-desync-badack-increment */ if (!parse_badseq_increment(optarg,¶ms.desync_badseq_ack_increment)) { fprintf(stderr, "dpi-desync-badack-increment should be signed decimal or signed 0xHEX\n"); exit_clean(1); } break; - case 25: /* dpi-desync-any-protocol */ + case 27: /* dpi-desync-any-protocol */ params.desync_any_proto = !optarg || atoi(optarg); break; - case 26: /* dpi-desync-fake-http */ + case 28: /* dpi-desync-fake-http */ params.fake_http_size = sizeof(params.fake_http); load_file_or_exit(optarg,params.fake_http,¶ms.fake_http_size); break; - case 27: /* dpi-desync-fake-tls */ + case 29: /* dpi-desync-fake-tls */ params.fake_tls_size = sizeof(params.fake_tls); load_file_or_exit(optarg,params.fake_tls,¶ms.fake_tls_size); break; - case 28: /* dpi-desync-fake-unknown */ + case 30: /* dpi-desync-fake-unknown */ params.fake_unknown_size = sizeof(params.fake_unknown); load_file_or_exit(optarg,params.fake_unknown,¶ms.fake_unknown_size); break; - case 29: /* dpi-desync-fake-unknown-udp */ + case 31: /* dpi-desync-fake-unknown-udp */ params.fake_unknown_udp_size = sizeof(params.fake_unknown_udp); load_file_or_exit(optarg,params.fake_unknown_udp,¶ms.fake_unknown_udp_size); break; - case 30: /* desync-cutoff */ + case 32: /* desync-cutoff */ if (!parse_cutoff(optarg, ¶ms.desync_cutoff, ¶ms.desync_cutoff_mode)) { fprintf(stderr, "invalid desync-cutoff value\n"); exit_clean(1); } break; - case 31: /* hostlist */ + case 33: /* hostlist */ if (!LoadHostList(¶ms.hostlist, optarg)) exit_clean(1); strncpy(params.hostfile,optarg,sizeof(params.hostfile)); diff --git a/nfq/nfqws.h b/nfq/nfqws.h index 216bb9e..c9d1870 100644 --- a/nfq/nfqws.h +++ b/nfq/nfqws.h @@ -2,5 +2,6 @@ typedef enum { - pass = 0, modify, drop + // frag=drop but do not fix checksum + pass = 0, modify, drop, frag } packet_process_result; diff --git a/nfq/params.h b/nfq/params.h index 3c10520..433bc23 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -20,6 +20,9 @@ #define BADSEQ_INCREMENT_DEFAULT -10000 #define BADSEQ_ACK_INCREMENT_DEFAULT -66000 +#define IPFRAG_UDP_DEFAULT 8 +#define IPFRAG_TCP_DEFAULT 32 + struct params_s { bool debug; @@ -36,7 +39,7 @@ struct params_s char hostspell[4]; enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2; bool desync_retrans,desync_skip_nosni,desync_any_proto; - int desync_repeats,desync_split_pos; + unsigned int desync_repeats,desync_split_pos,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp; char desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence unsigned int desync_cutoff; uint8_t desync_ttl, desync_ttl6;