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-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=<int|0xHEX> ; badseq fooling seq signed increment. default -10000
--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
@ -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.

View File

@ -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,

View File

@ -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;

View File

@ -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);

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))
{
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=<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-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=<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-any-protocol=0|1\t\t; 0(default)=desync only http and tls 1=desync any nonempty data packet\n"