Add args for UDP faking support.

This commit is contained in:
Vadim Vetrov 2024-12-06 13:45:50 +03:00
parent 7480cd31b8
commit f9a51944dd
No known key found for this signature in database
GPG Key ID: E8A308689D7A73A5
7 changed files with 324 additions and 80 deletions

View File

@ -133,7 +133,8 @@ Copy `youtubeUnblock.service` to `/usr/lib/systemd/system` (you should change th
On nftables you should put next nftables rules:
```sh
nft add chain inet fw4 youtubeUnblock '{ type filter hook postrouting priority mangle - 1; policy accept; }'
nft add rule inet fw4 youtubeUnblock 'meta l4proto { tcp, udp } th dport 443 ct original packets < 20 counter queue num 537 bypass'
nft add rule inet fw4 youtubeUnblock 'tcp dport 443 ct original packets < 20 counter queue num 537 bypass'
nft add rule inet fw4 youtubeUnblock 'meta l4proto udp ct original packets < 9 counter queue num 537 bypass'
nft insert rule inet fw4 output 'mark and 0x8000 == 0x8000 counter accept'
```
@ -143,7 +144,7 @@ On iptables you should put next iptables rules:
```sh
iptables -t mangle -N YOUTUBEUNBLOCK
iptables -t mangle -A YOUTUBEUNBLOCK -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
iptables -t mangle -A YOUTUBEUNBLOCK -p udp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
iptables -t mangle -A YOUTUBEUNBLOCK -p udp -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:8 -j NFQUEUE --queue-num 537 --queue-bypass
iptables -t mangle -A POSTROUTING -j YOUTUBEUNBLOCK
iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT
```
@ -154,7 +155,7 @@ For IPv6 on iptables you need to duplicate rules above for ip6tables:
```sh
ip6tables -t mangle -N YOUTUBEUNBLOCK
ip6tables -t mangle -A YOUTUBEUNBLOCK -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
ip6tables -t mangle -A YOUTUBEUNBLOCK -p udp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
ip6tables -t mangle -A YOUTUBEUNBLOCK -p udp -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:8 -j NFQUEUE --queue-num 537 --queue-bypass
ip6tables -t mangle -A POSTROUTING -j YOUTUBEUNBLOCK
ip6tables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT
```
@ -219,8 +220,6 @@ Available flags:
- `--frag-sni-pos=<pos>` With this option **youtubeUnblock** will split the packet at the position pos. Defaults to 1.
- `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Suitable for some TVs. Note, that for this option to work you should also add proxy udp to youtubeUnblock in firewall. `connbytes` may also be used with udp.
- `--fk-winsize=<winsize>` Specifies window size for the fragmented TCP packet. Applicable if you want for response to be fragmented. May slowdown connection initialization.
- `--synfake={1|0}` If 1, syn payload will be sent before each request. The idea is taken from syndata from zapret project. Syn payload will normally be discarded by endpoint but may be handled by TSPU. This option sends normal fake in that payload. Please note, that the option works for all the sites, so --sni-domains won't change anything.
@ -231,6 +230,18 @@ Available flags:
- `--seg2delay=<delay>` This flag forces **youtubeUnblock** to wait a little bit before send the 2nd part of the split packet.
- `--udp-mode={drop|fake}` This flag specifies udp handling strategy. If drop udp packets will be dropped (useful for quic when browser can fallback to tcp), if fake udp will be faked. Defaults to fake.
- `--udp-fake-seq-len=<amount of faking packets sent>` Specifies how much faking packets will be sent over the network. Defaults to 6.
- `--udp-fake-len=<size of udp fake>` Size of udp fake payload (typically payload is zeroes). Defaults to 64.
- `--udp-dport-filter=<5,6,200-500>` Filter the UDP destination ports. Defaults to no ports. Specifie the ports you want to be handled by youtubeUnblock.
- `--udp-filter-quic={disabled|all}` Enables QUIC filtering for UDP handler. If disabled, quic won't be processed, if all all quic initial packets will be handled. Defaults to disabled.
- `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Just an alias for `--udp-filter-quic=all --udp-mode=drop`.
- `--silent` Disables verbose mode.
- `--trace` Maximum verbosity for debugging purposes.

163
args.c
View File

@ -62,6 +62,12 @@ enum {
OPT_SILENT,
OPT_NO_GSO,
OPT_QUEUE_NUM,
OPT_UDP_MODE,
OPT_UDP_FAKE_SEQ_LEN,
OPT_UDP_FAKE_PAYLOAD_LEN,
OPT_UDP_FAKING_STRATEGY,
OPT_UDP_DPORT_FILTER,
OPT_UDP_FILTER_QUIC,
};
static struct option long_opt[] = {
@ -87,6 +93,12 @@ static struct option long_opt[] = {
{"quic-drop", 0, 0, OPT_QUIC_DROP},
{"sni-detection", 1, 0, OPT_SNI_DETECTION},
{"seg2delay", 1, 0, OPT_SEG2DELAY},
{"udp-mode", 1, 0, OPT_UDP_MODE},
{"udp-fake-seq-len", 1, 0, OPT_UDP_FAKE_SEQ_LEN},
{"udp-fake-len", 1, 0, OPT_UDP_FAKE_PAYLOAD_LEN},
{"udp-faking-strategy", 1, 0, OPT_UDP_FAKING_STRATEGY},
{"udp-dport-filter", 1, 0, OPT_UDP_DPORT_FILTER},
{"udp-filter-quic", 1, 0, OPT_UDP_FILTER_QUIC},
{"threads", 1, 0, OPT_THREADS},
{"silent", 0, 0, OPT_SILENT},
{"trace", 0, 0, OPT_TRACE},
@ -157,6 +169,12 @@ void print_usage(const char *argv0) {
printf("\t--quic-drop\n");
printf("\t--sni-detection={parse|brute}\n");
printf("\t--seg2delay=<delay>\n");
printf("\t--udp-mode={drop|fake}\n");
printf("\t--udp-fake-seq-len=<amount of faking packets sent>\n");
printf("\t--udp-fake-len=<size of upd fake>\n");
printf("\t--udp-faking-strategy={checksum|ttl}\n");
printf("\t--udp-dport-filter=<5,6,200-500>\n");
printf("\t--udp-filter-quic={disabled|all}\n");
printf("\t--threads=<threads number>\n");
printf("\t--packet-mark=<mark>\n");
printf("\t--silent\n");
@ -171,6 +189,87 @@ void print_usage(const char *argv0) {
printf("\n");
}
int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) {
int ret = 0;
int seclen = 1;
int strlen = 0;
const char *p = optarg;
while (*p != '\0') {
if (*p == ',')
seclen++;
p++;
}
strlen = p - optarg;
struct udp_dport_range *udp_dport_ranges = malloc(
seclen * sizeof(struct udp_dport_range));
int i = 0;
p = optarg;
const char *ep = p;
while (1) {
if (*ep == '\0' || *ep == ',') {
if (ep == p) {
if (*ep == '\0')
break;
p++, ep++;
continue;
}
char *endp;
long num1 = strtol(p, &endp, 10);
long num2 = num1;
if (errno)
goto erret;
if (endp != ep) {
if (*endp == '-') {
endp++;
num2 = strtol(endp, &endp, 10);
if (endp != ep || errno)
goto erret;
} else {
goto erret;
}
}
if (
!(num1 > 0 && num1 < (1 << 16)) ||
!(num2 > 0 && num2 < (1 << 16)) ||
num2 < num1
)
goto erret;
udp_dport_ranges[i] = (struct udp_dport_range){
.start = num1,
.end = num2
};
i++;
if (*ep == '\0') {
break;
} else {
p = ep + 1;
ep = p;
}
} else {
ep++;
}
}
*udpr = udp_dport_ranges;
*udpr_len = seclen;
return 0;
erret:
free(udp_dport_ranges);
return -1;
}
int parse_args(int argc, char *argv[]) {
int opt;
int optIdx = 0;
@ -443,7 +542,8 @@ int parse_args(int argc, char *argv[]) {
sect_config->seg2_delay = num;
break;
case OPT_QUIC_DROP:
sect_config->quic_drop = 1;
sect_config->udp_filter_quic = UDP_FILTER_QUIC_ALL;
sect_config->udp_mode = UDP_MODE_DROP;
break;
case OPT_SNI_DETECTION:
if (strcmp(optarg, "parse") == 0) {
@ -471,6 +571,63 @@ int parse_args(int argc, char *argv[]) {
goto invalid_opt;
}
sect_config->synfake_len = num;
break;
case OPT_UDP_MODE:
if (strcmp(optarg, "drop") == 0) {
sect_config->udp_mode = UDP_MODE_DROP;
} else if (strcmp(optarg, "fake") == 0) {
sect_config->udp_mode = UDP_MODE_FAKE;
} else {
goto invalid_opt;
}
break;
case OPT_UDP_FAKING_STRATEGY:
if (strcmp(optarg, "checksum") == 0) {
sect_config->udp_faking_strategy = FAKE_STRAT_UDP_CHECK;
} else if (strcmp(optarg, "ttl") == 0) {
sect_config->udp_faking_strategy = FAKE_STRAT_TTL;
} else {
goto invalid_opt;
}
break;
case OPT_UDP_FAKE_SEQ_LEN:
num = parse_numeric_option(optarg);
if (errno != 0 || num < 0) {
goto invalid_opt;
}
sect_config->udp_fake_seq_len = num;
break;
case OPT_UDP_FAKE_PAYLOAD_LEN:
num = parse_numeric_option(optarg);
if (errno != 0 || num < 0 || num > 1300) {
goto invalid_opt;
}
sect_config->udp_fake_len = num;
break;
case OPT_UDP_DPORT_FILTER:
{
struct udp_dport_range *udp_dport_range;
int udp_range_len = 0;
if (parse_udp_dport_range(optarg, &udp_dport_range, &udp_range_len) < 0) {
goto invalid_opt;
}
sect_config->udp_dport_range = udp_dport_range;
sect_config->udp_dport_range_len = udp_range_len;
break;
}
case OPT_UDP_FILTER_QUIC:
if (strcmp(optarg, "disabled") == 0) {
sect_config->udp_filter_quic = UDP_FILTER_QUIC_DISABLED;
} else if (strcmp(optarg, "all") == 0) {
sect_config->udp_filter_quic = UDP_FILTER_QUIC_ALL;
} else {
goto invalid_opt;
}
break;
default:
goto error;
@ -490,7 +647,7 @@ invalid_opt:
error:
print_usage(argv[0]);
errno = EINVAL;
return -1;
return -errno;
}
void print_welcome() {
@ -575,7 +732,7 @@ void print_welcome() {
lginfo("Fake SYN payload will be sent with each TCP request SYN packet\n");
}
if (section->quic_drop) {
if (section->udp_filter_quic && section->udp_mode == UDP_MODE_DROP) {
lginfo("All QUIC packets will be dropped\n");
}

View File

@ -6,6 +6,7 @@
#endif
#include "raw_replacements.h"
#include "types.h"
typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len);
/**
@ -20,6 +21,11 @@ struct instance_config_t {
};
extern struct instance_config_t instance_config;
struct udp_dport_range {
uint16_t start;
uint16_t end;
};
struct section_config_t {
const char *domains_str;
unsigned int domains_strlen;
@ -40,8 +46,6 @@ struct section_config_t {
#define FAKE_PAYLOAD_DEFAULT 2
int fake_sni_type;
int quic_drop;
/* In milliseconds */
unsigned int seg2_delay;
int synfake;
@ -64,6 +68,14 @@ struct section_config_t {
#define SNI_DETECTION_BRUTE 1
int sni_detection;
int udp_mode;
unsigned int udp_fake_seq_len;
unsigned int udp_fake_len;
int udp_faking_strategy;
struct udp_dport_range *udp_dport_range;
int udp_dport_range_len;
int udp_filter_quic;
};
#define MAX_CONFIGLIST_LEN 64
@ -96,37 +108,6 @@ for (struct section_config_t *section = &config.default_config + config.custom_c
#define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config)
#define default_section_config { \
.frag_sni_reverse = 1, \
.frag_sni_faked = 0, \
.fragmentation_strategy = FRAGMENTATION_STRATEGY, \
.faking_strategy = FAKING_STRATEGY, \
.faking_ttl = FAKE_TTL, \
.fake_sni = 1, \
.fake_sni_seq_len = 1, \
.fake_sni_type = FAKE_PAYLOAD_DEFAULT, \
.frag_middle_sni = 1, \
.frag_sni_pos = 1, \
.fakeseq_offset = 10000, \
.synfake = 0, \
.synfake_len = 0, \
.quic_drop = 0, \
\
.seg2_delay = 0, \
\
.domains_str = defaul_snistr, \
.domains_strlen = sizeof(defaul_snistr), \
\
.exclude_domains_str = "", \
.exclude_domains_strlen = 0, \
\
.fake_sni_pkt = fake_sni_old, \
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \
.fake_custom_pkt = custom_fake_buf, \
.fake_custom_pkt_sz = 0, \
.sni_detection = SNI_DETECTION_PARSE, \
}
#define MAX_THREADS 16
#ifndef THREADS_NUM
@ -199,4 +180,52 @@ if ((fake_bitmask) & strategy)
static const char defaul_snistr[] = DEFAULT_SNISTR;
enum {
UDP_MODE_DROP,
UDP_MODE_FAKE,
};
enum {
UDP_FILTER_QUIC_DISABLED,
UDP_FILTER_QUIC_ALL,
};
#define default_section_config { \
.frag_sni_reverse = 1, \
.frag_sni_faked = 0, \
.fragmentation_strategy = FRAGMENTATION_STRATEGY, \
.faking_strategy = FAKING_STRATEGY, \
.faking_ttl = FAKE_TTL, \
.fake_sni = 1, \
.fake_sni_seq_len = 1, \
.fake_sni_type = FAKE_PAYLOAD_DEFAULT, \
.frag_middle_sni = 1, \
.frag_sni_pos = 1, \
.fakeseq_offset = 10000, \
.synfake = 0, \
.synfake_len = 0, \
\
.seg2_delay = 0, \
\
.domains_str = defaul_snistr, \
.domains_strlen = sizeof(defaul_snistr), \
\
.exclude_domains_str = "", \
.exclude_domains_strlen = 0, \
\
.fake_sni_pkt = fake_sni_old, \
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \
.fake_custom_pkt = custom_fake_buf, \
.fake_custom_pkt_sz = 0, \
.sni_detection = SNI_DETECTION_PARSE, \
\
.udp_mode = UDP_MODE_FAKE, \
.udp_fake_seq_len = 6, \
.udp_fake_len = 64, \
.udp_faking_strategy = FAKE_STRAT_UDP_CHECK, \
.udp_dport_range = NULL, \
.udp_dport_range_len = 0, \
.udp_filter_quic = UDP_FILTER_QUIC_DISABLED, \
}
#endif /* YTB_CONFIG_H */

View File

@ -161,7 +161,7 @@ static const struct kernel_param_ops exclude_domains_ops = {
module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664);
module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664);
module_param_cb(quic_drop, &boolean_parameter_ops, &def_section->quic_drop, 0664);
// module_param_cb(quic_drop, &boolean_parameter_ops, &def_section->quic_drop, 0664);
static int verbosity_set(const char *val, const struct kernel_param *kp) {
size_t len;

View File

@ -363,43 +363,13 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk
}
if (!detect_udp_filtered(section, pkt, pktlen))
goto continue_flow;
if (section->quic_drop) {
lgtrace_addp("QUIC probe");
const struct quic_lhdr *qch;
uint32_t qch_len;
struct quic_cids qci;
uint8_t *quic_raw_payload;
uint32_t quic_raw_plen;
ret = quic_parse_data((uint8_t *)data, dlen,
(struct quic_lhdr **)&qch, &qch_len, &qci,
&quic_raw_payload, &quic_raw_plen);
if (ret < 0) {
lgtrace_addp("undefined type");
goto accept_quic;
}
lgtrace_addp("QUIC detected");
uint8_t qtype = qch->type;
if (section->udp_mode == UDP_MODE_DROP)
goto drop;
if (qch->version == QUIC_V1)
qtype = quic_convtype_v1(qtype);
else if (qch->version == QUIC_V2)
qtype = quic_convtype_v2(qtype);
if (qtype != QUIC_INITIAL_TYPE) {
lgtrace_addp("quic message type: %d", qtype);
goto accept_quic;
}
lgtrace_addp("quic initial message");
}
if (1) {
for (int i = 0; i < 6; i++) {
else if (section->udp_mode == UDP_MODE_FAKE) {
for (int i = 0; i < section->udp_fake_seq_len; i++) {
NETBUF_ALLOC(fake_udp, MAX_PACKET_SIZE);
if (!NETBUF_CHECK(fake_udp)) {
lgerror(-ENOMEM, "Allocation error");
@ -408,9 +378,9 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk
uint32_t fsn_len = MAX_PACKET_SIZE;
struct udp_fake_type fake_type = {
.fake_len = 64,
.fake_len = section->udp_fake_len,
.strategy = {
.strategy = FAKE_STRAT_UDP_CHECK,
.strategy = section->udp_faking_strategy,
},
};
ret = gen_fake_udp(fake_type, iph, iph_len, udph, fake_udp, &fsn_len);

76
quic.c
View File

@ -179,7 +179,7 @@ int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint
set_ip_checksum(iph, iph_len);
if (strategy.strategy == FAKE_STRAT_UDP_CHECK) {
lgtrace_addp("break fake tcp checksum");
lgtrace_addp("break fake udp checksum");
udph->check += 1;
}
@ -247,3 +247,77 @@ int gen_fake_udp(struct udp_fake_type type,
return 0;
}
int detect_udp_filtered(const struct section_config_t *section,
const uint8_t *payload, uint32_t plen) {
const void *iph;
uint32_t iph_len;
const struct udphdr *udph;
const uint8_t *data;
uint32_t dlen;
int ret;
int ipver;
ipver = netproto_version(payload, plen);
ret = udp_payload_split((uint8_t *)payload, plen,
(void **)&iph, &iph_len,
(struct udphdr **)&udph,
(uint8_t **)&data, &dlen);
int udp_dport = ntohs(udph->dest);
lgtrace_addp("UDP dport: %d", udp_dport);
if (ret < 0) {
goto skip;
}
if (section->udp_filter_quic) {
const struct quic_lhdr *qch;
uint32_t qch_len;
struct quic_cids qci;
uint8_t *quic_raw_payload;
uint32_t quic_raw_plen;
lgtrace_addp("QUIC probe");
ret = quic_parse_data((uint8_t *)data, dlen,
(struct quic_lhdr **)&qch, &qch_len, &qci,
&quic_raw_payload, &quic_raw_plen);
if (ret < 0) {
lgtrace_addp("undefined type");
goto skip;
}
lgtrace_addp("QUIC detected");
uint8_t qtype = qch->type;
goto approve;
// if (qch->version == QUIC_V1)
// qtype = quic_convtype_v1(qtype);
// else if (qch->version == QUIC_V2)
// qtype = quic_convtype_v2(qtype);
//
// if (qtype != QUIC_INITIAL_TYPE) {
// lgtrace_addp("quic message type: %d", qtype);
// goto accept_quic;
// }
//
// lgtrace_addp("quic initial message");
}
for (int i = 0; i < section->udp_dport_range_len; i++) {
struct udp_dport_range crange = section->udp_dport_range[i];
if (udp_dport >= crange.start && udp_dport <= crange.end) {
lgtrace_addp("matched to %d-%d", crange.start, crange.end);
goto approve;
}
}
skip:
return 0;
approve:
return 1;
}

3
quic.h
View File

@ -135,4 +135,7 @@ int gen_fake_udp(struct udp_fake_type type,
const struct udphdr *udph,
uint8_t *buf, uint32_t *buflen);
int detect_udp_filtered(const struct section_config_t *section,
const uint8_t *payload, uint32_t plen);
#endif /* QUIC_H */