nfqws: n|d|s cutoff modes

This commit is contained in:
bol-van
2021-12-27 16:51:30 +03:00
parent 2a3b9cf982
commit aece656a8d
7 changed files with 121 additions and 51 deletions

View File

@@ -150,18 +150,22 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
scale = tcp_find_scale_factor(tcphdr);
if (bReverse)
{
t->seq_last = htonl(tcphdr->th_ack);
t->ack_last = htonl(tcphdr->th_seq) + len_payload;
t->pos_orig = t->seq_last = htonl(tcphdr->th_ack);
t->ack_last = htonl(tcphdr->th_seq);
t->pos_reply = t->ack_last + len_payload;
t->pcounter_reply++;
t->pdcounter_reply+=!!len_payload;
t->winsize_reply = htons(tcphdr->th_win);
if (scale!=SCALE_NONE) t->scale_reply = scale;
}
else
{
t->seq_last = htonl(tcphdr->th_seq) + len_payload;
t->ack_last = htonl(tcphdr->th_ack);
t->seq_last = htonl(tcphdr->th_seq);
t->pos_orig = t->seq_last + len_payload;
t->pos_reply = t->ack_last = htonl(tcphdr->th_ack);
t->pcounter_orig++;
t->pdcounter_orig+=!!len_payload;
t->winsize_orig = htons(tcphdr->th_win);
if (scale!=SCALE_NONE) t->scale_orig = scale;
}
@@ -276,12 +280,14 @@ void ConntrackPoolPurge(t_conntrack *p)
HASH_ITER(hh, p, t, tmp) { \
*sa1=0; inet_ntop(AF_INET##f, &t->conn.e1.adr, sa1, sizeof(sa1)); \
*sa2=0; inet_ntop(AF_INET##f, &t->conn.e2.adr, sa2, sizeof(sa2)); \
printf("[%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=%llu packets_reply=%llu seq0=%u rseq=%u ack0=%u rack=%u wsize_orig=%u:%d wsize_reply=%u:%d cutoff=%u wss_cutoff=%u d_cutoff=%u\n", \
printf("[%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=d%llu/n%llu packets_reply=d%llu/n%llu seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d cutoff=%u wss_cutoff=%u d_cutoff=%u\n", \
sa1, t->conn.e1.port, sa2, t->conn.e2.port, \
connstate_s[t->track.state], \
(unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last), \
(unsigned long long)t->track.pcounter_orig, (unsigned long long)t->track.pcounter_reply, \
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.ack0, t->track.ack_last - t->track.ack0, \
(unsigned long long)t->track.pdcounter_orig, (unsigned long long)t->track.pcounter_orig, \
(unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply,\
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.pos_orig - t->track.seq0, \
t->track.ack0, t->track.ack_last - t->track.ack0, t->track.pos_reply - t->track.ack0, \
t->track.winsize_orig, t->track.scale_orig==SCALE_NONE ? -1 : t->track.scale_orig, \
t->track.winsize_reply, t->track.scale_reply==SCALE_NONE ? -1 : t->track.scale_reply, \
t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff); \

View File

@@ -49,8 +49,10 @@ typedef struct
t_connstate state;
time_t t_start, t_last;
uint32_t seq0, ack0; // starting seq and ack
uint32_t seq_last, ack_last; // current seq and ack
uint32_t seq_last, ack_last; // last seen seq and ack
uint32_t pos_orig, pos_reply; // seq_last+payload, ack_last+payload
uint64_t pcounter_orig, pcounter_reply; // packet counter
uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload)
uint16_t winsize_orig, winsize_reply; // last seen window size
uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none

View File

@@ -106,14 +106,29 @@ static bool rawsend_rep(const struct sockaddr* dst,uint32_t fwmark,const void *d
}
static uint64_t cutoff_get_limit(t_ctrack *ctrack, char mode)
{
switch(mode)
{
case 'n': return ctrack->pcounter_orig;
case 'd': return ctrack->pdcounter_orig;
case 's': return ctrack->seq_last - ctrack->seq0;
default: return 0;
}
}
static bool cutoff_test(t_ctrack *ctrack, uint64_t cutoff, char mode)
{
return cutoff && cutoff_get_limit(ctrack, mode)>=cutoff;
}
static void maybe_cutoff(t_ctrack *ctrack)
{
if (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;
ctrack->b_cutoff |= (!params.wssize || ctrack->b_wssize_cutoff) && !params.desync_cutoff;
ctrack->b_wssize_cutoff |= cutoff_test(ctrack, params.wssize_cutoff, params.wssize_cutoff_mode);
ctrack->b_desync_cutoff |= cutoff_test(ctrack, params.desync_cutoff, params.desync_cutoff_mode);
// we do not need conntrack entry anymore if all cutoff conditions are either not defined or reached
ctrack->b_cutoff |= (!params.wssize || ctrack->b_wssize_cutoff) && (!params.desync_cutoff || ctrack->b_desync_cutoff);
}
}
static void wssize_cutoff(t_ctrack *ctrack)
@@ -146,6 +161,9 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
if (ConntrackPoolFeed(&params.conntrack, ip, ip6hdr, tcphdr, len_payload, &ctrack, &bReverse))
maybe_cutoff(ctrack);
}
//ConntrackPoolDump(&params.conntrack);
if (params.wsize && tcp_synack_segment(tcphdr))
{
tcp_rewrite_winsize(tcphdr, params.wsize, params.wscale);
@@ -154,10 +172,25 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
if (bReverse) return res; // nothing to do. do not waste cpu
if (params.wssize && (ctrack && !ctrack->b_wssize_cutoff))
if (params.wssize)
{
tcp_rewrite_winsize(tcphdr, params.wssize, params.wsscale);
res=modify;
if (ctrack)
{
if (ctrack->b_wssize_cutoff)
{
DLOG("not changing wssize. wssize-cutoff reached\n");
}
else
{
if (params.wssize_cutoff) DLOG("wssize-cutoff not reached (mode %c): %llu/%u\n", params.wssize_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,params.wssize_cutoff_mode), params.wssize_cutoff);
tcp_rewrite_winsize(tcphdr, params.wssize, params.wsscale);
res=modify;
}
}
else
{
DLOG("not changing wssize. wssize is set but conntrack entry is missing\n");
}
}
if (params.desync_mode0!=DESYNC_NONE || params.desync_mode!=DESYNC_NONE) // save some cpu
@@ -186,6 +219,24 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
return res;
}
if (params.desync_cutoff)
{
if (ctrack)
{
if (ctrack->b_desync_cutoff)
{
DLOG("not desyncing. desync-cutoff reached (mode %c): %llu/%u\n", params.desync_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,params.desync_cutoff_mode), params.desync_cutoff);
return res;
}
DLOG("desync-cutoff not reached (mode %c): %llu/%u\n", params.desync_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,params.desync_cutoff_mode), params.desync_cutoff);
}
else
{
DLOG("not desyncing. desync-cutoff is set but conntrack entry is missing\n");
return res;
}
}
if (!params.wssize && params.desync_mode==DESYNC_NONE && !params.hostcase && !params.hostnospace && !params.domcase) return res; // nothing to do. do not waste cpu
if (!(tcphdr->th_flags & TH_SYN) && len_payload)
@@ -200,6 +251,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
if ((bIsHttp = IsHttp(data_payload,len_payload)))
{
DLOG("packet contains HTTP request\n")
if (params.wssize) DLOG("forced wssize-cutoff\n");
wssize_cutoff(ctrack);
fake = params.fake_http;
fake_size = params.fake_http_size;
@@ -213,6 +265,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
else if (IsTLSClientHello(data_payload,len_payload))
{
DLOG("packet contains TLS ClientHello\n")
if (params.wssize) DLOG("forced wssize-cutoff\n");
wssize_cutoff(ctrack);
fake = params.fake_tls;
fake_size = params.fake_tls_size;
@@ -280,16 +333,6 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
}
if (params.desync_mode==DESYNC_NONE) return res;
if (ctrack && ctrack->b_desync_cutoff)
{
DLOG("not desyncing. cutoff reached : %llu/%u\n", (unsigned long long)ctrack->pcounter_orig, params.desync_cutoff);
return res;
}
if (!ctrack && params.desync_cutoff)
{
DLOG("not desyncing. desync_cutoff is set but conntrack entry is missing\n");
return res;
}
if (params.debug)
{

View File

@@ -473,7 +473,7 @@ static void exithelp()
" --uid=uid[:gid]\t\t\t; drop root privs\n"
" --wsize=<window_size>[:<scale_factor>]\t; set window size. 0 = do not modify. OBSOLETE !\n"
" --wssize=<window_size>[:<scale_factor>]; set window size for server. 0 = do not modify. default scale_factor = 0.\n"
" --wssize-cutoff=N\t\t\t; apply server wsize only to packet numbers less than N\n"
" --wssize-cutoff=[n|d|s]N\t\t; apply server wsize only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n"
" --ctrack-timeouts=S:E:F\t\t; internal conntrack timeouts for SYN, ESTABLISHED and FIN stage. default %u:%u:%u\n"
" --hostcase\t\t\t\t; change Host: => host:\n"
" --hostspell\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
@@ -500,7 +500,7 @@ static void exithelp()
" --dpi-desync-fake-http=<filename>\t; file containing fake http request\n"
" --dpi-desync-fake-tls=<filename>\t; file containing fake TLS ClientHello (for https)\n"
" --dpi-desync-fake-unknown=<filename>\t; file containing unknown protocol fake payload\n"
" --dpi-desync-cutoff=N\t\t\t; apply dpi desync only to packet numbers less than N\n"
" --dpi-desync-cutoff=[n|d|s]N\t\t; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n"
" --hostlist=<filename>\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply)\n",
CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN,
#if defined(__linux__) || defined(SO_USER_COOKIE)
@@ -532,6 +532,23 @@ static void exit_clean(int code)
exit(code);
}
static bool parse_cutoff(const char *opt, unsigned int *value, char *mode)
{
*mode = (*opt=='n' || *opt=='d' || *opt=='s') ? *opt++ : 'n';
return sscanf(opt, "%u", value)>0;
}
static bool parse_badseq_increment(const char *opt, uint32_t *value)
{
if ((opt[0]=='0' && opt[1]=='x' || opt[0]=='-' && opt[1]=='0' && opt[2]=='x') && sscanf(opt+2+(opt[0]=='-'), "%X", (int32_t*)value)>0)
{
if (opt[0]=='-') params.desync_badseq_increment = -params.desync_badseq_increment;
return true;
}
else
{
return sscanf(opt, "%d", (int32_t*)value)>0;
}
}
int main(int argc, char **argv)
{
int result, v;
@@ -561,6 +578,7 @@ int main(int argc, char **argv)
params.desync_ttl6 = 0xFF; // unused
params.desync_badseq_increment = BADSEQ_INCREMENT_DEFAULT;
params.desync_badseq_ack_increment = BADSEQ_ACK_INCREMENT_DEFAULT;
params.wssize_cutoff_mode = params.desync_cutoff_mode = 'n'; // packet number by default
if (can_drop_root()) // are we root ?
{
@@ -666,7 +684,7 @@ int main(int argc, char **argv)
case 5: /* uid */
params.gid = 0x7FFFFFFF; // default gid. drop gid=0
params.droproot = true;
if (!sscanf(optarg, "%u:%u", &params.uid, &params.gid))
if (sscanf(optarg, "%u:%u", &params.uid, &params.gid)<1)
{
fprintf(stderr, "--uid should be : uid[:gid]\n");
exit_clean(1);
@@ -681,7 +699,7 @@ int main(int argc, char **argv)
exit_clean(1);
break;
case 8: /* wssize-cutoff */
if (!sscanf(optarg, "%u", &params.wssize_cutoff))
if (!parse_cutoff(optarg, &params.wssize_cutoff, &params.wssize_cutoff_mode))
{
fprintf(stderr, "invalid wssize-cutoff value\n");
exit_clean(1);
@@ -753,7 +771,7 @@ int main(int argc, char **argv)
case 15: /* dpi-desync-fwmark/dpi-desync-sockarg */
#if defined(__linux__) || defined(SO_USER_COOKIE)
params.desync_fwmark = 0;
if (!sscanf(optarg, "0x%X", &params.desync_fwmark)) sscanf(optarg, "%u", &params.desync_fwmark);
if (sscanf(optarg, "0x%X", &params.desync_fwmark)<=0) sscanf(optarg, "%u", &params.desync_fwmark);
if (!params.desync_fwmark)
{
fprintf(stderr, "fwmark/sockarg should be decimal or 0xHEX and should not be zero\n");
@@ -827,20 +845,15 @@ int main(int argc, char **argv)
}
break;
case 23: /* dpi-desync-badseq-increments */
if ((optarg[0]=='0' && optarg[1]=='x' || optarg[0]=='-' && optarg[1]=='0' && optarg[2]=='x') && sscanf(optarg+2+(optarg[0]=='-'), "%X", (int32_t*)&params.desync_badseq_increment))
{
if (optarg[0]=='-') params.desync_badseq_increment = -params.desync_badseq_increment;
} else if (!sscanf(optarg, "%d", (int32_t*)&params.desync_badseq_increment))
if (!parse_badseq_increment(optarg,&params.desync_badseq_increment))
{
fprintf(stderr, "dpi-desync-badseq-increment should be signed decimal or signed 0xHEX\n");
exit_clean(1);
}
printf("FFF %08X\n", params.desync_badseq_increment);
break;
case 24: /* dpi-desync-badack-increment */
if ((optarg[0]=='0' && optarg[1]=='x' || optarg[0]=='-' && optarg[1]=='0' && optarg[2]=='x') && sscanf(optarg+2+(optarg[0]=='-'), "%X", (int32_t*)&params.desync_badseq_ack_increment))
{
if (optarg[0]=='-') params.desync_badseq_ack_increment = -params.desync_badseq_ack_increment;
} else if (!sscanf(optarg, "%d", (int32_t*)&params.desync_badseq_ack_increment))
if (!parse_badseq_increment(optarg,&params.desync_badseq_ack_increment))
{
fprintf(stderr, "dpi-desync-badack-increment should be signed decimal or signed 0xHEX\n");
exit_clean(1);
@@ -874,7 +887,7 @@ int main(int argc, char **argv)
}
break;
case 29: /* desync-cutoff */
if (!sscanf(optarg, "%u", &params.desync_cutoff))
if (!parse_cutoff(optarg, &params.desync_cutoff, &params.desync_cutoff_mode))
{
fprintf(stderr, "invalid desync-cutoff value\n");
exit_clean(1);

View File

@@ -25,6 +25,7 @@ struct params_s
bool debug;
uint16_t wsize,wssize;
uint8_t wscale,wsscale;
char wssize_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int wssize_cutoff;
#ifdef __linux__
int qnum;
@@ -36,6 +37,7 @@ struct params_s
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;
char desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int desync_cutoff;
uint8_t desync_ttl, desync_ttl6;
uint8_t desync_tcp_fooling_mode;