diff --git a/common/list.sh b/common/list.sh index b9d2f26..eed0851 100644 --- a/common/list.sh +++ b/common/list.sh @@ -42,10 +42,12 @@ filter_apply_hostlist_target() parm7="${AUTOHOSTLIST_RETRANS_THRESHOLD:+--hostlist-auto-retrans-threshold=$AUTOHOSTLIST_RETRANS_THRESHOLD}" parm8="${AUTOHOSTLIST_RETRANS_MAXSEQ:+--hostlist-auto-retrans-maxseq=$AUTOHOSTLIST_RETRANS_MAXSEQ}" parm9="${AUTOHOSTLIST_INCOMING_MAXSEQ:+--hostlist-auto-incoming-maxseq=$AUTOHOSTLIST_INCOMING_MAXSEQ}" - parm10="--hostlist=$HOSTLIST_AUTO" + parm10="${AUTOHOSTLIST_UDP_IN:+--hostlist-auto-udp-in=$AUTOHOSTLIST_UDP_IN}" + parm11="${AUTOHOSTLIST_UDP_OUT:+--hostlist-auto-udp-out=$AUTOHOSTLIST_UDP_OUT}" + parm12="--hostlist=$HOSTLIST_AUTO" } - parm="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm4:+ $parm4}${parm5:+ $parm5}${parm6:+ $parm6}${parm7:+ $parm7}${parm8:+ $parm8}${parm9:+ $parm9}" - parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm10:+ $parm10}" + parm="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm4:+ $parm4}${parm5:+ $parm5}${parm6:+ $parm6}${parm7:+ $parm7}${parm8:+ $parm8}${parm9:+ $parm9}${parm10:+ $parm10}${parm11:+ $parm11}" + parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm10:+ $parm12}" } v="$(replace_str $HOSTLIST_NOAUTO_MARKER "$parmNA" "$v")" v="$(replace_str $HOSTLIST_MARKER "$parm" "$v")" diff --git a/config.default b/config.default index 3140c31..8371dfa 100644 --- a/config.default +++ b/config.default @@ -31,6 +31,8 @@ AUTOHOSTLIST_RETRANS_MAXSEQ=65536 AUTOHOSTLIST_RETRANS_THRESHOLD=3 AUTOHOSTLIST_FAIL_THRESHOLD=3 AUTOHOSTLIST_FAIL_TIME=60 +AUTOHOSTLIST_UDP_IN=1 +AUTOHOSTLIST_UDP_OUT=4 # 1 = debug autohostlist positives to ipset/zapret-hosts-auto-debug.log AUTOHOSTLIST_DEBUGLOG=0 diff --git a/docs/changes.txt b/docs/changes.txt index 77d4f70..0ce9c75 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -89,3 +89,4 @@ v0.7 * nfqws2: autohostlist: trigger http redirect failure if payload is http_req without connection proto check * nfqws2: push desync.track.pos.dt as float with nsec accuracy * zapret-auto: override host autostate key in automate_host_record +* nfqws2: rewrite udp autohostlist failure detector logic diff --git a/docs/readme.md b/docs/readme.md index 5e9b980..65499c3 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,5 +1,3 @@ -# zapret2 v0.2 - ## Зачем это нужно Автономное средство противодействия DPI, которое не требует подключения каких-либо сторонних серверов. Может помочь diff --git a/lua/zapret-auto.lua b/lua/zapret-auto.lua index a41980f..065a527 100644 --- a/lua/zapret-auto.lua +++ b/lua/zapret-auto.lua @@ -89,7 +89,7 @@ end -- arg: retrans=N - tcp: retrans count threshold. default is 3 -- arg: rst= - tcp: maximum relative sequence number to treat incoming RST as DPI reset. default is 1 -- arg: no_http_redirect - tcp: disable http_reply dpi redirect trigger --- arg: udp_out - udp: >= outgoing udp packets. default is 3 +-- arg: udp_out - udp: >= outgoing udp packets. default is 4 -- arg: udp_in - udp: with <= incoming udp packets. default is 1 function standard_failure_detector(desync, crec, arg) if crec.nocheck then return false end @@ -98,7 +98,7 @@ function standard_failure_detector(desync, crec, arg) local retrans = tonumber(arg.retrans) or 3 local maxseq = tonumber(arg.seq) or 0x10000 local udp_in = tonumber(arg.udp_in) or 1 - local udp_out = tonumber(arg.udp_out) or 3 + local udp_out = tonumber(arg.udp_out) or 4 local trigger = false if desync.dis.tcp then diff --git a/nfq2/desync.c b/nfq2/desync.c index a61c9ab..2122cea 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -323,23 +323,50 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname } } } - +static void fill_client_ip_port(const struct sockaddr *client, char *client_ip_port, size_t client_ip_port_size) +{ + if (*params.hostlist_auto_debuglog) + ntop46_port((struct sockaddr*)client, client_ip_port, client_ip_port_size); + else + *client_ip_port = 0; +} static void process_retrans_fail(t_ctrack *ctrack, uint8_t proto, const struct sockaddr *client) { if (params.server) return; // no autohostlists in server mode char client_ip_port[48]; - if (*params.hostlist_auto_debuglog) - ntop46_port((struct sockaddr*)client, client_ip_port, sizeof(client_ip_port)); - else - *client_ip_port = 0; + fill_client_ip_port(client, client_ip_port, sizeof(client_ip_port)); if (ctrack && ctrack->dp && ctrack->hostname && auto_hostlist_retrans(ctrack, proto, ctrack->dp->hostlist_auto_retrans_threshold, client_ip_port, ctrack->l7proto)) { HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : retrans threshold reached", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(ctrack->dp), client_ip_port, l7proto_str(ctrack->l7proto)); auto_hostlist_failed(ctrack->dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, ctrack->l7proto); } } - +static void process_udp_fail(t_ctrack *ctrack, const t_ctrack_positions *tpos, const struct sockaddr *client) +{ + // no autohostlists in server mode + if (!params.server && ctrack && ctrack->dp && ctrack->hostname && ctrack->hostname_ah_check && + !ctrack->failure_detect_finalized && ctrack->dp->hostlist_auto_udp_out) + { + if (!tpos) tpos = &ctrack->pos; + //printf("UDP_POS %u %u\n",tpos->client.pcounter, tpos->server.pcounter); + if (tpos->server.pcounter > ctrack->dp->hostlist_auto_udp_in) + // success + ctrack->failure_detect_finalized = true; + else if (tpos->client.pcounter >= ctrack->dp->hostlist_auto_udp_out) + { + // failure + char client_ip_port[48]; + ctrack->failure_detect_finalized = true; + fill_client_ip_port(client, client_ip_port, sizeof(client_ip_port)); + HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : udp_in %u<=%u udp_out %u>=%u", + ctrack->hostname, ctrack->dp->n, PROFILE_NAME(ctrack->dp), client_ip_port, l7proto_str(ctrack->l7proto), + tpos->server.pcounter, ctrack->dp->hostlist_auto_udp_in, + tpos->client.pcounter, ctrack->dp->hostlist_auto_udp_out); + auto_hostlist_failed(ctrack->dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, ctrack->l7proto); + } + } +} static bool send_delayed(t_ctrack *ctrack) { @@ -1101,10 +1128,7 @@ static uint8_t dpi_desync_tcp_packet_play( if (rseq) { char client_ip_port[48]; - if (*params.hostlist_auto_debuglog) - ntop46_port((struct sockaddr*)&dst, client_ip_port, sizeof(client_ip_port)); - else - *client_ip_port = 0; + fill_client_ip_port((struct sockaddr*)&dst, client_ip_port, sizeof(client_ip_port)); if (seq_within(ctrack->pos.server.seq_last, ctrack->pos.server.seq0 + 1, ctrack->pos.server.seq0 + dp->hostlist_auto_incoming_maxseq)) { bool bFail = false; @@ -1776,20 +1800,14 @@ static uint8_t dpi_desync_udp_packet_play( else { if (ctrack_replay) - { ctrack_replay->hostname_ah_check = dp->hostlist_auto && !bCheckExcluded; - if (ctrack_replay->hostname_ah_check) - { - // first request is not retrans - if (!bDiscoveredHostname && !reasm_offset) - process_retrans_fail(ctrack_replay, IPPROTO_UDP, (struct sockaddr*)&src); - } - } } } - } - } + + process_udp_fail(ctrack_replay, tpos, (struct sockaddr*)&src); + } // len_payload + if (bCheckDone && !bCheckResult) { DLOG("not applying tampering because of negative hostlist check\n"); diff --git a/nfq2/nfqws.c b/nfq2/nfqws.c index 0ff7527..be76572 100644 --- a/nfq2/nfqws.c +++ b/nfq2/nfqws.c @@ -1436,6 +1436,8 @@ static void exithelp(void) " --hostlist-auto-retrans-threshold=\t\t; how many request retransmissions cause attempt to fail (default : %d)\n" " --hostlist-auto-retrans-maxseq=\t\t\t; count retransmissions only within this relative sequence (default : %u)\n" " --hostlist-auto-incoming-maxseq=\t\t\t; treat tcp connection as successful if incoming relative sequence exceedes this threshold (default : %u)\n" + " --hostlist-auto-udp-out=\t\t\t\t; udp failure condition : sent at least `udp_out` packets (default : %u)\n" + " --hostlist-auto-udp-in=\t\t\t\t; udp failure condition : received not more than `udp_in` packets (default : %u)\n" " --hostlist-auto-debug=\t\t\t; debug auto hostlist positives (global parameter)\n" "\nLUA PACKET PASS MODE:\n" " --payload=type[,type]\t\t\t\t\t; set payload types following LUA functions should process : %s\n" @@ -1453,6 +1455,7 @@ static void exithelp(void) HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT, HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT, HOSTLIST_AUTO_RETRANS_MAXSEQ, HOSTLIST_AUTO_INCOMING_MAXSEQ, + HOSTLIST_AUTO_UDP_OUT, HOSTLIST_AUTO_UDP_IN, all_payloads ); exit(1); @@ -1551,6 +1554,8 @@ enum opt_indices { IDX_HOSTLIST_AUTO_RETRANS_THRESHOLD, IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ, IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ, + IDX_HOSTLIST_AUTO_UDP_IN, + IDX_HOSTLIST_AUTO_UDP_OUT, IDX_HOSTLIST_AUTO_DEBUG, IDX_NEW, IDX_SKIP, @@ -1637,6 +1642,8 @@ static const struct option long_options[] = { [IDX_HOSTLIST_AUTO_RETRANS_THRESHOLD] = {"hostlist-auto-retrans-threshold", required_argument, 0, 0}, [IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ] = {"hostlist-auto-retrans-maxseq", required_argument, 0, 0}, [IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ] = {"hostlist-auto-incoming-maxseq", required_argument, 0, 0}, + [IDX_HOSTLIST_AUTO_UDP_IN] = {"hostlist-auto-udp-in", required_argument, 0, 0}, + [IDX_HOSTLIST_AUTO_UDP_OUT] = {"hostlist-auto-udp-out", required_argument, 0, 0}, [IDX_HOSTLIST_AUTO_DEBUG] = {"hostlist-auto-debug", required_argument, 0, 0}, [IDX_NEW] = {"new", no_argument, 0, 0}, [IDX_SKIP] = {"skip", no_argument, 0, 0}, @@ -2091,6 +2098,12 @@ int main(int argc, char **argv) case IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ: dp->hostlist_auto_incoming_maxseq = (uint32_t)atoi(optarg); break; + case IDX_HOSTLIST_AUTO_UDP_OUT: + dp->hostlist_auto_udp_out = atoi(optarg); + break; + case IDX_HOSTLIST_AUTO_UDP_IN: + dp->hostlist_auto_udp_in = atoi(optarg); + break; case IDX_HOSTLIST_AUTO_DEBUG: { FILE *F = fopen(optarg, "a+t"); diff --git a/nfq2/params.c b/nfq2/params.c index d646059..5c1daa9 100644 --- a/nfq2/params.c +++ b/nfq2/params.c @@ -344,6 +344,8 @@ void dp_init(struct desync_profile *dp) dp->hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT; dp->hostlist_auto_retrans_maxseq = HOSTLIST_AUTO_RETRANS_MAXSEQ; dp->hostlist_auto_incoming_maxseq = HOSTLIST_AUTO_INCOMING_MAXSEQ; + dp->hostlist_auto_udp_out = HOSTLIST_AUTO_UDP_OUT; + dp->hostlist_auto_udp_in = HOSTLIST_AUTO_UDP_IN; dp->filter_ipv4 = dp->filter_ipv6 = true; } static void dp_clear_dynamic(struct desync_profile *dp) diff --git a/nfq2/params.h b/nfq2/params.h index 0fce471..0e158d8 100644 --- a/nfq2/params.h +++ b/nfq2/params.h @@ -32,6 +32,8 @@ #define HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT 3 #define HOSTLIST_AUTO_RETRANS_MAXSEQ 65536 #define HOSTLIST_AUTO_INCOMING_MAXSEQ 4096 +#define HOSTLIST_AUTO_UDP_OUT 4 +#define HOSTLIST_AUTO_UDP_IN 1 #define IPCACHE_LIFETIME 7200 @@ -80,6 +82,7 @@ struct desync_profile // pointer to autohostlist. NULL if no autohostlist for the profile. struct hostlist_file *hostlist_auto; int hostlist_auto_fail_threshold, hostlist_auto_fail_time, hostlist_auto_retrans_threshold; + int hostlist_auto_udp_in, hostlist_auto_udp_out; uint32_t hostlist_auto_retrans_maxseq, hostlist_auto_incoming_maxseq; hostfail_pool *hostlist_auto_fail_counters;