diff --git a/Kbuild b/Kbuild index 36eef22..3dbbfe3 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o -kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o getopt.o args.o ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/README.md b/README.md index 12b99f7..7a1ae50 100644 --- a/README.md +++ b/README.md @@ -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=` 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=` 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,20 @@ Available flags: - `--seg2delay=` 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=` Specifies how much faking packets will be sent over the network. Defaults to 6. + +- `--udp-fake-len=` 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`. + +- `--tls={enabled|disabled}` Set it if you want not to process TLS traffic in current section. May be used if you want to set only UDP-based section. (Here section is a unit between `--fbegin` and `--fend` flags). + - `--silent` Disables verbose mode. - `--trace` Maximum verbosity for debugging purposes. @@ -241,6 +254,8 @@ Available flags: - `--threads=` Specifies the amount of threads you want to be running for your program. This defaults to **1** and shouldn't be edited for normal use. But if you really want multiple queue instances of youtubeUnblock, note that you should change --queue-num to --queue balance. For example, with 4 threads, use `--queue-balance 537:540` on iptables and `queue num 537-540` on nftables. +- `--connbytes-limit=` **Kernel module only!** Specify how much packets of connection should be processed by kyoutubeUnblock. Pass 0 if you want for each packet to be processed. This flag may be useful for UDP traffic since unlimited youtubeUnblock may lead to traffic flood and unexpected bans. Defaults to 5. In most cases you don't want to change it. + - `--daemonize` Daemonizes the youtubeUnblock (forks and detaches it from the shell). Terminate the program with `killall youtubeUnblock`. If you want to track the logs of youtubeUnblock in logread or journalctl, use **--syslog** flag. - `--syslog` Redirects logs to the system log. You can read it with `journalctl` or `logread`. @@ -249,7 +264,7 @@ Available flags: - `--packet-mark=` Use this option if youtubeUnblock conflicts with other systems rely on packet mark. Note that you may want to change accept rule for iptables to follow the mark. -- `--fbegin` and `--fend` flags: youtubeUnblock supports multiple sets of strategies for specific filters. You may want to initiate a new set after the default one, like: `--sni-domains=googlevideo.com --faking-strategy=md5sum --fbegin --sni-domains=youtube.com --faking-strategy=tcp_check --fend --fbegin --sni-domains=l.google.com --faking-strategy=pastseq --fend`. Note, that the priority of these sets goes backwards: last is first, default (one that does not start with --fbegin) is last. If you start the new section, the default settings are implemented just like youtubeUnblock without any parameters. Note that the config above is just an example and won't work for you. +- `--fbegin` and `--fend` flags: youtubeUnblock supports multiple sets of strategies for specific filters. You may want to initiate a new set after the default one, like: `--sni-domains=googlevideo.com --faking-strategy=md5sum --fbegin --sni-domains=youtube.com --faking-strategy=tcp_check --fbegin --sni-domains=l.google.com --faking-strategy=pastseq`. Note, that the priority of these sets goes backwards: last is first, default (one that does not start with --fbegin) is last. If you start the new section, the default settings are implemented just like youtubeUnblock without any parameters. Note that the config above is just an example and won't work for you. ## Troubleshooting @@ -343,24 +358,20 @@ When compilation is done, the binary file will be in build directory. Copy it to This section describes the kernel module version of youtubeUnblock. The kernel module operates as a normal module inside the kernel and integrates within the netfilter stack to statelessly mangle the packets sent over the Internet. -You can configure the module with its flags in insmod: -``` -insmod kyoutubeUnblock.ko fake_sni=1 exclude_domains=.ru quic_drop=1 -``` +You can configure the module with its flags: -Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`quic_drop=1`) - -Also a good thig to mention is verbosity. The kernel module combines --trace and --silent option to the one parameter `verbosity`. This parameter accepts 3 arguments: `trace`, `debug` and `silent`. I highly don't recommend to enable `trace` mod on router because it may cause huge problems with performance and even freeze your device. - -Also a drop in replacement is supported for all the parameters excluding packet mark. A drop in replacement does not require module restart if you want to change the parameters. You can specify and check the parameters within module's directory inside the sysfs: `/sys/module/kyoutubeUnblock/parameters/`. For example, to set quic_drop to true you may use next command: ```sh -echo 1 | sudo tee /sys/module/kyoutubeUnblock/parameters/quic_drop +insmod kyoutubeUnblock.ko +echo "--fake_sni=1 --exclude_domains=.ru --quic_drop" | sudo tee /sys/module/kyoutubeUnblock/parameters/parameters ``` -and + +You can also do + ```sh -cat /sys/module/kyoutubeUnblock/parameters/quic_drop +cat /sys/module/kyoutubeUnblock/parameters/parameters ``` -to check the parameter. + +and check all the parameters configured. ### Building kernel module diff --git a/args.c b/args.c index 3d97d59..38c6c70 100644 --- a/args.c +++ b/args.c @@ -1,33 +1,217 @@ #include "config.h" -#include -#include -#include -#include -#include -#include -#include +#include "types.h" + #include "types.h" #include "args.h" #include "logging.h" +#include "getopt.h" +#include "raw_replacements.h" -static char custom_fake_buf[MAX_FAKE_SIZE]; +#ifdef KERNEL_SPACE +static int errno = 0; +#define strtol kstrtol +#endif -struct config_t config = { - .threads = THREADS_NUM, - .queue_start_num = DEFAULT_QUEUE_NUM, - .mark = DEFAULT_RAWSOCKET_MARK, - .use_ipv6 = 1, +struct config_t config = default_config_set; - .verbose = VERBOSE_DEBUG, - .use_gso = true, +static int parse_sni_domains(struct domains_list **dlist, const char *domains_str, size_t domains_strlen) { + // Empty and shouldn't be used + struct domains_list ndomain = {0}; + struct domains_list *cdomain = &ndomain; - .default_config = default_section_config, - .custom_configs_len = 0, + unsigned int j = 0; + for (unsigned int i = 0; i <= domains_strlen; i++) { + if (( i == domains_strlen || + domains_str[i] == '\0' || + domains_str[i] == ',' || + domains_str[i] == '\n' )) { - .daemonize = 0, - .noclose = 0, - .syslog = 0, -}; + if (i == j) { + j++; + continue; + } + + unsigned int domain_len = (i - j); + const char *domain_startp = domains_str + j; + struct domains_list *edomain = malloc(sizeof(struct domains_list)); + *edomain = (struct domains_list){0}; + if (edomain == NULL) { + return -ENOMEM; + } + + edomain->domain_len = domain_len; + edomain->domain_name = malloc(domain_len + 1); + if (edomain->domain_name == NULL) { + return -ENOMEM; + } + + strncpy(edomain->domain_name, domain_startp, domain_len); + edomain->domain_name[domain_len] = '\0'; + cdomain->next = edomain; + cdomain = edomain; + + j = i + 1; + } + } + + *dlist = ndomain.next; + return 0; +} + +static void free_sni_domains(struct domains_list *dlist) { + for (struct domains_list *ldl = dlist; ldl != NULL;) { + struct domains_list *ndl = ldl->next; + SFREE(ldl->domain_name); + SFREE(ldl); + ldl = ndl; + } +} + +static long parse_numeric_option(const char* value) { + errno = 0; + + if (*value == '\0') { + errno = EINVAL; + return 0; + } + + long result; + int len; + sscanf(value, "%ld%n", &result, &len); + if (*(value + len) != '\0') { + errno = EINVAL; + return 0; + } + + return result; +} + +static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) { + int seclen = 1; + const char *p = str; + while (*p != '\0') { + if (*p == ',') + seclen++; + p++; + } + +#ifdef KERNEL_SPACE + struct udp_dport_range *udp_dport_ranges = kmalloc( + seclen * sizeof(struct udp_dport_range), GFP_KERNEL); + +#else + struct udp_dport_range *udp_dport_ranges = malloc( + seclen * sizeof(struct udp_dport_range)); +#endif + if (udp_dport_ranges == NULL) { + return -ENOMEM; + } + + int i = 0; + + + p = str; + const char *ep = p; + while (1) { + if (*ep == '\0' || *ep == ',') { + if (ep == p) { + if (*ep == '\0') + break; + + p++, ep++; + continue; + } + + const char *endp; + long num1; + int len; + sscanf(p, "%ld%n", &num1, &len); + endp = p + len; + long num2 = num1; + + if (endp != ep) { + if (*endp == '-') { + endp++; + int len; + sscanf(endp, "%ld%n", &num2, &len); + endp = endp + len; + + if (endp != ep) + 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++; + } + } + + if (i == 0) { + free(udp_dport_ranges); + } + + *udpr = udp_dport_ranges; + *udpr_len = i; + return 0; + +erret: + free(udp_dport_ranges); + + return -1; +} + +// Allocates and fills custom fake buffer +static int parse_fake_custom_payload( + const char *custom_hex_fake, + char **custom_fake_buf, unsigned int *custom_fake_len) { + int ret; + + size_t custom_hlen = strlen(custom_hex_fake); + if ((custom_hlen & 1) == 1) { + printf("Custom fake hex should be divisible by two\n"); + return -EINVAL; + } + + size_t custom_len = custom_hlen >> 1; + if (custom_len > MAX_FAKE_SIZE) { + printf("Custom fake is too large\n"); + return -EINVAL; + } + unsigned char *custom_buf = malloc(custom_len); + + for (int i = 0; i < custom_len; i++) { + ret = sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); + if (ret != 1) { + free(custom_buf); + return -EINVAL; + } + } + + *custom_fake_buf = (char *)custom_buf; + *custom_fake_len = custom_len; + return 0; +} enum { OPT_SNI_DOMAINS, @@ -62,16 +246,28 @@ 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, + OPT_TLS_ENABLED, + OPT_CLS, + OPT_HELP, + OPT_VERSION, + OPT_CONNBYTES_LIMIT, }; static struct option long_opt[] = { - {"help", 0, 0, 'h'}, - {"version", 0, 0, 'v'}, + {"help", 0, 0, OPT_HELP}, + {"version", 0, 0, OPT_VERSION}, {"sni-domains", 1, 0, OPT_SNI_DOMAINS}, {"exclude-domains", 1, 0, OPT_EXCLUDE_DOMAINS}, {"fake-sni", 1, 0, OPT_FAKE_SNI}, {"synfake", 1, 0, OPT_SYNFAKE}, {"synfake-len", 1, 0, OPT_SYNFAKE_LEN}, + {"tls", 1, 0, OPT_TLS_ENABLED}, {"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN}, {"fake-sni-type", 1, 0, OPT_FAKE_SNI_TYPE}, {"fake-custom-payload", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD}, @@ -87,6 +283,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}, @@ -97,30 +299,14 @@ static struct option long_opt[] = { {"syslog", 0, 0, OPT_SYSLOG}, {"queue-num", 1, 0, OPT_QUEUE_NUM}, {"packet-mark", 1, 0, OPT_PACKET_MARK}, + {"connbytes-limit", 1, 0, OPT_CONNBYTES_LIMIT}, {"fbegin", 0, 0, OPT_START_SECTION}, {"fend", 0, 0, OPT_END_SECTION}, - {0,0,0,0} + {"cls", 0, 0, OPT_CLS}, + {0, 0, 0, 0}, }; -static long parse_numeric_option(const char* value) { - errno = 0; - - if (*value == '\0') { - errno = EINVAL; - return 0; - } - - char* end; - long result = strtol(value, &end, 10); - if (*end != '\0') { - errno = EINVAL; - return 0; - } - - return result; -} - -void print_version() { +void print_version(void) { printf("youtubeUnblock" #if defined(PKG_VERSION) " " PKG_VERSION @@ -139,6 +325,7 @@ void print_usage(const char *argv0) { printf("\t--queue-num=\n"); printf("\t--sni-domains=|all\n"); printf("\t--exclude-domains=\n"); + printf("\t--tls={enabled|disabled}\n"); printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni-seq-len=\n"); printf("\t--fake-sni-type={default|random|custom}\n"); @@ -157,8 +344,15 @@ void print_usage(const char *argv0) { printf("\t--quic-drop\n"); printf("\t--sni-detection={parse|brute}\n"); printf("\t--seg2delay=\n"); + printf("\t--udp-mode={drop|fake}\n"); + printf("\t--udp-fake-seq-len=\n"); + printf("\t--udp-fake-len=\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=\n"); printf("\t--packet-mark=\n"); + printf("\t--connbytes-limit=\n"); printf("\t--silent\n"); printf("\t--trace\n"); printf("\t--no-gso\n"); @@ -171,12 +365,22 @@ void print_usage(const char *argv0) { printf("\n"); } -int parse_args(int argc, char *argv[]) { +int yparse_args(int argc, char *argv[]) { int opt; int optIdx = 0; + optind=1, opterr=1, optreset=0; long num; + int ret; - struct section_config_t *sect_config = &config.default_config; + struct config_t rep_config; + ret = init_config(&rep_config); + if (ret < 0) + return ret; + struct section_config_t *default_section = rep_config.last_section; + + struct section_config_t *sect_config = rep_config.last_section; + int sect_i = 0; + sect_config->id = sect_i++; #define SECT_ITER_DEFAULT 1 #define SECT_ITER_INSIDE 2 @@ -184,109 +388,140 @@ int parse_args(int argc, char *argv[]) { int section_iter = SECT_ITER_DEFAULT; - while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) { + while ((opt = getopt_long(argc, argv, "", long_opt, &optIdx)) != -1) { switch (opt) { + case OPT_CLS: + free_config(rep_config); + ret = init_config(&rep_config); + if (ret < 0) + return ret; + default_section = rep_config.last_section; + + sect_config = rep_config.last_section; + sect_i = 0; + sect_config->id = sect_i++; + section_iter = SECT_ITER_DEFAULT; + + break; + /* config_t scoped configs */ - case 'h': + case OPT_HELP: print_usage(argv[0]); +#ifndef KERNEL_SPACE goto stop_exec; - case 'v': +#else + break; +#endif + case OPT_VERSION: print_version(); +#ifndef KERNEL_SPACE goto stop_exec; +#else + break; +#endif case OPT_TRACE: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - config.verbose = 2; + rep_config.verbose = 2; break; case OPT_SILENT: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.verbose = 0; + rep_config.verbose = 0; break; case OPT_NO_GSO: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.use_gso = 0; + rep_config.use_gso = 0; break; case OPT_NO_IPV6: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.use_ipv6 = 0; + rep_config.use_ipv6 = 0; break; case OPT_DAEMONIZE: - config.daemonize = 1; + rep_config.daemonize = 1; break; case OPT_NOCLOSE: - config.noclose = 1; + rep_config.noclose = 1; break; case OPT_SYSLOG: - config.syslog = 1; + rep_config.syslog = 1; break; case OPT_THREADS: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > MAX_THREADS) { goto invalid_opt; } - config.threads = num; + rep_config.threads = num; break; case OPT_QUEUE_NUM: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } - config.queue_start_num = num; + rep_config.queue_start_num = num; break; case OPT_PACKET_MARK: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } - config.mark = num; + rep_config.mark = num; break; - case OPT_START_SECTION: - if (section_iter != SECT_ITER_DEFAULT && section_iter != SECT_ITER_OUTSIDE) + case OPT_CONNBYTES_LIMIT: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0) { goto invalid_opt; - - sect_config = &config.custom_configs[config.custom_configs_len++]; - *sect_config = (struct section_config_t)default_section_config; + } + rep_config.connbytes_limit = num; + break; + case OPT_START_SECTION: + { + struct section_config_t *nsect; + ret = init_section_config(&nsect, rep_config.last_section); + if (ret < 0) { + goto error; + } + rep_config.last_section->next = nsect; + rep_config.last_section = nsect; + sect_config = nsect; + sect_config->id = sect_i++; section_iter = SECT_ITER_INSIDE; break; + } case OPT_END_SECTION: if (section_iter != SECT_ITER_INSIDE) goto invalid_opt; section_iter = SECT_ITER_OUTSIDE; - sect_config = &config.default_config; + sect_config = default_section; break; /* section_config_t scoped configs */ + case OPT_TLS_ENABLED: + if (strcmp(optarg, "enabled") == 0) { + sect_config->tls_enabled = 1; + } else if (strcmp(optarg, "disabled") == 0) { + sect_config->tls_enabled = 0; + } else { + goto invalid_opt; + } + + break; case OPT_SNI_DOMAINS: + free_sni_domains(sect_config->sni_domains); + sect_config->all_domains = 0; if (!strcmp(optarg, "all")) { sect_config->all_domains = 1; } - sect_config->domains_str = optarg; - sect_config->domains_strlen = strlen(sect_config->domains_str); + ret = parse_sni_domains(§_config->sni_domains, optarg, strlen(optarg)); + if (ret < 0) + goto error; break; case OPT_EXCLUDE_DOMAINS: - sect_config->exclude_domains_str = optarg; - sect_config->exclude_domains_strlen = strlen(sect_config->exclude_domains_str); + free_sni_domains(sect_config->exclude_sni_domains); + ret = parse_sni_domains(§_config->exclude_sni_domains, optarg, strlen(optarg)); + if (ret < 0) + goto error; + break; case OPT_FRAG: if (strcmp(optarg, "tcp") == 0) { @@ -400,30 +635,16 @@ int parse_args(int argc, char *argv[]) { } break; - case OPT_FAKE_CUSTOM_PAYLOAD: { - uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; + case OPT_FAKE_CUSTOM_PAYLOAD: + SFREE(sect_config->fake_custom_pkt); - const char *custom_hex_fake = optarg; - size_t custom_hlen = strlen(custom_hex_fake); - if ((custom_hlen & 1) == 1) { - printf("Custom fake hex should be divisible by two\n"); - goto invalid_opt; - } - - - size_t custom_len = custom_hlen >> 1; - if (custom_len > MAX_FAKE_SIZE) { - printf("Custom fake is too large\n"); - goto invalid_opt; - } - - for (int i = 0; i < custom_len; i++) { - sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); - } - - sect_config->fake_custom_pkt_sz = custom_len; - sect_config->fake_custom_pkt = (char *)custom_buf; + ret = parse_fake_custom_payload(optarg, §_config->fake_custom_pkt, §_config->fake_custom_pkt_sz); + if (ret == -EINVAL) { + goto invalid_opt; + } else if (ret < 0) { + goto error; } + break; case OPT_FK_WINSIZE: num = parse_numeric_option(optarg); @@ -443,7 +664,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 +693,60 @@ 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: + { + SFREE(sect_config->udp_dport_range); + if (parse_udp_dport_range(optarg, §_config->udp_dport_range, §_config->udp_dport_range_len) < 0) { + goto invalid_opt; + } + 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; @@ -478,116 +754,322 @@ int parse_args(int argc, char *argv[]) { } + struct config_t old_config = config; + config = rep_config; + free_config(old_config); errno = 0; return 0; + +#ifndef KERNEL_SPACE stop_exec: + free_config(rep_config); errno = 0; return 1; +#endif invalid_opt: printf("Invalid option %s\n", long_opt[optIdx].name); + ret = -EINVAL; error: +#ifndef KERNEL_SPACE print_usage(argv[0]); - errno = EINVAL; - return -1; +#endif + if (ret != -EINVAL) { + lgerror(ret, "Error thrown in %s\n", long_opt[optIdx].name); + } + + errno = -ret; + free_config(rep_config); + return -errno; } -void print_welcome() { - if (config.syslog) { - printf("Logging to system log\n"); - } - if (config.use_gso) { - lginfo("GSO is enabled\n"); - } +#define print_cnf_raw(fmt, ...) do { \ + sz = snprintf(buf_ptr, buf_sz, fmt, ##__VA_ARGS__); \ + if (sz > buf_sz) { buf_sz = 0; } \ + else { buf_sz -= sz; } \ + buf_ptr += sz; \ +} while(0) - if (config.use_ipv6) { - lginfo("IPv6 is enabled\n"); - } else { - lginfo("IPv6 is disabled\n"); - } - - lginfo("Detected %d config sections\n", config.custom_configs_len + 1); - lginfo("The sections will be processed in order they goes in this output\n"); +#define print_cnf_buf(fmt, ...) print_cnf_raw(fmt " ", ##__VA_ARGS__) +// Returns written buffer size +static size_t print_config_section(const struct section_config_t *section, char *buffer, size_t buffer_size) { + char *buf_ptr = buffer; + size_t buf_sz = buffer_size; + size_t sz; - ITER_CONFIG_SECTIONS(section) { - int section_number = CONFIG_SECTION_NUMBER(section); - lginfo("Section #%d\n", section_number); - - switch (section->fragmentation_strategy) { - case FRAG_STRAT_TCP: - lginfo("Using TCP segmentation\n"); - break; - case FRAG_STRAT_IP: - lginfo("Using IP fragmentation\n"); - break; - default: - lginfo("SNI fragmentation is disabled\n"); - break; + if (section->tls_enabled) { + print_cnf_buf("--tls=enabled"); + if (section->sni_domains != NULL) { + print_cnf_raw("--sni-domains="); + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + print_cnf_raw("%s,", sne->domain_name); + } + print_cnf_raw(" "); + } + if (section->exclude_sni_domains != NULL) { + print_cnf_raw("--exclude-domains="); + for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) { + print_cnf_raw("%s,", sne->domain_name); + } + print_cnf_raw(" "); } - if (section->seg2_delay) { - lginfo("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", section->seg2_delay); + switch(section->fragmentation_strategy) { + case FRAG_STRAT_IP: + print_cnf_buf("--frag=ip"); + break; + case FRAG_STRAT_TCP: + print_cnf_buf("--frag=tcp"); + break; + case FRAG_STRAT_NONE: + print_cnf_buf("--frag=none"); + break; } + print_cnf_buf("frag-sni-reverse=%d", section->frag_sni_reverse); + print_cnf_buf("frag-sni-faked=%d", section->frag_sni_faked); + print_cnf_buf("frag-middle-sni=%d", section->frag_middle_sni); + print_cnf_buf("frag-sni-pos=%d", section->frag_sni_pos); + print_cnf_buf("fk-winsize=%d", section->fk_winsize); + if (section->fake_sni) { - lginfo("Fake SNI will be sent before each target client hello\n"); - } else { - lginfo("Fake SNI is disabled\n"); - } + print_cnf_buf("--fake-sni=1"); + print_cnf_buf("--fake-sni-seq-len=%d", section->fake_sni_seq_len); + switch(section->fake_sni_type) { + case FAKE_PAYLOAD_CUSTOM: + print_cnf_buf("--fake-sni-type=custom"); + print_cnf_buf("--fake-custom-payload="); + break; + case FAKE_PAYLOAD_RANDOM: + print_cnf_buf("--fake-sni-type=random"); + break; + case FAKE_PAYLOAD_DEFAULT: + print_cnf_buf("--fake-sni-type=default"); + break; + } - if (section->frag_sni_reverse) { - lginfo("Fragmentation Client Hello will be reversed\n"); - } - - if (section->frag_sni_faked) { - lginfo("Fooling packets will be sent near the original Client Hello\n"); - } - - if (section->fake_sni_seq_len > 1) { - lginfo("Faking sequence of length %d will be built as fake sni\n", section->fake_sni_seq_len); - } - - switch (section->faking_strategy) { + switch(section->faking_strategy) { case FAKE_STRAT_TTL: - lginfo("TTL faking strategy will be used with TTL %d\n", section->faking_ttl); + print_cnf_buf("--faking-strategy=ttl"); + print_cnf_buf("--faking-ttl=%d", section->faking_ttl); break; case FAKE_STRAT_RAND_SEQ: - lginfo("Random seq faking strategy will be used\n"); - lginfo("Fake seq offset set to %u\n", section->fakeseq_offset); + print_cnf_buf("--faking-strategy=randseq"); break; case FAKE_STRAT_TCP_CHECK: - lginfo("TCP checksum faking strategy will be used\n"); - break; - case FAKE_STRAT_PAST_SEQ: - lginfo("Past seq faking strategy will be used\n"); + print_cnf_buf("--faking-strategy=tcp_check"); break; case FAKE_STRAT_TCP_MD5SUM: - lginfo("md5sum faking strategy will be used\n"); + print_cnf_buf("--faking-strategy=md5sum"); + break; + case FAKE_STRAT_PAST_SEQ: + print_cnf_buf("--faking-strategy=pastseq"); + print_cnf_buf("--fake-seq-offset=%d", section->fakeseq_offset); break; - } - if (section->fk_winsize) { - lginfo("Response TCP window will be set to %d with the appropriate scale\n", section->fk_winsize); - } + } - if (section->synfake) { - lginfo("Fake SYN payload will be sent with each TCP request SYN packet\n"); - } + switch(section->sni_detection) { + case SNI_DETECTION_BRUTE: + print_cnf_buf("--sni_detection=brute"); + break; + case SNI_DETECTION_PARSE: + print_cnf_buf("--sni_detection=parse"); + break; - if (section->quic_drop) { - lginfo("All QUIC packets will be dropped\n"); - } + } - if (section->sni_detection == SNI_DETECTION_BRUTE) { - lginfo("Server Name Extension will be parsed in the bruteforce mode\n"); + print_cnf_buf("--seg2delay=%d", section->seg2_delay); } + } else { + print_cnf_buf("--tls=disabled"); + } - if (section->all_domains) { - lginfo("All Client Hello will be targeted by youtubeUnblock!\n"); - } else { - lginfo("Target sni domains: %s\n", section->domains_str); + if (section->synfake) { + print_cnf_buf("--synfake=1"); + print_cnf_buf("--synfake-len=%d", section->synfake_len); + } else { + print_cnf_buf("--synfake=0"); + } + + + if (section->udp_filter_quic == UDP_FILTER_QUIC_ALL && section->udp_mode == UDP_MODE_DROP) { + print_cnf_buf("--drop-quic"); + } + + switch(section->udp_filter_quic) { + case UDP_FILTER_QUIC_ALL: + print_cnf_buf("--udp-filter-quic=all"); + break; + case UDP_FILTER_QUIC_DISABLED: + print_cnf_buf("--udp-filter-quic=disabled"); + break; + } + + if (section->udp_dport_range_len != 0) { + + print_cnf_raw("--udp-dport-filter="); + for (int i = 0; i < section->udp_dport_range_len; i++) { + struct udp_dport_range range = section->udp_dport_range[i]; + print_cnf_raw("%d-%d,", range.start, range.end); + } + print_cnf_raw(" "); + + } + + + if (section->udp_filter_quic != UDP_FILTER_QUIC_DISABLED || section->udp_dport_range_len != 0) { + switch(section->udp_mode) { + case UDP_MODE_DROP: + print_cnf_buf("--udp-mode=drop"); + break; + case UDP_MODE_FAKE: + print_cnf_buf("--udp-mode=fake"); + print_cnf_buf("--udp-fake-seq-len=%d", section->udp_fake_seq_len); + { + switch(section->udp_faking_strategy) { + case FAKE_STRAT_UDP_CHECK: + print_cnf_buf("--udp-faking-strategy=checksum"); + break; + case FAKE_STRAT_TTL: + print_cnf_buf("--udp-faking-strategy=ttl"); + } + } + break; } } + + return buffer_size - buf_sz; +} +// Returns written buffer length +size_t print_config(char *buffer, size_t buffer_size) { + char *buf_ptr = buffer; + size_t buf_sz = buffer_size; + size_t sz; + +#ifndef KERNEL_SPACE + print_cnf_buf("--queue-num=%d", config.queue_start_num); + print_cnf_buf("--threads=%d", config.threads); +#endif + print_cnf_buf("--mark=%d", config.mark); + +#ifndef KERNEL_SPACE + if (config.daemonize) { + print_cnf_buf("--daemonize"); + } + if (config.syslog) { + print_cnf_buf("--syslog"); + } + if (config.noclose) { + print_cnf_buf("--noclose"); + } + if (!config.use_gso) { + print_cnf_buf("--no-gso"); + } +#endif + +#ifdef KERNEL_SPACE + print_cnf_buf("--connbytes-limit=%d", config.connbytes_limit); +#endif + if (!config.use_ipv6) { + print_cnf_buf("--no-ipv6"); + } + if (config.verbose == VERBOSE_TRACE) { + print_cnf_buf("--trace"); + } + if (config.verbose == VERBOSE_INFO) { + print_cnf_buf("--silent"); + } + + size_t wbuf_len = print_config_section(config.first_section, buf_ptr, buf_sz); + buf_ptr += wbuf_len; + buf_sz -= wbuf_len; + + for (struct section_config_t *section = config.first_section->next; + section != NULL; section = section->next) { + print_cnf_buf("--fbegin"); + wbuf_len = print_config_section(section, buf_ptr, buf_sz); + buf_ptr += wbuf_len; + buf_sz -= wbuf_len; + print_cnf_buf("--fend"); + } + + return buffer_size - buf_sz; } +void print_welcome(void) { + char *welcome_message = malloc(4000); + if (welcome_message == NULL) + return; + + size_t sz = print_config(welcome_message, 4000); + printf("Running with flags: %.*s\n", (int)sz, welcome_message); + free(welcome_message); +} + +int init_section_config(struct section_config_t **section, struct section_config_t *prev) { + struct section_config_t *def_section = NULL; + int ret; +#ifdef KERNEL_SPACE + def_section = kmalloc(sizeof(struct section_config_t), GFP_KERNEL); +#else + def_section = malloc(sizeof(struct section_config_t)); +#endif + *def_section = (struct section_config_t)default_section_config; + def_section->prev = prev; + + if (def_section == NULL) + return -ENOMEM; + + ret = parse_sni_domains(&def_section->sni_domains, default_snistr, sizeof(default_snistr)); + if (ret < 0) { + free(def_section); + return ret; + } + + def_section->fake_sni_pkt = fake_sni_old; + def_section->fake_sni_pkt_sz = sizeof(fake_sni_old) - 1; + + *section = def_section; + return 0; +} + +int init_config(struct config_t *config) { + struct config_t def_config = default_config_set; + int ret = 0; + struct section_config_t *def_section = NULL; + ret = init_section_config(&def_section, NULL); + if (ret < 0) + return ret; + def_config.last_section = def_section; + def_config.first_section = def_section; + + *config = def_config; + + return 0; +} + +void free_config_section(struct section_config_t *section) { + if (section->udp_dport_range_len != 0) { + SFREE(section->udp_dport_range); + } + + free_sni_domains(section->sni_domains); + section->sni_domains = NULL; + free_sni_domains(section->exclude_sni_domains); + section->exclude_sni_domains = NULL; + + section->fake_custom_pkt_sz = 0; + SFREE(section->fake_custom_pkt); + + free(section); +} + +void free_config(struct config_t config) { + for (struct section_config_t *sct = config.last_section; sct != NULL;) { + struct section_config_t *psct = sct->prev; + free_config_section(sct); + sct = psct; + } +} diff --git a/args.h b/args.h index 98d65cf..8c4e88f 100644 --- a/args.h +++ b/args.h @@ -1,11 +1,23 @@ #ifndef ARGS_H #define ARGS_H +#include "types.h" +#include "config.h" -void print_version(); +void print_version(void); void print_usage(const char *argv0); -int parse_args(int argc, char *argv[]); +int yparse_args(int argc, char *argv[]); +size_t print_config(char *buffer, size_t buffer_size); + +// Initializes configuration storage. +int init_config(struct config_t *config); +// Allocates and initializes configuration section. +int init_section_config(struct section_config_t **section, struct section_config_t *prev); +// Frees configuration section +void free_config_section(struct section_config_t *config); +// Frees sections under config +void free_config(struct config_t config); /* Prints starting messages */ -void print_welcome(); +void print_welcome(void); #endif /* ARGS_H */ diff --git a/config.h b/config.h index 02c4969..5a1cd7a 100644 --- a/config.h +++ b/config.h @@ -5,7 +5,7 @@ #define USER_SPACE #endif -#include "raw_replacements.h" +#include "types.h" typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len); /** @@ -20,9 +20,28 @@ struct instance_config_t { }; extern struct instance_config_t instance_config; +struct udp_dport_range { + uint16_t start; + uint16_t end; +}; + +struct domains_list { + char *domain_name; + uint16_t domain_len; + + struct domains_list *next; +}; + struct section_config_t { - const char *domains_str; - unsigned int domains_strlen; + int id; + struct section_config_t *next; + struct section_config_t *prev; + + struct domains_list *sni_domains; + struct domains_list *exclude_sni_domains; + unsigned int all_domains; + + int tls_enabled; int fragmentation_strategy; int frag_sni_reverse; @@ -40,21 +59,15 @@ struct section_config_t { #define FAKE_PAYLOAD_DEFAULT 2 int fake_sni_type; - int quic_drop; - /* In milliseconds */ unsigned int seg2_delay; int synfake; unsigned int synfake_len; - const char *exclude_domains_str; - unsigned int exclude_domains_strlen; - unsigned int all_domains; - const char *fake_sni_pkt; unsigned int fake_sni_pkt_sz; - const char *fake_custom_pkt; + char *fake_custom_pkt; unsigned int fake_custom_pkt_sz; unsigned int fk_winsize; @@ -64,6 +77,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 @@ -79,53 +100,23 @@ struct config_t { int noclose; int syslog; + int connbytes_limit; + #define VERBOSE_INFO 0 #define VERBOSE_DEBUG 1 #define VERBOSE_TRACE 2 int verbose; - struct section_config_t default_config; - struct section_config_t custom_configs[MAX_CONFIGLIST_LEN]; - int custom_configs_len; + struct section_config_t *first_section; + struct section_config_t *last_section; }; extern struct config_t config; -#define ITER_CONFIG_SECTIONS(section) \ -for (struct section_config_t *section = &config.default_config + config.custom_configs_len; section >= &config.default_config; section--) +#define ITER_CONFIG_SECTIONS(config, section) \ +for (struct section_config_t *section = (config)->last_section; section != NULL; section = section->prev) -#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 CONFIG_SECTION_NUMBER(section) ((section)->id) #define MAX_THREADS 16 @@ -165,8 +156,9 @@ for (struct section_config_t *section = &config.default_config + config.custom_c #define FAKE_STRAT_PAST_SEQ (1 << 2) #define FAKE_STRAT_TCP_CHECK (1 << 3) #define FAKE_STRAT_TCP_MD5SUM (1 << 4) +#define FAKE_STRAT_UDP_CHECK (1 << 5) -#define FAKE_STRAT_COUNT 5 +#define FAKE_STRAT_COUNT 6 /** * This macros iterates through all faking strategies and executes code under it. @@ -196,6 +188,77 @@ if ((fake_bitmask) & strategy) #define DEFAULT_SNISTR "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com" -static const char defaul_snistr[] = DEFAULT_SNISTR; +static const char default_snistr[] = DEFAULT_SNISTR; + +enum { + UDP_MODE_DROP, + UDP_MODE_FAKE, +}; + +enum { + UDP_FILTER_QUIC_DISABLED, + UDP_FILTER_QUIC_ALL, +}; + +#define default_section_config { \ + .sni_domains = NULL, \ + .exclude_sni_domains = NULL, \ + .all_domains = 0, \ + .tls_enabled = 1, \ + .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, \ + .fake_custom_pkt = NULL, \ + .fake_custom_pkt_sz = 0, \ + .frag_middle_sni = 1, \ + .frag_sni_pos = 1, \ + .fakeseq_offset = 10000, \ + .synfake = 0, \ + .synfake_len = 0, \ + \ + .seg2_delay = 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, \ + \ + .prev = NULL, \ + .next = NULL, \ + .id = 0, \ +} + +#define default_config_set { \ + .threads = THREADS_NUM, \ + .queue_start_num = DEFAULT_QUEUE_NUM, \ + .mark = DEFAULT_RAWSOCKET_MARK, \ + .use_ipv6 = 1, \ + .connbytes_limit = 8, \ + \ + .verbose = VERBOSE_DEBUG, \ + .use_gso = 1, \ + \ + .first_section = NULL, \ + .last_section = NULL, \ + \ + .daemonize = 0, \ + .noclose = 0, \ + .syslog = 0, \ +} + +#define CONFIG_SET(config) \ +struct config_t config = default_config_set; \ +config->last_section = &(config.default_config) \ + #endif /* YTB_CONFIG_H */ diff --git a/getopt.c b/getopt.c new file mode 100644 index 0000000..48e76e1 --- /dev/null +++ b/getopt.c @@ -0,0 +1,204 @@ +#include "types.h" +#include "logging.h" +#include "getopt.h" + +char *optarg; +int optind=1, opterr=1, optopt, __optpos, optreset=0; + +#define optpos __optpos + +static void __getopt_msg(const char *b, const char *c, size_t l) +{ + lgerr("%s %.*s\n", b, (int)l, c); +} + +int getopt(int argc, char * const argv[], const char *optstring) +{ + int i, c, d; + int k, l; + char *optchar; + + if (!optind || optreset) { + optreset = 0; + __optpos = 0; + optind = 1; + } + + if (optind >= argc || !argv[optind]) + return -1; + + if (argv[optind][0] != '-') { + if (optstring[0] == '-') { + optarg = argv[optind++]; + return 1; + } + return -1; + } + + if (!argv[optind][1]) + return -1; + + if (argv[optind][1] == '-' && !argv[optind][2]) + return optind++, -1; + + if (!optpos) optpos++; + c = argv[optind][optpos], k = 1; + optchar = argv[optind]+optpos; + optopt = c; + optpos += k; + + if (!argv[optind][optpos]) { + optind++; + optpos = 0; + } + + if (optstring[0] == '-' || optstring[0] == '+') + optstring++; + + i = 0; + d = 0; + do { + d = optstring[i], l = 1; + if (l>0) i+=l; else i++; + } while (l && d != c); + + if (d != c) { + if (optstring[0] != ':' && opterr) + __getopt_msg("Unrecognized option: ", optchar, k); + return '?'; + } + if (optstring[i] == ':') { + if (optstring[i+1] == ':') optarg = 0; + else if (optind >= argc) { + if (optstring[0] == ':') return ':'; + if (opterr) __getopt_msg("Option requires an argument: ", + optchar, k); + return '?'; + } + if (optstring[i+1] != ':' || optpos) { + optarg = argv[optind++] + optpos; + optpos = 0; + } + } + return c; +} + +static void permute(char *const *argv, int dest, int src) +{ + char **av = (char **)argv; + char *tmp = av[src]; + int i; + for (i=src; i>dest; i--) + av[i] = av[i-1]; + av[dest] = tmp; +} + +static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) +{ + optarg = 0; + if (longopts && argv[optind][0] == '-' && + ((longonly && argv[optind][1] && argv[optind][1] != '-') || + (argv[optind][1] == '-' && argv[optind][2]))) + { + int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; + int i, cnt, match = -1; + char *opt; + for (cnt=i=0; longopts[i].name; i++) { + const char *name = longopts[i].name; + opt = argv[optind]+1; + if (*opt == '-') opt++; + for (; *name && *name == *opt; name++, opt++); + if (*opt && *opt != '=') continue; + match = i; + if (!*name) { + cnt = 1; + break; + } + cnt++; + } + if (cnt==1) { + i = match; + optind++; + optopt = longopts[i].val; + if (*opt == '=') { + if (!longopts[i].has_arg) { + if (colon || !opterr) + return '?'; + __getopt_msg( + "Option does not take an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optarg = opt+1; + } else if (longopts[i].has_arg == required_argument) { + if (!(optarg = argv[optind])) { + if (colon) return ':'; + if (!opterr) return '?'; + __getopt_msg( + "Option requires an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optind++; + } + if (idx) *idx = i; + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + return longopts[i].val; + } + if (argv[optind][1] == '-') { + if (!colon && opterr) + __getopt_msg(cnt ? + "Option is ambiguous: " : + "Unrecognized option: ", + argv[optind]+2, + strlen(argv[optind]+2)); + optind++; + return '?'; + } + } + return getopt(argc, argv, optstring); +} + +static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) +{ + int ret, skipped, resumed; + if (!optind || optreset) { + optreset = 0; + __optpos = 0; + optind = 1; + } + if (optind >= argc || !argv[optind]) return -1; + skipped = optind; + if (optstring[0] != '+' && optstring[0] != '-') { + int i; + for (i=optind; ; i++) { + if (i >= argc || !argv[i]) return -1; + if (argv[i][0] == '-' && argv[i][1]) break; + } + optind = i; + } + resumed = optind; + ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly); + if (resumed > skipped) { + int i, cnt = optind-resumed; + for (i=0; i #include "types.h" +#include "args.h" +#include "logging.h" -#define STR_MAXLEN 2048 +#define MAX_ARGC 1024 +static char *argv[MAX_ARGC]; -static char custom_fake_buf[MAX_FAKE_SIZE]; +static int params_set(const char *cval, const struct kernel_param *kp) { + int ret = 0; -struct config_t config = { - .threads = THREADS_NUM, - .queue_start_num = DEFAULT_QUEUE_NUM, - .mark = DEFAULT_RAWSOCKET_MARK, - .use_ipv6 = 1, - - .verbose = VERBOSE_DEBUG, - .use_gso = 1, - - .default_config = default_section_config, - .custom_configs_len = 0 -}; - -#define def_section (&config.default_config) - -static int unumeric_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; - ret = kstrtoint(val, 10, &n); - if (ret != 0 || n < 0) - return -EINVAL; - - - return param_set_int(val, kp); -} - -static int boolean_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; - ret = kstrtoint(val, 10, &n); - if (ret != 0 || (n != 0 && n != 1)) - return -EINVAL; - - return param_set_int(val, kp); -} - -static int inverse_boolean_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; - ret = kstrtoint(val, 10, &n); - if (ret != 0 || (n != 0 && n != 1)) - return -EINVAL; - - n = !n; - if (kp->arg == NULL) - return -EINVAL; - - *(int *)kp->arg = n; - return 0; -} - -static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) { - if (*(int *)kp->arg == 0) { - buffer[0] = '1'; - } else { - buffer[0] = '0'; - } - buffer[1] = '\0'; - return strlen(buffer); -} - -static const struct kernel_param_ops unumeric_parameter_ops = { - .set = unumeric_set, - .get = param_get_int -}; - -static const struct kernel_param_ops boolean_parameter_ops = { - .set = boolean_set, - .get = param_get_int -}; - -static const struct kernel_param_ops inverse_boolean_ops = { - .set = inverse_boolean_set, - .get = inverse_boolean_get, -}; - -module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664); -module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &def_section->fake_sni_seq_len, 0664); -module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664); -module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664); -module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &def_section->frag_sni_reverse, 0664); -module_param_cb(frag_sni_faked, &boolean_parameter_ops, &def_section->frag_sni_faked, 0664); -module_param_cb(frag_middle_sni, &boolean_parameter_ops, &def_section->frag_middle_sni, 0664); -module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664); -module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664); -module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664); -module_param_cb(synfake_len, &unumeric_parameter_ops, &def_section->synfake_len, 0664); -module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664); -// module_param_cb(seg2delay, &unumeric_parameter_ops, &def_section->seg2_delay, 0664); - -static int sni_domains_set(const char *val, const struct kernel_param *kp) { - size_t len; - int ret; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; + int cv_len = strlen(cval); + if (cv_len >= 1 && cval[cv_len - 1] == '\n') { + cv_len--; } - if (len >= 1 && val[len - 1] == '\n') { - len--; - } + const char *ytb_prefix = "youtubeUnblock "; + int ytbp_len = strlen(ytb_prefix); + int len = cv_len + ytbp_len; - ret = param_set_charp(val, kp); + char *val = kmalloc(len + 1, GFP_KERNEL); // 1 for null-terminator + strncpy(val, ytb_prefix, ytbp_len); + strncpy(val + ytbp_len, cval, cv_len); + val[len] = '\0'; - if (ret < 0) { - def_section->domains_strlen = 0; - } else { - def_section->domains_strlen = len; - if (len == 3 && !strncmp(val, "all", len)) { - def_section->all_domains = 1; - } else { - def_section->all_domains = 0; + int argc = 0; + argv[argc++] = val; + + for (int i = 0; i < len; i++) { + if (val[i] == ' ') { + val[i] = '\0'; + + // safe because of null-terminator + if (val[i + 1] != ' ' && val[i + 1] != '\0') { + argv[argc++] = val + i + 1; + } } } - + ret = yparse_args(argc, argv); + kfree(val); return ret; } -static const struct kernel_param_ops sni_domains_ops = { - .set = sni_domains_set, - .get = param_get_charp, +static int params_get(char *buffer, const struct kernel_param *kp) { + size_t len = print_config(buffer, 4000); + return len; +} + +static const struct kernel_param_ops params_ops = { + .set = params_set, + .get = params_get, }; -module_param_cb(sni_domains, &sni_domains_ops, &def_section->domains_str, 0664); - -static int exclude_domains_set(const char *val, const struct kernel_param *kp) { - size_t len; - int ret; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - ret = param_set_charp(val, kp); - - if (ret < 0) { - def_section->exclude_domains_strlen = 0; - } else { - def_section->exclude_domains_strlen = len; - } - - return ret; -} - -static const struct kernel_param_ops exclude_domains_ops = { - .set = exclude_domains_set, - .get = param_get_charp, -}; - -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); - -static int verbosity_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "trace", len) == 0) { - *(int *)kp->arg = VERBOSE_TRACE; - } else if (strncmp(val, "debug", len) == 0) { - *(int *)kp->arg = VERBOSE_DEBUG; - } else if (strncmp(val, "silent", len) == 0) { - *(int *)kp->arg = VERBOSE_INFO; - } else { - return -EINVAL; - } - - return 0; -} - - -static int verbosity_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case VERBOSE_TRACE: - strcpy(buffer, "trace\n"); - break; - case VERBOSE_DEBUG: - strcpy(buffer, "debug\n"); - break; - case VERBOSE_INFO: - strcpy(buffer, "silent\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops verbosity_ops = { - .set = verbosity_set, - .get = verbosity_get, -}; - -module_param_cb(verbosity, &verbosity_ops, &config.verbose, 0664); - -static int frag_strat_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "tcp", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_TCP; - } else if (strncmp(val, "ip", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_IP; - } else if (strncmp(val, "none", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_NONE; - } else { - return -EINVAL; - } - - return 0; -} - -static int frag_strat_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case FRAG_STRAT_TCP: - strcpy(buffer, "tcp\n"); - break; - case FRAG_STRAT_IP: - strcpy(buffer, "ip\n"); - break; - case FRAG_STRAT_NONE: - strcpy(buffer, "none\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops frag_strat_ops = { - .set = frag_strat_set, - .get = frag_strat_get, -}; - -module_param_cb(fragmentation_strategy, &frag_strat_ops, &def_section->fragmentation_strategy, 0664); - -static int fake_strat_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "randseq", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_RAND_SEQ; - } else if (strncmp(val, "ttl", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TTL; - } else if (strncmp(val, "tcp_check", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TCP_CHECK; - } else if (strncmp(val, "pastseq", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_PAST_SEQ; - } else if (strncmp(val, "md5sum", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TCP_MD5SUM; - } else { - return -EINVAL; - } - - return 0; -} - -static int fake_strat_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case FAKE_STRAT_RAND_SEQ: - strcpy(buffer, "randseq\n"); - break; - case FAKE_STRAT_TTL: - strcpy(buffer, "ttl\n"); - break; - case FAKE_STRAT_TCP_CHECK: - strcpy(buffer, "tcp_check\n"); - break; - case FAKE_STRAT_PAST_SEQ: - strcpy(buffer, "pastseq\n"); - break; - case FAKE_STRAT_TCP_MD5SUM: - strcpy(buffer, "md5sum\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops fake_strat_ops = { - .set = fake_strat_set, - .get = fake_strat_get, -}; - -module_param_cb(faking_strategy, &fake_strat_ops, &def_section->faking_strategy, 0664); - -static int sni_detection_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "parse", len) == 0) { - *(int *)kp->arg = SNI_DETECTION_PARSE; - } else if (strncmp(val, "brute", len) == 0) { - *(int *)kp->arg = SNI_DETECTION_BRUTE; - } else { - return -EINVAL; - } - - return 0; -} - -static int sni_detection_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case SNI_DETECTION_PARSE: - strcpy(buffer, "parse\n"); - break; - case SNI_DETECTION_BRUTE: - strcpy(buffer, "brute\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops sni_detection_ops = { - .set = sni_detection_set, - .get = sni_detection_get, -}; - -module_param_cb(sni_detection, &sni_detection_ops, &def_section->sni_detection, 0664); - -static int fake_type_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "default", len) == 0) { - *(int *)kp->arg = FAKE_PAYLOAD_DEFAULT; - } else if (strncmp(val, "custom", len) == 0) { - *(int *)kp->arg = FAKE_PAYLOAD_CUSTOM; - } else if (strncmp(val, "random", len) == 0) { - *(int *)kp->arg = FAKE_PAYLOAD_RANDOM; - } else { - return -EINVAL; - } - - return 0; -} - -static int fake_type_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case FAKE_PAYLOAD_DEFAULT: - strcpy(buffer, "default\n"); - break; - case FAKE_PAYLOAD_RANDOM: - strcpy(buffer, "random\n"); - break; - case FAKE_PAYLOAD_CUSTOM: - strcpy(buffer, "custom\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops fake_type_ops = { - .set = fake_type_set, - .get = fake_type_get, -}; - -module_param_cb(fake_sni_type, &fake_type_ops, &def_section->fake_sni_type, 0664); - -static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; - const char *custom_hex_fake = val; - size_t custom_hlen = len; - - if ((custom_hlen & 1) == 1) { - return -EINVAL; - } - - - size_t custom_len = custom_hlen >> 1; - if (custom_len > MAX_FAKE_SIZE) { - return -EINVAL; - } - - for (int i = 0; i < custom_len; i++) { - sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); - } - - def_section->fake_custom_pkt_sz = custom_len; - def_section->fake_custom_pkt = (char *)custom_buf; - - return 0; -} - -static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) { - int cflen = def_section->fake_custom_pkt_sz; - const uint8_t *cbf_data = def_section->fake_custom_pkt; - int bflen = def_section->fake_custom_pkt_sz << 1; - - for (int i = 0; i < cflen; i++) { - sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i)); - } - - return bflen; -} - -static const struct kernel_param_ops fake_custom_pl_ops = { - .set = fake_custom_pl_set, - .get = fake_custom_pl_get, -}; - -module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &def_section->fake_custom_pkt, 0664); +module_param_cb(parameters, ¶ms_ops, NULL, 0664); + + +// +// static char custom_fake_buf[MAX_FAKE_SIZE]; +// +// struct config_t config = { +// .threads = THREADS_NUM, +// .queue_start_num = DEFAULT_QUEUE_NUM, +// .mark = DEFAULT_RAWSOCKET_MARK, +// .use_ipv6 = 1, +// +// .verbose = VERBOSE_DEBUG, +// .use_gso = 1, +// +// .default_config = default_section_config, +// .custom_configs_len = 0 +// }; +// +// #define def_section (&config.default_config) +// +// static int unumeric_set(const char *val, const struct kernel_param *kp) { +// int n = 0, ret; +// ret = kstrtoint(val, 10, &n); +// if (ret != 0 || n < 0) +// return -EINVAL; +// +// +// return param_set_int(val, kp); +// } +// +// static int boolean_set(const char *val, const struct kernel_param *kp) { +// int n = 0, ret; +// ret = kstrtoint(val, 10, &n); +// if (ret != 0 || (n != 0 && n != 1)) +// return -EINVAL; +// +// return param_set_int(val, kp); +// } +// +// static int inverse_boolean_set(const char *val, const struct kernel_param *kp) { +// int n = 0, ret; +// ret = kstrtoint(val, 10, &n); +// if (ret != 0 || (n != 0 && n != 1)) +// return -EINVAL; +// +// n = !n; +// if (kp->arg == NULL) +// return -EINVAL; +// +// *(int *)kp->arg = n; +// return 0; +// } +// +// static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) { +// if (*(int *)kp->arg == 0) { +// buffer[0] = '1'; +// } else { +// buffer[0] = '0'; +// } +// buffer[1] = '\0'; +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops unumeric_parameter_ops = { +// .set = unumeric_set, +// .get = param_get_int +// }; +// +// static const struct kernel_param_ops boolean_parameter_ops = { +// .set = boolean_set, +// .get = param_get_int +// }; +// +// static const struct kernel_param_ops inverse_boolean_ops = { +// .set = inverse_boolean_set, +// .get = inverse_boolean_get, +// }; +// +// module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664); +// module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &def_section->fake_sni_seq_len, 0664); +// module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664); +// module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664); +// module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &def_section->frag_sni_reverse, 0664); +// module_param_cb(frag_sni_faked, &boolean_parameter_ops, &def_section->frag_sni_faked, 0664); +// module_param_cb(frag_middle_sni, &boolean_parameter_ops, &def_section->frag_middle_sni, 0664); +// module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664); +// module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664); +// module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664); +// module_param_cb(synfake_len, &unumeric_parameter_ops, &def_section->synfake_len, 0664); +// module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664); +// // module_param_cb(seg2delay, &unumeric_parameter_ops, &def_section->seg2_delay, 0664); +// +// static int sni_domains_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// int ret; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// ret = param_set_charp(val, kp); +// +// if (ret < 0) { +// def_section->domains_strlen = 0; +// } else { +// def_section->domains_strlen = len; +// if (len == 3 && !strncmp(val, "all", len)) { +// def_section->all_domains = 1; +// } else { +// def_section->all_domains = 0; +// } +// } +// +// +// return ret; +// } +// +// static const struct kernel_param_ops sni_domains_ops = { +// .set = sni_domains_set, +// .get = param_get_charp, +// }; +// +// module_param_cb(sni_domains, &sni_domains_ops, &def_section->domains_str, 0664); +// +// static int exclude_domains_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// int ret; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// ret = param_set_charp(val, kp); +// +// if (ret < 0) { +// def_section->exclude_domains_strlen = 0; +// } else { +// def_section->exclude_domains_strlen = len; +// } +// +// return ret; +// } +// +// static const struct kernel_param_ops exclude_domains_ops = { +// .set = exclude_domains_set, +// .get = param_get_charp, +// }; +// +// 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); +// +// static int quic_drop_set(const char *val, const struct kernel_param *kp) { +// int n = 0, ret; +// ret = kstrtoint(val, 10, &n); +// if (ret != 0 || (n != 0 && n != 1)) +// return -EINVAL; +// +// if (n) { +// def_section->udp_mode = UDP_MODE_DROP; +// def_section->udp_filter_quic = UDP_FILTER_QUIC_ALL; +// } else { +// def_section->udp_filter_quic = UDP_FILTER_QUIC_DISABLED; +// } +// +// return 0; +// } +// +// static int quic_drop_get(char *buffer, const struct kernel_param *kp) { +// if (def_section->udp_mode == UDP_MODE_DROP && +// def_section->udp_filter_quic == UDP_FILTER_QUIC_ALL) { +// return sprintf(buffer, "%d\n", 1); +// } else { +// return sprintf(buffer, "%d\n", 0); +// } +// } +// +// static const struct kernel_param_ops quic_drop_ops = { +// .set = quic_drop_set, +// .get = quic_drop_get +// }; +// +// module_param_cb(quic_drop, &quic_drop_ops, NULL, 0664); +// +// static int verbosity_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "trace", len) == 0) { +// *(int *)kp->arg = VERBOSE_TRACE; +// } else if (strncmp(val, "debug", len) == 0) { +// *(int *)kp->arg = VERBOSE_DEBUG; +// } else if (strncmp(val, "silent", len) == 0) { +// *(int *)kp->arg = VERBOSE_INFO; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// +// static int verbosity_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case VERBOSE_TRACE: +// strcpy(buffer, "trace\n"); +// break; +// case VERBOSE_DEBUG: +// strcpy(buffer, "debug\n"); +// break; +// case VERBOSE_INFO: +// strcpy(buffer, "silent\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops verbosity_ops = { +// .set = verbosity_set, +// .get = verbosity_get, +// }; +// +// module_param_cb(verbosity, &verbosity_ops, &config.verbose, 0664); +// +// static int frag_strat_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "tcp", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_TCP; +// } else if (strncmp(val, "ip", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_IP; +// } else if (strncmp(val, "none", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_NONE; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int frag_strat_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case FRAG_STRAT_TCP: +// strcpy(buffer, "tcp\n"); +// break; +// case FRAG_STRAT_IP: +// strcpy(buffer, "ip\n"); +// break; +// case FRAG_STRAT_NONE: +// strcpy(buffer, "none\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops frag_strat_ops = { +// .set = frag_strat_set, +// .get = frag_strat_get, +// }; +// +// module_param_cb(fragmentation_strategy, &frag_strat_ops, &def_section->fragmentation_strategy, 0664); +// +// static int fake_strat_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "randseq", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_RAND_SEQ; +// } else if (strncmp(val, "ttl", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TTL; +// } else if (strncmp(val, "tcp_check", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TCP_CHECK; +// } else if (strncmp(val, "pastseq", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_PAST_SEQ; +// } else if (strncmp(val, "md5sum", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TCP_MD5SUM; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int fake_strat_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case FAKE_STRAT_RAND_SEQ: +// strcpy(buffer, "randseq\n"); +// break; +// case FAKE_STRAT_TTL: +// strcpy(buffer, "ttl\n"); +// break; +// case FAKE_STRAT_TCP_CHECK: +// strcpy(buffer, "tcp_check\n"); +// break; +// case FAKE_STRAT_PAST_SEQ: +// strcpy(buffer, "pastseq\n"); +// break; +// case FAKE_STRAT_TCP_MD5SUM: +// strcpy(buffer, "md5sum\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops fake_strat_ops = { +// .set = fake_strat_set, +// .get = fake_strat_get, +// }; +// +// module_param_cb(faking_strategy, &fake_strat_ops, &def_section->faking_strategy, 0664); +// +// static int sni_detection_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "parse", len) == 0) { +// *(int *)kp->arg = SNI_DETECTION_PARSE; +// } else if (strncmp(val, "brute", len) == 0) { +// *(int *)kp->arg = SNI_DETECTION_BRUTE; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int sni_detection_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case SNI_DETECTION_PARSE: +// strcpy(buffer, "parse\n"); +// break; +// case SNI_DETECTION_BRUTE: +// strcpy(buffer, "brute\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops sni_detection_ops = { +// .set = sni_detection_set, +// .get = sni_detection_get, +// }; +// +// module_param_cb(sni_detection, &sni_detection_ops, &def_section->sni_detection, 0664); +// +// static int fake_type_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "default", len) == 0) { +// *(int *)kp->arg = FAKE_PAYLOAD_DEFAULT; +// } else if (strncmp(val, "custom", len) == 0) { +// *(int *)kp->arg = FAKE_PAYLOAD_CUSTOM; +// } else if (strncmp(val, "random", len) == 0) { +// *(int *)kp->arg = FAKE_PAYLOAD_RANDOM; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int fake_type_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case FAKE_PAYLOAD_DEFAULT: +// strcpy(buffer, "default\n"); +// break; +// case FAKE_PAYLOAD_RANDOM: +// strcpy(buffer, "random\n"); +// break; +// case FAKE_PAYLOAD_CUSTOM: +// strcpy(buffer, "custom\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops fake_type_ops = { +// .set = fake_type_set, +// .get = fake_type_get, +// }; +// +// module_param_cb(fake_sni_type, &fake_type_ops, &def_section->fake_sni_type, 0664); +// +// static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; +// const char *custom_hex_fake = val; +// size_t custom_hlen = len; +// +// if ((custom_hlen & 1) == 1) { +// return -EINVAL; +// } +// +// +// size_t custom_len = custom_hlen >> 1; +// if (custom_len > MAX_FAKE_SIZE) { +// return -EINVAL; +// } +// +// for (int i = 0; i < custom_len; i++) { +// sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); +// } +// +// def_section->fake_custom_pkt_sz = custom_len; +// def_section->fake_custom_pkt = (char *)custom_buf; +// +// return 0; +// } +// +// static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) { +// int cflen = def_section->fake_custom_pkt_sz; +// const uint8_t *cbf_data = def_section->fake_custom_pkt; +// int bflen = def_section->fake_custom_pkt_sz << 1; +// +// for (int i = 0; i < cflen; i++) { +// sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i)); +// } +// +// return bflen; +// } +// +// static const struct kernel_param_ops fake_custom_pl_ops = { +// .set = fake_custom_pl_set, +// .get = fake_custom_pl_get, +// }; +// +// module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &def_section->fake_custom_pkt, 0664); diff --git a/kytunblock.c b/kytunblock.c index d32cecf..7d079c5 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -16,10 +16,14 @@ #include #include +#include +#include + #include "mangle.h" #include "config.h" #include "utils.h" #include "logging.h" +#include "args.h" #if defined(PKG_VERSION) MODULE_VERSION(PKG_VERSION); @@ -206,9 +210,9 @@ erret_lc: int ipvx = netproto_version(pkt, pktlen); if (ipvx == IP4VERSION) { - return send_raw_ipv4(pkt, pktlen); + ret = send_raw_ipv4(pkt, pktlen); } else if (ipvx == IP6VERSION) { - return send_raw_ipv6(pkt, pktlen); + ret = send_raw_ipv6(pkt, pktlen); } else { printf("proto version %d is unsupported\n", ipvx); return -EINVAL; @@ -228,6 +232,32 @@ struct instance_config_t instance_config = { .send_delayed_packet = delay_packet_send, }; +static int connbytes_pkts(const struct sk_buff *skb) { + const struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + u_int64_t pkts = 0; + const struct nf_conn_counter *counters; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct) + return -1; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + const struct nf_conn_acct *acct; + acct = nf_conn_acct_find(ct); + if (!acct) + return -1; + counters = acct->counter; +#else + counters = nf_conn_acct_find(ct); + if (!counters) + return -1; +#endif + + pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets); + + return pkts; +} /* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */ #ifdef RHEL_RELEASE_CODE @@ -307,6 +337,9 @@ static NF_CALLBACK(ykb_nf_hook, skb) { if (skb->len > MAX_PACKET_SIZE) goto accept; + if (config.connbytes_limit != 0 && connbytes_pkts(skb) > config.connbytes_limit) + goto accept; + ret = skb_linearize(skb); if (ret < 0) { lgerror(ret, "Cannot linearize"); @@ -346,6 +379,8 @@ static struct nf_hook_ops ykb6_nf_reg __read_mostly = { static int __init ykb_init(void) { int ret = 0; + ret = init_config(&config); + if (ret < 0) goto err; ret = open_raw_socket(); if (ret < 0) goto err; @@ -420,6 +455,8 @@ static void __exit ykb_destroy(void) { #endif close_raw_socket(); + + free_config(config); lginfo("youtubeUnblock kernel module destroyed.\n"); } diff --git a/mangle.c b/mangle.c index cc8d811..d474f88 100644 --- a/mangle.c +++ b/mangle.c @@ -62,7 +62,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { lgtrace_addp("UDP"); - ITER_CONFIG_SECTIONS(section) { + ITER_CONFIG_SECTIONS(&config, section) { lgtrace_addp("Section #%d", CONFIG_SECTION_NUMBER(section)); switch (transport_proto) { @@ -74,16 +74,32 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { break; } - if (verdict == PKT_CONTINUE) + if (verdict == PKT_CONTINUE) { + lgtrace_addp("continue_flow"); continue; + } - lgtrace_end(); - return verdict; + goto ret_verdict; } accept: + verdict = PKT_ACCEPT; + +ret_verdict: + + switch (verdict) { + case PKT_ACCEPT: + lgtrace_addp("accept"); + break; + case PKT_DROP: + lgtrace_addp("drop"); + break; + default: + lgtrace_addp("unknow verdict: %d", verdict); + } lgtrace_end(); - return PKT_ACCEPT; + + return verdict; } int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, uint32_t raw_payload_len) { @@ -154,6 +170,9 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra if (tcph->syn) goto continue_flow; + if (!section->tls_enabled) + goto continue_flow; + struct tls_verdict vrd = analyze_tls_data(section, data, dlen); lgtrace_addp("TLS analyzed"); @@ -309,13 +328,10 @@ drop_lc: } continue_flow: - lgtrace_addp("continue_flow"); return PKT_CONTINUE; accept: - lgtrace_addp("accept"); return PKT_ACCEPT; drop: - lgtrace_addp("drop"); return PKT_DROP; } @@ -325,7 +341,6 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk const struct udphdr *udph; const uint8_t *data; uint32_t dlen; - int ipver = netproto_version(pkt, pktlen); int ret = udp_payload_split((uint8_t *)pkt, pktlen, (void **)&iph, &iph_len, @@ -350,75 +365,54 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk } - 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 (!detect_udp_filtered(section, pkt, pktlen)) + goto continue_flow; + 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) { - lgtrace_addp("Probe udp"); - if (ipver == IP4VERSION && ntohs(udph->dest) > 30) { - lgtrace_addp("udp fool"); - const uint8_t *payload; - uint32_t payload_len; - - uint32_t poses[10]; - int cnt = 3; - - poses[0] = 8; - for (int i = 1; i < cnt; i++) { - poses[i] = poses[i - 1] + 8; + 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"); + return -ENOMEM; } + uint32_t fsn_len = MAX_PACKET_SIZE; - ret = send_ip4_frags(pkt, pktlen, poses, cnt, 0); + struct udp_fake_type fake_type = { + .fake_len = section->udp_fake_len, + .strategy = { + .strategy = section->udp_faking_strategy, + }, + }; + ret = gen_fake_udp(fake_type, iph, iph_len, udph, fake_udp, &fsn_len); if (ret < 0) { - lgerror("ip4 send frags", ret); - goto accept; + lgerror(ret, "gen_fake_udp"); + goto erret_lc; } - goto drop; - } else { - lginfo("WARNING: IP fragmentation is supported only for IPv4\n"); + lgtrace_addp("post fake udp #%d", i + 1); + + ret = instance_config.send_raw_packet(fake_udp, fsn_len); + if (ret < 0) { + lgerror(ret, "send fake udp"); + goto erret_lc; + } + + NETBUF_FREE(fake_udp); + continue; +erret_lc: + NETBUF_FREE(fake_udp); goto accept; } - } -*/ + + ret = instance_config.send_raw_packet(pkt, pktlen); + goto drop; + } continue_flow: - lgtrace_addp("continue_flow"); return PKT_CONTINUE; -accept_quic: accept: return PKT_ACCEPT; drop: @@ -456,6 +450,7 @@ int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet return -ENOMEM; } +/* NETBUF_ALLOC(fake_pad, MAX_PACKET_SIZE); if (!NETBUF_CHECK(fake_pad)) { lgerror(-ENOMEM, "Allocation error"); @@ -463,10 +458,11 @@ int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet NETBUF_FREE(frag2); return -ENOMEM; } +*/ uint32_t f1len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE; - uint32_t fake_pad_len = MAX_PACKET_SIZE; + // uint32_t fake_pad_len = MAX_PACKET_SIZE; int ret; @@ -542,12 +538,12 @@ send_frag2: out_lc: NETBUF_FREE(frag1); NETBUF_FREE(frag2); - NETBUF_FREE(fake_pad); + // NETBUF_FREE(fake_pad); goto out; erret_lc: NETBUF_FREE(frag1); NETBUF_FREE(frag2); - NETBUF_FREE(fake_pad); + // NETBUF_FREE(fake_pad); return ret; } diff --git a/quic.c b/quic.c index 66bdb4e..46e7f91 100644 --- a/quic.c +++ b/quic.c @@ -138,3 +138,181 @@ invalid_packet: lgerror(-EINVAL, "QUIC invalid Initial packet"); return -EINVAL; } + +int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { + void *iph; + uint32_t iph_len; + struct udphdr *udph; + uint8_t *data; + uint32_t dlen; + int ret; + + ret = udp_payload_split(payload, *plen, + &iph, &iph_len, &udph, + &data, &dlen); + + uint32_t ipxv = netproto_version(payload, *plen); + + if (ret < 0) { + return ret; + } + + + if (strategy.strategy == FAKE_STRAT_TTL) { + lgtrace_addp("set fake ttl to %d", strategy.faking_ttl); + + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->ttl = strategy.faking_ttl; + } else if (ipxv == IP6VERSION) { + ((struct ip6_hdr *)iph)->ip6_hops = strategy.faking_ttl; + } else { + lgerror(-EINVAL, "fail_packet: IP version is unsupported"); + return -EINVAL; + } + } + + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->frag_off = 0; + } + + + set_ip_checksum(iph, iph_len); + + if (strategy.strategy == FAKE_STRAT_UDP_CHECK) { + lgtrace_addp("break fake udp checksum"); + udph->check += 1; + } + + return 0; +} + +int gen_fake_udp(struct udp_fake_type type, + const void *ipxh, uint32_t iph_len, + const struct udphdr *udph, + uint8_t *buf, uint32_t *buflen) { + uint32_t data_len = type.fake_len; + + if (!ipxh || !udph || !buf || !buflen) + return -EINVAL; + + int ipxv = netproto_version(ipxh, iph_len); + + if (ipxv == IP4VERSION) { + const struct iphdr *iph = ipxh; + + memcpy(buf, iph, iph_len); + struct iphdr *niph = (struct iphdr *)buf; + + niph->protocol = IPPROTO_UDP; + } else if (ipxv == IP6VERSION) { + const struct ip6_hdr *iph = ipxh; + + iph_len = sizeof(struct ip6_hdr); + memcpy(buf, iph, iph_len); + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + + niph->ip6_nxt = IPPROTO_UDP; + } else { + return -EINVAL; + } + + uint32_t dlen = iph_len + sizeof(struct udphdr) + data_len; + + if (*buflen < dlen) + return -ENOMEM; + + memcpy(buf + iph_len, udph, sizeof(struct udphdr)); + uint8_t *bfdptr = buf + iph_len + sizeof(struct udphdr); + + memset(bfdptr, 0, data_len); + + if (ipxv == IP4VERSION) { + struct iphdr *niph = (struct iphdr *)buf; + niph->tot_len = htons(dlen); + niph->id = randint(); + } else if (ipxv == IP6VERSION) { + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + niph->ip6_plen = htons(dlen - iph_len); + } + + struct udphdr *nudph = (struct udphdr *)(buf + iph_len); + nudph->len = htons(sizeof(struct udphdr) + data_len); + + + udp_fail_packet(type.strategy, buf, &dlen, *buflen); + + *buflen = dlen; + + 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; + + 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"); + + goto approve; + + // uint8_t qtype = qch->type; + // 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; +} diff --git a/quic.h b/quic.h index 1edbfca..0422941 100644 --- a/quic.h +++ b/quic.h @@ -1,6 +1,7 @@ #ifndef QUIC_H #define QUIC_H #include "types.h" +#include "utils.h" /** @@ -125,4 +126,16 @@ int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen, struct quici_hdr *qhdr, uint8_t **payload, uint32_t *plen); +// Like fail_packet for TCP +int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); + +// Like gen_fake_sni for TCP +int gen_fake_udp(struct udp_fake_type type, + const void *ipxh, uint32_t iph_len, + 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 */ diff --git a/tls.c b/tls.c index b3f0e4f..a32bb86 100644 --- a/tls.c +++ b/tls.c @@ -9,6 +9,114 @@ #include #endif +static int bruteforce_analyze_sni_str( + const struct section_config_t *section, + const uint8_t *data, size_t dlen, + struct tls_verdict *vrd +) { + if (section->all_domains) { + vrd->target_sni = 1; + vrd->sni_len = 0; + vrd->sni_offset = dlen / 2; + return 0; + } + + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + const char *domain_startp = sne->domain_name; + int domain_len = sne->domain_len; + + if (sne->domain_len + dlen + 1 > MAX_PACKET_SIZE) { + continue; + } + + NETBUF_ALLOC(buf, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buf)) { + lgerror(-ENOMEM, "Allocation error"); + return -ENOMEM; + } + NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); + if (!NETBUF_CHECK(nzbuf)) { + lgerror(-ENOMEM, "Allocation error"); + NETBUF_FREE(buf); + return -ENOMEM; + } + + int *zbuf = (void *)nzbuf; + + memcpy(buf, domain_startp, domain_len); + memcpy(buf + domain_len, "#", 1); + memcpy(buf + domain_len + 1, data, dlen); + + z_function((char *)buf, zbuf, domain_len + 1 + dlen); + + for (unsigned int k = 0; k < dlen; k++) { + if (zbuf[k] == domain_len) { + vrd->target_sni = 1; + vrd->sni_len = domain_len; + vrd->sni_offset = (k - domain_len - 1); + vrd->sni_target_offset = vrd->sni_offset; + vrd->sni_target_len = vrd->sni_len; + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + return 0; + } + } + + + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + } + + return 0; +} +static int analyze_sni_str( + const struct section_config_t *section, + const char *sni_name, int sni_len, const uint8_t *data, + struct tls_verdict *vrd +) { + if (section->all_domains) { + vrd->target_sni = 1; + goto check_domain; + } + + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + const char *sni_startp = sni_name + sni_len - sne->domain_len; + const char *domain_startp = sne->domain_name; + + if (sni_len >= sne->domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + sne->domain_len)) { + vrd->target_sni = 1; + vrd->sni_target_offset = (const uint8_t *)sni_startp - data; + vrd->sni_target_len = sne->domain_len; + break; + } + } + +check_domain: + if (vrd->target_sni == 1) { + for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) { + const char *sni_startp = sni_name + sni_len - sne->domain_len; + const char *domain_startp = sne->domain_name; + + if (sni_len >= sne->domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + sne->domain_len)) { + vrd->target_sni = 0; + lgdebugmsg("Excluded SNI: %.*s", + vrd->sni_len, data + vrd->sni_offset); + } + } + } + + return 0; +} + + #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 #define TLS_EXTENSION_SNI 0x0000 @@ -30,6 +138,11 @@ struct tls_verdict analyze_tls_data( size_t i = 0; const uint8_t *data_end = data + dlen; + if (section->sni_detection == SNI_DETECTION_BRUTE) { + bruteforce_analyze_sni_str(section, data, dlen, &vrd); + goto out; + } + while (i + 4 < dlen) { const uint8_t *msgData = data + i; @@ -44,10 +157,6 @@ struct tls_verdict analyze_tls_data( if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) goto nextMessage; - if (section->sni_detection == SNI_DETECTION_BRUTE) { - goto brute; - } - const uint8_t *handshakeProto = msgData + 5; if (handshakeProto + 1 >= data_end) break; @@ -120,76 +229,14 @@ struct tls_verdict analyze_tls_data( if (sni_ext_ptr + sni_len > sni_ext_end) break; - char *sni_name = (char *)sni_ext_ptr; + const char *sni_name = (char *)sni_ext_ptr; vrd.sni_offset = (uint8_t *)sni_name - data; vrd.sni_target_offset = vrd.sni_offset; vrd.sni_len = sni_len; vrd.sni_target_len = vrd.sni_len; - if (section->all_domains) { - vrd.target_sni = 1; - goto check_domain; - } - - unsigned int j = 0; - for (unsigned int i = 0; i <= section->domains_strlen; i++) { - if ( i > j && - (i == section->domains_strlen || - section->domains_str[i] == '\0' || - section->domains_str[i] == ',' || - section->domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = section->domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - domain_len)) { - vrd.target_sni = 1; - vrd.sni_target_offset = (const uint8_t *)sni_startp - data; - vrd.sni_target_len = domain_len; - goto check_domain; - } - - j = i + 1; - } - } - -check_domain: - if (vrd.target_sni == 1 && section->exclude_domains_strlen != 0) { - unsigned int j = 0; - for (unsigned int i = 0; i <= section->exclude_domains_strlen; i++) { - if ( i > j && - (i == section->exclude_domains_strlen || - section->exclude_domains_str[i] == '\0' || - section->exclude_domains_str[i] == ',' || - section->exclude_domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = section->exclude_domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - domain_len)) { - - vrd.target_sni = 0; - lgdebugmsg("Excluded SNI: %.*s", - vrd.sni_len, data + vrd.sni_offset); - goto out; - } - - j = i + 1; - } - } - } - + analyze_sni_str(section, sni_name, sni_len, data, &vrd); goto out; nextExtension: @@ -201,73 +248,6 @@ nextMessage: out: return vrd; - - -brute: - if (section->all_domains) { - vrd.target_sni = 1; - vrd.sni_len = 0; - vrd.sni_offset = dlen / 2; - goto out; - } - - unsigned int j = 0; - for (unsigned int i = 0; i <= section->domains_strlen; i++) { - if ( i > j && - (i == section->domains_strlen || - section->domains_str[i] == '\0' || - section->domains_str[i] == ',' || - section->domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *domain_startp = section->domains_str + j; - - if (domain_len + dlen + 1> MAX_PACKET_SIZE) { - continue; - } - - NETBUF_ALLOC(buf, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buf)) { - lgerror(-ENOMEM, "Allocation error"); - goto out; - } - NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); - if (!NETBUF_CHECK(nzbuf)) { - lgerror(-ENOMEM, "Allocation error"); - NETBUF_FREE(buf); - goto out; - } - - int *zbuf = (void *)nzbuf; - - memcpy(buf, domain_startp, domain_len); - memcpy(buf + domain_len, "#", 1); - memcpy(buf + domain_len + 1, data, dlen); - - z_function((char *)buf, zbuf, domain_len + 1 + dlen); - - for (unsigned int k = 0; k < dlen; k++) { - if (zbuf[k] == domain_len) { - vrd.target_sni = 1; - vrd.sni_len = domain_len; - vrd.sni_offset = (k - domain_len - 1); - vrd.sni_target_offset = vrd.sni_offset; - vrd.sni_target_len = vrd.sni_len; - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - goto out; - } - } - - - j = i + 1; - - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - } - } - - goto out; } int gen_fake_sni(struct fake_type type, @@ -275,7 +255,6 @@ int gen_fake_sni(struct fake_type type, const struct tcphdr *tcph, uint32_t tcph_len, uint8_t *buf, uint32_t *buflen) { uint32_t data_len = type.fake_len; - int ret; if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { data_len = (uint32_t)randint() % 1200; @@ -322,7 +301,8 @@ int gen_fake_sni(struct fake_type type, get_random_bytes(bfdptr, data_len); #else /* KERNEL_SPACE */ #if _NO_GETRANDOM - ret = open("/dev/urandom", O_RDONLY); + { + int ret = open("/dev/urandom", O_RDONLY); if (ret < 0) { lgerror(ret, "Unable to open /dev/urandom"); return ret; @@ -330,7 +310,7 @@ int gen_fake_sni(struct fake_type type, read(ret, bfdptr, data_len); close(ret); - + } #else /* _NO_GETRANDOM */ getrandom(bfdptr, data_len, 0); #endif /* _NO_GETRANDOM */ diff --git a/types.h b/types.h index 32f88aa..e35733b 100644 --- a/types.h +++ b/types.h @@ -34,6 +34,9 @@ #include // IWYU pragma: export #include +#define free kfree +#define malloc(size) kmalloc((size), GFP_KERNEL) + #define ip6_hdr ipv6hdr /* from */ @@ -67,6 +70,11 @@ #include // IWYU pragma: export #endif +#define SFREE(item) do { \ +free((item)); \ +(item) = NULL; \ +} while (0) + #ifndef KERNEL_SPACE #define max(a,b)__extension__\ diff --git a/uspace.mk b/uspace.mk index 2506c97..0e618a0 100644 --- a/uspace.mk +++ b/uspace.mk @@ -31,7 +31,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c +SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c getopt.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la diff --git a/utils.c b/utils.c index eb3f168..bfaf0ba 100644 --- a/utils.c +++ b/utils.c @@ -514,7 +514,9 @@ void z_function(const char *str, int *zbuf, size_t len) { for (int i = 1; i < (int)len; i++) { zbuf[i] = 0; if (i < rh) { - zbuf[i] = min(zbuf[i - lh], rh - i); + zbuf[i] = zbuf[i - lh]; + if (rh - i < zbuf[i]) + zbuf[i] = rh - i; } while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]]) diff --git a/utils.h b/utils.h index d78b7b5..7a5a629 100644 --- a/utils.h +++ b/utils.h @@ -142,6 +142,20 @@ struct fake_type { struct failing_strategy strategy; }; +struct udp_failing_strategy { + unsigned int strategy; + uint8_t faking_ttl; +}; + +struct udp_fake_type { + uint16_t fake_len; + + // faking strategy of the fake packet. + // Does not support bitmask, pass standalone strategy. + // Pass 0 if you don't want any faking procedures. + struct udp_failing_strategy strategy; +}; + /** * Invalidates the raw packet. The function aims to invalid the packet * in such way as it will be accepted by DPI, but dropped by target server diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 6e9dcb2..0d7d662 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -45,12 +45,12 @@ static int open_socket(struct mnl_socket **_nl) { nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) { - lgerror(errno, "mnl_socket_open"); + lgerror(-errno, "mnl_socket_open"); return -1; } if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - lgerror(errno, "mnl_socket_bind"); + lgerror(-errno, "mnl_socket_bind"); mnl_socket_close(nl); return -1; } @@ -65,7 +65,7 @@ static int close_socket(struct mnl_socket **_nl) { struct mnl_socket *nl = *_nl; if (nl == NULL) return 1; if (mnl_socket_close(nl) < 0) { - lgerror(errno, "mnl_socket_close"); + lgerror(-errno, "mnl_socket_close"); return -1; } @@ -77,26 +77,26 @@ static int close_socket(struct mnl_socket **_nl) { static int open_raw_socket(void) { if (rawsocket != -2) { errno = EALREADY; - lgerror(errno, "Raw socket is already opened"); + lgerror(-errno, "Raw socket is already opened"); return -1; } rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (rawsocket == -1) { - lgerror(errno, "Unable to create raw socket"); + lgerror(-errno, "Unable to create raw socket"); return -1; } int mark = config.mark; if (setsockopt(rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { - lgerror(errno, "setsockopt(SO_MARK, %d) failed\n", mark); + lgerror(-errno, "setsockopt(SO_MARK, %d) failed\n", mark); return -1; } int mst = pthread_mutex_init(&rawsocket_lock, NULL); if (mst) { - lgerror(errno, "Mutex err: %d\n", mst); + lgerror(-errno, "Mutex err: %d\n", mst); close(rawsocket); errno = mst; @@ -110,12 +110,12 @@ static int open_raw_socket(void) { static int close_raw_socket(void) { if (rawsocket < 0) { errno = EALREADY; - lgerror(errno, "Raw socket is not set"); + lgerror(-errno, "Raw socket is not set"); return -1; } if (close(rawsocket)) { - lgerror(errno, "Unable to close raw socket"); + lgerror(-errno, "Unable to close raw socket"); pthread_mutex_destroy(&rawsocket_lock); return -1; } @@ -129,28 +129,27 @@ static int close_raw_socket(void) { static int open_raw6_socket(void) { if (raw6socket != -2) { errno = EALREADY; - lgerror(errno, "Raw socket is already opened"); + lgerror(-errno, "Raw socket is already opened"); return -1; } raw6socket = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (rawsocket == -1) { - lgerror(errno, "Unable to create raw socket"); + lgerror(-errno, "Unable to create raw socket"); return -1; } int mark = config.mark; if (setsockopt(raw6socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { - lgerror(errno, "setsockopt(SO_MARK, %d) failed\n", mark); + lgerror(-errno, "setsockopt(SO_MARK, %d) failed\n", mark); return -1; } int mst = pthread_mutex_init(&raw6socket_lock, NULL); if (mst) { - lgerror(mst, "Mutex err: %d\n", mst); + lgerror(-errno, "Mutex err: %d\n", mst); close(raw6socket); - errno = mst; return -1; } @@ -162,12 +161,12 @@ static int open_raw6_socket(void) { static int close_raw6_socket(void) { if (raw6socket < 0) { errno = EALREADY; - lgerror(errno, "Raw socket is not set"); + lgerror(-errno, "Raw socket is not set"); return -1; } if (close(raw6socket)) { - lgerror(errno, "Unable to close raw socket"); + lgerror(-errno, "Unable to close raw socket"); pthread_mutex_destroy(&rawsocket_lock); return -1; } @@ -345,7 +344,7 @@ static int fallback_accept_packet(uint32_t id, struct queue_data qdata) { nfq_nlmsg_verdict_put(verdnlh, id, NF_ACCEPT); if (mnl_socket_sendto(*qdata._nl, verdnlh, verdnlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); return MNL_CB_ERROR; } @@ -370,7 +369,7 @@ void *delay_packet_send_fn(void *data) { int ret = send_raw_socket(pkt, pktlen); if (ret < 0) { errno = -ret; - lgerror(errno, "send delayed raw packet"); + lgerror(-errno, "send delayed raw packet"); } free(pkt); @@ -402,13 +401,13 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { struct packet_data packet = {0}; if (nfq_nlmsg_parse(nlh, attr) < 0) { - lgerror(errno, "Attr parse"); + lgerror(-errno, "Attr parse"); return MNL_CB_ERROR; } if (attr[NFQA_PACKET_HDR] == NULL) { errno = ENODATA; - lgerror(errno, "Metaheader not set"); + lgerror(-errno, "Metaheader not set"); return MNL_CB_ERROR; } @@ -449,7 +448,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { } if (mnl_socket_sendto(*qdata->_nl, verdnlh, verdnlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); return MNL_CB_ERROR; } @@ -462,7 +461,7 @@ int init_queue(int queue_num) { struct mnl_socket *nl; if (open_socket(&nl)) { - lgerror(errno, "Unable to open socket"); + lgerror(-errno, "Unable to open socket"); return -1; } @@ -484,7 +483,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, PF_INET, NFQNL_CFG_CMD_PF_UNBIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -492,7 +491,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, PF_INET, NFQNL_CFG_CMD_PF_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -501,7 +500,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, PF_INET6, NFQNL_CFG_CMD_PF_UNBIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -509,7 +508,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, PF_INET6, NFQNL_CFG_CMD_PF_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } } @@ -519,7 +518,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -532,7 +531,7 @@ int init_queue(int queue_num) { } if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -554,7 +553,7 @@ int init_queue(int queue_num) { while (1) { ret = mnl_socket_recvfrom(nl, buf, BUF_SIZE); if (ret == -1) { - lgerror(errno, "mnl_socket_recvfrom"); + lgerror(-errno, "mnl_socket_recvfrom"); goto die; } @@ -562,9 +561,9 @@ int init_queue(int queue_num) { if (ret < 0) { lgerror(-EPERM, "mnl_cb_run"); if (errno == EPERM) { - lgerror(errno, "Probably another instance of youtubeUnblock with the same queue number is running\n"); + lgerror(-errno, "Probably another instance of youtubeUnblock with the same queue number is running\n"); } else { - lgerror(errno, "Make sure the nfnetlink_queue kernel module is loaded\n"); + lgerror(-errno, "Make sure the nfnetlink_queue kernel module is loaded\n"); } goto die; } @@ -613,9 +612,9 @@ struct instance_config_t instance_config = { int main(int argc, char *argv[]) { int ret; - if ((ret = parse_args(argc, argv)) != 0) { + if ((ret = yparse_args(argc, argv)) != 0) { if (ret < 0) { - lgerror(errno, "Unable to parse args"); + lgerror(-errno, "Unable to parse args"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); @@ -626,13 +625,13 @@ int main(int argc, char *argv[]) { if (open_raw_socket() < 0) { - lgerror(errno, "Unable to open raw socket"); + lgerror(-errno, "Unable to open raw socket"); exit(EXIT_FAILURE); } if (config.use_ipv6) { if (open_raw6_socket() < 0) { - lgerror(errno, "Unable to open raw socket for ipv6"); + lgerror(-errno, "Unable to open raw socket for ipv6"); close_raw_socket(); exit(EXIT_FAILURE); }