openbsd: discovered how to apply divert-packet to outgoing only

This commit is contained in:
bol-van 2021-03-22 12:02:55 +03:00
parent 3a020be7d5
commit 483658485e
6 changed files with 33 additions and 47 deletions

View File

@ -170,7 +170,8 @@ dvtws for all traffic:
/etc/pf.conf
------------
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989
pass in quick on em0 proto tcp from port {80,443} no state
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989
------------
pfctl -f /etc/pf.conf
./dvtws --port=989 --dpi-desync=split2
@ -179,32 +180,34 @@ dwtws only for table zapret with the exception of table nozapret :
/etc/pf.conf
------------
set limit table-entries 2000000
table <zapret> file "/opt/zapret/ipset/zapret-ip.txt"
table <zapret-user> file "/opt/zapret/ipset/zapret-ip-user.txt"
table <nozapret> file "/opt/zapret/ipset/zapret-ip-exclude.txt"
pass out quick on em0 inet proto tcp to <nozapret> port {80,443}
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989
pass out quick on em0 inet proto tcp to <nozapret> port {80,443}
pass in quick on em0 inet proto tcp from <zapret> port {80,443} no state
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989 no state
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} no state
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989 no state
table <zapret6> file "/opt/zapret/ipset/zapret-ip6.txt"
table <zapret6-user> file "/opt/zapret/ipset/zapret-ip-user6.txt"
table <nozapret6> file "/opt/zapret/ipset/zapret-ip-exclude6.txt"
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443}
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443}
pass in quick on em0 inet6 proto tcp from <zapret6> port {80,443} no state
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989 no state
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} no state
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989 no state
------------
pfctl -f /etc/pf.conf
./dvtws --port=989 --dpi-desync=split2
divert-packet automatically adds the reverse rule. By default also incoming traffic will be passwed to dvtws.
This is highly undesired because it is waste of cpu resources and speed limiter.
The trick with "no state" and "in" rules allows to bypass auto reverse rule.
dvtws in OpenBSD sends all fakes through a divert socket because raw sockets have critical artificial limitations.
Looks like pf automatically prevent reinsertion of diverted frames. Loop problem does not exist.
Sadly PF auto applies return rule to divert-packet.
Not only outgoing packets go through dvtws but also incoming.
This adds great unneeded overhead that will be the most noticable on http/https downloads.
I could not figure out how to disable this feature.
Thats why you are encouraged to use table filters with your personal blocked site lists.
OpenBSD forcibly recomputes tcp checksum after divert. Thats why most likely
dpi-desync-fooling=badsum will not work. dvtws will warn if you specify this parameter.

View File

@ -188,7 +188,8 @@ dvtws для всего трафика :
/etc/pf.conf
------------
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989
pass in quick on em0 proto tcp from port {80,443} no state
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 no state
------------
pfctl -f /etc/pf.conf
./dvtws --port=989 --dpi-desync=split2
@ -201,29 +202,29 @@ set limit table-entries 2000000
table <zapret> file "/opt/zapret/ipset/zapret-ip.txt"
table <zapret-user> file "/opt/zapret/ipset/zapret-ip-user.txt"
table <nozapret> file "/opt/zapret/ipset/zapret-ip-exclude.txt"
pass out quick on em0 inet proto tcp to <nozapret> port {80,443}
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989
pass out quick on em0 inet proto tcp to <nozapret> port {80,443}
pass in quick on em0 inet proto tcp from <zapret> port {80,443} no state
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989 no state
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} no state
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989 no state
table <zapret6> file "/opt/zapret/ipset/zapret-ip6.txt"
table <zapret6-user> file "/opt/zapret/ipset/zapret-ip-user6.txt"
table <nozapret6> file "/opt/zapret/ipset/zapret-ip-exclude6.txt"
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443}
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443}
pass in quick on em0 inet6 proto tcp from <zapret6> port {80,443} no state
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989 no state
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} no state
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989 no state
------------
pfctl -f /etc/pf.conf
./dvtws --port=989 --dpi-desync=split2
divert-packet автоматически вносит обратное правило для перенаправления.
Трюк с no state и in правилом позволяет обойти эту проблему, чтобы напрасно не гнать массивный трафик через dvtws.
В OpenBSD dvtws все фейки отсылает через divert socket, поскольку эта возможность через raw sockets заблокирована.
Видимо pf автоматически предотвращает повторный заворот diverted фреймов, поэтому проблемы зацикливания нет.
К сожалению, в PF присутствует "удобная" функция, которая автоматически применяет к правилу divert-packet
обратный трафик. Через divert пойдет все соединение, а не только исходящие пакеты.
Это добавит огромный ненужный overhead по процессингу входящих пакетов в dvtws, который будет наиболее заметен
на скачивании по http/https. Мне не удалось понять как этого избежать.
Поэтому использование фильтр-таблиц крайне рекомендовано !
OpenBSD принудительно пересчитывает tcp checksum после divert, поэтому скорее всего
dpi-desync-fooling=badsum у вас не заработает. При использовании этого параметра
dvtws предупредит о возможной проблеме.

View File

@ -55,6 +55,8 @@ typedef struct
uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none
bool b_cutoff; // mark for deletion
bool b_wssize_cutoff, b_desync_cutoff;
} t_ctrack;
// use separate pools for ipv4 and ipv6 to save RAM. otherwise could use union key

View File

@ -107,11 +107,7 @@ static void maybe_cutoff(t_ctrack *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)
@ -122,11 +118,7 @@ static void wssize_cutoff(t_ctrack *ctrack)
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
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)
{

View File

@ -38,13 +38,7 @@
#define CTRACK_T_SYN 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;

View File

@ -11,16 +11,9 @@
#include <stdbool.h>
#include <stdio.h>
#if defined(__OpenBSD__) || defined (__APPLE__)
// divert-packet also diverts return traffic. sockets will experience high load
#define Q_RCVBUF (256*1024) // in bytes
#define Q_SNDBUF (256*1024) // in bytes
#define RAW_SNDBUF (64*1024) // in bytes
#else
#define Q_RCVBUF (128*1024) // in bytes
#define Q_SNDBUF (64*1024) // in bytes
#define RAW_SNDBUF (64*1024) // in bytes
#endif
#define Q_MAXLEN 1024 // in packets
@ -40,6 +33,7 @@ struct params_s
enum dpi_desync_mode desync_mode,desync_mode2;
bool desync_retrans,desync_skip_nosni,desync_any_proto;
int desync_repeats,desync_split_pos;
unsigned int desync_cutoff;
uint8_t desync_ttl;
uint8_t desync_tcp_fooling_mode;
uint32_t desync_fwmark; // unused in BSD