From 460c727deae294bda698d67939082be8d4131b69 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 30 Jul 2024 15:01:58 +0300 Subject: [PATCH 01/19] Support for multithreading. Cleanup code --- youtubeUnblock.c | 275 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 188 insertions(+), 87 deletions(-) diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 5c258fa..8cb88e3 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -20,6 +20,8 @@ #include #include +#include + #ifndef NOUSE_GSO #define USE_GSO #endif @@ -29,15 +31,28 @@ #endif #define RAWSOCKET_MARK 0xfc70 +#define MAX_THREADS 16 + +#ifndef THREADS_NUM +#define THREADS_NUM 1 +#endif + +#if THREADS_NUM > MAX_THREADS +#error "Too much threads" +#endif + +#ifndef __linux__ +#error "The package is linux only!" +#endif static struct { - uint32_t queue_num; - struct mnl_socket *nl; - uint32_t portid; + uint32_t queue_start_num; int rawsocket; - + pthread_mutex_t rawsocket_lock; + int threads; } config = { - .rawsocket = -2 + .rawsocket = -2, + .threads=THREADS_NUM }; static int parse_args(int argc, const char *argv[]) { @@ -52,7 +67,7 @@ static int parse_args(int argc, const char *argv[]) { uint32_t queue_num = strtoul(argv[1], &end, 10); if (errno != 0 || *end != '\0') goto errormsg_help; - config.queue_num = queue_num; + config.queue_start_num = queue_num; return 0; errormsg_help: @@ -64,13 +79,7 @@ errormsg_help: return -1; } -static int open_socket(void) { - if (config.nl != NULL) { - errno = EALREADY; - perror("socket is already opened"); - return -1; - } - +static int open_socket(struct mnl_socket **_nl) { struct mnl_socket *nl = NULL; nl = mnl_socket_open(NETLINK_NETFILTER); @@ -85,21 +94,21 @@ static int open_socket(void) { return -1; } - config.nl = nl; - config.portid = mnl_socket_get_portid(nl); + *_nl = nl; return 0; } -static int close_socket(void) { - if (config.nl == NULL) return 1; - if (mnl_socket_close(config.nl) < 0) { +static int close_socket(struct mnl_socket **_nl) { + struct mnl_socket *nl = *_nl; + if (nl == NULL) return 1; + if (mnl_socket_close(nl) < 0) { perror("mnl_socket_close"); return -1; } - config.nl = NULL; + *_nl = NULL; return 0; } @@ -121,17 +130,27 @@ static int open_raw_socket(void) { const int *val = &one; if (setsockopt(config.rawsocket, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) { - printf("setsockopt(IP_HDRINCL, 1) failed\n"); + fprintf(stderr, "setsockopt(IP_HDRINCL, 1) failed\n"); return -1; } int mark = RAWSOCKET_MARK; if (setsockopt(config.rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { - printf("setsockopt(SO_MARK, %d) failed\n", mark); + fprintf(stderr, "setsockopt(SO_MARK, %d) failed\n", mark); return -1; } + int mst = pthread_mutex_init(&config.rawsocket_lock, NULL); + if (mst) { + fprintf(stderr, "Mutex err: %d\n", mst); + close(config.rawsocket); + errno = mst; + + return -1; + } + + return config.rawsocket; } @@ -144,9 +163,12 @@ static int close_raw_socket(void) { if (close(config.rawsocket)) { perror("Unable to close raw socket"); + pthread_mutex_destroy(&config.rawsocket_lock); return -1; } + pthread_mutex_destroy(&config.rawsocket_lock); + config.rawsocket = -2; return 0; } @@ -383,9 +405,14 @@ static int send_raw_socket(struct pkt_buff *pktb) { } }; + pthread_mutex_lock(&config.rawsocket_lock); + int sent = sendto(config.rawsocket, pktb_data(pktb), pktb_len(pktb), 0, (struct sockaddr *)&daddr, sizeof(daddr)); + + pthread_mutex_unlock(&config.rawsocket_lock); + return sent; } @@ -398,22 +425,6 @@ struct packet_data { uint16_t payload_len; }; -/** - * Used to accept unsupported packets (GSOs) - */ -static int fallback_accept_packet(uint32_t id) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *verdnlh; - verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, config.queue_num); - nfq_nlmsg_verdict_put(verdnlh, id, NF_ACCEPT); - - if (mnl_socket_sendto(config.nl, verdnlh, verdnlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); - return MNL_CB_ERROR; - } - - return MNL_CB_OK; -} #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 @@ -564,9 +575,30 @@ nextMessage: return vrd; } +// Per-queue data. Passed to queue_cb. +struct queue_data { + struct mnl_socket **_nl; + int queue_num; +}; +/** + * Used to accept unsupported packets (GSOs) + */ +static int fallback_accept_packet(uint32_t id, struct queue_data qdata) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *verdnlh; + verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata.queue_num); + nfq_nlmsg_verdict_put(verdnlh, id, NF_ACCEPT); + + if (mnl_socket_sendto(*qdata._nl, verdnlh, verdnlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + return MNL_CB_ERROR; + } + + return MNL_CB_OK; +} + +static int process_packet(const struct packet_data packet, struct queue_data qdata) { - -static int process_packet(const struct packet_data packet) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *verdnlh; @@ -576,7 +608,7 @@ static int process_packet(const struct packet_data packet) { #endif if (packet.hw_proto != ETH_P_IP) { - return fallback_accept_packet(packet.id); + return fallback_accept_packet(packet.id, qdata); } const int family = AF_INET; @@ -609,7 +641,7 @@ static int process_packet(const struct packet_data packet) { struct verdict vrd = analyze_tls_data(data, data_len); - verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, config.queue_num); + verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata.queue_num); nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_ACCEPT); if (vrd.gvideo_hello) { @@ -690,7 +722,7 @@ static int process_packet(const struct packet_data packet) { } */ - if (mnl_socket_sendto(config.nl, verdnlh, verdnlh->nlmsg_len) < 0) { + if (mnl_socket_sendto(*qdata._nl, verdnlh, verdnlh->nlmsg_len) < 0) { perror("mnl_socket_send"); goto error; @@ -699,12 +731,15 @@ static int process_packet(const struct packet_data packet) { return MNL_CB_OK; fallback: - return fallback_accept_packet(packet.id); + return fallback_accept_packet(packet.id, qdata); error: return MNL_CB_ERROR; } static int queue_cb(const struct nlmsghdr *nlh, void *data) { + + struct queue_data *qdata = data; + struct nfqnl_msg_packet_hdr *ph = NULL; struct nlattr *attr[NFQA_MAX+1] = {0}; struct packet_data packet = {0}; @@ -730,45 +765,30 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { if (attr[NFQA_CAP_LEN] != NULL && ntohl(mnl_attr_get_u32(attr[NFQA_CAP_LEN])) != packet.payload_len) { fprintf(stderr, "The packet was truncated! Skip!\n"); - return fallback_accept_packet(packet.id); + return fallback_accept_packet(packet.id, *qdata); } if (attr[NFQA_MARK] != NULL) { // Skip packets sent by rawsocket to escape infinity loop. if (ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) == RAWSOCKET_MARK) { - return fallback_accept_packet(packet.id); + return fallback_accept_packet(packet.id, *qdata); } } - return process_packet(packet); + return process_packet(packet, *qdata); } -int main(int argc, const char *argv[]) -{ +int init_queue(int queue_num) { + struct mnl_socket *nl; - if (parse_args(argc, argv)) { - perror("Unable to parse args"); - exit(EXIT_FAILURE); - } - -#ifdef USE_TCP_SEGMENTATION - printf("Using TCP segmentation!\n"); -#else - printf("Using IP fragmentation!\n"); -#endif - - if (open_socket()) { + if (open_socket(&nl)) { perror("Unable to open socket"); - exit(EXIT_FAILURE); + return -1; } - if (open_raw_socket() < 0) { - perror("Unable to open raw socket"); - close_socket(); - exit(EXIT_FAILURE); - } + uint32_t portid = mnl_socket_get_portid(nl); struct nlmsghdr *nlh; char *buf; @@ -780,63 +800,144 @@ int main(int argc, const char *argv[]) } - nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, config.queue_num); + nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); - if (mnl_socket_sendto(config.nl, nlh, nlh->nlmsg_len) < 0) { + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { perror("mnl_socket_send"); - goto die_buf; + goto die; } - nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, config.queue_num); + nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); #ifdef USE_GSO - printf("GSO is enabled!\n"); - mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_GSO)); mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(NFQA_CFG_F_GSO)); #endif - if (mnl_socket_sendto(config.nl, nlh, nlh->nlmsg_len) < 0) { + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { perror("mnl_socket_send"); - goto die_buf; + goto die; } + /* ENOBUFS is signalled to userspace when packets were lost * on kernel side. In most cases, userspace isn't interested * in this information, so turn it off. */ int ret = 1; - mnl_socket_setsockopt(config.nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int)); + mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int)); + + struct queue_data qdata = { + ._nl = &nl, + .queue_num = queue_num + }; + + printf("Queue %d started!\n", qdata.queue_num); while (1) { - ret = mnl_socket_recvfrom(config.nl, buf, buf_size); + ret = mnl_socket_recvfrom(nl, buf, buf_size); if (ret == -1) { perror("mnl_socket_recvfrom"); - goto die_buf; + continue; } - ret = mnl_cb_run(buf, ret, 0, config.portid, queue_cb, NULL); + ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, &qdata); if (ret < 0) { perror("mnl_cb_run"); - // goto die_buf; } } - printf("%d\n", config.queue_num); - errno = 0; - free(buf); - close_socket(); + close_socket(&nl); return 0; -die_buf: +die: free(buf); die_sock: - close_raw_socket(); - close_socket(); - exit(EXIT_FAILURE); + close_socket(&nl); + return -1; +} + +// Per-queue config. Used to initialize a queue. Passed to wrapper +struct queue_conf { + uint16_t i; + int queue_num; +}; + +struct queue_res { + int status; +}; + +static struct queue_res threads_reses[MAX_THREADS]; + +void *init_queue_wrapper(void *qdconf) { + struct queue_conf *qconf = qdconf; + struct queue_res *thres = threads_reses + qconf->i; + + thres->status = init_queue(qconf->queue_num); + + fprintf(stderr, "Thread %d exited with status %d\n", qconf->i, thres->status); + + return thres; +} + +int main(int argc, const char *argv[]) { + if (parse_args(argc, argv)) { + perror("Unable to parse args"); + exit(EXIT_FAILURE); + } + +#ifdef USE_TCP_SEGMENTATION + printf("Using TCP segmentation!\n"); +#else + printf("Using IP fragmentation!\n"); +#endif + +#ifdef USE_GSO + printf("GSO is enabled!\n"); +#endif + + if (open_raw_socket() < 0) { + perror("Unable to open raw socket"); + exit(EXIT_FAILURE); + } + +#if THREADS_NUM == 1 + struct queue_conf tconf = { + .i = 0, + .queue_num = config.queue_start_num + }; + + struct queue_res *qres = init_queue_wrapper(&tconf); +#else + struct queue_conf thread_confs[MAX_THREADS]; + pthread_t threads[MAX_THREADS]; + for (int i = 0; i < config.threads; i++) { + struct queue_conf *tconf = thread_confs + i; + pthread_t *thr = threads + i; + + tconf->queue_num = config.queue_start_num + i; + tconf->i = i; + + pthread_create(thr, NULL, init_queue_wrapper, tconf); + } + + void *res; + for (int i = 0; i < config.threads; i++) { + pthread_join(threads[i], &res); + + struct queue_res *qres = res; + } +#endif + + if (close_raw_socket() < 0) { + perror("Unable to close raw socket"); + exit(EXIT_FAILURE); + } + + return qres->status; } From 74906587084af9ff3fa845843f22e624ec039d25 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 30 Jul 2024 23:10:00 +0300 Subject: [PATCH 02/19] Carry split and detection functions to separate file This needed to escape code duplication in further development of kernel module. Also this cleanups the code. --- Makefile | 2 +- mangle.c | 430 +++++++++++++++++++++++++++++++++++++++++++++++ mangle.h | 63 +++++++ youtubeUnblock.c | 428 ++++++---------------------------------------- 4 files changed, 546 insertions(+), 377 deletions(-) create mode 100644 mangle.c create mode 100644 mangle.h diff --git a/Makefile b/Makefile index d84f590..c621152 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ export CC LD CFLAGS LDFLAGS APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c +SRCS := youtubeUnblock.c mangle.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a diff --git a/mangle.c b/mangle.c new file mode 100644 index 0000000..60fc05e --- /dev/null +++ b/mangle.c @@ -0,0 +1,430 @@ +#include "mangle.h" +#include + +#ifdef KERNEL_SPACE +static __u16 nfq_checksum(__u32 sum, __u16 *buf, int size) +{ + while (size > 1) { + sum += *buf++; + size -= sizeof(__u16); + } + if (size) { +#if __BYTE_ORDER == __BIG_ENDIAN + sum += (uint16_t)*(uint8_t *)buf << 8; +#else + sum += (__u16)*(__u8 *)buf; +#endif + } + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + return (__u16)(~sum); +} + +static __u16 nfq_checksum_tcpudp_ipv4(struct iphdr *iph, __u16 protonum) +{ + __u32 sum = 0; + __u32 iph_len = iph->ihl*4; + __u32 len = ntohs(iph->tot_len) - iph_len; + __u8 *payload = (__u8 *)iph + iph_len; + + sum += (iph->saddr >> 16) & 0xFFFF; + sum += (iph->saddr) & 0xFFFF; + sum += (iph->daddr >> 16) & 0xFFFF; + sum += (iph->daddr) & 0xFFFF; + sum += htons(protonum); + sum += htons(len); + + return nfq_checksum(sum, (__u16 *)payload, len); +} + +static void nfq_ip_set_checksum(struct iphdr *iph) +{ + __u32 iph_len = iph->ihl * 4; + + iph->check = 0; + iph->check = nfq_checksum(0, (__u16 *)iph, iph_len); +} + +static void +nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) +{ + /* checksum field in header needs to be zero for calculation. */ + tcph->check = 0; + tcph->check = nfq_checksum_tcpudp_ipv4(iph, IPPROTO_TCP); +} +#else +#include +#include +#include + +typedef uint8_t __u8; +typedef uint32_t __u32; +typedef uint16_t __u16; +#endif + + +int ip4_payload_split(__u8 *pkt, __u32 buflen, + struct iphdr **iph, __u32 *iph_len, + __u8 **payload, __u32 *plen) { + if (pkt == NULL || buflen < sizeof(struct iphdr)) { + return -EINVAL; + } + + struct iphdr *hdr = (struct iphdr *)pkt; + if (hdr->version != IPVERSION) return -EINVAL; + + __u32 hdr_len = hdr->ihl * 4; + __u32 pktlen = ntohs(hdr->tot_len); + if (pktlen < buflen || hdr_len > pktlen) return -EINVAL; + + if (iph) + *iph = hdr; + if (iph_len) + *iph_len = hdr_len; + if (payload) + *payload = pkt + hdr_len; + if (plen) + *plen = pktlen - hdr_len; + + return 0; +} + +int tcp4_payload_split(__u8 *pkt, __u32 buflen, + struct iphdr **iph, __u32 *iph_len, + struct tcphdr **tcph, __u32 *tcph_len, + __u8 **payload, __u32 *plen) { + struct iphdr *hdr; + __u32 hdr_len; + struct tcphdr *thdr; + __u32 thdr_len; + + __u8 *tcph_pl; + __u32 tcph_plen; + + if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len, + &tcph_pl, &tcph_plen)){ + return -EINVAL; + } + + if ( + hdr->protocol != IPPROTO_TCP || + !(ntohs(hdr->frag_off) & IP_DF) || + tcph_plen < sizeof(struct tcphdr)) { + return -EINVAL; + } + + + thdr = (struct tcphdr *)(tcph_pl); + thdr_len = (*tcph)->doff * 4; + + if (thdr_len > tcph_plen) { + return -EINVAL; + } + + if (iph) *iph = hdr; + if (iph_len) *iph_len = hdr_len; + if (tcph) *tcph = thdr; + if (tcph_len) *tcph_len = thdr_len; + if (payload) *payload = tcph_pl + thdr_len; + if (plen) *plen = tcph_plen - thdr_len; + + return 0; +} + +// split packet to two ipv4 fragments. +int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, + __u8 *frag1, __u32 *f1len, + __u8 *frag2, __u32 *f2len) { + + struct iphdr *hdr; + const __u8 *payload; + __u32 plen; + __u32 hdr_len; + + if (ip4_payload_split( + (__u8 *)pkt, buflen, + &hdr, &hdr_len, (__u8 **)&payload, &plen)) { + return -EINVAL; + } + + if (plen <= payload_offset) { + return -EINVAL; + } + + if (payload_offset & ((1 << 3) - 1)) { +#ifdef KERNEL_SPACE +#else + errno = EINVAL; + perror("Payload offset MUST be a multiply of 8!"); +#endif + + return -EINVAL; + } + + __u32 f1_plen = payload_offset; + __u32 f1_dlen = f1_plen + hdr_len; + + __u32 f2_plen = plen - payload_offset; + __u32 f2_dlen = f2_plen + hdr_len; + + if (*f1len < f1_dlen || *f2len < f2_dlen) { + return -ENOMEM; + } + *f1len = f1_dlen; + *f2len = f2_dlen; + + memcpy(frag1, hdr, hdr_len); + memcpy(frag2, hdr, hdr_len); + + memcpy(frag1 + hdr_len, payload, f1_plen); + memcpy(frag2 + hdr_len, payload + payload_offset, f2_plen); + + struct iphdr *f1_hdr = (void *)frag1; + struct iphdr *f2_hdr = (void *)frag2; + + __u16 f1_frag_off = ntohs(f1_hdr->frag_off); + __u16 f2_frag_off = ntohs(f2_hdr->frag_off); + + f1_frag_off &= IP_OFFMASK; + f1_frag_off |= IP_MF; + + if ((f2_frag_off & ~IP_OFFMASK) == IP_MF) { + f2_frag_off &= IP_OFFMASK; + f2_frag_off |= IP_MF; + } else { + f2_frag_off &= IP_OFFMASK; + } + + f2_frag_off += (__u16)payload_offset / 8; + + f1_hdr->frag_off = htons(f1_frag_off); + f1_hdr->tot_len = htons(f1_dlen); + + f2_hdr->frag_off = htons(f2_frag_off); + f2_hdr->tot_len = htons(f2_dlen); + + +#if defined(DEBUG) && !defined(KERNEL_SPACE) + printf("Packet split in portion %u %u\n", f1_plen, f2_plen); +#endif + + nfq_ip_set_checksum(f1_hdr); + nfq_ip_set_checksum(f2_hdr); + + return 0; +} + +// split packet to two tcp-on-ipv4 segments. +int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, + __u8 *seg1, __u32 *s1len, + __u8 *seg2, __u32 *s2len) { + + struct iphdr *hdr; + __u32 hdr_len; + struct tcphdr *tcph; + __u32 tcph_len; + __u32 plen; + const __u8 *payload; + + if (tcp4_payload_split((__u8 *)pkt, buflen, + &hdr, &hdr_len, + &tcph, &tcph_len, + (__u8 **)&payload, &plen)) { + return -EINVAL; + } + + if (plen <= payload_offset) { + return -EINVAL; + } + + __u32 s1_plen = payload_offset; + __u32 s1_dlen = s1_plen + hdr_len + tcph_len; + + __u32 s2_plen = plen - payload_offset; + __u32 s2_dlen = s2_plen + hdr_len + tcph_len; + + if (*s1len < s1_dlen || *s2len < s2_dlen) return -ENOMEM; + + *s1len = s1_dlen; + *s2len = s2_dlen; + + memcpy(seg1, hdr, hdr_len); + memcpy(seg2, hdr, hdr_len); + + memcpy(seg1 + hdr_len, tcph, tcph_len); + memcpy(seg2 + hdr_len, tcph, tcph_len); + + memcpy(seg1 + hdr_len + tcph_len, payload, s1_plen); + memcpy(seg2 + hdr_len + tcph_len, payload + payload_offset, s2_plen); + + struct iphdr *s1_hdr = (void *)seg1; + struct iphdr *s2_hdr = (void *)seg2; + + struct tcphdr *s1_tcph = (void *)(seg1 + hdr_len); + struct tcphdr *s2_tcph = (void *)(seg2 + hdr_len); + + s1_hdr->tot_len = htons(s1_dlen); + s2_hdr->tot_len = htons(s2_dlen); + + s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset); + +#if defined(DEBUG) && !defined(KERNEL_SPACE) + 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; +} + +#define TLS_CONTENT_TYPE_HANDSHAKE 0x16 +#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 +#define TLS_EXTENSION_SNI 0x0000 +#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d + +const char googlevideo_ending[] = "googlevideo.com"; +const int googlevideo_len = 15; + +#define GOOGLEVIDEO_MARK 0xfc74 + + +typedef __u8 uint8_t; +typedef __u32 uint32_t; +typedef __u16 uint16_t; + +/** + * Processes tls payload of the tcp request. + * + * data Payload data of TCP. + * dlen Length of `data`. + */ +struct verdict analyze_tls_data( + const uint8_t *data, + uint32_t dlen) +{ + struct verdict vrd = {0}; + + size_t i = 0; + const uint8_t *data_end = data + dlen; + + while (i + 4 < dlen) { + const uint8_t *msgData = data + i; + + 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 (i + 5 + message_length > dlen) break; + + if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) + goto nextMessage; + + + const uint8_t *handshakeProto = msgData + 5; + + if (handshakeProto + 1 >= data_end) break; + + uint8_t handshakeType = *handshakeProto; + + if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) + goto nextMessage; + + 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; + uint8_t sessionIdLength = *msgPtr; + msgPtr++; + msgPtr += sessionIdLength; + + if (msgPtr + 2 >= data_end) break; + uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr); + msgPtr += 2; + msgPtr += ciphersLength; + + if (msgPtr + 1 >= data_end) break; + uint8_t compMethodsLen = *msgPtr; + msgPtr++; + msgPtr += compMethodsLen; + + 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; + const uint8_t *extensions_end = extensionsPtr + extensionsLen; + if (extensions_end > data_end) break; + + while (extensionsPtr < extensions_end) { + const uint8_t *extensionPtr = extensionsPtr; + if (extensionPtr + 4 >= extensions_end) break; + + uint16_t extensionType = + ntohs(*(uint16_t *)extensionPtr); + extensionPtr += 2; + + uint16_t extensionLen = + ntohs(*(uint16_t *)extensionPtr); + const uint8_t *extensionLen_ptr = extensionPtr; + extensionPtr += 2; + + + if (extensionPtr + extensionLen > extensions_end) + break; + + if (extensionType != TLS_EXTENSION_SNI) + goto nextExtension; + + const uint8_t *sni_ext_ptr = extensionPtr; + + 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++; + uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr); + sni_ext_ptr += 2; + + if (sni_ext_ptr + sni_len > sni_ext_end) break; + + char *sni_name = (char *)sni_ext_ptr; + // sni_len + + vrd.sni_offset = (uint8_t *)sni_name - data; + vrd.sni_len = sni_len; + + char *gv_startp = sni_name + sni_len - googlevideo_len; + if (sni_len >= googlevideo_len && + sni_len < 128 && + !strncmp(gv_startp, + googlevideo_ending, + googlevideo_len)) { + + vrd.gvideo_hello = 1; + } + +nextExtension: + extensionsPtr += 2 + 2 + extensionLen; + } +nextMessage: + i += 5 + message_length; + } + + return vrd; +} + diff --git a/mangle.h b/mangle.h new file mode 100644 index 0000000..f3c15e3 --- /dev/null +++ b/mangle.h @@ -0,0 +1,63 @@ +#ifndef YU_MANGLE_H +#define YU_MANGLE_H +#define RAWSOCKET_MARK 0xfc70 + +#ifdef KERNEL_SPACE +#include +typedef __u8 uint8_t; +typedef __u32 uint32_t; + +#include +#include +#include +#include +#include +#include +#include + +#include +#define ntohs(x) __constant_ntohs(x) +#define ntohl(x) __constant_ntohl(x) +#define htons(x) __constant_htons(x) +#define htonl(x) __constant_htonl(x) + +#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 */ +#else +#include +#include +#include +#include +#include +#include +#endif + +struct verdict { + int gvideo_hello; /* google video hello packet */ + int sni_offset; /* offset from start of tcp _payload_ */ + int sni_len; +}; + +struct verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); + +int ip4_frag(const uint8_t *pkt, uint32_t pktlen, + uint32_t payload_offset, + uint8_t *frag1, uint32_t *f1len, + uint8_t *frag2, uint32_t *f2len); + +int tcp4_frag(const uint8_t *pkt, uint32_t pktlen, + uint32_t payload_offset, + uint8_t *seg1, uint32_t *s1len, + uint8_t *seg2, uint32_t *s2len); + +int ip4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + uint8_t **payload, uint32_t *plen); + +int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + struct tcphdr **tcph, uint32_t *tcph_len, + uint8_t **payload, uint32_t *plen); +#endif /* YU_MANGLE_H */ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 8cb88e3..3a4730a 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -1,10 +1,15 @@ #define _GNU_SOURCE +#ifndef __linux__ +#error "The package is linux only!" +#endif + +#ifdef KERNEL_SPACE +#error "The build aims to the kernel, not userspace" +#endif + #include #include -#include #include -#include -#include #include #include @@ -21,6 +26,7 @@ #include #include +#include "mangle.h" #ifndef NOUSE_GSO #define USE_GSO @@ -41,9 +47,6 @@ #error "Too much threads" #endif -#ifndef __linux__ -#error "The package is linux only!" -#endif static struct { uint32_t queue_start_num; @@ -173,229 +176,63 @@ static int close_raw_socket(void) { return 0; } -// split packet to two ipv4 fragments. -static int ipv4_frag(struct pkt_buff *pktb, size_t payload_offset, - struct pkt_buff **frag1, struct pkt_buff **frag2) { - uint8_t buff1[MNL_SOCKET_BUFFER_SIZE]; - uint8_t buff2[MNL_SOCKET_BUFFER_SIZE]; - - struct iphdr *hdr = nfq_ip_get_hdr(pktb); - size_t hdr_len = hdr->ihl * 4; - - uint8_t *payload = pktb_data(pktb) + hdr_len; - size_t plen = pktb_len(pktb) - hdr_len; - - if (hdr == NULL || payload == NULL || plen <= payload_offset) { - errno = EINVAL; - return -1; - } - - if (payload_offset & ((1 << 3) - 1)) { - fprintf(stderr, "Payload offset MUST be a multiply of 8!\n"); - errno = EINVAL; - return -1; - } - - size_t f1_plen = payload_offset; - size_t f1_dlen = f1_plen + hdr_len; - - size_t f2_plen = plen - payload_offset; - size_t f2_dlen = f2_plen + hdr_len; - - memcpy(buff1, hdr, hdr_len); - memcpy(buff2, hdr, hdr_len); - - memcpy(buff1 + hdr_len, payload, f1_plen); - memcpy(buff2 + hdr_len, payload + payload_offset, f2_plen); - - struct iphdr *f1_hdr = (void *)buff1; - struct iphdr *f2_hdr = (void *)buff2; - - uint16_t f1_frag_off = ntohs(f1_hdr->frag_off); - uint16_t f2_frag_off = ntohs(f2_hdr->frag_off); - - f1_frag_off &= IP_OFFMASK; - f1_frag_off |= IP_MF; - - if ((f2_frag_off & ~IP_OFFMASK) == IP_MF) { - f2_frag_off &= IP_OFFMASK; - f2_frag_off |= IP_MF; - } else { - f2_frag_off &= IP_OFFMASK; - } - - f2_frag_off += (uint16_t)payload_offset / 8; - - f1_hdr->frag_off = htons(f1_frag_off); - f1_hdr->tot_len = htons(f1_dlen); - - f2_hdr->frag_off = htons(f2_frag_off); - f2_hdr->tot_len = htons(f2_dlen); - - -#ifdef DEBUG - printf("Packet split in portion %zu %zu\n", f1_dlen, f2_dlen); -#endif - - nfq_ip_set_checksum(f1_hdr); - nfq_ip_set_checksum(f2_hdr); - - *frag1 = pktb_alloc(AF_INET, buff1, f1_dlen, 0); - if (*frag1 == NULL) - return -1; - - *frag2 = pktb_alloc(AF_INET, buff2, f2_dlen, 0); - if (*frag2 == NULL) { - pktb_free(*frag1); - return -1; - } - - return 0; -} - -// split packet to two tcp-on-ipv4 segments. -static int tcp4_frag(struct pkt_buff *pktb, size_t payload_offset, - struct pkt_buff **seg1, struct pkt_buff **seg2) { - uint8_t buff1[MNL_SOCKET_BUFFER_SIZE]; - uint8_t buff2[MNL_SOCKET_BUFFER_SIZE]; - - struct iphdr *hdr = nfq_ip_get_hdr(pktb); - size_t hdr_len = hdr->ihl * 4; - if (hdr == NULL) {errno = EINVAL; return -1;} - if (hdr->protocol != IPPROTO_TCP || !(ntohs(hdr->frag_off) & IP_DF)) { - errno = EINVAL; - return -1; - } - - if (nfq_ip_set_transport_header(pktb, hdr)) - return -1; - - struct tcphdr *tcph = nfq_tcp_get_hdr(pktb); - size_t tcph_len = tcph->doff * 4; - if (tcph == NULL) { - errno = EINVAL; - return -1; - } - - uint8_t *payload = nfq_tcp_get_payload(tcph, pktb); - size_t plen = nfq_tcp_get_payload_len(tcph, pktb); - - if (hdr == NULL || payload == NULL || plen <= payload_offset) { - errno = EINVAL; - return -1; - } - - size_t s1_plen = payload_offset; - size_t s1_dlen = s1_plen + hdr_len + tcph_len; - - size_t s2_plen = plen - payload_offset; - size_t s2_dlen = s2_plen + hdr_len + tcph_len; - - memcpy(buff1, hdr, hdr_len); - memcpy(buff2, hdr, hdr_len); - - memcpy(buff1 + hdr_len, tcph, tcph_len); - memcpy(buff2 + hdr_len, tcph, tcph_len); - - memcpy(buff1 + hdr_len + tcph_len, payload, s1_plen); - memcpy(buff2 + hdr_len + tcph_len, payload + payload_offset, s2_plen); - - struct iphdr *s1_hdr = (void *)buff1; - struct iphdr *s2_hdr = (void *)buff2; - - struct tcphdr *s1_tcph = (void *)(buff1 + hdr_len); - struct tcphdr *s2_tcph = (void *)(buff2 + hdr_len); - - s1_hdr->tot_len = htons(s1_dlen); - s2_hdr->tot_len = htons(s2_dlen); - - // s2_hdr->id = htons(ntohs(s1_hdr->id) + 1); - s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset); - // printf("%zu %du %du\n", payload_offset, ntohs(s1_tcph->seq), ntohs(s2_tcph->seq)); - -#ifdef DEBUG - printf("Packet split in portion %zu %zu\n", s1_dlen, s2_dlen); -#endif - - nfq_tcp_compute_checksum_ipv4(s1_tcph, s1_hdr); - nfq_tcp_compute_checksum_ipv4(s2_tcph, s2_hdr); - - *seg1 = pktb_alloc(AF_INET, buff1, s1_dlen, 0); - if (*seg1 == NULL) - return -1; - - *seg2 = pktb_alloc(AF_INET, buff2, s2_dlen, 0); - if (*seg2 == NULL) { - pktb_free(*seg1); - return -1; - } - - - return 0; -} #define AVAILABLE_MTU 1384 -static int send_raw_socket(struct pkt_buff *pktb) { - if (pktb_len(pktb) > AVAILABLE_MTU) { +static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { + if (pktlen > AVAILABLE_MTU) { #ifdef DEBUG printf("Split packet!\n"); #endif - struct pkt_buff *buff1; - struct pkt_buff *buff2; + uint8_t buff1[MNL_SOCKET_BUFFER_SIZE]; + uint32_t buff1_size = MNL_SOCKET_BUFFER_SIZE; + uint8_t buff2[MNL_SOCKET_BUFFER_SIZE]; + uint32_t buff2_size = MNL_SOCKET_BUFFER_SIZE; #ifdef USE_TCP_SEGMENTATION - if (tcp4_frag(pktb, AVAILABLE_MTU-128, &buff1, &buff2) < 0) + if ((errno = tcp4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) return -1; #else - if (ipv4_frag(pktb, AVAILABLE_MTU-128, &buff1, &buff2) < 0) + if ((errno = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) return -1; #endif int sent = 0; - int status = send_raw_socket(buff1); + int status = send_raw_socket(buff1, buff1_size); if (status >= 0) sent += status; else { - pktb_free(buff1); - pktb_free(buff2); return status; } - pktb_free(buff1); - status = send_raw_socket(buff2); + status = send_raw_socket(buff2, buff2_size); if (status >= 0) sent += status; else { - pktb_free(buff2); return status; } - pktb_free(buff2); return sent; } - struct iphdr *iph = nfq_ip_get_hdr(pktb); - if (iph == NULL) - return -1; - if(nfq_ip_set_transport_header(pktb, iph)) + struct iphdr *iph; + + if ((errno = ip4_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + errno *= -1; return -1; + } + int sin_port = 0; - struct tcphdr *tcph = nfq_tcp_get_hdr(pktb); - struct udphdr *udph = nfq_udp_get_hdr(pktb); - - if (tcph != NULL) { + struct tcphdr *tcph; + if (tcp4_payload_split((uint8_t *)pkt, pktlen, NULL, NULL, &tcph, NULL, NULL, NULL) == 0) sin_port = tcph->dest; - errno = 0; - } else if (udph != NULL) { - sin_port = udph->dest; - } else { - return -1; - } struct sockaddr_in daddr = { .sin_family = AF_INET, @@ -408,7 +245,7 @@ static int send_raw_socket(struct pkt_buff *pktb) { pthread_mutex_lock(&config.rawsocket_lock); int sent = sendto(config.rawsocket, - pktb_data(pktb), pktb_len(pktb), 0, + pkt, pktlen, 0, (struct sockaddr *)&daddr, sizeof(daddr)); pthread_mutex_unlock(&config.rawsocket_lock); @@ -426,160 +263,13 @@ struct packet_data { }; -#define TLS_CONTENT_TYPE_HANDSHAKE 0x16 -#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 -#define TLS_EXTENSION_SNI 0x0000 -#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d - -const char googlevideo_ending[] = "googlevideo.com"; -const int googlevideo_len = 15; - -#define GOOGLEVIDEO_MARK 0xfc74 - -struct verdict { - int gvideo_hello; /* google video hello packet */ - int sni_offset; /* offset from start of tcp _payload_ */ - int sni_len; -}; - -/** - * Processes tls payload of the tcp request. - * - * data Payload data of TCP. - * dlen Length of `data`. - */ -static struct verdict analyze_tls_data( - const uint8_t *data, - uint32_t dlen) -{ - struct verdict vrd = {0}; - - size_t i = 0; - const uint8_t *data_end = data + dlen; - - while (i + 4 < dlen) { - const uint8_t *msgData = data + i; - - 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 (i + 5 + message_length > dlen) break; - - if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) - goto nextMessage; - - - const uint8_t *handshakeProto = msgData + 5; - - if (handshakeProto + 1 >= data_end) break; - - uint8_t handshakeType = *handshakeProto; - - if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) - goto nextMessage; - - 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; - uint8_t sessionIdLength = *msgPtr; - msgPtr++; - msgPtr += sessionIdLength; - - if (msgPtr + 2 >= data_end) break; - uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr); - msgPtr += 2; - msgPtr += ciphersLength; - - if (msgPtr + 1 >= data_end) break; - uint8_t compMethodsLen = *msgPtr; - msgPtr++; - msgPtr += compMethodsLen; - - 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; - const uint8_t *extensions_end = extensionsPtr + extensionsLen; - if (extensions_end > data_end) break; - - while (extensionsPtr < extensions_end) { - const uint8_t *extensionPtr = extensionsPtr; - if (extensionPtr + 4 >= extensions_end) break; - - uint16_t extensionType = - ntohs(*(uint16_t *)extensionPtr); - extensionPtr += 2; - - uint16_t extensionLen = - ntohs(*(uint16_t *)extensionPtr); - const uint8_t *extensionLen_ptr = extensionPtr; - extensionPtr += 2; - - - if (extensionPtr + extensionLen > extensions_end) - break; - - if (extensionType != TLS_EXTENSION_SNI) - goto nextExtension; - - const uint8_t *sni_ext_ptr = extensionPtr; - - 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++; - uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr); - sni_ext_ptr += 2; - - if (sni_ext_ptr + sni_len > sni_ext_end) break; - - char *sni_name = (char *)sni_ext_ptr; - // sni_len - - vrd.sni_offset = (uint8_t *)sni_name - data; - vrd.sni_len = sni_len; - - char *gv_startp = sni_name + sni_len - googlevideo_len; - if (sni_len >= googlevideo_len && - sni_len < 128 && - !strncmp(gv_startp, - googlevideo_ending, - googlevideo_len)) { - - vrd.gvideo_hello = 1; - } - -nextExtension: - extensionsPtr += 2 + 2 + extensionLen; - } -nextMessage: - i += 5 + message_length; - } - - return vrd; -} - // Per-queue data. Passed to queue_cb. struct queue_data { struct mnl_socket **_nl; int queue_num; }; + + /** * Used to accept unsupported packets (GSOs) */ @@ -655,8 +345,10 @@ static int process_packet(const struct packet_data packet, struct queue_data qda #endif } - struct pkt_buff *frag1; - struct pkt_buff *frag2; + uint8_t frag1[MNL_SOCKET_BUFFER_SIZE]; + uint8_t frag2[MNL_SOCKET_BUFFER_SIZE]; + uint32_t f1len = MNL_SOCKET_BUFFER_SIZE; + uint32_t f2len = MNL_SOCKET_BUFFER_SIZE; nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_DROP); @@ -664,26 +356,18 @@ static int process_packet(const struct packet_data packet, struct queue_data qda size_t ipd_offset = vrd.sni_offset; size_t mid_offset = ipd_offset + vrd.sni_len / 2; - struct pkt_buff *pktb = pktb_alloc( - family, - packet.payload, - packet.payload_len, - 0); - - if (tcp4_frag(pktb, mid_offset, &frag1, &frag2) < 0) { + if ((errno = tcp4_frag(raw_payload, raw_payload_len, + mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { + errno *= -1; perror("tcp4_frag"); - pktb_free(pktb); goto fallback; } - if ((send_raw_socket(frag2) == -1) || (send_raw_socket(frag1) == -1)) { + if ((send_raw_socket(frag2, f2len) < 0) || + (send_raw_socket(frag1, f1len) < 0)) { perror("raw frags send"); } - pktb_free(frag1); - pktb_free(frag2); - pktb_free(pktb); - #else // TODO: Implement compute of tcp checksum // GSO may turn kernel to not compute the tcp checksum. @@ -694,19 +378,19 @@ static int process_packet(const struct packet_data packet, struct queue_data qda 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 (ipv4_frag(pktb, mid_offset, &frag1, &frag2) < 0) { - perror("ipv4_frag"); + 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(frag1) == -1) || (send_raw_socket(frag2) == -1)) { + if ((send_raw_socket(frag2, f2len) < 0) || + (send_raw_socket(frag1, f1len) < 0)) { perror("raw frags send"); } - pktb_free(frag1); - pktb_free(frag2); #endif } @@ -780,6 +464,8 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { return process_packet(packet, *qdata); } +#define BUF_SIZE (0xffff + (MNL_SOCKET_BUFFER_SIZE / 2)) + int init_queue(int queue_num) { struct mnl_socket *nl; @@ -791,14 +477,7 @@ int init_queue(int queue_num) { uint32_t portid = mnl_socket_get_portid(nl); struct nlmsghdr *nlh; - char *buf; - size_t buf_size = 0xffff + (MNL_SOCKET_BUFFER_SIZE / 2); - buf = malloc(buf_size); - if (!buf) { - perror("Allocate recieve buffer"); - goto die_sock; - } - + char buf[BUF_SIZE]; nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); @@ -837,7 +516,7 @@ int init_queue(int queue_num) { printf("Queue %d started!\n", qdata.queue_num); while (1) { - ret = mnl_socket_recvfrom(nl, buf, buf_size); + ret = mnl_socket_recvfrom(nl, buf, BUF_SIZE); if (ret == -1) { perror("mnl_socket_recvfrom"); continue; @@ -850,13 +529,10 @@ int init_queue(int queue_num) { } - free(buf); close_socket(&nl); return 0; die: - free(buf); -die_sock: close_socket(&nl); return -1; } From 79d592a0cafaf067a36f15b6de1ce572d5c84449 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 31 Jul 2024 02:12:59 +0300 Subject: [PATCH 03/19] Makesystem for kernel module --- .editorconfig | 6 ++ .gitignore | 15 +++ Kbuild | 3 + Makefile | 91 ++--------------- deps/libnfnetlink/.gitignore | 1 + deps/libnfnetlink/doxygen.cfg | 180 ---------------------------------- kmake.mk | 26 +++++ mangle.c | 3 +- mangle.h | 8 +- uspace.mk | 89 +++++++++++++++++ youtubeKblock.c | 25 +++++ youtubeUnblock.c | 2 - 12 files changed, 180 insertions(+), 269 deletions(-) create mode 100644 .editorconfig create mode 100644 Kbuild delete mode 100644 deps/libnfnetlink/doxygen.cfg create mode 100644 kmake.mk create mode 100644 uspace.mk create mode 100644 youtubeKblock.c diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8126eab --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +indent_style = tab +indent_size = 8 +tab_width = 8 diff --git a/.gitignore b/.gitignore index cdb521e..e72e28f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,18 @@ build configure~ +# Kernel module files +.Module.* +.modules.* +Module.* +modules.* +.youtubeKblock.* +youtubeKblock.ko +youtubeKblock.mod* +youtubeKblock.o +.mangle.* +mangle.o +.youtubeKUnblock.* +youtubeKUnblock.ko +youtubeKUnblock.mod* +youtubeKUnblock.o diff --git a/Kbuild b/Kbuild new file mode 100644 index 0000000..2d99162 --- /dev/null +++ b/Kbuild @@ -0,0 +1,3 @@ +obj-m := youtubeKUnblock.o +youtubeKUnblock-objs := youtubeKblock.o mangle.o +ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE diff --git a/Makefile b/Makefile index c621152..4a0e1e8 100644 --- a/Makefile +++ b/Makefile @@ -1,85 +1,14 @@ -BUILD_DIR := $(CURDIR)/build -DEPSDIR := $(BUILD_DIR)/deps +USPACE_TARGETS := default all install uninstall dev run_dev +KMAKE_TARGETS := kmake kload kunload kreload -CC := gcc -LD := gcc -CFLAGS:=-Wall -Wpedantic -Wno-unused-variable -I$(DEPSDIR)/include -Os -LDFLAGS:=-L$(DEPSDIR)/lib -static +.PHONY: $(USPACE_TARGETS) $(KMAKE_TARGETS) clean +$(USPACE_TARGETS): + @$(MAKE) -f uspace.mk $@ -# PREFIX is environment variable, if not set default to /usr/local -ifeq ($(PREFIX),) - PREFIX := /usr/local -else - PREFIX := $(DESTDIR) -endif - -export CC LD CFLAGS LDFLAGS - -APP:=$(BUILD_DIR)/youtubeUnblock - -SRCS := youtubeUnblock.c mangle.c -OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) - -LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a -LIBMNL := $(DEPSDIR)/lib/libmnl.a -LIBNETFILTER_QUEUE := $(DEPSDIR)/lib/libnetfilter_queue.a - - -.PHONY: default all dev dev_attrs prepare_dirs -default: all - -run_dev: dev - bash -c "sudo $(APP) 537" - -dev: dev_attrs all - -dev_attrs: - $(eval CFLAGS := $(CFLAGS) -DDEBUG -ggdb -g3) - -all: prepare_dirs $(APP) - -prepare_dirs: - mkdir -p $(BUILD_DIR) - mkdir -p $(DEPSDIR) - -$(LIBNFNETLINK): - cd deps/libnfnetlink && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) - $(MAKE) -C deps/libnfnetlink - $(MAKE) install -C deps/libnfnetlink - -$(LIBMNL): - cd deps/libmnl && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) - $(MAKE) -C deps/libmnl - $(MAKE) install -C deps/libmnl - -$(LIBNETFILTER_QUEUE): $(LIBNFNETLINK) $(LIBMNL) - cd deps/libnetfilter_queue && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) - $(MAKE) -C deps/libnetfilter_queue - $(MAKE) install -C deps/libnetfilter_queue - -$(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL) - @echo 'LD $(APP)' - @$(LD) $(OBJS) -o $(APP) -L$(DEPSDIR)/lib -lmnl -lnetfilter_queue - -$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) - @echo 'CC $@' - @$(CC) -c $(CFLAGS) $^ -o $@ - -install: all - install -d $(PREFIX)/bin/ - install -m 755 $(APP) $(PREFIX)/bin/ - install -d $(PREFIX)/lib/systemd/system/ - @cp youtubeUnblock.service $(BUILD_DIR) - @sed -i 's/$$(PREFIX)/$(subst /,\/,$(PREFIX))/g' $(BUILD_DIR)/youtubeUnblock.service - install -m 644 $(BUILD_DIR)/youtubeUnblock.service $(PREFIX)/lib/systemd/system/ - -uninstall: - rm $(PREFIX)/bin/youtubeUnblock - rm $(PREFIX)/lib/systemd/system/youtubeUnblock.service - systemctl disable youtubeUnblock.service +$(KMAKE_TARGETS): + @$(MAKE) -f kmake.mk $@ clean: - rm -rf $(BUILD_DIR) - $(MAKE) distclean -C deps/libnetfilter_queue || true - $(MAKE) distclean -C deps/libmnl || true - $(MAKE) distclean -C deps/libnfnetlink || true + -@$(MAKE) -f kmake.mk kclean + @$(MAKE) -f uspace.mk clean + diff --git a/deps/libnfnetlink/.gitignore b/deps/libnfnetlink/.gitignore index 6bf816e..e485b47 100644 --- a/deps/libnfnetlink/.gitignore +++ b/deps/libnfnetlink/.gitignore @@ -15,3 +15,4 @@ Makefile.in /stamp-h1 /*.pc +doxygen.cfg diff --git a/deps/libnfnetlink/doxygen.cfg b/deps/libnfnetlink/doxygen.cfg deleted file mode 100644 index cedc44d..0000000 --- a/deps/libnfnetlink/doxygen.cfg +++ /dev/null @@ -1,180 +0,0 @@ -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = libnfnetlink -PROJECT_NUMBER = 1.0.2 -OUTPUT_DIRECTORY = doxygen -CREATE_SUBDIRS = NO -OUTPUT_LANGUAGE = English -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -ABBREVIATE_BRIEF = -ALWAYS_DETAILED_SEC = NO -INLINE_INHERITED_MEMB = NO -FULL_PATH_NAMES = NO -STRIP_FROM_PATH = -STRIP_FROM_INC_PATH = -SHORT_NAMES = NO -JAVADOC_AUTOBRIEF = NO -QT_AUTOBRIEF = NO -MULTILINE_CPP_IS_BRIEF = NO -INHERIT_DOCS = YES -SEPARATE_MEMBER_PAGES = NO -TAB_SIZE = 8 -ALIASES = -OPTIMIZE_OUTPUT_FOR_C = YES -OPTIMIZE_OUTPUT_JAVA = NO -OPTIMIZE_FOR_FORTRAN = NO -OPTIMIZE_OUTPUT_VHDL = NO -BUILTIN_STL_SUPPORT = NO -CPP_CLI_SUPPORT = NO -SIP_SUPPORT = NO -DISTRIBUTE_GROUP_DOC = NO -SUBGROUPING = YES -TYPEDEF_HIDES_STRUCT = NO -EXTRACT_ALL = NO -EXTRACT_PRIVATE = NO -EXTRACT_STATIC = NO -EXTRACT_LOCAL_CLASSES = YES -EXTRACT_LOCAL_METHODS = NO -EXTRACT_ANON_NSPACES = NO -HIDE_UNDOC_MEMBERS = NO -HIDE_UNDOC_CLASSES = NO -HIDE_FRIEND_COMPOUNDS = NO -HIDE_IN_BODY_DOCS = NO -INTERNAL_DOCS = NO -CASE_SENSE_NAMES = YES -HIDE_SCOPE_NAMES = NO -SHOW_INCLUDE_FILES = YES -INLINE_INFO = YES -SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = NO -SORT_GROUP_NAMES = NO -SORT_BY_SCOPE_NAME = NO -GENERATE_TODOLIST = YES -GENERATE_TESTLIST = YES -GENERATE_BUGLIST = YES -GENERATE_DEPRECATEDLIST= YES -ENABLED_SECTIONS = -MAX_INITIALIZER_LINES = 30 -SHOW_USED_FILES = YES -FILE_VERSION_FILTER = -QUIET = NO -WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES -WARN_IF_DOC_ERROR = YES -WARN_NO_PARAMDOC = NO -WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = -INPUT = . -INPUT_ENCODING = UTF-8 -FILE_PATTERNS = *.c libnfnetlink.h -RECURSIVE = YES -EXCLUDE = -EXCLUDE_SYMLINKS = NO -EXCLUDE_PATTERNS = */.git/* .*.d -EXCLUDE_SYMBOLS = EXPORT_SYMBOL -EXAMPLE_PATH = -EXAMPLE_PATTERNS = -EXAMPLE_RECURSIVE = NO -IMAGE_PATH = -INPUT_FILTER = -FILTER_PATTERNS = -FILTER_SOURCE_FILES = NO -SOURCE_BROWSER = YES -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES -REFERENCED_BY_RELATION = NO -REFERENCES_RELATION = NO -REFERENCES_LINK_SOURCE = YES -USE_HTAGS = NO -VERBATIM_HEADERS = YES -ALPHABETICAL_INDEX = NO -COLS_IN_ALPHA_INDEX = 5 -IGNORE_PREFIX = -GENERATE_HTML = YES -HTML_OUTPUT = html -HTML_FILE_EXTENSION = .html -HTML_HEADER = -HTML_STYLESHEET = -GENERATE_HTMLHELP = NO -GENERATE_DOCSET = NO -DOCSET_FEEDNAME = "Doxygen generated docs" -DOCSET_BUNDLE_ID = org.doxygen.Project -HTML_DYNAMIC_SECTIONS = NO -CHM_FILE = -HHC_LOCATION = -GENERATE_CHI = NO -BINARY_TOC = NO -TOC_EXPAND = NO -DISABLE_INDEX = NO -ENUM_VALUES_PER_LINE = 4 -GENERATE_TREEVIEW = NO -TREEVIEW_WIDTH = 250 -GENERATE_LATEX = NO -LATEX_OUTPUT = latex -LATEX_CMD_NAME = latex -MAKEINDEX_CMD_NAME = makeindex -COMPACT_LATEX = NO -PAPER_TYPE = a4wide -EXTRA_PACKAGES = -LATEX_HEADER = -PDF_HYPERLINKS = YES -USE_PDFLATEX = YES -LATEX_BATCHMODE = NO -LATEX_HIDE_INDICES = NO -GENERATE_RTF = NO -RTF_OUTPUT = rtf -COMPACT_RTF = NO -RTF_HYPERLINKS = NO -RTF_STYLESHEET_FILE = -RTF_EXTENSIONS_FILE = -GENERATE_MAN = YES -MAN_OUTPUT = man -MAN_EXTENSION = .3 -MAN_LINKS = NO -GENERATE_XML = NO -XML_OUTPUT = xml -XML_PROGRAMLISTING = YES -GENERATE_AUTOGEN_DEF = NO -GENERATE_PERLMOD = NO -PERLMOD_LATEX = NO -PERLMOD_PRETTY = YES -PERLMOD_MAKEVAR_PREFIX = -ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = NO -EXPAND_ONLY_PREDEF = NO -SEARCH_INCLUDES = YES -INCLUDE_PATH = -INCLUDE_FILE_PATTERNS = -PREDEFINED = -EXPAND_AS_DEFINED = -SKIP_FUNCTION_MACROS = YES -TAGFILES = -GENERATE_TAGFILE = -ALLEXTERNALS = NO -EXTERNAL_GROUPS = YES -PERL_PATH = /usr/bin/perl -CLASS_DIAGRAMS = YES -MSCGEN_PATH = -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = YES -CLASS_GRAPH = YES -COLLABORATION_GRAPH = YES -GROUP_GRAPHS = YES -UML_LOOK = NO -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = NO -CALLER_GRAPH = NO -GRAPHICAL_HIERARCHY = YES -DIRECTORY_GRAPH = YES -DOT_IMAGE_FORMAT = png -DOT_PATH = -DOTFILE_DIRS = -DOT_GRAPH_MAX_NODES = 50 -MAX_DOT_GRAPH_DEPTH = 0 -DOT_TRANSPARENT = YES -DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = YES -DOT_CLEANUP = YES -SEARCHENGINE = NO diff --git a/kmake.mk b/kmake.mk new file mode 100644 index 0000000..9a01090 --- /dev/null +++ b/kmake.mk @@ -0,0 +1,26 @@ +#Kernel module makes here +PWD := $(CURDIR) + +override CC := $(OCC) +override LD := $(OLD) +override CFLAGS := +override LDFLAGS := + +export CC LD CFLAGS LDFLAGS + +.PHONY: kmake kload kunload kreload kclean +kmake: + $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +kload: + insmod youtubeKUnblock.ko + +kunload: + -rmmod youtubeKUnblock + +kreload: kunload kload + +kclean: + $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + + diff --git a/mangle.c b/mangle.c index 60fc05e..e43047f 100644 --- a/mangle.c +++ b/mangle.c @@ -1,5 +1,4 @@ #include "mangle.h" -#include #ifdef KERNEL_SPACE static __u16 nfq_checksum(__u32 sum, __u16 *buf, int size) @@ -9,7 +8,7 @@ static __u16 nfq_checksum(__u32 sum, __u16 *buf, int size) size -= sizeof(__u16); } if (size) { -#if __BYTE_ORDER == __BIG_ENDIAN +#ifdef __LITTLE_ENDIAN sum += (uint16_t)*(uint8_t *)buf << 8; #else sum += (__u16)*(__u8 *)buf; diff --git a/mangle.h b/mangle.h index f3c15e3..9c100b3 100644 --- a/mangle.h +++ b/mangle.h @@ -16,10 +16,10 @@ typedef __u32 uint32_t; #include #include -#define ntohs(x) __constant_ntohs(x) -#define ntohl(x) __constant_ntohl(x) -#define htons(x) __constant_htons(x) -#define htonl(x) __constant_htonl(x) +// #define ntohs(x) __constant_ntohs(x) +// #define ntohl(x) __constant_ntohl(x) +// #define htons(x) __constant_htons(x) +// #define htonl(x) __constant_htonl(x) #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ diff --git a/uspace.mk b/uspace.mk new file mode 100644 index 0000000..d2c2147 --- /dev/null +++ b/uspace.mk @@ -0,0 +1,89 @@ +#Userspace app makes here +BUILD_DIR := $(CURDIR)/build +DEPSDIR := $(BUILD_DIR)/deps + +CC:=gcc +LD:=gcc +CFLAGS:=-Wall -Wpedantic -Wno-unused-variable -I$(DEPSDIR)/include -Os +LDFLAGS:=-L$(DEPSDIR)/lib -static + +export CC LD CFLAGS LDFLAGS + +# PREFIX is environment variable, if not set default to /usr/local +ifeq ($(PREFIX),) + PREFIX := /usr/local +else + PREFIX := $(DESTDIR) +endif + + +APP:=$(BUILD_DIR)/youtubeUnblock + +SRCS := youtubeUnblock.c mangle.c +OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) + +LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a +LIBMNL := $(DEPSDIR)/lib/libmnl.a +LIBNETFILTER_QUEUE := $(DEPSDIR)/lib/libnetfilter_queue.a + + +.PHONY: default all dev dev_attrs prepare_dirs +default: all + +run_dev: dev + bash -c "sudo $(APP) 537" + +dev: dev_attrs all + +dev_attrs: + $(eval CFLAGS := $(CFLAGS) -DDEBUG -ggdb -g3) + +all: prepare_dirs $(APP) + +prepare_dirs: + mkdir -p $(BUILD_DIR) + mkdir -p $(DEPSDIR) + +$(LIBNFNETLINK): + cd deps/libnfnetlink && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) + $(MAKE) -C deps/libnfnetlink + $(MAKE) install -C deps/libnfnetlink + +$(LIBMNL): + cd deps/libmnl && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) + $(MAKE) -C deps/libmnl + $(MAKE) install -C deps/libmnl + +$(LIBNETFILTER_QUEUE): $(LIBNFNETLINK) $(LIBMNL) + cd deps/libnetfilter_queue && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) + $(MAKE) -C deps/libnetfilter_queue + $(MAKE) install -C deps/libnetfilter_queue + +$(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL) + @echo 'LD $(APP)' + @$(LD) $(OBJS) -o $(APP) -L$(DEPSDIR)/lib -lmnl -lnetfilter_queue + +$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) + @echo 'CC $@' + @$(CC) -c $(CFLAGS) $^ -o $@ + +install: all + install -d $(PREFIX)/bin/ + install -m 755 $(APP) $(PREFIX)/bin/ + install -d $(PREFIX)/lib/systemd/system/ + @cp youtubeUnblock.service $(BUILD_DIR) + @sed -i 's/$$(PREFIX)/$(subst /,\/,$(PREFIX))/g' $(BUILD_DIR)/youtubeUnblock.service + install -m 644 $(BUILD_DIR)/youtubeUnblock.service $(PREFIX)/lib/systemd/system/ + +uninstall: + rm $(PREFIX)/bin/youtubeUnblock + rm $(PREFIX)/lib/systemd/system/youtubeUnblock.service + systemctl disable youtubeUnblock.service + +clean: + rm -rf $(BUILD_DIR) + $(MAKE) distclean -C deps/libnetfilter_queue || true + $(MAKE) distclean -C deps/libmnl || true + $(MAKE) distclean -C deps/libnfnetlink || true + + diff --git a/youtubeKblock.c b/youtubeKblock.c new file mode 100644 index 0000000..3f622f6 --- /dev/null +++ b/youtubeKblock.c @@ -0,0 +1,25 @@ +// Kernel module for youtubeUnblock. +#include +#include +#include +#include "mangle.h" + + + + +static int __init ykb_init(void) { + pr_info("youtubeUnblock kernel module started.\n"); + return 0; +} + +static void __exit ykb_destroy(void) { + pr_info("youtubeUnblock kernel module destroyed.\n"); +} + +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Vadim Vetrov "); +MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); + +module_init(ykb_init); +module_exit(ykb_destroy); diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 3a4730a..7dea2dd 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -316,13 +316,11 @@ static int process_packet(const struct packet_data packet, struct queue_data qda const struct tcphdr *tcph = (const void *)(raw_payload + iph_len); if ((const uint8_t *)tcph + 20 > raw_payload + raw_payload_len) { - printf("LZ\n"); goto fallback; } int tcph_len = tcph->doff * 4; if ((const uint8_t *)tcph + tcph_len > raw_payload + raw_payload_len) { - printf("LZ\n"); goto fallback; } From b14abda6000710f67abf3b94b054bb51cf531b4b Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 31 Jul 2024 22:13:06 +0300 Subject: [PATCH 04/19] Add skeleton for module and userspace iptables management Special thanks to https://github.com/drivenets/iptables_extensions repository with detailed explanations of iptables userspace interaction with the kernel module. --- .gitignore | 22 +++------ Kbuild | 4 +- Makefile | 2 +- ipt_YTUNBLOCK.c | 118 +++++++++++++++++++++++++++++++++++++++++++++ ipt_YTUNBLOCK.h | 6 +++ kmake.mk | 38 ++++++++++----- libipt_YTUNBLOCK.c | 26 ++++++++++ mangle.h | 5 +- youtubeKblock.c | 25 ---------- 9 files changed, 189 insertions(+), 57 deletions(-) create mode 100644 ipt_YTUNBLOCK.c create mode 100644 ipt_YTUNBLOCK.h create mode 100644 libipt_YTUNBLOCK.c delete mode 100644 youtubeKblock.c diff --git a/.gitignore b/.gitignore index e72e28f..f865d01 100644 --- a/.gitignore +++ b/.gitignore @@ -5,17 +5,11 @@ build configure~ # Kernel module files -.Module.* -.modules.* -Module.* -modules.* -.youtubeKblock.* -youtubeKblock.ko -youtubeKblock.mod* -youtubeKblock.o -.mangle.* -mangle.o -.youtubeKUnblock.* -youtubeKUnblock.ko -youtubeKUnblock.mod* -youtubeKUnblock.o +*.o +.* +*.mod.* +*.mod +modules.order +Module.symvers +*.so +*.ko diff --git a/Kbuild b/Kbuild index 2d99162..35c36db 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ -obj-m := youtubeKUnblock.o -youtubeKUnblock-objs := youtubeKblock.o mangle.o +obj-m := ipt_YTUNBLOCK.o +youtubeKUnblock-objs := ipt_YTUNBLOCK.o mangle.o ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE diff --git a/Makefile b/Makefile index 4a0e1e8..0e05091 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ USPACE_TARGETS := default all install uninstall dev run_dev -KMAKE_TARGETS := kmake kload kunload kreload +KMAKE_TARGETS := kmake kload kunload kreload xmod xtclean .PHONY: $(USPACE_TARGETS) $(KMAKE_TARGETS) clean $(USPACE_TARGETS): diff --git a/ipt_YTUNBLOCK.c b/ipt_YTUNBLOCK.c new file mode 100644 index 0000000..de23ece --- /dev/null +++ b/ipt_YTUNBLOCK.c @@ -0,0 +1,118 @@ +// 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/ipt_YTUNBLOCK.h b/ipt_YTUNBLOCK.h new file mode 100644 index 0000000..9394f39 --- /dev/null +++ b/ipt_YTUNBLOCK.h @@ -0,0 +1,6 @@ +#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 9a01090..00e3f0f 100644 --- a/kmake.mk +++ b/kmake.mk @@ -1,26 +1,42 @@ #Kernel module makes here PWD := $(CURDIR) -override CC := $(OCC) -override LD := $(OLD) -override CFLAGS := -override LDFLAGS := +CC := gcc +CCLD := $(CC) +LD := ld +CFLAGS := +LDFLAGS := -export CC LD CFLAGS LDFLAGS +IPT_CFLAGS := -Wall -Wpedantic -O2 -.PHONY: kmake kload kunload kreload kclean -kmake: +.PHONY: kmake kload kunload kreload kclean kmclean xclean +kmake: kmod xmod + +kmod: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules +xmod: libipt_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 $@ $<; + kload: - insmod youtubeKUnblock.ko + insmod ipt_YTUNBLOCK.ko + cp ./libipt_YTUNBLOCK.so /usr/lib/xtables/ kunload: - -rmmod youtubeKUnblock + -rmmod ipt_YTUNBLOCK + -/bin/rm /usr/lib/xtables/libipt_YTUNBLOCK.so kreload: kunload kload -kclean: - $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean +kclean: xtclean 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/libipt_YTUNBLOCK.c b/libipt_YTUNBLOCK.c new file mode 100644 index 0000000..5b7b099 --- /dev/null +++ b/libipt_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 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); +} diff --git a/mangle.h b/mangle.h index 9c100b3..22b9df2 100644 --- a/mangle.h +++ b/mangle.h @@ -16,11 +16,8 @@ typedef __u32 uint32_t; #include #include -// #define ntohs(x) __constant_ntohs(x) -// #define ntohl(x) __constant_ntohl(x) -// #define htons(x) __constant_htons(x) -// #define htonl(x) __constant_htonl(x) +/* from */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ diff --git a/youtubeKblock.c b/youtubeKblock.c deleted file mode 100644 index 3f622f6..0000000 --- a/youtubeKblock.c +++ /dev/null @@ -1,25 +0,0 @@ -// Kernel module for youtubeUnblock. -#include -#include -#include -#include "mangle.h" - - - - -static int __init ykb_init(void) { - pr_info("youtubeUnblock kernel module started.\n"); - return 0; -} - -static void __exit ykb_destroy(void) { - pr_info("youtubeUnblock kernel module destroyed.\n"); -} - -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); -MODULE_AUTHOR("Vadim Vetrov "); -MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); - -module_init(ykb_init); -module_exit(ykb_destroy); From 457911abfa46aa7272f62c77838d9d02164141c0 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 1 Aug 2024 02:40:58 +0300 Subject: [PATCH 05/19] Implement googlevideo packet handling Mangling itself is in TODO state. --- Kbuild | 2 +- ipt_YTUNBLOCK.c | 118 ------------------ iptk_YTUNBLOCK.c | 314 +++++++++++++++++++++++++++++++++++++++++++++++ mangle.c | 15 ++- 4 files changed, 325 insertions(+), 124 deletions(-) delete mode 100644 ipt_YTUNBLOCK.c create mode 100644 iptk_YTUNBLOCK.c 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; } From 66191b10f7a3b95affe90b4eaeaf20f82a5a6182 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 4 Aug 2024 01:29:57 +0300 Subject: [PATCH 06/19] Implement packet send --- iptk_YTUNBLOCK.c | 30 ++++-------------------------- mangle.c | 8 +++----- mangle.h | 4 ++++ 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/iptk_YTUNBLOCK.c b/iptk_YTUNBLOCK.c index a952768..a906fdf 100644 --- a/iptk_YTUNBLOCK.c +++ b/iptk_YTUNBLOCK.c @@ -47,13 +47,6 @@ static int open_raw_socket(void) { 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); @@ -71,7 +64,6 @@ 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 @@ -123,12 +115,6 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { 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; @@ -137,15 +123,9 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { 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_port = 0, .sin_addr = { .s_addr = iph->daddr } @@ -158,20 +138,18 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t 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_name = &daddr; + msg.msg_namelen = sizeof(struct sockaddr_in); msg.msg_control = NULL; msg.msg_controllen = 0; mutex_lock(&rslock); - // ret = sock_sendmsg(rawsocket, &msg); - ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, 1); + ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); 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) { diff --git a/mangle.c b/mangle.c index 994ab0a..2c8872d 100644 --- a/mangle.c +++ b/mangle.c @@ -57,6 +57,7 @@ nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) } #define printf pr_info +#define perror pr_err #else #include #include @@ -158,11 +159,10 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, } if (payload_offset & ((1 << 3) - 1)) { -#ifdef KERNEL_SPACE -#else +#ifdef USER_SPACE errno = EINVAL; - perror("Payload offset MUST be a multiply of 8!"); #endif + perror("Payload offset MUST be a multiply of 8!"); return -EINVAL; } @@ -292,8 +292,6 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, const char googlevideo_ending[] = "googlevideo.com"; const int googlevideo_len = 15; -#define GOOGLEVIDEO_MARK 0xfc74 - typedef __u8 uint8_t; typedef __u32 uint32_t; diff --git a/mangle.h b/mangle.h index 22b9df2..0155561 100644 --- a/mangle.h +++ b/mangle.h @@ -2,6 +2,8 @@ #define YU_MANGLE_H #define RAWSOCKET_MARK 0xfc70 +#define DEBUG + #ifdef KERNEL_SPACE #include typedef __u8 uint8_t; @@ -23,6 +25,8 @@ typedef __u32 uint32_t; #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ #else +#define USER_SPACE + #include #include #include From 62a5627c505fa6e70fcfb913b576d1e309464b46 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 4 Aug 2024 12:57:16 +0300 Subject: [PATCH 07/19] Update userspace interactions --- mangle.c | 42 ++++++++++++++++++++---------- uspace.mk | 4 +-- youtubeUnblock.c | 67 ++++++++++++++++++++++++++++-------------------- 3 files changed, 70 insertions(+), 43 deletions(-) diff --git a/mangle.c b/mangle.c index acaf289..1b9ec37 100644 --- a/mangle.c +++ b/mangle.c @@ -59,6 +59,7 @@ nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) #define printf pr_info #define perror pr_err +#define lgerror(msg, ret) (pr_err(msg ": %d\n", ret)) #else #include #include @@ -67,6 +68,8 @@ nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) typedef uint8_t __u8; typedef uint32_t __u32; typedef uint16_t __u16; + +#define lgerror(msg, ret) ({errno = -ret; perror(msg);}) #endif @@ -116,7 +119,6 @@ int tcp4_payload_split(__u8 *pkt, __u32 buflen, if ( hdr->protocol != IPPROTO_TCP || - !(ntohs(hdr->frag_off) & IP_DF) || tcph_plen < sizeof(struct tcphdr)) { return -EINVAL; } @@ -148,10 +150,15 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, const __u8 *payload; __u32 plen; __u32 hdr_len; + int ret; - if (ip4_payload_split( + if (!frag1 || !f1len || !frag2 || !f2len) + return -EINVAL; + + if ((ret = ip4_payload_split( (__u8 *)pkt, buflen, - &hdr, &hdr_len, (__u8 **)&payload, &plen)) { + &hdr, &hdr_len, (__u8 **)&payload, &plen)) < 0) { + lgerror("ipv4_frag: TCP Header extract error", ret); return -EINVAL; } @@ -160,10 +167,7 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, } if (payload_offset & ((1 << 3) - 1)) { -#ifdef USER_SPACE - errno = EINVAL; -#endif - perror("Payload offset MUST be a multiply of 8!"); + lgerror("ipv4_frag: Payload offset MUST be a multiply of 8!", -EINVAL); return -EINVAL; } @@ -232,11 +236,23 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, __u32 tcph_len; __u32 plen; const __u8 *payload; + int ret; - if (tcp4_payload_split((__u8 *)pkt, buflen, + if (!seg1 || !s1len || !seg2 || !s2len) + return -EINVAL; + + if ((ret = tcp4_payload_split((__u8 *)pkt, buflen, &hdr, &hdr_len, &tcph, &tcph_len, - (__u8 **)&payload, &plen)) { + (__u8 **)&payload, &plen)) < 0) { + lgerror("tcp4_frag: tcp4_payload_split", ret); + + return -EINVAL; + } + + + if (!(ntohs(hdr->frag_off) & IP_DF)) { + lgerror("tcp4_frag: ip fragmentation is set", -EINVAL); return -EINVAL; } @@ -251,7 +267,8 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, __u32 s2_plen = plen - payload_offset; __u32 s2_dlen = s2_plen + hdr_len + tcph_len; - if (*s1len < s1_dlen || *s2len < s2_dlen) return -ENOMEM; + if (*s1len < s1_dlen || *s2len < s2_dlen) + return -ENOMEM; *s1len = s1_dlen; *s2len = s2_dlen; @@ -434,7 +451,7 @@ nextMessage: int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, uint8_t *buf, uint32_t *buflen) { - if (iph == NULL || tcph == NULL || buf == NULL || buflen == NULL) + if (!iph || !tcph || !buf || !buflen) return -EINVAL; int ip_len = iph->ihl * 4; @@ -444,6 +461,7 @@ int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, if (*buflen < dlen) return -ENOMEM; + *buflen = dlen; memcpy(buf, iph, ip_len); memcpy(buf + ip_len, fake_sni, data_len); @@ -466,7 +484,5 @@ int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, nfq_ip_set_checksum(niph); nfq_tcp_compute_checksum_ipv4(ntcph, niph); - *buflen = dlen; - return 0; } diff --git a/uspace.mk b/uspace.mk index e8d091f..4a5071f 100644 --- a/uspace.mk +++ b/uspace.mk @@ -68,9 +68,9 @@ $(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL) @echo 'CCLD $(APP)' @$(CCLD) $(OBJS) -o $(APP) -L$(DEPSDIR)/lib -lmnl -lnetfilter_queue -$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) +$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) config.h @echo 'CC $@' - @$(CC) -c $(CFLAGS) $^ -o $@ + @$(CC) -c $(CFLAGS) $< -o $@ install: all install -d $(PREFIX)/bin/ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index fe7fbcb..918fcd3 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -21,22 +21,22 @@ #include #include -#include #include -#include +#include #include +#include -#include "mangle.h" #include "config.h" +#include "mangle.h" static struct { - uint32_t queue_start_num; - int rawsocket; - pthread_mutex_t rawsocket_lock; - int threads; + uint32_t queue_start_num; + int rawsocket; + pthread_mutex_t rawsocket_lock; + int threads; } config = { - .rawsocket = -2, - .threads=THREADS_NUM + .rawsocket = -2, + .threads = THREADS_NUM }; static int parse_args(int argc, const char *argv[]) { @@ -160,6 +160,8 @@ static int close_raw_socket(void) { #define AVAILABLE_MTU 1384 static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { + int ret; + if (pktlen > AVAILABLE_MTU) { #ifdef DEBUG printf("Split packet!\n"); @@ -171,19 +173,25 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { uint32_t buff2_size = MNL_SOCKET_BUFFER_SIZE; #if defined(USE_TCP_SEGMENTATION) || defined(RAWSOCK_TCP_FSTRAT) - if ((errno = tcp4_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) - return -1; + if ((ret = tcp4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { + + errno = -ret; + return ret; + } #elif defined(USE_IP_FRAGMENTATION) || defined(RAWSOCK_IP_FSTRAT) - if ((errno = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) - return -1; + if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { + + errno = -ret; + return ret; + } #else errno = EINVAL; printf("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 -1; + return -EINVAL; #endif int sent = 0; @@ -206,22 +214,16 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { struct iphdr *iph; - if ((errno = ip4_payload_split( + if ((ret = ip4_payload_split( (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { - errno *= -1; - return -1; + errno = -ret; + 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, + /* Always 0 for raw socket */ + .sin_port = 0, .sin_addr = { .s_addr = iph->daddr } @@ -235,6 +237,9 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { pthread_mutex_unlock(&config.rawsocket_lock); + /* The function will return -errno on error as well as errno value set itself */ + if (sent < 0) sent = -errno; + return sent; } @@ -287,7 +292,11 @@ void *delay_packet_send(void *data) { uint32_t pktlen = dpdt->pktlen; usleep(dpdt->timer * 1000); - send_raw_socket(pkt, pktlen); + int ret = send_raw_socket(pkt, pktlen); + if (ret < 0) { + errno = -ret; + perror("send delayed raw packet"); + } free(pkt); free(dpdt); @@ -425,6 +434,8 @@ static int process_packet(const struct packet_data packet, struct queue_data qda #ifdef SEG2_DELAY struct dps_t *dpdt = malloc(sizeof(struct dps_t)); dpdt->pkt = malloc(f1len); + memcpy(dpdt->pkt, frag1, f1len); + dpdt->pktlen = f1len; dpdt->timer = SEG2_DELAY; pthread_t thr; pthread_create(&thr, NULL, delay_packet_send, dpdt); From 8bb2bb28d2b801cd451633158a0e29e127b471e0 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 4 Aug 2024 15:55:07 +0300 Subject: [PATCH 08/19] Module for kernel works now on local machine. SEG2_DELAY is not implemented yet. --- Kbuild | 2 +- config.h | 4 +- iptk_YTUNBLOCK.c | 186 ++++++++++++++++++++++++++++----------------- mangle.c | 94 +++++++++-------------- mangle.h | 3 + raw_replacements.h | 4 +- youtubeUnblock.c | 14 ++-- 7 files changed, 168 insertions(+), 139 deletions(-) diff --git a/Kbuild b/Kbuild index 1a41539..4c0ef45 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := ipt_YTUNBLOCK.o ipt_YTUNBLOCK-objs := iptk_YTUNBLOCK.o mangle.o -ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE +ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE -DDEBUG diff --git a/config.h b/config.h index 49ead05..8def60d 100644 --- a/config.h +++ b/config.h @@ -23,6 +23,7 @@ #elif FRAGMENTATION_STRATEGY == FRAG_STRAT_IP #define USE_IP_FRAGMENTATION #elif FRAGMENTATION_STRATEGY == FRAG_STRAT_NONE + #define USE_NO_FRAGMENTATION #endif #define RAWSOCKET_MARK (1 << 15) @@ -35,6 +36,7 @@ #define FAKE_SNI #endif -#ifndef SILENT +#if !defined(SILENT) && !defined(KERNEL_SPACE) #define DEBUG #endif + diff --git a/iptk_YTUNBLOCK.c b/iptk_YTUNBLOCK.c index a906fdf..0f11f24 100644 --- a/iptk_YTUNBLOCK.c +++ b/iptk_YTUNBLOCK.c @@ -1,4 +1,3 @@ -// // 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 @@ -9,15 +8,16 @@ #include #include #include "ipt_YTUNBLOCK.h" + #include "mangle.h" +#include "config.h" +#include "raw_replacements.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); @@ -63,16 +63,13 @@ static void close_raw_socket(void) { static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { if (pktlen > AVAILABLE_MTU) { - pr_alert("The packet is too big!"); -#ifdef DEBUG - printf("Split packet!\n"); -#endif + pr_warn("The packet is too big and may cause issues!"); __u32 buff1_size = pktlen; __u32 buff2_size = pktlen; - __u8 *buff1 = kmalloc(pktlen, GFP_KERNEL); + __u8 *buff1 = kmalloc(pktlen, GFP_ATOMIC); if (buff1 == NULL) return -1; - __u8 *buff2 = kmalloc(pktlen, GFP_KERNEL); + __u8 *buff2 = kmalloc(pktlen, GFP_ATOMIC); if (buff2 == NULL) { kfree(buff1); return -1; @@ -80,15 +77,19 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { int ret; -#ifdef USE_TCP_SEGMENTATION +#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; -#else +#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; @@ -147,100 +148,147 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); 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->mark & RAWSOCKET_MARK) == RAWSOCKET_MARK) + return XT_CONTINUE; + 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); + + // TODO: Mallocs are bad! + uint32_t buflen = skb->len; + __u8 *buf = kmalloc(skb->len, GFP_ATOMIC); if (buf == NULL) { - pr_alert("Cannot alloc enough buffer"); + pr_err("Cannot alloc enough buffer space"); goto accept; } if (skb_copy_bits(skb, 0, buf, skb->len) < 0) { - pr_alert("Unable copy bits\n"); + 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; - const __u8 *payload = buf + iph_len + tcph_len; - __u32 plen = skb->len - iph_len - tcph_len; + 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) { - pr_alert("Googlevideo detected!\n"); + int ret; + pr_info("Googlevideo detected\n"); + + ip4_set_checksum(iph); + tcp4_set_checksum(tcph, iph); + uint32_t f1len = skb->len; uint32_t f2len = skb->len; - __u8 *frag1 = kmalloc(f1len, GFP_KERNEL); - __u8 *frag2 = kmalloc(f2len, GFP_KERNEL); + __u8 *frag1 = kmalloc(f1len, GFP_ATOMIC); + if (!frag1) { + pr_err("Cannot alloc enough gv frag1 buffer space"); + goto ac_fkb; + } + __u8 *frag2 = kmalloc(f2len, GFP_ATOMIC); + if (!frag2) { + pr_err("Cannot alloc enough gv frag1 buffer space"); + kfree(frag1); + goto ac_fkb; + } -#ifdef USE_TCP_SEGMENTATION + +#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; + } + + ret = gen_fake_sni(iph, tcph, fksn_buf, &fksn_len); + if (ret < 0) { + pr_err("Cannot alloc enough gksn buffer space"); + goto fksn_fb; + } +#endif + +#if defined(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"); + pr_err("tcp4_frag: %d", ret); + goto fksn_fb; } - - 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; +#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 ((errno = ip4_frag(raw_payload, raw_payload_len, + if ((ret = ip4_frag(buf, skb->len, mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { - errno *= -1; - perror("ip4_frag"); - goto fallback; + pr_err("ip4_frag: %d", ret); + goto fksn_fb; } - - if ((send_raw_socket(frag2, f2len) < 0) || - (send_raw_socket(frag1, f1len) < 0)) { - perror("raw frags send"); - } -*/ #endif +#ifdef FAKE_SNI + ret = send_raw_socket(fksn_buf, fksn_len); + if (ret < 0) { + pr_err("fksn_send: %d", ret); + goto fksn_fb; + } +#endif + +#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 + + ret = send_raw_socket(frag2, f2len); + if (ret < 0) { + pr_err("raw frag2 send: %d", ret); + goto fksn_fb; + } + +#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 + +fksn_fb: +#ifdef FAKE_SNI + kfree(fksn_buf); +#endif fallback: +#ifndef SEG2_DELAY kfree(frag1); +#endif kfree(frag2); kfree(buf); - return XT_CONTINUE; - // return NF_DROP; + kfree_skb(skb); + return NF_STOLEN; } ac_fkb: kfree(buf); diff --git a/mangle.c b/mangle.c index 1b9ec37..feb6c9a 100644 --- a/mangle.c +++ b/mangle.c @@ -1,61 +1,10 @@ #include "mangle.h" #include "raw_replacements.h" +#include "config.h" #ifdef KERNEL_SPACE #include - -static __u16 nfq_checksum(__u32 sum, __u16 *buf, int size) -{ - while (size > 1) { - sum += *buf++; - size -= sizeof(__u16); - } - if (size) { -#ifdef __LITTLE_ENDIAN - sum += (uint16_t)*(uint8_t *)buf << 8; -#else - sum += (__u16)*(__u8 *)buf; -#endif - } - - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); - - return (__u16)(~sum); -} - -static __u16 nfq_checksum_tcpudp_ipv4(struct iphdr *iph, __u16 protonum) -{ - __u32 sum = 0; - __u32 iph_len = iph->ihl*4; - __u32 len = ntohs(iph->tot_len) - iph_len; - __u8 *payload = (__u8 *)iph + iph_len; - - sum += (iph->saddr >> 16) & 0xFFFF; - sum += (iph->saddr) & 0xFFFF; - sum += (iph->daddr >> 16) & 0xFFFF; - sum += (iph->daddr) & 0xFFFF; - sum += htons(protonum); - sum += htons(len); - - return nfq_checksum(sum, (__u16 *)payload, len); -} - -static void nfq_ip_set_checksum(struct iphdr *iph) -{ - __u32 iph_len = iph->ihl * 4; - - iph->check = 0; - iph->check = nfq_checksum(0, (__u16 *)iph, iph_len); -} - -static void -nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) -{ - /* checksum field in header needs to be zero for calculation. */ - tcph->check = 0; - tcph->check = nfq_checksum_tcpudp_ipv4(iph, IPPROTO_TCP); -} +#include #define printf pr_info #define perror pr_err @@ -69,9 +18,33 @@ typedef uint8_t __u8; typedef uint32_t __u32; typedef uint16_t __u16; -#define lgerror(msg, ret) ({errno = -ret; perror(msg);}) +#define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);}) #endif +void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph) +{ +#ifdef KERNEL_SPACE + uint32_t tcp_packet_len = ntohs(iph->tot_len) - (iph->ihl << 2); + tcph->check = 0; + tcph->check = csum_tcpudp_magic( + iph->saddr, iph->daddr, tcp_packet_len, + IPPROTO_TCP, + csum_partial(tcph, tcp_packet_len, 0)); +#else + nfq_tcp_compute_checksum_ipv4(tcph, iph); +#endif +} + +void ip4_set_checksum(struct iphdr *iph) +{ +#ifdef KERNEL_SPACE + iph->check = 0; + iph->check = ip_fast_csum(iph, iph->ihl); +#else + nfq_ip_set_checksum(iph); +#endif +} + int ip4_payload_split(__u8 *pkt, __u32 buflen, struct iphdr **iph, __u32 *iph_len, @@ -219,8 +192,8 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, printf("Packet split in portion %u %u\n", f1_plen, f2_plen); #endif - nfq_ip_set_checksum(f1_hdr); - nfq_ip_set_checksum(f2_hdr); + ip4_set_checksum(f1_hdr); + ip4_set_checksum(f2_hdr); return 0; } @@ -297,8 +270,9 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, 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); + tcp4_set_checksum(s1_tcph, s1_hdr); + tcp4_set_checksum(s2_tcph, s2_hdr); + return 0; } @@ -481,8 +455,8 @@ int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, ntcph->th_sport = tcph->th_sport; #endif - nfq_ip_set_checksum(niph); - nfq_tcp_compute_checksum_ipv4(ntcph, niph); + ip4_set_checksum(niph); + tcp4_set_checksum(ntcph, niph); return 0; } diff --git a/mangle.h b/mangle.h index d8b0c87..e7bdfa9 100644 --- a/mangle.h +++ b/mangle.h @@ -59,6 +59,9 @@ int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, struct tcphdr **tcph, uint32_t *tcph_len, uint8_t **payload, uint32_t *plen); +void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph); +void ip4_set_checksum(struct iphdr *iph); + int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, uint8_t *buf, uint32_t *buflen); #endif /* YU_MANGLE_H */ diff --git a/raw_replacements.h b/raw_replacements.h index c6f86b2..0da19d9 100644 --- a/raw_replacements.h +++ b/raw_replacements.h @@ -1,6 +1,8 @@ #ifndef RAW_REPLACEMENTS_H #define RAW_REPLACEMENTS_H -const char fake_sni[] = "\276(\001\273\366\234|\335\213\222\023\330\200\030\001\366\350e\000\000\001\001\b\n}\355\267Hm/\217\347\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356"; +#define FAKE_SNI_MAXLEN 1500 + +#define fake_sni "\276(\001\273\366\234|\335\213\222\023\330\200\030\001\366\350e\000\000\001\001\b\n}\355\267Hm/\217\347\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356" #endif /*RAW_REPLACEMENTS_H*/ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 918fcd3..3c8f804 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -110,13 +110,13 @@ static int open_raw_socket(void) { return -1; } - int one = 1; - const int *val = &one; - if (setsockopt(config.rawsocket, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) - { - fprintf(stderr, "setsockopt(IP_HDRINCL, 1) failed\n"); - return -1; - } + // int one = 1; + // const int *val = &one; + // if (setsockopt(config.rawsocket, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) + // { + // fprintf(stderr, "setsockopt(IP_HDRINCL, 1) failed\n"); + // return -1; + // } int mark = RAWSOCKET_MARK; if (setsockopt(config.rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) From 1e36a55d9d4d8941712505770c7c972f6503832f Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 5 Aug 2024 00:07:34 +0300 Subject: [PATCH 09/19] Userspace compilation for openwrt \#define _GNU_SOURCE specified explicitly --- README.md | 4 ++-- iptk_YTUNBLOCK.c | 1 + mangle.c | 2 ++ mangle.h | 1 - 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dc1c077..fcd99f6 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,11 @@ EPERM may occur in a lot of places but generally here are two: mnl_cb_run and wh * Step 3, ultimate solution. Use mark (don't confuse with connmark). The youtubeUnblock uses mark internally to avoid infinity packet loops (when the packet is sent by youtubeUnblock but on next step handled by itself). Currently it uses mark (1 << 15) = 32768. You should put iptables/nftables that ultimately accepts such marks at the very start of the filter OUTPUT chain: `iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT` or `nft insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept`. ## OpenWRT case -The package is also compatible with routers. The router should be running by free opensource linux-based system such as [OpenWRT](https://openwrt.org/). You should cross-compile it under your host machine. Be ready for compilation errors and a lot of googling about it. It is not such a trivial process! You can get crosscompilation toolsuite compatible with your router from OpenWRT repositories. For example, I have ramips/mt76x8 based router so for me the toolsuite is on https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-toolchain-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64.tar.xz`. You can find out more about your router model on it's openwrt page. When you download the toolsuite, untar it somewhere. Now we are ready for compilation. My cross gcc asked me to create a staging dir for it and pass it as an environment variable. Also you should notice toolsuite packages and replace my make command with yours. ```STAGING_DIR=temp make CC=/usr/bin/mipsel-openwrt-linux-gcc LD=/usr/bin/mipsel-openwrt-linux-ld AR=/usr/bin/mipsel-openwrt-linux-ar OBJDUMP=/usr/bin/mipsel-openwrt-linux-objdump NM=/usr/bin/mipsel-openwrt-linux-nm STRIP=/usr/bin/mipsel-openwrt-linux-strip CROSS_COMPILE_PLATFORM=mipsel-buildroot-linux-gnu```. Take a look at `CROSS_COMPILE_PLATFORM` It is required by autotools but I think it is not necessary. Anyways I put `mipsel-buildroot-linux-gnu` in here. For your model may be an [automake cross-compile manual](https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html) will be helpful. When compilation is done, the binary file will be in build directory. Copy it to your router. Note that an 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`-ed and run. +The package is also compatible with routers. The router should be running by free opensource linux-based system such as [OpenWRT](https://openwrt.org/). You should cross-compile it under your host machine. Be ready for compilation errors and a lot of googling about it. It is not such a trivial process! You can get crosscompilation toolsuite compatible with your router from OpenWRT repositories. For example, I have ramips/mt76x8 based router so for me the toolsuite is on https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-toolchain-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64.tar.xz`. You can find out more about your router model on it's openwrt page. When you download the toolsuite, untar it somewhere. Now we are ready for compilation. My cross gcc asked me to create a staging dir for it and pass it as an environment variable. Also you should notice toolsuite packages and replace my make command with yours. ```STAGING_DIR=temp make CC=/usr/bin/mipsel-openwrt-linux-gcc LD=/usr/bin/mipsel-openwrt-linux-ld AR=/usr/bin/mipsel-openwrt-linux-ar OBJDUMP=/usr/bin/mipsel-openwrt-linux-objdump NM=/usr/bin/mipsel-openwrt-linux-nm STRIP=/usr/bin/mipsel-openwrt-linux-strip CROSS_COMPILE_PLATFORM=mipsel-buildroot-linux-gnu```. Take a look at `CROSS_COMPILE_PLATFORM` It is required by autotools but I think it is not necessary. Anyways I put `mipsel-buildroot-linux-gnu` in here. For your model may be an [automake cross-compile manual](https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html) will be helpful. You can learn more about cross compilation for OpenWRT on [their wiki page](https://openwrt.org/docs/guide-developer/toolchain/crosscompile). When compilation is done, the binary file will be in build directory. Copy it to your router. Note that an 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`-ed and run. Now let's talk about a router configuration. I installed a normal iptables user-space app: `xtables-legacy iptables-zz-legacy` and kernel/iptables nfqueue extensions: `iptables-mod-nfqueue kmod-ipt-nfqueue` and add `iptables -t mangle -A FORWARD -p tcp -m tcp --dport 443 -j NFQUEUE --queue-num 537 --queue-bypass` rule. -If you prefer nftables, this should work: `nft add rule inet fw4 mangle_forward tcp dport 443 counter queue num 537 bypass`. Note that kmod-nft-queue should be installed. +If you prefer nftables, this should work: `nft add rule inet fw4 mangle_forward tcp dport 443 counter queue num 537 bypass`. Note that `kmod-nft-queue` should be installed. Also you can copy `owrt/537-youtubeUnblock.nft` to `/usr/share/nftables.d/ruleset-post/537-youtubeUnblock.nft` and run `/etc/init.d/firewall reload`. This will reload the nftables ruleset and automatically link 537-youtubeUnblock.nft with it. diff --git a/iptk_YTUNBLOCK.c b/iptk_YTUNBLOCK.c index 0f11f24..0f9910a 100644 --- a/iptk_YTUNBLOCK.c +++ b/iptk_YTUNBLOCK.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE // 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 diff --git a/mangle.c b/mangle.c index feb6c9a..7144551 100644 --- a/mangle.c +++ b/mangle.c @@ -1,3 +1,5 @@ +#define _GNU_SOURCE + #include "mangle.h" #include "raw_replacements.h" #include "config.h" diff --git a/mangle.h b/mangle.h index e7bdfa9..103b3ea 100644 --- a/mangle.h +++ b/mangle.h @@ -23,7 +23,6 @@ typedef __u32 uint32_t; #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ #else #define USER_SPACE - #include #include #include From 3f1e8d3f013706a24f40e351fa07922afcd26ba1 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 5 Aug 2024 23:13:35 +0300 Subject: [PATCH 10/19] Suuport for fake sni elimination strategy - TTL --- config.h | 10 ++++++++++ mangle.c | 7 +++++++ raw_replacements.h | 3 ++- uspace.mk | 4 ++-- youtubeUnblock.c | 8 ++++++++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/config.h b/config.h index 8def60d..9e75e2b 100644 --- a/config.h +++ b/config.h @@ -36,6 +36,16 @@ #define FAKE_SNI #endif +#define FAKE_SNI_TTL 8 + +// Will invalidate fake client hello by out-of-ack_seq out-of-seq request +#define FKSN_STRAT_ACK_SEQ 0 +// Will assume that GGC server is located further than FAKE_SNI_TTL +// Thus, Fake Client Hello will be eliminated automatically. +#define FKSN_STRAT_TTL 1 + +#define FAKE_SNI_STRATEGY FKSN_STRAT_ACK_SEQ + #if !defined(SILENT) && !defined(KERNEL_SPACE) #define DEBUG #endif diff --git a/mangle.c b/mangle.c index 7144551..8ccf705 100644 --- a/mangle.c +++ b/mangle.c @@ -455,6 +455,13 @@ int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, #else ntcph->th_dport = tcph->th_dport; ntcph->th_sport = tcph->th_sport; + +#if FAKE_SNI_STRATEGY == FKSN_STRAT_TTL + ntcph->ack = tcph->ack; + ntcph->ack_seq = tcph->ack_seq; + niph->ttl = FAKE_SNI_TTL; +#endif + #endif ip4_set_checksum(niph); diff --git a/raw_replacements.h b/raw_replacements.h index 0da19d9..3a40ae7 100644 --- a/raw_replacements.h +++ b/raw_replacements.h @@ -3,6 +3,7 @@ #define FAKE_SNI_MAXLEN 1500 -#define fake_sni "\276(\001\273\366\234|\335\213\222\023\330\200\030\001\366\350e\000\000\001\001\b\n}\355\267Hm/\217\347\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356" +static const char fake_sni[] = "\276(\001\273\366\234|\335\213\222\023\330\200\030\001\366\350e\000\000\001\001\b\n}\355\267Hm/\217\347\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356"; + #endif /*RAW_REPLACEMENTS_H*/ diff --git a/uspace.mk b/uspace.mk index 643c28f..8950133 100644 --- a/uspace.mk +++ b/uspace.mk @@ -24,7 +24,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c +SRCS := youtubeUnblock.c mangle.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a @@ -68,7 +68,7 @@ $(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL) @echo 'CCLD $(APP)' $(CCLD) $(OBJS) -o $(APP) $(LDFLAGS) -lmnl -lnetfilter_queue -$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) +$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) config.h @echo 'CC $@' $(CC) -c $(CFLAGS) $(LDFLAGS) $< -o $@ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 3c8f804..62f5736 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -639,6 +639,14 @@ int main(int argc, const char *argv[]) { #ifdef FAKE_SNI printf("Fake SNI will be sent before each googlevideo request\n"); + + printf("Fake SNI will use strategy: " +#if FAKE_SNI_STRATEGY == FKSN_STRAT_TTL + "TTL" +#else + "Ack-Seq" +#endif + "\n"); #endif #ifdef USE_GSO From 458780d8a265deee44075bf7999311efaed23ba2 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 5 Aug 2024 23:37:45 +0300 Subject: [PATCH 11/19] Add definitions guards --- config.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config.h b/config.h index 9e75e2b..ce0948e 100644 --- a/config.h +++ b/config.h @@ -16,7 +16,9 @@ #define FRAG_STRAT_IP 1 #define FRAG_STRAT_NONE 2 +#ifndef FRAGMENTATION_STRATEGY #define FRAGMENTATION_STRATEGY FRAG_STRAT_TCP +#endif #if FRAGMENTATION_STRATEGY == FRAG_STRAT_TCP #define USE_TCP_SEGMENTATION @@ -44,7 +46,9 @@ // Thus, Fake Client Hello will be eliminated automatically. #define FKSN_STRAT_TTL 1 +#ifndef FAKE_SNI_STRATEGY #define FAKE_SNI_STRATEGY FKSN_STRAT_ACK_SEQ +#endif #if !defined(SILENT) && !defined(KERNEL_SPACE) #define DEBUG From 0860d5c5767db8ad4958b8dafd7533818f4e76c6 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 6 Aug 2024 23:54:52 +0300 Subject: [PATCH 12/19] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e7a40c7..7cfd580 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Available flags: - -DUSE_SEG2_DELAY This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet. You can tune the amount of time in `#define SEG2_DELAY 100` where 100 stands for milliseconds. - -DNO_FAKE_SNI This flag disables -DFAKE_SNI which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. This flag may be related to some Operation not permitted error messages, so befor open an issue refer to FAQ for EPERMS. - -DNOUSE_GSO This flag disables fix for Google Chrome fat ClientHello. The GSO is well tested now, so this flag probably won't fix anything. +- -DFAKE_SNI_STRATEGY It is possible that your ISP has conntrack/some custom stuff enabled. So if it is enabled as well as firewall rule to drop tcp packets considered invalid (I'm not sure who and how does that, may be TSPU does so only for google domains). So fake Client Hello won't work normally. The solution is to invalidate client hello by TTL. Use -DFAKE_SNI_STRATEGY=FKSN_STRAT_TTL to enable this and -FKSN_STRAT_TTL=8 to change the TTL (based on hops number between you and the google server). If you are on Chromium you may have to disable kyber (the feature that makes the TLS ClientHello very fat). I've got the problem with it on router, so to escape possibly errors it is better to just disable it: in chrome://flags search for kyber and switch it to disabled state. @@ -56,6 +57,8 @@ If you have bad performance you can queue to youtubeUnblock only first, say, 20 The same behavior is also possible in iptables: `iptables -t mangle -A FORWARD -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass`. (The package iptables-mod-conntrack-extra is required for connbytes on OpenWRT) +You can use `--queue-balance` with multiple instances of youtubeUnblock. This behavior is supported via multithreading. Just pass -DTHREADS_NUM=n where n stands for an amount of threads you want to be enabled. The n defaults to 1. The maximum threads defaults to 16 but may be altered programatically. Note, that if you are about to increase it, here is 100% chance that you are on the wrong way. + ## OpenWRT case The package is also compatible with routers. The router should be running by free opensource linux-based system such as [OpenWRT](https://openwrt.org/). You can build under openwrt with two options: first - through the SDK, which is preferred way or cross-compile manually with openwrt toolchain. From 1b8d3a0e66842d7cb5a9eb5777854d4dc4ac5d97 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 7 Aug 2024 00:22:52 +0300 Subject: [PATCH 13/19] Code cleanup --- config.h | 3 +++ youtubeUnblock.c | 48 ++++++++++++++++-------------------------------- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/config.h b/config.h index ce0948e..4373364 100644 --- a/config.h +++ b/config.h @@ -54,3 +54,6 @@ #define DEBUG #endif +// The Maximum Transmission Unit size for rawsocket +// Larger packets will be fragmented. Applicable for Chrome's kyber. +#define AVAILABLE_MTU 1384 diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 62f5736..4eda718 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -110,14 +110,6 @@ static int open_raw_socket(void) { return -1; } - // int one = 1; - // const int *val = &one; - // if (setsockopt(config.rawsocket, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) - // { - // fprintf(stderr, "setsockopt(IP_HDRINCL, 1) failed\n"); - // return -1; - // } - int mark = RAWSOCKET_MARK; if (setsockopt(config.rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { @@ -157,7 +149,6 @@ static int close_raw_socket(void) { return 0; } -#define AVAILABLE_MTU 1384 static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { int ret; @@ -252,14 +243,12 @@ struct packet_data { uint16_t payload_len; }; - // Per-queue data. Passed to queue_cb. struct queue_data { struct mnl_socket **_nl; int queue_num; }; - /** * Used to accept unsupported packets (GSOs) */ @@ -317,32 +306,27 @@ static int process_packet(const struct packet_data packet, struct queue_data qda } const int family = AF_INET; + const uint8_t *raw_payload = packet.payload; size_t raw_payload_len = packet.payload_len; - if (raw_payload == NULL) return MNL_CB_ERROR; + const struct iphdr *iph; + uint32_t iph_len; + const struct tcphdr *tcph; + uint32_t tcph_len; + const uint8_t *data; + uint32_t dlen; - const struct iphdr *ip_header = (const void *)raw_payload; + int ret = tcp4_payload_split((uint8_t *)raw_payload, raw_payload_len, + (struct iphdr **)&iph, &iph_len, (struct tcphdr **)&tcph, &tcph_len, + (uint8_t **)&data, &dlen); - if (ip_header->version != IPPROTO_IPIP || ip_header->protocol != IPPROTO_TCP) - goto fallback; - - int iph_len = ip_header->ihl * 4; - - const struct tcphdr *tcph = (const void *)(raw_payload + iph_len); - if ((const uint8_t *)tcph + 20 > raw_payload + raw_payload_len) { + if (ret < 0) { goto fallback; } - int tcph_len = tcph->doff * 4; - if ((const uint8_t *)tcph + tcph_len > raw_payload + raw_payload_len) { - goto fallback; - } - int data_len = ntohs(ip_header->tot_len) - iph_len - tcph_len; - const uint8_t *data = (const uint8_t *)(raw_payload + iph_len + tcph_len); - - struct verdict vrd = analyze_tls_data(data, data_len); + struct verdict vrd = analyze_tls_data(data, dlen); verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata.queue_num); nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_ACCEPT); @@ -352,7 +336,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda printf("Google video!\n"); #endif - if (data_len > 1480) { + if (dlen > 1480) { #ifdef DEBUG fprintf(stderr, "WARNING! Google video packet is too big and may cause issues!\n"); #endif @@ -370,12 +354,12 @@ static int process_packet(const struct packet_data packet, struct queue_data qda nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_DROP); int ret = 0; - nfq_ip_set_checksum((struct iphdr *)ip_header); + nfq_ip_set_checksum((struct iphdr *)iph); nfq_tcp_compute_checksum_ipv4( - (struct tcphdr *)tcph, (struct iphdr *)ip_header); + (struct tcphdr *)tcph, (struct iphdr *)iph); #ifdef FAKE_SNI - ret = gen_fake_sni(ip_header, tcph, fake_sni, &fsn_len); + ret = gen_fake_sni(iph, tcph, fake_sni, &fsn_len); if (ret < 0) { errno = -ret; perror("gen_fake_sni"); From 255371cbaa45e458e338ee6c795fa773e8973f62 Mon Sep 17 00:00:00 2001 From: zabbius Date: Wed, 7 Aug 2024 02:22:33 +0300 Subject: [PATCH 14/19] start-stop priority in rc script - now service can be enabled --- owrt/youtubeUnblock.owrt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/owrt/youtubeUnblock.owrt b/owrt/youtubeUnblock.owrt index 26382d3..23be195 100755 --- a/owrt/youtubeUnblock.owrt +++ b/owrt/youtubeUnblock.owrt @@ -1,6 +1,9 @@ #!/bin/sh /etc/rc.common USE_PROCD=1 +START=50 +STOP=50 + # Openwrt procd script: https://openwrt.org/docs/guide-developer/procd-init-script-example # The program should be put into /usr/bin/ # This file should be put into /etc/init.d/ From 69955d52378f1a6ea67cd5dbd5887d94b219679a Mon Sep 17 00:00:00 2001 From: zabbius Date: Wed, 7 Aug 2024 03:31:10 +0300 Subject: [PATCH 15/19] commandline options --- config.h | 23 +-- youtubeUnblock.c | 445 +++++++++++++++++++++++++++++++---------------- 2 files changed, 303 insertions(+), 165 deletions(-) diff --git a/config.h b/config.h index 4373364..a8b1b74 100644 --- a/config.h +++ b/config.h @@ -20,31 +20,26 @@ #define FRAGMENTATION_STRATEGY FRAG_STRAT_TCP #endif -#if FRAGMENTATION_STRATEGY == FRAG_STRAT_TCP - #define USE_TCP_SEGMENTATION -#elif FRAGMENTATION_STRATEGY == FRAG_STRAT_IP - #define USE_IP_FRAGMENTATION -#elif FRAGMENTATION_STRATEGY == FRAG_STRAT_NONE - #define USE_NO_FRAGMENTATION -#endif - #define RAWSOCKET_MARK (1 << 15) #ifdef USE_SEG2_DELAY #define SEG2_DELAY 100 #endif -#ifndef NO_FAKE_SNI -#define FAKE_SNI -#endif - #define FAKE_SNI_TTL 8 +// No fake SNI +#define FKSN_STRAT_NONE 0 // Will invalidate fake client hello by out-of-ack_seq out-of-seq request -#define FKSN_STRAT_ACK_SEQ 0 +#define FKSN_STRAT_ACK_SEQ 1 // Will assume that GGC server is located further than FAKE_SNI_TTL // Thus, Fake Client Hello will be eliminated automatically. -#define FKSN_STRAT_TTL 1 +#define FKSN_STRAT_TTL 2 + + +#ifndef NO_FAKE_SNI +#define FAKE_SNI_STRATEGY FKSN_STRAT_NONE +#endif #ifndef FAKE_SNI_STRATEGY #define FAKE_SNI_STRATEGY FKSN_STRAT_ACK_SEQ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 4eda718..f86ed6a 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -34,11 +34,134 @@ static struct { int rawsocket; pthread_mutex_t rawsocket_lock; int threads; + bool use_gso; + int fragmentation_strategy; + uint8_t fake_sni_ttl; + int fake_sni_strategy; + bool verbose; + uint32_t seg2_delay; } config = { - .rawsocket = -2, - .threads = THREADS_NUM + .rawsocket = -2, + .threads = THREADS_NUM, + .fragmentation_strategy = FRAGMENTATION_STRATEGY, + .fake_sni_strategy = FAKE_SNI_STRATEGY, + .fake_sni_ttl = FAKE_SNI_TTL, + +#ifdef SEG2_DELAY + .seg2_delay = SEG2_DELAY, +#else + .seg2_delay = 0, +#endif + +#ifdef USE_GSO + .use_gso = true, +#else + .use_gso = false, +#endif + +#ifdef DEBUG + .verbose = true, +#else + .verbose = false, +#endif }; +const char* get_value(const char *option, const char *prefix) +{ + size_t prefix_len = strlen(prefix); + size_t option_len = strlen(option); + + if (option_len < prefix_len || memcmp(prefix, option, prefix_len)) + return 0; + + return option + prefix_len; +} + +bool parse_bool_option(const char *value) { + errno = 0; + if (strcmp(value, "1") == 0) { + return true; + } + else if (strcmp(value, "0") == 0) { + return false; + } + errno = EINVAL; + return false; +} + +uint32_t parse_uint_option(const char* value) { + char* end; + uint32_t result = strtoul(value, &end, 10); + if (*end != '\0') { + errno = EINVAL; + return -1; + } + return result; +} + +bool parse_option(const char* option) { + const char* value; + + if ((value = get_value(option, "--gso=")) != 0) { + config.use_gso = parse_bool_option(value); + return errno == 0; + } + + if ((value = get_value(option, "--verbose=")) != 0) { + config.verbose = parse_bool_option(value); + return errno == 0; + } + + if ((value = get_value(option, "--frag=")) != 0) { + if (strcmp(value, "tcp") == 0) { + config.fragmentation_strategy = FRAG_STRAT_TCP; + } + else if (strcmp(value, "ip") == 0) { + config.fragmentation_strategy = FRAG_STRAT_IP; + } + else if (strcmp(value, "none") == 0) { + config.fragmentation_strategy = FRAG_STRAT_NONE; + } + else { + return false; + } + return true; + } + + if ((value = get_value(option, "--fake-sni=")) != 0) { + if (strcmp(value, "ack") == 0) { + config.fake_sni_strategy = FKSN_STRAT_ACK_SEQ; + } + else if (strcmp(value, "ttl") == 0) { + config.fake_sni_strategy = FKSN_STRAT_TTL; + } + else if (strcmp(value, "none") == 0) { + config.fake_sni_strategy = FKSN_STRAT_NONE; + } + else { + return false; + } + return true; + } + + if ((value = get_value(option, "--seg2delay=")) != 0) { + config.seg2_delay = parse_uint_option(value); + return errno == 0; + } + + if ((value = get_value(option, "--threads=")) != 0) { + config.threads = parse_uint_option(value); + return errno == 0 && config.threads <= MAX_THREADS; + } + + if ((value = get_value(option, "--fake-sni-ttl=")) != 0) { + config.fake_sni_ttl = parse_uint_option(value); + return errno == 0; + } + + return false; +} + static int parse_args(int argc, const char *argv[]) { int err; char *end; @@ -48,15 +171,29 @@ static int parse_args(int argc, const char *argv[]) { goto errormsg_help; } - uint32_t queue_num = strtoul(argv[1], &end, 10); - if (errno != 0 || *end != '\0') goto errormsg_help; + config.queue_start_num = parse_uint_option(argv[1]); + if (errno != 0) goto errormsg_help; + + for (int i = 2; i < argc; i++) { + if (!parse_option(argv[i])) { + printf("Invalid option %s\n", argv[i]); + goto errormsg_help; + } + } - config.queue_start_num = queue_num; return 0; errormsg_help: err = errno; - printf("Usage: %s [queue_num]\n", argv[0]); + printf("Usage: %s [OPTIONS]\n", argv[0]); + printf("Options:\n"); + printf("\t--gso={0,1}\n"); + printf("\t--fake-sni={ack,ttl,none}\n"); + printf("\t--fake-sni-ttl=\n"); + printf("\t--frag={tcp,ip,none}\n"); + printf("\t--seg2delay=\n"); + printf("\t--threads=\n"); + printf("\t--verbose={0,1}\n"); errno = err; if (errno == 0) errno = EINVAL; @@ -154,36 +291,38 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { int ret; if (pktlen > AVAILABLE_MTU) { -#ifdef DEBUG - printf("Split packet!\n"); -#endif + if (config.verbose) + printf("Split packet!\n"); uint8_t buff1[MNL_SOCKET_BUFFER_SIZE]; uint32_t buff1_size = MNL_SOCKET_BUFFER_SIZE; uint8_t buff2[MNL_SOCKET_BUFFER_SIZE]; uint32_t buff2_size = MNL_SOCKET_BUFFER_SIZE; -#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) { + switch (config.fragmentation_strategy) { + case FRAG_STRAT_TCP: + if ((ret = tcp4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { - errno = -ret; - 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) { + errno = -ret; + return ret; + } + break; + case FRAG_STRAT_IP: + if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { - errno = -ret; - return ret; + errno = -ret; + return ret; + } + break; + default: + errno = EINVAL; + printf("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; } -#else - errno = EINVAL; - printf("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; int status = send_raw_socket(buff1, buff1_size); @@ -332,20 +471,16 @@ static int process_packet(const struct packet_data packet, struct queue_data qda nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_ACCEPT); if (vrd.gvideo_hello) { -#ifdef DEBUG - printf("Google video!\n"); -#endif + if (config.verbose) + printf("Google video!\n"); if (dlen > 1480) { -#ifdef DEBUG - fprintf(stderr, "WARNING! Google video packet is too big and may cause issues!\n"); -#endif + if (config.verbose) + fprintf(stderr, "WARNING! Google video packet is too big and may cause issues!\n"); } -#ifdef FAKE_SNI uint8_t fake_sni[MNL_SOCKET_BUFFER_SIZE]; uint32_t fsn_len = MNL_SOCKET_BUFFER_SIZE; -#endif uint8_t frag1[MNL_SOCKET_BUFFER_SIZE]; uint8_t frag2[MNL_SOCKET_BUFFER_SIZE]; @@ -358,54 +493,59 @@ static int process_packet(const struct packet_data packet, struct queue_data qda nfq_tcp_compute_checksum_ipv4( (struct tcphdr *)tcph, (struct iphdr *)iph); -#ifdef FAKE_SNI - ret = gen_fake_sni(iph, tcph, fake_sni, &fsn_len); - if (ret < 0) { - errno = -ret; - perror("gen_fake_sni"); - goto fallback; + if (config.fake_sni_strategy != FKSN_STRAT_NONE) { + ret = gen_fake_sni(iph, tcph, fake_sni, &fsn_len); + if (ret < 0) { + errno = -ret; + perror("gen_fake_sni"); + goto fallback; + } + + ret = send_raw_socket(fake_sni, fsn_len); + if (ret < 0) { + errno = -ret; + perror("send fake sni"); + goto fallback; + } } - ret = send_raw_socket(fake_sni, fsn_len); - if (ret < 0) { - errno = -ret; - perror("send fake sni"); - goto fallback; + size_t ipd_offset; + size_t mid_offset; + + switch (config.fragmentation_strategy) { + case FRAG_STRAT_TCP: + ipd_offset = vrd.sni_offset; + mid_offset = ipd_offset + vrd.sni_len / 2; + + if ((ret = tcp4_frag(raw_payload, raw_payload_len, + mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { + + errno = -ret; + perror("tcp4_frag"); + goto send_verd; + } + break; + case FRAG_STRAT_IP: + ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset; + mid_offset = ipd_offset + vrd.sni_len / 2; + mid_offset += 8 - mid_offset % 8; + + if ((ret = ip4_frag(raw_payload, raw_payload_len, + mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { + + errno = -ret; + perror("ip4_frag"); + goto send_verd; + } + break; + default: + ret = send_raw_socket(raw_payload, raw_payload_len); + if (ret < 0) { + errno = -ret; + perror("raw pack send"); + } + goto send_verd; } -#endif - - -#if defined(USE_TCP_SEGMENTATION) - size_t ipd_offset = vrd.sni_offset; - size_t mid_offset = ipd_offset + vrd.sni_len / 2; - - if ((ret = tcp4_frag(raw_payload, raw_payload_len, - mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { - errno = -ret; - perror("tcp4_frag"); - goto send_verd; - } - -#elif defined(USE_IP_FRAGMENTATION) - 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 ((ret = ip4_frag(raw_payload, raw_payload_len, - mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { - errno = -ret; - perror("ip4_frag"); - goto send_verd; - } - -#else - ret = send_raw_socket(raw_payload, raw_payload_len); - if (ret < 0) { - errno = -ret; - perror("raw pack send"); - } - goto send_verd; -#endif ret = send_raw_socket(frag2, f2len); if (ret < 0) { @@ -414,35 +554,34 @@ static int process_packet(const struct packet_data packet, struct queue_data qda goto send_verd; } - -#ifdef SEG2_DELAY - struct dps_t *dpdt = malloc(sizeof(struct dps_t)); - dpdt->pkt = malloc(f1len); - memcpy(dpdt->pkt, frag1, f1len); - dpdt->pktlen = f1len; - dpdt->timer = SEG2_DELAY; - pthread_t thr; - pthread_create(&thr, NULL, delay_packet_send, dpdt); - pthread_detach(thr); -#else - ret = send_raw_socket(frag1, f1len); - if (ret < 0) { - errno = -ret; - perror("raw frags send: frag1"); - goto send_verd; + if (config.seg2_delay) { + struct dps_t *dpdt = malloc(sizeof(struct dps_t)); + dpdt->pkt = malloc(f1len); + memcpy(dpdt->pkt, frag1, f1len); + dpdt->pktlen = f1len; + dpdt->timer = config.seg2_delay; + pthread_t thr; + pthread_create(&thr, NULL, delay_packet_send, dpdt); + pthread_detach(thr); } + else { + ret = send_raw_socket(frag1, f1len); + if (ret < 0) { + errno = -ret; + perror("raw frags send: frag1"); -#endif - + goto send_verd; + } + } } /* if (pktb_mangled(pktb)) { -#ifdef DEBUG - printf("Mangled!\n"); -#endif + if (config.versose) + printf("Mangled!\n"); + nfq_nlmsg_verdict_put_pkt( verdnlh, pktb_data(pktb), pktb_len(pktb)); } @@ -533,10 +672,10 @@ int init_queue(int queue_num) { nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); -#ifdef USE_GSO + if (config.use_gso) { mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_GSO)); mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(NFQA_CFG_F_GSO)); -#endif + } if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { perror("mnl_socket_send"); @@ -609,69 +748,73 @@ int main(int argc, const char *argv[]) { exit(EXIT_FAILURE); } -#if defined(USE_TCP_SEGMENTATION) - printf("Using TCP segmentation\n"); -#elif defined(USE_IP_FRAGMENTATION) - printf("Using IP fragmentation\n"); -#else - printf("SNI fragmentation is disabled\n"); -#endif + switch (config.fragmentation_strategy) { + case FRAG_STRAT_TCP: + printf("Using TCP segmentation\n"); + break; + case FRAG_STRAT_IP: + printf("Using IP fragmentation\n"); + break; + default: + printf("SNI fragmentation is disabled\n"); + break; + } -#ifdef SEG2_DELAY - printf("Some outgoing googlevideo request segments will be delayed for %d ms as of SEG2_DELAY define\n", SEG2_DELAY); -#endif + if (config.seg2_delay) + printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay); -#ifdef FAKE_SNI - printf("Fake SNI will be sent before each googlevideo request\n"); + switch (config.fake_sni_strategy) { + case FKSN_STRAT_TTL: + printf("Fake SNI will be sent before each googlevideo request, TTL strategy will be used with TTL %d\n", config.fake_sni_ttl); + break; + case FRAG_STRAT_IP: + printf("Fake SNI will be sent before each googlevideo request, Ack-Seq strategy will be used\n"); + break; + default: + printf("SNI fragmentation is disabled\n"); + break; + } - printf("Fake SNI will use strategy: " -#if FAKE_SNI_STRATEGY == FKSN_STRAT_TTL - "TTL" -#else - "Ack-Seq" -#endif - "\n"); -#endif - -#ifdef USE_GSO - printf("GSO is enabled!\n"); -#endif + if (config.use_gso) + printf("GSO is enabled\n"); if (open_raw_socket() < 0) { perror("Unable to open raw socket"); exit(EXIT_FAILURE); } + struct queue_res *qres; + if (config.threads == 1) { + struct queue_conf tconf = { + .i = 0, + .queue_num = config.queue_start_num + }; -#if THREADS_NUM == 1 - struct queue_conf tconf = { - .i = 0, - .queue_num = config.queue_start_num - }; - - struct queue_res *qres = init_queue_wrapper(&tconf); -#else - struct queue_conf thread_confs[MAX_THREADS]; - pthread_t threads[MAX_THREADS]; - for (int i = 0; i < config.threads; i++) { - struct queue_conf *tconf = thread_confs + i; - pthread_t *thr = threads + i; - - tconf->queue_num = config.queue_start_num + i; - tconf->i = i; - - pthread_create(thr, NULL, init_queue_wrapper, tconf); + qres = init_queue_wrapper(&tconf); } + else { + printf("%d threads wil be used\n", config.threads); - void *res; - struct queue_res *qres - for (int i = 0; i < config.threads; i++) { - pthread_join(threads[i], &res); + struct queue_conf thread_confs[MAX_THREADS]; + pthread_t threads[MAX_THREADS]; + for (int i = 0; i < config.threads; i++) { + struct queue_conf *tconf = thread_confs + i; + pthread_t *thr = threads + i; - qres = res; + tconf->queue_num = config.queue_start_num + i; + tconf->i = i; + + pthread_create(thr, NULL, init_queue_wrapper, tconf); + } + + void *res; + for (int i = 0; i < config.threads; i++) { + pthread_join(threads[i], &res); + + qres = res; + } } -#endif if (close_raw_socket() < 0) { perror("Unable to close raw socket"); From 6eca2cb77c56920d9d679f786e1d5eee54d1310c Mon Sep 17 00:00:00 2001 From: zabbius Date: Wed, 7 Aug 2024 04:00:48 +0300 Subject: [PATCH 16/19] minor --- config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.h b/config.h index a8b1b74..5b3e763 100644 --- a/config.h +++ b/config.h @@ -37,7 +37,7 @@ #define FKSN_STRAT_TTL 2 -#ifndef NO_FAKE_SNI +#ifdef NO_FAKE_SNI #define FAKE_SNI_STRATEGY FKSN_STRAT_NONE #endif From c9949a3605173a5af75f53e8b1a96633ebe1072b Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 7 Aug 2024 11:38:26 +0300 Subject: [PATCH 17/19] Fix tcp4 frag error caused by enabled IP fragmentation If the packet is fragmented here is no way to deal with it, but we don't care about DF flag in real --- mangle.c | 6 +++++- youtubeUnblock.c | 10 +++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mangle.c b/mangle.c index 8ccf705..f0e2cea 100644 --- a/mangle.c +++ b/mangle.c @@ -226,7 +226,11 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, } - if (!(ntohs(hdr->frag_off) & IP_DF)) { + if ( + ntohs(hdr->frag_off) & IP_MF || + ntohs(hdr->frag_off) & IP_OFFMASK) { + printf("tcp4_frag: frag value: %d\n", + ntohs(hdr->frag_off)); lgerror("tcp4_frag: ip fragmentation is set", -EINVAL); return -EINVAL; } diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 4eda718..239d116 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -383,7 +383,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { errno = -ret; perror("tcp4_frag"); - goto send_verd; + goto fallback; } #elif defined(USE_IP_FRAGMENTATION) @@ -395,7 +395,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda mid_offset, frag1, &f1len, frag2, &f2len)) < 0) { errno = -ret; perror("ip4_frag"); - goto send_verd; + goto fallback; } #else @@ -404,7 +404,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda errno = -ret; perror("raw pack send"); } - goto send_verd; + goto fallback; #endif ret = send_raw_socket(frag2, f2len); @@ -412,7 +412,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda errno = -ret; perror("raw frags send: frag2"); - goto send_verd; + goto fallback; } #ifdef SEG2_DELAY @@ -430,7 +430,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda errno = -ret; perror("raw frags send: frag1"); - goto send_verd; + goto fallback; } #endif From 66906dbe0cc625f29997c176c8d4234686d24804 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 7 Aug 2024 13:32:01 +0300 Subject: [PATCH 18/19] Code cleanup --- config.h | 14 ++++ mangle.c | 10 ++- youtubeUnblock.c | 183 +++++++++++++++++++++++++++-------------------- 3 files changed, 123 insertions(+), 84 deletions(-) diff --git a/config.h b/config.h index 5b3e763..483703a 100644 --- a/config.h +++ b/config.h @@ -1,3 +1,17 @@ + +struct config_t { + unsigned int queue_start_num; + int rawsocket; + int threads; + int use_gso; + int fragmentation_strategy; + unsigned char fake_sni_ttl; + int fake_sni_strategy; + int verbose; + unsigned int seg2_delay; +}; +extern struct config_t config; + #define MAX_THREADS 16 #ifndef THREADS_NUM diff --git a/mangle.c b/mangle.c index 8ccf705..d330785 100644 --- a/mangle.c +++ b/mangle.c @@ -190,9 +190,8 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, f2_hdr->tot_len = htons(f2_dlen); -#if defined(DEBUG) - printf("Packet split in portion %u %u\n", f1_plen, f2_plen); -#endif + if (config.verbose) + printf("Packet split in portion %u %u\n", f1_plen, f2_plen); ip4_set_checksum(f1_hdr); ip4_set_checksum(f2_hdr); @@ -268,9 +267,8 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset); -#if defined(DEBUG) - printf("Packet split in portion %u %u\n", s1_plen, s2_plen); -#endif + if (config.verbose) + printf("Packet split in portion %u %u\n", s1_plen, s2_plen); tcp4_set_checksum(s1_tcph, s1_hdr); tcp4_set_checksum(s2_tcph, s2_hdr); diff --git a/youtubeUnblock.c b/youtubeUnblock.c index f86ed6a..049fc93 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -29,23 +29,14 @@ #include "config.h" #include "mangle.h" -static struct { - uint32_t queue_start_num; - int rawsocket; - pthread_mutex_t rawsocket_lock; - int threads; - bool use_gso; - int fragmentation_strategy; - uint8_t fake_sni_ttl; - int fake_sni_strategy; - bool verbose; - uint32_t seg2_delay; -} config = { +pthread_mutex_t rawsocket_lock; + +struct config_t config = { .rawsocket = -2, .threads = THREADS_NUM, - .fragmentation_strategy = FRAGMENTATION_STRATEGY, + .fragmentation_strategy = FRAGMENTATION_STRATEGY, .fake_sni_strategy = FAKE_SNI_STRATEGY, - .fake_sni_ttl = FAKE_SNI_TTL, + .fake_sni_ttl = FAKE_SNI_TTL, #ifdef SEG2_DELAY .seg2_delay = SEG2_DELAY, @@ -68,114 +59,148 @@ static struct { const char* get_value(const char *option, const char *prefix) { + errno = 0; + size_t prefix_len = strlen(prefix); size_t option_len = strlen(option); - if (option_len < prefix_len || memcmp(prefix, option, prefix_len)) - return 0; + if (option_len <= prefix_len || strncmp(prefix, option, prefix_len)) { + return NULL; + } return option + prefix_len; } -bool parse_bool_option(const char *value) { +int parse_bool_option(const char *value) { errno = 0; - if (strcmp(value, "1") == 0) { - return true; + if (strcmp(value, "1") == 0) { + return 1; } else if (strcmp(value, "0") == 0) { - return false; + return 0; } - errno = EINVAL; - return false; + + errno = EINVAL; + return -1; } -uint32_t parse_uint_option(const char* value) { +long parse_numeric_option(const char* value) { + errno = 0; + char* end; - uint32_t result = strtoul(value, &end, 10); + long result = strtol(value, &end, 10); if (*end != '\0') { errno = EINVAL; - return -1; + return 0; } + return result; } -bool parse_option(const char* option) { +int parse_option(const char* option) { const char* value; + int ret; - if ((value = get_value(option, "--gso=")) != 0) { - config.use_gso = parse_bool_option(value); - return errno == 0; + if (!strcmp(option, "--no-gso")) { + config.use_gso = 0; + goto out; } - if ((value = get_value(option, "--verbose=")) != 0) { - config.verbose = parse_bool_option(value); - return errno == 0; + if (!strcmp(option, "--silent")) { + config.verbose = 0; + goto out; } if ((value = get_value(option, "--frag=")) != 0) { + if (!value) { + goto err; + } + if (strcmp(value, "tcp") == 0) { config.fragmentation_strategy = FRAG_STRAT_TCP; - } - else if (strcmp(value, "ip") == 0) { + } else if (strcmp(value, "ip") == 0) { config.fragmentation_strategy = FRAG_STRAT_IP; - } - else if (strcmp(value, "none") == 0) { + } else if (strcmp(value, "none") == 0) { config.fragmentation_strategy = FRAG_STRAT_NONE; + } else { + goto err; } - else { - return false; - } - return true; + + goto out; } if ((value = get_value(option, "--fake-sni=")) != 0) { if (strcmp(value, "ack") == 0) { config.fake_sni_strategy = FKSN_STRAT_ACK_SEQ; - } - else if (strcmp(value, "ttl") == 0) { + } else if (strcmp(value, "ttl") == 0) { config.fake_sni_strategy = FKSN_STRAT_TTL; } else if (strcmp(value, "none") == 0) { config.fake_sni_strategy = FKSN_STRAT_NONE; + } else { + goto err; } - else { - return false; - } - return true; + + goto out; } if ((value = get_value(option, "--seg2delay=")) != 0) { - config.seg2_delay = parse_uint_option(value); - return errno == 0; + long num = parse_numeric_option(value); + if (errno != 0 || + num < 0) + goto err; + + config.seg2_delay = num; + goto out; } if ((value = get_value(option, "--threads=")) != 0) { - config.threads = parse_uint_option(value); - return errno == 0 && config.threads <= MAX_THREADS; + long num = parse_numeric_option(value); + if (errno != 0 || + num < 0 || + num > MAX_THREADS) { + errno = EINVAL; + goto err; + } + + config.threads = num; + goto out; } if ((value = get_value(option, "--fake-sni-ttl=")) != 0) { - config.fake_sni_ttl = parse_uint_option(value); - return errno == 0; + long num = parse_numeric_option(value); + if (errno != 0 || + num < 0 || + num > 255) { + goto err; + } + + config.fake_sni_ttl = num; + goto out; } - return false; +err: + errno = EINVAL; + return -1; +out: + errno = 0; + return 0; } static int parse_args(int argc, const char *argv[]) { int err; char *end; - if (argc != 2) { + if (argc < 2) { errno = EINVAL; goto errormsg_help; } - config.queue_start_num = parse_uint_option(argv[1]); + config.queue_start_num = parse_numeric_option(argv[1]); if (errno != 0) goto errormsg_help; for (int i = 2; i < argc; i++) { - if (!parse_option(argv[i])) { + if (parse_option(argv[i])) { printf("Invalid option %s\n", argv[i]); goto errormsg_help; } @@ -187,13 +212,13 @@ errormsg_help: err = errno; printf("Usage: %s [OPTIONS]\n", argv[0]); printf("Options:\n"); - printf("\t--gso={0,1}\n"); printf("\t--fake-sni={ack,ttl,none}\n"); printf("\t--fake-sni-ttl=\n"); printf("\t--frag={tcp,ip,none}\n"); printf("\t--seg2delay=\n"); printf("\t--threads=\n"); - printf("\t--verbose={0,1}\n"); + printf("\t--silent\n"); + printf("\t--no-gso\n"); errno = err; if (errno == 0) errno = EINVAL; @@ -254,7 +279,7 @@ static int open_raw_socket(void) { return -1; } - int mst = pthread_mutex_init(&config.rawsocket_lock, NULL); + int mst = pthread_mutex_init(&rawsocket_lock, NULL); if (mst) { fprintf(stderr, "Mutex err: %d\n", mst); close(config.rawsocket); @@ -276,11 +301,11 @@ static int close_raw_socket(void) { if (close(config.rawsocket)) { perror("Unable to close raw socket"); - pthread_mutex_destroy(&config.rawsocket_lock); + pthread_mutex_destroy(&rawsocket_lock); return -1; } - pthread_mutex_destroy(&config.rawsocket_lock); + pthread_mutex_destroy(&rawsocket_lock); config.rawsocket = -2; return 0; @@ -314,13 +339,11 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { errno = -ret; return ret; - } + } break; - default: - errno = EINVAL; - printf("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"); + default: + errno = EINVAL; + printf("send_raw_socket: Packet is too big but fragmentation is disabled!\n"); return -EINVAL; } @@ -359,13 +382,13 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { } }; - pthread_mutex_lock(&config.rawsocket_lock); + pthread_mutex_lock(&rawsocket_lock); int sent = sendto(config.rawsocket, pkt, pktlen, 0, (struct sockaddr *)&daddr, sizeof(daddr)); - pthread_mutex_unlock(&config.rawsocket_lock); + pthread_mutex_unlock(&rawsocket_lock); /* The function will return -errno on error as well as errno value set itself */ if (sent < 0) sent = -errno; @@ -524,6 +547,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda perror("tcp4_frag"); goto send_verd; } + break; case FRAG_STRAT_IP: ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset; @@ -537,6 +561,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda perror("ip4_frag"); goto send_verd; } + break; default: ret = send_raw_socket(raw_payload, raw_payload_len); @@ -555,7 +580,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda goto send_verd; } - if (config.seg2_delay) { + if (config.seg2_delay) { struct dps_t *dpdt = malloc(sizeof(struct dps_t)); dpdt->pkt = malloc(f1len); memcpy(dpdt->pkt, frag1, f1len); @@ -564,8 +589,7 @@ static int process_packet(const struct packet_data packet, struct queue_data qda pthread_t thr; pthread_create(&thr, NULL, delay_packet_send, dpdt); pthread_detach(thr); - } - else { + } else { ret = send_raw_socket(frag1, f1len); if (ret < 0) { errno = -ret; @@ -672,9 +696,9 @@ int init_queue(int queue_num) { nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); - if (config.use_gso) { - mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_GSO)); - mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(NFQA_CFG_F_GSO)); + if (config.use_gso) { + mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_GSO)); + mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(NFQA_CFG_F_GSO)); } if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { @@ -728,6 +752,7 @@ struct queue_conf { struct queue_res { int status; }; +static struct queue_res defqres = {0}; static struct queue_res threads_reses[MAX_THREADS]; @@ -760,8 +785,9 @@ int main(int argc, const char *argv[]) { break; } - if (config.seg2_delay) + if (config.seg2_delay) { printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay); + } switch (config.fake_sni_strategy) { case FKSN_STRAT_TTL: @@ -775,15 +801,16 @@ int main(int argc, const char *argv[]) { break; } - if (config.use_gso) + if (config.use_gso) { printf("GSO is enabled\n"); + } if (open_raw_socket() < 0) { perror("Unable to open raw socket"); exit(EXIT_FAILURE); } - struct queue_res *qres; + struct queue_res *qres = &defqres; if (config.threads == 1) { struct queue_conf tconf = { From 768a29baec2b48504b75dfc18da06393492c5021 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 7 Aug 2024 22:28:03 +0300 Subject: [PATCH 19/19] Document flags in readme --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 16cd838..2cd6a15 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,15 @@ If you don't want youtubeUnblock to log on each googlevideo request pass -DSILEN Also DNS over HTTPS (DOH) is preferred for additional anonimity. -## Troubleshooting -If you have any troubles with youtubeUnblock, here are some options to tune. If them don't work in your case, please, open an issue. You can pass these options in make CFLAGS (`make CFLAGS=...`) or edit CFLAGS variable in Makefile. +## Flags Available flags: -- -DUSE_SEG2_DELAY This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet. You can tune the amount of time in `#define SEG2_DELAY 100` where 100 stands for milliseconds. -- -DNO_FAKE_SNI This flag disables -DFAKE_SNI which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. This flag may be related to some Operation not permitted error messages, so befor open an issue refer to FAQ for EPERMS. -- -DNOUSE_GSO This flag disables fix for Google Chrome fat ClientHello. The GSO is well tested now, so this flag probably won't fix anything. -- -DFAKE_SNI_STRATEGY It is possible that your ISP has conntrack/some custom stuff enabled. So if it is enabled as well as firewall rule to drop tcp packets considered invalid (I'm not sure who and how does that, may be TSPU does so only for google domains). So fake Client Hello won't work normally. The solution is to invalidate client hello by TTL. Use -DFAKE_SNI_STRATEGY=FKSN_STRAT_TTL to enable this and -FKSN_STRAT_TTL=8 to change the TTL (based on hops number between you and the google server). +- `--seg2delay=` - This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet. +- `--fake-sni={ack,ttl, none}` This flag enables fake-sni which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. This flag may be related to some Operation not permitted error messages, so befor open an issue refer to FAQ for EPERMS. Note, that this flag is set to `ack` by default. You may disable fake sni by setting it to `none`. Note, that your ISP may have conntrack drop on invalid state enabled, so this flag won't work. Use `ttl` to escape that. +- `--fake-sni-ttl=` Tunes the time to live of fake sni messages. TTL is specified like that the packet will go through the TSPU and captured by it, but will not reach the destination server. Defaults to 8. +- `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything. +- `--threads=` Specifies the amount of threads you want to be running for your program. This defaults to 1 and shouldn't be edited for normal use. If you have performance issues, consult [performance chaptr](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#performance) +- `--silent` - Disables Google video detected debug logs. +- `--frag={tcp,ip,none}` Specifies the fragmentation strategy for the packet. tcp is used by default. Ip fragmentation may be blocked by TSPU. None specifies no fragmentation. Probably this won't work, but may be will work for some fake sni strategies. If you are on Chromium you may have to disable kyber (the feature that makes the TLS ClientHello very fat). I've got the problem with it on router, so to escape possibly errors it is better to just disable it: in chrome://flags search for kyber and switch it to disabled state.