diff --git a/Kbuild b/Kbuild index 35c36db..1a41539 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := ipt_YTUNBLOCK.o -youtubeKUnblock-objs := ipt_YTUNBLOCK.o mangle.o +ipt_YTUNBLOCK-objs := iptk_YTUNBLOCK.o mangle.o ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE diff --git a/ipt_YTUNBLOCK.c b/ipt_YTUNBLOCK.c deleted file mode 100644 index de23ece..0000000 --- a/ipt_YTUNBLOCK.c +++ /dev/null @@ -1,118 +0,0 @@ -// Kernel module for youtubeUnblock. -#include -#include -#include -#include -#include -#include -#include -#include "ipt_YTUNBLOCK.h" -#include "mangle.h" - -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); -MODULE_AUTHOR("Vadim Vetrov "); -MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); - - -static int rsfd; -static struct socket *rawsocket; -DEFINE_MUTEX(rslock); - -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 = RAWSOCKET_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 err; - } - int one = 1; - optval.kernel = &one; - - // ret = sock_setsockopt(rawsocket, IPPROTO_IP, IP_HDRINCL, optval, sizeof(one)); - // if (ret < 0) - // { - // pr_alert("setsockopt(IP_HDRINCL, 1) failed\n"); - // goto err; - // } - - return 0; -err: - return ret; -} - -static void close_raw_socket(void) { - sock_release(rawsocket); -} - -static unsigned int ykb_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ - if (skb->head == NULL) return XT_CONTINUE; - const __u8 *rawdata = skb->head + skb->network_header; - const __u32 rawsize = skb->len; - struct iphdr *iph = ip_hdr(skb); - - pr_info("Lengths: %d %d %d %d\n", skb->len, skb->mac_len, skb->hdr_len, skb->data_len); - pr_info("Lengths: %d %d\n", skb->network_header == skb->mac_len, skb->hdr_len == iph->ihl * 4); - - return XT_CONTINUE; -} - -static int ykb_chk(const struct xt_tgchk_param *par) { - pr_info("Checkentry\n"); - 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), - .proto = IPPROTO_TCP, - .family = NFPROTO_IPV4, - .checkentry = ykb_chk, - .me = THIS_MODULE, -}; - -static int __init ykb_init(void) { - int ret = 0; - - ret = open_raw_socket(); - if (ret < 0) goto err; - - ret = xt_register_target(&ykb_tg_reg); - if (ret < 0) goto close_rawsocket; - - pr_info("youtubeUnblock kernel module started.\n"); - return 0; -close_rawsocket: - close_raw_socket(); -err: - return ret; -} - -static void __exit ykb_destroy(void) { - xt_unregister_target(&ykb_tg_reg); - close_raw_socket(); - pr_info("youtubeUnblock kernel module destroyed.\n"); -} - -module_init(ykb_init); -module_exit(ykb_destroy); diff --git a/iptk_YTUNBLOCK.c b/iptk_YTUNBLOCK.c new file mode 100644 index 0000000..a952768 --- /dev/null +++ b/iptk_YTUNBLOCK.c @@ -0,0 +1,314 @@ +// +// 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 +#include +#include +#include +#include +#include +#include +#include "ipt_YTUNBLOCK.h" +#include "mangle.h" + +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Vadim Vetrov "); +MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); + +#define USE_TCP_SEGMENTATION + +static int rsfd; +static struct socket *rawsocket; +DEFINE_MUTEX(rslock); + +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 = RAWSOCKET_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; + + // ret = sock_setsockopt(rawsocket, IPPROTO_IP, IP_HDRINCL, optval, sizeof(one)); + // if (ret < 0) + // { + // pr_alert("setsockopt(IP_HDRINCL, 1) failed\n"); + // goto err; + // } + + return 0; +sr_err: + sock_release(rawsocket); +err: + return ret; +} + +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_alert("The packet is too big!"); + return -ENOMEM; +#ifdef DEBUG + printf("Split packet!\n"); +#endif + + __u32 buff1_size = pktlen; + __u32 buff2_size = pktlen; + __u8 *buff1 = kmalloc(pktlen, GFP_KERNEL); + if (buff1 == NULL) return -1; + __u8 *buff2 = kmalloc(pktlen, GFP_KERNEL); + if (buff2 == NULL) { + kfree(buff1); + return -1; + } + + int ret; + +#ifdef USE_TCP_SEGMENTATION + if ((ret = tcp4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) + return ret; +#else + if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) + return ret; + +#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; + } + + // TODO: Implement packet send via kernel + //https://stackoverflow.com/questions/25958715/linux-kernel-module-how-to-reinject-packets-the-kernel-considers-as-nf-stolen + //https://stackoverflow.com/questions/15934513/cannot-send-out-packets-by-dev-queue-xmit + //https://stackoverflow.com/questions/66846959/send-packet-in-linux-kernel + return 0; +/* + struct iphdr *iph; + + int ret; + if ((ret = ip4_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + return ret; + } + + int sin_port = 0; + + struct tcphdr *tcph; + if (tcp4_payload_split((uint8_t *)pkt, pktlen, NULL, NULL, &tcph, NULL, NULL, NULL) == 0) + sin_port = tcph->dest; + + struct sockaddr_in daddr = { + .sin_family = AF_INET, + .sin_port = sin_port, + .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 = (struct sockaddr *)&daddr; + msg.msg_namelen = sizeof(daddr); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + mutex_lock(&rslock); + // ret = sock_sendmsg(rawsocket, &msg); + ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, 1); + mutex_unlock(&rslock); + + pr_info("%d\n", ret); + + return ret; +*/ +} +static unsigned int ykb_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + if (skb->head == NULL) return XT_CONTINUE; + struct iphdr *iph = ip_hdr(skb); + if (iph == NULL) { + pr_alert("iph is NULL!\n"); + goto accept; + } + __u32 iph_len = iph->ihl * 4; + + struct tcphdr *tcph = tcp_hdr(skb); + if (tcph == NULL) { + pr_alert("tcph is NULL!\n"); + goto accept; + } + __u32 tcph_len = tcp_hdrlen(skb); + + // Mallocs are bad! + __u8 *buf = kmalloc(skb->len, GFP_KERNEL); + if (buf == NULL) { + pr_alert("Cannot alloc enough buffer"); + goto accept; + } + if (skb_copy_bits(skb, 0, buf, skb->len) < 0) { + pr_alert("Unable copy bits\n"); + goto ac_fkb; + } + + const __u8 *payload = buf + iph_len + tcph_len; + __u32 plen = skb->len - iph_len - tcph_len; + + struct verdict vrd = analyze_tls_data(payload, plen); + + if (vrd.gvideo_hello) { + pr_alert("Googlevideo detected!\n"); + uint32_t f1len = skb->len; + uint32_t f2len = skb->len; + __u8 *frag1 = kmalloc(f1len, GFP_KERNEL); + __u8 *frag2 = kmalloc(f2len, GFP_KERNEL); + +#ifdef USE_TCP_SEGMENTATION + size_t ipd_offset = vrd.sni_offset; + size_t mid_offset = ipd_offset + vrd.sni_len / 2; + + + int ret; + if ((ret = tcp4_frag(buf, skb->len, + mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { + pr_err("tcp4_frag"); + } + + if ((ret = send_raw_socket(frag2, f2len) < 0) || + (ret = send_raw_socket(frag1, f1len) < 0)) { + pr_err("raw frags send"); + goto fallback; + } + +#else +// TODO: Implement ip fragmentation +/* + // TODO: Implement compute of tcp checksum + // GSO may turn kernel to not compute the tcp checksum. + // Also it will never be meaningless to ensure the + // checksum is right. + // nfq_tcp_compute_checksum_ipv4(tcph, ip_header); + + size_t ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset; + size_t mid_offset = ipd_offset + vrd.sni_len / 2; + mid_offset += 8 - mid_offset % 8; + + if ((errno = ip4_frag(raw_payload, raw_payload_len, + mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { + errno *= -1; + perror("ip4_frag"); + goto fallback; + } + + if ((send_raw_socket(frag2, f2len) < 0) || + (send_raw_socket(frag1, f1len) < 0)) { + perror("raw frags send"); + } +*/ +#endif + +fallback: + kfree(frag1); + kfree(frag2); + kfree(buf); + return XT_CONTINUE; + // return NF_DROP; + } +ac_fkb: + kfree(buf); +accept: + return XT_CONTINUE; +} + +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), + .proto = IPPROTO_TCP, + .family = NFPROTO_IPV4, + .checkentry = ykb_chk, + .me = THIS_MODULE, +}; + +static int __init ykb_init(void) { + int ret = 0; + + ret = open_raw_socket(); + if (ret < 0) goto err; + + ret = xt_register_target(&ykb_tg_reg); + if (ret < 0) goto close_rawsocket; + + pr_info("youtubeUnblock kernel module started.\n"); + return 0; +close_rawsocket: + close_raw_socket(); +err: + return ret; +} + +static void __exit ykb_destroy(void) { + xt_unregister_target(&ykb_tg_reg); + close_raw_socket(); + pr_info("youtubeUnblock kernel module destroyed.\n"); +} + +module_init(ykb_init); +module_exit(ykb_destroy); diff --git a/mangle.c b/mangle.c index e43047f..994ab0a 100644 --- a/mangle.c +++ b/mangle.c @@ -1,6 +1,8 @@ #include "mangle.h" #ifdef KERNEL_SPACE +#include + static __u16 nfq_checksum(__u32 sum, __u16 *buf, int size) { while (size > 1) { @@ -53,6 +55,8 @@ nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) tcph->check = 0; tcph->check = nfq_checksum_tcpudp_ipv4(iph, IPPROTO_TCP); } + +#define printf pr_info #else #include #include @@ -76,7 +80,7 @@ int ip4_payload_split(__u8 *pkt, __u32 buflen, __u32 hdr_len = hdr->ihl * 4; __u32 pktlen = ntohs(hdr->tot_len); - if (pktlen < buflen || hdr_len > pktlen) return -EINVAL; + if (buflen < pktlen || hdr_len > pktlen) return -EINVAL; if (iph) *iph = hdr; @@ -107,6 +111,7 @@ int tcp4_payload_split(__u8 *pkt, __u32 buflen, return -EINVAL; } + if ( hdr->protocol != IPPROTO_TCP || !(ntohs(hdr->frag_off) & IP_DF) || @@ -116,7 +121,7 @@ int tcp4_payload_split(__u8 *pkt, __u32 buflen, thdr = (struct tcphdr *)(tcph_pl); - thdr_len = (*tcph)->doff * 4; + thdr_len = thdr->doff * 4; if (thdr_len > tcph_plen) { return -EINVAL; @@ -205,7 +210,7 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, f2_hdr->tot_len = htons(f2_dlen); -#if defined(DEBUG) && !defined(KERNEL_SPACE) +#if defined(DEBUG) printf("Packet split in portion %u %u\n", f1_plen, f2_plen); #endif @@ -234,6 +239,7 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, return -EINVAL; } + if (plen <= payload_offset) { return -EINVAL; } @@ -269,13 +275,12 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset); -#if defined(DEBUG) && !defined(KERNEL_SPACE) +#if defined(DEBUG) printf("Packet split in portion %u %u\n", s1_plen, s2_plen); #endif nfq_tcp_compute_checksum_ipv4(s1_tcph, s1_hdr); nfq_tcp_compute_checksum_ipv4(s2_tcph, s2_hdr); - return 0; }