diff --git a/args.c b/args.c index 61c9a8f..46677e6 100644 --- a/args.c +++ b/args.c @@ -32,10 +32,11 @@ struct config_t config = { #endif #ifdef DEBUG - .verbose = true, + .verbose = 1, #else - .verbose = false, + .verbose = 0, #endif + .domains_str = defaul_snistr, .domains_strlen = sizeof(defaul_snistr), @@ -53,13 +54,15 @@ struct config_t config = { #define OPT_FRAG_SNI_REVERSE 12 #define OPT_FRAG_SNI_FAKED 13 #define OPT_FK_WINSIZE 14 +#define OPT_TRACE 15 +#define OPT_QUIC_DROP 16 #define OPT_SEG2DELAY 5 #define OPT_THREADS 6 #define OPT_SILENT 7 #define OPT_NO_GSO 8 #define OPT_QUEUE_NUM 9 -#define OPT_MAX OPT_FRAG_SNI_FAKED +#define OPT_MAX OPT_QUIC_DROP static struct option long_opt[] = { {"help", 0, 0, 'h'}, @@ -73,9 +76,11 @@ static struct option long_opt[] = { {"frag-sni-reverse", 1, 0, OPT_FRAG_SNI_REVERSE}, {"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED}, {"fk-winsize", 1, 0, OPT_FK_WINSIZE}, + {"quic-drop", 0, 0, OPT_QUIC_DROP}, {"seg2delay", 1, 0, OPT_SEG2DELAY}, {"threads", 1, 0, OPT_THREADS}, {"silent", 0, 0, OPT_SILENT}, + {"trace", 0, 0, OPT_TRACE}, {"no-gso", 0, 0, OPT_NO_GSO}, {"queue-num", 1, 0, OPT_QUEUE_NUM}, {0,0,0,0} @@ -120,9 +125,11 @@ void print_usage(const char *argv0) { printf("\t--frag-sni-reverse={0|1}\n"); printf("\t--frag-sni-faked={0|1}\n"); printf("\t--fk-winsize=\n"); + printf("\t--quic-drop\n"); printf("\t--seg2delay=\n"); printf("\t--threads=\n"); printf("\t--silent\n"); + printf("\t--trace\n"); printf("\t--no-gso\n"); printf("\n"); } @@ -140,12 +147,18 @@ int parse_args(int argc, char *argv[]) { case 'v': print_version(); goto stop_exec; + case OPT_TRACE: + config.verbose = 2; + break; case OPT_SILENT: config.verbose = 0; break; case OPT_NO_GSO: config.use_gso = 0; break; + case OPT_QUIC_DROP: + config.quic_drop = 1; + break; case OPT_SNI_DOMAINS: if (!strcmp(optarg, "all")) { config.all_domains = 1; @@ -329,7 +342,12 @@ void print_welcome() { printf("GSO is enabled\n"); } + if (config.quic_drop) { + printf("All QUIC packets will be dropped\n"); + } + if (config.all_domains) { printf("All Client Hello will be targetted by youtubeUnblock!\n"); } + } diff --git a/config.h b/config.h index 93db448..2769176 100644 --- a/config.h +++ b/config.h @@ -25,7 +25,11 @@ struct config_t { unsigned char faking_ttl; int fake_sni; unsigned int fake_sni_seq_len; +#define VERBOSE_INFO 0 +#define VERBOSE_DEBUG 1 +#define VERBOSE_TRACE 2 int verbose; + int quic_drop; /* In milliseconds */ unsigned int seg2_delay; const char *domains_str; diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..60b21eb --- /dev/null +++ b/logging.h @@ -0,0 +1,39 @@ +#ifndef LOGGING_H +#define LOGGING_H +#include "config.h" + +#define LOG_LEVEL (config.verbose) + +#ifdef KERNEL_SPACE +#include +#define printf pr_info +#define perror pr_err +#define lgerror(msg, ret, ...) __extension__ ({ \ + printf(msg ": %d\n", ##__VA_ARGS__, ret); \ +}) +#else +#include // IWYU pragma: export +#include +#define lgerror(msg, ret, ...) __extension__ ({ \ + errno = -(ret); \ + printf(msg ": %s\n", ##__VA_ARGS__, strerror(errno)); \ +}) +#endif /* PROGRAM_SPACE */ + + +#define lgdebugmsg(msg, ...) \ +(LOG_LEVEL >= VERBOSE_DEBUG ? printf(msg "\n", ##__VA_ARGS__) : 0) + +#define lgtracemsg(msg, ...) \ +(LOG_LEVEL >= VERBOSE_TRACE ? printf(msg "\n", ##__VA_ARGS__) : 0) + +#define lgtrace_start(msg, ...) \ +(LOG_LEVEL >= VERBOSE_TRACE ? printf("[TRACE] " msg " ( ", ##__VA_ARGS__) : 0) + +#define lgtrace_addp(msg, ...) \ +(LOG_LEVEL >= VERBOSE_TRACE ? printf(msg", ", ##__VA_ARGS__) : 0) + +#define lgtrace_end() \ +(LOG_LEVEL >= VERBOSE_TRACE ? printf(") \n") : 0) + +#endif /* LOGGING_H */ diff --git a/mangle.c b/mangle.c index d47733a..0d7c60d 100644 --- a/mangle.c +++ b/mangle.c @@ -2,22 +2,50 @@ #include "types.h" // IWYU pragma: keep #include "mangle.h" #include "config.h" +#include "utils.h" +#include "quic.h" +#include "logging.h" -#ifdef KERNEL_SPACE -#include - -#else +#ifndef KERNEL_SCOPE #include -#include -#include #endif - int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (raw_payload_len > MAX_PACKET_SIZE) { return PKT_ACCEPT; } + const struct iphdr *iph; + uint32_t iph_len; + const uint8_t *ip_payload; + uint32_t ip_payload_len; + + int ret; + + ret = ip4_payload_split((uint8_t *)raw_payload, raw_payload_len, + (struct iphdr **)&iph, &iph_len, + (uint8_t **)&ip_payload, &ip_payload_len); + + + if (ret < 0) + goto accept; + + switch (iph->protocol) { + case IPPROTO_TCP: + return process_tcp4_packet(raw_payload, raw_payload_len); + case IPPROTO_UDP: + return process_udp4_packet(raw_payload, raw_payload_len); + default: + goto accept; + } + +accept: + return PKT_ACCEPT; +drop: + return PKT_DROP; +} + +int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { const struct iphdr *iph; uint32_t iph_len; const struct tcphdr *tcph; @@ -36,8 +64,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { struct tls_verdict vrd = analyze_tls_data(data, dlen); if (vrd.target_sni) { - if (config.verbose) - printf("Target SNI detected: %.*s\n", vrd.sni_len, data + vrd.sni_offset); + lgdebugmsg("Target SNI detected: %.*s", vrd.sni_len, data + vrd.sni_offset); uint8_t payload[MAX_PACKET_SIZE]; uint32_t payload_len = raw_payload_len; @@ -63,7 +90,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (dlen > 1480 && config.verbose) { - printf("WARNING! Client Hello packet is too big and may cause issues!\n"); + lgdebugmsg("WARNING! Client Hello packet is too big and may cause issues!"); } if (config.fake_sni) { @@ -126,6 +153,79 @@ drop: return PKT_DROP; } +int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen) { + const struct iphdr *iph; + uint32_t iph_len; + const struct udphdr *udph; + const uint8_t *data; + uint32_t dlen; + + int ret = udp4_payload_split((uint8_t *)pkt, pktlen, + (struct iphdr **)&iph, &iph_len, + (struct udphdr **)&udph, + (uint8_t **)&data, &dlen); + + lgtrace_start("Got udp packet"); + + if (ret < 0) { + lgtrace_addp("undefined"); + goto accept; + } + + if (dlen > 10 && config.verbose >= VERBOSE_TRACE) { + printf("UDP payload start: [ "); + for (int i = 0; i < 10; i++) { + printf("%02x ", data[i]); + } + printf("], "); + } + + lgtrace_addp("QUIC probe"); + const struct quic_lhdr *qch; + uint32_t qch_len; + struct quic_cids qci; + uint8_t *quic_raw_payload; + uint32_t quic_raw_plen; + ret = quic_parse_data((uint8_t *)data, dlen, + (struct quic_lhdr **)&qch, &qch_len, &qci, + &quic_raw_payload, &quic_raw_plen); + + if (ret < 0) { + lgtrace_addp("undefined type"); + goto accept; + } + + lgtrace_addp("QUIC detected"); + uint8_t qtype = qch->type; + + if (config.quic_drop) { + goto drop; + } + + if (qch->version == QUIC_V1) + qtype = quic_convtype_v1(qtype); + else if (qch->version == QUIC_V2) + qtype = quic_convtype_v2(qtype); + + if (qtype != QUIC_INITIAL_TYPE) { + lgtrace_addp("quic message type: %d", qtype); + goto accept; + } + + lgtrace_addp("quic initial message"); + +accept: + lgtrace_addp("accepted"); + lgtrace_end(); + + return PKT_ACCEPT; +drop: + lgtrace_addp("dropped"); + lgtrace_end(); + + return PKT_DROP; +} + int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_sz, uint32_t dvs) { if (poses_sz == 0) { if (config.seg2_delay && ((dvs > 0) ^ config.frag_sni_reverse)) { @@ -150,7 +250,7 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses int ret; if (dvs > poses[0]) { - printf("send_frags: Recursive dvs(%d) is more than poses0(%d)\n", dvs, poses[0]); + lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]); return -EINVAL; } @@ -158,8 +258,7 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses frag1, &f1len, frag2, &f2len); if (ret < 0) { - lgerror("send_frags: frag", ret); - printf("Error context: packet with size %d, position: %d, recursive dvs: %d\n", pktlen, poses[0], dvs); + lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs); return ret; } @@ -214,7 +313,7 @@ int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *pose int ret; if (dvs > poses[0]) { - printf("send_frags: Recursive dvs(%d) is more than poses0(%d)\n", dvs, poses[0]); + lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]); return -EINVAL; } @@ -222,8 +321,7 @@ int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *pose frag1, &f1len, frag2, &f2len); if (ret < 0) { - lgerror("send_frags: frag", ret); - printf("Error context: packet with size %d, position: %d, recursive dvs: %d\n", pktlen, poses[0], dvs); + lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs); return ret; } @@ -330,269 +428,7 @@ int post_fake_sni(const struct iphdr *iph, unsigned int iph_len, return 0; } -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(uint8_t *pkt, uint32_t buflen, - struct iphdr **iph, uint32_t *iph_len, - uint8_t **payload, uint32_t *plen) { - if (pkt == NULL || buflen < sizeof(struct iphdr)) { - lgerror("ip4_payload_split: pkt|buflen", -EINVAL); - return -EINVAL; - } - - struct iphdr *hdr = (struct iphdr *)pkt; - if (hdr->version != IPVERSION) { - lgerror("ip4_payload_split: ipversion", -EINVAL); - return -EINVAL; - } - - uint32_t hdr_len = hdr->ihl * 4; - uint32_t pktlen = ntohs(hdr->tot_len); - if (buflen < pktlen || hdr_len > pktlen) { - lgerror("ip4_payload_split: buflen cmp pktlen", -EINVAL); - 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(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) { - struct iphdr *hdr; - uint32_t hdr_len; - struct tcphdr *thdr; - uint32_t thdr_len; - - uint8_t *tcph_pl; - uint32_t tcph_plen; - - if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len, - &tcph_pl, &tcph_plen)){ - return -EINVAL; - } - - - if ( - hdr->protocol != IPPROTO_TCP || - tcph_plen < sizeof(struct tcphdr)) { - return -EINVAL; - } - - - thdr = (struct tcphdr *)(tcph_pl); - thdr_len = thdr->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 uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, - uint8_t *frag1, uint32_t *f1len, - uint8_t *frag2, uint32_t *f2len) { - - struct iphdr *hdr; - const uint8_t *payload; - uint32_t plen; - uint32_t hdr_len; - int ret; - - if (!frag1 || !f1len || !frag2 || !f2len) - return -EINVAL; - - if ((ret = ip4_payload_split( - (uint8_t *)pkt, buflen, - &hdr, &hdr_len, (uint8_t **)&payload, &plen)) < 0) { - lgerror("ipv4_frag: TCP Header extract error", ret); - return -EINVAL; - } - - if (plen <= payload_offset) { - return -EINVAL; - } - - if (payload_offset & ((1 << 3) - 1)) { - lgerror("ipv4_frag: Payload offset MUST be a multiply of 8!", -EINVAL); - - return -EINVAL; - } - - uint32_t f1_plen = payload_offset; - uint32_t f1_dlen = f1_plen + hdr_len; - - uint32_t f2_plen = plen - payload_offset; - uint32_t 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; - - 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); - - - 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); - - return 0; -} - -// split packet to two tcp-on-ipv4 segments. -int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, - uint8_t *seg1, uint32_t *s1len, - uint8_t *seg2, uint32_t *s2len) { - - struct iphdr *hdr; - uint32_t hdr_len; - struct tcphdr *tcph; - uint32_t tcph_len; - uint32_t plen; - const uint8_t *payload; - int ret; - - if (!seg1 || !s1len || !seg2 || !s2len) - return -EINVAL; - - if ((ret = tcp4_payload_split((uint8_t *)pkt, buflen, - &hdr, &hdr_len, - &tcph, &tcph_len, - (uint8_t **)&payload, &plen)) < 0) { - lgerror("tcp4_frag: tcp4_payload_split", ret); - - return -EINVAL; - } - - - 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; - } - - - if (plen <= payload_offset) { - return -EINVAL; - } - - uint32_t s1_plen = payload_offset; - uint32_t s1_dlen = s1_plen + hdr_len + tcph_len; - - uint32_t s2_plen = plen - payload_offset; - uint32_t 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 (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); - - return 0; -} #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 @@ -807,8 +643,13 @@ int fail4_packet(uint8_t *payload, uint32_t plen) { } if (config.faking_strategy == FAKE_STRAT_ACK_SEQ) { +#ifdef KERNEL_SCOPE + tcph->seq = 124; + tcph->ack_seq = 124; +#else tcph->seq = random(); tcph->ack_seq = random(); +#endif } else if (config.faking_strategy == FAKE_STRAT_TTL) { iph->ttl = config.faking_ttl; } diff --git a/mangle.h b/mangle.h index 7e98358..2196255 100644 --- a/mangle.h +++ b/mangle.h @@ -3,30 +3,6 @@ #include "types.h" -#ifdef KERNEL_SPACE -#include -#include -#include -#include -#include -#include - -#include - -/* from */ -#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 -#define USER_SPACE -#include -#include -#include -#include -#include -#endif - /** * Result of analyze_tls_data function */ @@ -42,42 +18,6 @@ struct tls_verdict { */ struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); -/** - * Splits the packet to two IP fragments on position payload_offset. - * payload_offset indicates the position relatively to start of IP payload - * (start of transport header) - */ -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); - -/** - * Splits the packet to two TCP segments on position payload_offset - * payload_offset indicates the position relatively to start of TCP payload. - */ -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); - -/** - * Splits the raw packet payload to ip header and ip payload. - */ -int ip4_payload_split(uint8_t *pkt, uint32_t buflen, - struct iphdr **iph, uint32_t *iph_len, - uint8_t **payload, uint32_t *plen); - -/** - * Splits the raw packet payload to ip header, tcp header and tcp payload. - */ -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); - -void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph); -void ip4_set_checksum(struct iphdr *iph); /** * Generates fake client hello message @@ -100,6 +40,20 @@ int fail4_packet(uint8_t *payload, uint32_t plen); */ int process_packet(const uint8_t *packet, uint32_t packet_len); + +/** + * Processe the TCP packet. + * Returns verdict. + */ +int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len); + + +/** + * Processes the UDP packet. + * Returns verdict. + */ +int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen); + /** * Sends fake client hello. */ diff --git a/quic.c b/quic.c index a2dc291..31ec7df 100644 --- a/quic.c +++ b/quic.c @@ -1,9 +1,6 @@ #include "quic.h" +#include "logging.h" -static const uint32_t supported_versions[] = { - 1, // version 1, RFC 9000 - 0x6b3343cf, // version 2, RFC 9369 -}; /** * Packet number. @@ -45,18 +42,24 @@ int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len, struct quic_lhdr *nqch = (struct quic_lhdr *)raw_payload; uint32_t left_len = raw_payload_len - sizeof(struct quic_lhdr); uint8_t *cur_rawptr = raw_payload + sizeof(struct quic_lhdr); - if (!nqch->fixed) + if (!nqch->fixed) { + lgtrace_addp("quic fixed uset"); return -EPROTO; + } uint8_t found = 0; for (uint8_t i = 0; i < sizeof(supported_versions); i++) { - if (nqch->version == supported_versions[i]) { + if (ntohl(nqch->version) == supported_versions[i]) { found = 1; } } - if (!found) + if (!found) { + lgtrace_addp("quic version undefined %d", ntohl(nqch->version)); return -EPROTO; + } + + lgtrace_addp("quic version valid %d", ntohl(nqch->version)); if (left_len < 2) goto invalid_packet; struct quic_cids nqci = {0}; diff --git a/quic.h b/quic.h index cf46df6..1edbfca 100644 --- a/quic.h +++ b/quic.h @@ -2,6 +2,7 @@ #define QUIC_H #include "types.h" + /** * @macro * @@ -22,38 +23,52 @@ "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb" \ "\xf9\xbd\x2e\xd9" -#define QUIC_INITIAL_TYPE_V1 0x00 -#define QUIC_0_RTT_TYPE_V1 0x01 -#define QUIC_HANDSHAKE_TYPE_V1 0x02 -#define QUIC_RETRY_TYPE_V1 0x03 +#define QUIC_INITIAL_TYPE 0 +#define QUIC_0_RTT_TYPE 1 +#define QUIC_HANDSHAKE_TYPE 2 +#define QUIC_RETRY_TYPE 3 -#define QUIC_INITIAL_TYPE_V2 0b01 -#define QUIC_0_RTT_TYPE_V2 0b10 -#define QUIC_HANDSHAKE_TYPE_V2 0b11 -#define QUIC_RETRY_TYPE_V2 0b00 +#define QUIC_INITIAL_TYPE_V1 0b00 +#define QUIC_0_RTT_TYPE_V1 0b01 +#define QUIC_HANDSHAKE_TYPE_V1 0b10 +#define QUIC_RETRY_TYPE_V1 0b11 +#define quic_convtype_v1(type) (type) + +#define QUIC_INITIAL_TYPE_V2 0b01 +#define QUIC_0_RTT_TYPE_V2 0b10 +#define QUIC_HANDSHAKE_TYPE_V2 0b11 +#define QUIC_RETRY_TYPE_V2 0b00 +#define quic_convtype_v2(type) (((type) + 1) & 0b11) + +#define QUIC_V1 1 // RFC 9000 +#define QUIC_V2 0x6b3343cf // RFC 9369 + +static const uint32_t supported_versions[] = { + QUIC_V1, + QUIC_V2, +}; /** * Quic Large Header */ struct quic_lhdr { #if __BYTE_ORDER == __LITTLE_ENDIAN - uint8_t number_length: 2; - uint8_t reserved: 2; - uint8_t type: 2; - uint8_t fixed: 1; - uint8_t form: 1; + uint8_t number_length:2; + uint8_t reserved:2; + uint8_t type:2; + uint8_t fixed:1; + uint8_t form:1; #elif __BYTE_ORDER == __BIG_ENDIAN - uint8_t form: 1; - uint8_t fixed: 1; - uint8_t type: 2; - uint8_t reserved: 2; - uint8_t number_length: 2; + uint8_t form:1; + uint8_t fixed:1; + uint8_t type:2; + uint8_t reserved:2; + uint8_t number_length:2; #else #error "Undefined endian" #endif - uint32_t version; -}; +}__attribute__((packed)); /** * Quic Large Header Ids diff --git a/types.h b/types.h index 324afd7..07599fe 100644 --- a/types.h +++ b/types.h @@ -15,22 +15,33 @@ typedef __i8 int8_t; typedef __i16 int16_t; typedef __i32 int32_t; typedef __i64 int64_t; - -#include -#define printf pr_info -#define perror pr_err -#define lgerror(msg, ret) (pr_err(msg ": %d\n", ret)) - #else /* USERSPACE_SCOPE */ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export -#include // IWYU pragma: export -#define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);}) - - #endif /* SCOPES */ +// Network specific structures +#ifdef KERNEL_SPACE +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +/* from */ +#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 +#define USER_SPACE +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#endif + #endif /* TYPES_H */ diff --git a/uspace.mk b/uspace.mk index 5c8d062..971a54a 100644 --- a/uspace.mk +++ b/uspace.mk @@ -22,7 +22,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c mangle.c args.c +SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..dee16d9 --- /dev/null +++ b/utils.c @@ -0,0 +1,312 @@ +#include "utils.h" +#include "logging.h" + +#ifdef KERNEL_SPACE +#include +#else +#include +#include +#include +#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(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + uint8_t **payload, uint32_t *plen) { + if (pkt == NULL || buflen < sizeof(struct iphdr)) { + lgerror("ip4_payload_split: pkt|buflen", -EINVAL); + return -EINVAL; + } + + struct iphdr *hdr = (struct iphdr *)pkt; + if (hdr->version != IPVERSION) { + lgerror("ip4_payload_split: ipversion", -EINVAL); + return -EINVAL; + } + + uint32_t hdr_len = hdr->ihl * 4; + uint32_t pktlen = ntohs(hdr->tot_len); + if (buflen < pktlen || hdr_len > pktlen) { + lgerror("ip4_payload_split: buflen cmp pktlen", -EINVAL); + 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(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) { + struct iphdr *hdr; + uint32_t hdr_len; + struct tcphdr *thdr; + uint32_t thdr_len; + + uint8_t *tcph_pl; + uint32_t tcph_plen; + + if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len, + &tcph_pl, &tcph_plen)){ + return -EINVAL; + } + + + if ( + hdr->protocol != IPPROTO_TCP || + tcph_plen < sizeof(struct tcphdr)) { + return -EINVAL; + } + + + thdr = (struct tcphdr *)(tcph_pl); + thdr_len = thdr->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; +} + +int udp4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + struct udphdr **udph, + uint8_t **payload, uint32_t *plen) { + struct iphdr *hdr; + uint32_t hdr_len; + struct udphdr *uhdr; + uint32_t uhdr_len; + + uint8_t *ip_ph; + uint32_t ip_phlen; + + if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len, + &ip_ph, &ip_phlen)){ + return -EINVAL; + } + + + if ( + hdr->protocol != IPPROTO_UDP || + ip_phlen < sizeof(struct udphdr)) { + return -EINVAL; + } + + + uhdr = (struct udphdr *)(ip_ph); + if (uhdr->len != 0 && ntohs(uhdr->len) != ip_phlen) { + return -EINVAL; + } + + if (iph) *iph = hdr; + if (iph_len) *iph_len = hdr_len; + if (udph) *udph = uhdr; + if (payload) *payload = ip_ph + sizeof(struct udphdr); + if (plen) *plen = ip_phlen - sizeof(struct udphdr); + + return 0; +} + +// split packet to two ipv4 fragments. +int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, + uint8_t *frag1, uint32_t *f1len, + uint8_t *frag2, uint32_t *f2len) { + + struct iphdr *hdr; + const uint8_t *payload; + uint32_t plen; + uint32_t hdr_len; + int ret; + + if (!frag1 || !f1len || !frag2 || !f2len) + return -EINVAL; + + if ((ret = ip4_payload_split( + (uint8_t *)pkt, buflen, + &hdr, &hdr_len, (uint8_t **)&payload, &plen)) < 0) { + lgerror("ipv4_frag: TCP Header extract error", ret); + return -EINVAL; + } + + if (plen <= payload_offset) { + return -EINVAL; + } + + if (payload_offset & ((1 << 3) - 1)) { + lgerror("ipv4_frag: Payload offset MUST be a multiply of 8!", -EINVAL); + + return -EINVAL; + } + + uint32_t f1_plen = payload_offset; + uint32_t f1_dlen = f1_plen + hdr_len; + + uint32_t f2_plen = plen - payload_offset; + uint32_t 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; + + 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); + + + lgdebugmsg("Packet split in portion %u %u", f1_plen, f2_plen); + + ip4_set_checksum(f1_hdr); + ip4_set_checksum(f2_hdr); + + return 0; +} + +// split packet to two tcp-on-ipv4 segments. +int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, + uint8_t *seg1, uint32_t *s1len, + uint8_t *seg2, uint32_t *s2len) { + + struct iphdr *hdr; + uint32_t hdr_len; + struct tcphdr *tcph; + uint32_t tcph_len; + uint32_t plen; + const uint8_t *payload; + int ret; + + if (!seg1 || !s1len || !seg2 || !s2len) + return -EINVAL; + + if ((ret = tcp4_payload_split((uint8_t *)pkt, buflen, + &hdr, &hdr_len, + &tcph, &tcph_len, + (uint8_t **)&payload, &plen)) < 0) { + lgerror("tcp4_frag: tcp4_payload_split", ret); + + return -EINVAL; + } + + + if ( + ntohs(hdr->frag_off) & IP_MF || + ntohs(hdr->frag_off) & IP_OFFMASK) { + lgdebugmsg("tcp4_frag: frag value: %d", + ntohs(hdr->frag_off)); + lgerror("tcp4_frag: ip fragmentation is set", -EINVAL); + return -EINVAL; + } + + + if (plen <= payload_offset) { + return -EINVAL; + } + + uint32_t s1_plen = payload_offset; + uint32_t s1_dlen = s1_plen + hdr_len + tcph_len; + + uint32_t s2_plen = plen - payload_offset; + uint32_t 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); + + lgdebugmsg("Packet split in portion %u %u", s1_plen, s2_plen); + + tcp4_set_checksum(s1_tcph, s1_hdr); + tcp4_set_checksum(s2_tcph, s2_hdr); + + return 0; +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..e0f512e --- /dev/null +++ b/utils.h @@ -0,0 +1,51 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "types.h" + +/** + * Splits the packet to two IP fragments on position payload_offset. + * payload_offset indicates the position relatively to start of IP payload + * (start of transport header) + */ +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); + +/** + * Splits the packet to two TCP segments on position payload_offset + * payload_offset indicates the position relatively to start of TCP payload. + */ +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); + +/** + * Splits the raw packet payload to ip header and ip payload. + */ +int ip4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + uint8_t **payload, uint32_t *plen); + +/** + * Splits the raw packet payload to ip header, tcp header and tcp payload. + */ +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); + +/** + * Splits the raw packet payload to ip header, udp header and udp payload. + */ +int udp4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + struct udphdr **udph, + uint8_t **payload, uint32_t *plen); + +void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph); +void ip4_set_checksum(struct iphdr *iph); + +#endif /* UTILS_H */ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 1b72351..f526acf 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -31,6 +31,7 @@ #include "config.h" #include "mangle.h" #include "args.h" +#include "utils.h" pthread_mutex_t rawsocket_lock; int rawsocket = -2;