nfqws: ipv6 fragment at transport header

This commit is contained in:
bol-van 2022-01-05 15:34:57 +03:00
parent 85517a3851
commit 6b39411454
13 changed files with 42 additions and 32 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

@ -148,8 +148,8 @@ nfqws takes the following parameters:
--dpi-desync-repeats=<N> ; send every desync packet N times --dpi-desync-repeats=<N> ; 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-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-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-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 second header (usually transport header). multiple of 8, default 32. --dpi-desync-ipfrag-pos-udp=<8..9216> ; ip frag position starting from the transport header. multiple of 8, default 32.
--dpi-desync-badseq-increment=<int|0xHEX> ; badseq fooling seq signed increment. default -10000 --dpi-desync-badseq-increment=<int|0xHEX> ; badseq fooling seq signed increment. default -10000
--dpi-desync-badack-increment=<int|0xHEX> ; badseq fooling ackseq signed increment. default -66000 --dpi-desync-badack-increment=<int|0xHEX> ; 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 --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. 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. 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. 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. 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 in conntrack. ipv6 : There's no way for an application to reliably send fragments without defragmentation by conntrack.
Sometimes it works, sometimes system defragments packets. 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 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. and its dependency nf_defrag_ipv6 helps but this severely impacts functionality.

View File

@ -1,4 +1,4 @@
zapret v.44 zapret v.44
English English
------- -------
@ -427,12 +427,12 @@ window size итоговый размер окна стал максимальн
IP ФРАГМЕНТАЦИЯ IP ФРАГМЕНТАЦИЯ
В современной сети с этом все очень плохо. Фрагментированные пакеты застревают по пути, часто отбрасываются. В современной сети с этом все очень плохо. Фрагментированные пакеты застревают по пути, часто отбрасываются.
Иногда доходят. Иногда то доходят, то не доходят. Может зависеть от версии ipv4/ipv6. Иногда доходят. Иногда то доходят, то не доходят. Может зависеть от версии ipv4/ipv6.
Роутеры на базе linux и freebsd могут самопроизвольно собирать или перефрагментировать пакеты. Роутеры на базе linux могут самопроизвольно собирать или перефрагментировать пакеты.
Позиция фрагментации задается отдельно для tcp и udp. По умолчанию 24 и 8 соответственно, должна быть кратна 8. Позиция фрагментации задается отдельно для tcp и udp. По умолчанию 24 и 8 соответственно, должна быть кратна 8.
Смещение считается с заголовка, следующего за ip. В большинство случаев это транспортный заголовок. Смещение считается с транспортного заголовка.
Существует ряд моментов вокруг работы с фрагментами на Linux, без понимания которых может ничего не получиться. Существует ряд моментов вокруг работы с фрагментами на Linux, без понимания которых может ничего не получиться.
ipv4 : Linux дает отсылать ipv4 фрагменты, но стандартные настройки iptables в цепочке OUTPUT могут их дропать. ipv4 : Linux дает отсылать ipv4 фрагменты, но стандартные настройки iptables в цепочке OUTPUT могут вызывать ошибки отправки.
ipv6 : Нет способа для приложения гарантированно отослать фрагменты без дефрагментации в conntrack. ipv6 : Нет способа для приложения гарантированно отослать фрагменты без дефрагментации в conntrack.
На разных системах получается по-разному. Где-то нормально уходят, где-то пакеты дефрагментируются. На разных системах получается по-разному. Где-то нормально уходят, где-то пакеты дефрагментируются.
Для ядер <4.16 похоже, что нет иного способа решить эту проблему, кроме как выгрузить модуль nf_conntrack, Для ядер <4.16 похоже, что нет иного способа решить эту проблему, кроме как выгрузить модуль nf_conntrack,

View File

@ -346,42 +346,50 @@ bool ip_frag6(
uint8_t *pkt1, size_t *pkt1_size, uint8_t *pkt1, size_t *pkt1_size,
uint8_t *pkt2, size_t *pkt2_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; uint8_t proto;
struct ip6_frag *frag; struct ip6_frag *frag;
const uint8_t *payload;
if (frag_pos & 7 || pkt_size < sizeof(struct ip6_hdr)) return false; 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); payload_len = sizeof(struct ip6_hdr) + 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 || if (pkt_size < payload_len) return false;
*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 = 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; return false;
} }
proto = ((struct ip6_hdr*)pkt)->ip6_ctlun.ip6_un1.ip6_un1_nxt; 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);
memcpy(pkt1, pkt, sizeof(struct ip6_hdr)); pkt1[last_header_type - pkt] = IPPROTO_FRAGMENT;
((struct ip6_hdr*)pkt1)->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(sizeof(struct ip6_frag)+frag_pos); frag = (struct ip6_frag*)(pkt1 + unfragmentable);
((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_nxt = proto;
frag->ip6f_reserved = 0; frag->ip6f_reserved = 0;
frag->ip6f_offlg = IP6F_MORE_FRAG; frag->ip6f_offlg = IP6F_MORE_FRAG;
frag->ip6f_ident = ident; frag->ip6f_ident = ident;
memcpy(frag+1, pkt+sizeof(struct ip6_hdr), frag_pos); memcpy(frag+1, pkt + unfragmentable, frag_pos);
*pkt1_size = sizeof(struct ip6_hdr)+sizeof(struct ip6_frag)+frag_pos; *pkt1_size = unfragmentable + sizeof(struct ip6_frag) + frag_pos;
memcpy(pkt2, pkt, sizeof(struct ip6_hdr)); 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_plen = htons(unfragmentable - sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + payload_len - frag_pos);
((struct ip6_hdr*)pkt2)->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_FRAGMENT; pkt2[last_header_type - pkt] = IPPROTO_FRAGMENT;
frag = (struct ip6_frag*)(((struct ip6_hdr*)pkt2)+1); frag = (struct ip6_frag*)(pkt2 + unfragmentable);
frag->ip6f_nxt = proto; frag->ip6f_nxt = proto;
frag->ip6f_reserved = 0; frag->ip6f_reserved = 0;
frag->ip6f_offlg = htons(frag_pos); frag->ip6f_offlg = htons(frag_pos);
frag->ip6f_ident = ident; frag->ip6f_ident = ident;
memcpy(frag+1, pkt+sizeof(struct ip6_hdr)+frag_pos, payload_len-frag_pos); memcpy(frag+1, pkt + unfragmentable + frag_pos, payload_len - frag_pos);
*pkt2_size = sizeof(struct ip6_hdr)+sizeof(struct ip6_frag)+payload_len-frag_pos; *pkt2_size = unfragmentable + sizeof(struct ip6_frag) + payload_len - frag_pos;
return true; return true;
} }
@ -619,7 +627,7 @@ bool proto_check_ipv6(const uint8_t *data, size_t len)
} }
// move to transport protocol // move to transport protocol
// proto_type = 0 => error // 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; size_t hdrlen;
uint8_t HeaderType; 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 if (proto_type) *proto_type = 0; // put error in advance
HeaderType = (*data)[6]; // NextHeader field HeaderType = (*data)[6]; // NextHeader field
if (last_header_type) *last_header_type = (*data)+6;
*data += 40; *len -= 40; // skip ipv6 base header *data += 40; *len -= 40; // skip ipv6 base header
while (*len > 0) // need at least one byte for NextHeader field 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 if (*len < hdrlen) return; // error
HeaderType = **data; HeaderType = **data;
if (last_header_type) *last_header_type = *data;
// advance to the next header location // advance to the next header location
*len -= hdrlen; *len -= hdrlen;
*data += hdrlen; *data += hdrlen;

View File

@ -125,7 +125,7 @@ void print_udphdr(const struct udphdr *udphdr);
bool proto_check_ipv4(const uint8_t *data, size_t len); bool proto_check_ipv4(const uint8_t *data, size_t len);
void proto_skip_ipv4(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); 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); bool proto_check_tcp(const uint8_t *data, size_t len);
void proto_skip_tcp(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); bool proto_check_udp(const uint8_t *data, size_t len);

View File

@ -115,7 +115,7 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt
else if (proto_check_ipv6(data, len)) else if (proto_check_ipv6(data, len))
{ {
ip6hdr = (struct ip6_hdr *) data; ip6hdr = (struct ip6_hdr *) data;
proto_skip_ipv6(&data, &len, &proto); proto_skip_ipv6(&data, &len, &proto, NULL);
if (params.debug) if (params.debug)
{ {
printf("IP6: "); printf("IP6: ");
@ -520,8 +520,8 @@ static void exithelp()
" --dpi-desync-repeats=<N>\t\t; send every desync packet N times\n" " --dpi-desync-repeats=<N>\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-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-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-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 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 transport header. multiple of 8, default %u.\n"
" --dpi-desync-badseq-increment=<int|0xHEX> ; badseq fooling seq signed increment. default %d\n" " --dpi-desync-badseq-increment=<int|0xHEX> ; badseq fooling seq signed increment. default %d\n"
" --dpi-desync-badack-increment=<int|0xHEX> ; badseq fooling ackseq signed increment. default %d\n" " --dpi-desync-badack-increment=<int|0xHEX> ; 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" " --dpi-desync-any-protocol=0|1\t\t; 0(default)=desync only http and tls 1=desync any nonempty data packet\n"