From 16ba8801c127eccb78044e0dfe78792683741a92 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 8 Jan 2025 15:43:15 +0300 Subject: [PATCH 01/12] Concurrency defenders in config parse and module destroy This commit is parr of #213 fix. In this issue kernel module crashes on high bandwidth usage has been reported. The part of the problem is concurrency usage: when config gets freed, callbacks keep to depend on it. --- src/kargs.c | 31 ++++++++++++++++++++++++++++++- src/kytunblock.c | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/kargs.c b/src/kargs.c index a995c14..9fb0e42 100644 --- a/src/kargs.c +++ b/src/kargs.c @@ -24,11 +24,23 @@ #include "args.h" #include "logging.h" +/** +* Defined in kyoutubeUnblock.c +*/ +extern struct spinlock hot_config_spinlock; +extern atomic_t hot_config_counter; +extern atomic_t hot_config_rep; +extern struct mutex config_free_mutex; + #define MAX_ARGC 1024 static char *argv[MAX_ARGC]; static int params_set(const char *cval, const struct kernel_param *kp) { - int ret = 0; + int ret; + ret = mutex_trylock(&config_free_mutex); + if (ret == 0) + return -EBUSY; + int cv_len = strlen(cval); if (cv_len >= 1 && cval[cv_len - 1] == '\n') { @@ -58,8 +70,25 @@ static int params_set(const char *cval, const struct kernel_param *kp) { } } + spin_lock(&hot_config_spinlock); + // lock netfilter youtubeUnblock + atomic_set(&hot_config_rep, 1); + spin_unlock(&hot_config_spinlock); + + // lock config hot replacement process until all + // netfilter callbacks keep running + while (atomic_read(&hot_config_counter) > 0) {} + ret = yparse_args(argc, argv); + + spin_lock(&hot_config_spinlock); + // relaunch youtubeUnblock + atomic_set(&hot_config_rep, 0); + spin_unlock(&hot_config_spinlock); + kfree(val); + + mutex_unlock(&config_free_mutex); return ret; } diff --git a/src/kytunblock.c b/src/kytunblock.c index 483cc31..96c95c0 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -61,9 +61,15 @@ MODULE_AUTHOR("Vadim Vetrov "); MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock"); static struct socket *rawsocket; - static struct socket *raw6socket; +DEFINE_SPINLOCK(hot_config_spinlock); +DEFINE_MUTEX(config_free_mutex); +atomic_t hot_config_counter = ATOMIC_INIT(0); +// boolean flag for hot config replacement +// if 1, youtubeUnblock should stop processing +atomic_t hot_config_rep = ATOMIC_INIT(0); + static int open_raw_socket(void) { int ret = 0; ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket); @@ -385,6 +391,17 @@ static NF_CALLBACK(ykb_nf_hook, skb) { int ret; struct packet_data pd = {0}; + spin_lock(&hot_config_spinlock); + // if set flag to disable processing, + // explicitly accept all packets + if (atomic_read(&hot_config_rep)) { + spin_unlock(&hot_config_spinlock); + return NF_ACCEPT; + } else { + atomic_inc(&hot_config_counter); + } + spin_unlock(&hot_config_spinlock); + if ((skb->mark & config.mark) == config.mark) goto accept; @@ -422,8 +439,10 @@ static NF_CALLBACK(ykb_nf_hook, skb) { } accept: + atomic_dec(&hot_config_counter); return NF_ACCEPT; drop: + atomic_dec(&hot_config_counter); kfree_skb(skb); return NF_STOLEN; } @@ -505,6 +524,17 @@ static int __init ykb_init(void) { } static void __exit ykb_destroy(void) { + mutex_lock(&config_free_mutex); + // acquire all locks. + spin_lock(&hot_config_spinlock); + // lock netfilter youtubeUnblock + atomic_set(&hot_config_rep, 1); + spin_unlock(&hot_config_spinlock); + + // wait until all + // netfilter callbacks keep running + while (atomic_read(&hot_config_counter) > 0) {} + if (config.use_ipv6) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) struct net *n; @@ -513,7 +543,6 @@ static void __exit ykb_destroy(void) { #else nf_unregister_hook(&ykb6_nf_reg); #endif - close_raw6_socket(); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) @@ -523,9 +552,12 @@ static void __exit ykb_destroy(void) { #else nf_unregister_hook(&ykb_nf_reg); #endif + + if (config.use_ipv6) { + close_raw6_socket(); + } close_raw_socket(); - free_config(config); lginfo("youtubeUnblock kernel module destroyed.\n"); } From 37c8a798fd1bfc2682199e6349fa77808139690f Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 8 Jan 2025 23:58:55 +0300 Subject: [PATCH 02/12] Disallow to use --no-ipv6 in kernel space --- src/args.c | 8 ++++++ src/kytunblock.c | 66 ++++++++++++++++++++++++++---------------------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/args.c b/src/args.c index 2f6a2c8..db72ebd 100644 --- a/src/args.c +++ b/src/args.c @@ -478,7 +478,15 @@ int yparse_args(int argc, char *argv[]) { #endif break; case OPT_NO_IPV6: +#ifndef KERNEL_SPACE rep_config.use_ipv6 = 0; +#else + lgerr("--no-ipv6 argument is not available " + "in the kernel module. " + "If you want to disable ipv6, compile with " + "make kmake EXTRA_CFLAGS=\"-DNO_IPV6\"."); + goto invalid_opt; +#endif break; case OPT_DAEMONIZE: rep_config.daemonize = 1; diff --git a/src/kytunblock.c b/src/kytunblock.c index 96c95c0..2f4e959 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -464,10 +464,14 @@ static struct nf_hook_ops ykb6_nf_reg __read_mostly = { static int __init ykb_init(void) { #ifdef NO_CONNTRACK - lgwarning("Conntrack disabled."); + lgwarning("Conntrack is disabled."); +#endif +#ifdef NO_IPV6 + lgwarning("IPv6 is disabled."); #endif int ret = 0; + struct net *n; ret = init_config(&config); if (ret < 0) goto err; @@ -475,8 +479,6 @@ static int __init ykb_init(void) { if (ret < 0) goto err; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - struct net *n; - for_each_net(n) { ret = nf_register_net_hook(n, &ykb_nf_reg); if (ret < 0) { @@ -491,31 +493,35 @@ static int __init ykb_init(void) { #endif - if (config.use_ipv6) { - ret = open_raw6_socket(); - if (ret < 0) { - config.use_ipv6 = 0; - lgwarning("ipv6 disabled!"); - goto ipv6_fallback; - } +#ifndef NO_IPV6 + ret = open_raw6_socket(); + if (ret < 0) { + lgerror(ret, "ipv6 initialization failed!"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - struct net *n; - for_each_net(n) { - ret = nf_register_net_hook(n, &ykb6_nf_reg); - if (ret < 0) { - lgerror(ret, "register net6_hook"); - } - } + for_each_net(n) + nf_unregister_net_hook(n, &ykb_nf_reg); #else - ret = nf_register_hook(&ykb6_nf_reg); + nf_unregister_hook(&ykb_nf_reg); +#endif + goto err; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) + for_each_net(n) { + ret = nf_register_net_hook(n, &ykb6_nf_reg); if (ret < 0) { lgerror(ret, "register net6_hook"); } -#endif } +#else + ret = nf_register_hook(&ykb6_nf_reg); + if (ret < 0) { + lgerror(ret, "register net6_hook"); + } +#endif +#endif /* NO_IPV6 */ -ipv6_fallback: lginfo("youtubeUnblock kernel module started.\n"); return 0; @@ -524,6 +530,8 @@ static int __init ykb_init(void) { } static void __exit ykb_destroy(void) { + struct net *n; + mutex_lock(&config_free_mutex); // acquire all locks. spin_lock(&hot_config_spinlock); @@ -535,27 +543,25 @@ static void __exit ykb_destroy(void) { // netfilter callbacks keep running while (atomic_read(&hot_config_counter) > 0) {} - if (config.use_ipv6) { +#ifndef NO_IPV6 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - struct net *n; - for_each_net(n) - nf_unregister_net_hook(n, &ykb6_nf_reg); + for_each_net(n) + nf_unregister_net_hook(n, &ykb6_nf_reg); #else - nf_unregister_hook(&ykb6_nf_reg); + nf_unregister_hook(&ykb6_nf_reg); #endif - } +#endif /* NO_IPV6 */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - struct net *n; for_each_net(n) nf_unregister_net_hook(n, &ykb_nf_reg); #else nf_unregister_hook(&ykb_nf_reg); #endif - if (config.use_ipv6) { - close_raw6_socket(); - } +#ifndef NO_IPV6 + close_raw6_socket(); +#endif close_raw_socket(); free_config(config); From 9b58869864b69fa92901c6ee3f5d587e93589153 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 9 Jan 2025 00:53:56 +0300 Subject: [PATCH 03/12] Add --no-dport-filter flag This flag allows to reduce amount of network packet to analyze. --- README.md | 2 ++ src/args.c | 10 ++++++++++ src/config.h | 3 +++ src/mangle.c | 5 +++++ src/quic.c | 4 ++++ 5 files changed, 24 insertions(+) diff --git a/README.md b/README.md index e977668..2efdce5 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,8 @@ Flags that do not scoped to a specific section, used over all the youtubeUnblock - `--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`. +- `--no-dport-filter` By default, youtubeUnblock will filter for TLS and QUIC 443. If you want to disable it, pass this flag. (this does not affect `--udp-dport-filter`) + ## UDP/QUIC UDP is another communication protocol. Well-known technologies that use it are DNS, QUIC, voice chats. UDP does not provide reliable connection and its header is much simpler than TCP thus fragmentation is limited. The support provided primarily by faking. diff --git a/src/args.c b/src/args.c index db72ebd..828e0b0 100644 --- a/src/args.c +++ b/src/args.c @@ -269,6 +269,7 @@ enum { OPT_PACKET_MARK, OPT_SYNFAKE, OPT_SYNFAKE_LEN, + OPT_NO_DPORT_FILTER, OPT_SEG2DELAY, OPT_THREADS, OPT_SILENT, @@ -318,6 +319,7 @@ static struct option long_opt[] = { {"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}, + {"no-dport-filter", 0, 0, OPT_NO_DPORT_FILTER}, {"threads", 1, 0, OPT_THREADS}, {"silent", 0, 0, OPT_SILENT}, {"trace", 0, 0, OPT_TRACE}, @@ -381,6 +383,7 @@ void print_usage(const char *argv0) { printf("\t--udp-faking-strategy={checksum|ttl|none}\n"); printf("\t--udp-dport-filter=<5,6,200-500>\n"); printf("\t--udp-filter-quic={disabled|all|parse}\n"); + printf("\t--no-dport-filter\n"); printf("\t--threads=\n"); printf("\t--packet-mark=\n"); printf("\t--connbytes-limit=\n"); @@ -712,6 +715,9 @@ int yparse_args(int argc, char *argv[]) { sect_config->fk_winsize = num; break; + case OPT_NO_DPORT_FILTER: + sect_config->dport_filter = 0; + break; case OPT_SEG2DELAY: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { @@ -1014,6 +1020,10 @@ static size_t print_config_section(const struct section_config_t *section, char } } + if (section->dport_filter == 0) { + print_cnf_buf("--no-dport-filter"); + } + return buffer_size - buf_sz; } // Returns written buffer length diff --git a/src/config.h b/src/config.h index 141d43b..b2c57f3 100644 --- a/src/config.h +++ b/src/config.h @@ -92,6 +92,8 @@ struct section_config_t { unsigned int fk_winsize; int fakeseq_offset; + int dport_filter; + #define SNI_DETECTION_PARSE 0 #define SNI_DETECTION_BRUTE 1 int sni_detection; @@ -244,6 +246,7 @@ enum { .synfake = 0, \ .synfake_len = 0, \ \ + .dport_filter = 1, \ .seg2_delay = 0, \ \ .sni_detection = SNI_DETECTION_PARSE, \ diff --git a/src/mangle.c b/src/mangle.c index a584195..9f5cf86 100644 --- a/src/mangle.c +++ b/src/mangle.c @@ -214,6 +214,11 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra goto accept; } + // As defined by TLS standard. + if (section->dport_filter && ntohs(tcph->dest) != 443) { + goto accept; + } + if (tcph->syn && section->synfake) { lgtrace_addp("TCP syn alter"); diff --git a/src/quic.c b/src/quic.c index 5119331..5d896c3 100644 --- a/src/quic.c +++ b/src/quic.c @@ -431,6 +431,10 @@ int detect_udp_filtered(const struct section_config_t *section, } if (section->udp_filter_quic != UDP_FILTER_QUIC_DISABLED) { + if (section->dport_filter && ntohs(udph->dest) != 443) + goto match_port; + + const struct quic_lhdr *qch; size_t qch_len; struct quic_cids qci; From 6393c119605e3da78a53c8e45fd58d6414cb2978 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 9 Jan 2025 18:30:23 +0300 Subject: [PATCH 04/12] Use skb_copy_bits instead of skb_linearize Encountered noticeable issues in performance caused by skb_linearize --- src/kytunblock.c | 56 +++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/kytunblock.c b/src/kytunblock.c index 2f4e959..78e6ae3 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -385,11 +385,11 @@ static int conntrack_parse(const struct sk_buff *skb, #endif /* RHEL or not RHEL */ - - static NF_CALLBACK(ykb_nf_hook, skb) { int ret; struct packet_data pd = {0}; + uint8_t *data_buf = NULL; + int nf_verdict = NF_ACCEPT; spin_lock(&hot_config_spinlock); // if set flag to disable processing, @@ -402,14 +402,17 @@ static NF_CALLBACK(ykb_nf_hook, skb) { } spin_unlock(&hot_config_spinlock); - if ((skb->mark & config.mark) == config.mark) - goto accept; + if ((skb->mark & config.mark) == config.mark) { + goto send_verdict; + } - if (skb->head == NULL) - goto accept; + if (skb->head == NULL) { + goto send_verdict; + } - if (skb->len > MAX_PACKET_SIZE) - goto accept; + if (skb->len >= MAX_PACKET_SIZE) { + goto send_verdict; + } ret = conntrack_parse(skb, &pd.yct); if (ret < 0) { @@ -417,34 +420,43 @@ static NF_CALLBACK(ykb_nf_hook, skb) { } if (config.connbytes_limit != 0 && yct_is_mask_attr(YCTATTR_ORIG_PACKETS, &pd.yct) && pd.yct.orig_packets > config.connbytes_limit) - goto accept; + goto send_verdict; - ret = skb_linearize(skb); - if (ret < 0) { - lgerror(ret, "Cannot linearize"); - goto accept; + if (skb_is_nonlinear(skb)) { + data_buf = kmalloc(skb->len, GFP_KERNEL); + if (data_buf == NULL) { + lgerror(-ENOMEM, "Cannot allocate packet buffer"); + } + ret = skb_copy_bits(skb, 0, data_buf, skb->len); + if (ret) { + lgerror(ret, "Cannot copy bits"); + goto send_verdict; + } + + pd.payload = data_buf; + } else { + pd.payload = skb->data; } - pd.payload = skb->data; pd.payload_len = skb->len; int vrd = process_packet(&pd); switch(vrd) { case PKT_ACCEPT: - goto accept; + nf_verdict = NF_ACCEPT; + break; case PKT_DROP: - goto drop; + nf_verdict = NF_STOLEN; + kfree_skb(skb); + break; } -accept: - atomic_dec(&hot_config_counter); - return NF_ACCEPT; -drop: +send_verdict: + kfree(data_buf); atomic_dec(&hot_config_counter); - kfree_skb(skb); - return NF_STOLEN; + return nf_verdict; } From 9985fcea4905324e15f34e86b6620e68b6b2ab94 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 9 Jan 2025 18:31:38 +0300 Subject: [PATCH 05/12] More verbose payload_split loggers --- src/utils.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils.c b/src/utils.c index 98a89e7..17e1e8a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -154,7 +154,9 @@ int ip4_payload_split(uint8_t *pkt, size_t buflen, size_t hdr_len = hdr->ihl * 4; size_t pktlen = ntohs(hdr->tot_len); if (buflen < pktlen || hdr_len > pktlen) { - lgerror(-EINVAL, "ip4_payload_split: buflen cmp pktlen"); + lgerror(-EINVAL, "ip4_payload_split: buflen cmp pktlen: " + "buflen = %zu pktlen = %zu hdr_len = %zu", + buflen, pktlen, hdr_len); return -EINVAL; } @@ -229,7 +231,8 @@ int ip6_payload_split(uint8_t *pkt, size_t buflen, size_t hdr_len = sizeof(struct ip6_hdr); size_t pktlen = ntohs(hdr->ip6_plen); if (buflen < pktlen) { - lgerror(-EINVAL, "ip6_payload_split: buflen cmp pktlen: %zu %zu", buflen, pktlen); + lgerror(-EINVAL, "ip6_payload_split: buflen cmp pktlen: " + "buflen = %zu pktlen = %zu", buflen, pktlen); return -EINVAL; } From d87ab29199645a00e333e29ccd4cd85c2ef32085 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 9 Jan 2025 19:44:30 +0300 Subject: [PATCH 06/12] Use mallocs instead of NETBUF_ALLOC malloc won't hurt when youtubeUnblock is processing the packet. But it is better for kmod and a way cleaner than NETBUF_ALLOC defines. --- src/kytunblock.c | 24 +-- src/mangle.c | 376 ++++++++++++++++++------------------------- src/quic.c | 37 +++-- src/quic.h | 4 +- src/tls.c | 84 ++++++---- src/tls.h | 4 +- src/types.h | 25 --- src/youtubeUnblock.c | 56 +++---- 8 files changed, 272 insertions(+), 338 deletions(-) diff --git a/src/kytunblock.c b/src/kytunblock.c index 78e6ae3..54dbcfa 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -195,21 +195,21 @@ static int send_raw_socket(const uint8_t *pkt, size_t pktlen) { int ret; if (pktlen > AVAILABLE_MTU) { - lgdebug("The packet is too big and may cause issues!"); + lgtrace("Split packet!"); - NETBUF_ALLOC(buff1, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buff1)) { + size_t buff1_size = pktlen; + uint8_t *buff1 = malloc(buff1_size); + if (buff1 == NULL) { lgerror(-ENOMEM, "Allocation error"); return -ENOMEM; } - NETBUF_ALLOC(buff2, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buff2)) { + size_t buff2_size = pktlen; + uint8_t *buff2 = malloc(buff2_size); + if (buff2 == NULL) { lgerror(-ENOMEM, "Allocation error"); - NETBUF_FREE(buff2); + free(buff1); return -ENOMEM; } - size_t buff1_size = MAX_PACKET_SIZE; - size_t buff2_size = MAX_PACKET_SIZE; if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, buff1, &buff1_size, buff2, &buff2_size)) < 0) { @@ -231,12 +231,12 @@ static int send_raw_socket(const uint8_t *pkt, size_t pktlen) { goto erret_lc; } - NETBUF_FREE(buff1); - NETBUF_FREE(buff2); + free(buff1); + free(buff2); return sent; erret_lc: - NETBUF_FREE(buff1); - NETBUF_FREE(buff2); + free(buff1); + free(buff2); return ret; } diff --git a/src/mangle.c b/src/mangle.c index 9f5cf86..b1725e2 100644 --- a/src/mangle.c +++ b/src/mangle.c @@ -211,32 +211,32 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra if (ret < 0) { - goto accept; + return PKT_ACCEPT; } // As defined by TLS standard. if (section->dport_filter && ntohs(tcph->dest) != 443) { - goto accept; + return PKT_ACCEPT; } if (tcph->syn && section->synfake) { lgtrace_addp("TCP syn alter"); - NETBUF_ALLOC(payload, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(payload)) { - lgerror(-ENOMEM, "Allocation error"); - goto accept; - } - - memcpy(payload, ipxh, iph_len); - memcpy(payload + iph_len, tcph, tcph_len); size_t fake_len = section->fake_sni_pkt_sz; - if (section->synfake_len) fake_len = min(section->synfake_len, fake_len); - memcpy(payload + iph_len + tcph_len, section->fake_sni_pkt, 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) { @@ -252,22 +252,23 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra } - ret = instance_config.send_raw_packet(payload, iph_len + tcph_len + fake_len); + ret = instance_config.send_raw_packet(payload, payload_len); if (ret < 0) { lgerror(ret, "send_syn_altered"); - NETBUF_FREE(payload); - goto accept; + free(payload); + return PKT_ACCEPT; } - NETBUF_FREE(payload); - goto drop; + free(payload); + return PKT_DROP; } - if (tcph->syn) goto continue_flow; + if (tcph->syn) + return PKT_CONTINUE; if (!section->tls_enabled) - goto continue_flow; + return PKT_CONTINUE; struct tls_verdict vrd = analyze_tls_data(section, data, dlen); lgtrace_addp("TLS analyzed"); @@ -282,12 +283,11 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra size_t target_sni_offset = vrd.target_sni_ptr - data; size_t payload_len = raw_payload_len; - NETBUF_ALLOC(payload, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(payload)) { + uint8_t *payload = malloc(raw_payload_len); + if (payload == NULL) { lgerror(-ENOMEM, "Allocation error"); - goto accept; + return PKT_ACCEPT; } - memcpy(payload, raw_payload, raw_payload_len); void *iph; @@ -311,6 +311,7 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra set_tcp_checksum(tcph, iph, iph_len); } +/* if (0) { int delta = 2; ret = seqovl_packet(payload, &payload_len, delta); @@ -321,116 +322,108 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra lgerror(ret, "seqovl_packet delta %d", delta); } } - +*/ - if (dlen > 1480 && config.verbose) { + if (dlen > AVAILABLE_MTU) { lgdebug("WARNING! Client Hello packet is too big and may cause issues!"); } if (section->fake_sni) { - post_fake_sni(args_default_fake_type(section), iph, iph_len, tcph, tcph_len); + post_fake_sni(args_default_fake_type(section), 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; - - 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; + case FRAG_STRAT_TCP: + { + ipd_offset = target_sni_offset; + mid_offset = ipd_offset + vrd.target_sni_len / 2; + + size_t poses[2]; + int cnt = 0; + + if (section->frag_sni_pos && dlen > section->frag_sni_pos) { + poses[cnt++] = section->frag_sni_pos; } - 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; + + 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; } - default: - 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; + 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: - NETBUF_FREE(payload); - goto accept; + free(payload); + return PKT_ACCEPT; drop_lc: - NETBUF_FREE(payload); - goto drop; - + free(payload); + return PKT_DROP; } -continue_flow: return PKT_CONTINUE; -accept: - return PKT_ACCEPT; -drop: - return PKT_DROP; } int process_udp_packet(const struct section_config_t *section, const uint8_t *pkt, size_t pktlen) { @@ -458,12 +451,8 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk goto drop; 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; - } - size_t fsn_len = MAX_PACKET_SIZE; + uint8_t *fake_udp; + size_t fake_udp_len; struct udp_fake_type fake_type = { .fake_len = section->udp_fake_len, @@ -472,24 +461,25 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk .faking_ttl = section->faking_ttl, }, }; - ret = gen_fake_udp(fake_type, iph, iph_len, udph, fake_udp, &fsn_len); + 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_lc; + goto erret; } lgtrace_addp("post fake udp #%d", i + 1); - ret = instance_config.send_raw_packet(fake_udp, fsn_len); + ret = instance_config.send_raw_packet(fake_udp, fake_udp_len); if (ret < 0) { lgerror(ret, "send fake udp"); goto erret_lc; } - NETBUF_FREE(fake_udp); + free(fake_udp); continue; erret_lc: - NETBUF_FREE(fake_udp); + free(fake_udp); +erret: goto accept; } @@ -524,32 +514,20 @@ int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet packet, pktlen); } } else { - NETBUF_ALLOC(frag1, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(frag1)) { - lgerror(-ENOMEM, "Allocation error"); - return -ENOMEM; - } - - NETBUF_ALLOC(frag2, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(frag2)) { + size_t f1len = pktlen; + uint8_t *frag1 = malloc(f1len); + if (frag1 == NULL) { lgerror(-ENOMEM, "Allocation error"); - NETBUF_FREE(frag1); return -ENOMEM; } -/* - NETBUF_ALLOC(fake_pad, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(fake_pad)) { + size_t f2len = pktlen; + uint8_t *frag2 = malloc(f2len); + if (frag2 == NULL) { lgerror(-ENOMEM, "Allocation error"); - NETBUF_FREE(frag1); - NETBUF_FREE(frag2); + free(frag1); return -ENOMEM; } -*/ - - size_t f1len = MAX_PACKET_SIZE; - size_t f2len = MAX_PACKET_SIZE; - // size_t fake_pad_len = MAX_PACKET_SIZE; int ret; @@ -570,8 +548,6 @@ int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet goto erret_lc; } - dvs += frag_pos; - if (section->frag_sni_reverse) goto send_frag2; send_frag1: @@ -583,54 +559,22 @@ int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet if (section->frag_sni_reverse) goto out_lc; -send_fake: -/* - if (section->frag_sni_faked) { - ITER_FAKE_STRAT(section->faking_strategy, strategy) { - size_t iphfl; - fake_pad_len = f2len; - ret = ip4_payload_split(frag2, f2len, NULL, &iphfl, NULL, NULL); - if (ret < 0) { - lgerror("Invalid frag2", ret); - goto erret_lc; - } - memcpy(fake_pad, frag2, iphfl + sizeof(struct udphdr)); - memset(fake_pad + iphfl + sizeof(struct udphdr), 0, f2len - iphfl - sizeof(struct udphdr)); - ((struct iphdr *)fake_pad)->tot_len = htons(fake_pad_len); - ((struct iphdr *)fake_pad)->id = 1; - ((struct iphdr *)fake_pad)->ttl = 8; - ((struct iphdr *)fake_pad)->frag_off = 0; - ip4_set_checksum((struct iphdr*)fake_pad); - // *(struct udphdr *)(fake_pad + iphfl) = *(struct udphdr *)(frag2 + iphfl); - ret = send_ip4_frags(fake_pad, fake_pad_len, NULL, 0, 0); - if (ret < 0) { - goto erret_lc; - } - } - } -*/ - - if (section->frag_sni_reverse) - goto send_frag1; - send_frag2: - ret = send_ip4_frags(section, frag2, f2len, poses + 1, poses_sz - 1, dvs); + ret = send_ip4_frags(section, frag2, f2len, poses + 1, poses_sz - 1, poses[0]); if (ret < 0) { goto erret_lc; } if (section->frag_sni_reverse) - goto send_fake; + goto send_frag1; out_lc: - NETBUF_FREE(frag1); - NETBUF_FREE(frag2); - // NETBUF_FREE(fake_pad); + free(frag1); + free(frag2); goto out; erret_lc: - NETBUF_FREE(frag1); - NETBUF_FREE(frag2); - // NETBUF_FREE(fake_pad); + free(frag1); + free(frag2); return ret; } @@ -656,22 +600,21 @@ int send_tcp_frags(const struct section_config_t *section, const uint8_t *packet packet, pktlen); } } else { - NETBUF_ALLOC(frag1, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(frag1)) { + size_t f1len = pktlen; + uint8_t *frag1 = malloc(f1len); + if (frag1 == NULL) { lgerror(-ENOMEM, "Allocation error"); return -ENOMEM; } - NETBUF_ALLOC(frag2, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(frag2)) { + size_t f2len = pktlen; + uint8_t *frag2 = malloc(f2len); + if (frag2 == NULL) { lgerror(-ENOMEM, "Allocation error"); - NETBUF_FREE(frag1); + free(frag1); return -ENOMEM; } - size_t f1len = MAX_PACKET_SIZE; - size_t f2len = MAX_PACKET_SIZE; - int ret; if (dvs > poses[0]) { @@ -680,7 +623,6 @@ int send_tcp_frags(const struct section_config_t *section, const uint8_t *packet goto erret_lc; } - ret = tcp_frag(packet, pktlen, poses[0] - dvs, frag1, &f1len, frag2, &f2len); @@ -697,16 +639,14 @@ int send_tcp_frags(const struct section_config_t *section, const uint8_t *packet goto send_frag2; send_frag1: - { - ret = send_tcp_frags(section, frag1, f1len, NULL, 0, 0); - if (ret < 0) { - goto erret_lc; - } - - if (section->frag_sni_reverse) - goto out_lc; + ret = send_tcp_frags(section, frag1, f1len, NULL, 0, 0); + if (ret < 0) { + goto erret_lc; } + if (section->frag_sni_reverse) + goto out_lc; + send_fake: if (section->frag_sni_faked) { size_t iphfl, tcphfl; @@ -729,22 +669,20 @@ int send_tcp_frags(const struct section_config_t *section, const uint8_t *packet goto send_frag1; send_frag2: - { - ret = send_tcp_frags(section, frag2, f2len, poses + 1, poses_sz - 1, poses[0]); - if (ret < 0) { - goto erret_lc; - } - - if (section->frag_sni_reverse) - goto send_fake; + ret = send_tcp_frags(section, frag2, f2len, poses + 1, poses_sz - 1, poses[0]); + if (ret < 0) { + goto erret_lc; } + + if (section->frag_sni_reverse) + goto send_fake; out_lc: - NETBUF_FREE(frag1); - NETBUF_FREE(frag2); + free(frag1); + free(frag2); goto out; erret_lc: - NETBUF_FREE(frag1); - NETBUF_FREE(frag2); + free(frag1); + free(frag2); return ret; } out: @@ -773,28 +711,24 @@ int post_fake_sni(struct fake_type f_type, // one goes for default fake for (int i = 0; i < fake_seq_type.sequence_len; i++) { - NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(fake_sni)) { - lgerror(-ENOMEM, "Allocation error"); - return -ENOMEM; - } - size_t fsn_len = MAX_PACKET_SIZE; - + uint8_t *fake_sni; + size_t fake_sni_len; + ret = gen_fake_sni( fake_seq_type, fsiph, iph_len, fstcph, tcph_len, - fake_sni, &fsn_len); + &fake_sni, &fake_sni_len); if (ret < 0) { lgerror(ret, "gen_fake_sni"); - goto erret_lc; + return ret; } lgtrace_addp("post fake sni #%d", i + 1); if (f_type.seg2delay) { - ret = instance_config.send_delayed_packet(fake_sni, fsn_len, f_type.seg2delay); + ret = instance_config.send_delayed_packet(fake_sni, fake_sni_len, f_type.seg2delay); } else { - ret = instance_config.send_raw_packet(fake_sni, fsn_len); + ret = instance_config.send_raw_packet(fake_sni, fake_sni_len); } if (ret < 0) { lgerror(ret, "send fake sni"); @@ -804,7 +738,7 @@ int post_fake_sni(struct fake_type f_type, size_t tcph_len; size_t plen; ret = tcp_payload_split( - fake_sni, fsn_len, + fake_sni, fake_sni_len, &fsiph, &iph_len, &fstcph, &tcph_len, NULL, &plen); @@ -830,10 +764,10 @@ int post_fake_sni(struct fake_type f_type, fsiph = (void *)rfsiph; fstcph = (void *)rfstcph; - NETBUF_FREE(fake_sni); + free(fake_sni); continue; erret_lc: - NETBUF_FREE(fake_sni); + free(fake_sni); return ret; } } diff --git a/src/quic.c b/src/quic.c index 5d896c3..28e5da1 100644 --- a/src/quic.c +++ b/src/quic.c @@ -285,13 +285,25 @@ int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, size int gen_fake_udp(struct udp_fake_type type, const void *ipxh, size_t iph_len, const struct udphdr *udph, - uint8_t *buf, size_t *buflen) { + uint8_t **ubuf, size_t *ubuflen) { size_t data_len = type.fake_len; + int ret; - if (!ipxh || !udph || !buf || !buflen) + if (!ipxh || !udph || !ubuf || !ubuflen) return -EINVAL; int ipxv = netproto_version(ipxh, iph_len); + + if (ipxv == IP6VERSION) { + iph_len = sizeof(struct ip6_hdr); + } + + size_t dlen = iph_len + sizeof(struct udphdr) + data_len; + size_t buffer_len = dlen + 50; + uint8_t *buf = malloc(buffer_len); + if (buf == NULL) { + return -ENOMEM; + } if (ipxv == IP4VERSION) { const struct iphdr *iph = ipxh; @@ -303,20 +315,15 @@ int gen_fake_udp(struct udp_fake_type type, } 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; + ret = -EINVAL; + goto error; } - size_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); @@ -336,11 +343,19 @@ int gen_fake_udp(struct udp_fake_type type, set_udp_checksum(nudph, buf, iph_len); - udp_fail_packet(type.strategy, buf, &dlen, *buflen); + ret = udp_fail_packet(type.strategy, buf, &dlen, buffer_len); + if (ret < 0) { + lgerror(ret, "udp_fail_packet"); + goto error; + } - *buflen = dlen; + *ubuflen = dlen; + *ubuf = buf; return 0; +error: + free(buf); + return ret; } int parse_quic_decrypted( diff --git a/src/quic.h b/src/quic.h index 5478101..f184c23 100644 --- a/src/quic.h +++ b/src/quic.h @@ -234,11 +234,11 @@ int parse_quic_decrypted( // Like fail_packet for TCP int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, size_t *plen, size_t avail_buflen); -// Like gen_fake_sni for TCP +// Like gen_fake_sni for TCP, Allocates and generates udp fake int gen_fake_udp(struct udp_fake_type type, const void *ipxh, size_t iph_len, const struct udphdr *udph, - uint8_t *buf, size_t *buflen); + uint8_t **buf, size_t *buflen); int detect_udp_filtered(const struct section_config_t *section, const uint8_t *payload, size_t plen); diff --git a/src/tls.c b/src/tls.c index 6e3eb36..c0c9043 100644 --- a/src/tls.c +++ b/src/tls.c @@ -41,27 +41,28 @@ int bruteforce_analyze_sni_str( vrd->sni_ptr = data + dlen / 2; return 0; } + int max_domain_len = 0; + for (struct domains_list *sne = section->sni_domains; sne != NULL; + sne = sne->next) { + max_domain_len = max(sne->domain_len, max_domain_len); + } + + size_t buf_size = max_domain_len + dlen + 1; + uint8_t *buf = malloc(buf_size); + if (buf == NULL) { + return -ENOMEM; + } + int *nzbuf = malloc(buf_size * sizeof(int)); + if (nzbuf == NULL) { + free(buf); + return -ENOMEM; + } + 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); @@ -77,17 +78,13 @@ int bruteforce_analyze_sni_str( vrd->sni_ptr = data + (k - domain_len - 1); vrd->target_sni_ptr = vrd->sni_ptr; vrd->target_sni_len = vrd->sni_len; - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - return 0; + goto return_vrd; } } - - - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); } - +return_vrd: + free(buf); + free(nzbuf); return 0; } static int analyze_sni_str( @@ -319,18 +316,31 @@ struct tls_verdict analyze_tls_data( int gen_fake_sni(struct fake_type type, const void *ipxh, size_t iph_len, const struct tcphdr *tcph, size_t tcph_len, - uint8_t *buf, size_t *buflen) { + uint8_t **ubuf, size_t *ubuflen) { size_t data_len = type.fake_len; + uint8_t *buf = NULL; + int ret; if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { data_len = (size_t)randint() % 1200; } - if (!ipxh || !tcph || !buf || !buflen) + if (!ipxh || !tcph || !ubuf || !ubuflen) return -EINVAL; int ipxv = netproto_version(ipxh, iph_len); + if (ipxv == IP6VERSION) { + iph_len = sizeof(struct ip6_hdr); + } + + size_t dlen = iph_len + tcph_len + data_len; + size_t buffer_len = dlen + 50; + buf = malloc(buffer_len); + if (buf == NULL) { + return -ENOMEM; + } + if (ipxv == IP4VERSION) { const struct iphdr *iph = ipxh; @@ -341,20 +351,15 @@ int gen_fake_sni(struct fake_type type, } 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_TCP; } else { - return -EINVAL; + ret = -EINVAL; + goto error; } - size_t dlen = iph_len + tcph_len + data_len; - - if (*buflen < dlen) - return -ENOMEM; - memcpy(buf + iph_len, tcph, tcph_len); uint8_t *bfdptr = buf + iph_len + tcph_len; @@ -391,10 +396,19 @@ int gen_fake_sni(struct fake_type type, niph->ip6_plen = htons(dlen - iph_len); } - fail_packet(type.strategy, buf, &dlen, *buflen); + ret = fail_packet(type.strategy, buf, &dlen, buffer_len); + if (ret < 0) { + lgerror(ret, "fail_packet"); + goto error; + } + - *buflen = dlen; + *ubuflen = dlen; + *ubuf = buf; return 0; +error: + free(buf); + return ret; } diff --git a/src/tls.h b/src/tls.h index 6f96fcc..427fa91 100644 --- a/src/tls.h +++ b/src/tls.h @@ -75,11 +75,11 @@ struct tls_verdict analyze_tls_data(const struct section_config_t *section, cons /** - * Generates the fake client hello message + * Allocates and generates the fake client hello message */ int gen_fake_sni(struct fake_type type, const void *iph, size_t iph_len, const struct tcphdr *tcph, size_t tcph_len, - uint8_t *buf, size_t *buflen); + uint8_t **ubuf, size_t *ubuflen); #endif /* TLS_H */ diff --git a/src/types.h b/src/types.h index fdf824e..0dc9705 100644 --- a/src/types.h +++ b/src/types.h @@ -126,31 +126,6 @@ free((item)); \ #endif /* not a KERNEL_SPACE */ -/* An alternative memory allocation strategy for userspace app */ -// #define ALLOC_MALLOC - -/** - * Use NETBUF_ALLOC and NETBUF_FREE as an abstraction of memory allocation. - * Do not use it within expressions, consider these defines as separate statements. - * - * Use NETBUF_CHECK to check that buffer was properly allocated. - */ -#ifdef KERNEL_SPACE -#include -#define NETBUF_ALLOC(buf, buf_len) __u8* buf = kmalloc(buf_len, GFP_KERNEL); -#define NETBUF_CHECK(buf) ((buf) != NULL) -#define NETBUF_FREE(buf) kfree(buf); -#elif defined(ALLOC_MALLOC) -#include -#define NETBUF_ALLOC(buf, buf_len) __u8* buf = malloc(buf_len); -#define NETBUF_CHECK(buf) ((buf) != NULL) -#define NETBUF_FREE(buf) free(buf); -#else -#define NETBUF_ALLOC(buf, buf_len) __u8 buf[buf_len]; -#define NETBUF_CHECK(buf) (1) -#define NETBUF_FREE(buf) ; -#endif - static inline int randint(void) { int rnd; diff --git a/src/youtubeUnblock.c b/src/youtubeUnblock.c index 1fe671f..9b941df 100644 --- a/src/youtubeUnblock.c +++ b/src/youtubeUnblock.c @@ -501,50 +501,46 @@ static int send_raw_socket(const uint8_t *pkt, size_t pktlen) { if (pktlen > AVAILABLE_MTU) { lgtrace("Split packet!"); - NETBUF_ALLOC(buff1, MNL_SOCKET_BUFFER_SIZE); - if (!NETBUF_CHECK(buff1)) { + size_t buff1_size = pktlen; + uint8_t *buff1 = malloc(buff1_size); + if (buff1 == NULL) { lgerror(-ENOMEM, "Allocation error"); return -ENOMEM; } - - NETBUF_ALLOC(buff2, MNL_SOCKET_BUFFER_SIZE); - if (!NETBUF_CHECK(buff2)) { + size_t buff2_size = pktlen; + uint8_t *buff2 = malloc(buff2_size); + if (buff2 == NULL) { lgerror(-ENOMEM, "Allocation error"); - NETBUF_FREE(buff1); + free(buff1); return -ENOMEM; } - size_t buff1_size = MNL_SOCKET_BUFFER_SIZE; - size_t buff2_size = MNL_SOCKET_BUFFER_SIZE; - if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, buff1, &buff1_size, buff2, &buff2_size)) < 0) { - errno = -ret; - goto free_buffs; + goto erret_lc; } int sent = 0; - int status = send_raw_socket(buff1, buff1_size); + ret = send_raw_socket(buff1, buff1_size); - if (status >= 0) sent += status; + if (ret >= 0) sent += ret; else { - ret = status; - goto free_buffs; + goto erret_lc; } - status = send_raw_socket(buff2, buff2_size); - if (status >= 0) sent += status; + ret = send_raw_socket(buff2, buff2_size); + if (ret >= 0) sent += ret; else { - ret = status; - goto free_buffs; + goto erret_lc; } - ret = sent; - -free_buffs: - NETBUF_FREE(buff1) - NETBUF_FREE(buff2) + free(buff1); + free(buff2); + return sent; +erret_lc: + free(buff1); + free(buff2); return ret; } @@ -555,7 +551,7 @@ static int send_raw_socket(const uint8_t *pkt, size_t pktlen) { } else if (ipvx == IP6VERSION) { ret = send_raw_ipv6(pkt, pktlen); } else { - lginfo("proto version %d is unsupported", ipvx); + printf("proto version %d is unsupported\n", ipvx); return -EINVAL; } @@ -563,6 +559,7 @@ static int send_raw_socket(const uint8_t *pkt, size_t pktlen) { return ret; } + // Per-queue data. Passed to queue_cb. struct queue_data { struct mnl_socket **_nl; @@ -725,12 +722,11 @@ int init_queue(int queue_num) { uint32_t portid = mnl_socket_get_portid(nl); struct nlmsghdr *nlh; - NETBUF_ALLOC(bbuf, BUF_SIZE); - if (!NETBUF_CHECK(bbuf)) { + char *buf = malloc(BUF_SIZE); + if (buf == NULL) { lgerror(-ENOMEM, "Allocation error"); goto die_alloc; } - char *buf = (char *)bbuf; /* Support for kernels versions < 3.8 */ // Obsolete and ignored in kernel version 3.8 @@ -837,12 +833,12 @@ int init_queue(int queue_num) { } - NETBUF_FREE(bbuf) + free(buf); close_socket(&nl); return 0; die: - NETBUF_FREE(bbuf) + free(buf); die_alloc: close_socket(&nl); return -1; From 2e67c161f84c90791a8f66323d316cc61593cab3 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 9 Jan 2025 21:40:34 +0300 Subject: [PATCH 07/12] Fix sni bruteforce when domain is at the end --- src/tls.c | 2 +- test/tls.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/tls.c b/src/tls.c index c0c9043..eae6555 100644 --- a/src/tls.c +++ b/src/tls.c @@ -71,7 +71,7 @@ int bruteforce_analyze_sni_str( z_function((char *)buf, zbuf, domain_len + 1 + dlen); - for (unsigned int k = 0; k < dlen; k++) { + for (size_t k = 0; k < domain_len + 1 + dlen; k++) { if (zbuf[k] == domain_len) { vrd->target_sni = 1; vrd->sni_len = domain_len; diff --git a/test/tls.c b/test/tls.c index d31caec..cd25aa0 100644 --- a/test/tls.c +++ b/test/tls.c @@ -1,5 +1,7 @@ static const char tls_chlo_message[] = "\001\000\002\000\003\003*{D\360FDTZ\305\231\272\006\240\246oa\365}ut\321\033\354\361}\334\227\342\215\257]\332\000\000\006\023\001\023\002\023\003\001\000\001\321\0009\000_\t\002@g\017\000\005\004\200`\000\000q'\004\200\001\026\210\a\004\200`\000\000\001\004\200\000u0\003\002E\300\006\004\200`\000\000\316E,\310\0160;\306\003g\201k\004\004\200\360\000\000\200\000GR\004\000\000\000\001 \004\200\001\000\000\200\377s\333\f\000\000\000\001\n\212\nJ\000\000\000\001\b\002@d\000\020\000\005\000\003\002h3\000+\000\003\002\003\004\000\n\000\b\000\006\000\035\000\027\000\030\000\033\000\003\002\000\002Di\000\005\000\003\002h3\000\r\000\024\000\022\004\003\b\004\004\001\005\003\b\005\005\001\b\006\006\001\002\001\000-\000\002\001\001\376\r\000\332\000\000\001\000\001|\000 \004\256\340\330}\337lC3\304gv\325}\rT\370O,i^\001\357\323\373?\205@3\023\354{\000\260\247cf\207\3276\312\205G\017\213Y\231\b\301~\225r\v\001X\026\335\254H\231\237\237\263\027b\b\327\0351W\000\177tc\213:^\f\362\340\225_\272\331\351\002\026rds\326\034\345*5!\221\265\206\270\240\375\nw\v\340 \003\340\307\230H\203#\212\371\364\257H\220\230L\230{\243\355\v'\325@\240EZ\306\230a\233;\033|=(\372P\232\216\215\203\374\234\222\375\004\3058l\275+?\f\306\335\342Q\313\"F\377G<2Jqb\033\033,|\302w\337bO\032\276\374\312X\364}\255xq\274\2348\247K\345t\327\345\322M\004\220\376*\344\365\0003\000&\000$\000\035\000 W\356I\271\201\350\263[cn\\H?\376s``\v\230\306?E=2\017u\306\027\nc{c\000\000\000\030\000\026\000\000\023abc.defghijklm.ndev"; +static const char tls_bruteforce_message[] = "ahl qlwer 12oi34j 1l2kjrdosij f982j jfa osdijwoeij rasdjf oiajsqw9erj pqwoijf lasdj foijyoutube.com"; + #include "unity.h" #include "unity_fixture.h" @@ -31,8 +33,27 @@ TEST(TLSTest, Test_CHLO_message_detect) TEST_ASSERT_EQUAL_STRING_LEN("abc.defghijklm.ndev", tlsv.sni_ptr, 19); } +TEST(TLSTest, Test_Bruteforce_detects) +{ + struct tls_verdict tlsv; + struct domains_list dmns = { + .domain_name = "youtube.com", + .domain_len = 11, + .next = NULL + }; + sconf.sni_domains = &dmns; + + int ret = bruteforce_analyze_sni_str(&sconf, (const uint8_t *)tls_bruteforce_message, sizeof(tls_bruteforce_message) - 1, &tlsv); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(11, tlsv.sni_len); + TEST_ASSERT_EQUAL_STRING_LEN("youtube.com", tlsv.sni_ptr, 11); + TEST_ASSERT_EQUAL_PTR(tls_bruteforce_message + + sizeof(tls_bruteforce_message) - 12, tlsv.sni_ptr); +} + TEST_GROUP_RUNNER(TLSTest) { RUN_TEST_CASE(TLSTest, Test_CHLO_message_detect); + RUN_TEST_CASE(TLSTest, Test_Bruteforce_detects); } From 351bbfb09759a94e5ca01cd7b3b73b117e09b329 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 10 Jan 2025 13:10:55 +0300 Subject: [PATCH 08/12] Use register_pernet_subsys for nf hook registration --- src/kytunblock.c | 107 ++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/src/kytunblock.c b/src/kytunblock.c index 54dbcfa..08e3519 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -459,91 +459,91 @@ static NF_CALLBACK(ykb_nf_hook, skb) { return nf_verdict; } - -static struct nf_hook_ops ykb_nf_reg __read_mostly = { +static struct nf_hook_ops ykb_hook_ops[] = { +{ .hook = ykb_nf_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_MANGLE, -}; - -static struct nf_hook_ops ykb6_nf_reg __read_mostly = { +} +#ifndef NO_IPV6 +,{ .hook = ykb_nf_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_MANGLE, +} +#endif +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) +static int ykb_net_init(struct net *net) +{ + return nf_register_net_hooks(net, ykb_hook_ops, sizeof(ykb_hook_ops)); +} + +static void ykb_net_exit(struct net *net) +{ + nf_unregister_net_hooks(net, ykb_hook_ops, sizeof(ykb_hook_ops)); +} + +static struct pernet_operations ykb_pernet_ops = { + .init = ykb_net_init, + .exit = ykb_net_exit }; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) */ static int __init ykb_init(void) { + int ret; + #ifdef NO_CONNTRACK lgwarning("Conntrack is disabled."); #endif #ifdef NO_IPV6 lgwarning("IPv6 is disabled."); #endif - - int ret = 0; - struct net *n; + ret = init_config(&config); if (ret < 0) goto err; ret = open_raw_socket(); - if (ret < 0) goto err; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - for_each_net(n) { - ret = nf_register_net_hook(n, &ykb_nf_reg); - if (ret < 0) { - lgerror(ret, "register net_hook"); - } - } -#else - ret = nf_register_hook(&ykb_nf_reg); if (ret < 0) { - lgerror(ret, "register net_hook"); + lgerror(ret, "ipv4 rawsocket initialization failed!"); + goto err; } -#endif - #ifndef NO_IPV6 ret = open_raw6_socket(); if (ret < 0) { - lgerror(ret, "ipv6 initialization failed!"); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - for_each_net(n) - nf_unregister_net_hook(n, &ykb_nf_reg); -#else - nf_unregister_hook(&ykb_nf_reg); -#endif - goto err; + lgerror(ret, "ipv6 rawsocket initialization failed!"); + goto err_close4_sock; } +#endif /* NO_IPV6 */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - for_each_net(n) { - ret = nf_register_net_hook(n, &ykb6_nf_reg); - if (ret < 0) { - lgerror(ret, "register net6_hook"); - } - } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) + ret = register_pernet_subsys(&ykb_pernet_ops); #else - ret = nf_register_hook(&ykb6_nf_reg); - if (ret < 0) { - lgerror(ret, "register net6_hook"); - } + ret = nf_register_hooks(ykb_hook_ops, sizeof(ykb_hook_ops)); #endif -#endif /* NO_IPV6 */ + + if (ret < 0) + goto err_close_sock; + lginfo("youtubeUnblock kernel module started.\n"); return 0; +err_close_sock: +#ifndef NO_IPV6 + close_raw6_socket(); +#endif +err_close4_sock: + close_raw_socket(); err: return ret; } static void __exit ykb_destroy(void) { - struct net *n; - mutex_lock(&config_free_mutex); // acquire all locks. spin_lock(&hot_config_spinlock); @@ -555,22 +555,13 @@ static void __exit ykb_destroy(void) { // netfilter callbacks keep running while (atomic_read(&hot_config_counter) > 0) {} -#ifndef NO_IPV6 -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - for_each_net(n) - nf_unregister_net_hook(n, &ykb6_nf_reg); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) + unregister_pernet_subsys(&ykb_pernet_ops); #else - nf_unregister_hook(&ykb6_nf_reg); + nf_unregister_hooks(ykb_hook_ops, sizeof(ykb_hook_ops)); #endif -#endif /* NO_IPV6 */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - for_each_net(n) - nf_unregister_net_hook(n, &ykb_nf_reg); -#else - nf_unregister_hook(&ykb_nf_reg); -#endif - + #ifndef NO_IPV6 close_raw6_socket(); #endif From 10272400626f24eb3a53daaf2cb4a5731791c73d Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 10 Jan 2025 13:43:54 +0300 Subject: [PATCH 09/12] Fix old kernel versions builders --- .../kernel-3.0.101.Dockerfile | 12 -- .github/workflows/test.yml | 1 - Kbuild | 2 +- src/kargs.c | 105 ------------------ src/kytunblock.c | 103 +++++++++++++++-- src/mangle.c | 5 +- src/quic.c | 2 - src/tls.c | 6 +- 8 files changed, 99 insertions(+), 137 deletions(-) delete mode 100644 .github/builder_containers/kernel-3.0.101.Dockerfile delete mode 100644 src/kargs.c diff --git a/.github/builder_containers/kernel-3.0.101.Dockerfile b/.github/builder_containers/kernel-3.0.101.Dockerfile deleted file mode 100644 index d46fa44..0000000 --- a/.github/builder_containers/kernel-3.0.101.Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM ubuntu:14.04 - -RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget - -RUN wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.0.101.tar.xz -O kernel.tar.xz -RUN tar -xf kernel.tar.xz -RUN rm -f kernel.tar.xz -RUN /bin/bash -c "mv linux-* linux" - -WORKDIR /linux -RUN make defconfig -RUN make -j$(nproc) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 65ce0ca..009f86b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,7 +106,6 @@ jobs: - 4.19.322 - 4.4.302 - 3.10.108 - - 3.0.101 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/Kbuild b/Kbuild index c675e87..de094d3 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/kargs.o src/tls.o src/getopt.o src/inet_ntop.o src/args.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/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 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/kargs.c b/src/kargs.c deleted file mode 100644 index 9fb0e42..0000000 --- a/src/kargs.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - 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 . -*/ - -#include "config.h" -#include "types.h" -#include -#include "types.h" -#include "args.h" -#include "logging.h" - -/** -* Defined in kyoutubeUnblock.c -*/ -extern struct spinlock hot_config_spinlock; -extern atomic_t hot_config_counter; -extern atomic_t hot_config_rep; -extern struct mutex config_free_mutex; - -#define MAX_ARGC 1024 -static char *argv[MAX_ARGC]; - -static int params_set(const char *cval, const struct kernel_param *kp) { - int ret; - ret = mutex_trylock(&config_free_mutex); - if (ret == 0) - return -EBUSY; - - - int cv_len = strlen(cval); - if (cv_len >= 1 && cval[cv_len - 1] == '\n') { - cv_len--; - } - - const char *ytb_prefix = "youtubeUnblock "; - int ytbp_len = strlen(ytb_prefix); - int len = cv_len + ytbp_len; - - 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'; - - 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; - } - } - } - - spin_lock(&hot_config_spinlock); - // lock netfilter youtubeUnblock - atomic_set(&hot_config_rep, 1); - spin_unlock(&hot_config_spinlock); - - // lock config hot replacement process until all - // netfilter callbacks keep running - while (atomic_read(&hot_config_counter) > 0) {} - - ret = yparse_args(argc, argv); - - spin_lock(&hot_config_spinlock); - // relaunch youtubeUnblock - atomic_set(&hot_config_rep, 0); - spin_unlock(&hot_config_spinlock); - - kfree(val); - - mutex_unlock(&config_free_mutex); - return ret; -} - -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(parameters, ¶ms_ops, NULL, 0664); diff --git a/src/kytunblock.c b/src/kytunblock.c index 08e3519..69c2cb8 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -63,12 +63,85 @@ MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock"); static struct socket *rawsocket; static struct socket *raw6socket; -DEFINE_SPINLOCK(hot_config_spinlock); -DEFINE_MUTEX(config_free_mutex); -atomic_t hot_config_counter = ATOMIC_INIT(0); +static DEFINE_SPINLOCK(hot_config_spinlock); +static DEFINE_MUTEX(config_free_mutex); +static atomic_t hot_config_counter = ATOMIC_INIT(0); // boolean flag for hot config replacement // if 1, youtubeUnblock should stop processing -atomic_t hot_config_rep = ATOMIC_INIT(0); +static atomic_t hot_config_rep = ATOMIC_INIT(0); + +#define MAX_ARGC 1024 +static char *argv[MAX_ARGC]; + +static int params_set(const char *cval, const struct kernel_param *kp) { + int ret; + ret = mutex_trylock(&config_free_mutex); + if (ret == 0) + return -EBUSY; + + + int cv_len = strlen(cval); + if (cv_len >= 1 && cval[cv_len - 1] == '\n') { + cv_len--; + } + + const char *ytb_prefix = "youtubeUnblock "; + int ytbp_len = strlen(ytb_prefix); + int len = cv_len + ytbp_len; + + 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'; + + 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; + } + } + } + + spin_lock(&hot_config_spinlock); + // lock netfilter youtubeUnblock + atomic_set(&hot_config_rep, 1); + spin_unlock(&hot_config_spinlock); + + // lock config hot replacement process until all + // netfilter callbacks keep running + while (atomic_read(&hot_config_counter) > 0) {} + + ret = yparse_args(argc, argv); + + spin_lock(&hot_config_spinlock); + // relaunch youtubeUnblock + atomic_set(&hot_config_rep, 0); + spin_unlock(&hot_config_spinlock); + + kfree(val); + + mutex_unlock(&config_free_mutex); + return ret; +} + +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(parameters, ¶ms_ops, NULL, 0664); + static int open_raw_socket(void) { int ret = 0; @@ -475,16 +548,22 @@ static struct nf_hook_ops ykb_hook_ops[] = { } #endif }; +static const size_t ykb_hooks_sz = sizeof(ykb_hook_ops) / sizeof(struct nf_hook_ops); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) static int ykb_net_init(struct net *net) { - return nf_register_net_hooks(net, ykb_hook_ops, sizeof(ykb_hook_ops)); + int ret; + ret = nf_register_net_hooks(net, ykb_hook_ops, ykb_hooks_sz); + if (ret < 0) + return ret; + + return 0; } static void ykb_net_exit(struct net *net) { - nf_unregister_net_hooks(net, ykb_hook_ops, sizeof(ykb_hook_ops)); + nf_unregister_net_hooks(net, ykb_hook_ops, ykb_hooks_sz); } static struct pernet_operations ykb_pernet_ops = { @@ -504,12 +583,14 @@ static int __init ykb_init(void) { #endif ret = init_config(&config); - if (ret < 0) goto err; + if (ret < 0) { + goto err; + } ret = open_raw_socket(); if (ret < 0) { lgerror(ret, "ipv4 rawsocket initialization failed!"); - goto err; + goto err_config; } #ifndef NO_IPV6 @@ -523,7 +604,7 @@ static int __init ykb_init(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) ret = register_pernet_subsys(&ykb_pernet_ops); #else - ret = nf_register_hooks(ykb_hook_ops, sizeof(ykb_hook_ops)); + ret = nf_register_hooks(ykb_hook_ops, ykb_hooks_sz); #endif if (ret < 0) @@ -539,6 +620,8 @@ static int __init ykb_init(void) { #endif err_close4_sock: close_raw_socket(); +err_config: + free_config(config); err: return ret; } @@ -558,7 +641,7 @@ static void __exit ykb_destroy(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) unregister_pernet_subsys(&ykb_pernet_ops); #else - nf_unregister_hooks(ykb_hook_ops, sizeof(ykb_hook_ops)); + nf_unregister_hooks(ykb_hook_ops, ykb_hooks_sz); #endif diff --git a/src/mangle.c b/src/mangle.c index b1725e2..6434380 100644 --- a/src/mangle.c +++ b/src/mangle.c @@ -142,7 +142,7 @@ int process_packet(const struct packet_data *pd) { lgtrace_write(); lgtrace_wr("Transport payload: [ "); - for (int i = 0; i < min(16, transport_payload_len); i++) { + for (int i = 0; i < min((int)16, (int)transport_payload_len); i++) { lgtrace_wr("%02x ", transport_payload[i]); } lgtrace_wr("]"); @@ -224,7 +224,7 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra size_t fake_len = section->fake_sni_pkt_sz; if (section->synfake_len) - fake_len = min(section->synfake_len, fake_len); + fake_len = min((int)section->synfake_len, (int)fake_len); size_t payload_len = iph_len + tcph_len + fake_len; @@ -279,7 +279,6 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra if (vrd.target_sni) { lgdebug("Target SNI detected: %.*s", vrd.sni_len, vrd.sni_ptr); - size_t sni_offset = vrd.sni_ptr - data; size_t target_sni_offset = vrd.target_sni_ptr - data; size_t payload_len = raw_payload_len; diff --git a/src/quic.c b/src/quic.c index 28e5da1..82050d3 100644 --- a/src/quic.c +++ b/src/quic.c @@ -366,8 +366,6 @@ int parse_quic_decrypted( const uint8_t *curptr = decrypted_message; ssize_t curptr_len = decrypted_message_len; ssize_t fret; - int ret; - struct tls_verdict tlsv = {0}; struct quic_frame_crypto fr_cr; uint8_t *crypto_message = calloc(AVAILABLE_MTU, 1); diff --git a/src/tls.c b/src/tls.c index eae6555..91287fc 100644 --- a/src/tls.c +++ b/src/tls.c @@ -45,7 +45,7 @@ int bruteforce_analyze_sni_str( for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { - max_domain_len = max(sne->domain_len, max_domain_len); + max_domain_len = max((int)sne->domain_len, max_domain_len); } size_t buf_size = max_domain_len + dlen + 1; @@ -274,7 +274,7 @@ struct tls_verdict analyze_tls_data( if (tls_vmajor != 0x03) break; message_ptr++; - uint8_t tls_vminor = *message_ptr; + // uint8_t tls_vminor = *message_ptr; message_ptr++; uint16_t message_length = ntohs(*(const uint16_t *)message_ptr); @@ -283,7 +283,7 @@ struct tls_verdict analyze_tls_data( const uint8_t *tls_message_data = message_ptr; // Since real length may be truncated use minimum of two - size_t tls_message_length = min(message_length, data_end - message_ptr); + size_t tls_message_length = min((int)message_length, (int)(data_end - message_ptr)); if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) goto nextMessage; From dfedde9aa80f865f90e32f8ef873ef3577b8c9d8 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 11 Jan 2025 03:36:09 +0300 Subject: [PATCH 10/12] Deglobalize struct config. New config structure allows to implement refcounting in the kernel module to escape borderline cases on module exit or config hot swap. --- .github/workflows/test.yml | 1 + src/args.c | 105 +++++++++++++++---------------- src/args.h | 12 ++-- src/config.h | 24 ++++++-- src/kytunblock.c | 122 +++++++++++++++++++------------------ src/logging.h | 37 +++++------ src/mangle.c | 6 +- src/mangle.h | 2 +- src/youtubeUnblock.c | 35 ++++++----- 9 files changed, 186 insertions(+), 158 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 009f86b..65ce0ca 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,6 +106,7 @@ jobs: - 4.19.322 - 4.4.302 - 3.10.108 + - 3.0.101 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/src/args.c b/src/args.c index 828e0b0..5d10648 100644 --- a/src/args.c +++ b/src/args.c @@ -34,12 +34,18 @@ size_t ylgh_leftbuf = LOGGING_BUFSIZE; char *ylgh_curptr = ylgh_buf; int ylgh_ndnl = 0; +struct logging_config_t logging_conf = default_logging_config_set; + #ifdef KERNEL_SPACE static int errno = 0; #define strtol kstrtol #endif -struct config_t config = default_config_set; +void parse_global_lgconf(const struct config_t *config) { + logging_conf.syslog = config->syslog; + logging_conf.verbose = config->verbose; + logging_conf.instaflush = config->instaflush; +} static int parse_sni_domains(struct domains_list **dlist, const char *domains_str, size_t domains_strlen) { // Empty and shouldn't be used @@ -401,20 +407,19 @@ void print_usage(const char *argv0) { printf("\n"); } -int yparse_args(int argc, char *argv[]) { +int yparse_args(struct config_t *config, int argc, char *argv[]) { int opt; int optIdx = 0; optind=1, opterr=1, optreset=0; long num; int ret; - struct config_t rep_config; - ret = init_config(&rep_config); + ret = init_config(config); if (ret < 0) return ret; - struct section_config_t *default_section = rep_config.last_section; + struct section_config_t *default_section = config->last_section; - struct section_config_t *sect_config = rep_config.last_section; + struct section_config_t *sect_config = config->last_section; int sect_i = 0; sect_config->id = sect_i++; @@ -427,13 +432,13 @@ int yparse_args(int argc, char *argv[]) { while ((opt = getopt_long(argc, argv, "", long_opt, &optIdx)) != -1) { switch (opt) { case OPT_CLS: - free_config(rep_config); - ret = init_config(&rep_config); + free_config(config); + ret = init_config(config); if (ret < 0) return ret; - default_section = rep_config.last_section; + default_section = config->last_section; - sect_config = rep_config.last_section; + sect_config = config->last_section; sect_i = 0; sect_config->id = sect_i++; section_iter = SECT_ITER_DEFAULT; @@ -456,17 +461,17 @@ int yparse_args(int argc, char *argv[]) { break; #endif case OPT_TRACE: - rep_config.verbose = VERBOSE_TRACE; + config->verbose = VERBOSE_TRACE; break; case OPT_INSTAFLUSH: - rep_config.instaflush = 1; + config->instaflush = 1; break; case OPT_SILENT: - rep_config.verbose = VERBOSE_INFO; + config->verbose = VERBOSE_INFO; break; case OPT_NO_GSO: #ifndef KERNEL_SPACE - rep_config.use_gso = 0; + config->use_gso = 0; #else lgerr("--no-gso is not supported in kernel space"); goto invalid_opt; @@ -474,7 +479,7 @@ int yparse_args(int argc, char *argv[]) { break; case OPT_NO_CONNTRACK: #ifndef KERNEL_SPACE - rep_config.use_conntrack = 0; + config->use_conntrack = 0; #else lgerr("--no-conntrack is not supported in kernel space. Compile with make kmake EXTRA_CFLAGS=\"-DNO_CONNTRACK\" instead." ); goto invalid_opt; @@ -482,7 +487,7 @@ int yparse_args(int argc, char *argv[]) { break; case OPT_NO_IPV6: #ifndef KERNEL_SPACE - rep_config.use_ipv6 = 0; + config->use_ipv6 = 0; #else lgerr("--no-ipv6 argument is not available " "in the kernel module. " @@ -492,13 +497,13 @@ int yparse_args(int argc, char *argv[]) { #endif break; case OPT_DAEMONIZE: - rep_config.daemonize = 1; + config->daemonize = 1; break; case OPT_NOCLOSE: - rep_config.noclose = 1; + config->noclose = 1; break; case OPT_SYSLOG: - rep_config.syslog = 1; + config->syslog = 1; break; case OPT_THREADS: num = parse_numeric_option(optarg); @@ -506,7 +511,7 @@ int yparse_args(int argc, char *argv[]) { goto invalid_opt; } - rep_config.threads = num; + config->threads = num; break; case OPT_QUEUE_NUM: num = parse_numeric_option(optarg); @@ -514,7 +519,7 @@ int yparse_args(int argc, char *argv[]) { goto invalid_opt; } - rep_config.queue_start_num = num; + config->queue_start_num = num; break; case OPT_PACKET_MARK: num = parse_numeric_option(optarg); @@ -522,24 +527,24 @@ int yparse_args(int argc, char *argv[]) { goto invalid_opt; } - rep_config.mark = num; + config->mark = num; break; case OPT_CONNBYTES_LIMIT: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } - rep_config.connbytes_limit = num; + config->connbytes_limit = num; break; case OPT_START_SECTION: { struct section_config_t *nsect; - ret = init_section_config(&nsect, rep_config.last_section); + ret = init_section_config(&nsect, config->last_section); if (ret < 0) { goto error; } - rep_config.last_section->next = nsect; - rep_config.last_section = nsect; + config->last_section->next = nsect; + config->last_section = nsect; sect_config = nsect; sect_config->id = sect_i++; section_iter = SECT_ITER_INSIDE; @@ -821,16 +826,12 @@ int yparse_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); + free_config(config); errno = 0; return 1; #endif @@ -849,7 +850,7 @@ int yparse_args(int argc, char *argv[]) { } errno = -ret; - free_config(rep_config); + free_config(config); return ret; } @@ -1027,56 +1028,56 @@ static size_t print_config_section(const struct section_config_t *section, char return buffer_size - buf_sz; } // Returns written buffer length -size_t print_config(char *buffer, size_t buffer_size) { +size_t print_config(const struct config_t *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); + print_cnf_buf("--queue-num=%d", config->queue_start_num); + print_cnf_buf("--threads=%d", config->threads); #endif - print_cnf_buf("--packet-mark=%d", config.mark); + print_cnf_buf("--packet-mark=%d", config->mark); #ifndef KERNEL_SPACE - if (config.daemonize) { + if (config->daemonize) { print_cnf_buf("--daemonize"); } - if (config.syslog) { + if (config->syslog) { print_cnf_buf("--syslog"); } - if (config.noclose) { + if (config->noclose) { print_cnf_buf("--noclose"); } - if (!config.use_gso) { + if (!config->use_gso) { print_cnf_buf("--no-gso"); } - if (!config.use_conntrack) { + if (!config->use_conntrack) { print_cnf_buf("--no-conntrack"); } #endif #ifdef KERNEL_SPACE - print_cnf_buf("--connbytes-limit=%d", config.connbytes_limit); + print_cnf_buf("--connbytes-limit=%d", config->connbytes_limit); #endif - if (!config.use_ipv6) { + if (!config->use_ipv6) { print_cnf_buf("--no-ipv6"); } - if (config.verbose == VERBOSE_TRACE) { + if (config->verbose == VERBOSE_TRACE) { print_cnf_buf("--trace"); } - if (config.instaflush) { + if (config->instaflush) { print_cnf_buf("--instaflush"); } - if (config.verbose == VERBOSE_INFO) { + if (config->verbose == VERBOSE_INFO) { print_cnf_buf("--silent"); } - size_t wbuf_len = print_config_section(config.first_section, buf_ptr, buf_sz); + 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; + 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); @@ -1088,12 +1089,12 @@ size_t print_config(char *buffer, size_t buffer_size) { return buffer_size - buf_sz; } -void print_welcome(void) { +void print_welcome(const struct config_t *config) { char *welcome_message = malloc(4000); if (welcome_message == NULL) return; - size_t sz = print_config(welcome_message, 4000); + size_t sz = print_config(config, welcome_message, 4000); printf("Running with flags: %.*s\n", (int)sz, welcome_message); free(welcome_message); } @@ -1156,8 +1157,8 @@ void free_config_section(struct section_config_t *section) { free(section); } -void free_config(struct config_t config) { - for (struct section_config_t *sct = config.last_section; sct != NULL;) { +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/src/args.h b/src/args.h index 8860dbd..5ef19c9 100644 --- a/src/args.h +++ b/src/args.h @@ -24,8 +24,12 @@ void print_version(void); void print_usage(const char *argv0); -int yparse_args(int argc, char *argv[]); -size_t print_config(char *buffer, size_t buffer_size); +/** + * Initializes _config_ and parses args to it. + */ +int yparse_args(struct config_t *config, int argc, char *argv[]); +size_t print_config(const struct config_t *config, char *buffer, size_t buffer_size); +void parse_global_lgconf(const struct config_t *config); // Initializes configuration storage. int init_config(struct config_t *config); @@ -34,9 +38,9 @@ int init_section_config(struct section_config_t **section, struct section_config // Frees configuration section void free_config_section(struct section_config_t *config); // Frees sections under config -void free_config(struct config_t config); +void free_config(struct config_t *config); /* Prints starting messages */ -void print_welcome(void); +void print_welcome(const struct config_t *config); #endif /* ARGS_H */ diff --git a/src/config.h b/src/config.h index b2c57f3..2df8fe8 100644 --- a/src/config.h +++ b/src/config.h @@ -39,6 +39,14 @@ struct instance_config_t { }; extern struct instance_config_t instance_config; + +struct logging_config_t { + int verbose; + int instaflush; + int syslog; +}; +extern struct logging_config_t logging_conf; + struct udp_dport_range { uint16_t start; uint16_t end; @@ -132,9 +140,12 @@ struct config_t { struct section_config_t *first_section; struct section_config_t *last_section; + +#ifdef KERNEL_SPACE + struct kref refcount; +#endif }; -extern struct config_t config; #define ITER_CONFIG_SECTIONS(config, section) \ for (struct section_config_t *section = (config)->last_section; section != NULL; section = section->prev) @@ -208,7 +219,7 @@ if ((fake_bitmask) & strategy) #define DEFAULT_QUEUE_NUM 537 -#define MAX_PACKET_SIZE 8192 +#define MAX_PACKET_SIZE (1 << 16) #define DEFAULT_SNISTR "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,youtubei.googleapis.com,youtube.googleapis.com,youtubeembeddedplayer.googleapis.com,googleusercontent.com,gstatic.com,l.google.com" @@ -284,10 +295,11 @@ enum { .instaflush = 0, \ } -#define CONFIG_SET(config) \ -struct config_t config = default_config_set; \ -config->last_section = &(config.default_config) \ - +#define default_logging_config_set { \ + .verbose = VERBOSE_DEBUG, \ + .syslog = 0, \ + .instaflush = 0, \ +} struct ytb_conntrack { uint32_t mask; diff --git a/src/kytunblock.c b/src/kytunblock.c index 69c2cb8..ab27f2c 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -63,22 +63,21 @@ MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock"); static struct socket *rawsocket; static struct socket *raw6socket; -static DEFINE_SPINLOCK(hot_config_spinlock); -static DEFINE_MUTEX(config_free_mutex); -static atomic_t hot_config_counter = ATOMIC_INIT(0); -// boolean flag for hot config replacement -// if 1, youtubeUnblock should stop processing -static atomic_t hot_config_rep = ATOMIC_INIT(0); - #define MAX_ARGC 1024 static char *argv[MAX_ARGC]; +static struct config_t *cur_config; + +static void config_release(struct kref *ref) +{ + struct config_t *config = container_of(ref, struct config_t, refcount); + free_config(config); + kfree(config); + pr_warn("Config release\n"); +} + static int params_set(const char *cval, const struct kernel_param *kp) { int ret; - ret = mutex_trylock(&config_free_mutex); - if (ret == 0) - return -EBUSY; - int cv_len = strlen(cval); if (cv_len >= 1 && cval[cv_len - 1] == '\n') { @@ -108,30 +107,36 @@ static int params_set(const char *cval, const struct kernel_param *kp) { } } - spin_lock(&hot_config_spinlock); - // lock netfilter youtubeUnblock - atomic_set(&hot_config_rep, 1); - spin_unlock(&hot_config_spinlock); - // lock config hot replacement process until all - // netfilter callbacks keep running - while (atomic_read(&hot_config_counter) > 0) {} + struct config_t *config; - ret = yparse_args(argc, argv); + config = kmalloc(sizeof(*config), GFP_KERNEL); + if (!config) { + ret = -ENOMEM; + goto ret_fval; + } - spin_lock(&hot_config_spinlock); - // relaunch youtubeUnblock - atomic_set(&hot_config_rep, 0); - spin_unlock(&hot_config_spinlock); + ret = yparse_args(config, argc, argv); - kfree(val); + if (ret < 0) { + kfree(config); + goto ret_fval; + } + kref_init(&config->refcount); + + struct config_t *old_config = cur_config; + cur_config = config; + parse_global_lgconf(cur_config); - mutex_unlock(&config_free_mutex); + kref_put(&old_config->refcount, config_release); + +ret_fval: + kfree(val); return ret; } static int params_get(char *buffer, const struct kernel_param *kp) { - size_t len = print_config(buffer, 4000); + size_t len = print_config(cur_config, buffer, 4000); return len; } @@ -154,7 +159,7 @@ static int open_raw_socket(void) { // That's funny, but this is how it is done in the kernel // https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916 - rawsocket->sk->sk_mark=config.mark; + rawsocket->sk->sk_mark=cur_config->mark; return 0; @@ -164,10 +169,15 @@ static int open_raw_socket(void) { static void close_raw_socket(void) { sock_release(rawsocket); + rawsocket = NULL; } static int send_raw_ipv4(const uint8_t *pkt, size_t pktlen) { int ret = 0; + if (rawsocket == NULL) { + return -ENOTSOCK; + + } if (pktlen > AVAILABLE_MTU) return -ENOMEM; struct iphdr *iph; @@ -216,7 +226,7 @@ static int open_raw6_socket(void) { // That's funny, but this is how it is done in the kernel // https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916 - raw6socket->sk->sk_mark=config.mark; + raw6socket->sk->sk_mark=cur_config->mark; return 0; @@ -226,10 +236,16 @@ static int open_raw6_socket(void) { static void close_raw6_socket(void) { sock_release(raw6socket); + raw6socket = NULL; } static int send_raw_ipv6(const uint8_t *pkt, size_t pktlen) { int ret = 0; + if (raw6socket == NULL) { + return -ENOTSOCK; + + } + if (pktlen > AVAILABLE_MTU) return -ENOMEM; struct ip6_hdr *iph; @@ -303,7 +319,7 @@ static int send_raw_socket(const uint8_t *pkt, size_t pktlen) { else { goto erret_lc; } - + free(buff1); free(buff2); return sent; @@ -464,18 +480,10 @@ static NF_CALLBACK(ykb_nf_hook, skb) { uint8_t *data_buf = NULL; int nf_verdict = NF_ACCEPT; - spin_lock(&hot_config_spinlock); - // if set flag to disable processing, - // explicitly accept all packets - if (atomic_read(&hot_config_rep)) { - spin_unlock(&hot_config_spinlock); - return NF_ACCEPT; - } else { - atomic_inc(&hot_config_counter); - } - spin_unlock(&hot_config_spinlock); + struct config_t *config = cur_config; + kref_get(&config->refcount); - if ((skb->mark & config.mark) == config.mark) { + if ((skb->mark & config->mark) == config->mark) { goto send_verdict; } @@ -492,7 +500,7 @@ static NF_CALLBACK(ykb_nf_hook, skb) { lgtrace("[TRACE] conntrack_parse error code\n"); } - if (config.connbytes_limit != 0 && yct_is_mask_attr(YCTATTR_ORIG_PACKETS, &pd.yct) && pd.yct.orig_packets > config.connbytes_limit) + if (config->connbytes_limit != 0 && yct_is_mask_attr(YCTATTR_ORIG_PACKETS, &pd.yct) && pd.yct.orig_packets > config->connbytes_limit) goto send_verdict; @@ -514,7 +522,7 @@ static NF_CALLBACK(ykb_nf_hook, skb) { pd.payload_len = skb->len; - int vrd = process_packet(&pd); + int vrd = process_packet(config, &pd); switch(vrd) { case PKT_ACCEPT: @@ -528,7 +536,7 @@ static NF_CALLBACK(ykb_nf_hook, skb) { send_verdict: kfree(data_buf); - atomic_dec(&hot_config_counter); + kref_put(&config->refcount, config_release); return nf_verdict; } @@ -581,12 +589,18 @@ static int __init ykb_init(void) { #ifdef NO_IPV6 lgwarning("IPv6 is disabled."); #endif - - ret = init_config(&config); + cur_config = kmalloc(sizeof(*cur_config), GFP_KERNEL); + if (!cur_config) { + return -ENOMEM; + } + ret = init_config(cur_config); if (ret < 0) { + kfree(cur_config); goto err; } + kref_init(&cur_config->refcount); + ret = open_raw_socket(); if (ret < 0) { lgerror(ret, "ipv4 rawsocket initialization failed!"); @@ -621,36 +635,24 @@ static int __init ykb_init(void) { err_close4_sock: close_raw_socket(); err_config: - free_config(config); + kref_put(&cur_config->refcount, config_release); err: return ret; } -static void __exit ykb_destroy(void) { - mutex_lock(&config_free_mutex); - // acquire all locks. - spin_lock(&hot_config_spinlock); - // lock netfilter youtubeUnblock - atomic_set(&hot_config_rep, 1); - spin_unlock(&hot_config_spinlock); - - // wait until all - // netfilter callbacks keep running - while (atomic_read(&hot_config_counter) > 0) {} - +static void __exit ykb_destroy(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) unregister_pernet_subsys(&ykb_pernet_ops); #else nf_unregister_hooks(ykb_hook_ops, ykb_hooks_sz); #endif - #ifndef NO_IPV6 close_raw6_socket(); #endif close_raw_socket(); - free_config(config); + kref_put(&cur_config->refcount, config_release); lginfo("youtubeUnblock kernel module destroyed.\n"); } diff --git a/src/logging.h b/src/logging.h index 482ebc1..7aaf982 100644 --- a/src/logging.h +++ b/src/logging.h @@ -21,16 +21,28 @@ #define LOGGING_H #include "config.h" +#define LOG_ERR KERN_ERR +#define LOG_INFO KERN_INFO +#define LOG_WARN KERN_WARNING + +/** + * Defined in args.c + */ +#define LOGGING_BUFSIZE 4096 +extern char ylgh_buf[LOGGING_BUFSIZE]; +extern size_t ylgh_leftbuf; +extern char *ylgh_curptr; +extern int ylgh_ndnl; + +#define LOG_LEVEL (logging_conf.verbose) +#define DO_INSTAFLUSH (logging_conf.instaflush) +#define DO_SYSLOG (logging_conf.syslog) + #ifdef KERNEL_SPACE #include #include #define printf pr_info #define perror pr_err - -#define LOG_ERR KERN_ERR -#define LOG_INFO KERN_INFO -#define LOG_WARN KERN_WARNING - #define print_message(level, msg, ...) \ (printk(level msg, ##__VA_ARGS__)) @@ -40,21 +52,10 @@ #include #define print_message(level, msg, ...) \ - (config.syslog ? (void)(syslog((level), msg, ##__VA_ARGS__)) : (void)(printf(msg, ##__VA_ARGS__) + fflush(stdout))) + (DO_SYSLOG ? (void)(syslog((level), msg, ##__VA_ARGS__)) : (void)(printf(msg, ##__VA_ARGS__) + fflush(stdout))) #endif /* PROGRAM_SPACE */ -/** - * Defined in args.c - */ -#define LOGGING_BUFSIZE 4096 -extern char ylgh_buf[LOGGING_BUFSIZE]; -extern size_t ylgh_leftbuf; -extern char *ylgh_curptr; -extern int ylgh_ndnl; - -#define LOG_LEVEL (config.verbose) - /** * For flushing only. Use log_buf_write for writing. */ @@ -128,7 +129,7 @@ extern int ylgh_ndnl; if (LOG_LEVEL >= VERBOSE_TRACE) { \ ylgh_ndnl = 1; \ log_buf(LOG_INFO, msg, ##__VA_ARGS__); \ - if (config.instaflush) { \ + if (DO_INSTAFLUSH) { \ log_buf_flush(LOG_INFO); \ } \ } \ diff --git a/src/mangle.c b/src/mangle.c index 6434380..5a89ed5 100644 --- a/src/mangle.c +++ b/src/mangle.c @@ -32,7 +32,7 @@ #include "linux/inet.h" #endif -int process_packet(const struct packet_data *pd) { +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; @@ -65,7 +65,7 @@ int process_packet(const struct packet_data *pd) { transport_proto = iph->protocol; - } else if (ipver == IP6VERSION && config.use_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); @@ -151,7 +151,7 @@ int process_packet(const struct packet_data *pd) { int verdict = PKT_CONTINUE; - ITER_CONFIG_SECTIONS(&config, section) { + ITER_CONFIG_SECTIONS(config, section) { lgtrace_wr("Section #%d: ", CONFIG_SECTION_NUMBER(section)); switch (transport_proto) { diff --git a/src/mangle.h b/src/mangle.h index 77567fd..1228867 100644 --- a/src/mangle.h +++ b/src/mangle.h @@ -33,7 +33,7 @@ * Processes the packet and returns verdict. * This is the primary function that traverses the packet. */ -int process_packet(const struct packet_data *pd); +int process_packet(const struct config_t *config, const struct packet_data *pd); /** diff --git a/src/youtubeUnblock.c b/src/youtubeUnblock.c index 9b941df..7bb41ec 100644 --- a/src/youtubeUnblock.c +++ b/src/youtubeUnblock.c @@ -59,6 +59,8 @@ int rawsocket = -2; pthread_mutex_t raw6socket_lock; 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); @@ -106,7 +108,7 @@ static int open_raw_socket(void) { return -1; } - int mark = config.mark; + int mark = cur_config->mark; if (setsockopt(rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { lgerror(-errno, "setsockopt(SO_MARK, %d) failed", mark); @@ -158,7 +160,7 @@ static int open_raw6_socket(void) { return -1; } - int mark = config.mark; + int mark = cur_config->mark; if (setsockopt(raw6socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { lgerror(-errno, "setsockopt(SO_MARK, %d) failed", mark); @@ -442,14 +444,14 @@ static int send_raw_ipv4(const uint8_t *pkt, size_t pktlen) { } }; - if (config.threads != 1) + if (cur_config->threads != 1) pthread_mutex_lock(&rawsocket_lock); int sent = sendto(rawsocket, pkt, pktlen, MSG_DONTWAIT, (struct sockaddr *)&daddr, sizeof(daddr)); - if (config.threads != 1) + if (cur_config->threads != 1) pthread_mutex_unlock(&rawsocket_lock); /* The function will return -errno on error as well as errno value set itself */ @@ -477,7 +479,7 @@ static int send_raw_ipv6(const uint8_t *pkt, size_t pktlen) { .sin6_addr = iph->ip6_dst }; - if (config.threads != 1) + if (cur_config->threads != 1) pthread_mutex_lock(&rawsocket_lock); int sent = sendto(raw6socket, @@ -486,7 +488,7 @@ static int send_raw_ipv6(const uint8_t *pkt, size_t pktlen) { lgtrace_addp("rawsocket sent %d", sent); - if (config.threads != 1) + if (cur_config->threads != 1) pthread_mutex_unlock(&rawsocket_lock); /* The function will return -errno on error as well as errno value set itself */ @@ -666,8 +668,8 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { if (attr[NFQA_MARK] != NULL) { // Skip packets sent by rawsocket to escape infinity loop. - if ((ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) & config.mark) == - config.mark) { + if ((ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) & cur_config->mark) == + cur_config->mark) { return fallback_accept_packet(id, *qdata); } } @@ -690,7 +692,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { ct_out: verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata->queue_num); - ret = process_packet(&packet); + ret = process_packet(cur_config, &packet); switch (ret) { case PKT_DROP: @@ -748,7 +750,7 @@ int init_queue(int queue_num) { goto die; } - if (config.use_ipv6) { + if (cur_config->use_ipv6) { nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_cmd(nlh, PF_INET6, NFQNL_CFG_CMD_PF_UNBIND); @@ -781,10 +783,10 @@ int init_queue(int queue_num) { unsigned int cfg_flags = NFQA_CFG_F_GSO | NFQA_CFG_F_CONNTRACK | NFQA_CFG_F_FAIL_OPEN; unsigned int cfg_mask = 0; - if (config.use_gso) { + if (cur_config->use_gso) { cfg_mask |= NFQA_CFG_F_GSO; } - if (config.use_conntrack) { + if (cur_config->use_conntrack) { cfg_mask |= NFQA_CFG_F_CONNTRACK; } cfg_mask |= NFQA_CFG_F_FAIL_OPEN; @@ -875,7 +877,9 @@ struct instance_config_t instance_config = { int main(int argc, char *argv[]) { int ret; - if ((ret = yparse_args(argc, argv)) != 0) { + struct config_t config; + + if ((ret = yparse_args(&config, argc, argv)) != 0) { if (ret < 0) { lgerror(-errno, "Unable to parse args"); exit(EXIT_FAILURE); @@ -884,7 +888,10 @@ int main(int argc, char *argv[]) { } print_version(); - print_welcome(); + print_welcome(&config); + + parse_global_lgconf(&config); + cur_config = &config; if (open_raw_socket() < 0) { From 452e640d9fc8b492e9126b00e9090e444c4c9fd1 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 12 Jan 2025 01:43:50 +0300 Subject: [PATCH 11/12] kmod -DNO_IPV6 packet filter --- src/mangle.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mangle.c b/src/mangle.c index 5a89ed5..a8ce942 100644 --- a/src/mangle.c +++ b/src/mangle.c @@ -65,7 +65,9 @@ int process_packet(const struct config_t *config, const struct packet_data *pd) transport_proto = iph->protocol; - } else if (ipver == IP6VERSION && config->use_ipv6) { + } +#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); @@ -75,7 +77,9 @@ int process_packet(const struct config_t *config, const struct packet_data *pd) transport_proto = ip6h->ip6_nxt; - } else { + } +#endif + else { lgtrace("Unknown layer 3 protocol version: %d", ipver); goto accept; } From f68f1ff6c78a447ae39f8050dfc24e03e5908a17 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 12 Jan 2025 03:34:33 +0300 Subject: [PATCH 12/12] Builder for 3.0.101 --- .github/builder_containers/kernel-3.0.101.Dockerfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/builder_containers/kernel-3.0.101.Dockerfile diff --git a/.github/builder_containers/kernel-3.0.101.Dockerfile b/.github/builder_containers/kernel-3.0.101.Dockerfile new file mode 100644 index 0000000..d46fa44 --- /dev/null +++ b/.github/builder_containers/kernel-3.0.101.Dockerfile @@ -0,0 +1,12 @@ +FROM ubuntu:14.04 + +RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget + +RUN wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.0.101.tar.xz -O kernel.tar.xz +RUN tar -xf kernel.tar.xz +RUN rm -f kernel.tar.xz +RUN /bin/bash -c "mv linux-* linux" + +WORKDIR /linux +RUN make defconfig +RUN make -j$(nproc)