From 27629ba0ccdc25389ab737caaf4c19dbad4af760 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 1 Sep 2024 16:07:47 +0300 Subject: [PATCH 01/22] Kernel module basic ipv4 with debug settings --- Kbuild | 2 +- config.h | 4 + iptk_YTUNBLOCK.c | 340 ++++++++++++++++++++++------------------------- logging.h | 2 +- mangle.c | 215 ++++++++++++++++++++---------- quic.c | 2 +- types.h | 57 ++++++-- utils.c | 37 ++++-- utils.h | 2 + 9 files changed, 390 insertions(+), 271 deletions(-) diff --git a/Kbuild b/Kbuild index 4c0ef45..42383ac 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := ipt_YTUNBLOCK.o -ipt_YTUNBLOCK-objs := iptk_YTUNBLOCK.o mangle.o +ipt_YTUNBLOCK-objs := iptk_YTUNBLOCK.o mangle.o quic.o utils.o ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE -DDEBUG diff --git a/config.h b/config.h index 96b7bff..189785a 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,10 @@ #ifndef YTB_CONFIG_H #define YTB_CONFIG_H +#ifndef KERNEL_SPACE +#define USER_SPACE +#endif + typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len); /** * Sends the packet after delay_ms. The function should schedule send and return immediately diff --git a/iptk_YTUNBLOCK.c b/iptk_YTUNBLOCK.c index 0f9910a..f14968f 100644 --- a/iptk_YTUNBLOCK.c +++ b/iptk_YTUNBLOCK.c @@ -13,9 +13,56 @@ #include "mangle.h" #include "config.h" #include "raw_replacements.h" +#include "utils.h" +#include "logging.h" + +struct config_t config = { + .threads = THREADS_NUM, + .frag_sni_reverse = 1, + .frag_sni_faked = 0, + .fragmentation_strategy = FRAGMENTATION_STRATEGY, + .faking_strategy = FAKING_STRATEGY, + .faking_ttl = FAKE_TTL, + .fake_sni = 1, + .fake_sni_seq_len = 1, + .frag_middle_sni = 1, + .frag_sni_pos = 1, + .use_ipv6 = 1, + .fakeseq_offset = 10000, + .mark = DEFAULT_RAWSOCKET_MARK, + .synfake = 0, + .synfake_len = 0, + + .sni_detection = SNI_DETECTION_PARSE, + +#ifdef SEG2_DELAY + .seg2_delay = SEG2_DELAY, +#else + .seg2_delay = 0, +#endif + +#ifdef USE_GSO + .use_gso = 1, +#else + .use_gso = false, +#endif + +#ifdef DEBUG + .verbose = 2, +#else + .verbose = 0, +#endif + + .domains_str = defaul_snistr, + .domains_strlen = sizeof(defaul_snistr), + + .queue_start_num = DEFAULT_QUEUE_NUM, + .fake_sni_pkt = fake_sni_old, + .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator +}; MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.3.2"); MODULE_AUTHOR("Vadim Vetrov "); MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); @@ -37,7 +84,7 @@ static int open_raw_socket(void) { .is_kernel = 1 }; - int mark = RAWSOCKET_MARK; + int mark = config.mark; optval.kernel = &mark; ret = sock_setsockopt(rawsocket, SOL_SOCKET, SO_MARK, optval, sizeof(mark)); if (ret < 0) @@ -59,67 +106,12 @@ static void close_raw_socket(void) { sock_release(rawsocket); } -#define AVAILABLE_MTU 1384 - -static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { - - if (pktlen > AVAILABLE_MTU) { - pr_warn("The packet is too big and may cause issues!"); - - __u32 buff1_size = pktlen; - __u32 buff2_size = pktlen; - __u8 *buff1 = kmalloc(pktlen, GFP_ATOMIC); - if (buff1 == NULL) return -1; - __u8 *buff2 = kmalloc(pktlen, GFP_ATOMIC); - if (buff2 == NULL) { - kfree(buff1); - return -1; - } - - int ret; - -#if defined(USE_TCP_SEGMENTATION) || defined(RAWSOCK_TCP_FSTRAT) - if ((ret = tcp4_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) - return ret; -#elif defined(USE_IP_FRAGMENTATION) || defined(RAWSOCK_IP_FSTRAT) - if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) - return ret; -#else - pr_warn("send_raw_socket: Packet is too big but fragmentation is disabled! " - "Pass -DRAWSOCK_TCP_FSTRAT or -DRAWSOCK_IP_FSTRAT as CFLAGS " - "To enable it only for raw socket\n"); - return -EINVAL; -#endif - - int sent = 0; - ret = send_raw_socket(buff1, buff1_size); - - if (ret >= 0) sent += ret; - else { - kfree(buff1); - kfree(buff2); - return ret; - } - - kfree(buff1); - - ret = send_raw_socket(buff2, buff2_size); - if (ret >= 0) sent += ret; - else { - kfree(buff2); - return ret; - } - - kfree(buff2); - - return sent; - } +static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { + int ret = 0; + if (pktlen > AVAILABLE_MTU) return -ENOMEM; struct iphdr *iph; - int ret; if ((ret = ip4_payload_split( (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { return ret; @@ -151,150 +143,130 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { return ret; } -static unsigned int ykb_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ - if ((skb->mark & RAWSOCKET_MARK) == RAWSOCKET_MARK) - return XT_CONTINUE; - - if (skb->head == NULL) return XT_CONTINUE; - - // TODO: Mallocs are bad! - uint32_t buflen = skb->len; - __u8 *buf = kmalloc(skb->len, GFP_ATOMIC); - if (buf == NULL) { - pr_err("Cannot alloc enough buffer space"); - goto accept; - } - if (skb_copy_bits(skb, 0, buf, skb->len) < 0) { - pr_err("Unable copy bits\n"); - goto ac_fkb; - } - struct iphdr *iph; - uint32_t iph_len; - struct tcphdr *tcph; - uint32_t tcph_len; - __u8 *payload; - uint32_t plen; - int ret = tcp4_payload_split(buf, buflen, &iph, &iph_len, - &tcph, &tcph_len, &payload, &plen); - - if (ret < 0) - goto ac_fkb; - - struct verdict vrd = analyze_tls_data(payload, plen); - - if (vrd.gvideo_hello) { - int ret; - pr_info("Googlevideo detected\n"); +static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { + int ret; - ip4_set_checksum(iph); - tcp4_set_checksum(tcph, iph); + if (pktlen > AVAILABLE_MTU) { + pr_warn("The packet is too big and may cause issues!"); - uint32_t f1len = skb->len; - uint32_t f2len = skb->len; - __u8 *frag1 = kmalloc(f1len, GFP_ATOMIC); - if (!frag1) { - pr_err("Cannot alloc enough gv frag1 buffer space"); - goto ac_fkb; + NETBUF_ALLOC(buff1, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buff1)) { + lgerror("Allocation error", -ENOMEM); + return -ENOMEM; } - __u8 *frag2 = kmalloc(f2len, GFP_ATOMIC); - if (!frag2) { - pr_err("Cannot alloc enough gv frag1 buffer space"); - kfree(frag1); - goto ac_fkb; + NETBUF_ALLOC(buff2, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buff2)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(buff2); + return -ENOMEM; + } + uint32_t buff1_size = MAX_PACKET_SIZE; + uint32_t buff2_size = MAX_PACKET_SIZE; + + switch (config.fragmentation_strategy) { + case FRAG_STRAT_TCP: + if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { + + goto erret_lc; + } + break; + case FRAG_STRAT_IP: + if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { + + goto erret_lc; + } + break; + default: + pr_warn("send_raw_socket: Packet is too big but fragmentation is disabled!"); + ret = -EINVAL; + goto erret_lc; } + int sent = 0; + ret = send_raw_socket(buff1, buff1_size); -#ifdef FAKE_SNI - uint32_t fksn_len = FAKE_SNI_MAXLEN; - __u8 *fksn_buf = kmalloc(fksn_len, GFP_ATOMIC); - if (!fksn_buf) { - pr_err("Cannot alloc enough gksn buffer space"); - goto fallback; + if (ret >= 0) sent += ret; + else { + goto erret_lc; } - - ret = gen_fake_sni(iph, tcph, fksn_buf, &fksn_len); - if (ret < 0) { - pr_err("Cannot alloc enough gksn buffer space"); - goto fksn_fb; + + ret = send_raw_socket(buff2, buff2_size); + if (ret >= 0) sent += ret; + else { + goto erret_lc; } -#endif -#if defined(USE_TCP_SEGMENTATION) - size_t ipd_offset = vrd.sni_offset; - size_t mid_offset = ipd_offset + vrd.sni_len / 2; + NETBUF_FREE(buff1); + NETBUF_FREE(buff2); + return sent; +erret_lc: + NETBUF_FREE(buff1); + NETBUF_FREE(buff2); + return ret; + } + + int ipvx = netproto_version(pkt, pktlen); + if (ipvx == IP4VERSION) + return send_raw_ipv4(pkt, pktlen); - if ((ret = tcp4_frag(buf, skb->len, - mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { - pr_err("tcp4_frag: %d", ret); - goto fksn_fb; - } -#elif defined(USE_IP_FRAGMENTATION) - size_t ipd_offset = tcph_len + vrd.sni_offset; - size_t mid_offset = ipd_offset + vrd.sni_len / 2; - mid_offset += 8 - mid_offset % 8; - - if ((ret = ip4_frag(buf, skb->len, - mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { - pr_err("ip4_frag: %d", ret); - goto fksn_fb; - } -#endif + // else if (ipvx == IP6VERSION) + // return send_raw_ipv6(pkt, pktlen); -#ifdef FAKE_SNI - ret = send_raw_socket(fksn_buf, fksn_len); - if (ret < 0) { - pr_err("fksn_send: %d", ret); - goto fksn_fb; - } -#endif + printf("proto version %d is unsupported\n", ipvx); + return -EINVAL; +} -#if defined(USE_NO_FRAGMENTATION) -#ifdef SEG2_DELAY -#error "SEG2_DELAY is incompatible with NO FRAGMENTATION" -#endif - ret = send_raw_socket(buf, buflen); - if (ret < 0) { - pr_err("nofrag_send: %d", ret); - } - goto fksn_fb; -#endif +static void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { + pr_warn("delay_packet_send won't work on current youtubeUnblock version"); + send_raw_socket(data, data_len); +} - ret = send_raw_socket(frag2, f2len); - if (ret < 0) { - pr_err("raw frag2 send: %d", ret); - goto fksn_fb; - } +struct instance_config_t instance_config = { + .send_raw_packet = send_raw_socket, + .send_delayed_packet = delay_packet_send, +}; -#ifdef SEG2_DELAY -#error "Seg2 delay is unsupported yet for kmod" -#else - ret = send_raw_socket(frag1, f1len); - if (ret < 0) { - pr_err("raw frag1 send: %d", ret); - goto fksn_fb; - } -#endif +static unsigned int ykb_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + if ((skb->mark & config.mark) == config.mark) + return XT_CONTINUE; + + if (skb->head == NULL) return XT_CONTINUE; + + uint32_t buflen = skb->len; + if (buflen > MAX_PACKET_SIZE) + goto accept; -fksn_fb: -#ifdef FAKE_SNI - kfree(fksn_buf); -#endif -fallback: -#ifndef SEG2_DELAY - kfree(frag1); -#endif - kfree(frag2); - kfree(buf); - kfree_skb(skb); - return NF_STOLEN; + NETBUF_ALLOC(buf, buflen); + if (!NETBUF_CHECK(buf)) + goto no_free; + + if (skb_copy_bits(skb, 0, buf, buflen) < 0) { + pr_err("Unable copy bits\n"); + goto accept; + } + + int vrd = process_packet(buf, buflen); + + switch(vrd) { + case PKT_ACCEPT: + goto accept; + case PKT_DROP: + goto drop; } -ac_fkb: - kfree(buf); + accept: + NETBUF_FREE(buf); +no_free: return XT_CONTINUE; +drop: + NETBUF_FREE(buf); + kfree_skb(skb); + return NF_STOLEN; } static int ykb_chk(const struct xt_tgchk_param *par) { diff --git a/logging.h b/logging.h index 694f993..a1998d9 100644 --- a/logging.h +++ b/logging.h @@ -9,7 +9,7 @@ #define printf pr_info #define perror pr_err #define lgerror(msg, ret, ...) __extension__ ({ \ - printf(msg ": %d\n", ##__VA_ARGS__, ret); \ + pr_err(msg ": %d\n", ##__VA_ARGS__, ret); \ }) #else #include // IWYU pragma: export diff --git a/mangle.c b/mangle.c index 931e00c..91814f1 100644 --- a/mangle.c +++ b/mangle.c @@ -6,7 +6,7 @@ #include "quic.h" #include "logging.h" -#ifndef KERNEL_SCOPE +#ifndef KERNEL_SPACE #include #endif @@ -25,6 +25,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { int ipver = netproto_version(raw_payload, raw_payload_len); int ret; + if (ipver == IP4VERSION) { ret = ip4_payload_split((uint8_t *)raw_payload, raw_payload_len, (struct iphdr **)&iph, &iph_len, @@ -33,7 +34,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (ret < 0) goto accept; - transport_proto = iph ->protocol; + transport_proto = iph->protocol; } else if (ipver == IP6VERSION && config.use_ipv6) { ret = ip6_payload_split((uint8_t *)raw_payload, raw_payload_len, @@ -43,7 +44,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (ret < 0) goto accept; - transport_proto = ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt; + transport_proto = ip6h->ip6_nxt; } else { lgtracemsg("Unknown layer 3 protocol version: %d", ipver); @@ -74,6 +75,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { const uint8_t *data; uint32_t dlen; + int ipxv = netproto_version(raw_payload, raw_payload_len); lgtrace_start("TCP"); @@ -89,9 +91,15 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { goto accept; } - if (tcph->syn) { + if (tcph->syn && config.synfake) { lgtrace_addp("TCP syn alter"); - uint8_t payload[MAX_PACKET_SIZE]; + + NETBUF_ALLOC(payload, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(payload)) { + lgerror("Allocation error", -ENOMEM); + goto accept; + } + memcpy(payload, ipxh, iph_len); memcpy(payload + iph_len, tcph, tcph_len); uint32_t fake_len = config.fake_sni_pkt_sz; @@ -110,32 +118,40 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { set_tcp_checksum(tcph, iph, iph_len); } else if (ipxv == IP6VERSION) { struct ip6_hdr *ip6h = (struct ip6_hdr *)payload; - ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen = - ntohs(tcph_len + fake_len); + 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, iph_len + tcph_len + fake_len); if (ret < 0) { lgerror("send_syn_altered", ret); + + NETBUF_FREE(payload); goto accept; } lgtrace_addp("rawsocket sent %d", ret); + + NETBUF_FREE(payload); goto drop; } if (tcph->syn) goto accept; struct tls_verdict vrd = analyze_tls_data(data, dlen); + lgtrace_addp("Analyzed, %d", vrd.target_sni); if (vrd.target_sni) { lgdebugmsg("Target SNI detected: %.*s", vrd.sni_len, data + vrd.sni_offset); - uint8_t payload[MAX_PACKET_SIZE]; uint32_t payload_len = raw_payload_len; + NETBUF_ALLOC(payload, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(payload)) { + lgerror("Allocation error", -ENOMEM); + goto accept; + } + memcpy(payload, raw_payload, raw_payload_len); void *iph; @@ -151,7 +167,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (ret < 0) { lgerror("tcp_payload_split in targ_sni", ret); - goto accept; + goto accept_lc; } if (config.fk_winsize) { @@ -198,10 +214,10 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { ret = send_tcp_frags(payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("tcp4 send frags", ret); - goto accept; + goto accept_lc; } - goto drop; + goto drop_lc; } break; case FRAG_STRAT_IP: @@ -232,11 +248,10 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { ret = send_ip4_frags(payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("ip4 send frags", ret); - goto accept; + goto accept_lc; } - goto drop; - break; + goto drop_lc; } else { printf("WARNING: IP fragmentation is supported only for IPv4\n"); } @@ -244,15 +259,23 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { ret = instance_config.send_raw_packet(payload, payload_len); if (ret < 0) { lgerror("raw pack send", ret); - goto accept; + goto accept_lc; } - goto drop; + goto drop_lc; } + goto drop_lc; + +accept_lc: + NETBUF_FREE(payload); + goto accept; +drop_lc: + NETBUF_FREE(payload); goto drop; + } accept: @@ -356,8 +379,19 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses packet, pktlen); } } else { - uint8_t frag1[MAX_PACKET_SIZE]; - uint8_t frag2[MAX_PACKET_SIZE]; + NETBUF_ALLOC(frag1, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(frag1)) { + lgerror("Allocation error", -ENOMEM); + return -ENOMEM; + } + + NETBUF_ALLOC(frag2, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(frag2)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(frag1); + return -ENOMEM; + } + uint32_t f1len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE; @@ -365,7 +399,8 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses if (dvs > poses[0]) { lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]); - return -EINVAL; + ret = -EINVAL; + goto erret_lc; } ret = ip4_frag(packet, pktlen, poses[0] - dvs, @@ -373,7 +408,7 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses if (ret < 0) { lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs); - return ret; + goto erret_lc; } if (config.frag_sni_reverse) @@ -381,21 +416,30 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses send_frag1: ret = send_ip4_frags(frag1, f1len, NULL, 0, 0); if (ret < 0) { - return ret; + goto erret_lc; } if (config.frag_sni_reverse) - goto out; + goto out_lc; send_frag2: dvs += poses[0]; ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { - return ret; + goto erret_lc; } if (config.frag_sni_reverse) goto send_frag1; + +out_lc: + NETBUF_FREE(frag1); + NETBUF_FREE(frag2); + goto out; +erret_lc: + NETBUF_FREE(frag1); + NETBUF_FREE(frag2); + return ret; } out: @@ -419,9 +463,28 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses packet, pktlen); } } else { - uint8_t frag1[MAX_PACKET_SIZE]; - uint8_t frag2[MAX_PACKET_SIZE]; - uint8_t fake_pad[MAX_PACKET_SIZE]; + NETBUF_ALLOC(frag1, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(frag1)) { + lgerror("Allocation error", -ENOMEM); + return -ENOMEM; + } + + NETBUF_ALLOC(frag2, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(frag2)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(frag1); + return -ENOMEM; + } + + NETBUF_ALLOC(fake_pad, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(fake_pad)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(frag1); + NETBUF_FREE(frag2); + return -ENOMEM; + } + + uint32_t f1len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE; @@ -429,7 +492,8 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses if (dvs > poses[0]) { lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]); - return -EINVAL; + ret = -EINVAL; + goto erret_lc; } @@ -440,7 +504,7 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses if (ret < 0) { lgerror("send_frags: tcp_frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs); - return ret; + goto erret_lc; } @@ -451,21 +515,20 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses { ret = send_tcp_frags(frag1, f1len, NULL, 0, 0); if (ret < 0) { - return ret; + goto erret_lc; } if (config.frag_sni_reverse) - goto out; + goto out_lc; } send_fake: - // TODO if (config.frag_sni_faked) { uint32_t iphfl, tcphfl; ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); if (ret < 0) { lgerror("Invalid frag2", ret); - return ret; + goto erret_lc; } memcpy(fake_pad, frag2, iphfl + tcphfl); memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); @@ -478,11 +541,11 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses ret = fail_packet(fake_pad, f2len); if (ret < 0) { lgerror("Failed to fail packet", ret); - return ret; + goto erret_lc; } ret = send_tcp_frags(fake_pad, f2len, NULL, 0, 0); if (ret < 0) { - return ret; + goto erret_lc; } } @@ -495,12 +558,22 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses dvs += poses[0]; ret = send_tcp_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { - return ret; + goto erret_lc; } if (config.frag_sni_reverse) goto send_fake; } +out_lc: + NETBUF_FREE(frag1); + NETBUF_FREE(frag2); + NETBUF_FREE(fake_pad); + goto out; +erret_lc: + NETBUF_FREE(frag1); + NETBUF_FREE(frag2); + NETBUF_FREE(fake_pad); + return ret; } out: return 0; @@ -520,20 +593,24 @@ int post_fake_sni(const void *iph, unsigned int iph_len, struct tcphdr *fstcph = (void *)rfstcph; for (int i = 0; i < sequence_len; i++) { - uint8_t fake_sni[MAX_PACKET_SIZE]; + NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(fake_sni)) { + lgerror("Allocation error", -ENOMEM); + return -ENOMEM; + } uint32_t fsn_len = MAX_PACKET_SIZE; ret = gen_fake_sni(fsiph, iph_len, fstcph, tcph_len, fake_sni, &fsn_len); if (ret < 0) { lgerror("gen_fake_sni", ret); - return ret; + goto erret_lc; } lgtrace_addp("post fake sni #%d", i + 1); ret = instance_config.send_raw_packet(fake_sni, fsn_len); if (ret < 0) { lgerror("send fake sni", ret); - return ret; + goto erret_lc; } uint32_t iph_len; @@ -551,32 +628,17 @@ int post_fake_sni(const void *iph, unsigned int iph_len, memcpy(rfstcph, fstcph, tcph_len); fsiph = (void *)rfsiph; fstcph = (void *)rfstcph; - +out_lc: + NETBUF_FREE(fake_sni); + continue; +erret_lc: + NETBUF_FREE(fake_sni); + return ret; } return 0; } -void z_function(const char *str, int *zbuf, size_t len) { - zbuf[0] = len; - - ssize_t lh = 0, rh = 1; - for (ssize_t i = 1; i < len; i++) { - zbuf[i] = 0; - if (i < rh) { - zbuf[i] = min(zbuf[i - lh], rh - i); - } - - while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]]) - zbuf[i]++; - - if (i + zbuf[i] > rh) { - lh = i; - rh = i + zbuf[i]; - } - } -} - #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 #define TLS_EXTENSION_SNI 0x0000 @@ -758,12 +820,26 @@ struct tls_verdict analyze_tls_data( config.domains_str[i] == ',' || config.domains_str[i] == '\n' )) { - uint8_t buf[MAX_PACKET_SIZE]; - int zbuf[MAX_PACKET_SIZE]; unsigned int domain_len = (i - j); const char *domain_startp = config.domains_str + j; - if (domain_len + dlen + 1> MAX_PACKET_SIZE) continue; + if (domain_len + dlen + 1> MAX_PACKET_SIZE) { + continue; + } + + NETBUF_ALLOC(buf, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buf)) { + lgerror("Allocation error", -ENOMEM); + goto out; + } + NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); + if (!NETBUF_CHECK(nzbuf)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(buf); + goto out; + } + + int *zbuf = (void *)nzbuf; memcpy(buf, domain_startp, domain_len); memcpy(buf + domain_len, "#", 1); @@ -776,12 +852,17 @@ struct tls_verdict analyze_tls_data( vrd.target_sni = 1; vrd.sni_len = domain_len; vrd.sni_offset = (k - domain_len - 1); + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); goto out; } } j = i + 1; + + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); } } @@ -811,7 +892,7 @@ int gen_fake_sni(const void *ipxh, uint32_t iph_len, memcpy(buf, iph, iph_len); struct ip6_hdr *niph = (struct ip6_hdr *)buf; - niph->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_TCP; + niph->ip6_nxt = IPPROTO_TCP; } else { return -EINVAL; } @@ -834,7 +915,7 @@ int gen_fake_sni(const void *ipxh, uint32_t iph_len, niph->tot_len = htons(dlen); } else if (ipxv == IP6VERSION) { struct ip6_hdr *niph = (struct ip6_hdr *)buf; - niph->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(dlen - iph_len); + niph->ip6_plen = htons(dlen - iph_len); } fail_packet(buf, *buflen); @@ -868,7 +949,7 @@ int fail_packet(uint8_t *payload, uint32_t plen) { if (config.fakeseq_offset) { tcph->seq = htonl(ntohl(tcph->seq) - config.fakeseq_offset); } else { -#ifdef KERNEL_SCOPE +#ifdef KERNEL_SPACE tcph->seq = 124; #else tcph->seq = random(); @@ -889,7 +970,7 @@ int fail_packet(uint8_t *payload, uint32_t plen) { if (ipxv == IP4VERSION) { ((struct iphdr *)iph)->ttl = config.faking_ttl; } else if (ipxv == IP6VERSION) { - ((struct ip6_hdr *)iph)->ip6_ctlun.ip6_un1.ip6_un1_hlim = config.faking_ttl; + ((struct ip6_hdr *)iph)->ip6_hops = config.faking_ttl; } else { lgerror("fail_packet: IP version is unsupported", -EINVAL); return -EINVAL; diff --git a/quic.c b/quic.c index 31ec7df..05ab089 100644 --- a/quic.c +++ b/quic.c @@ -48,7 +48,7 @@ int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len, } uint8_t found = 0; - for (uint8_t i = 0; i < sizeof(supported_versions); i++) { + for (uint8_t i = 0; i < 2; i++) { if (ntohl(nqch->version) == supported_versions[i]) { found = 1; } diff --git a/types.h b/types.h index b177e96..df0e983 100644 --- a/types.h +++ b/types.h @@ -3,7 +3,7 @@ #define TYPES_H #include -#ifdef KERNEL_SCOPE +#ifdef KERNEL_SPACE #include // IWYU pragma: export #include // IWYU pragma: export @@ -12,17 +12,17 @@ typedef __u8 uint8_t; typedef __u16 uint16_t; typedef __u32 uint32_t; typedef __u64 uint64_t; -typedef __i8 int8_t; -typedef __i16 int16_t; -typedef __i32 int32_t; -typedef __i64 int64_t; -#else /* USERSPACE_SCOPE */ +//typedef __i8 int8_t; +//typedef __i16 int16_t; +//typedef __i32 int32_t; +//typedef __i64 int64_t; +#else /* USER_SPACE */ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export -#endif /* SCOPES */ +#endif /* SPACES */ // Network specific structures #ifdef KERNEL_SPACE @@ -30,15 +30,35 @@ typedef __i64 int64_t; #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export #include // IWYU pragma: export +#define ip6_hdr ipv6hdr + /* from */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + +#ifdef __LITTLE_ENDIAN +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN +#elif defined(__BIG_ENDIAN) +#define __LITTLE_ENDIAN 1234 +#define __BYTE_ORDER __BIG_ENDIAN #else -#define USER_SPACE +#error "Unsupported endian" +#endif + +#define ip6_plen payload_len +#define ip6_nxt nexthdr +#define ip6_hops hop_limit +#define ip6_hlim hop_limit +#define ip6_src saddr +#define ip6_dst daddr + +#else /* USER_SPACE */ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export @@ -46,6 +66,8 @@ typedef __i64 int64_t; #include // IWYU pragma: export #endif +#ifndef KERNEL_SPACE + #define max(a,b)__extension__\ ({ \ __typeof__ (a) _a = (a); \ @@ -60,4 +82,23 @@ typedef __i64 int64_t; _a < _b ? _a : _b; \ }) +#endif /* not a KERNEL_SPACE */ + +/** + * 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_ATOMIC); +#define NETBUF_CHECK(buf) ((buf) != NULL) +#define NETBUF_FREE(buf) kfree(buf); +#else +#define NETBUF_ALLOC(buf, buf_len) __u8 buf[buf_len]; +#define NETBUF_CHECK(buf) (1) +#define NETBUF_FREE(buf) ; +#endif + #endif /* TYPES_H */ diff --git a/utils.c b/utils.c index e727d61..8a2b2f8 100644 --- a/utils.c +++ b/utils.c @@ -1,10 +1,8 @@ #include "utils.h" #include "logging.h" -#include +#include "types.h" -#ifdef KERNEL_SPACE -#include -#else +#ifndef KERNEL_SPACE #include #include #include @@ -39,7 +37,7 @@ void ip4_set_checksum(struct iphdr *iph) void tcp6_set_checksum(struct tcphdr *tcph, struct ip6_hdr *iph) { uint16_t old_check = ntohs(tcph->check); - nfq_tcp_compute_checksum_ipv6(tcph, iph); + // nfq_tcp_compute_checksum_ipv6(tcph, iph); } int set_ip_checksum(void *iph, uint32_t iphb_len) { @@ -157,7 +155,7 @@ int ip6_payload_split(uint8_t *pkt, uint32_t buflen, } uint32_t hdr_len = sizeof(struct ip6_hdr); - uint32_t pktlen = ntohs(hdr->ip6_ctlun.ip6_un1.ip6_un1_plen); + uint32_t pktlen = ntohs(hdr->ip6_plen); if (buflen < pktlen) { lgerror("ip6_payload_split: buflen cmp pktlen: %d %d", -EINVAL, buflen, pktlen); return -EINVAL; @@ -194,7 +192,7 @@ int tcp6_payload_split(uint8_t *pkt, uint32_t buflen, if ( - hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP || + hdr->ip6_nxt != IPPROTO_TCP || tcph_plen < sizeof(struct tcphdr)) { return -EINVAL; } @@ -426,8 +424,8 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, } else { struct ip6_hdr *s1_hdr = (void *)seg1; struct ip6_hdr *s2_hdr = (void *)seg2; - s1_hdr->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(s1_dlen - hdr_len); - s2_hdr->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(s2_dlen - hdr_len); + s1_hdr->ip6_plen = htons(s1_dlen - hdr_len); + s2_hdr->ip6_plen = htons(s2_dlen - hdr_len); } struct tcphdr *s1_tcph = (void *)(seg1 + hdr_len); @@ -440,3 +438,24 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, return 0; } + +void z_function(const char *str, int *zbuf, size_t len) { + zbuf[0] = len; + + ssize_t lh = 0, rh = 1; + for (ssize_t i = 1; i < len; i++) { + zbuf[i] = 0; + if (i < rh) { + zbuf[i] = min(zbuf[i - lh], rh - i); + } + + while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]]) + zbuf[i]++; + + if (i + zbuf[i] > rh) { + lh = i; + rh = i + zbuf[i]; + } + } +} + diff --git a/utils.h b/utils.h index f5b6c04..74267d1 100644 --- a/utils.h +++ b/utils.h @@ -89,4 +89,6 @@ void tcp6_set_checksum(struct tcphdr *tcph, struct ip6_hdr *iph); int set_ip_checksum(void *iph, uint32_t iphb_len); int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len); +void z_function(const char *str, int *zbuf, size_t len); + #endif /* UTILS_H */ From 9c839a5094881070d47ec15b1e1ff6111d316078 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 1 Sep 2024 16:52:41 +0300 Subject: [PATCH 02/22] IPv6 for kernel module --- iptk_YTUNBLOCK.c | 110 ++++++++++++++++++++++++++++++++++++++++++-- kmake.mk | 10 +++- libip6t_YTUNBLOCK.c | 26 +++++++++++ utils.c | 9 +++- 4 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 libip6t_YTUNBLOCK.c diff --git a/iptk_YTUNBLOCK.c b/iptk_YTUNBLOCK.c index f14968f..6908732 100644 --- a/iptk_YTUNBLOCK.c +++ b/iptk_YTUNBLOCK.c @@ -66,10 +66,12 @@ MODULE_VERSION("0.3.2"); MODULE_AUTHOR("Vadim Vetrov "); MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); -static int rsfd; static struct socket *rawsocket; DEFINE_MUTEX(rslock); +static struct socket *raw6socket; +DEFINE_MUTEX(rs6lock); + static int open_raw_socket(void) { int ret = 0; ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket); @@ -144,6 +146,79 @@ static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { return ret; } +static int open_raw6_socket(void) { + int ret = 0; + ret = sock_create(AF_INET6, SOCK_RAW, IPPROTO_RAW, &raw6socket); + + if (ret < 0) { + pr_alert("Unable to create raw socket\n"); + goto err; + } + + sockptr_t optval = { + .kernel = NULL, + .is_kernel = 1 + }; + + int mark = config.mark; + optval.kernel = &mark; + ret = sock_setsockopt(raw6socket, SOL_SOCKET, SO_MARK, optval, sizeof(mark)); + if (ret < 0) + { + pr_alert("setsockopt(SO_MARK, %d) failed\n", mark); + goto sr_err; + } + int one = 1; + optval.kernel = &one; + + return 0; +sr_err: + sock_release(raw6socket); +err: + return ret; +} + +static void close_raw6_socket(void) { + sock_release(raw6socket); +} + +static int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { + int ret = 0; + if (pktlen > AVAILABLE_MTU) return -ENOMEM; + + struct ip6_hdr *iph; + + if ((ret = ip6_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + return ret; + } + + struct sockaddr_in6 daddr = { + .sin6_family = AF_INET6, + /* Always 0 for raw socket */ + .sin6_port = 0, + .sin6_addr = iph->ip6_dst + }; + + struct msghdr msg; + struct kvec iov; + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); + + msg.msg_flags = 0; + msg.msg_name = &daddr; + msg.msg_namelen = sizeof(struct sockaddr_in6); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + mutex_lock(&rs6lock); + ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen); + mutex_unlock(&rs6lock); + + return ret; +} + static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { int ret; @@ -213,8 +288,8 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { if (ipvx == IP4VERSION) return send_raw_ipv4(pkt, pktlen); - // else if (ipvx == IP6VERSION) - // return send_raw_ipv6(pkt, pktlen); + else if (ipvx == IP6VERSION) + return send_raw_ipv6(pkt, pktlen); printf("proto version %d is unsupported\n", ipvx); return -EINVAL; @@ -286,17 +361,42 @@ static struct xt_target ykb_tg_reg __read_mostly = { .me = THIS_MODULE, }; +static struct xt_target ykb6_tg_reg __read_mostly = { + .name = "YTUNBLOCK", + .target = ykb_tg, + .table = "mangle", + .hooks = (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD), + .targetsize = sizeof(struct xt_ytunblock_tginfo), + .proto = IPPROTO_TCP, + .family = NFPROTO_IPV6, + .checkentry = ykb_chk, + .me = THIS_MODULE, +}; + static int __init ykb_init(void) { int ret = 0; ret = open_raw_socket(); if (ret < 0) goto err; + if (config.use_ipv6) { + ret = open_raw6_socket(); + if (ret < 0) goto close_rawsocket; + + ret = xt_register_target(&ykb6_tg_reg); + if (ret < 0) goto close_raw6socket; + } + ret = xt_register_target(&ykb_tg_reg); - if (ret < 0) goto close_rawsocket; + if (ret < 0) goto close_xt6_target; pr_info("youtubeUnblock kernel module started.\n"); return 0; + +close_xt6_target: + if (config.use_ipv6) xt_unregister_target(&ykb6_tg_reg); +close_raw6socket: + if (config.use_ipv6) close_raw6_socket(); close_rawsocket: close_raw_socket(); err: @@ -305,6 +405,8 @@ static int __init ykb_init(void) { static void __exit ykb_destroy(void) { xt_unregister_target(&ykb_tg_reg); + if (config.use_ipv6) xt_unregister_target(&ykb6_tg_reg); + if (config.use_ipv6) close_raw6_socket(); close_raw_socket(); pr_info("youtubeUnblock kernel module destroyed.\n"); } diff --git a/kmake.mk b/kmake.mk index 00e3f0f..8520c51 100644 --- a/kmake.mk +++ b/kmake.mk @@ -15,7 +15,7 @@ kmake: kmod xmod kmod: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules -xmod: libipt_YTUNBLOCK.so +xmod: libipt_YTUNBLOCK.so libip6t_YTUNBLOCK.so libipt_YTUNBLOCK.so: libipt_YTUNBLOCK.o $(CCLD) -shared -fPIC ${IPT_CFLAGS} -o $@ $^; @@ -23,13 +23,21 @@ libipt_YTUNBLOCK.so: libipt_YTUNBLOCK.o libipt_YTUNBLOCK.o: libipt_YTUNBLOCK.c $(CC) ${IPT_CFLAGS} -D_INIT=lib$*_init -fPIC -c -o $@ $<; +libip6t_YTUNBLOCK.so: libip6t_YTUNBLOCK.o + $(CCLD) -shared -fPIC ${IPT_CFLAGS} -o $@ $^; + +libip6t_YTUNBLOCK.o: libip6t_YTUNBLOCK.c + $(CC) ${IPT_CFLAGS} -D_INIT=lib$*_init -fPIC -c -o $@ $<; + kload: insmod ipt_YTUNBLOCK.ko cp ./libipt_YTUNBLOCK.so /usr/lib/xtables/ + cp ./libip6t_YTUNBLOCK.so /usr/lib/xtables/ kunload: -rmmod ipt_YTUNBLOCK -/bin/rm /usr/lib/xtables/libipt_YTUNBLOCK.so + -/bin/rm /usr/lib/xtables/libip6t_YTUNBLOCK.so kreload: kunload kload diff --git a/libip6t_YTUNBLOCK.c b/libip6t_YTUNBLOCK.c new file mode 100644 index 0000000..f7d4d1d --- /dev/null +++ b/libip6t_YTUNBLOCK.c @@ -0,0 +1,26 @@ +// Used to register target in iptables +#include +#include + +#include +#include "ipt_YTUNBLOCK.h" + +#define _init __attribute__((constructor)) _INIT +#define __maybe_unused __attribute__((__unused__)) + +static void YTKB_help(void) { + printf("Youtube Unblock - bypass youtube slowdown DPI in Russia\n"); +} + +static struct xtables_target ykb6_tg_reg = { + .name = "YTUNBLOCK", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_ytunblock_tginfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_ytunblock_tginfo)), + .help = YTKB_help, +}; + +void _init(void) { + xtables_register_target(&ykb6_tg_reg); +} diff --git a/utils.c b/utils.c index 8a2b2f8..1bbc6b2 100644 --- a/utils.c +++ b/utils.c @@ -37,7 +37,14 @@ void ip4_set_checksum(struct iphdr *iph) void tcp6_set_checksum(struct tcphdr *tcph, struct ip6_hdr *iph) { uint16_t old_check = ntohs(tcph->check); - // nfq_tcp_compute_checksum_ipv6(tcph, iph); +#ifdef KERNEL_SPACE + tcph->check = 0; + tcph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, + ntohs(iph->ip6_plen), IPPROTO_TCP, + csum_partial(tcph, ntohs(iph->ip6_plen), 0)); +#else + nfq_tcp_compute_checksum_ipv6(tcph, iph); +#endif } int set_ip_checksum(void *iph, uint32_t iphb_len) { From 731da0dd50862e34c29de54e62b2815227c44619 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 1 Sep 2024 16:53:11 +0300 Subject: [PATCH 03/22] Delete redunant checksum setter --- youtubeUnblock.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 26c4660..a57cb69 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -234,9 +234,6 @@ static int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { .sin6_addr = iph->ip6_dst }; - tcp6_set_checksum((void *)(uint8_t *)pkt + sizeof(struct ip6_hdr), (void *)pkt); - - if (config.threads != 1) pthread_mutex_lock(&rawsocket_lock); From 5e327497bbf14bc88c43433eba78d14d346e0344 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 1 Sep 2024 19:56:38 +0300 Subject: [PATCH 04/22] Split raw socket logic from iptables kernel module, add udp over ipv6 support --- Kbuild | 2 +- iptk_YTUNBLOCK.c | 246 +-------------------------------------------- kmod_utils.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++ kmod_utils.h | 14 +++ mangle.c | 10 +- mangle.h | 2 +- utils.c | 54 ++++++++++ utils.h | 10 ++ 8 files changed, 344 insertions(+), 249 deletions(-) create mode 100644 kmod_utils.c create mode 100644 kmod_utils.h diff --git a/Kbuild b/Kbuild index 42383ac..6501558 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := ipt_YTUNBLOCK.o -ipt_YTUNBLOCK-objs := iptk_YTUNBLOCK.o mangle.o quic.o utils.o +ipt_YTUNBLOCK-objs := iptk_YTUNBLOCK.o mangle.o quic.o utils.o kmod_utils.o ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE -DDEBUG diff --git a/iptk_YTUNBLOCK.c b/iptk_YTUNBLOCK.c index 6908732..f94c2b8 100644 --- a/iptk_YTUNBLOCK.c +++ b/iptk_YTUNBLOCK.c @@ -1,4 +1,6 @@ -#define _GNU_SOURCE +#ifndef KERNEL_SPACE +#error "You are trying to compile the kernel module not in the kernel space" +#endif // Kernel module for youtubeUnblock. // Make with make kmake && sudo iptables -t mangle -D OUTPUT 1 && sudo make kreload && sudo iptables -t mangle -I OUTPUT -p tcp -j YTUNBLOCK #include @@ -15,6 +17,7 @@ #include "raw_replacements.h" #include "utils.h" #include "logging.h" +#include "kmod_utils.h" struct config_t config = { .threads = THREADS_NUM, @@ -66,245 +69,6 @@ MODULE_VERSION("0.3.2"); MODULE_AUTHOR("Vadim Vetrov "); MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); -static struct socket *rawsocket; -DEFINE_MUTEX(rslock); - -static struct socket *raw6socket; -DEFINE_MUTEX(rs6lock); - -static int open_raw_socket(void) { - int ret = 0; - ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket); - - if (ret < 0) { - pr_alert("Unable to create raw socket\n"); - goto err; - } - - sockptr_t optval = { - .kernel = NULL, - .is_kernel = 1 - }; - - int mark = config.mark; - optval.kernel = &mark; - ret = sock_setsockopt(rawsocket, SOL_SOCKET, SO_MARK, optval, sizeof(mark)); - if (ret < 0) - { - pr_alert("setsockopt(SO_MARK, %d) failed\n", mark); - goto sr_err; - } - int one = 1; - optval.kernel = &one; - - return 0; -sr_err: - sock_release(rawsocket); -err: - return ret; -} - -static void close_raw_socket(void) { - sock_release(rawsocket); -} - -static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { - int ret = 0; - if (pktlen > AVAILABLE_MTU) return -ENOMEM; - - struct iphdr *iph; - - if ((ret = ip4_payload_split( - (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { - return ret; - } - - struct sockaddr_in daddr = { - .sin_family = AF_INET, - .sin_port = 0, - .sin_addr = { - .s_addr = iph->daddr - } - }; - - struct msghdr msg; - struct kvec iov; - iov.iov_base = (__u8 *)pkt; - iov.iov_len = pktlen; - iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); - - msg.msg_flags = 0; - msg.msg_name = &daddr; - msg.msg_namelen = sizeof(struct sockaddr_in); - msg.msg_control = NULL; - msg.msg_controllen = 0; - - mutex_lock(&rslock); - ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); - mutex_unlock(&rslock); - - return ret; -} - -static int open_raw6_socket(void) { - int ret = 0; - ret = sock_create(AF_INET6, SOCK_RAW, IPPROTO_RAW, &raw6socket); - - if (ret < 0) { - pr_alert("Unable to create raw socket\n"); - goto err; - } - - sockptr_t optval = { - .kernel = NULL, - .is_kernel = 1 - }; - - int mark = config.mark; - optval.kernel = &mark; - ret = sock_setsockopt(raw6socket, SOL_SOCKET, SO_MARK, optval, sizeof(mark)); - if (ret < 0) - { - pr_alert("setsockopt(SO_MARK, %d) failed\n", mark); - goto sr_err; - } - int one = 1; - optval.kernel = &one; - - return 0; -sr_err: - sock_release(raw6socket); -err: - return ret; -} - -static void close_raw6_socket(void) { - sock_release(raw6socket); -} - -static int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { - int ret = 0; - if (pktlen > AVAILABLE_MTU) return -ENOMEM; - - struct ip6_hdr *iph; - - if ((ret = ip6_payload_split( - (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { - return ret; - } - - struct sockaddr_in6 daddr = { - .sin6_family = AF_INET6, - /* Always 0 for raw socket */ - .sin6_port = 0, - .sin6_addr = iph->ip6_dst - }; - - struct msghdr msg; - struct kvec iov; - iov.iov_base = (__u8 *)pkt; - iov.iov_len = pktlen; - iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); - - msg.msg_flags = 0; - msg.msg_name = &daddr; - msg.msg_namelen = sizeof(struct sockaddr_in6); - msg.msg_control = NULL; - msg.msg_controllen = 0; - - mutex_lock(&rs6lock); - ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen); - mutex_unlock(&rs6lock); - - return ret; -} - -static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { - int ret; - - if (pktlen > AVAILABLE_MTU) { - pr_warn("The packet is too big and may cause issues!"); - - NETBUF_ALLOC(buff1, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buff1)) { - lgerror("Allocation error", -ENOMEM); - return -ENOMEM; - } - NETBUF_ALLOC(buff2, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buff2)) { - lgerror("Allocation error", -ENOMEM); - NETBUF_FREE(buff2); - return -ENOMEM; - } - uint32_t buff1_size = MAX_PACKET_SIZE; - uint32_t buff2_size = MAX_PACKET_SIZE; - - switch (config.fragmentation_strategy) { - case FRAG_STRAT_TCP: - if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) { - - goto erret_lc; - } - break; - case FRAG_STRAT_IP: - if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) { - - goto erret_lc; - } - break; - default: - pr_warn("send_raw_socket: Packet is too big but fragmentation is disabled!"); - ret = -EINVAL; - goto erret_lc; - } - - int sent = 0; - ret = send_raw_socket(buff1, buff1_size); - - if (ret >= 0) sent += ret; - else { - goto erret_lc; - } - - ret = send_raw_socket(buff2, buff2_size); - if (ret >= 0) sent += ret; - else { - goto erret_lc; - } - - NETBUF_FREE(buff1); - NETBUF_FREE(buff2); - return sent; -erret_lc: - NETBUF_FREE(buff1); - NETBUF_FREE(buff2); - return ret; - } - - int ipvx = netproto_version(pkt, pktlen); - - if (ipvx == IP4VERSION) - return send_raw_ipv4(pkt, pktlen); - - else if (ipvx == IP6VERSION) - return send_raw_ipv6(pkt, pktlen); - - printf("proto version %d is unsupported\n", ipvx); - return -EINVAL; -} - -static void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { - pr_warn("delay_packet_send won't work on current youtubeUnblock version"); - send_raw_socket(data, data_len); -} - -struct instance_config_t instance_config = { - .send_raw_packet = send_raw_socket, - .send_delayed_packet = delay_packet_send, -}; - static unsigned int ykb_tg(struct sk_buff *skb, const struct xt_action_param *par) { if ((skb->mark & config.mark) == config.mark) @@ -355,7 +119,6 @@ static struct xt_target ykb_tg_reg __read_mostly = { .table = "mangle", .hooks = (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD), .targetsize = sizeof(struct xt_ytunblock_tginfo), - .proto = IPPROTO_TCP, .family = NFPROTO_IPV4, .checkentry = ykb_chk, .me = THIS_MODULE, @@ -367,7 +130,6 @@ static struct xt_target ykb6_tg_reg __read_mostly = { .table = "mangle", .hooks = (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD), .targetsize = sizeof(struct xt_ytunblock_tginfo), - .proto = IPPROTO_TCP, .family = NFPROTO_IPV6, .checkentry = ykb_chk, .me = THIS_MODULE, diff --git a/kmod_utils.c b/kmod_utils.c new file mode 100644 index 0000000..93dbccd --- /dev/null +++ b/kmod_utils.c @@ -0,0 +1,255 @@ +#ifndef KERNEL_SPACE +#error "You are trying to compile the kernel module not in the kernel space" +#endif +#include "kmod_utils.h" + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "utils.h" +#include "logging.h" + +static struct socket *rawsocket; +DEFINE_MUTEX(rslock); + +static struct socket *raw6socket; +DEFINE_MUTEX(rs6lock); + + +int open_raw_socket(void) { + int ret = 0; + ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket); + + if (ret < 0) { + pr_alert("Unable to create raw socket\n"); + goto err; + } + + sockptr_t optval = { + .kernel = NULL, + .is_kernel = 1 + }; + + int mark = config.mark; + optval.kernel = &mark; + ret = sock_setsockopt(rawsocket, SOL_SOCKET, SO_MARK, optval, sizeof(mark)); + if (ret < 0) + { + pr_alert("setsockopt(SO_MARK, %d) failed\n", mark); + goto sr_err; + } + int one = 1; + optval.kernel = &one; + + return 0; +sr_err: + sock_release(rawsocket); +err: + return ret; +} + +void close_raw_socket(void) { + sock_release(rawsocket); +} + +static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { + int ret = 0; + if (pktlen > AVAILABLE_MTU) return -ENOMEM; + + struct iphdr *iph; + + if ((ret = ip4_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + return ret; + } + + struct sockaddr_in daddr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { + .s_addr = iph->daddr + } + }; + + struct msghdr msg; + struct kvec iov; + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); + + msg.msg_flags = 0; + msg.msg_name = &daddr; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + mutex_lock(&rslock); + ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); + mutex_unlock(&rslock); + + return ret; +} + +int open_raw6_socket(void) { + int ret = 0; + ret = sock_create(AF_INET6, SOCK_RAW, IPPROTO_RAW, &raw6socket); + + if (ret < 0) { + pr_alert("Unable to create raw socket\n"); + goto err; + } + + sockptr_t optval = { + .kernel = NULL, + .is_kernel = 1 + }; + + int mark = config.mark; + optval.kernel = &mark; + ret = sock_setsockopt(raw6socket, SOL_SOCKET, SO_MARK, optval, sizeof(mark)); + if (ret < 0) + { + pr_alert("setsockopt(SO_MARK, %d) failed\n", mark); + goto sr_err; + } + int one = 1; + optval.kernel = &one; + + return 0; +sr_err: + sock_release(raw6socket); +err: + return ret; +} + +void close_raw6_socket(void) { + sock_release(raw6socket); +} + +int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { + int ret = 0; + if (pktlen > AVAILABLE_MTU) return -ENOMEM; + + struct ip6_hdr *iph; + + if ((ret = ip6_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + return ret; + } + + struct sockaddr_in6 daddr = { + .sin6_family = AF_INET6, + /* Always 0 for raw socket */ + .sin6_port = 0, + .sin6_addr = iph->ip6_dst + }; + + struct msghdr msg; + struct kvec iov; + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); + + msg.msg_flags = 0; + msg.msg_name = &daddr; + msg.msg_namelen = sizeof(struct sockaddr_in6); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + mutex_lock(&rs6lock); + ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen); + mutex_unlock(&rs6lock); + + return ret; +} + +int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { + int ret; + + if (pktlen > AVAILABLE_MTU) { + pr_warn("The packet is too big and may cause issues!"); + + NETBUF_ALLOC(buff1, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buff1)) { + lgerror("Allocation error", -ENOMEM); + return -ENOMEM; + } + NETBUF_ALLOC(buff2, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buff2)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(buff2); + return -ENOMEM; + } + uint32_t buff1_size = MAX_PACKET_SIZE; + uint32_t buff2_size = MAX_PACKET_SIZE; + + switch (config.fragmentation_strategy) { + case FRAG_STRAT_TCP: + if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { + + goto erret_lc; + } + break; + case FRAG_STRAT_IP: + if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { + + goto erret_lc; + } + break; + default: + pr_warn("send_raw_socket: Packet is too big but fragmentation is disabled!"); + ret = -EINVAL; + goto erret_lc; + } + + int sent = 0; + ret = send_raw_socket(buff1, buff1_size); + + if (ret >= 0) sent += ret; + else { + goto erret_lc; + } + + ret = send_raw_socket(buff2, buff2_size); + if (ret >= 0) sent += ret; + else { + goto erret_lc; + } + + NETBUF_FREE(buff1); + NETBUF_FREE(buff2); + return sent; +erret_lc: + NETBUF_FREE(buff1); + NETBUF_FREE(buff2); + return ret; + } + + int ipvx = netproto_version(pkt, pktlen); + + if (ipvx == IP4VERSION) + return send_raw_ipv4(pkt, pktlen); + + else if (ipvx == IP6VERSION) + return send_raw_ipv6(pkt, pktlen); + + printf("proto version %d is unsupported\n", ipvx); + return -EINVAL; +} + +void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { + pr_warn("delay_packet_send won't work on current youtubeUnblock version"); + send_raw_socket(data, data_len); +} + +struct instance_config_t instance_config = { + .send_raw_packet = send_raw_socket, + .send_delayed_packet = delay_packet_send, +}; diff --git a/kmod_utils.h b/kmod_utils.h new file mode 100644 index 0000000..ade128e --- /dev/null +++ b/kmod_utils.h @@ -0,0 +1,14 @@ +#include "types.h" + +#ifndef KMOD_UTILS_H +#define KMOD_UTILS_H + +int open_raw_socket(void); +void close_raw_socket(void); +int open_raw6_socket(void); +void close_raw6_socket(void); +int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen); +int send_raw_socket(const uint8_t *pkt, uint32_t pktlen); +void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms); + +#endif /* KMOD_UTILS_H */ diff --git a/mangle.c b/mangle.c index 91814f1..f8bcb9d 100644 --- a/mangle.c +++ b/mangle.c @@ -56,7 +56,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { case IPPROTO_TCP: return process_tcp_packet(raw_payload, raw_payload_len); case IPPROTO_UDP: - return process_udp4_packet(raw_payload, raw_payload_len); + return process_udp_packet(raw_payload, raw_payload_len); default: goto accept; } @@ -290,15 +290,15 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { return PKT_DROP; } -int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen) { - const struct iphdr *iph; +int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) { + const void *iph; uint32_t iph_len; const struct udphdr *udph; const uint8_t *data; uint32_t dlen; - int ret = udp4_payload_split((uint8_t *)pkt, pktlen, - (struct iphdr **)&iph, &iph_len, + int ret = udp_payload_split((uint8_t *)pkt, pktlen, + (void **)&iph, &iph_len, (struct udphdr **)&udph, (uint8_t **)&data, &dlen); diff --git a/mangle.h b/mangle.h index da0de99..812c72b 100644 --- a/mangle.h +++ b/mangle.h @@ -53,7 +53,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len); * Processes the UDP packet. * Returns verdict. */ -int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen); +int process_udp_packet(const uint8_t *pkt, uint32_t pktlen); /** * Sends fake client hello. diff --git a/utils.c b/utils.c index 1bbc6b2..f31d427 100644 --- a/utils.c +++ b/utils.c @@ -277,6 +277,60 @@ int udp4_payload_split(uint8_t *pkt, uint32_t buflen, return 0; } +int udp6_payload_split(uint8_t *pkt, uint32_t buflen, + struct ip6_hdr **iph, uint32_t *iph_len, + struct udphdr **udph, + uint8_t **payload, uint32_t *plen) { + struct ip6_hdr *hdr; + uint32_t hdr_len; + struct udphdr *uhdr; + uint32_t uhdr_len; + + uint8_t *ip_ph; + uint32_t ip_phlen; + + if (ip6_payload_split(pkt, buflen, &hdr, &hdr_len, + &ip_ph, &ip_phlen)){ + return -EINVAL; + } + + + if ( + hdr->ip6_nxt != IPPROTO_UDP || + ip_phlen < sizeof(struct udphdr)) { + return -EINVAL; + } + + + uhdr = (struct udphdr *)(ip_ph); + if (uhdr->len != 0 && ntohs(uhdr->len) != ip_phlen) { + return -EINVAL; + } + + if (iph) *iph = hdr; + if (iph_len) *iph_len = hdr_len; + if (udph) *udph = uhdr; + if (payload) *payload = ip_ph + sizeof(struct udphdr); + if (plen) *plen = ip_phlen - sizeof(struct udphdr); + + return 0; +} + +int udp_payload_split(uint8_t *pkt, uint32_t buflen, + void **iph, uint32_t *iph_len, + struct udphdr **udph, + uint8_t **payload, uint32_t *plen) { + int netvers = netproto_version(pkt, buflen); + if (netvers == IP4VERSION) { + return udp4_payload_split(pkt, buflen, (struct iphdr **)iph, iph_len, udph, payload, plen); + } else if (netvers == IP6VERSION) { + return udp6_payload_split(pkt, buflen, (struct ip6_hdr **)iph, iph_len, udph, payload, plen); + } else { + lgerror("Internet Protocol version is unsupported", -EINVAL); + return -EINVAL; + } +} + // split packet to two ipv4 fragments. int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, uint8_t *frag1, uint32_t *f1len, diff --git a/utils.h b/utils.h index 74267d1..b9ad085 100644 --- a/utils.h +++ b/utils.h @@ -81,6 +81,16 @@ int udp4_payload_split(uint8_t *pkt, uint32_t buflen, struct udphdr **udph, uint8_t **payload, uint32_t *plen); +int udp6_payload_split(uint8_t *pkt, uint32_t buflen, + struct ip6_hdr **iph, uint32_t *iph_len, + struct udphdr **udph, + uint8_t **payload, uint32_t *plen); + +int udp_payload_split(uint8_t *pkt, uint32_t buflen, + void **iph, uint32_t *iph_len, + struct udphdr **udph, + uint8_t **payload, uint32_t *plen); + void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph); void ip4_set_checksum(struct iphdr *iph); void ip6_set_checksum(struct ip6_hdr *iph); From 5eeff9bc0d0abc3f142ad2488a9106794dcd5bc7 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 1 Sep 2024 20:58:50 +0300 Subject: [PATCH 05/22] Use netfilter hook instead of iptables target KISS principle is in action. No need to specify rules, just insmod youtubeUnblock and it works! --- Kbuild | 4 +- ipt_YTUNBLOCK.h | 6 -- kmake.mk | 29 ++------ iptk_YTUNBLOCK.c => kytunblock.c | 111 +++++++++++++++++++------------ libip6t_YTUNBLOCK.c | 26 -------- libipt_YTUNBLOCK.c | 26 -------- 6 files changed, 74 insertions(+), 128 deletions(-) delete mode 100644 ipt_YTUNBLOCK.h rename iptk_YTUNBLOCK.c => kytunblock.c (62%) delete mode 100644 libip6t_YTUNBLOCK.c delete mode 100644 libipt_YTUNBLOCK.c diff --git a/Kbuild b/Kbuild index 6501558..160e2eb 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ -obj-m := ipt_YTUNBLOCK.o -ipt_YTUNBLOCK-objs := iptk_YTUNBLOCK.o mangle.o quic.o utils.o kmod_utils.o +obj-m := kyoutubeUnblock.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE -DDEBUG diff --git a/ipt_YTUNBLOCK.h b/ipt_YTUNBLOCK.h deleted file mode 100644 index 9394f39..0000000 --- a/ipt_YTUNBLOCK.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef IPT_YTUNBLOCK_H -#define IPT_YTUNBLOCK_H - -struct xt_ytunblock_tginfo {}; - -#endif /* IPT_YTUNBLOCK_H */ diff --git a/kmake.mk b/kmake.mk index 8520c51..9e7bbdf 100644 --- a/kmake.mk +++ b/kmake.mk @@ -10,41 +10,20 @@ LDFLAGS := IPT_CFLAGS := -Wall -Wpedantic -O2 .PHONY: kmake kload kunload kreload kclean kmclean xclean -kmake: kmod xmod +kmake: kmod kmod: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules -xmod: libipt_YTUNBLOCK.so libip6t_YTUNBLOCK.so - -libipt_YTUNBLOCK.so: libipt_YTUNBLOCK.o - $(CCLD) -shared -fPIC ${IPT_CFLAGS} -o $@ $^; - -libipt_YTUNBLOCK.o: libipt_YTUNBLOCK.c - $(CC) ${IPT_CFLAGS} -D_INIT=lib$*_init -fPIC -c -o $@ $<; - -libip6t_YTUNBLOCK.so: libip6t_YTUNBLOCK.o - $(CCLD) -shared -fPIC ${IPT_CFLAGS} -o $@ $^; - -libip6t_YTUNBLOCK.o: libip6t_YTUNBLOCK.c - $(CC) ${IPT_CFLAGS} -D_INIT=lib$*_init -fPIC -c -o $@ $<; - kload: - insmod ipt_YTUNBLOCK.ko - cp ./libipt_YTUNBLOCK.so /usr/lib/xtables/ - cp ./libip6t_YTUNBLOCK.so /usr/lib/xtables/ + insmod kyoutubeUnblock.ko kunload: - -rmmod ipt_YTUNBLOCK - -/bin/rm /usr/lib/xtables/libipt_YTUNBLOCK.so - -/bin/rm /usr/lib/xtables/libip6t_YTUNBLOCK.so + -rmmod kyoutubeUnblock kreload: kunload kload -kclean: xtclean kmclean +kclean: kmclean kmclean: -$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean - -xtclean: - -/bin/rm -f libipt_YTUNBLOCK.so libipt_YTUNBLOCK.o diff --git a/iptk_YTUNBLOCK.c b/kytunblock.c similarity index 62% rename from iptk_YTUNBLOCK.c rename to kytunblock.c index f94c2b8..607aaf7 100644 --- a/iptk_YTUNBLOCK.c +++ b/kytunblock.c @@ -9,8 +9,11 @@ #include #include #include -#include -#include "ipt_YTUNBLOCK.h" +#include + +#include +#include +#include #include "mangle.h" #include "config.h" @@ -69,20 +72,23 @@ MODULE_VERSION("0.3.2"); MODULE_AUTHOR("Vadim Vetrov "); MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); -static unsigned int ykb_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ + +static unsigned int ykb_nf_hook(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) { if ((skb->mark & config.mark) == config.mark) - return XT_CONTINUE; + goto accept_no_free; - if (skb->head == NULL) return XT_CONTINUE; + if (skb->head == NULL) + goto accept_no_free; uint32_t buflen = skb->len; if (buflen > MAX_PACKET_SIZE) - goto accept; + goto accept_no_free; NETBUF_ALLOC(buf, buflen); if (!NETBUF_CHECK(buf)) - goto no_free; + goto accept_no_free; if (skb_copy_bits(skb, 0, buf, buflen) < 0) { pr_err("Unable copy bits\n"); @@ -100,39 +106,27 @@ static unsigned int ykb_tg(struct sk_buff *skb, const struct xt_action_param *pa accept: NETBUF_FREE(buf); -no_free: - return XT_CONTINUE; +accept_no_free: + return NF_ACCEPT; drop: NETBUF_FREE(buf); kfree_skb(skb); return NF_STOLEN; } -static int ykb_chk(const struct xt_tgchk_param *par) { - return 0; -} - -static struct xt_target ykb_tg_reg __read_mostly = { - .name = "YTUNBLOCK", - .target = ykb_tg, - .table = "mangle", - .hooks = (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD), - .targetsize = sizeof(struct xt_ytunblock_tginfo), - .family = NFPROTO_IPV4, - .checkentry = ykb_chk, - .me = THIS_MODULE, +static struct nf_hook_ops ykb_nf_reg __read_mostly = { + .hook = ykb_nf_hook, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_MANGLE, }; -static struct xt_target ykb6_tg_reg __read_mostly = { - .name = "YTUNBLOCK", - .target = ykb_tg, - .table = "mangle", - .hooks = (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD), - .targetsize = sizeof(struct xt_ytunblock_tginfo), - .family = NFPROTO_IPV6, - .checkentry = ykb_chk, - .me = THIS_MODULE, +static struct nf_hook_ops ykb6_nf_reg __read_mostly = { + .hook = ykb_nf_hook, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP6_PRI_MANGLE, }; static int __init ykb_init(void) { @@ -141,24 +135,39 @@ static int __init ykb_init(void) { ret = open_raw_socket(); if (ret < 0) goto err; + if (config.use_ipv6) { ret = open_raw6_socket(); if (ret < 0) goto close_rawsocket; - ret = xt_register_target(&ykb6_tg_reg); - if (ret < 0) goto close_raw6socket; +#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("bad rat",ret); + } +#else + nf_register_hook(&ykb6_nf_reg); +#endif } - ret = xt_register_target(&ykb_tg_reg); - if (ret < 0) goto close_xt6_target; + +#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) + lgerror("bad rat",ret); + } +#else + nf_register_hook(&ykb_nf_reg); +#endif pr_info("youtubeUnblock kernel module started.\n"); return 0; -close_xt6_target: - if (config.use_ipv6) xt_unregister_target(&ykb6_tg_reg); -close_raw6socket: - if (config.use_ipv6) close_raw6_socket(); close_rawsocket: close_raw_socket(); err: @@ -166,9 +175,25 @@ static int __init ykb_init(void) { } static void __exit ykb_destroy(void) { - xt_unregister_target(&ykb_tg_reg); - if (config.use_ipv6) xt_unregister_target(&ykb6_tg_reg); - if (config.use_ipv6) close_raw6_socket(); + if (config.use_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); +#else + nf_unregister_hook(&ykb6_nf_reg); +#endif + close_raw6_socket(); + } + +#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 + close_raw_socket(); pr_info("youtubeUnblock kernel module destroyed.\n"); } diff --git a/libip6t_YTUNBLOCK.c b/libip6t_YTUNBLOCK.c deleted file mode 100644 index f7d4d1d..0000000 --- a/libip6t_YTUNBLOCK.c +++ /dev/null @@ -1,26 +0,0 @@ -// Used to register target in iptables -#include -#include - -#include -#include "ipt_YTUNBLOCK.h" - -#define _init __attribute__((constructor)) _INIT -#define __maybe_unused __attribute__((__unused__)) - -static void YTKB_help(void) { - printf("Youtube Unblock - bypass youtube slowdown DPI in Russia\n"); -} - -static struct xtables_target ykb6_tg_reg = { - .name = "YTUNBLOCK", - .version = XTABLES_VERSION, - .family = NFPROTO_IPV6, - .size = XT_ALIGN(sizeof(struct xt_ytunblock_tginfo)), - .userspacesize = XT_ALIGN(sizeof(struct xt_ytunblock_tginfo)), - .help = YTKB_help, -}; - -void _init(void) { - xtables_register_target(&ykb6_tg_reg); -} diff --git a/libipt_YTUNBLOCK.c b/libipt_YTUNBLOCK.c deleted file mode 100644 index 5b7b099..0000000 --- a/libipt_YTUNBLOCK.c +++ /dev/null @@ -1,26 +0,0 @@ -// Used to register target in iptables -#include -#include - -#include -#include "ipt_YTUNBLOCK.h" - -#define _init __attribute__((constructor)) _INIT -#define __maybe_unused __attribute__((__unused__)) - -static void YTKB_help(void) { - printf("Youtube Unblock - bypass youtube slowdown DPI in Russia\n"); -} - -static struct xtables_target ykb_tg_reg = { - .name = "YTUNBLOCK", - .version = XTABLES_VERSION, - .family = NFPROTO_IPV4, - .size = XT_ALIGN(sizeof(struct xt_ytunblock_tginfo)), - .userspacesize = XT_ALIGN(sizeof(struct xt_ytunblock_tginfo)), - .help = YTKB_help, -}; - -void _init(void) { - xtables_register_target(&ykb_tg_reg); -} From b20f15086e4f8f57ef2596a5b8ef11c0ad63a838 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 1 Sep 2024 23:14:22 +0300 Subject: [PATCH 06/22] Kernel versions --- Kbuild | 2 +- kmake.mk | 2 -- types.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Kbuild b/Kbuild index 160e2eb..0a47ec4 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o -ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE -DDEBUG +ccflags-y := -std=gnu11 -DKERNEL_SPACE -Wno-error diff --git a/kmake.mk b/kmake.mk index 9e7bbdf..aacd391 100644 --- a/kmake.mk +++ b/kmake.mk @@ -7,8 +7,6 @@ LD := ld CFLAGS := LDFLAGS := -IPT_CFLAGS := -Wall -Wpedantic -O2 - .PHONY: kmake kload kunload kreload kclean kmclean xclean kmake: kmod diff --git a/types.h b/types.h index df0e983..c807e18 100644 --- a/types.h +++ b/types.h @@ -91,7 +91,7 @@ typedef __u64 uint64_t; * Use NETBUF_CHECK to check that buffer was properly allocated. */ #ifdef KERNEL_SPACE -#include +#include #define NETBUF_ALLOC(buf, buf_len) __u8* buf = kmalloc(buf_len, GFP_ATOMIC); #define NETBUF_CHECK(buf) ((buf) != NULL) #define NETBUF_FREE(buf) kfree(buf); From 5870df44df86e2e642465b56a8392a3a77b4f9e0 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 2 Sep 2024 00:11:58 +0300 Subject: [PATCH 07/22] Linearize instead of kmalloc --- kytunblock.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/kytunblock.c b/kytunblock.c index 607aaf7..6d51378 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -56,7 +56,7 @@ struct config_t config = { #ifdef DEBUG .verbose = 2, #else - .verbose = 0, + .verbose = 1, #endif .domains_str = defaul_snistr, @@ -76,26 +76,24 @@ MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); static unsigned int ykb_nf_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { + int ret; + if ((skb->mark & config.mark) == config.mark) - goto accept_no_free; + goto accept; if (skb->head == NULL) - goto accept_no_free; + goto accept; - uint32_t buflen = skb->len; - if (buflen > MAX_PACKET_SIZE) - goto accept_no_free; - - NETBUF_ALLOC(buf, buflen); - if (!NETBUF_CHECK(buf)) - goto accept_no_free; + if (skb->len > MAX_PACKET_SIZE) + goto accept; - if (skb_copy_bits(skb, 0, buf, buflen) < 0) { - pr_err("Unable copy bits\n"); + ret = skb_linearize(skb); + if (ret < 0) { + lgerror("Cannot linearize", ret); goto accept; } - int vrd = process_packet(buf, buflen); + int vrd = process_packet(skb->data, skb->len); switch(vrd) { case PKT_ACCEPT: @@ -105,11 +103,8 @@ static unsigned int ykb_nf_hook(void *priv, } accept: - NETBUF_FREE(buf); -accept_no_free: return NF_ACCEPT; drop: - NETBUF_FREE(buf); kfree_skb(skb); return NF_STOLEN; } From b249903ead191c874c549b593af074d2b7109c34 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 2 Sep 2024 00:39:54 +0300 Subject: [PATCH 08/22] CI for kmod --- .github/workflows/build-ci.yml | 137 ++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index aca7cde..3c902c1 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -108,31 +108,31 @@ jobs: branch: - openwrt-23.05 arch: - - aarch64_cortex-a53 - - aarch64_cortex-a72 - - aarch64_generic - - arm_arm1176jzf-s_vfp - - arm_arm926ej-s - - arm_cortex-a15_neon-vfpv4 - - arm_cortex-a5_vfpv4 - - arm_cortex-a7 - - arm_cortex-a7_neon-vfpv4 - - arm_cortex-a7_vfpv4 - - arm_cortex-a8_vfpv3 - - arm_cortex-a9 - - arm_cortex-a9_neon - - arm_cortex-a9_vfpv3-d16 - - arm_fa526 - - arm_mpcore - - arm_xscale - - mips64_octeonplus - - mips_24kc - - mips_4kec - - mips_mips32 - - mipsel_24kc - - mipsel_24kc_24kf - - mipsel_74kc - - mipsel_mips32 + # - aarch64_cortex-a53 + # - aarch64_cortex-a72 + # - aarch64_generic + # - arm_arm1176jzf-s_vfp + # - arm_arm926ej-s + # - arm_cortex-a15_neon-vfpv4 + # - arm_cortex-a5_vfpv4 + # - arm_cortex-a7 + # - arm_cortex-a7_neon-vfpv4 + # - arm_cortex-a7_vfpv4 + # - arm_cortex-a8_vfpv3 + # - arm_cortex-a9 + # - arm_cortex-a9_neon + # - arm_cortex-a9_vfpv3-d16 + # - arm_fa526 + # - arm_mpcore + # - arm_xscale + # - mips64_octeonplus + # - mips_24kc + # - mips_4kec + # - mips_mips32 + # - mipsel_24kc + # - mipsel_24kc_24kf + # - mipsel_74kc + # - mipsel_mips32 - x86_64 container: image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }} @@ -172,6 +172,91 @@ jobs: name: ${{ matrix.branch }}-${{ matrix.arch }} path: /builder/youtubeUnblock*.ipk if-no-files-found: error + build-openwrt-kmod: + needs: prepare + runs-on: ubuntu-latest + strategy: + matrix: + branch: + - openwrt-23.05 + - openwrt-22.03 + - openwrt-21.02 + - openwrt-19.07 + include: + - branch: openwrt-23.05 + wd_path: /builder + - branch: openwrt-22.03 + wd_path: /builder + - branch: openwrt-21.02 + wd_path: /builder + - branch: openwrt-19.07 + wd_path: /home/build/openwrt + arch: + # - aarch64_cortex-a53 + # - aarch64_cortex-a72 + # - aarch64_generic + # - arm_arm1176jzf-s_vfp + # - arm_arm926ej-s + # - arm_cortex-a15_neon-vfpv4 + # - arm_cortex-a5_vfpv4 + # - arm_cortex-a7 + # - arm_cortex-a7_neon-vfpv4 + # - arm_cortex-a7_vfpv4 + # - arm_cortex-a8_vfpv3 + # - arm_cortex-a9 + # - arm_cortex-a9_neon + # - arm_cortex-a9_vfpv3-d16 + # - arm_fa526 + # - arm_mpcore + # - arm_xscale + # - mips64_octeonplus + # - mips_24kc + # - mips_4kec + # - mips_mips32 + - mipsel_24kc + # - mipsel_24kc_24kf + # - mipsel_74kc + # - mipsel_mips32 + # - x86_64 + container: + image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }} + options: --user root + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: 'openwrt' + + - name: Prepare build + env: + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + run: | + sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" kyoutubeUnblock/Makefile + + - name: Build packages + id: build + env: + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + working-directory: ${{ matrix.wd_path }} + run: | + pwd + echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf + cat feeds.conf + ./scripts/feeds update youtubeUnblock + ./scripts/feeds install -a -p youtubeUnblock + make defconfig + make package/kyoutubeUnblock/compile V=s + cp $(find ./bin -type f -name 'youtubeUnblock*.ipk') ./ + + - name: Upload packages + if: steps.build.outcome == 'success' + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.branch }}-${{ matrix.arch }} + path: ${{ matrix.wd_path }}/youtubeUnblock*.ipk + if-no-files-found: error build-entware: needs: prepare @@ -267,7 +352,7 @@ jobs: pre-release: if: github.event_name != 'pull_request' && github.ref_name == 'main' - needs: [build-static, build-openwrt, build-entware] + needs: [build-static, build-openwrt, build-entware, build-openwrt-kmod] permissions: contents: write runs-on: ubuntu-latest From 8bb5368b85931984fbfeeb01efe64828b44d9f52 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 2 Sep 2024 01:44:37 +0300 Subject: [PATCH 09/22] Fix setsockopt (deleted it) --- kmod_utils.c | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/kmod_utils.c b/kmod_utils.c index 93dbccd..fabb215 100644 --- a/kmod_utils.c +++ b/kmod_utils.c @@ -30,21 +30,9 @@ int open_raw_socket(void) { goto err; } - sockptr_t optval = { - .kernel = NULL, - .is_kernel = 1 - }; - - int mark = config.mark; - optval.kernel = &mark; - ret = sock_setsockopt(rawsocket, SOL_SOCKET, SO_MARK, optval, sizeof(mark)); - if (ret < 0) - { - pr_alert("setsockopt(SO_MARK, %d) failed\n", mark); - goto sr_err; - } - int one = 1; - optval.kernel = &one; + // 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; return 0; sr_err: @@ -104,21 +92,9 @@ int open_raw6_socket(void) { goto err; } - sockptr_t optval = { - .kernel = NULL, - .is_kernel = 1 - }; - - int mark = config.mark; - optval.kernel = &mark; - ret = sock_setsockopt(raw6socket, SOL_SOCKET, SO_MARK, optval, sizeof(mark)); - if (ret < 0) - { - pr_alert("setsockopt(SO_MARK, %d) failed\n", mark); - goto sr_err; - } - int one = 1; - optval.kernel = &one; + // 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; return 0; sr_err: From c0dc5d265253820923edad8322556818f38669d4 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 2 Sep 2024 01:50:39 +0300 Subject: [PATCH 10/22] CI for kernel module --- .github/workflows/build-ci.yml | 106 +++++++++++++++++---------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 3c902c1..66f386f 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -108,31 +108,31 @@ jobs: branch: - openwrt-23.05 arch: - # - aarch64_cortex-a53 - # - aarch64_cortex-a72 - # - aarch64_generic - # - arm_arm1176jzf-s_vfp - # - arm_arm926ej-s - # - arm_cortex-a15_neon-vfpv4 - # - arm_cortex-a5_vfpv4 - # - arm_cortex-a7 - # - arm_cortex-a7_neon-vfpv4 - # - arm_cortex-a7_vfpv4 - # - arm_cortex-a8_vfpv3 - # - arm_cortex-a9 - # - arm_cortex-a9_neon - # - arm_cortex-a9_vfpv3-d16 - # - arm_fa526 - # - arm_mpcore - # - arm_xscale - # - mips64_octeonplus - # - mips_24kc - # - mips_4kec - # - mips_mips32 - # - mipsel_24kc - # - mipsel_24kc_24kf - # - mipsel_74kc - # - mipsel_mips32 + - aarch64_cortex-a53 + - aarch64_cortex-a72 + - aarch64_generic + - arm_arm1176jzf-s_vfp + - arm_arm926ej-s + - arm_cortex-a15_neon-vfpv4 + - arm_cortex-a5_vfpv4 + - arm_cortex-a7 + - arm_cortex-a7_neon-vfpv4 + - arm_cortex-a7_vfpv4 + - arm_cortex-a8_vfpv3 + - arm_cortex-a9 + - arm_cortex-a9_neon + - arm_cortex-a9_vfpv3-d16 + - arm_fa526 + - arm_mpcore + - arm_xscale + - mips64_octeonplus + - mips_24kc + - mips_4kec + - mips_mips32 + - mipsel_24kc + - mipsel_24kc_24kf + - mipsel_74kc + - mipsel_mips32 - x86_64 container: image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }} @@ -192,32 +192,33 @@ jobs: - branch: openwrt-19.07 wd_path: /home/build/openwrt arch: - # - aarch64_cortex-a53 - # - aarch64_cortex-a72 - # - aarch64_generic - # - arm_arm1176jzf-s_vfp - # - arm_arm926ej-s - # - arm_cortex-a15_neon-vfpv4 - # - arm_cortex-a5_vfpv4 - # - arm_cortex-a7 - # - arm_cortex-a7_neon-vfpv4 - # - arm_cortex-a7_vfpv4 - # - arm_cortex-a8_vfpv3 - # - arm_cortex-a9 - # - arm_cortex-a9_neon - # - arm_cortex-a9_vfpv3-d16 - # - arm_fa526 - # - arm_mpcore - # - arm_xscale - # - mips64_octeonplus - # - mips_24kc - # - mips_4kec - # - mips_mips32 + - aarch64_cortex-a53 + - aarch64_cortex-a72 + - aarch64_generic + - arm_arm1176jzf-s_vfp + - arm_arm926ej-s + - arm_cortex-a15_neon-vfpv4 + - arm_cortex-a5_vfpv4 + - arm_cortex-a7 + - arm_cortex-a7_neon-vfpv4 + - arm_cortex-a7_vfpv4 + - arm_cortex-a8_vfpv3 + - arm_cortex-a9 + - arm_cortex-a9_neon + - arm_cortex-a9_vfpv3-d16 + - arm_fa526 + - arm_mpcore + - arm_xscale + - mips64_octeonplus + - mips_24kc + - mips_4kec + - mips_mips32 - mipsel_24kc - # - mipsel_24kc_24kf - # - mipsel_74kc - # - mipsel_mips32 - # - x86_64 + - mipsel_24kc_24kf + - mipsel_74kc + - mipsel_mips32 + - ramips/mt76x8 + - x86_64 container: image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }} options: --user root @@ -248,14 +249,14 @@ jobs: ./scripts/feeds install -a -p youtubeUnblock make defconfig make package/kyoutubeUnblock/compile V=s - cp $(find ./bin -type f -name 'youtubeUnblock*.ipk') ./ + cp $(find ./bin -type f -name 'kmod-youtubeUnblock*.ipk') ./ - name: Upload packages if: steps.build.outcome == 'success' uses: actions/upload-artifact@v4 with: name: ${{ matrix.branch }}-${{ matrix.arch }} - path: ${{ matrix.wd_path }}/youtubeUnblock*.ipk + path: ${{ matrix.wd_path }}/kmod-youtubeUnblock*.ipk if-no-files-found: error build-entware: @@ -369,4 +370,5 @@ jobs: title: 'Development build' files: | ./**/youtubeUnblock*.ipk + ./**/kmod-youtubeUnblock*.ipk ./**/youtubeUnblock*.tar.gz From 1e6a9496f6806932eddb3d2f0208296fb2d12433 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 2 Sep 2024 13:41:25 +0300 Subject: [PATCH 11/22] Fix compilationg errors for some kernels --- Kbuild | 2 +- kytunblock.c | 1 - types.h | 1 + utils.c | 7 +++++++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Kbuild b/Kbuild index 0a47ec4..57f1c6d 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o -ccflags-y := -std=gnu11 -DKERNEL_SPACE -Wno-error +ccflags-y := -std=gnu11 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/kytunblock.c b/kytunblock.c index 6d51378..6ed9e07 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include diff --git a/types.h b/types.h index c807e18..23cac9f 100644 --- a/types.h +++ b/types.h @@ -32,6 +32,7 @@ typedef __u64 uint64_t; #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export +#include #define ip6_hdr ipv6hdr diff --git a/utils.c b/utils.c index f31d427..5f4c5f1 100644 --- a/utils.c +++ b/utils.c @@ -7,6 +7,13 @@ #include #include #include +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)) + #include + #include +#else + #include +#endif #endif From f96ac2252bac277a962959d46858a2ed483ee201 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 2 Sep 2024 13:59:11 +0300 Subject: [PATCH 12/22] CI devices --- .github/workflows/build-ci.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 66f386f..4123746 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -97,7 +97,7 @@ jobs: if: steps.build.outcome == 'success' uses: actions/upload-artifact@v4 with: - name: static-${{ matrix.arch }} + name: youtubeUnblock-static-${{ matrix.arch }} path: ./**/youtubeUnblock*.tar.gz build-openwrt: @@ -169,7 +169,7 @@ jobs: if: steps.build.outcome == 'success' uses: actions/upload-artifact@v4 with: - name: ${{ matrix.branch }}-${{ matrix.arch }} + name: youtubeUnblock-${{ matrix.branch }}-${{ matrix.arch }} path: /builder/youtubeUnblock*.ipk if-no-files-found: error build-openwrt-kmod: @@ -217,8 +217,20 @@ jobs: - mipsel_24kc_24kf - mipsel_74kc - mipsel_mips32 - - ramips/mt76x8 + - ramips-mt76x8 - x86_64 + exclude: + - branch: openwrt-19.07 + arch: arm_cortex-a7 + - branch: openwrt-19.07 + arch: mips_4kec + - branch: openwrt-19.07 + arch: ramips-mt76x8 + - branch: openwrt-19.07 + arch: arm_cortex-a7_vfpv4 + - branch: openwrt-21.02 + arch: arm_cortex-a7_vfpv4 + container: image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }} options: --user root @@ -255,7 +267,7 @@ jobs: if: steps.build.outcome == 'success' uses: actions/upload-artifact@v4 with: - name: ${{ matrix.branch }}-${{ matrix.arch }} + name: kmod-youtubeUnblock-${{ matrix.branch }}-${{ matrix.arch }} path: ${{ matrix.wd_path }}/kmod-youtubeUnblock*.ipk if-no-files-found: error @@ -347,7 +359,7 @@ jobs: if: steps.build.outcome == 'success' uses: actions/upload-artifact@v4 with: - name: entware-${{ matrix.arch }} + name: youtubeUnblock-entware-${{ matrix.arch }} path: ./**/youtubeUnblock*-entware.tar.gz if-no-files-found: error From b3668f07ba7bdb96c3fb5602ce4fe3b101e4f083 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 7 Sep 2024 09:13:50 +0300 Subject: [PATCH 13/22] Add flags for kernel module --- Kbuild | 2 +- config.h | 4 +- kargs.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++ kytunblock.c | 47 ----------------- utils.c | 4 -- 5 files changed, 150 insertions(+), 53 deletions(-) create mode 100644 kargs.c diff --git a/Kbuild b/Kbuild index 57f1c6d..28e0379 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o -kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o kargs.o ccflags-y := -std=gnu11 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/config.h b/config.h index 189785a..23de902 100644 --- a/config.h +++ b/config.h @@ -111,6 +111,8 @@ extern struct config_t config; #define MAX_PACKET_SIZE 8192 -static const char defaul_snistr[] = "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com"; +#define DEFAULT_SNISTR "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com" + +static const char defaul_snistr[] = DEFAULT_SNISTR; #endif /* YTB_CONFIG_H */ diff --git a/kargs.c b/kargs.c new file mode 100644 index 0000000..9c5411b --- /dev/null +++ b/kargs.c @@ -0,0 +1,146 @@ +#include "config.h" +#include "raw_replacements.h" +#include "types.h" +#include + +#define STR_MAXLEN 1024 + +struct config_t config = { + .frag_sni_reverse = 1, + .frag_sni_faked = 0, + .fragmentation_strategy = FRAGMENTATION_STRATEGY, + .faking_strategy = FAKING_STRATEGY, + .faking_ttl = FAKE_TTL, + .fake_sni = 1, + .fake_sni_seq_len = 1, + .frag_middle_sni = 1, + .frag_sni_pos = 1, + .use_ipv6 = 1, + .fakeseq_offset = 10000, + .mark = DEFAULT_RAWSOCKET_MARK, + .synfake = 0, + .synfake_len = 0, + + .sni_detection = SNI_DETECTION_PARSE, + +#ifdef SEG2_DELAY + .seg2_delay = SEG2_DELAY, +#else + .seg2_delay = 0, +#endif + +#ifdef USE_GSO + .use_gso = 1, +#else + .use_gso = false, +#endif + +#ifdef DEBUG + .verbose = 2, +#else + .verbose = 1, +#endif + + .domains_str = defaul_snistr, + .domains_strlen = sizeof(defaul_snistr), + + .queue_start_num = DEFAULT_QUEUE_NUM, + .fake_sni_pkt = fake_sni_old, + .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator +}; + +static int unumeric_set(const char *val, const struct kernel_param *kp) { + int n = 0, ret; + ret = kstrtoint(val, 10, &n); + if (ret != 0 || n < 0) + return -EINVAL; + + return param_set_int(val, kp); +} + +static int boolean_set(const char *val, const struct kernel_param *kp) { + int n = 0, ret; + ret = kstrtoint(val, 10, &n); + if (ret != 0 || (n != 0 && n != 1)) + return -EINVAL; + + return param_set_int(val, kp); +} + +static const struct kernel_param_ops unumeric_parameter_ops = { + .set = unumeric_set, + .get = param_get_int +}; + +static const struct kernel_param_ops boolean_parameter_ops = { + .set = boolean_set, + .get = param_get_int +}; + +module_param_cb(fake_sni, &boolean_parameter_ops, &config.fake_sni, 0664); +module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &config.fake_sni_seq_len, 0664); +module_param_cb(faking_ttl, &unumeric_parameter_ops, &config.faking_ttl, 0664); +module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &config.fakeseq_offset, 0664); +module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &config.frag_sni_reverse, 0664); +module_param_cb(frag_sni_faked, &boolean_parameter_ops, &config.frag_sni_faked, 0664); +module_param_cb(frag_middle_sni, &boolean_parameter_ops, &config.frag_middle_sni, 0664); +module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &config.frag_sni_pos, 0664); +module_param_cb(fk_winsize, &unumeric_parameter_ops, &config.fk_winsize, 0664); +module_param_cb(synfake, &boolean_parameter_ops, &config.synfake, 0664); +module_param_cb(synfake_len, &unumeric_parameter_ops, &config.synfake_len, 0664); +module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664); + +static int sni_domains_set(const char *val, const struct kernel_param *kp) { + size_t len; + int ret; + + len = strnlen(val, STR_MAXLEN + 1); + if (len == STR_MAXLEN + 1) { + pr_err("%s: string parameter too long\n", kp->name); + return -ENOSPC; + } + + ret = param_set_charp(val, kp); + + if (ret < 0) { + config.domains_strlen = 0; + } else { + config.domains_strlen = len; + if (len == 3 && !strcmp(config.domains_str, "all")) { + config.all_domains = 1; + } + } + + + return ret; +} + +static const struct kernel_param_ops sni_domains_ops = { + .set = sni_domains_set, + .get = param_get_charp, +}; + +module_param_cb(sni_domains, &sni_domains_ops, &config.domains_str, 0664); + +static int exclude_domains_set(const char *val, const struct kernel_param *kp) { + size_t len; + int ret; + + len = strnlen(val, STR_MAXLEN + 1); + if (len == STR_MAXLEN + 1) { + pr_err("%s: string parameter too long\n", kp->name); + return -ENOSPC; + } + + ret = param_set_charp(val, kp); + + if (ret < 0) { + config.domains_strlen = 0; + } else { + config.domains_strlen = len; + } + + return ret; +} + + diff --git a/kytunblock.c b/kytunblock.c index 6ed9e07..2d6e8cf 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -16,62 +16,15 @@ #include "mangle.h" #include "config.h" -#include "raw_replacements.h" #include "utils.h" #include "logging.h" #include "kmod_utils.h" -struct config_t config = { - .threads = THREADS_NUM, - .frag_sni_reverse = 1, - .frag_sni_faked = 0, - .fragmentation_strategy = FRAGMENTATION_STRATEGY, - .faking_strategy = FAKING_STRATEGY, - .faking_ttl = FAKE_TTL, - .fake_sni = 1, - .fake_sni_seq_len = 1, - .frag_middle_sni = 1, - .frag_sni_pos = 1, - .use_ipv6 = 1, - .fakeseq_offset = 10000, - .mark = DEFAULT_RAWSOCKET_MARK, - .synfake = 0, - .synfake_len = 0, - - .sni_detection = SNI_DETECTION_PARSE, - -#ifdef SEG2_DELAY - .seg2_delay = SEG2_DELAY, -#else - .seg2_delay = 0, -#endif - -#ifdef USE_GSO - .use_gso = 1, -#else - .use_gso = false, -#endif - -#ifdef DEBUG - .verbose = 2, -#else - .verbose = 1, -#endif - - .domains_str = defaul_snistr, - .domains_strlen = sizeof(defaul_snistr), - - .queue_start_num = DEFAULT_QUEUE_NUM, - .fake_sni_pkt = fake_sni_old, - .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator -}; - MODULE_LICENSE("GPL"); MODULE_VERSION("0.3.2"); MODULE_AUTHOR("Vadim Vetrov "); MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); - static unsigned int ykb_nf_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { diff --git a/utils.c b/utils.c index 5f4c5f1..8d16d63 100644 --- a/utils.c +++ b/utils.c @@ -42,8 +42,6 @@ void ip4_set_checksum(struct iphdr *iph) } void tcp6_set_checksum(struct tcphdr *tcph, struct ip6_hdr *iph) { - uint16_t old_check = ntohs(tcph->check); - #ifdef KERNEL_SPACE tcph->check = 0; tcph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, @@ -252,7 +250,6 @@ int udp4_payload_split(uint8_t *pkt, uint32_t buflen, struct iphdr *hdr; uint32_t hdr_len; struct udphdr *uhdr; - uint32_t uhdr_len; uint8_t *ip_ph; uint32_t ip_phlen; @@ -291,7 +288,6 @@ int udp6_payload_split(uint8_t *pkt, uint32_t buflen, struct ip6_hdr *hdr; uint32_t hdr_len; struct udphdr *uhdr; - uint32_t uhdr_len; uint8_t *ip_ph; uint32_t ip_phlen; From 7919f82f4b7a3fbb78bda27c763a2306407f0fbf Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 7 Sep 2024 16:02:07 +0300 Subject: [PATCH 14/22] Delete kmod from workflow The systems and kernels are very various so we can't offer modules for all the systems. It is just easier to simply delete it. --- .github/workflows/build-ci.yml | 101 +-------------------------------- 1 file changed, 1 insertion(+), 100 deletions(-) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 4123746..7e80790 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -172,104 +172,6 @@ jobs: name: youtubeUnblock-${{ matrix.branch }}-${{ matrix.arch }} path: /builder/youtubeUnblock*.ipk if-no-files-found: error - build-openwrt-kmod: - needs: prepare - runs-on: ubuntu-latest - strategy: - matrix: - branch: - - openwrt-23.05 - - openwrt-22.03 - - openwrt-21.02 - - openwrt-19.07 - include: - - branch: openwrt-23.05 - wd_path: /builder - - branch: openwrt-22.03 - wd_path: /builder - - branch: openwrt-21.02 - wd_path: /builder - - branch: openwrt-19.07 - wd_path: /home/build/openwrt - arch: - - aarch64_cortex-a53 - - aarch64_cortex-a72 - - aarch64_generic - - arm_arm1176jzf-s_vfp - - arm_arm926ej-s - - arm_cortex-a15_neon-vfpv4 - - arm_cortex-a5_vfpv4 - - arm_cortex-a7 - - arm_cortex-a7_neon-vfpv4 - - arm_cortex-a7_vfpv4 - - arm_cortex-a8_vfpv3 - - arm_cortex-a9 - - arm_cortex-a9_neon - - arm_cortex-a9_vfpv3-d16 - - arm_fa526 - - arm_mpcore - - arm_xscale - - mips64_octeonplus - - mips_24kc - - mips_4kec - - mips_mips32 - - mipsel_24kc - - mipsel_24kc_24kf - - mipsel_74kc - - mipsel_mips32 - - ramips-mt76x8 - - x86_64 - exclude: - - branch: openwrt-19.07 - arch: arm_cortex-a7 - - branch: openwrt-19.07 - arch: mips_4kec - - branch: openwrt-19.07 - arch: ramips-mt76x8 - - branch: openwrt-19.07 - arch: arm_cortex-a7_vfpv4 - - branch: openwrt-21.02 - arch: arm_cortex-a7_vfpv4 - - container: - image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }} - options: --user root - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: 'openwrt' - - - name: Prepare build - env: - VERSION: ${{ needs.prepare.outputs.version }} - SHA: ${{ needs.prepare.outputs.sha }} - run: | - sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" kyoutubeUnblock/Makefile - - - name: Build packages - id: build - env: - VERSION: ${{ needs.prepare.outputs.version }} - SHA: ${{ needs.prepare.outputs.sha }} - working-directory: ${{ matrix.wd_path }} - run: | - pwd - echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf - cat feeds.conf - ./scripts/feeds update youtubeUnblock - ./scripts/feeds install -a -p youtubeUnblock - make defconfig - make package/kyoutubeUnblock/compile V=s - cp $(find ./bin -type f -name 'kmod-youtubeUnblock*.ipk') ./ - - - name: Upload packages - if: steps.build.outcome == 'success' - uses: actions/upload-artifact@v4 - with: - name: kmod-youtubeUnblock-${{ matrix.branch }}-${{ matrix.arch }} - path: ${{ matrix.wd_path }}/kmod-youtubeUnblock*.ipk - if-no-files-found: error build-entware: needs: prepare @@ -365,7 +267,7 @@ jobs: pre-release: if: github.event_name != 'pull_request' && github.ref_name == 'main' - needs: [build-static, build-openwrt, build-entware, build-openwrt-kmod] + needs: [build-static, build-openwrt, build-entware] permissions: contents: write runs-on: ubuntu-latest @@ -382,5 +284,4 @@ jobs: title: 'Development build' files: | ./**/youtubeUnblock*.ipk - ./**/kmod-youtubeUnblock*.ipk ./**/youtubeUnblock*.tar.gz From 5e28fe83c21ceae9c6a9dffc39d412b2b28f54b0 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 14 Sep 2024 20:37:19 +0300 Subject: [PATCH 15/22] Allow allocate in user-space with malloc --- types.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/types.h b/types.h index 23cac9f..81b3b8e 100644 --- a/types.h +++ b/types.h @@ -96,6 +96,11 @@ typedef __u64 uint64_t; #define NETBUF_ALLOC(buf, buf_len) __u8* buf = kmalloc(buf_len, GFP_ATOMIC); #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) From 49a48c33cffddbf3a03012ff82177fffb00b2da1 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 15 Sep 2024 22:02:04 +0300 Subject: [PATCH 16/22] Update kernel module arguments --- kargs.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/kargs.c b/kargs.c index 3660703..b355c6c 100644 --- a/kargs.c +++ b/kargs.c @@ -3,7 +3,7 @@ #include "types.h" #include -#define STR_MAXLEN 1024 +#define STR_MAXLEN 2048 struct config_t config = { .frag_sni_reverse = 1, @@ -55,6 +55,7 @@ static int unumeric_set(const char *val, const struct kernel_param *kp) { if (ret != 0 || n < 0) return -EINVAL; + return param_set_int(val, kp); } @@ -67,6 +68,30 @@ static int boolean_set(const char *val, const struct kernel_param *kp) { return param_set_int(val, kp); } +static int inverse_boolean_set(const char *val, const struct kernel_param *kp) { + int n = 0, ret; + ret = kstrtoint(val, 10, &n); + if (ret != 0 || (n != 0 && n != 1)) + return -EINVAL; + + n = !n; + if (kp->arg == NULL) + return -EINVAL; + + *(int *)kp->arg = n; + return 0; +} + +static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) { + if (*(int *)kp->arg == 0) { + buffer[0] = '1'; + } else { + buffer[0] = '0'; + } + buffer[1] = '\0'; + return 0; +} + static const struct kernel_param_ops unumeric_parameter_ops = { .set = unumeric_set, .get = param_get_int @@ -77,6 +102,11 @@ static const struct kernel_param_ops boolean_parameter_ops = { .get = param_get_int }; +static const struct kernel_param_ops inverse_boolean_ops = { + .set = inverse_boolean_set, + .get = inverse_boolean_get, +}; + module_param_cb(fake_sni, &boolean_parameter_ops, &config.fake_sni, 0664); module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &config.fake_sni_seq_len, 0664); module_param_cb(faking_ttl, &unumeric_parameter_ops, &config.faking_ttl, 0664); @@ -100,14 +130,20 @@ static int sni_domains_set(const char *val, const struct kernel_param *kp) { return -ENOSPC; } + if (len >= 1 && val[len - 1] == '\n') { + len--; + } + ret = param_set_charp(val, kp); if (ret < 0) { config.domains_strlen = 0; } else { config.domains_strlen = len; - if (len == 3 && !strcmp(config.domains_str, "all")) { + if (len == 3 && !strncmp(val, "all", len)) { config.all_domains = 1; + } else { + config.all_domains = 0; } } @@ -150,3 +186,30 @@ static const struct kernel_param_ops exclude_domains_ops = { module_param_cb(exclude_domains, &exclude_domains_ops, &config.exclude_domains_str, 0664); +module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664); +module_param_cb(silent, &inverse_boolean_ops, &config.verbose, 0664); +module_param_cb(quic_drop, &boolean_parameter_ops, &config.quic_drop, 0664); + +static int verbose_trace_set(const char *val, const struct kernel_param *kp) { + int n = 0, ret; + ret = kstrtoint(val, 10, &n); + if (ret != 0 || (n != 0 && n != 1)) + return -EINVAL; + + if (n) { + n = VERBOSE_TRACE; + } else { + n = VERBOSE_DEBUG; + } + if (kp->arg == NULL) + return -EINVAL; + + *(int *)kp->arg = n; + return 0; +} + +static const struct kernel_param_ops verbose_trace_ops = { + .set = verbose_trace_set, + .get = param_get_int, +}; +module_param_cb(trace, &verbose_trace_ops, &config.verbose, 0664); From 1ade21aa2206131892f3e2d3446bab2e74e1e46c Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 16 Sep 2024 00:28:10 +0300 Subject: [PATCH 17/22] Update README for kernel module --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ kmake.mk | 6 ++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 22e9852..e4eb05e 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,11 @@ - [OpenWRT case](#openwrt-case) - [Building OpenWRT .ipk package](#building-openwrt-ipk-package) - [Building with toolchain](#building-with-toolchain) + - [Kernel module](#kernel-module) + - [Building kernel module](#building-kernel-module) + - [Building on host system](#building-on-host-system) + - [Building on any kernel](#building-on-any-kernel) + - [Building with openwrt SDK](#building-with-openwrt-sdk) # youtubeUnblock @@ -26,6 +31,10 @@ Bypasses Deep Packet Inspection (DPI) systems that relies on SNI. The package is The program was primarily developed to bypass YouTube Outage in Russia. +The program is distributed in two version: +- A userspace application works on top of nfnetlink queue which requires nfnetlink modules in the kernel and firewall rules. This approach is default and normally should be used but it has some limitations on embedded devices which may have no nfnetlink support. Also this solution may break down the internet speed and CPU load on your device because of jumps between userspace and kernelspace for each packet (this behavior may be fixed with connbytes but it also requires conntrack kernel module). +- A kernel module which integrates deeply within the netfilter stack and does not interact with the userspace firewall. The module requires only netfilter kernel support but it definetly present on every device connected to the Internet. The only difficulity is how to build it. I cannot provide modules within Github Actions for each single one kernel, even if we talk only about OpenWRT versions. If you want to learn more about the module, jump on [its section in the README](#kernel-module) + The program is compatible with routers based on OpenWRT, Entware(Keenetic/ASUS) and host machines. The program offers binaries via Github Actions. The binaries of main branch are published in the [development pre-release](https://github.com/Waujito/youtubeUnblock/releases/tag/continuous). Check out [Github Actions](https://github.com/Waujito/youtubeUnblock/actions/workflows/build-ci.yml) if you want to see all the binaries compiled ever. You should know the arcitecture of your hardware to use binaries. On OpenWRT you can check it with command `grep ARCH /etc/openwrt_release`. On both OpenWRT and Entware install the program with opkg. If you got read-only filesystem error you may unpack the binary manually or specify opkg path `opkg -o `. @@ -314,5 +323,46 @@ Take a look at `CROSS_COMPILE_PLATFORM` It is required by autotools but I think When compilation is done, the binary file will be in build directory. Copy it to your router. Note that a ssh access is likely to be required to proceed. *sshfs* don't work on my model so I injected the application to the router via *Software Upload Package* page. It has given me an error, but also a `/tmp/upload.ipk` file which I copied in root directory, `chmod +x` it and run. +# Kernel module + +This section describes the kernel module version of youtubeUnblock. The kernel module operates as a normal module inside the kernel and integrates within the netfilter stack to statelessly mangle the packets sent over the Internet. + +You can configure the module with its flags in insmod: +``` +insmod kyoutubeUnblock.ko fake_sni=1 exclude_domains=.ru quic_drop=1 +``` + +Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`silent=1 quic_drop=1`) + +Also a drop in replacement is supported for all the parameters excluding packet mark. A drop in replacement does not require module restart if you want to change the parameters. You can specify and check the parameters within module's directory inside the sysfs: `/sys/module/kyoutubeUnblock/parameters/`. For example, to set quic_drop to true you may use next command: +```sh +echo 1 | sudo tee /sys/module/kyoutubeUnblock/parameters/quic_drop +``` +and +```sh +cat /sys/module/kyoutubeUnblock/parameters/quic_drop +``` +to check the parameter. + +## Building kernel module + +### Building on host system + +To build the kernel module on your host system you should install `linux-headers` which will provide build essential tools and `gcc` compiler suite. On host system you may build the module with +```sh +make kmake +``` + +### Building on any kernel + +To build the module for external kernel you should build that kernel locally and point make to it. Use `KERNEL_BUILDER_MAKEDIR=~/linux` flag for make, for example: +``` +make kmake KERNEL_BUILDER_MAKEDIR=~/linux +``` +Note, that the kernel should be already configured and built. See linux kernel building manuals for more information about your specific case. + +### Building with openwrt SDK + +This parts needs in further maintance. >If you have any questions/suggestions/problems feel free to open an [issue](https://github.com/Waujito/youtubeUnblock/issues). diff --git a/kmake.mk b/kmake.mk index aacd391..3ad7d87 100644 --- a/kmake.mk +++ b/kmake.mk @@ -7,11 +7,13 @@ LD := ld CFLAGS := LDFLAGS := +KERNEL_BUILDER_MAKEDIR:=/lib/modules/$(shell uname -r)/build + .PHONY: kmake kload kunload kreload kclean kmclean xclean kmake: kmod kmod: - $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + $(MAKE) -C $(KERNEL_BUILDER_MAKEDIR) M=$(PWD) modules kload: insmod kyoutubeUnblock.ko @@ -24,4 +26,4 @@ kreload: kunload kload kclean: kmclean kmclean: - -$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + -$(MAKE) -C $(KERNEL_BUILDER_MAKEDIR) M=$(PWD) clean From 1cacac7adc27697ada5a28893ac8aae1c967ee2a Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 17 Sep 2024 19:28:13 +0300 Subject: [PATCH 18/22] Add notices about kernel module and openwrt sdk --- README.md | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c4b48e0..44d41e8 100644 --- a/README.md +++ b/README.md @@ -310,7 +310,7 @@ Take a look at `CROSS_COMPILE_PLATFORM` It is required by autotools but I think When compilation is done, the binary file will be in build directory. Copy it to your router. Note that a ssh access is likely to be required to proceed. *sshfs* don't work on my model so I injected the application to the router via *Software Upload Package* page. It has given me an error, but also a `/tmp/upload.ipk` file which I copied in root directory, `chmod +x` it and run. -# Kernel module +## Kernel module This section describes the kernel module version of youtubeUnblock. The kernel module operates as a normal module inside the kernel and integrates within the netfilter stack to statelessly mangle the packets sent over the Internet. @@ -331,16 +331,16 @@ cat /sys/module/kyoutubeUnblock/parameters/quic_drop ``` to check the parameter. -## Building kernel module +### Building kernel module -### Building on host system +#### Building on host system To build the kernel module on your host system you should install `linux-headers` which will provide build essential tools and `gcc` compiler suite. On host system you may build the module with ```sh make kmake ``` -### Building on any kernel +#### Building on any kernel To build the module for external kernel you should build that kernel locally and point make to it. Use `KERNEL_BUILDER_MAKEDIR=~/linux` flag for make, for example: ``` @@ -348,8 +348,26 @@ make kmake KERNEL_BUILDER_MAKEDIR=~/linux ``` Note, that the kernel should be already configured and built. See linux kernel building manuals for more information about your specific case. -### Building with openwrt SDK +#### Building with openwrt SDK -This parts needs in further maintance. +Building with openwrt SDK is not such a hard thing. The only thing you should do is to obtain the sdk. You can find it by looking to your architecture and version of the openwrt currently used. You should use the exactly your version of openwrt since kernels there change often. You can find the sdk in two ways: by downloading it from their site or by using the openwrt sdk docker container (recommended). + +If you decide to download the tar archive, follow next steps: +For me the archive lives in https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-sdk-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64`. You will need to [install sdk requirements on your system](https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem) If you have any problems, use docker ubuntu:24.04 image. Make sure to be a non-root user since some makesystem fails with it. Next, untar the SDK and cd into it. + +Or you can obtain the docker image with sdk built-in: [https://hub.docker.com/u/openwrt/sdk](https://hub.docker.com/u/openwrt/sdk). In my case the image has tag `ramips-mt76x8-23.05.3`. A good thing here is that you don't need to install any dependencies inside the docker container. Also docker hub has a perfect search around tags if you don't sure which one corresponds to your device. + +When you unpacked/installed the sdk, you is ready to start with building the kernel module. + +Do +```sh +echo "src-git youtubeUnblock https://github.com/Waujito/youtubeUnblock.git;openwrt" >> feeds.conf +./scripts/feeds update youtubeUnblock +./scripts/feeds install -a -p youtubeUnblock +make defconfig +make package/kyoutubeUnblock/compile V=s +``` + +When the commands finish, the module is ready. Find it with `find bin -name "kmod-youtubeUnblock*.ipk"`, copy to your host and install to the router via gui software interface. The module should start immediately. If not, do `modprobe kyoutubeUnblock`. >If you have any questions/suggestions/problems feel free to open an [issue](https://github.com/Waujito/youtubeUnblock/issues). From 3187b3ca61be83a5fdfaf314f209bd6e0a96a62a Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 19 Sep 2024 16:47:18 +0300 Subject: [PATCH 19/22] Delete mutexes from rawsocket The program works in sync way with kernel --- kmod_utils.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/kmod_utils.c b/kmod_utils.c index fabb215..930f06d 100644 --- a/kmod_utils.c +++ b/kmod_utils.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -15,10 +14,8 @@ #include "logging.h" static struct socket *rawsocket; -DEFINE_MUTEX(rslock); static struct socket *raw6socket; -DEFINE_MUTEX(rs6lock); int open_raw_socket(void) { @@ -76,9 +73,7 @@ static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { msg.msg_control = NULL; msg.msg_controllen = 0; - mutex_lock(&rslock); ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); - mutex_unlock(&rslock); return ret; } @@ -137,9 +132,7 @@ int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { msg.msg_control = NULL; msg.msg_controllen = 0; - mutex_lock(&rs6lock); ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen); - mutex_unlock(&rs6lock); return ret; } From 5415bc37ec0cade5f51708f23952496fd1fc3d1f Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 19 Sep 2024 19:21:49 +0300 Subject: [PATCH 20/22] Compatibility for kernel version 3 --- .github/workflows/build-ci.yml | 101 ++++++++++++++++++++- Kbuild | 2 +- kargs.c | 161 ++++++++++++++++++++++++++++++++- kmod_utils.c | 28 +++++- kytunblock.c | 5 +- nf_wrapper.h | 92 +++++++++++++++++++ utils.c | 4 +- 7 files changed, 380 insertions(+), 13 deletions(-) create mode 100644 nf_wrapper.h diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index e288e5d..8fa0858 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -229,6 +229,104 @@ jobs: path: /builder/youtubeUnblock*.ipk if-no-files-found: error + + build-openwrt-kmod: + needs: prepare + runs-on: ubuntu-latest + strategy: + matrix: + branch: + - openwrt-23.05 + - openwrt-22.03 + - openwrt-21.02 + - openwrt-19.07 + include: + - branch: openwrt-23.05 + wd_path: /builder + - branch: openwrt-22.03 + wd_path: /builder + - branch: openwrt-21.02 + wd_path: /builder + - branch: openwrt-19.07 + wd_path: /home/build/openwrt + arch: + - aarch64_cortex-a53 + - aarch64_cortex-a72 + - aarch64_generic + - arm_arm1176jzf-s_vfp + - arm_arm926ej-s + - arm_cortex-a15_neon-vfpv4 + - arm_cortex-a5_vfpv4 + - arm_cortex-a7 + - arm_cortex-a7_neon-vfpv4 + - arm_cortex-a7_vfpv4 + - arm_cortex-a8_vfpv3 + - arm_cortex-a9 + - arm_cortex-a9_neon + - arm_cortex-a9_vfpv3-d16 + - arm_fa526 + - arm_mpcore + - arm_xscale + - mips64_octeonplus + - mips_24kc + - mips_4kec + - mips_mips32 + - mipsel_24kc + - mipsel_24kc_24kf + - mipsel_74kc + - mipsel_mips32 + - ramips-mt76x8 + - x86_64 + exclude: + - branch: openwrt-19.07 + arch: arm_cortex-a7 + - branch: openwrt-19.07 + arch: mips_4kec + - branch: openwrt-19.07 + arch: ramips-mt76x8 + - branch: openwrt-19.07 + arch: arm_cortex-a7_vfpv4 + - branch: openwrt-21.02 + arch: arm_cortex-a7_vfpv4 + + container: + image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }} + options: --user root + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: 'openwrt' + + - name: Prepare build + env: + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + run: | + sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" kyoutubeUnblock/Makefile + - name: Build packages + id: build + env: + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + working-directory: ${{ matrix.wd_path }} + run: | + pwd + echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf + cat feeds.conf + ./scripts/feeds update youtubeUnblock + ./scripts/feeds install -a -p youtubeUnblock + make defconfig + make package/kyoutubeUnblock/compile V=s + cp $(find ./bin -type f -name 'kmod-youtubeUnblock*.ipk') ./ + - name: Upload packages + if: steps.build.outcome == 'success' + uses: actions/upload-artifact@v4 + with: + name: kmod-youtubeUnblock-${{ matrix.branch }}-${{ matrix.arch }} + path: ${{ matrix.wd_path }}/kmod-youtubeUnblock*.ipk + if-no-files-found: error + build-entware: needs: prepare runs-on: ubuntu-latest @@ -323,7 +421,7 @@ jobs: pre-release: if: github.event_name != 'pull_request' && github.ref_name == 'main' - needs: [build-static, build-static-cross, build-openwrt, build-entware] + needs: [build-static, build-static-cross, build-openwrt, build-entware, build-openwrt-kmod] permissions: contents: write runs-on: ubuntu-latest @@ -340,4 +438,5 @@ jobs: title: 'Development build' files: | ./**/youtubeUnblock*.ipk + ./**/kmod-youtubeUnblock*.ipk ./**/youtubeUnblock*.tar.gz diff --git a/Kbuild b/Kbuild index 28e0379..3962a79 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o kargs.o -ccflags-y := -std=gnu11 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement +ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/kargs.c b/kargs.c index b355c6c..00ed9c0 100644 --- a/kargs.c +++ b/kargs.c @@ -89,7 +89,7 @@ static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) { buffer[0] = '0'; } buffer[1] = '\0'; - return 0; + return strlen(buffer); } static const struct kernel_param_ops unumeric_parameter_ops = { @@ -212,4 +212,163 @@ static const struct kernel_param_ops verbose_trace_ops = { .set = verbose_trace_set, .get = param_get_int, }; + module_param_cb(trace, &verbose_trace_ops, &config.verbose, 0664); + +static int frag_strat_set(const char *val, const struct kernel_param *kp) { + size_t len; + + len = strnlen(val, STR_MAXLEN + 1); + if (len == STR_MAXLEN + 1) { + pr_err("%s: string parameter too long\n", kp->name); + return -ENOSPC; + } + + if (len >= 1 && val[len - 1] == '\n') { + len--; + } + + if (strncmp(val, "tcp", len) == 0) { + *(int *)kp->arg = FRAG_STRAT_TCP; + } else if (strncmp(val, "ip", len) == 0) { + *(int *)kp->arg = FRAG_STRAT_IP; + } else if (strncmp(val, "none", len) == 0) { + *(int *)kp->arg = FRAG_STRAT_NONE; + } else { + return -EINVAL; + } + + return 0; +} + +static int frag_strat_get(char *buffer, const struct kernel_param *kp) { + switch (*(int *)kp->arg) { + case FRAG_STRAT_TCP: + strcpy(buffer, "tcp\n"); + break; + case FRAG_STRAT_IP: + strcpy(buffer, "ip\n"); + break; + case FRAG_STRAT_NONE: + strcpy(buffer, "none\n"); + break; + default: + strcpy(buffer, "unknown\n"); + } + + return strlen(buffer); +} + +static const struct kernel_param_ops frag_strat_ops = { + .set = frag_strat_set, + .get = frag_strat_get, +}; + +module_param_cb(fragmentation_strategy, &frag_strat_ops, &config.fragmentation_strategy, 0664); + +static int fake_strat_set(const char *val, const struct kernel_param *kp) { + size_t len; + + len = strnlen(val, STR_MAXLEN + 1); + if (len == STR_MAXLEN + 1) { + pr_err("%s: string parameter too long\n", kp->name); + return -ENOSPC; + } + + if (len >= 1 && val[len - 1] == '\n') { + len--; + } + + if (strncmp(val, "randseq", len) == 0) { + *(int *)kp->arg = FAKE_STRAT_RAND_SEQ; + } else if (strncmp(val, "ttl", len) == 0) { + *(int *)kp->arg = FAKE_STRAT_TTL; + } else if (strncmp(val, "tcp_check", len) == 0) { + *(int *)kp->arg = FAKE_STRAT_TCP_CHECK; + } else if (strncmp(val, "pastseq", len) == 0) { + *(int *)kp->arg = FAKE_STRAT_PAST_SEQ; + } else if (strncmp(val, "md5sum", len) == 0) { + *(int *)kp->arg = FAKE_STRAT_TCP_MD5SUM; + } else { + return -EINVAL; + } + + return 0; +} + +static int fake_strat_get(char *buffer, const struct kernel_param *kp) { + switch (*(int *)kp->arg) { + case FAKE_STRAT_RAND_SEQ: + strcpy(buffer, "randseq\n"); + break; + case FAKE_STRAT_TTL: + strcpy(buffer, "ttl\n"); + break; + case FAKE_STRAT_TCP_CHECK: + strcpy(buffer, "tcp_check\n"); + break; + case FAKE_STRAT_PAST_SEQ: + strcpy(buffer, "pastseq\n"); + break; + case FAKE_STRAT_TCP_MD5SUM: + strcpy(buffer, "md5sum\n"); + break; + default: + strcpy(buffer, "unknown\n"); + } + + return strlen(buffer); +} + +static const struct kernel_param_ops fake_strat_ops = { + .set = fake_strat_set, + .get = fake_strat_get, +}; + +module_param_cb(faking_strategy, &fake_strat_ops, &config.faking_strategy, 0664); + +static int sni_detection_set(const char *val, const struct kernel_param *kp) { + size_t len; + + len = strnlen(val, STR_MAXLEN + 1); + if (len == STR_MAXLEN + 1) { + pr_err("%s: string parameter too long\n", kp->name); + return -ENOSPC; + } + + if (len >= 1 && val[len - 1] == '\n') { + len--; + } + + if (strncmp(val, "parse", len) == 0) { + *(int *)kp->arg = SNI_DETECTION_PARSE; + } else if (strncmp(val, "brute", len) == 0) { + *(int *)kp->arg = SNI_DETECTION_BRUTE; + } else { + return -EINVAL; + } + + return 0; +} + +static int sni_detection_get(char *buffer, const struct kernel_param *kp) { + switch (*(int *)kp->arg) { + case SNI_DETECTION_PARSE: + strcpy(buffer, "parse\n"); + break; + case SNI_DETECTION_BRUTE: + strcpy(buffer, "brute\n"); + break; + default: + strcpy(buffer, "unknown\n"); + } + + return strlen(buffer); +} + +static const struct kernel_param_ops sni_detection_ops = { + .set = sni_detection_set, + .get = sni_detection_get, +}; + +module_param_cb(sni_detection, &sni_detection_ops, &config.sni_detection, 0664); diff --git a/kmod_utils.c b/kmod_utils.c index 930f06d..2884e9a 100644 --- a/kmod_utils.c +++ b/kmod_utils.c @@ -32,8 +32,7 @@ int open_raw_socket(void) { rawsocket->sk->sk_mark=config.mark; return 0; -sr_err: - sock_release(rawsocket); + err: return ret; } @@ -63,9 +62,19 @@ static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { struct msghdr msg; struct kvec iov; + + memset(&msg, 0, sizeof(msg)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) iov.iov_base = (__u8 *)pkt; iov.iov_len = pktlen; iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); +#else + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + // msg.msg_iov = (struct iovec *)&iov; + // msg.msg_iovlen = iov.iov_len; +#endif msg.msg_flags = 0; msg.msg_name = &daddr; @@ -73,6 +82,7 @@ static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { msg.msg_control = NULL; msg.msg_controllen = 0; + ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); return ret; @@ -92,8 +102,7 @@ int open_raw6_socket(void) { raw6socket->sk->sk_mark=config.mark; return 0; -sr_err: - sock_release(raw6socket); + err: return ret; } @@ -120,11 +129,20 @@ int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { .sin6_addr = iph->ip6_dst }; - struct msghdr msg; struct kvec iov; + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) iov.iov_base = (__u8 *)pkt; iov.iov_len = pktlen; iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); +#else + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + // msg.msg_iov = (struct iovec *)&iov; + // msg.msg_iovlen = iov.iov_len; +#endif msg.msg_flags = 0; msg.msg_name = &daddr; diff --git a/kytunblock.c b/kytunblock.c index 2d6e8cf..b558ea7 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -1,3 +1,4 @@ +#include "nf_wrapper.h" #ifndef KERNEL_SPACE #error "You are trying to compile the kernel module not in the kernel space" #endif @@ -25,9 +26,7 @@ MODULE_VERSION("0.3.2"); MODULE_AUTHOR("Vadim Vetrov "); MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); -static unsigned int ykb_nf_hook(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) { +static NF_CALLBACK(ykb_nf_hook, skb) { int ret; if ((skb->mark & config.mark) == config.mark) diff --git a/nf_wrapper.h b/nf_wrapper.h new file mode 100644 index 0000000..2b4fc4b --- /dev/null +++ b/nf_wrapper.h @@ -0,0 +1,92 @@ +/** + * Thanks https://github.com/NICMx/Jool/blob/5f60dcda5944b01cc43c3be342aad26af8161bcb/include/nat64/mod/common/nf_wrapper.h for mapped kernel versions + */ +#ifndef _JOOL_MOD_NF_WRAPPER_H +#define _JOOL_MOD_NF_WRAPPER_H + +/** + * @file + * The kernel API is far from static. In particular, the Netfilter packet entry + * function keeps changing. nf_hook.c, the file where we declare our packet + * entry function, has been quite difficult to read for a while now. It's pretty + * amusing, because we don't even use any of the noisy arguments. + * + * This file declares a usable function header that abstracts away all those + * useless arguments. + */ + +#include + +/* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */ +#ifdef RHEL_RELEASE_CODE + +#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 2) +#define NF_CALLBACK(name, skb) unsigned int name( \ + const struct nf_hook_ops *ops, \ + struct sk_buff *skb, \ + const struct net_device *in, \ + const struct net_device *out, \ + const struct nf_hook_state *state) \ + +#elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + const struct nf_hook_ops *ops, \ + struct sk_buff *skb, \ + const struct net_device *in, \ + const struct net_device *out, \ + int (*okfn)(struct sk_buff *)) + +#else + +/* + * Sorry, I don't have headers for RHEL 6 and below because I'm in a bit of a + * deadline right now. + * If this is causing you trouble, find `nf_hookfn` in your kernel headers + * (typically in include/linux/netfilter.h) and add your version of the + * NF_CALLBACK macro here. + * Also, kernel headers per version can be found here: http://vault.centos.org/ + */ +#error "Sorry; this version of RHEL is not supported because it's kind of old." + +#endif /* RHEL_RELEASE_CODE >= x */ + + +/* If this NOT a RedHat-based kernel (Ubuntu, Debian, SuSE, etc)... */ +#else + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + void *priv, \ + struct sk_buff *skb, \ + const struct nf_hook_state *state) + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + const struct nf_hook_ops *ops, \ + struct sk_buff *skb, \ + const struct nf_hook_state *state) + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + const struct nf_hook_ops *ops, \ + struct sk_buff *skb, \ + const struct net_device *in, \ + const struct net_device *out, \ + int (*okfn)(struct sk_buff *)) + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + unsigned int hooknum, \ + struct sk_buff *skb, \ + const struct net_device *in, \ + const struct net_device *out, \ + int (*okfn)(struct sk_buff *)) + +#else +#error "Linux < 3.0 isn't supported at all." + +#endif /* LINUX_VERSION_CODE > n */ + +#endif /* RHEL or not RHEL */ + +#endif /* _JOOL_MOD_NF_WRAPPER_H */ diff --git a/utils.c b/utils.c index 8d16d63..19f14fa 100644 --- a/utils.c +++ b/utils.c @@ -506,8 +506,8 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, void z_function(const char *str, int *zbuf, size_t len) { zbuf[0] = len; - ssize_t lh = 0, rh = 1; - for (ssize_t i = 1; i < len; i++) { + int lh = 0, rh = 1; + for (int i = 1; i < (int)len; i++) { zbuf[i] = 0; if (i < rh) { zbuf[i] = min(zbuf[i - lh], rh - i); From 2fd3107401327099f78aedd5f7db43f6ba70fa78 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 19 Sep 2024 22:28:32 +0300 Subject: [PATCH 21/22] Fix memcpy --- kmod_utils.c | 16 ---------------- mangle.c | 7 ++++++- types.h | 2 +- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/kmod_utils.c b/kmod_utils.c index 2884e9a..68b244d 100644 --- a/kmod_utils.c +++ b/kmod_utils.c @@ -65,16 +65,8 @@ static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { memset(&msg, 0, sizeof(msg)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) iov.iov_base = (__u8 *)pkt; iov.iov_len = pktlen; - iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); -#else - iov.iov_base = (__u8 *)pkt; - iov.iov_len = pktlen; - // msg.msg_iov = (struct iovec *)&iov; - // msg.msg_iovlen = iov.iov_len; -#endif msg.msg_flags = 0; msg.msg_name = &daddr; @@ -133,16 +125,8 @@ int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { struct msghdr msg; memset(&msg, 0, sizeof(msg)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) iov.iov_base = (__u8 *)pkt; iov.iov_len = pktlen; - iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1); -#else - iov.iov_base = (__u8 *)pkt; - iov.iov_len = pktlen; - // msg.msg_iov = (struct iovec *)&iov; - // msg.msg_iovlen = iov.iov_len; -#endif msg.msg_flags = 0; msg.msg_name = &daddr; diff --git a/mangle.c b/mangle.c index c0918e5..3265f7d 100644 --- a/mangle.c +++ b/mangle.c @@ -1037,7 +1037,12 @@ int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { return -1; } uint8_t *ndata = data + delta; - memcpy(ndata, data, dlen); + uint8_t *ndptr = ndata + dlen; + uint8_t *dptr = data + dlen; + for (size_t i = dlen + 1; i > 0; i--) { + *ndptr = *dptr; + --ndptr, --dptr; + } data = ndata; tcph_len = tcph_len + delta; tcph->doff = tcph_len >> 2; diff --git a/types.h b/types.h index 81b3b8e..777601d 100644 --- a/types.h +++ b/types.h @@ -93,7 +93,7 @@ typedef __u64 uint64_t; */ #ifdef KERNEL_SPACE #include -#define NETBUF_ALLOC(buf, buf_len) __u8* buf = kmalloc(buf_len, GFP_ATOMIC); +#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) From c786a44dd55612437275b5f54646f75c374f05d8 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 20 Sep 2024 22:32:30 +0300 Subject: [PATCH 22/22] Fix warnings --- kmod_utils.c | 8 ++++---- kytunblock.c | 1 - logging.h | 3 ++- mangle.c | 24 ++++++------------------ nf_wrapper.h | 8 -------- types.h | 8 -------- 6 files changed, 12 insertions(+), 40 deletions(-) diff --git a/kmod_utils.c b/kmod_utils.c index 68b244d..12ca4ac 100644 --- a/kmod_utils.c +++ b/kmod_utils.c @@ -3,9 +3,9 @@ #endif #include "kmod_utils.h" +#include #include #include -#include #include #include @@ -143,7 +143,7 @@ int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { int ret; if (pktlen > AVAILABLE_MTU) { - pr_warn("The packet is too big and may cause issues!"); + lgdebug("The packet is too big and may cause issues!"); NETBUF_ALLOC(buff1, MAX_PACKET_SIZE); if (!NETBUF_CHECK(buff1)) { @@ -175,7 +175,7 @@ int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { } break; default: - pr_warn("send_raw_socket: Packet is too big but fragmentation is disabled!"); + pr_info("send_raw_socket: Packet is too big but fragmentation is disabled!"); ret = -EINVAL; goto erret_lc; } @@ -216,7 +216,7 @@ int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { } void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { - pr_warn("delay_packet_send won't work on current youtubeUnblock version"); + pr_info("delay_packet_send won't work on current youtubeUnblock version"); send_raw_socket(data, data_len); } diff --git a/kytunblock.c b/kytunblock.c index b558ea7..31f1c54 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -6,7 +6,6 @@ // Make with make kmake && sudo iptables -t mangle -D OUTPUT 1 && sudo make kreload && sudo iptables -t mangle -I OUTPUT -p tcp -j YTUNBLOCK #include #include -#include #include #include #include diff --git a/logging.h b/logging.h index a1998d9..bf6676d 100644 --- a/logging.h +++ b/logging.h @@ -5,7 +5,8 @@ #define LOG_LEVEL (config.verbose) #ifdef KERNEL_SPACE -#include +#include +#include #define printf pr_info #define perror pr_err #define lgerror(msg, ret, ...) __extension__ ({ \ diff --git a/mangle.c b/mangle.c index 3265f7d..0b6c45d 100644 --- a/mangle.c +++ b/mangle.c @@ -63,8 +63,6 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { accept: return PKT_ACCEPT; -drop: - return PKT_DROP; } int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { @@ -221,7 +219,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { } break; case FRAG_STRAT_IP: - if (ipxv != IP4VERSION) { + if (ipxv == IP4VERSION) { ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset; mid_offset = ipd_offset + vrd.sni_len / 2; mid_offset += 8 - mid_offset % 8; @@ -254,8 +252,10 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { goto drop_lc; } else { printf("WARNING: IP fragmentation is supported only for IPv4\n"); + goto default_send; } default: + default_send: ret = instance_config.send_raw_packet(payload, payload_len); if (ret < 0) { lgerror("raw pack send", ret); @@ -611,6 +611,7 @@ int post_fake_sni(const void *iph, unsigned int iph_len, } lgtrace_addp("post fake sni #%d", i + 1); + lgtrace_addp("post with %d", fsn_len); ret = instance_config.send_raw_packet(fake_sni, fsn_len); if (ret < 0) { lgerror("send fake sni", ret); @@ -632,7 +633,7 @@ int post_fake_sni(const void *iph, unsigned int iph_len, memcpy(rfstcph, fstcph, tcph_len); fsiph = (void *)rfsiph; fstcph = (void *)rfstcph; -out_lc: + NETBUF_FREE(fake_sni); continue; erret_lc: @@ -648,10 +649,6 @@ int post_fake_sni(const void *iph, unsigned int iph_len, #define TLS_EXTENSION_SNI 0x0000 #define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d -typedef uint8_t uint8_t; -typedef uint32_t uint32_t; -typedef uint16_t uint16_t; - /** * Processes tls payload of the tcp request. * @@ -672,9 +669,7 @@ struct tls_verdict analyze_tls_data( uint8_t tls_content_type = *msgData; uint8_t tls_vmajor = *(msgData + 1); - uint8_t tls_vminor = *(msgData + 2); uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3)); - const uint8_t *message_length_ptr = msgData + 3; if (tls_vmajor != 0x03) goto nextMessage; @@ -698,7 +693,6 @@ struct tls_verdict analyze_tls_data( const uint8_t *msgPtr = handshakeProto; msgPtr += 1; - const uint8_t *handshakeProto_length_ptr = msgPtr + 1; msgPtr += 3 + 2 + 32; if (msgPtr + 1 >= data_end) break; @@ -718,7 +712,6 @@ struct tls_verdict analyze_tls_data( if (msgPtr + 2 >= data_end) break; uint16_t extensionsLen = ntohs(*(uint16_t *)msgPtr); - const uint8_t *extensionsLen_ptr = msgPtr; msgPtr += 2; const uint8_t *extensionsPtr = msgPtr; @@ -735,7 +728,6 @@ struct tls_verdict analyze_tls_data( uint16_t extensionLen = ntohs(*(uint16_t *)extensionPtr); - const uint8_t *extensionLen_ptr = extensionPtr; extensionPtr += 2; @@ -750,14 +742,13 @@ struct tls_verdict analyze_tls_data( if (sni_ext_ptr + 2 >= extensions_end) break; uint16_t sni_ext_dlen = ntohs(*(uint16_t *)sni_ext_ptr); - const uint8_t *sni_ext_dlen_ptr = sni_ext_ptr; sni_ext_ptr += 2; const uint8_t *sni_ext_end = sni_ext_ptr + sni_ext_dlen; if (sni_ext_end >= extensions_end) break; if (sni_ext_ptr + 3 >= sni_ext_end) break; - uint8_t sni_type = *sni_ext_ptr++; + sni_ext_ptr++; uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr); sni_ext_ptr += 2; @@ -947,7 +938,6 @@ int gen_fake_sni(const void *ipxh, uint32_t iph_len, memcpy(buf + iph_len, tcph, tcph_len); memcpy(buf + iph_len + tcph_len, data, data_len); - struct tcphdr *ntcph = (struct tcphdr *)(buf + iph_len); if (ipxv == IP4VERSION) { struct iphdr *niph = (struct iphdr *)buf; @@ -993,8 +983,6 @@ int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { return ret; } - int sizedelta = 0; - if (config.faking_strategy == FAKE_STRAT_RAND_SEQ) { lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); diff --git a/nf_wrapper.h b/nf_wrapper.h index 2b4fc4b..a254a6d 100644 --- a/nf_wrapper.h +++ b/nf_wrapper.h @@ -38,14 +38,6 @@ #else -/* - * Sorry, I don't have headers for RHEL 6 and below because I'm in a bit of a - * deadline right now. - * If this is causing you trouble, find `nf_hookfn` in your kernel headers - * (typically in include/linux/netfilter.h) and add your version of the - * NF_CALLBACK macro here. - * Also, kernel headers per version can be found here: http://vault.centos.org/ - */ #error "Sorry; this version of RHEL is not supported because it's kind of old." #endif /* RHEL_RELEASE_CODE >= x */ diff --git a/types.h b/types.h index 777601d..65b4749 100644 --- a/types.h +++ b/types.h @@ -8,14 +8,6 @@ #include // IWYU pragma: export #include -typedef __u8 uint8_t; -typedef __u16 uint16_t; -typedef __u32 uint32_t; -typedef __u64 uint64_t; -//typedef __i8 int8_t; -//typedef __i16 int16_t; -//typedef __i32 int32_t; -//typedef __i64 int64_t; #else /* USER_SPACE */ #include // IWYU pragma: export