From d2985e80bd288a7a63b2adc64ed48332c8679997 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 23 Jul 2024 17:18:51 +0300 Subject: [PATCH] Use TCP Segmentation This commit is proposed to be a fix of #1. Some DPIs drops IP fragmented packets so we are going to higher level and fragmenting TCPs. Fragmented TCPs are normally handled by DPIs but if they are going in reversed order everything is ok. In further reviews of this technique may be a delay for 2nd packet should be implemented. --- youtubeUnblock.c | 130 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 13 deletions(-) diff --git a/youtubeUnblock.c b/youtubeUnblock.c index a3203ff..a194bf8 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -20,9 +20,11 @@ #include #ifndef NOUSE_GSO - #define USE_GSO +#endif +#ifndef USE_IP_FRAGMENTATION +#define USE_TCP_SEGMENTATION #endif #define RAWSOCKET_MARK 0xfc70 @@ -207,8 +209,6 @@ static int ipv4_frag(struct pkt_buff *pktb, size_t payload_offset, f2_hdr->frag_off = htons(f2_frag_off); f2_hdr->tot_len = htons(f2_dlen); - *frag1 = pktb_alloc(AF_INET, buff1, f1_dlen, 256); - *frag2 = pktb_alloc(AF_INET, buff2, f2_dlen, 256); #ifdef DEBUG printf("Packet split in portion %zu %zu\n", f1_dlen, f2_dlen); @@ -217,6 +217,82 @@ static int ipv4_frag(struct pkt_buff *pktb, size_t payload_offset, nfq_ip_set_checksum(f1_hdr); nfq_ip_set_checksum(f2_hdr); + *frag1 = pktb_alloc(AF_INET, buff1, f1_dlen, 256); + *frag2 = pktb_alloc(AF_INET, buff2, f2_dlen, 256); + + 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, 256); + *seg2 = pktb_alloc(AF_INET, buff2, s2_dlen, 256); + return 0; } @@ -231,7 +307,11 @@ static int send_raw_socket(struct pkt_buff *pktb) { struct pkt_buff *buff1; struct pkt_buff *buff2; - ipv4_frag(pktb, AVAILABLE_MTU-24, &buff1, &buff2); +#ifdef USE_TCP_SEGMENTATION + tcp4_frag(pktb, AVAILABLE_MTU-128, &buff1, &buff2); +#else + ipv4_frag(pktb, AVAILABLE_MTU-128, &buff1, &buff2); +#endif int sent = 0; int status = send_raw_socket(buff1); @@ -242,16 +322,14 @@ static int send_raw_socket(struct pkt_buff *pktb) { pktb_free(buff2); return status; } + pktb_free(buff1); status = send_raw_socket(buff2); if (status >= 0) sent += status; else { - pktb_free(buff1); pktb_free(buff2); return status; } - - pktb_free(buff1); pktb_free(buff2); return sent; @@ -543,16 +621,34 @@ static int process_packet(const struct packet_data packet) { // Also it will never be meaningless to ensure the // checksum is right. nfq_tcp_compute_checksum_ipv4(tcph, ip_header); - nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_DROP); - 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; - struct pkt_buff *frag1; struct pkt_buff *frag2; + + +#ifdef USE_TCP_SEGMENTATION + size_t ipd_offset = vrd.sni_offset; + size_t mid_offset = ipd_offset + vrd.sni_len / 2; + + if (tcp4_frag(pktb, mid_offset, &frag1, &frag2) < 0) { + perror("tcp4_frag"); + goto fallback; + } + + if ((send_raw_socket(frag2) == -1) || (send_raw_socket(frag1) == -1)) { + perror("raw frags send"); + } + + pktb_free(frag1); + pktb_free(frag2); + +#else + 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"); @@ -565,6 +661,8 @@ static int process_packet(const struct packet_data packet) { pktb_free(frag1); pktb_free(frag2); +#endif + } @@ -648,6 +746,12 @@ int main(int argc, const char *argv[]) exit(EXIT_FAILURE); } +#ifdef USE_TCP_SEGMENTATION + printf("Using TCP segmentation!\n"); +#else + printf("Using IP fragmentation!\n"); +#endif + if (open_socket()) { perror("Unable to open socket"); exit(EXIT_FAILURE);