From 581364ed9d6aa81bebfa0385f86b22646a07e5e4 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 31 Jan 2026 20:01:29 +0300 Subject: [PATCH] TCP Conntrack matching + TCP ports filtering --- Kbuild | 2 +- src/args.c | 73 ++++-- src/config.h | 12 +- src/dpi.c | 509 ++++++++++++++++++++++++++++++++++++++++++ src/dpi.h | 87 ++++++++ src/mangle.c | 513 ++++--------------------------------------- src/mangle.h | 20 +- src/quic.c | 2 +- src/types.h | 2 + src/youtubeUnblock.c | 470 ++++++++++++++++++++------------------- uspace.mk | 2 +- 11 files changed, 944 insertions(+), 748 deletions(-) create mode 100644 src/dpi.c create mode 100644 src/dpi.h diff --git a/Kbuild b/Kbuild index adb8dd1..85ab089 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o -kyoutubeUnblock-objs := src/kytunblock.o src/mangle.o src/quic.o src/quic_crypto.o src/utils.o src/tls.o src/getopt.o src/inet_ntop.o src/args.o src/trie.o deps/cyclone/aes.o deps/cyclone/cpu_endian.o deps/cyclone/ecb.o deps/cyclone/gcm.o deps/cyclone/hkdf.o deps/cyclone/hmac.o deps/cyclone/sha256.o +kyoutubeUnblock-objs := src/kytunblock.o src/dpi.c src/mangle.o src/quic.o src/quic_crypto.o src/utils.o src/tls.o src/getopt.o src/inet_ntop.o src/args.o src/trie.o deps/cyclone/aes.o deps/cyclone/cpu_endian.o deps/cyclone/ecb.o deps/cyclone/gcm.o deps/cyclone/hkdf.o deps/cyclone/hmac.o deps/cyclone/sha256.o ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement -I$(src)/src -I$(src)/deps/cyclone/include diff --git a/src/args.c b/src/args.c index 0b3bc06..e66bb51 100644 --- a/src/args.c +++ b/src/args.c @@ -215,7 +215,7 @@ static int parse_faking_strategy(char *optarg, int *faking_strategy) { return 0; } -static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) { +static int parse_dport_range(char *str, struct dport_range **udpr, int *udpr_len) { int seclen = 1; const char *p = str; while (*p != '\0') { @@ -225,14 +225,14 @@ static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int * } #ifdef KERNEL_SPACE - struct udp_dport_range *udp_dport_ranges = kmalloc( - seclen * sizeof(struct udp_dport_range), GFP_KERNEL); + struct dport_range *dport_ranges = kmalloc( + seclen * sizeof(struct port_range), GFP_KERNEL); #else - struct udp_dport_range *udp_dport_ranges = malloc( - seclen * sizeof(struct udp_dport_range)); + struct dport_range *dport_ranges = malloc( + seclen * sizeof(struct dport_range)); #endif - if (udp_dport_ranges == NULL) { + if (dport_ranges == NULL) { return -ENOMEM; } @@ -279,7 +279,7 @@ static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int * ) goto erret; - udp_dport_ranges[i] = (struct udp_dport_range){ + dport_ranges[i] = (struct dport_range){ .start = num1, .end = num2 }; @@ -297,15 +297,15 @@ static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int * } if (i == 0) { - free(udp_dport_ranges); + free(dport_ranges); } - *udpr = udp_dport_ranges; + *udpr = dport_ranges; *udpr_len = i; return 0; erret: - free(udp_dport_ranges); + free(dport_ranges); return -1; } @@ -350,6 +350,7 @@ enum { OPT_EXCLUDE_DOMAINS, OPT_SNI_DOMAINS_FILE, OPT_EXCLUDE_DOMAINS_FILE, + OPT_TCP_DPORT_FILTER, OPT_FAKE_SNI, OPT_FAKING_TTL, OPT_FAKING_STRATEGY, @@ -397,6 +398,7 @@ enum { OPT_HELP, OPT_VERSION, OPT_CONNBYTES_LIMIT, + OPT_TCP_M_CONNPKTS, }; static struct option long_opt[] = { @@ -410,6 +412,7 @@ static struct option long_opt[] = { {"synfake", 1, 0, OPT_SYNFAKE}, {"synfake-len", 1, 0, OPT_SYNFAKE_LEN}, {"tls", 1, 0, OPT_TLS_ENABLED}, + {"tcp-dport-filter", 1, 0, OPT_TCP_DPORT_FILTER}, {"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}, @@ -435,6 +438,7 @@ static struct option long_opt[] = { {"udp-stun-filter", 0, 0, OPT_UDP_STUN_FILTER}, {"udp-filter-quic", 1, 0, OPT_UDP_FILTER_QUIC}, {"no-dport-filter", 0, 0, OPT_NO_DPORT_FILTER}, + {"tcp-match-connpackets", 1, 0, OPT_TCP_M_CONNPKTS}, {"threads", 1, 0, OPT_THREADS}, {"silent", 0, 0, OPT_SILENT}, {"trace", 0, 0, OPT_TRACE}, @@ -476,6 +480,7 @@ void print_usage(const char *argv0) { printf("\t--sni-domains-file=\n"); printf("\t--exclude-domains-file=\n"); printf("\t--tls={enabled|disabled}\n"); + printf("\t--tcp-dport-filter=<5,6,200-500>\n"); printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni-seq-len=\n"); printf("\t--fake-sni-type={default|random|custom}\n"); @@ -507,6 +512,7 @@ void print_usage(const char *argv0) { printf("\t--threads=\n"); printf("\t--packet-mark=\n"); printf("\t--connbytes-limit=\n"); + printf("\t--tcp-match-connpackets=\n"); printf("\t--silent\n"); printf("\t--trace\n"); printf("\t--instaflush\n"); @@ -788,6 +794,23 @@ int yparse_args(struct config_t *config, int argc, char *argv[]) { sect_config->frag_sni_pos = num; break; + case OPT_TCP_DPORT_FILTER: + { + SFREE(sect_config->tcp_dport_range); + if (parse_dport_range(optarg, §_config->tcp_dport_range, §_config->tcp_dport_range_len) < 0) { + goto invalid_opt; + } + break; + } + case OPT_TCP_M_CONNPKTS: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0) { + goto invalid_opt; + } + + sect_config->tcp_match_connpkts = num; + break; + case OPT_FAKING_STRATEGY: if (parse_faking_strategy( optarg, §_config->faking_strategy) < 0) { @@ -982,7 +1005,7 @@ int yparse_args(struct config_t *config, int argc, char *argv[]) { 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) { + if (parse_dport_range(optarg, §_config->udp_dport_range, §_config->udp_dport_range_len) < 0) { goto invalid_opt; } break; @@ -1055,8 +1078,26 @@ static size_t print_config_section(const struct section_config_t *section, char size_t buf_sz = buffer_size; size_t sz; - if (section->tls_enabled) { - print_cnf_buf("--tls=enabled"); + if (section->tcp_dport_range_len != 0) { + print_cnf_raw("--tcp-dport-filter="); + for (int i = 0; i < section->tcp_dport_range_len; i++) { + struct dport_range range = section->tcp_dport_range[i]; + print_cnf_raw("%d-%d,", range.start, range.end); + } + print_cnf_raw(" "); + + } + + if (section->tcp_match_connpkts) { + print_cnf_buf("--tcp-match-connpackets=%d", + section->tcp_match_connpkts); + } + + if (section->tls_enabled || section->tcp_dport_range_len != 0) { + if (section->tls_enabled) { + print_cnf_buf("--tls=enabled"); + } + switch(section->fragmentation_strategy) { case FRAG_STRAT_IP: @@ -1205,7 +1246,7 @@ static size_t print_config_section(const struct section_config_t *section, char 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]; + struct dport_range range = section->udp_dport_range[i]; print_cnf_raw("%d-%d,", range.start, range.end); } print_cnf_raw(" "); @@ -1364,6 +1405,10 @@ void free_config_section(struct section_config_t *section) { SFREE(section->udp_dport_range); } + if (section->tcp_dport_range_len != 0) { + SFREE(section->tcp_dport_range); + } + free_sni_domains(§ion->sni_domains); free_sni_domains(§ion->exclude_sni_domains); diff --git a/src/config.h b/src/config.h index 4bfb6f8..c5507eb 100644 --- a/src/config.h +++ b/src/config.h @@ -48,7 +48,7 @@ struct logging_config_t { }; extern struct logging_config_t logging_conf; -struct udp_dport_range { +struct dport_range { uint16_t start; uint16_t end; }; @@ -64,6 +64,11 @@ struct section_config_t { int tls_enabled; + struct dport_range *tcp_dport_range; + int tcp_dport_range_len; + + int tcp_match_connpkts; + int fragmentation_strategy; int frag_sni_reverse; int frag_sni_faked; @@ -106,7 +111,7 @@ struct section_config_t { unsigned int udp_fake_len; int udp_faking_strategy; - struct udp_dport_range *udp_dport_range; + struct dport_range *udp_dport_range; int udp_dport_range_len; int udp_stun_filter; int udp_filter_quic; @@ -238,6 +243,9 @@ enum { .sni_domains = {0}, \ .exclude_sni_domains = {0}, \ .all_domains = 0, \ + .tcp_dport_range = NULL, \ + .tcp_dport_range_len = 0, \ + .tcp_match_connpkts = 0, \ .tls_enabled = 1, \ .frag_sni_reverse = 1, \ .frag_sni_faked = 0, \ diff --git a/src/dpi.c b/src/dpi.c new file mode 100644 index 0000000..17dc23c --- /dev/null +++ b/src/dpi.c @@ -0,0 +1,509 @@ +/* + youtubeUnblock - https://github.com/Waujito/youtubeUnblock + + Copyright (C) 2024-2025 Vadim Vetrov + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** +* dpi.c - Inspects packets for blocked patterns. +* "If you want to bypass the DPI, you should became the DPI" +*/ + +#define _GNU_SOURCE +#include "types.h" // IWYU pragma: keep +#ifndef KERNEL_SPACE +#include +#else +#include "linux/inet.h" +#endif + +#include "dpi.h" +#include "config.h" +#include "utils.h" +#include "quic.h" +#include "logging.h" +#include "tls.h" + +#include "mangle.h" + +int log_packet(const struct parsed_packet *pkt); + +#define MAX_FRAGMENTATION_PTS 16 + +struct fragmentation_points { + size_t payload_points[16]; + int used_points; +}; + +int process_packet(const struct config_t *config, const struct packet_data *pd) { + assert (config); + assert (pd); + + struct parsed_packet pkt = {0}; + int ret = 0; + + pkt.yct = pd->yct; + + lgtrace_start(); + + pkt.raw_payload = pd->payload; + pkt.raw_payload_len = pd->payload_len; + + if (pkt.raw_payload_len > MAX_PACKET_SIZE) { + return PKT_ACCEPT; + } + + pkt.ipver = netproto_version(pkt.raw_payload, pkt.raw_payload_len); + + lgtrace_wr("IPv%d ", pkt.ipver); + + pkt.transport_proto = -1; + if (pkt.ipver == IP4VERSION) { + ret = ip4_payload_split((uint8_t *)pkt.raw_payload, pkt.raw_payload_len, + (struct iphdr **)&pkt.iph, &pkt.iph_len, + (uint8_t **)&pkt.ip_payload, &pkt.ip_payload_len); + + if (ret < 0) + goto accept; + + pkt.transport_proto = pkt.iph->protocol; + } +#ifndef NO_IPV6 + else if (pkt.ipver == IP6VERSION && config->use_ipv6) { + ret = ip6_payload_split((uint8_t *)pkt.raw_payload, pkt.raw_payload_len, + (struct ip6_hdr **)&pkt.ip6h, &pkt.iph_len, + (uint8_t **)&pkt.ip_payload, &pkt.ip_payload_len); + + + if (ret < 0) + goto accept; + + pkt.transport_proto = pkt.ip6h->ip6_nxt; + } +#endif + + if (pkt.transport_proto == IPPROTO_TCP) { + int ret = tcp_payload_split((uint8_t *)pkt.raw_payload, pkt.raw_payload_len, + NULL, NULL, + (struct tcphdr **)&pkt.tcph, &pkt.tcph_len, + (uint8_t **)&pkt.transport_payload, &pkt.transport_payload_len); + if (ret < 0) + goto accept; + } else if (pkt.transport_proto == IPPROTO_UDP) { + int ret = udp_payload_split((uint8_t *)pkt.raw_payload, pkt.raw_payload_len, + NULL, NULL, + (struct udphdr **)&pkt.udph, + (uint8_t **)&pkt.transport_payload, &pkt.transport_payload_len); + + if (ret < 0) + goto accept; + } + + if (LOG_LEVEL >= VERBOSE_TRACE) { + log_packet(&pkt); + } + + int verdict = PKT_CONTINUE; + + ITER_CONFIG_SECTIONS(config, section) { + lgtrace_wr("Section #%d: ", CONFIG_SECTION_NUMBER(section)); + + switch (pkt.transport_proto) { + case IPPROTO_TCP: + verdict = process_tcp_packet(section, &pkt); + break; + case IPPROTO_UDP: + verdict = process_udp_packet(section, &pkt); + break; + } + + if (verdict == PKT_CONTINUE) { + lgtrace_wr("continue_flow"); + lgtrace_write(); + continue; + } + + lgtrace_write(); + goto ret_verdict; + } + +accept: + verdict = PKT_ACCEPT; + +ret_verdict: + + switch (verdict) { + case PKT_ACCEPT: + lgtrace_wr("accept"); + break; + case PKT_DROP: + lgtrace_wr("drop"); + break; + default: + lgtrace_wr("unknown verdict: %d", verdict); + } + lgtrace_end(); + + return verdict; +} + +enum tls_proc_verdict { + TLS_NOT_MATCHED, + TLS_ERROR, + TLS_MATCHED, +}; + +enum tls_proc_verdict process_tls_packet(const struct section_config_t *section, + const struct parsed_packet *pkt, + struct fragmentation_points *frag_pts); + + +int perform_attack(const struct section_config_t *section, + const struct parsed_packet *pkt, const struct fragmentation_points *frag_pts); + +int process_tcp_packet(const struct section_config_t *section, const struct parsed_packet *pkt) { + assert (section); + assert (pkt); + + assert (pkt->transport_proto == IPPROTO_TCP); + + uint16_t dport = ntohs(pkt->tcph->dest); + + if (section->tcp_dport_range_len) { + int is_dport_matched = 0; + + for (int i = 0; i < section->tcp_dport_range_len; i++) { + struct dport_range crange = section->tcp_dport_range[i]; + if (dport >= crange.start && dport <= crange.end) { + lgtrace_addp("matched to %d-%d", crange.start, crange.end); + is_dport_matched = 1; + } + } + + if (!is_dport_matched) { + return PKT_CONTINUE; + } + } else if (section->dport_filter && dport != 443) { + return PKT_CONTINUE; + } + + if (pkt->tcph->syn && section->synfake) { + return send_synfake(section, pkt); + } + + if (pkt->tcph->syn) + return PKT_CONTINUE; + + int is_matched = 0; + struct fragmentation_points frag_pts = {0}; + + if (!is_matched && section->tls_enabled) { + enum tls_proc_verdict vrd = process_tls_packet(section, pkt, &frag_pts); + + if (vrd == TLS_ERROR) { + return PKT_ACCEPT; + } + + if (vrd == TLS_MATCHED) { + is_matched = 1; + } + } + + if (!is_matched && section->tcp_match_connpkts && pkt->yct.orig_packets) { + if (pkt->yct.orig_packets <= section->tcp_match_connpkts) { + lgtrace_addp("connpackets match: %lu <= %d", + pkt->yct.orig_packets, section->tcp_match_connpkts); + is_matched = 1; + + frag_pts.used_points = 0; + if (section->frag_sni_pos && + pkt->transport_payload_len > section->frag_sni_pos) { + frag_pts.payload_points[frag_pts.used_points++] = + section->frag_sni_pos; + lgtrace_addp("frag set to %d", section->frag_sni_pos); + } + } + } + + + if (is_matched) { + return perform_attack(section, pkt, &frag_pts); + } + + return PKT_CONTINUE; +} + +static void bubblesort(size_t arr[], size_t n){ + for (int i = 0; i < n - 1; i++) { + for (int j = 0; j < n - 1 - i; j++) { + if (arr[j] > arr[j + 1]) { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } +} + +enum tls_proc_verdict process_tls_packet(const struct section_config_t *section, + const struct parsed_packet *pkt, + struct fragmentation_points *frag_pts) { + assert (section); + assert (pkt); + + + struct tls_verdict vrd = analyze_tls_data(section, + pkt->transport_payload, pkt->transport_payload_len); + lgtrace_addp("TLS analyzed"); + + if (vrd.sni_len != 0) { + lgtrace_addp("SNI detected: %.*s", vrd.sni_len, vrd.sni_ptr); + } + + if (vrd.target_sni) { + lgdebug("Target SNI detected: %.*s", vrd.sni_len, vrd.sni_ptr); + size_t target_sni_offset = vrd.target_sni_ptr - pkt->transport_payload; + + size_t ipd_offset = target_sni_offset; + size_t mid_offset = ipd_offset + vrd.target_sni_len / 2; + + // hardcode googlevideo.com split + // googlevideo domains are very long, so + // it is possible for the entire domain to not be + // splitted (split goes for subdomain) + if (vrd.target_sni_len > 30) { + mid_offset = ipd_offset + + vrd.target_sni_len - 12; + } + + frag_pts->used_points = 0; + + if (section->frag_sni_pos && pkt->transport_payload_len > section->frag_sni_pos) { + frag_pts->payload_points[frag_pts->used_points++] = section->frag_sni_pos; + } + + if (section->frag_middle_sni) { + frag_pts->payload_points[frag_pts->used_points++] = mid_offset; + } + + bubblesort(frag_pts->payload_points, frag_pts->used_points); + + return TLS_MATCHED; + } + + return TLS_NOT_MATCHED; +} + +int perform_attack(const struct section_config_t *section, + const struct parsed_packet *pkt, const struct fragmentation_points *frag_pts) { + assert (section); + assert (pkt); + assert (frag_pts); + + int ret = 0; + + size_t payload_len = pkt->raw_payload_len; + uint8_t *payload = malloc(pkt->raw_payload_len); + if (payload == NULL) { + lgerror(-ENOMEM, "Allocation error"); + return PKT_ACCEPT; + } + + memcpy(payload, pkt->raw_payload, pkt->raw_payload_len); + + if (pkt->transport_payload_len > AVAILABLE_MTU) { + lgdebug("WARNING! Tartget packet is too big and may cause issues!"); + } + + if (section->fake_sni) { + void *iph; + size_t iph_len; + struct tcphdr *tcph; + size_t tcph_len; + uint8_t *data; + size_t dlen; + + int ret = tcp_payload_split(payload, payload_len, + &iph, &iph_len, &tcph, &tcph_len, + &data, &dlen); + + if (ret < 0) { + lgerror(ret, "tcp_payload_split in targ_sni"); + goto accept_lc; + } + + if (section->fk_winsize) { + tcph->window = htons(section->fk_winsize); + set_tcp_checksum(tcph, iph, iph_len); + } + + + struct fake_type f_type = args_default_fake_type(section); + post_fake_sni(f_type, iph, iph_len, tcph, tcph_len); + } + + + if (frag_pts->used_points > 0) { + if (section->fragmentation_strategy == FRAG_STRAT_TCP) { + ret = send_tcp_frags(section, payload, payload_len, frag_pts->payload_points, + frag_pts->used_points, 0); + if (ret < 0) { + lgerror(ret, "tcp4 send frags"); + goto accept_lc; + } + + goto drop_lc; + } else if (section->fragmentation_strategy == FRAG_STRAT_IP && pkt->ipver == IP4VERSION) { + ret = send_ip4_frags(section, payload, payload_len, frag_pts->payload_points, + frag_pts->used_points, 0); + if (ret < 0) { + lgerror(ret, "tcp4 send frags"); + goto accept_lc; + } + + goto drop_lc; + + } else if (section->fragmentation_strategy == FRAG_STRAT_IP && pkt->ipver != IP4VERSION) { + lginfo("WARNING: IP fragmentation is supported only for IPv4"); + goto accept_lc; + } + } + + +accept_lc: + free(payload); + return PKT_ACCEPT; +drop_lc: + free(payload); + return PKT_DROP; +} + +int process_udp_packet(const struct section_config_t *section, const struct parsed_packet *pkt) { + assert (section); + assert (pkt); + + assert (pkt->transport_proto == IPPROTO_UDP); + + int ret = 0; + + if (!detect_udp_filtered(section, pkt->raw_payload, pkt->raw_payload_len)) + goto continue_flow; + + if (section->udp_mode == UDP_MODE_DROP) + goto drop; + else if (section->udp_mode == UDP_MODE_FAKE) { + for (int i = 0; i < section->udp_fake_seq_len; i++) { + uint8_t *fake_udp = NULL; + size_t fake_udp_len = 0; + + struct udp_fake_type fake_type = { + .fake_len = section->udp_fake_len, + .strategy = { + .strategy = section->udp_faking_strategy, + .faking_ttl = section->faking_ttl, + }, + }; + ret = gen_fake_udp(fake_type, pkt->iph, pkt->iph_len, pkt->udph, + &fake_udp, &fake_udp_len); + if (ret < 0) { + lgerror(ret, "gen_fake_udp"); + goto erret; + } + + lgtrace_addp("post fake udp #%d", i + 1); + + ret = instance_config.send_raw_packet(fake_udp, fake_udp_len); + if (ret < 0) { + lgerror(ret, "send fake udp"); + goto erret_lc; + } + + free(fake_udp); + continue; +erret_lc: + free(fake_udp); +erret: + goto accept; + } + + + // requeue + ret = instance_config.send_raw_packet(pkt->raw_payload, pkt->raw_payload_len); + goto drop; + } + +continue_flow: + return PKT_CONTINUE; +accept: + return PKT_ACCEPT; +drop: + return PKT_DROP; +} + +int log_packet(const struct parsed_packet *pkt) { + int ret = 0; + + const char *bpt = inet_ntop( + pkt->ipver == IP4VERSION ? AF_INET : AF_INET6, + pkt->ipver == IP4VERSION ? (void *)(&pkt->iph->saddr) : + (void *)(&pkt->ip6h->ip6_src), + ylgh_curptr, ylgh_leftbuf); + if (bpt != NULL) { + ret = strnlen(bpt, ylgh_leftbuf); + ylgh_leftbuf -= ret; + ylgh_curptr += ret; + } + + lgtrace_wr(" => "); + + bpt = inet_ntop( + pkt->ipver == IP4VERSION ? AF_INET : AF_INET6, + pkt->ipver == IP4VERSION ? (void *)(&pkt->iph->daddr) : + (void *)(&pkt->ip6h->ip6_dst), + ylgh_curptr, ylgh_leftbuf); + if (bpt != NULL) { + ret = strnlen(bpt, ylgh_leftbuf); + ylgh_leftbuf -= ret; + ylgh_curptr += ret; + + } + + lgtrace_wr(" "); + int sport = -1, dport = -1; + + if (pkt->transport_proto == IPPROTO_TCP) { + lgtrace_wr("TCP "); + + sport = ntohs(pkt->tcph->source); + dport = ntohs(pkt->tcph->dest); + + } else if (pkt->transport_proto == IPPROTO_UDP) { + lgtrace_wr("UDP "); + + sport = ntohs(pkt->udph->source); + dport = ntohs(pkt->udph->dest); + } + + lgtrace_wr("%d => %d ", sport, dport); + lgtrace_write(); + + lgtrace_wr("Transport payload: [ "); + for (int i = 0; i < min((int)16, (int)pkt->transport_payload_len); i++) { + lgtrace_wr("%02x ", pkt->transport_payload[i]); + } + lgtrace_wr("]"); + lgtrace_write(); +} diff --git a/src/dpi.h b/src/dpi.h new file mode 100644 index 0000000..17dd2de --- /dev/null +++ b/src/dpi.h @@ -0,0 +1,87 @@ +/* + youtubeUnblock - https://github.com/Waujito/youtubeUnblock + + Copyright (C) 2024-2025 Vadim Vetrov + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef YU_DPI_H +#define YU_DPI_H + +#include "types.h" +#include "tls.h" +#include "config.h" + +#define PKT_ACCEPT 0 +#define PKT_DROP 1 +// Used for section config +#define PKT_CONTINUE 2 + +struct parsed_packet { + const uint8_t *raw_payload; + uint32_t raw_payload_len; + + int ipver; + union { + void *ipxh; + const struct iphdr *iph; + +#ifndef NO_IPV6 + const struct ip6_hdr *ip6h; +#endif + }; + size_t iph_len; + + const uint8_t *ip_payload; + size_t ip_payload_len; + + int transport_proto; + union { + struct { + const struct tcphdr *tcph; + size_t tcph_len; + }; + struct { + const struct udphdr *udph; + }; + }; + + const uint8_t *transport_payload; + size_t transport_payload_len; + + struct ytb_conntrack yct; +}; + +/** + * Processes the packet and returns verdict. + * This is the primary function that traverses the packet. + */ +int process_packet(const struct config_t *config, const struct packet_data *pd); + + +/** + * Processe the TCP packet. + * Returns verdict. + */ +int process_tcp_packet(const struct section_config_t *section, const struct parsed_packet *pkt); + + +/** + * Processes the UDP packet. + * Returns verdict. + */ +int process_udp_packet(const struct section_config_t *section, const struct parsed_packet *pkt); + +#endif /* DPI_H */ diff --git a/src/mangle.c b/src/mangle.c index c7e37d5..78bb9f6 100644 --- a/src/mangle.c +++ b/src/mangle.c @@ -25,6 +25,7 @@ #include "quic.h" #include "logging.h" #include "tls.h" +#include "dpi.h" #ifndef KERNEL_SPACE #include @@ -32,493 +33,53 @@ #include "linux/inet.h" #endif -int process_packet(const struct config_t *config, const struct packet_data *pd) { - const uint8_t *raw_payload = pd->payload; - uint32_t raw_payload_len = pd->payload_len; +int send_synfake(const struct section_config_t *section, const struct parsed_packet *pkt) { + assert (section); + assert (pkt); - if (raw_payload_len > MAX_PACKET_SIZE) { + assert (pkt->transport_proto == IPPROTO_TCP); + assert (pkt->tcph->syn); + + lgtrace_addp("TCP syn alter"); + + size_t fake_len = section->fake_sni_pkt_sz; + if (section->synfake_len) + fake_len = min((int)section->synfake_len, (int)fake_len); + + + size_t payload_len = pkt->iph_len + pkt->tcph_len + fake_len; + uint8_t *payload = malloc(payload_len); + if (payload == NULL) { + lgerror(-ENOMEM, "Allocation error"); return PKT_ACCEPT; } - const struct iphdr *iph; - const struct ip6_hdr *ip6h; - size_t iph_len; - const uint8_t *ip_payload; - size_t ip_payload_len; - const char *bpt; + memcpy(payload, pkt->ipxh, pkt->iph_len); + memcpy(payload + pkt->iph_len, pkt->tcph, pkt->tcph_len); + memcpy(payload + pkt->iph_len + pkt->tcph_len, section->fake_sni_pkt, fake_len); - int transport_proto = -1; - int ipver = netproto_version(raw_payload, raw_payload_len); - int ret; - - lgtrace_start(); - - lgtrace_wr("IPv%d ", ipver); - - if (ipver == IP4VERSION) { - ret = ip4_payload_split((uint8_t *)raw_payload, raw_payload_len, - (struct iphdr **)&iph, &iph_len, - (uint8_t **)&ip_payload, &ip_payload_len); - - if (ret < 0) - goto accept; - - transport_proto = iph->protocol; - - } -#ifndef NO_IPV6 - else if (ipver == IP6VERSION && config->use_ipv6) { - ret = ip6_payload_split((uint8_t *)raw_payload, raw_payload_len, - (struct ip6_hdr **)&ip6h, &iph_len, - (uint8_t **)&ip_payload, &ip_payload_len); - - if (ret < 0) - goto accept; - - transport_proto = ip6h->ip6_nxt; - - } -#endif - else { - lgtrace("Unknown layer 3 protocol version: %d", ipver); - goto accept; + struct tcphdr *tcph = (struct tcphdr *)(payload + pkt->iph_len); + if (pkt->ipver == IP4VERSION) { + struct iphdr *iph = (struct iphdr *)payload; + iph->tot_len = htons(pkt->iph_len + pkt->tcph_len + fake_len); + set_ip_checksum(payload, pkt->iph_len); + set_tcp_checksum(tcph, iph, pkt->iph_len); + } else if (pkt->ipver == IP6VERSION) { + struct ip6_hdr *ip6h = (struct ip6_hdr *)payload; + ip6h->ip6_plen = ntohs(pkt->tcph_len + fake_len); + set_ip_checksum(ip6h, pkt->iph_len); + set_tcp_checksum(tcph, ip6h, pkt->iph_len); } - if (LOG_LEVEL >= VERBOSE_TRACE) { - bpt = inet_ntop( - ipver == IP4VERSION ? AF_INET : AF_INET6, - ipver == IP4VERSION ? (void *)(&iph->saddr) : - (void *)(&ip6h->ip6_src), - ylgh_curptr, ylgh_leftbuf); - if (bpt != NULL) { - ret = strnlen(bpt, ylgh_leftbuf); - ylgh_leftbuf -= ret; - ylgh_curptr += ret; - } - - lgtrace_wr(" => "); - - bpt = inet_ntop( - ipver == IP4VERSION ? AF_INET : AF_INET6, - ipver == IP4VERSION ? (void *)(&iph->daddr) : - (void *)(&ip6h->ip6_dst), - ylgh_curptr, ylgh_leftbuf); - if (bpt != NULL) { - ret = strnlen(bpt, ylgh_leftbuf); - ylgh_leftbuf -= ret; - ylgh_curptr += ret; - - } - - lgtrace_wr(" "); - const uint8_t *transport_payload = NULL; - size_t transport_payload_len = 0; - int sport = -1, dport = -1; - - if (transport_proto == IPPROTO_TCP) { - lgtrace_wr("TCP "); - const struct tcphdr *tcph; - ret = tcp_payload_split((uint8_t *)raw_payload, raw_payload_len, - NULL, NULL, - (struct tcphdr **)&tcph, NULL, - (uint8_t **)&transport_payload, &transport_payload_len); - - if (ret == 0) { - sport = ntohs(tcph->source); - dport = ntohs(tcph->dest); - } - - } else if (transport_proto == IPPROTO_UDP) { - lgtrace_wr("UDP "); - const struct udphdr *udph = ((const struct udphdr *)ip_payload); - ret = udp_payload_split((uint8_t *)raw_payload, raw_payload_len, - NULL, NULL, - (struct udphdr **)&udph, - (uint8_t **)&transport_payload, &transport_payload_len); - - if (ret == 0) { - sport = ntohs(udph->source); - dport = ntohs(udph->dest); - } - } - - lgtrace_wr("%d => %d ", sport, dport); - lgtrace_write(); - - lgtrace_wr("Transport payload: [ "); - for (int i = 0; i < min((int)16, (int)transport_payload_len); i++) { - lgtrace_wr("%02x ", transport_payload[i]); - } - lgtrace_wr("]"); - lgtrace_write(); - } - - int verdict = PKT_CONTINUE; - - ITER_CONFIG_SECTIONS(config, section) { - lgtrace_wr("Section #%d: ", CONFIG_SECTION_NUMBER(section)); - - switch (transport_proto) { - case IPPROTO_TCP: - verdict = process_tcp_packet(section, raw_payload, raw_payload_len); - break; - case IPPROTO_UDP: - verdict = process_udp_packet(section, raw_payload, raw_payload_len); - break; - } - - if (verdict == PKT_CONTINUE) { - lgtrace_wr("continue_flow"); - lgtrace_write(); - continue; - } - - lgtrace_write(); - goto ret_verdict; - } - -accept: - verdict = PKT_ACCEPT; - -ret_verdict: - - switch (verdict) { - case PKT_ACCEPT: - lgtrace_wr("accept"); - break; - case PKT_DROP: - lgtrace_wr("drop"); - break; - default: - lgtrace_wr("unknown verdict: %d", verdict); - } - lgtrace_end(); - - return verdict; -} - -int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, size_t raw_payload_len) { - const void *ipxh; - size_t iph_len; - const struct tcphdr *tcph; - size_t tcph_len; - const uint8_t *data; - size_t dlen; - - - int ipxv = netproto_version(raw_payload, raw_payload_len); - - int ret = tcp_payload_split((uint8_t *)raw_payload, raw_payload_len, - (void *)&ipxh, &iph_len, - (struct tcphdr **)&tcph, &tcph_len, - (uint8_t **)&data, &dlen); - - + int ret = instance_config.send_raw_packet(payload, payload_len); if (ret < 0) { + lgerror(ret, "send_syn_altered"); + + free(payload); return PKT_ACCEPT; } - // As defined by TLS standard. - if (section->dport_filter && ntohs(tcph->dest) != 443) { - return PKT_ACCEPT; - } - - if (tcph->syn && section->synfake) { - lgtrace_addp("TCP syn alter"); - - size_t fake_len = section->fake_sni_pkt_sz; - if (section->synfake_len) - fake_len = min((int)section->synfake_len, (int)fake_len); - - - size_t payload_len = iph_len + tcph_len + fake_len; - uint8_t *payload = malloc(payload_len); - if (payload == NULL) { - lgerror(-ENOMEM, "Allocation error"); - return PKT_ACCEPT; - } - - memcpy(payload, ipxh, iph_len); - memcpy(payload + iph_len, tcph, tcph_len); - memcpy(payload + iph_len + tcph_len, section->fake_sni_pkt, fake_len); - - struct tcphdr *tcph = (struct tcphdr *)(payload + iph_len); - if (ipxv == IP4VERSION) { - struct iphdr *iph = (struct iphdr *)payload; - iph->tot_len = htons(iph_len + tcph_len + fake_len); - set_ip_checksum(payload, iph_len); - set_tcp_checksum(tcph, iph, iph_len); - } else if (ipxv == IP6VERSION) { - struct ip6_hdr *ip6h = (struct ip6_hdr *)payload; - ip6h->ip6_plen = ntohs(tcph_len + fake_len); - set_ip_checksum(ip6h, iph_len); - set_tcp_checksum(tcph, ip6h, iph_len); - } - - - ret = instance_config.send_raw_packet(payload, payload_len); - if (ret < 0) { - lgerror(ret, "send_syn_altered"); - - free(payload); - return PKT_ACCEPT; - } - - free(payload); - return PKT_DROP; - } - - if (tcph->syn) - return PKT_CONTINUE; - - if (!section->tls_enabled) - return PKT_CONTINUE; - - struct tls_verdict vrd = analyze_tls_data(section, data, dlen); - lgtrace_addp("TLS analyzed"); - - if (vrd.sni_len != 0) { - lgtrace_addp("SNI detected: %.*s", vrd.sni_len, vrd.sni_ptr); - } - - if (vrd.target_sni) { - lgdebug("Target SNI detected: %.*s", vrd.sni_len, vrd.sni_ptr); - size_t target_sni_offset = vrd.target_sni_ptr - data; - - - size_t payload_len = raw_payload_len; - uint8_t *payload = malloc(raw_payload_len); - if (payload == NULL) { - lgerror(-ENOMEM, "Allocation error"); - return PKT_ACCEPT; - } - memcpy(payload, raw_payload, raw_payload_len); - - void *iph; - size_t iph_len; - struct tcphdr *tcph; - size_t tcph_len; - uint8_t *data; - size_t dlen; - - int ret = tcp_payload_split(payload, payload_len, - &iph, &iph_len, &tcph, &tcph_len, - &data, &dlen); - - if (ret < 0) { - lgerror(ret, "tcp_payload_split in targ_sni"); - goto accept_lc; - } - - if (section->fk_winsize) { - tcph->window = htons(section->fk_winsize); - set_tcp_checksum(tcph, iph, iph_len); - } - - if (0) { - int delta = 2; - ret = seqovl_packet(payload, &payload_len, delta); - int ret = tcp_payload_split(payload, payload_len, - &iph, &iph_len, &tcph, &tcph_len, - &data, &dlen); - if (ret < 0) { - lgerror(ret, "seqovl_packet delta %d", delta); - } - } - - if (dlen > AVAILABLE_MTU) { - lgdebug("WARNING! Client Hello packet is too big and may cause issues!"); - } - - if (section->fake_sni) { - struct fake_type f_type = args_default_fake_type(section); - - // f_type.strategy.strategy = FAKE_STRAT_RAND_SEQ; - // f_type.strategy.randseq_offset = f_type.fake_len - 1; - // f_type.fake_data = "\x16"; - // f_type.fake_len = 1; - // - // post_fake_sni(f_type, iph, iph_len, tcph, tcph_len); - // - // f_type = args_default_fake_type(section); - // // f_type.strategy.strategy = FAKE_STRAT_RAND_SEQ; - // // f_type.strategy.randseq_offset = 0; - // f_type.fake_data += 1; - // f_type.fake_len -= 1; - - post_fake_sni(f_type, iph, iph_len, tcph, tcph_len); - } - - size_t ipd_offset; - size_t mid_offset; - - switch (section->fragmentation_strategy) { - case FRAG_STRAT_TCP: - { - ipd_offset = target_sni_offset; - mid_offset = ipd_offset + vrd.target_sni_len / 2; - - // hardcode googlevideo.com split - // googlevideo domains are very long, so - // it is possible for the entire domain to not be - // splitted (split goes for subdomain) - if (vrd.target_sni_len > 30) { - mid_offset = ipd_offset + - vrd.target_sni_len - 12; - } - - size_t poses[2]; - int cnt = 0; - - if (section->frag_sni_pos && dlen > section->frag_sni_pos) { - poses[cnt++] = section->frag_sni_pos; - } - - if (section->frag_middle_sni) { - poses[cnt++] = mid_offset; - } - - if (cnt > 1 && poses[0] > poses[1]) { - size_t tmp = poses[0]; - poses[0] = poses[1]; - poses[1] = tmp; - } - - ret = send_tcp_frags(section, payload, payload_len, poses, cnt, 0); - if (ret < 0) { - lgerror(ret, "tcp4 send frags"); - goto accept_lc; - } - - goto drop_lc; - } - break; - case FRAG_STRAT_IP: - if (ipxv == IP4VERSION) { - ipd_offset = ((char *)data - (char *)tcph) + target_sni_offset; - mid_offset = ipd_offset + vrd.target_sni_len / 2; - mid_offset += 8 - mid_offset % 8; - - size_t poses[2]; - int cnt = 0; - - if (section->frag_sni_pos && dlen > section->frag_sni_pos) { - poses[cnt] = section->frag_sni_pos + ((char *)data - (char *)tcph); - poses[cnt] += 8 - poses[cnt] % 8; - cnt++; - } - - if (section->frag_middle_sni) { - poses[cnt++] = mid_offset; - } - - if (cnt > 1 && poses[0] > poses[1]) { - size_t tmp = poses[0]; - poses[0] = poses[1]; - poses[1] = tmp; - } - - ret = send_ip4_frags(section, payload, payload_len, poses, cnt, 0); - if (ret < 0) { - lgerror(ret, "ip4 send frags"); - goto accept_lc; - } - - goto drop_lc; - } else { - lginfo("WARNING: IP fragmentation is supported only for IPv4"); - goto default_send; - } - break; - } - -default_send: - ret = instance_config.send_raw_packet(payload, payload_len); - if (ret < 0) { - lgerror(ret, "raw pack send"); - goto accept_lc; - } - - goto drop_lc; - -accept_lc: - free(payload); - return PKT_ACCEPT; -drop_lc: - free(payload); - return PKT_DROP; - } - - return PKT_CONTINUE; -} - -int process_udp_packet(const struct section_config_t *section, const uint8_t *pkt, size_t pktlen) { - const void *iph; - size_t iph_len; - const struct udphdr *udph; - const uint8_t *data; - size_t dlen; - - int ret = udp_payload_split((uint8_t *)pkt, pktlen, - (void **)&iph, &iph_len, - (struct udphdr **)&udph, - (uint8_t **)&data, &dlen); - - - if (ret < 0) { - lgtrace_addp("undefined"); - goto accept; - } - - if (!detect_udp_filtered(section, pkt, pktlen)) - goto continue_flow; - - if (section->udp_mode == UDP_MODE_DROP) - goto drop; - else if (section->udp_mode == UDP_MODE_FAKE) { - for (int i = 0; i < section->udp_fake_seq_len; i++) { - uint8_t *fake_udp; - size_t fake_udp_len; - - struct udp_fake_type fake_type = { - .fake_len = section->udp_fake_len, - .strategy = { - .strategy = section->udp_faking_strategy, - .faking_ttl = section->faking_ttl, - }, - }; - ret = gen_fake_udp(fake_type, iph, iph_len, udph, &fake_udp, &fake_udp_len); - if (ret < 0) { - lgerror(ret, "gen_fake_udp"); - goto erret; - } - - lgtrace_addp("post fake udp #%d", i + 1); - - ret = instance_config.send_raw_packet(fake_udp, fake_udp_len); - if (ret < 0) { - lgerror(ret, "send fake udp"); - goto erret_lc; - } - - free(fake_udp); - continue; -erret_lc: - free(fake_udp); -erret: - goto accept; - } - - - ret = instance_config.send_raw_packet(pkt, pktlen); - goto drop; - } - -continue_flow: - return PKT_CONTINUE; -accept: - return PKT_ACCEPT; -drop: + free(payload); return PKT_DROP; } diff --git a/src/mangle.h b/src/mangle.h index 1228867..2a34bee 100644 --- a/src/mangle.h +++ b/src/mangle.h @@ -23,32 +23,18 @@ #include "types.h" #include "tls.h" #include "config.h" +#include "dpi.h" #define PKT_ACCEPT 0 #define PKT_DROP 1 // Used for section config #define PKT_CONTINUE 2 -/** - * Processes the packet and returns verdict. - * This is the primary function that traverses the packet. - */ -int process_packet(const struct config_t *config, const struct packet_data *pd); - /** - * Processe the TCP packet. - * Returns verdict. + * Sends synfake message */ -int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, size_t raw_payload_len); - - -/** - * Processes the UDP packet. - * Returns verdict. - */ -int process_udp_packet(const struct section_config_t *section, const uint8_t *pkt, size_t pktlen); - +int send_synfake(const struct section_config_t *section, const struct parsed_packet *pkt); /** diff --git a/src/quic.c b/src/quic.c index 651a01e..3615724 100644 --- a/src/quic.c +++ b/src/quic.c @@ -579,7 +579,7 @@ int detect_udp_filtered(const struct section_config_t *section, match_port: for (int i = 0; i < section->udp_dport_range_len; i++) { - struct udp_dport_range crange = section->udp_dport_range[i]; + struct 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; diff --git a/src/types.h b/src/types.h index c18f90c..c7289c9 100644 --- a/src/types.h +++ b/src/types.h @@ -22,6 +22,8 @@ #define TYPES_H #include +#include + #ifdef KERNEL_SPACE #include // IWYU pragma: export #include // IWYU pragma: export diff --git a/src/youtubeUnblock.c b/src/youtubeUnblock.c index 8eed35b..793dc66 100644 --- a/src/youtubeUnblock.c +++ b/src/youtubeUnblock.c @@ -49,7 +49,7 @@ #include #include "config.h" -#include "mangle.h" +#include "dpi.h" #include "args.h" #include "utils.h" #include "logging.h" @@ -63,8 +63,9 @@ int raw6socket = -2; static struct config_t *cur_config = NULL; static int open_socket(struct mnl_socket **_nl) { - struct mnl_socket *nl = NULL; - nl = mnl_socket_open(NETLINK_NETFILTER); + assert (_nl); + + struct mnl_socket *nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) { lgerror(-errno, "mnl_socket_open"); @@ -83,15 +84,15 @@ static int open_socket(struct mnl_socket **_nl) { } -static int close_socket(struct mnl_socket **_nl) { - struct mnl_socket *nl = *_nl; - if (nl == NULL) return 1; - if (mnl_socket_close(nl) < 0) { +static int close_socket(struct mnl_socket **nl) { + assert (nl); + + if (*nl && mnl_socket_close(*nl) < 0) { lgerror(-errno, "mnl_socket_close"); return -1; } - *_nl = NULL; + *nl = NULL; return 0; } @@ -199,231 +200,6 @@ static int close_raw6_socket(void) { return 0; } -/* - * libnetfilter_conntrack - * (C) 2005-2012 by Pablo Neira Ayuso - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This code has been sponsored by Vyatta Inc. - */ - -enum ctattr_counters { - CTA_COUNTERS_UNSPEC, - CTA_COUNTERS_PACKETS, /* 64bit counters */ - CTA_COUNTERS_BYTES, /* 64bit counters */ - CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ - CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ - CTA_COUNTERS_PAD, - __CTA_COUNTERS_MAX -}; -#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) - -enum ctattr_type { - CTA_UNSPEC, - CTA_TUPLE_ORIG, - CTA_TUPLE_REPLY, - CTA_STATUS, - CTA_PROTOINFO, - CTA_HELP, - CTA_NAT_SRC, -#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ - CTA_TIMEOUT, - CTA_MARK, - CTA_COUNTERS_ORIG, - CTA_COUNTERS_REPLY, - CTA_USE, - CTA_ID, - CTA_NAT_DST, - CTA_TUPLE_MASTER, - CTA_SEQ_ADJ_ORIG, - CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, - CTA_SEQ_ADJ_REPLY, - CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, - CTA_SECMARK, /* obsolete */ - CTA_ZONE, - CTA_SECCTX, - CTA_TIMESTAMP, - CTA_MARK_MASK, - CTA_LABELS, - CTA_LABELS_MASK, - CTA_SYNPROXY, - CTA_FILTER, - CTA_STATUS_MASK, - __CTA_MAX -}; -#define CTA_MAX (__CTA_MAX - 1) - -enum { - __DIR_ORIG, - __DIR_REPL -}; - -static int -yct_parse_counters_attr_cb(const struct nlattr *attr, void *data) -{ - const struct nlattr **tb = data; - int type = mnl_attr_get_type(attr); - - if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0) - return MNL_CB_OK; - - switch(type) { - case CTA_COUNTERS_PACKETS: - case CTA_COUNTERS_BYTES: - if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) - return MNL_CB_ERROR; - break; - case CTA_COUNTERS32_PACKETS: - case CTA_COUNTERS32_BYTES: - if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) - return MNL_CB_ERROR; - break; - } - tb[type] = attr; - return MNL_CB_OK; -} - -static int -yct_parse_counters(const struct nlattr *attr, struct ytb_conntrack *yct, - int dir) -{ - struct nlattr *tb[CTA_COUNTERS_MAX+1] = {0}; - - if (mnl_attr_parse_nested(attr, yct_parse_counters_attr_cb, tb) < 0) - return -1; - - if (tb[CTA_COUNTERS_PACKETS] || tb[CTA_COUNTERS32_PACKETS]) { - uint64_t packets_counter; - if (tb[CTA_COUNTERS32_PACKETS]) { - packets_counter = - ntohl(mnl_attr_get_u32(tb[CTA_COUNTERS32_PACKETS])); - } - if (tb[CTA_COUNTERS_PACKETS]) { - packets_counter = - be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS])); - } - switch(dir) { - case __DIR_ORIG: - yct->orig_packets = packets_counter; - yct_set_mask_attr(YCTATTR_ORIG_PACKETS, yct); - break; - case __DIR_REPL: - yct->repl_packets = packets_counter; - yct_set_mask_attr(YCTATTR_REPL_PACKETS, yct); - break; - } - } - if (tb[CTA_COUNTERS_BYTES] || tb[CTA_COUNTERS32_BYTES]) { - uint64_t bytes_counter; - if (tb[CTA_COUNTERS32_BYTES]) { - bytes_counter = - ntohl(mnl_attr_get_u32(tb[CTA_COUNTERS32_BYTES])); - } - if (tb[CTA_COUNTERS_BYTES]) { - bytes_counter = - be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES])); - } - - switch(dir) { - case __DIR_ORIG: - yct->orig_bytes = bytes_counter; - yct_set_mask_attr(YCTATTR_ORIG_BYTES, yct); - break; - case __DIR_REPL: - yct->repl_bytes = bytes_counter; - yct_set_mask_attr(YCTATTR_REPL_BYTES, yct); - break; - } - } - - return 0; -} - -static int -yct_parse_conntrack_attr_cb(const struct nlattr *attr, void *data){ - const struct nlattr **tb = data; - int type = mnl_attr_get_type(attr); - - if (mnl_attr_type_valid(attr, CTA_MAX) < 0) - return MNL_CB_OK; - - switch(type) { - case CTA_TUPLE_ORIG: - case CTA_TUPLE_REPLY: - case CTA_TUPLE_MASTER: - case CTA_NAT_SEQ_ADJ_ORIG: - case CTA_NAT_SEQ_ADJ_REPLY: - case CTA_PROTOINFO: - case CTA_COUNTERS_ORIG: - case CTA_COUNTERS_REPLY: - case CTA_HELP: - case CTA_SECCTX: - case CTA_TIMESTAMP: - if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) - return MNL_CB_ERROR; - break; - case CTA_STATUS: - case CTA_TIMEOUT: - case CTA_MARK: - case CTA_SECMARK: - case CTA_USE: - case CTA_ID: - if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) - return MNL_CB_ERROR; - break; - case CTA_ZONE: - if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) - return MNL_CB_ERROR; - break; - case CTA_NAT_SRC: - case CTA_NAT_DST: - /* deprecated */ - break; - } - tb[type] = attr; - return MNL_CB_OK; -} - -static int -yct_payload_parse(const void *payload, size_t payload_len, - uint16_t l3num, struct ytb_conntrack *yct) -{ - struct nlattr *tb[CTA_MAX+1] = {0}; - - if (mnl_attr_parse_payload(payload, payload_len, - yct_parse_conntrack_attr_cb, tb) < 0) - return -1; - - if (tb[CTA_MARK]) { - yct->connmark = ntohl(mnl_attr_get_u32(tb[CTA_MARK])); - yct_set_mask_attr(YCTATTR_CONNMARK, yct); - } - - - if (tb[CTA_COUNTERS_ORIG]) { - if (yct_parse_counters(tb[CTA_COUNTERS_ORIG], - yct, __DIR_ORIG) < 0) - return -1; - } - - if (tb[CTA_ID]) { - yct->id = ntohl(mnl_attr_get_u32(tb[CTA_ID])); - yct_set_mask_attr(YCTATTR_CONNID, yct); - } - - if (tb[CTA_COUNTERS_REPLY]) { - if (yct_parse_counters(tb[CTA_COUNTERS_REPLY], - yct, __DIR_REPL) < 0) - return -1; - } - - return 0; -} - static int send_raw_ipv4(const uint8_t *pkt, size_t pktlen) { int ret; if (pktlen > AVAILABLE_MTU) return -ENOMEM; @@ -563,6 +339,227 @@ erret_lc: return ret; } +/* + * libnetfilter_conntrack + * (C) 2005-2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Vyatta Inc. + */ + +enum ctattr_counters { + CTA_COUNTERS_UNSPEC, + CTA_COUNTERS_PACKETS, /* 64bit counters */ + CTA_COUNTERS_BYTES, /* 64bit counters */ + CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ + CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ + CTA_COUNTERS_PAD, + __CTA_COUNTERS_MAX +}; +#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) + +enum ctattr_type { + CTA_UNSPEC, + CTA_TUPLE_ORIG, + CTA_TUPLE_REPLY, + CTA_STATUS, + CTA_PROTOINFO, + CTA_HELP, + CTA_NAT_SRC, +#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ + CTA_TIMEOUT, + CTA_MARK, + CTA_COUNTERS_ORIG, + CTA_COUNTERS_REPLY, + CTA_USE, + CTA_ID, + CTA_NAT_DST, + CTA_TUPLE_MASTER, + CTA_SEQ_ADJ_ORIG, + CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, + CTA_SEQ_ADJ_REPLY, + CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, + CTA_SECMARK, /* obsolete */ + CTA_ZONE, + CTA_SECCTX, + CTA_TIMESTAMP, + CTA_MARK_MASK, + CTA_LABELS, + CTA_LABELS_MASK, + CTA_SYNPROXY, + CTA_FILTER, + CTA_STATUS_MASK, + __CTA_MAX +}; +#define CTA_MAX (__CTA_MAX - 1) + +enum { + __DIR_ORIG, + __DIR_REPL +}; + +static int yct_parse_counters_attr_cb(const struct nlattr *attr, + void *data) { + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_COUNTERS_PACKETS: + case CTA_COUNTERS_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + return MNL_CB_ERROR; + break; + case CTA_COUNTERS32_PACKETS: + case CTA_COUNTERS32_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int yct_parse_counters(const struct nlattr *attr, + struct ytb_conntrack *yct, int dir) { + struct nlattr *tb[CTA_COUNTERS_MAX+1] = {0}; + + if (mnl_attr_parse_nested(attr, yct_parse_counters_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_COUNTERS_PACKETS] || tb[CTA_COUNTERS32_PACKETS]) { + uint64_t packets_counter; + if (tb[CTA_COUNTERS32_PACKETS]) { + packets_counter = + ntohl(mnl_attr_get_u32(tb[CTA_COUNTERS32_PACKETS])); + } + if (tb[CTA_COUNTERS_PACKETS]) { + packets_counter = + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS])); + } + switch(dir) { + case __DIR_ORIG: + yct->orig_packets = packets_counter; + yct_set_mask_attr(YCTATTR_ORIG_PACKETS, yct); + break; + case __DIR_REPL: + yct->repl_packets = packets_counter; + yct_set_mask_attr(YCTATTR_REPL_PACKETS, yct); + break; + } + } + if (tb[CTA_COUNTERS_BYTES] || tb[CTA_COUNTERS32_BYTES]) { + uint64_t bytes_counter; + if (tb[CTA_COUNTERS32_BYTES]) { + bytes_counter = + ntohl(mnl_attr_get_u32(tb[CTA_COUNTERS32_BYTES])); + } + if (tb[CTA_COUNTERS_BYTES]) { + bytes_counter = + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES])); + } + + switch(dir) { + case __DIR_ORIG: + yct->orig_bytes = bytes_counter; + yct_set_mask_attr(YCTATTR_ORIG_BYTES, yct); + break; + case __DIR_REPL: + yct->repl_bytes = bytes_counter; + yct_set_mask_attr(YCTATTR_REPL_BYTES, yct); + break; + } + } + + return 0; +} + +static int yct_parse_conntrack_attr_cb(const struct nlattr *attr, + void *data) { + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_ORIG: + case CTA_TUPLE_REPLY: + case CTA_TUPLE_MASTER: + case CTA_NAT_SEQ_ADJ_ORIG: + case CTA_NAT_SEQ_ADJ_REPLY: + case CTA_PROTOINFO: + case CTA_COUNTERS_ORIG: + case CTA_COUNTERS_REPLY: + case CTA_HELP: + case CTA_SECCTX: + case CTA_TIMESTAMP: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + break; + case CTA_STATUS: + case CTA_TIMEOUT: + case CTA_MARK: + case CTA_SECMARK: + case CTA_USE: + case CTA_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case CTA_ZONE: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + break; + case CTA_NAT_SRC: + case CTA_NAT_DST: + /* deprecated */ + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int yct_payload_parse(const void *payload, + size_t payload_len, uint16_t l3num, + struct ytb_conntrack *yct) { + struct nlattr *tb[CTA_MAX+1] = {0}; + + if (mnl_attr_parse_payload(payload, payload_len, + yct_parse_conntrack_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_MARK]) { + yct->connmark = ntohl(mnl_attr_get_u32(tb[CTA_MARK])); + yct_set_mask_attr(YCTATTR_CONNMARK, yct); + } + + + if (tb[CTA_COUNTERS_ORIG]) { + if (yct_parse_counters(tb[CTA_COUNTERS_ORIG], + yct, __DIR_ORIG) < 0) + return -1; + } + + if (tb[CTA_ID]) { + yct->id = ntohl(mnl_attr_get_u32(tb[CTA_ID])); + yct_set_mask_attr(YCTATTR_CONNID, yct); + } + + if (tb[CTA_COUNTERS_REPLY]) { + if (yct_parse_counters(tb[CTA_COUNTERS_REPLY], + yct, __DIR_REPL) < 0) + return -1; + } + + return 0; +} + // Per-queue data. Passed to queue_cb. struct queue_data { @@ -684,15 +681,16 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { packet.payload_len = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]); packet.payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); - if (attr[NFQA_CAP_LEN] != NULL && ntohl(mnl_attr_get_u32(attr[NFQA_CAP_LEN])) != packet.payload_len) { + if (attr[NFQA_CAP_LEN] != NULL && + ntohl(mnl_attr_get_u32(attr[NFQA_CAP_LEN])) != packet.payload_len) { lgerr("The packet was truncated! Skip!"); return fallback_accept_packet(id, *qdata); } if (attr[NFQA_MARK] != NULL) { // Skip packets sent by rawsocket to escape infinity loop. - if ((ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) & cur_config->mark) == - cur_config->mark) { + if (CHECK_BITFIELD(ntohl(mnl_attr_get_u32(attr[NFQA_MARK])), + cur_config->mark)) { return fallback_accept_packet(id, *qdata); } } diff --git a/uspace.mk b/uspace.mk index 0a34a9e..8e24e0a 100644 --- a/uspace.mk +++ b/uspace.mk @@ -34,7 +34,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock TEST_APP:=$(BUILD_DIR)/testYoutubeUnblock -SRCS := mangle.c args.c utils.c quic.c tls.c getopt.c quic_crypto.c inet_ntop.c trie.c +SRCS := mangle.c args.c utils.c quic.c tls.c getopt.c quic_crypto.c inet_ntop.c trie.c dpi.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) APP_EXEC := youtubeUnblock.c APP_OBJ := $(APP_EXEC:%.c=$(BUILD_DIR)/%.o)