From 5e327497bbf14bc88c43433eba78d14d346e0344 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 1 Sep 2024 19:56:38 +0300 Subject: [PATCH] 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 @@ drop: 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);