diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index 8d28353..7d5baba 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/arm/nfqws b/binaries/arm/nfqws index e82244a..eef0329 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 efea6be..391e652 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 cc0cb61..2edf9ac 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 2889415..45b25e4 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 8be2c97..a34272c 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index e468f3d..37e0821 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 3d334f6..1ae0068 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/docs/readme.eng.md b/docs/readme.eng.md index b294793..8d1002c 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -148,8 +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-ipfrag-pos-tcp=<8..9216> ; ip frag position starting from the transport header. multiple of 8, default 8. + --dpi-desync-ipfrag-pos-udp=<8..9216> ; ip frag position starting from the 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 @@ -397,11 +397,11 @@ By default fake payload is 64 zeroes. Can be overriden using `--dpi-desync-fake- Modern network is very hostile to IP fragmentation. Fragmented packets are often not delivered or refragmented/reassembled on the way. 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. +Offset starts from the transport header. There are important nuances when working with fragments in Linux. -ipv4 : Linux allows to send ipv4 fragments but standard firewall rules in OUTPUT chain can drop them. -ipv6 : There's no way for an application to reliably send fragments without defragmentation in conntrack. +ipv4 : Linux allows to send ipv4 fragments but standard firewall rules in OUTPUT chain can cause raw send to fail. +ipv6 : There's no way for an application to reliably send fragments without defragmentation by conntrack. Sometimes it works, sometimes system defragments packets. Looks like kernels <4.16 have no simple way to solve this problem. Unloading of nf_conntrack module and its dependency nf_defrag_ipv6 helps but this severely impacts functionality. diff --git a/docs/readme.txt b/docs/readme.txt index 331d825..ef5e232 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -1,4 +1,4 @@ -zapret v.44 +zapret v.44 English ------- @@ -427,12 +427,12 @@ window size итоговый размер окна стал максимальн IP ФРАГМЕНТАЦИЯ В современной сети с этом все очень плохо. Фрагментированные пакеты застревают по пути, часто отбрасываются. Иногда доходят. Иногда то доходят, то не доходят. Может зависеть от версии ipv4/ipv6. -Роутеры на базе linux и freebsd могут самопроизвольно собирать или перефрагментировать пакеты. +Роутеры на базе linux могут самопроизвольно собирать или перефрагментировать пакеты. Позиция фрагментации задается отдельно для tcp и udp. По умолчанию 24 и 8 соответственно, должна быть кратна 8. -Смещение считается с заголовка, следующего за ip. В большинство случаев это транспортный заголовок. +Смещение считается с транспортного заголовка. Существует ряд моментов вокруг работы с фрагментами на Linux, без понимания которых может ничего не получиться. -ipv4 : Linux дает отсылать ipv4 фрагменты, но стандартные настройки iptables в цепочке OUTPUT могут их дропать. +ipv4 : Linux дает отсылать ipv4 фрагменты, но стандартные настройки iptables в цепочке OUTPUT могут вызывать ошибки отправки. ipv6 : Нет способа для приложения гарантированно отослать фрагменты без дефрагментации в conntrack. На разных системах получается по-разному. Где-то нормально уходят, где-то пакеты дефрагментируются. Для ядер <4.16 похоже, что нет иного способа решить эту проблему, кроме как выгрузить модуль nf_conntrack, diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c index d66d555..01a0edf 100644 --- a/nfq/darkmagic.c +++ b/nfq/darkmagic.c @@ -346,42 +346,50 @@ bool ip_frag6( uint8_t *pkt1, size_t *pkt1_size, uint8_t *pkt2, size_t *pkt2_size) { - uint16_t payload_len; + size_t payload_len, unfragmentable; + uint8_t *last_header_type; uint8_t proto; struct ip6_frag *frag; + const uint8_t *payload; 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)) + payload_len = sizeof(struct ip6_hdr) + htons(((struct ip6_hdr*)pkt)->ip6_ctlun.ip6_un1.ip6_un1_plen); + if (pkt_size < payload_len) return false; + + payload = pkt; + proto_skip_ipv6((uint8_t**)&payload, &payload_len, &proto, &last_header_type); + unfragmentable = payload - pkt; + + //printf("pkt_size=%zu FRAG_POS=%zu payload_len=%zu unfragmentable=%zu dh=%zu\n",pkt_size,frag_pos,payload_len,unfragmentable,last_header_type - pkt); + + if (frag_pos>=payload_len || + *pkt1_size<(unfragmentable + sizeof(struct ip6_frag) + frag_pos) || + *pkt2_size<(unfragmentable + 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); + memcpy(pkt1, pkt, unfragmentable); + ((struct ip6_hdr*)pkt1)->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(unfragmentable - sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + frag_pos); + pkt1[last_header_type - pkt] = IPPROTO_FRAGMENT; + frag = (struct ip6_frag*)(pkt1 + unfragmentable); 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(frag+1, pkt + unfragmentable, frag_pos); + *pkt1_size = unfragmentable + 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); + ((struct ip6_hdr*)pkt2)->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(unfragmentable - sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + payload_len - frag_pos); + pkt2[last_header_type - pkt] = IPPROTO_FRAGMENT; + frag = (struct ip6_frag*)(pkt2 + unfragmentable); 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; + memcpy(frag+1, pkt + unfragmentable + frag_pos, payload_len - frag_pos); + *pkt2_size = unfragmentable + sizeof(struct ip6_frag) + payload_len - frag_pos; return true; } @@ -619,7 +627,7 @@ bool proto_check_ipv6(const uint8_t *data, size_t len) } // move to transport protocol // proto_type = 0 => error -void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type) +void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type, uint8_t **last_header_type) { size_t hdrlen; uint8_t HeaderType; @@ -627,6 +635,7 @@ void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type) if (proto_type) *proto_type = 0; // put error in advance HeaderType = (*data)[6]; // NextHeader field + if (last_header_type) *last_header_type = (*data)+6; *data += 40; *len -= 40; // skip ipv6 base header while (*len > 0) // need at least one byte for NextHeader field { @@ -654,6 +663,7 @@ void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type) } if (*len < hdrlen) return; // error HeaderType = **data; + if (last_header_type) *last_header_type = *data; // advance to the next header location *len -= hdrlen; *data += hdrlen; diff --git a/nfq/darkmagic.h b/nfq/darkmagic.h index ebf2666..bdbf62c 100644 --- a/nfq/darkmagic.h +++ b/nfq/darkmagic.h @@ -125,7 +125,7 @@ void print_udphdr(const struct udphdr *udphdr); bool proto_check_ipv4(const uint8_t *data, size_t len); void proto_skip_ipv4(uint8_t **data, size_t *len); bool proto_check_ipv6(const uint8_t *data, size_t len); -void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type); +void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type, uint8_t **last_header_type); bool proto_check_tcp(const uint8_t *data, size_t len); void proto_skip_tcp(uint8_t **data, size_t *len); bool proto_check_udp(const uint8_t *data, size_t len); diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 2039864..3fb30e5 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -115,7 +115,7 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt else if (proto_check_ipv6(data, len)) { ip6hdr = (struct ip6_hdr *) data; - proto_skip_ipv6(&data, &len, &proto); + proto_skip_ipv6(&data, &len, &proto, NULL); if (params.debug) { printf("IP6: "); @@ -520,8 +520,8 @@ static void exithelp() " --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; 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-ipfrag-pos-tcp=<8..%u>\t; ip frag position starting from the transport header. multiple of 8, default %u.\n" + " --dpi-desync-ipfrag-pos-udp=<8..%u>\t; ip frag position starting from the 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"