nfqws: user mode ipset support

This commit is contained in:
bol-van 2024-10-16 13:42:59 +03:00
parent 8b0a1ffd2b
commit 29c8aec111
13 changed files with 934 additions and 197 deletions

View File

@ -12,6 +12,29 @@ static void ut_oom_recover(void *elem)
oom = true;
}
const char *l7proto_str(t_l7proto l7)
{
switch(l7)
{
case HTTP: return "http";
case TLS: return "tls";
case QUIC: return "quic";
case WIREGUARD: return "wireguard";
case DHT: return "dht";
default: return "unknown";
}
}
bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7)
{
return (l7proto==UNKNOWN && (filter_l7 & L7_PROTO_UNKNOWN)) ||
(l7proto==HTTP && (filter_l7 & L7_PROTO_HTTP)) ||
(l7proto==TLS && (filter_l7 & L7_PROTO_TLS)) ||
(l7proto==QUIC && (filter_l7 & L7_PROTO_QUIC)) ||
(l7proto==WIREGUARD && (filter_l7 & L7_PROTO_WIREGUARD)) ||
(l7proto==DHT && (filter_l7 & L7_PROTO_DHT));
}
static const char *connstate_s[]={"SYN","ESTABLISHED","FIN"};
static void connswap(const t_conn *c, t_conn *c2)
@ -316,18 +339,6 @@ static void taddr2str(uint8_t l3proto, const t_addr *a, char *buf, size_t bufsiz
if (!inet_ntop(family_from_proto(l3proto), a, buf, bufsize) && bufsize) *buf=0;
}
static const char *ConntrackProtoName(t_l7proto proto)
{
switch(proto)
{
case HTTP: return "HTTP";
case TLS: return "TLS";
case QUIC: return "QUIC";
case WIREGUARD: return "WIREGUARD";
case DHT: return "DHT";
default: return "UNKNOWN";
}
}
void ConntrackPoolDump(const t_conntrack *p)
{
t_conntrack_pool *t, *tmp;
@ -354,7 +365,7 @@ void ConntrackPoolDump(const t_conntrack *p)
t->track.seq_last, t->track.pos_orig,
t->track.ack_last, t->track.pos_reply);
printf(" req_retrans=%u cutoff=%u wss_cutoff=%u d_cutoff=%u hostname=%s l7proto=%s\n",
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff, t->track.hostname, ConntrackProtoName(t->track.l7proto));
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff, t->track.hostname, l7proto_str(t->track.l7proto));
};
}

View File

@ -52,12 +52,23 @@ typedef struct {
// ESTABLISHED - any except SYN or SYN/ACK received
// FIN - FIN or RST received
typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate;
typedef enum {UNKNOWN=0, HTTP, TLS, QUIC, WIREGUARD, DHT} t_l7proto;
#define L7_PROTO_HTTP 0x00000001
#define L7_PROTO_TLS 0x00000002
#define L7_PROTO_QUIC 0x00000004
#define L7_PROTO_WIREGUARD 0x00000008
#define L7_PROTO_DHT 0x00000010
#define L7_PROTO_UNKNOWN 0x80000000
const char *l7proto_str(t_l7proto l7);
bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7);
typedef struct
{
bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache
struct desync_profile *dp; // desync profile cache
bool dp_search_complete;
bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache
// common state
time_t t_start, t_last;
@ -76,12 +87,13 @@ typedef struct
bool req_seq_present,req_seq_finalized,req_seq_abandoned;
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
uint8_t autottl;
uint8_t incoming_ttl, autottl;
bool b_cutoff; // mark for deletion
bool b_wssize_cutoff, b_desync_cutoff;
t_l7proto l7proto;
bool l7proto_discovered;
char *hostname;
bool hostname_ah_check; // should perform autohostlist checks

View File

@ -5,6 +5,7 @@
#include "params.h"
#include "helpers.h"
#include "hostlist.h"
#include "ipset.h"
#include "conntrack.h"
#include <string.h>
@ -145,20 +146,32 @@ enum dpi_desync_mode desync_mode_from_string(const char *s)
return DESYNC_INVALID;
}
static bool dp_match_l3l4(struct desync_profile *dp, bool ipv6, uint16_t tcp_port, uint16_t udp_port)
static bool dp_match_l3l4(struct desync_profile *dp, uint8_t l3proto, const struct sockaddr *dest)
{
return \
((!ipv6 && dp->filter_ipv4) || (ipv6 && dp->filter_ipv6)) &&
(!tcp_port || pf_in_range(tcp_port,&dp->pf_tcp)) &&
(!udp_port || pf_in_range(udp_port,&dp->pf_udp));
return ((dest->sa_family==AF_INET && dp->filter_ipv4) || (dest->sa_family==AF_INET6 && dp->filter_ipv6)) &&
(l3proto==IPPROTO_TCP && pf_in_range(saport(dest), &dp->pf_tcp) || l3proto==IPPROTO_UDP && pf_in_range(saport(dest), &dp->pf_tcp)) &&
IpsetCheck(dp, dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL);
}
static bool dp_impossible(struct desync_profile *dp, const char *hostname, t_l7proto l7proto)
{
return !PROFILE_IPSETS_EMPTY(dp) &&
((dp->filter_l7 && !l7_proto_match(l7proto, dp->filter_l7)) || (!*dp->hostlist_auto_filename && !hostname && (dp->hostlist || dp->hostlist_exclude)));
}
static bool dp_match(
struct desync_profile *dp, bool ipv6, uint16_t tcp_port, uint16_t udp_port, const char *hostname,
struct desync_profile *dp,
uint8_t l3proto, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto,
bool *bCheckDone, bool *bCheckResult, bool *bExcluded)
{
if (bCheckDone) *bCheckDone = false;
if (dp_match_l3l4(dp,ipv6,tcp_port,udp_port))
// impossible case, hard filter
// impossible check avoids relatively slow ipset search
if (!dp_impossible(dp,hostname,l7proto) && dp_match_l3l4(dp,l3proto,dest))
{
// soft filter
if (dp->filter_l7 && !l7_proto_match(l7proto, dp->filter_l7))
return false;
// autohostlist profile matching l3/l4 filter always win
if (*dp->hostlist_auto_filename) return true;
@ -181,15 +194,21 @@ static bool dp_match(
return false;
}
static struct desync_profile *dp_find(
struct desync_profile_list_head *head, bool ipv6, uint16_t tcp_port, uint16_t udp_port, const char *hostname,
struct desync_profile_list_head *head,
uint8_t l3proto, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto,
bool *bCheckDone, bool *bCheckResult, bool *bExcluded)
{
struct desync_profile_list *dpl;
DLOG("desync profile search for hostname='%s' ipv6=%u tcp_port=%u udp_port=%u\n", hostname ? hostname : "", ipv6, tcp_port, udp_port);
if (params.debug)
{
char ip_port[48];
ntop46_port(dest, ip_port,sizeof(ip_port));
DLOG("desync profile search for %s target=%s l7proto=%s hostname='%s'\n", proto_name(l3proto), ip_port, l7proto_str(l7proto), hostname ? hostname : "");
}
if (bCheckDone) *bCheckDone = false;
LIST_FOREACH(dpl, head, next)
{
if (dp_match(&dpl->dp,ipv6,tcp_port,udp_port,hostname,bCheckDone,bCheckResult,bExcluded))
if (dp_match(&dpl->dp,l3proto,dest,hostname,l7proto,bCheckDone,bCheckResult,bExcluded))
{
DLOG("desync profile %d matches\n",dpl->dp.n);
return &dpl->dp;
@ -326,7 +345,7 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
fail_counter = HostFailPoolAdd(&dp->hostlist_auto_fail_counters, hostname, dp->hostlist_auto_fail_time);
if (!fail_counter)
{
fprintf(stderr, "HostFailPoolAdd: out of memory\n");
DLOG_ERR("HostFailPoolAdd: out of memory\n");
return;
}
}
@ -346,7 +365,7 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : adding to %s", hostname, dp->n, dp->hostlist_auto_filename);
if (!StrPoolAddStr(&dp->hostlist, hostname))
{
fprintf(stderr, "StrPoolAddStr out of memory\n");
DLOG_ERR("StrPoolAddStr out of memory\n");
return;
}
if (!append_to_list_file(dp->hostlist_auto_filename, hostname))
@ -575,6 +594,21 @@ static size_t pos_normalize(size_t split_pos, size_t reasm_offset, size_t len_pa
return split_pos;
}
static void autottl_discover(t_ctrack *ctrack, bool bIpv6)
{
if (ctrack && ctrack->incoming_ttl)
{
autottl *attl = bIpv6 ? &ctrack->dp->desync_autottl6 : &ctrack->dp->desync_autottl;
if (AUTOTTL_ENABLED(*attl))
{
ctrack->autottl = autottl_guess(ctrack->incoming_ttl, attl);
if (ctrack->autottl)
DLOG("autottl: guessed %u\n",ctrack->autottl);
else
DLOG("autottl: could not guess\n");
}
}
}
static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t transport_len, uint8_t *data_payload, size_t len_payload)
{
@ -593,10 +627,10 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
size_t pkt1_len, pkt2_len;
uint8_t ttl_orig,ttl_fake,flags_orig,scale_factor;
uint32_t *timestamps;
t_l7proto l7proto = UNKNOWN;
ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim;
uint32_t desync_fwmark = fwmark | params.desync_fwmark;
extract_endpoints(ip, ip6hdr, tcphdr, NULL, &src, &dst);
if (replay)
{
@ -611,10 +645,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
DLOG("using cached desync profile %d\n",dp->n);
else if (!ctrack_replay->dp_search_complete)
{
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, !!ip6hdr, ntohs(bReverse ? tcphdr->th_sport : tcphdr->th_dport), 0, ctrack_replay->hostname, NULL, NULL, NULL);
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, IPPROTO_TCP, (struct sockaddr *)&dst, ctrack_replay->hostname, ctrack_replay->l7proto, NULL, NULL, NULL);
ctrack_replay->dp_search_complete = true;
}
if (!dp)
{
DLOG("matching desync profile not found\n");
@ -635,7 +668,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
DLOG("using cached desync profile %d\n",dp->n);
else if (!ctrack || !ctrack->dp_search_complete)
{
dp = dp_find(&params.desync_profiles, !!ip6hdr, ntohs(bReverse ? tcphdr->th_sport : tcphdr->th_dport), 0, ctrack ? ctrack->hostname : NULL, NULL, NULL, NULL);
dp = dp_find(&params.desync_profiles, IPPROTO_TCP, (struct sockaddr *)&dst, ctrack ? ctrack->hostname : NULL, ctrack ? ctrack->l7proto : UNKNOWN, NULL, NULL, NULL);
if (ctrack)
{
ctrack->dp = dp;
@ -661,17 +694,14 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
if (bReverse)
{
if (ctrack && !ctrack->autottl && ctrack->pcounter_reply==1)
if (ctrack)
{
autottl *attl = ip ? &dp->desync_autottl : &dp->desync_autottl6;
if (AUTOTTL_ENABLED(*attl))
if (!ctrack->incoming_ttl)
{
ctrack->autottl = autottl_guess(ttl_orig, attl);
if (ctrack->autottl)
DLOG("autottl: guessed %u\n",ctrack->autottl);
else
DLOG("autottl: could not guess\n");
DLOG("incoming TTL %u\n",ttl_orig);
ctrack->incoming_ttl = ttl_orig;
}
if (!ctrack->autottl) autottl_discover(ctrack,!!ip6hdr);
}
// process reply packets for auto hostlist mode
@ -744,10 +774,12 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
flags_orig = *((uint8_t*)tcphdr+13);
scale_factor = tcp_find_scale_factor(tcphdr);
timestamps = tcp_find_timestamps(tcphdr);
extract_endpoints(ip, ip6hdr, tcphdr, NULL, &src, &dst);
if (!replay)
{
// start and cutoff limiters
if (!process_desync_interval(dp, ctrack)) return verdict;
if (tcp_syn_segment(tcphdr))
{
switch (dp->desync_mode0)
@ -797,8 +829,6 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
return verdict;
}
// start and cutoff limiters
if (!process_desync_interval(dp, ctrack)) return verdict;
} // !replay
if (!(tcphdr->th_flags & TH_SYN) && len_payload)
@ -811,6 +841,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
const uint8_t *rdata_payload = data_payload;
size_t rlen_payload = len_payload;
size_t split_pos;
t_l7proto l7proto = UNKNOWN;
if (replay)
{
@ -829,7 +860,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
{
DLOG("packet contains HTTP request\n");
l7proto = HTTP;
if (ctrack && !ctrack->l7proto) ctrack->l7proto = l7proto;
if (ctrack && ctrack->l7proto==UNKNOWN) ctrack->l7proto = l7proto;
// we do not reassemble http
reasm_orig_cancel(ctrack);
@ -874,7 +905,6 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
reasm_orig_cancel(ctrack);
return verdict;
}
}
if (!ctrack->req_seq_finalized)
{
@ -901,7 +931,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
}
else
{
fprintf(stderr, "rawpacket_queue failed !'\n");
DLOG_ERR("rawpacket_queue failed !\n");
reasm_orig_cancel(ctrack);
return verdict;
}
@ -929,70 +959,97 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
if (dseq>=0x1000000 && !(dseq & 0x80000000)) ctrack->req_seq_abandoned=true;
}
if (bHaveHost)
if (bHaveHost) DLOG("hostname: %s\n",host);
bool bDiscoveredL7;
if (ctrack_replay)
{
bool bCheckDone=false, bCheckResult=false, bCheckExcluded=false;
DLOG("hostname: %s\n",host);
bDiscoveredL7 = !ctrack_replay->l7proto_discovered && ctrack_replay->l7proto!=UNKNOWN;
ctrack_replay->l7proto_discovered=true;
}
else
bDiscoveredL7 = !ctrack_replay && l7proto!=UNKNOWN;
if (bDiscoveredL7) DLOG("discovered l7 protocol\n");
bool bDiscoveredHostname = bHaveHost && !(ctrack_replay && ctrack_replay->hostname);
if (bDiscoveredHostname)
{
DLOG("discovered hostname\n");
if (ctrack_replay)
{
ctrack_replay->hostname=strdup(host);
if (!ctrack_replay->hostname)
{
ctrack_replay->hostname=strdup(host);
if (!ctrack_replay->hostname)
{
DLOG_ERR("hostname dup : out of memory");
reasm_orig_cancel(ctrack);
return verdict;
}
DLOG("we have hostname now. searching desync profile again.\n");
struct desync_profile *dp_prev = dp;
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, !!ip6hdr, ntohs(bReverse ? tcphdr->th_sport : tcphdr->th_dport), 0, ctrack_replay->hostname, &ctrack_replay->bCheckDone, &ctrack_replay->bCheckResult, &ctrack_replay->bCheckExcluded);
ctrack_replay->dp_search_complete = true;
if (!dp)
{
reasm_orig_cancel(ctrack);
return verdict;
}
if (dp!=dp_prev)
{
DLOG("desync profile changed by revealed hostname !\n");
// re-evaluate start/cutoff limiters
if (!replay)
{
maybe_cutoff(ctrack, IPPROTO_TCP);
if (!process_desync_interval(dp, ctrack))
{
reasm_orig_cancel(ctrack);
return verdict;
}
}
}
}
bCheckDone = ctrack_replay->bCheckDone;
bCheckResult = ctrack_replay->bCheckResult;
bCheckExcluded = ctrack_replay->bCheckExcluded;
}
if (dp->hostlist || dp->hostlist_exclude)
{
if (!bCheckDone)
bCheckResult = HostlistCheck(dp, host, &bCheckExcluded);
if (bCheckResult)
ctrack_stop_retrans_counter(ctrack_replay);
else
{
if (ctrack_replay)
{
ctrack_replay->hostname_ah_check = *dp->hostlist_auto_filename && !bCheckExcluded;
if (!ctrack_replay->hostname_ah_check)
ctrack_stop_retrans_counter(ctrack_replay);
}
DLOG("not applying tampering to this request\n");
DLOG_ERR("hostname dup : out of memory");
reasm_orig_cancel(ctrack);
return verdict;
}
}
}
bool bCheckDone=false, bCheckResult=false, bCheckExcluded=false;
if (bDiscoveredL7 || bDiscoveredHostname)
{
struct desync_profile *dp_prev = dp;
dp = dp_find(&params.desync_profiles, IPPROTO_TCP, (struct sockaddr *)&dst, ctrack_replay ? ctrack_replay->hostname : host, ctrack_replay ? ctrack_replay->l7proto : l7proto, &bCheckDone, &bCheckResult, &bCheckExcluded);
if (ctrack_replay)
{
ctrack_replay->dp = dp;
ctrack_replay->dp_search_complete = true;
ctrack_replay->bCheckDone = bCheckDone;
ctrack_replay->bCheckResult = bCheckResult;
ctrack_replay->bCheckExcluded = bCheckExcluded;
}
if (!dp)
{
reasm_orig_cancel(ctrack);
return verdict;
}
if (dp!=dp_prev)
{
DLOG("desync profile changed by revealed l7 protocol or hostname !\n");
// rediscover autottl
autottl_discover(ctrack_replay,!!ip6hdr);
// re-evaluate start/cutoff limiters
if (!replay)
{
maybe_cutoff(ctrack, IPPROTO_TCP);
if (!process_desync_interval(dp, ctrack))
{
reasm_orig_cancel(ctrack);
return verdict;
}
}
}
}
else if (ctrack_replay)
{
bCheckDone = ctrack_replay->bCheckDone;
bCheckResult = ctrack_replay->bCheckResult;
bCheckExcluded = ctrack_replay->bCheckExcluded;
}
if (bHaveHost && (dp->hostlist || dp->hostlist_exclude))
{
if (!bCheckDone)
bCheckResult = HostlistCheck(dp, host, &bCheckExcluded);
if (bCheckResult)
ctrack_stop_retrans_counter(ctrack_replay);
else
{
if (ctrack_replay)
{
ctrack_replay->hostname_ah_check = *dp->hostlist_auto_filename && !bCheckExcluded;
if (!ctrack_replay->hostname_ah_check)
ctrack_stop_retrans_counter(ctrack_replay);
}
DLOG("not applying tampering to this request\n");
reasm_orig_cancel(ctrack);
return verdict;
}
}
// desync profile may have changed after hostname was revealed
switch(l7proto)
{
@ -1019,24 +1076,28 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
if (l7proto==UNKNOWN)
{
if (!dp->desync_any_proto) return verdict;
if (!dp->desync_any_proto)
{
DLOG("not applying tampering to unknown protocol\n");
return verdict;
}
DLOG("applying tampering to unknown protocol\n");
}
ttl_fake = (ctrack_replay && ctrack_replay->autottl) ? ctrack_replay->autottl : (ip6hdr ? (dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig) : (dp->desync_ttl ? dp->desync_ttl : ttl_orig));
if ((l7proto == HTTP) && (dp->hostcase || dp->hostnospace || dp->domcase) && (phost = (uint8_t*)memmem(data_payload, len_payload, "\r\nHost: ", 8)))
if ((l7proto == HTTP) && (dp->hostcase || dp->hostnospace || dp->domcase) && HttpFindHost(&phost,data_payload,len_payload))
{
if (dp->hostcase)
{
DLOG("modifying Host: => %c%c%c%c:\n", dp->hostspell[0], dp->hostspell[1], dp->hostspell[2], dp->hostspell[3]);
memcpy(phost + 2, dp->hostspell, 4);
memcpy(phost, dp->hostspell, 4);
verdict=VERDICT_MODIFY;
}
if (dp->domcase)
{
DLOG("mixing domain case\n");
for (p = phost+7; p < (data_payload + len_payload) && *p != '\r' && *p != '\n'; p++)
for (p = phost+5; p < (data_payload + len_payload) && *p != '\r' && *p != '\n'; p++)
*p = (((size_t)p) & 1) ? tolower(*p) : toupper(*p);
verdict=VERDICT_MODIFY;
}
@ -1048,12 +1109,12 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
DLOG("removing space after Host: and adding it to User-Agent:\n");
if (pua > phost)
{
memmove(phost + 7, phost + 8, pua - phost - 8);
phost[pua - phost - 1] = ' ';
memmove(phost + 5, phost + 6, pua - phost - 6);
pua[-1]=' ';
}
else
{
memmove(pua + 1, pua, phost - pua + 7);
memmove(pua + 1, pua, phost - pua + 5);
*pua = ' ';
}
verdict=VERDICT_MODIFY;
@ -1404,6 +1465,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
uint8_t ttl_orig,ttl_fake;
t_l7proto l7proto = UNKNOWN;
ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim;
extract_endpoints(ip, ip6hdr, NULL, udphdr, &src, &dst);
if (replay)
{
// in replay mode conntrack_replay is not NULL and ctrack is NULL
@ -1417,7 +1481,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
DLOG("using cached desync profile %d\n",dp->n);
else if (!ctrack_replay->dp_search_complete)
{
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, !!ip6hdr, 0, ntohs(bReverse ? udphdr->uh_sport : udphdr->uh_dport), ctrack_replay->hostname, NULL, NULL, NULL);
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, IPPROTO_UDP, (struct sockaddr *)&dst, ctrack_replay->hostname, ctrack_replay->l7proto, NULL, NULL, NULL);
ctrack_replay->dp_search_complete = true;
}
if (!dp)
@ -1440,7 +1504,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
DLOG("using cached desync profile %d\n",dp->n);
else if (!ctrack || !ctrack->dp_search_complete)
{
dp = dp_find(&params.desync_profiles, !!ip6hdr, 0, ntohs(bReverse ? udphdr->uh_sport : udphdr->uh_dport), ctrack ? ctrack->hostname : NULL, NULL, NULL, NULL);
dp = dp_find(&params.desync_profiles, IPPROTO_UDP, (struct sockaddr *)&dst, ctrack ? ctrack->hostname : NULL, ctrack ? ctrack->l7proto : UNKNOWN, NULL, NULL, NULL);
if (ctrack)
{
ctrack->dp = dp;
@ -1458,16 +1522,22 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
//ConntrackPoolDump(&params.conntrack);
}
if (bReverse) return verdict; // nothing to do. do not waste cpu
if (bReverse && ctrack)
{
if (!ctrack->incoming_ttl)
{
DLOG("incoming TTL %u\n",ttl_orig);
ctrack->incoming_ttl = ttl_orig;
}
if (!ctrack->autottl) autottl_discover(ctrack,!!ip6hdr);
return verdict; // nothing to do. do not waste cpu
}
// start and cutoff limiters
if (!replay && !process_desync_interval(dp, ctrack)) return verdict;
uint32_t desync_fwmark = fwmark | params.desync_fwmark;
ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim;
ttl_fake = ip6hdr ? dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig : dp->desync_ttl ? dp->desync_ttl : ttl_orig;
extract_endpoints(ip, ip6hdr, NULL, udphdr, &src, &dst);
if (len_payload)
{
const uint8_t *fake;
@ -1480,7 +1550,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
{
DLOG("packet contains QUIC initial\n");
l7proto = QUIC;
if (ctrack && !ctrack->l7proto) ctrack->l7proto = l7proto;
if (ctrack && ctrack->l7proto==UNKNOWN) ctrack->l7proto = l7proto;
uint8_t clean[16384], *pclean;
size_t clean_len;
@ -1542,7 +1612,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
}
else
{
fprintf(stderr, "rawpacket_queue failed !'\n");
DLOG_ERR("rawpacket_queue failed !\n");
reasm_orig_cancel(ctrack);
return verdict;
}
@ -1593,80 +1663,116 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
{
DLOG("packet contains wireguard handshake initiation\n");
l7proto = WIREGUARD;
if (ctrack && !ctrack->l7proto) ctrack->l7proto = l7proto;
if (ctrack && ctrack->l7proto==UNKNOWN) ctrack->l7proto = l7proto;
}
else if (IsDhtD1(data_payload,len_payload))
{
DLOG("packet contains DHT d1...e\n");
l7proto = DHT;
if (ctrack && !ctrack->l7proto) ctrack->l7proto = l7proto;
if (ctrack && ctrack->l7proto==UNKNOWN) ctrack->l7proto = l7proto;
}
else
{
if (!dp->desync_any_proto) return verdict;
if (!dp->desync_any_proto)
{
DLOG("not applying tampering to unknown protocol\n");
return verdict;
}
DLOG("applying tampering to unknown protocol\n");
}
}
if (bHaveHost)
if (bHaveHost) DLOG("hostname: %s\n",host);
bool bDiscoveredL7;
if (ctrack_replay)
{
bool bCheckDone=false, bCheckResult=false, bCheckExcluded=false;
DLOG("hostname: %s\n",host);
bDiscoveredL7 = !ctrack_replay->l7proto_discovered && ctrack_replay->l7proto!=UNKNOWN;
ctrack_replay->l7proto_discovered=true;
}
else
bDiscoveredL7 = !ctrack_replay && l7proto!=UNKNOWN;
if (bDiscoveredL7) DLOG("discovered l7 protocol\n");
bool bDiscoveredHostname = bHaveHost && !(ctrack_replay && ctrack_replay->hostname);
if (bDiscoveredHostname)
{
DLOG("discovered hostname\n");
if (ctrack_replay)
{
ctrack_replay->hostname=strdup(host);
if (!ctrack_replay->hostname)
{
ctrack_replay->hostname=strdup(host);
if (!ctrack_replay->hostname)
{
DLOG_ERR("hostname dup : out of memory");
return verdict;
}
DLOG("we have hostname now. searching desync profile again.\n");
struct desync_profile *dp_prev = dp;
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, !!ip6hdr, 0, ntohs(bReverse ? udphdr->uh_sport : udphdr->uh_dport), ctrack_replay->hostname, &ctrack_replay->bCheckDone, &ctrack_replay->bCheckResult, &ctrack_replay->bCheckExcluded);
ctrack_replay->dp_search_complete = true;
if (!dp) return verdict;
if (dp!=dp_prev)
{
DLOG("desync profile changed by reavealed hostname !\n");
// re-evaluate start/cutoff limiters
if (!replay)
{
maybe_cutoff(ctrack, IPPROTO_UDP);
if (!process_desync_interval(dp, ctrack)) return verdict;
}
}
}
bCheckDone = ctrack_replay->bCheckDone;
bCheckResult = ctrack_replay->bCheckResult;
bCheckExcluded = ctrack_replay->bCheckExcluded;
}
if (dp->hostlist || dp->hostlist_exclude)
{
bool bCheckExcluded;
if (!bCheckDone)
bCheckResult = HostlistCheck(dp, host, &bCheckExcluded);
if (!bCheckResult)
{
if (ctrack_replay)
{
ctrack_replay->hostname_ah_check = *dp->hostlist_auto_filename && !bCheckExcluded;
if (ctrack_replay->hostname_ah_check)
{
// first request is not retrans
if (ctrack_replay->hostname)
process_retrans_fail(ctrack_replay, IPPROTO_UDP);
else
ctrack_replay->hostname=strdup(host);
}
}
DLOG("not applying tampering to this request\n");
DLOG_ERR("hostname dup : out of memory");
return verdict;
}
}
}
bool bCheckDone=false, bCheckResult=false, bCheckExcluded=false;
if (bDiscoveredL7 || bDiscoveredHostname)
{
struct desync_profile *dp_prev = dp;
dp = dp_find(&params.desync_profiles, IPPROTO_UDP, (struct sockaddr *)&dst, ctrack_replay ? ctrack_replay->hostname : host, ctrack_replay ? ctrack_replay->l7proto : l7proto, &bCheckDone, &bCheckResult, &bCheckExcluded);
if (ctrack_replay)
{
ctrack_replay->dp = dp;
ctrack_replay->dp_search_complete = true;
ctrack_replay->bCheckDone = bCheckDone;
ctrack_replay->bCheckResult = bCheckResult;
ctrack_replay->bCheckExcluded = bCheckExcluded;
}
if (!dp)
{
reasm_orig_cancel(ctrack);
return verdict;
}
if (dp!=dp_prev)
{
DLOG("desync profile changed by revealed l7 protocol or hostname !\n");
// rediscover autottl
autottl_discover(ctrack_replay,!!ip6hdr);
// re-evaluate start/cutoff limiters
if (!replay)
{
maybe_cutoff(ctrack, IPPROTO_UDP);
if (!process_desync_interval(dp, ctrack)) return verdict;
}
}
}
else if (ctrack_replay)
{
bCheckDone = ctrack_replay->bCheckDone;
bCheckResult = ctrack_replay->bCheckResult;
bCheckExcluded = ctrack_replay->bCheckExcluded;
}
if (bHaveHost && (dp->hostlist || dp->hostlist_exclude))
{
if (!bCheckDone)
bCheckResult = HostlistCheck(dp, host, &bCheckExcluded);
if (bCheckResult)
ctrack_stop_retrans_counter(ctrack_replay);
else
{
if (ctrack_replay)
{
ctrack_replay->hostname_ah_check = *dp->hostlist_auto_filename && !bCheckExcluded;
if (ctrack_replay->hostname_ah_check)
{
// first request is not retrans
if (ctrack_replay->hostname)
process_retrans_fail(ctrack_replay, IPPROTO_UDP);
else
ctrack_replay->hostname=strdup(host);
}
}
DLOG("not applying tampering to this request\n");
return verdict;
}
}
// desync profile may have changed after hostname was revealed
switch(l7proto)
{
@ -1687,7 +1793,8 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
fake_size = dp->fake_unknown_udp_size;
break;
}
ttl_fake = ip6hdr ? dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig : dp->desync_ttl ? dp->desync_ttl : ttl_orig;
ttl_fake = (ctrack_replay && ctrack_replay->autottl) ? ctrack_replay->autottl : (ip6hdr ? (dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig) : (dp->desync_ttl ? dp->desync_ttl : ttl_orig));
enum dpi_desync_mode desync_mode = dp->desync_mode;
uint32_t fooling_orig = FOOL_NONE;

View File

@ -11,6 +11,12 @@
#include "params.h"
void rtrim(char *s)
{
if (s)
for (char *p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r'); p--) *p = '\0';
}
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
{
size_t k;
@ -181,6 +187,11 @@ bool pton6_port(const char *s, struct sockaddr_in6 *sa)
return true;
}
uint16_t saport(const struct sockaddr *sa)
{
return htons(sa->sa_family==AF_INET ? ((struct sockaddr_in*)sa)->sin_port :
sa->sa_family==AF_INET6 ? ((struct sockaddr_in6*)sa)->sin6_port : 0);
}
void dbgprint_socket_buffers(int fd)
{
@ -385,3 +396,99 @@ bool cd_to_exe_dir(const char *argv0)
}
return bOK;
}
static void mask_from_preflen6_make(uint8_t plen, struct in6_addr *a)
{
if (plen >= 128)
memset(a->s6_addr,0xFF,16);
else
{
uint8_t n = plen >> 3;
memset(a->s6_addr,0xFF,n);
memset(a->s6_addr+n,0x00,16-n);
a->s6_addr[n] = (uint8_t)(0xFF00 >> (plen & 7));
}
}
struct in6_addr ip6_mask[129];
void mask_from_preflen6_prepare(void)
{
for (int plen=0;plen<=128;plen++) mask_from_preflen6_make(plen, ip6_mask+plen);
}
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize ("no-strict-aliasing")))
#endif
void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result)
{
// int128 requires 16-bit alignment. in struct sockaddr_in6.sin6_addr is 8-byte aligned.
// it causes segfault on x64 arch with lastest compiler. it can cause misalign slowdown on other archs
// use 64-bit AND
((uint64_t*)result->s6_addr)[0] = ((uint64_t*)a->s6_addr)[0] & ((uint64_t*)b->s6_addr)[0];
((uint64_t*)result->s6_addr)[1] = ((uint64_t*)a->s6_addr)[1] & ((uint64_t*)b->s6_addr)[1];
}
void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr)
{
char s_ip[16];
*s_ip=0;
inet_ntop(AF_INET, &cidr->addr, s_ip, sizeof(s_ip));
snprintf(s,s_len,cidr->preflen<32 ? "%s/%u" : "%s", s_ip, cidr->preflen);
}
void print_cidr4(const struct cidr4 *cidr)
{
char s[19];
str_cidr4(s,sizeof(s),cidr);
printf("%s",s);
}
void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr)
{
char s_ip[40];
*s_ip=0;
inet_ntop(AF_INET6, &cidr->addr, s_ip, sizeof(s_ip));
snprintf(s,s_len,cidr->preflen<128 ? "%s/%u" : "%s", s_ip, cidr->preflen);
}
void print_cidr6(const struct cidr6 *cidr)
{
char s[44];
str_cidr6(s,sizeof(s),cidr);
printf("%s",s);
}
bool parse_cidr4(char *s, struct cidr4 *cidr)
{
char *p,d;
bool b;
unsigned int plen;
if ((p = strchr(s, '/')))
{
if (sscanf(p + 1, "%u", &plen)!=1 || plen>32)
return false;
cidr->preflen = (uint8_t)plen;
d=*p; *p=0; // backup char
}
else
cidr->preflen = 32;
b = (inet_pton(AF_INET, s, &cidr->addr)==1);
if (p) *p=d; // restore char
return b;
}
bool parse_cidr6(char *s, struct cidr6 *cidr)
{
char *p,d;
bool b;
unsigned int plen;
if ((p = strchr(s, '/')))
{
if (sscanf(p + 1, "%u", &plen)!=1 || plen>128)
return false;
cidr->preflen = (uint8_t)plen;
d=*p; *p=0; // backup char
}
else
cidr->preflen = 128;
b = (inet_pton(AF_INET6, s, &cidr->addr)==1);
if (p) *p=d; // restore char
return b;
}

View File

@ -9,6 +9,16 @@
#include <stdio.h>
#include <time.h>
// this saves memory. sockaddr_storage is larger than required. it can be 128 bytes. sockaddr_in6 is 28 bytes.
typedef union
{
struct sockaddr_in sa4; // size 16
struct sockaddr_in6 sa6; // size 28
char _align[32]; // force 16-byte alignment for ip6_and int128 ops
} sockaddr_in46;
void rtrim(char *s);
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit);
char *strncasestr(const char *s,const char *find, size_t slen);
bool load_file(const char *filename,void *buffer,size_t *buffer_size);
@ -22,6 +32,8 @@ void ntop46_port(const struct sockaddr *sa, char *str, size_t len);
bool pton4_port(const char *s, struct sockaddr_in *sa);
bool pton6_port(const char *s, struct sockaddr_in6 *sa);
uint16_t saport(const struct sockaddr *sa);
bool seq_within(uint32_t s, uint32_t s1, uint32_t s2);
void dbgprint_socket_buffers(int fd);
@ -64,3 +76,33 @@ void fill_random_az(uint8_t *p,size_t sz);
void fill_random_az09(uint8_t *p,size_t sz);
bool cd_to_exe_dir(const char *argv0);
struct cidr4
{
struct in_addr addr;
uint8_t preflen;
};
struct cidr6
{
struct in6_addr addr;
uint8_t preflen;
};
void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr);
void print_cidr4(const struct cidr4 *cidr);
void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr);
void print_cidr6(const struct cidr6 *cidr);
bool parse_cidr4(char *s, struct cidr4 *cidr);
bool parse_cidr6(char *s, struct cidr6 *cidr);
static inline uint32_t mask_from_preflen(uint32_t preflen)
{
return preflen ? preflen<32 ? ~((1 << (32-preflen)) - 1) : 0xFFFFFFFF : 0;
}
void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result);
extern struct in6_addr ip6_mask[129];
void mask_from_preflen6_prepare(void);
static inline const struct in6_addr *mask_from_preflen6(uint8_t preflen)
{
return ip6_mask+preflen;
}

View File

@ -55,7 +55,7 @@ bool AppendHostList(strpool **hostlist, const char *filename)
if (r==Z_OK)
{
DLOG_CONDUP("zlib compression detected. uncompressed size : %zu\n", zsize);
p = zbuf;
e = zbuf + zsize;
while(p<e)
@ -78,7 +78,7 @@ bool AppendHostList(strpool **hostlist, const char *filename)
else
{
DLOG_CONDUP("loading plain text list\n");
while (fgets(s, sizeof(s), F))
{
p = s;
@ -174,7 +174,7 @@ static bool LoadIncludeHostListsForProfile(struct desync_profile *dp)
// return : true = apply fooling, false = do not apply
bool HostlistCheck(struct desync_profile *dp, const char *host, bool *excluded)
{
DLOG("* Hostlist check for profile %d\n",dp->n);
DLOG("* hostlist check for profile %d\n",dp->n);
if (*dp->hostlist_auto_filename)
{
time_t t = file_mod_time(dp->hostlist_auto_filename);

195
nfq/ipset.c Normal file
View File

@ -0,0 +1,195 @@
#include <stdio.h>
#include "ipset.h"
#include "gzip.h"
#include "helpers.h"
// inplace tolower() and add to pool
static bool addpool(ipset *ips, char **s, const char *end, int *ct)
{
char *p, cidr[128];
size_t l;
struct cidr4 c4;
struct cidr6 c6;
// advance until eol
for (p=*s; p<end && *p && *p!='\r' && *p != '\n'; p++);
// comment line
if (!(**s == '#' || **s == ';' || **s == '/' || **s == '\r' || **s == '\n' ))
{
l = p-*s;
if (l>=sizeof(cidr)) l=sizeof(cidr)-1;
memcpy(cidr,*s,l);
cidr[l]=0;
rtrim(cidr);
if (parse_cidr4(cidr,&c4))
{
if (!ipset4AddCidr(&ips->ips4, &c4))
{
ipsetDestroy(ips);
return false;
}
(*ct)++;
}
else if (parse_cidr6(cidr,&c6))
{
if (!ipset6AddCidr(&ips->ips6, &c6))
{
ipsetDestroy(ips);
return false;
}
(*ct)++;
}
else
DLOG_ERR("bad ip or subnet : %s\n",cidr);
}
// advance to the next line
for (; p<end && (!*p || *p=='\r' || *p=='\n') ; p++);
*s = p;
return true;
}
static bool AppendIpset(ipset *ips, const char *filename)
{
char *p, *e, s[256], *zbuf;
size_t zsize;
int ct = 0;
FILE *F;
int r;
DLOG_CONDUP("Loading ipset %s\n",filename);
if (!(F = fopen(filename, "rb")))
{
DLOG_ERR("Could not open %s\n", filename);
return false;
}
if (is_gzip(F))
{
r = z_readfile(F,&zbuf,&zsize);
fclose(F);
if (r==Z_OK)
{
DLOG_CONDUP("zlib compression detected. uncompressed size : %zu\n", zsize);
p = zbuf;
e = zbuf + zsize;
while(p<e)
{
if (!addpool(ips,&p,e,&ct))
{
DLOG_ERR("Not enough memory to store ipset : %s\n", filename);
free(zbuf);
return false;
}
}
free(zbuf);
}
else
{
DLOG_ERR("zlib decompression failed : result %d\n",r);
return false;
}
}
else
{
DLOG_CONDUP("loading plain text list\n");
while (fgets(s, sizeof(s)-1, F))
{
p = s;
if (!addpool(ips,&p,p+strlen(p),&ct))
{
DLOG_ERR("Not enough memory to store ipset : %s\n", filename);
fclose(F);
return false;
}
}
fclose(F);
}
DLOG_CONDUP("Loaded %d ip/subnets from %s\n", ct, filename);
return true;
}
static bool LoadIpsets(ipset *ips, struct str_list_head *file_list)
{
struct str_list *file;
ipsetDestroy(ips);
LIST_FOREACH(file, file_list, next)
{
if (!AppendIpset(ips, file->str)) return false;
}
return true;
}
bool LoadIncludeIpsets()
{
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, &params.desync_profiles, next)
if (!LoadIpsets(&dpl->dp.ips, &dpl->dp.ipset_files))
return false;
return true;
}
bool LoadExcludeIpsets()
{
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, &params.desync_profiles, next)
if (!LoadIpsets(&dpl->dp.ips_exclude, &dpl->dp.ipset_exclude_files))
return false;
return true;
}
bool SearchIpset(const ipset *ips, const struct in_addr *ipv4, const struct in6_addr *ipv6)
{
char s_ip[40];
bool bInSet=false;
if (!!ipv4 != !!ipv6)
{
*s_ip=0;
if (ipv4)
{
if (params.debug) inet_ntop(AF_INET, ipv4, s_ip, sizeof(s_ip));
if (ips->ips4) bInSet = ipset4Check(ips->ips4, ipv4, 32);
}
if (ipv6)
{
if (params.debug) inet_ntop(AF_INET6, ipv6, s_ip, sizeof(s_ip));
if (ips->ips6) bInSet = ipset6Check(ips->ips6, ipv6, 128);
}
DLOG("ipset check for %s : %s\n", s_ip, bInSet ? "positive" : "negative");
}
else
// ipv4 and ipv6 are both empty or non-empty
DLOG("ipset check error !!!!!!!! ipv4=%p ipv6=%p\n",ipv4,ipv6);
return bInSet;
}
static bool IpsetCheck_(const ipset *ips, const ipset *ips_exclude, const struct in_addr *ipv4, const struct in6_addr *ipv6)
{
if (!IPSET_EMPTY(ips_exclude))
{
DLOG("exclude ");
if (SearchIpset(ips_exclude, ipv4, ipv6))
return false;
}
if (!IPSET_EMPTY(ips))
{
DLOG("include ");
return SearchIpset(ips, ipv4, ipv6);
}
return true;
}
bool IpsetCheck(struct desync_profile *dp, const struct in_addr *ipv4, const struct in6_addr *ipv6)
{
if (!PROFILE_IPSETS_EMPTY(dp)) DLOG("* ipset check for profile %d\n",dp->n);
return IpsetCheck_(&dp->ips,&dp->ips_exclude,ipv4,ipv6);
}

11
nfq/ipset.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <stdbool.h>
#include <arpa/inet.h>
#include "params.h"
#include "pools.h"
bool LoadIncludeIpsets();
bool LoadExcludeIpsets();
bool SearchIpset(const ipset *ips, const struct in_addr *ipv4, const struct in6_addr *ipv6);
bool IpsetCheck(struct desync_profile *dp, const struct in_addr *ipv4, const struct in6_addr *ipv6);

View File

@ -8,6 +8,7 @@
#include "params.h"
#include "protocol.h"
#include "hostlist.h"
#include "ipset.h"
#include "gzip.h"
#include "pools.h"
@ -54,7 +55,7 @@ static bool bHup = false;
static void onhup(int sig)
{
printf("HUP received !\n");
printf("Will reload hostlist on next request (if any)\n");
printf("Will reload hostlists and ipsets on next request (if any)\n");
bHup = true;
}
// should be called in normal execution
@ -62,7 +63,7 @@ static void dohup(void)
{
if (bHup)
{
if (!LoadIncludeHostLists() || !LoadExcludeHostLists())
if (!LoadIncludeHostLists() || !LoadExcludeHostLists() || !LoadIncludeIpsets() || !LoadExcludeIpsets())
{
// what will we do without hostlist ?? sure, gonna die
exit(1);
@ -621,7 +622,7 @@ static void load_file_or_exit(const char *filename, void *buf, size_t *size)
}
}
bool parse_autottl(const char *s, autottl *t)
static bool parse_autottl(const char *s, autottl *t)
{
unsigned int delta,min,max;
AUTOTTL_SET_DEFAULT(*t);
@ -647,6 +648,42 @@ bool parse_autottl(const char *s, autottl *t)
return true;
}
static bool parse_l7_list(char *opt, uint32_t *l7)
{
char *e,*p,c;
for (p=opt,*l7=0 ; p ; )
{
if ((e = strchr(p,',')))
{
c=*e;
*e=0;
}
if (!strcmp(p,"http"))
*l7 |= L7_PROTO_HTTP;
else if (!strcmp(p,"tls"))
*l7 |= L7_PROTO_TLS;
else if (!strcmp(p,"quic"))
*l7 |= L7_PROTO_QUIC;
else if (!strcmp(p,"wireguard"))
*l7 |= L7_PROTO_WIREGUARD;
else if (!strcmp(p,"dht"))
*l7 |= L7_PROTO_DHT;
else if (!strcmp(p,"unknown"))
*l7 |= L7_PROTO_UNKNOWN;
else return false;
if (e)
{
*e++=c;
}
p = e;
}
return true;
}
static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6)
{
char *e,*p,c;
@ -673,6 +710,7 @@ static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6)
}
return true;
}
#ifdef __CYGWIN__
static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *buf, size_t len)
{
@ -831,6 +869,9 @@ static void exithelp(void)
" --filter-l3=ipv4|ipv6\t\t\t\t; L3 protocol filter. multiple comma separated values allowed.\n"
" --filter-tcp=[~]port1[-port2]\t\t\t; TCP port filter. ~ means negation. setting tcp and not setting udp filter denies udp.\n"
" --filter-udp=[~]port1[-port2]\t\t\t; UDP port filter. ~ means negation. setting udp and not setting tcp filter denies tcp.\n"
" --filter-l7=[http|tls|quic|wireguard|dht|unknown] ; L6-L7 protocol filter. multiple comma separated values allowed.\n"
" --ipset=<filename>\t\t\t\t; ipset include filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)\n"
" --ipset-exclude=<filename>\t\t\t; ipset exclude filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)\n"
"\nHOSTLIST FILTER:\n"
" --hostlist=<filename>\t\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
" --hostlist-exclude=<filename>\t\t\t; do not apply dpi desync to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
@ -945,6 +986,7 @@ int main(int argc, char **argv)
#endif
srandom(time(NULL));
mask_from_preflen6_prepare();
memset(&params, 0, sizeof(params));
*pidfile = 0;
@ -1056,19 +1098,22 @@ int main(int argc, char **argv)
{"filter-l3",required_argument,0,0}, // optidx=53
{"filter-tcp",required_argument,0,0}, // optidx=54
{"filter-udp",required_argument,0,0}, // optidx=55
{"filter-l7",required_argument,0,0}, // optidx=56
{"ipset",required_argument,0,0}, // optidx=57
{"ipset-exclude",required_argument,0,0},// optidx=58
#ifdef __linux__
{"bind-fix4",no_argument,0,0}, // optidx=56
{"bind-fix6",no_argument,0,0}, // optidx=57
{"bind-fix4",no_argument,0,0}, // optidx=59
{"bind-fix6",no_argument,0,0}, // optidx=60
#elif defined(__CYGWIN__)
{"wf-iface",required_argument,0,0}, // optidx=56
{"wf-l3",required_argument,0,0}, // optidx=57
{"wf-tcp",required_argument,0,0}, // optidx=58
{"wf-udp",required_argument,0,0}, // optidx=59
{"wf-raw",required_argument,0,0}, // optidx=60
{"wf-save",required_argument,0,0}, // optidx=61
{"ssid-filter",required_argument,0,0}, // optidx=62
{"nlm-filter",required_argument,0,0}, // optidx=63
{"nlm-list",optional_argument,0,0}, // optidx=64
{"wf-iface",required_argument,0,0}, // optidx=59
{"wf-l3",required_argument,0,0}, // optidx=60
{"wf-tcp",required_argument,0,0}, // optidx=61
{"wf-udp",required_argument,0,0}, // optidx=62
{"wf-raw",required_argument,0,0}, // optidx=63
{"wf-save",required_argument,0,0}, // optidx=64
{"ssid-filter",required_argument,0,0}, // optidx=65
{"nlm-filter",required_argument,0,0}, // optidx=66
{"nlm-list",optional_argument,0,0}, // optidx=67
#endif
{NULL,0,NULL,0}
};
@ -1596,30 +1641,53 @@ int main(int argc, char **argv)
// deny tcp if not set
if (pf_is_empty(&dp->pf_tcp)) dp->pf_tcp.neg=true;
break;
case 56: /* filter-l7 */
if (!parse_l7_list(optarg,&dp->filter_l7))
{
DLOG_ERR("Invalid l7 filter : %s\n",optarg);
exit_clean(1);
}
break;
case 57: /* ipset */
if (!strlist_add(&dp->ipset_files, optarg))
{
DLOG_ERR("strlist_add failed\n");
exit_clean(1);
}
break;
case 58: /* ipset-exclude */
if (!strlist_add(&dp->ipset_exclude_files, optarg))
{
DLOG_ERR("strlist_add failed\n");
exit_clean(1);
}
break;
#ifdef __linux__
case 56: /* bind-fix4 */
case 59: /* bind-fix4 */
params.bind_fix4 = true;
break;
case 57: /* bind-fix6 */
case 60: /* bind-fix6 */
params.bind_fix6 = true;
break;
#elif defined(__CYGWIN__)
case 56: /* wf-iface */
case 59: /* wf-iface */
if (!sscanf(optarg,"%u.%u",&IfIdx,&SubIfIdx))
{
DLOG_ERR("bad value for --wf-iface\n");
exit_clean(1);
}
break;
case 57: /* wf-l3 */
case 60: /* wf-l3 */
if (!wf_make_l3(optarg,&wf_ipv4,&wf_ipv6))
{
DLOG_ERR("bad value for --wf-l3\n");
exit_clean(1);
}
break;
case 58: /* wf-tcp */
case 61: /* wf-tcp */
hash_wf_tcp=hash_jen(optarg,strlen(optarg));
if (!wf_make_pf(optarg,"tcp","SrcPort",wf_pf_tcp_src,sizeof(wf_pf_tcp_src)) ||
!wf_make_pf(optarg,"tcp","DstPort",wf_pf_tcp_dst,sizeof(wf_pf_tcp_dst)))
@ -1628,7 +1696,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 59: /* wf-udp */
case 62: /* wf-udp */
hash_wf_udp=hash_jen(optarg,strlen(optarg));
if (!wf_make_pf(optarg,"udp","SrcPort",wf_pf_udp_src,sizeof(wf_pf_udp_src)) ||
!wf_make_pf(optarg,"udp","DstPort",wf_pf_udp_dst,sizeof(wf_pf_udp_dst)))
@ -1637,7 +1705,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 60: /* wf-raw */
case 63: /* wf-raw */
hash_wf_raw=hash_jen(optarg,strlen(optarg));
if (optarg[0]=='@')
{
@ -1651,11 +1719,11 @@ int main(int argc, char **argv)
windivert_filter[sizeof(windivert_filter) - 1] = '\0';
}
break;
case 61: /* wf-save */
case 64: /* wf-save */
strncpy(wf_save_file, optarg, sizeof(wf_save_file));
wf_save_file[sizeof(wf_save_file) - 1] = '\0';
break;
case 62: /* ssid-filter */
case 65: /* ssid-filter */
hash_ssid_filter=hash_jen(optarg,strlen(optarg));
{
char *e,*p = optarg;
@ -1673,7 +1741,7 @@ int main(int argc, char **argv)
}
}
break;
case 63: /* nlm-filter */
case 66: /* nlm-filter */
hash_nlm_filter=hash_jen(optarg,strlen(optarg));
{
char *e,*p = optarg;
@ -1691,7 +1759,7 @@ int main(int argc, char **argv)
}
}
break;
case 64: /* nlm-list */
case 67: /* nlm-list */
if (!nlm_list(optarg && !strcmp(optarg,"all")))
{
DLOG_ERR("could not get list of NLM networks\n");
@ -1776,9 +1844,9 @@ int main(int argc, char **argv)
if (dp->desync_ttl6 == 0xFF) dp->desync_ttl6=dp->desync_ttl;
if (!AUTOTTL_ENABLED(dp->desync_autottl6)) dp->desync_autottl6 = dp->desync_autottl;
if (AUTOTTL_ENABLED(dp->desync_autottl))
DLOG("[profile %d] autottl ipv4 %u:%u-%u\n",v,dp->desync_autottl.delta,dp->desync_autottl.min,dp->desync_autottl.max);
DLOG("[profile %d] autottl ipv4 %u:%u-%u\n",dp->n,dp->desync_autottl.delta,dp->desync_autottl.min,dp->desync_autottl.max);
if (AUTOTTL_ENABLED(dp->desync_autottl6))
DLOG("[profile %d] autottl ipv6 %u:%u-%u\n",v,dp->desync_autottl6.delta,dp->desync_autottl6.min,dp->desync_autottl6.max);
DLOG("[profile %d] autottl ipv6 %u:%u-%u\n",dp->n,dp->desync_autottl6.delta,dp->desync_autottl6.min,dp->desync_autottl6.max);
if (dp->desync_split_tls==tlspos_none && dp->desync_split_pos) dp->desync_split_tls=tlspos_pos;
if (dp->desync_split_http_req==httpreqpos_none && dp->desync_split_pos) dp->desync_split_http_req=httpreqpos_pos;
}
@ -1793,6 +1861,16 @@ int main(int argc, char **argv)
DLOG_ERR("Exclude hostlists load failed\n");
exit_clean(1);
}
if (!LoadIncludeIpsets())
{
DLOG_ERR("Include ipset load failed\n");
exit_clean(1);
}
if (!LoadExcludeIpsets())
{
DLOG_ERR("Exclude ipset load failed\n");
exit_clean(1);
}
if (daemon) daemonize();

View File

@ -207,8 +207,12 @@ static void dp_entry_destroy(struct desync_profile_list *entry)
{
strlist_destroy(&entry->dp.hostlist_files);
strlist_destroy(&entry->dp.hostlist_exclude_files);
strlist_destroy(&entry->dp.ipset_files);
strlist_destroy(&entry->dp.ipset_exclude_files);
StrPoolDestroy(&entry->dp.hostlist_exclude);
StrPoolDestroy(&entry->dp.hostlist);
ipsetDestroy(&entry->dp.ips);
ipsetDestroy(&entry->dp.ips_exclude);
HostFailPoolDestroy(&entry->dp.hostlist_auto_fail_counters);
free(entry);
}

View File

@ -66,6 +66,10 @@ struct desync_profile
bool filter_ipv4,filter_ipv6;
port_filter pf_tcp,pf_udp;
uint32_t filter_l7; // L7_PROTO_* bits
ipset ips,ips_exclude;
struct str_list_head ipset_files, ipset_exclude_files;
strpool *hostlist, *hostlist_exclude;
struct str_list_head hostlist_files, hostlist_exclude_files;
char hostlist_auto_filename[PATH_MAX];
@ -74,6 +78,8 @@ struct desync_profile
hostfail_pool *hostlist_auto_fail_counters;
};
#define PROFILE_IPSETS_EMPTY(dp) (IPSET_EMPTY(&dp->ips) && IPSET_EMPTY(&dp->ips_exclude))
struct desync_profile_list {
struct desync_profile dp;
LIST_ENTRY(desync_profile_list) next;

View File

@ -151,3 +151,127 @@ void strlist_destroy(struct str_list_head *head)
strlist_entry_destroy(entry);
}
}
void ipset4Destroy(ipset4 **ipset)
{
ipset4 *elem, *tmp;
HASH_ITER(hh, *ipset, elem, tmp)
{
HASH_DEL(*ipset, elem);
free(elem);
}
}
bool ipset4Check(ipset4 *ipset, const struct in_addr *a, uint8_t preflen)
{
uint32_t ip = ntohl(a->s_addr);
struct cidr4 cidr;
ipset4 *ips_found;
// zero alignment bytes
memset(&cidr,0,sizeof(cidr));
cidr.preflen = preflen+1;
do
{
cidr.preflen--;
cidr.addr.s_addr = htonl(ip & mask_from_preflen(cidr.preflen));
HASH_FIND(hh, ipset, &cidr, sizeof(cidr), ips_found);
if (ips_found) return true;
} while(cidr.preflen);
return false;
}
bool ipset4Add(ipset4 **ipset, const struct in_addr *a, uint8_t preflen)
{
if (preflen>32) return false;
// avoid dups
if (ipset4Check(*ipset, a, preflen)) return true; // already included
struct ipset4 *entry = calloc(1,sizeof(ipset4));
if (!entry) return false;
entry->cidr.addr.s_addr = htonl(ntohl(a->s_addr) & mask_from_preflen(preflen));
entry->cidr.preflen = preflen;
oom = false;
HASH_ADD(hh, *ipset, cidr, sizeof(entry->cidr), entry);
if (oom) { free(entry); return false; }
return true;
}
void ipset4Print(ipset4 *ipset)
{
ipset4 *ips, *tmp;
HASH_ITER(hh, ipset , ips, tmp)
{
print_cidr4(&ips->cidr);
printf("\n");
}
}
void ipset6Destroy(ipset6 **ipset)
{
ipset6 *elem, *tmp;
HASH_ITER(hh, *ipset, elem, tmp)
{
HASH_DEL(*ipset, elem);
free(elem);
}
}
bool ipset6Check(ipset6 *ipset, const struct in6_addr *a, uint8_t preflen)
{
struct cidr6 cidr;
ipset6 *ips_found;
// zero alignment bytes
memset(&cidr,0,sizeof(cidr));
cidr.preflen = preflen+1;
do
{
cidr.preflen--;
ip6_and(a, mask_from_preflen6(cidr.preflen), &cidr.addr);
HASH_FIND(hh, ipset, &cidr, sizeof(cidr), ips_found);
if (ips_found) return true;
} while(cidr.preflen);
return false;
}
bool ipset6Add(ipset6 **ipset, const struct in6_addr *a, uint8_t preflen)
{
if (preflen>128) return false;
// avoid dups
if (ipset6Check(*ipset, a, preflen)) return true; // already included
struct ipset6 *entry = calloc(1,sizeof(ipset6));
if (!entry) return false;
ip6_and(a, mask_from_preflen6(preflen), &entry->cidr.addr);
entry->cidr.preflen = preflen;
oom = false;
HASH_ADD(hh, *ipset, cidr, sizeof(entry->cidr), entry);
if (oom) { free(entry); return false; }
return true;
}
void ipset6Print(ipset6 *ipset)
{
ipset6 *ips, *tmp;
HASH_ITER(hh, ipset , ips, tmp)
{
print_cidr6(&ips->cidr);
printf("\n");
}
}
void ipsetDestroy(ipset *ipset)
{
ipset4Destroy(&ipset->ips4);
ipset6Destroy(&ipset->ips6);
}
void ipsetPrint(ipset *ipset)
{
ipset4Print(ipset->ips4);
ipset6Print(ipset->ips6);
}

View File

@ -5,6 +5,8 @@
#include <sys/queue.h>
#include <time.h>
#include "helpers.h"
//#define HASH_BLOOM 20
#define HASH_NONFATAL_OOM 1
#define HASH_FUNCTION HASH_BER
@ -43,3 +45,41 @@ void HostFailPoolDump(hostfail_pool *p);
bool strlist_add(struct str_list_head *head, const char *filename);
void strlist_destroy(struct str_list_head *head);
typedef struct ipset4 {
struct cidr4 cidr; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} ipset4;
typedef struct ipset6 {
struct cidr6 cidr; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} ipset6;
// combined ipset ipv4 and ipv6
typedef struct ipset {
ipset4 *ips4;
ipset6 *ips6;
} ipset;
#define IPSET_EMPTY(ips) (!(ips)->ips4 && !(ips)->ips6)
void ipset4Destroy(ipset4 **ipset);
bool ipset4Add(ipset4 **ipset, const struct in_addr *a, uint8_t preflen);
static inline bool ipset4AddCidr(ipset4 **ipset, const struct cidr4 *cidr)
{
return ipset4Add(ipset,&cidr->addr,cidr->preflen);
}
bool ipset4Check(ipset4 *ipset, const struct in_addr *a, uint8_t preflen);
void ipset4Print(ipset4 *ipset);
void ipset6Destroy(ipset6 **ipset);
bool ipset6Add(ipset6 **ipset, const struct in6_addr *a, uint8_t preflen);
static inline bool ipset6AddCidr(ipset6 **ipset, const struct cidr6 *cidr)
{
return ipset6Add(ipset,&cidr->addr,cidr->preflen);
}
bool ipset6Check(ipset6 *ipset, const struct in6_addr *a, uint8_t preflen);
void ipset6Print(ipset6 *ipset);
void ipsetDestroy(ipset *ipset);
void ipsetPrint(ipset *ipset);