From 263a04bb9578024d8a0032e26d87b80e33c8cf7c Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 28 Sep 2024 22:17:11 +0300 Subject: [PATCH] Kernel module code cleanup --- Kbuild | 2 +- kmod_utils.c | 226 ---------------------------------------- kmod_utils.h | 14 --- kytunblock.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++- nf_wrapper.h | 84 --------------- 5 files changed, 284 insertions(+), 329 deletions(-) delete mode 100644 kmod_utils.c delete mode 100644 kmod_utils.h delete mode 100644 nf_wrapper.h diff --git a/Kbuild b/Kbuild index 1290c84..36eef22 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 tls.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/kmod_utils.c b/kmod_utils.c deleted file mode 100644 index 12ca4ac..0000000 --- a/kmod_utils.c +++ /dev/null @@ -1,226 +0,0 @@ -#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 "config.h" -#include "utils.h" -#include "logging.h" - -static struct socket *rawsocket; - -static struct socket *raw6socket; - - -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; - } - - // 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; - -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; - - memset(&msg, 0, sizeof(msg)); - - iov.iov_base = (__u8 *)pkt; - iov.iov_len = pktlen; - - msg.msg_flags = 0; - msg.msg_name = &daddr; - msg.msg_namelen = sizeof(struct sockaddr_in); - msg.msg_control = NULL; - msg.msg_controllen = 0; - - - ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); - - 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; - } - - // 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; - -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 kvec iov; - struct msghdr msg; - memset(&msg, 0, sizeof(msg)); - - iov.iov_base = (__u8 *)pkt; - iov.iov_len = pktlen; - - msg.msg_flags = 0; - msg.msg_name = &daddr; - msg.msg_namelen = sizeof(struct sockaddr_in6); - msg.msg_control = NULL; - msg.msg_controllen = 0; - - ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen); - - return ret; -} - -int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { - int ret; - - if (pktlen > AVAILABLE_MTU) { - lgdebug("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_info("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_info("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 deleted file mode 100644 index ade128e..0000000 --- a/kmod_utils.h +++ /dev/null @@ -1,14 +0,0 @@ -#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/kytunblock.c b/kytunblock.c index 31f1c54..ff68d36 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -1,14 +1,16 @@ -#include "nf_wrapper.h" #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 +// Build with make kmake #include #include #include #include #include +#include +#include #include #include @@ -18,12 +20,289 @@ #include "config.h" #include "utils.h" #include "logging.h" -#include "kmod_utils.h" MODULE_LICENSE("GPL"); MODULE_VERSION("0.3.2"); MODULE_AUTHOR("Vadim Vetrov "); -MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); +MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock"); + +static struct socket *rawsocket; + +static struct socket *raw6socket; + +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; + } + + // 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; + +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; + + memset(&msg, 0, sizeof(msg)); + + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + + msg.msg_flags = 0; + msg.msg_name = &daddr; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + + ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, 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; + } + + // 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; + +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 kvec iov; + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + + msg.msg_flags = 0; + msg.msg_name = &daddr; + msg.msg_namelen = sizeof(struct sockaddr_in6); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen); + + return ret; +} + +static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { + int ret; + + if (pktlen > AVAILABLE_MTU) { + lgdebug("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_info("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_info("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, +}; + + +/* 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 + +#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 */ + + static NF_CALLBACK(ykb_nf_hook, skb) { int ret; diff --git a/nf_wrapper.h b/nf_wrapper.h deleted file mode 100644 index a254a6d..0000000 --- a/nf_wrapper.h +++ /dev/null @@ -1,84 +0,0 @@ -/** - * 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 - -#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 */