diff --git a/docs/manual.en.md b/docs/manual.en.md index e9037f1..609bed0 100644 --- a/docs/manual.en.md +++ b/docs/manual.en.md @@ -712,6 +712,7 @@ Specific parameters for nfqws2: --bind-fix4 ; fix for the issue where generated packets on Linux exit from the wrong interface when using PBR (IPv4) --bind-fix6 ; same as above for IPv6 --fwmark= ; mark bit to prevent looping. default = 0x40000000 + --filter-mark=mark[/mask] ; profile filter: filter by packet mark. mark and mask can be decimal or 0xHEX --filter-ssid=ssid1[,ssid2,ssid3,...] ; profile filter: WiFi network name (SSID) --filter-ssid-neg=[0|1] ; profile filter: negate SSID filter ``` diff --git a/docs/manual.md b/docs/manual.md index 2ec5c66..64dd275 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -772,6 +772,7 @@ LUA DESYNC ACTION: --bind-fix4 ; лечение проблемы ухода генерированных пакетов на Linux с неверного интерфейса при использовании PBR (ipv4) --bind-fix6 ; аналогично для ipv6 --fwmark= ; бит в mark для предотвращения зацикливания. default = 0x40000000 + --filter-mark=mark[/mask] ; фильтр профиля : фильтрация по mark пакета. mark и mask могут быть десятичные или 0xHEX --filter-ssid=ssid1[,ssid2,ssid3,...] ; фильтр профиля : имя wifi сети --filter-ssid-neg=[0|1] ; фильтр профиля : инверсия фильтра wifi сети ``` diff --git a/nfq2/desync.c b/nfq2/desync.c index 22fa113..0f8c830 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -208,7 +208,8 @@ static bool dp_match( const struct in_addr *ip, const struct in6_addr *ip6, const struct in_addr *ipr, const struct in6_addr *ipr6, uint16_t port, uint8_t icmp_type, uint8_t icmp_code, - const char *hostname, bool bNoSubdom, t_l7proto l7proto, const char *ssid, + const char *hostname, bool bNoSubdom, t_l7proto l7proto, + const char *ssid, uint32_t fwmark, bool *bCheckDone, bool *bCheckResult, bool *bExcluded) { bool bHostlistsEmpty; @@ -240,6 +241,10 @@ static bool dp_match( if (!l7_proto_match(l7proto, dp->filter_l7)) // L7 filter does not match return false; +#ifdef __linux__ + if ((fwmark & dp->filter_mark_mask)!=dp->filter_mark) + return false; +#endif #ifdef HAS_FILTER_SSID if (!LIST_EMPTY(&dp->filter_ssid) && (!strlist_search(&dp->filter_ssid, ssid) ^ dp->filter_ssid_neg)) return false; @@ -283,7 +288,8 @@ static struct desync_profile *dp_find( const struct in_addr *ip, const struct in6_addr *ip6, const struct in_addr *ipr, const struct in6_addr *ipr6, uint16_t port, uint8_t icmp_type, uint8_t icmp_code, - const char *hostname, bool bNoSubdom, t_l7proto l7proto, const char *ssid, + const char *hostname, bool bNoSubdom, t_l7proto l7proto, + const char *ssid, uint32_t fwmark, bool *bCheckDone, bool *bCheckResult, bool *bExcluded) { struct desync_profile_list *dpl; @@ -295,17 +301,17 @@ static struct desync_profile *dp_find( { char sr[INET6_ADDRSTRLEN]; ntopa46(ipr, ipr6, sr, sizeof(sr)); - DLOG("desync profile search for %s ip1=%s ip2=%s port=%u icmp=%u:%u l7proto=%s ssid='%s' hostname='%s'\n", - proto_name(l3proto), s, sr, port, icmp_type, icmp_code, l7proto_str(l7proto), ssid ? ssid : "", hostname ? hostname : ""); + DLOG("desync profile search for %s ip1=%s ip2=%s port=%u icmp=%u:%u l7proto=%s ssid='%s' mark=0x%08X hostname='%s'\n", + proto_name(l3proto), s, sr, port, icmp_type, icmp_code, l7proto_str(l7proto), ssid ? ssid : "", fwmark, hostname ? hostname : ""); } else - DLOG("desync profile search for %s ip=%s port=%u icmp=%u:%u l7proto=%s ssid='%s' hostname='%s'\n", - proto_name(l3proto), s, port, icmp_type, icmp_code, l7proto_str(l7proto), ssid ? ssid : "", hostname ? hostname : ""); + DLOG("desync profile search for %s ip=%s port=%u icmp=%u:%u l7proto=%s ssid='%s' mark=0x%08X hostname='%s'\n", + proto_name(l3proto), s, port, icmp_type, icmp_code, l7proto_str(l7proto), ssid ? ssid : "", fwmark, hostname ? hostname : ""); } if (bCheckDone) *bCheckDone = false; LIST_FOREACH(dpl, head, next) { - if (dp_match(&dpl->dp, l3proto, ip, ip6, ipr, ipr6, port, icmp_type, icmp_code, hostname, bNoSubdom, l7proto, ssid, bCheckDone, bCheckResult, bExcluded)) + if (dp_match(&dpl->dp, l3proto, ip, ip6, ipr, ipr6, port, icmp_type, icmp_code, hostname, bNoSubdom, l7proto, ssid, fwmark, bCheckDone, bCheckResult, bExcluded)) { DLOG("desync profile %u (%s) matches\n", dpl->dp.n, PROFILE_NAME(&dpl->dp)); return &dpl->dp; @@ -1188,6 +1194,7 @@ struct play_state t_l7proto l7proto; t_l7payload l7payload; const char *ssid; + uint32_t fwmark; bool bReverse, bReverseFixed, bHaveHost; }; static bool play_prolog( @@ -1195,7 +1202,8 @@ static bool play_prolog( const struct dissect *dis, const t_ctrack_positions *tpos, bool bReplay, - const char *ifin, const char *ifout) + const char *ifin, const char *ifout, + uint32_t fwmark) { ps->verdict = VERDICT_PASS; @@ -1210,6 +1218,7 @@ static bool play_prolog( ps->l7proto = L7_UNKNOWN; ps->l7payload = dis->len_payload ? L7P_UNKNOWN : L7P_EMPTY; ps->ssid = NULL; + ps->fwmark = fwmark; const char *ifname; @@ -1233,7 +1242,7 @@ static bool play_prolog( DLOG("using cached desync profile %u (%s)\n", ps->dp->n, PROFILE_NAME(ps->dp)); else if (!ps->ctrack_replay->dp_search_complete) { - ps->dp = ps->ctrack_replay->dp = dp_find(¶ms.desync_profiles, dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, ps->ctrack_replay->hostname, ps->ctrack_replay->hostname_is_ip, ps->l7proto, ps->ssid, NULL, NULL, NULL); + ps->dp = ps->ctrack_replay->dp = dp_find(¶ms.desync_profiles, dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, ps->ctrack_replay->hostname, ps->ctrack_replay->hostname_is_ip, ps->l7proto, ps->ssid, ps->fwmark, NULL, NULL, NULL); ps->ctrack_replay->dp_search_complete = true; } if (!ps->dp) @@ -1281,7 +1290,7 @@ static bool play_prolog( DLOG_ERR("strdup(host): out of memory\n"); } } - ps->dp = dp_find(¶ms.desync_profiles, dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, hostname, hostname_is_ip, ps->l7proto, ps->ssid, NULL, NULL, NULL); + ps->dp = dp_find(¶ms.desync_profiles, dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, hostname, hostname_is_ip, ps->l7proto, ps->ssid, ps->fwmark, NULL, NULL, NULL); if (ps->ctrack) { ps->ctrack->dp = ps->dp; @@ -1369,7 +1378,7 @@ static bool dp_rediscovery(struct play_state *ps) ps->dp = dp_find(¶ms.desync_profiles, ps->dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, ps->ctrack_replay ? ps->ctrack_replay->hostname : ps->bHaveHost ? ps->host : NULL, ps->ctrack_replay ? ps->ctrack_replay->hostname_is_ip : bHostIsIp, - ps->l7proto, ps->ssid, + ps->l7proto, ps->ssid, ps->fwmark, &bCheckDone, &bCheckResult, &bCheckExcluded); if (ps->ctrack_replay) { @@ -1447,7 +1456,7 @@ static uint8_t dpi_desync_tcp_packet_play( { struct play_state ps; - if (!play_prolog(&ps, dis, tpos, !!replay_piece_count, ifin, ifout)) + if (!play_prolog(&ps, dis, tpos, !!replay_piece_count, ifin, ifout, fwmark)) return ps.verdict; uint32_t desync_fwmark = fwmark | params.desync_fwmark; @@ -1806,7 +1815,7 @@ static uint8_t dpi_desync_udp_packet_play( { struct play_state ps; - if (!play_prolog(&ps, dis, tpos, !!replay_piece_count, ifin, ifout)) + if (!play_prolog(&ps, dis, tpos, !!replay_piece_count, ifin, ifout, fwmark)) return ps.verdict; uint32_t desync_fwmark = fwmark | params.desync_fwmark; @@ -2068,7 +2077,7 @@ static uint8_t dpi_desync_icmp_packet( dis->ip ? &dis->ip->ip_src : NULL, dis->ip6 ? &dis->ip6->ip6_src : NULL, 0, dis->icmp->icmp_type, dis->icmp->icmp_code, hostname, hostname_is_ip, - L7_UNKNOWN, ssid, NULL, NULL, NULL); + L7_UNKNOWN, ssid, fwmark, NULL, NULL, NULL); if (!dp) { DLOG("matching desync profile not found\n"); @@ -2134,7 +2143,7 @@ static uint8_t dpi_desync_ip_packet( dis->ip ? &dis->ip->ip_src : NULL, dis->ip6 ? &dis->ip6->ip6_src : NULL, 0, 0xFF, 0xFF, hostname, hostname_is_ip, - L7_UNKNOWN, ssid, NULL, NULL, NULL); + L7_UNKNOWN, ssid, fwmark, NULL, NULL, NULL); if (!dp) { DLOG("matching desync profile not found\n"); diff --git a/nfq2/nfqws.c b/nfq2/nfqws.c index d2a88d5..ae4bef9 100644 --- a/nfq2/nfqws.c +++ b/nfq2/nfqws.c @@ -1890,6 +1890,9 @@ static void exithelp(void) #ifdef HAS_FILTER_SSID " --filter-ssid=ssid1[,ssid2,ssid3,...]\t\t\t; per profile wifi SSID filter\n" " --filter-ssid-neg=[0|1]\t\t\t\t; invert SSID filter\n" +#endif +#ifdef __linux__ + " --filter-mark=mark[/mask]\t\t\t\t; filter by fwmark. mark and mask can be decimal or 0xHEX\n" #endif " --ipset=\t\t\t\t\t; ipset include filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)\n" " --ipset-ip=\t\t\t\t\t; comma separated fixed subnet list\n" @@ -2043,6 +2046,9 @@ enum opt_indices { #ifdef HAS_FILTER_SSID IDX_FILTER_SSID, IDX_FILTER_SSID_NEG, +#endif +#ifdef __linux__ + IDX_FILTER_MARK, #endif IDX_IPSET, IDX_IPSET_IP, @@ -2148,6 +2154,9 @@ static const struct option long_options[] = { #ifdef HAS_FILTER_SSID [IDX_FILTER_SSID] = {"filter-ssid", required_argument, 0, 0}, [IDX_FILTER_SSID_NEG] = {"filter-ssid-neg", optional_argument, 0, 0}, +#endif +#ifdef __linux__ + [IDX_FILTER_MARK] = {"filter-mark", required_argument, 0, 0}, #endif [IDX_IPSET] = {"ipset", required_argument, 0, 0}, [IDX_IPSET_IP] = {"ipset-ip", required_argument, 0, 0}, @@ -2522,7 +2531,7 @@ int main(int argc, char **argv) #endif #if defined(__linux__) || defined(SO_USER_COOKIE) params.desync_fwmark = 0; - if (sscanf(optarg, "0x%X", ¶ms.desync_fwmark) <= 0) sscanf(optarg, "%u", ¶ms.desync_fwmark); + if (sscanf(optarg, "0x%X", ¶ms.desync_fwmark)!=1) sscanf(optarg, "%u", ¶ms.desync_fwmark); if (!params.desync_fwmark) { DLOG_ERR("fwmark/sockarg should be decimal or 0xHEX and should not be zero\n"); @@ -2855,6 +2864,26 @@ int main(int argc, char **argv) dp->filter_ssid_neg = !optarg || atoi(optarg); break; #endif +#ifdef __linux__ + case IDX_FILTER_MARK: + { + char *smask = strchr(optarg,'/'); + if (smask) *smask++=0; + if (sscanf(optarg, "0x%X", &dp->filter_mark)!=1 && sscanf(optarg, "%u", &dp->filter_mark)!=1) goto fmark_err; + if (smask) + { + if (sscanf(smask, "0x%X", &dp->filter_mark_mask)!=1 && sscanf(smask, "%u", &dp->filter_mark_mask)!=1) goto fmark_err; + } + else + // no mask - use the whole 32-bit value + dp->filter_mark_mask=0xFFFFFFFF; + break; +fmark_err: + DLOG_ERR("filter mark or mask format error\n"); + exit_clean(1); + } +#endif + case IDX_IPSET: if (bSkip) break; if (!RegisterIpset(dp, false, optarg)) diff --git a/nfq2/params.h b/nfq2/params.h index 811e435..309fb75 100644 --- a/nfq2/params.h +++ b/nfq2/params.h @@ -74,6 +74,9 @@ struct desync_profile struct str_list_head filter_ssid; bool filter_ssid_neg; #endif +#ifdef __linux__ + uint32_t filter_mark,filter_mark_mask; +#endif // list of pointers to ipsets struct ipset_collection_head ips_collection, ips_collection_exclude; @@ -97,7 +100,6 @@ struct desync_profile bool b_hostlist_auto_retrans_maxseq, b_hostlist_auto_incoming_maxseq, b_hostlist_auto_retrans_reset; bool b_hostlist_auto_udp_out, b_hostlist_auto_udp_in; bool b_filter_l3, b_filter_l7; - }; #define PROFILE_NAME(dp) ((dp)->name ? (dp)->name : "noname")