From d530dd26d1f0df70f8d03025dbaee75a8c7c5789 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 27 Aug 2024 19:27:27 +0300 Subject: [PATCH] Support for ipv6 --- logging.h | 5 +- mangle.c | 179 ++++++++++++++++++++++++++++++++------------- mangle.h | 11 +-- types.h | 1 + utils.c | 180 +++++++++++++++++++++++++++++++++++++++------- utils.h | 45 +++++++++++- youtubeUnblock.c | 184 +++++++++++++++++++++++++++++++++++++---------- 7 files changed, 485 insertions(+), 120 deletions(-) diff --git a/logging.h b/logging.h index 60b21eb..41a8c28 100644 --- a/logging.h +++ b/logging.h @@ -20,9 +20,10 @@ }) #endif /* PROGRAM_SPACE */ +#define lgdebug(msg, ...) \ +(LOG_LEVEL >= VERBOSE_DEBUG ? printf(msg, ##__VA_ARGS__) : 0) -#define lgdebugmsg(msg, ...) \ -(LOG_LEVEL >= VERBOSE_DEBUG ? printf(msg "\n", ##__VA_ARGS__) : 0) +#define lgdebugmsg(msg, ...) lgdebug(msg "\n", ##__VA_ARGS__) #define lgtracemsg(msg, ...) \ (LOG_LEVEL >= VERBOSE_TRACE ? printf(msg "\n", ##__VA_ARGS__) : 0) diff --git a/mangle.c b/mangle.c index bfb1932..b160e0f 100644 --- a/mangle.c +++ b/mangle.c @@ -16,23 +16,44 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { } const struct iphdr *iph; + const struct ip6_hdr *ip6h; uint32_t iph_len; const uint8_t *ip_payload; uint32_t ip_payload_len; + int transport_proto = -1; + int ipver = netproto_version(raw_payload, raw_payload_len); int ret; - ret = ip4_payload_split((uint8_t *)raw_payload, raw_payload_len, + if (ipver == IP4VERSION) { + 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; + + transport_proto = iph ->protocol; + + } else if (ipver == IP6VERSION) { + ret = ip6_payload_split((uint8_t *)raw_payload, raw_payload_len, + (struct ip6_hdr **)&ip6h, &iph_len, + (uint8_t **)&ip_payload, &ip_payload_len); + + if (ret < 0) + goto accept; + + transport_proto = ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt; + + } + if (ret < 0) goto accept; - switch (iph->protocol) { + switch (transport_proto) { case IPPROTO_TCP: - return process_tcp4_packet(raw_payload, raw_payload_len); + return process_tcp_packet(raw_payload, raw_payload_len); case IPPROTO_UDP: return process_udp4_packet(raw_payload, raw_payload_len); default: @@ -45,18 +66,23 @@ 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; +int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { const struct tcphdr *tcph; uint32_t tcph_len; const uint8_t *data; uint32_t dlen; - int ret = tcp4_payload_split((uint8_t *)raw_payload, raw_payload_len, - (struct iphdr **)&iph, &iph_len, (struct tcphdr **)&tcph, &tcph_len, + int ipxv = netproto_version(raw_payload, raw_payload_len); + + lgtrace_start("TCP"); + lgtrace_addp("IPv%d", ipxv); + + int ret = tcp_payload_split((uint8_t *)raw_payload, raw_payload_len, + NULL, NULL, + (struct tcphdr **)&tcph, &tcph_len, (uint8_t **)&data, &dlen); + if (ret < 0) { goto accept; } @@ -70,24 +96,28 @@ int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { uint32_t payload_len = raw_payload_len; memcpy(payload, raw_payload, raw_payload_len); - struct iphdr *iph; + void *iph; uint32_t iph_len; struct tcphdr *tcph; uint32_t tcph_len; uint8_t *data; uint32_t dlen; - int ret = tcp4_payload_split(payload, payload_len, + int ret = tcp_payload_split(payload, payload_len, &iph, &iph_len, &tcph, &tcph_len, &data, &dlen); + if (ret < 0) { + lgerror("tcp_payload_split in targ_sni", ret); + goto accept; + } + if (config.fk_winsize) { tcph->window = htons(config.fk_winsize); } - ip4_set_checksum(iph); - tcp4_set_checksum(tcph, iph); - + set_ip_checksum(iph, iph_len); + set_tcp_checksum(tcph, iph, iph_len); if (dlen > 1480 && config.verbose) { lgdebugmsg("WARNING! Client Hello packet is too big and may cause issues!"); @@ -123,7 +153,7 @@ int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { poses[1] = tmp; } - ret = send_tcp4_frags(payload, payload_len, poses, cnt, 0); + ret = send_tcp_frags(payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("tcp4 send frags", ret); goto accept; @@ -132,7 +162,8 @@ int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { goto drop; } break; - case FRAG_STRAT_IP: { + case FRAG_STRAT_IP: + if (ipxv != IP4VERSION) { ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset; mid_offset = ipd_offset + vrd.sni_len / 2; mid_offset += 8 - mid_offset % 8; @@ -163,8 +194,10 @@ int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { } goto drop; + break; + } else { + printf("WARNING: IP fragmentation is supported only for IPv4\n"); } - break; default: ret = instance_config.send_raw_packet(payload, payload_len); if (ret < 0) { @@ -181,8 +214,14 @@ int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { } accept: + lgtrace_addp("accept"); + lgtrace_end(); + return PKT_ACCEPT; drop: + lgtrace_addp("drop"); + lgtrace_end(); + return PKT_DROP; } @@ -321,7 +360,7 @@ out: return 0; } -int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_sz, uint32_t dvs) { +int send_tcp_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)) { if (!instance_config.send_delayed_packet) { @@ -333,6 +372,7 @@ int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *pose return 0; } else { + lgtrace_addp("raw send packet of %d bytes with %d dvs", pktlen, dvs); return instance_config.send_raw_packet( packet, pktlen); } @@ -350,20 +390,24 @@ int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *pose return -EINVAL; } - ret = tcp4_frag(packet, pktlen, poses[0] - dvs, + + ret = tcp_frag(packet, pktlen, poses[0] - dvs, frag1, &f1len, frag2, &f2len); + lgtrace_addp("Packet split in %d bytes position of payload start, dvs: %d to two packets of %d and %d lengths", poses[0], dvs, f1len, f2len); + if (ret < 0) { - lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs); + lgerror("send_frags: tcp_frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs); return ret; } + if (config.frag_sni_reverse) goto send_frag2; send_frag1: { - ret = send_tcp4_frags(frag1, f1len, NULL, 0, 0); + ret = send_tcp_frags(frag1, f1len, NULL, 0, 0); if (ret < 0) { return ret; } @@ -373,21 +417,22 @@ send_frag1: } send_fake: + // TODO if (config.frag_sni_faked) { uint32_t iphfl, tcphfl; - ret = tcp4_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); + ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); if (ret < 0) { lgerror("Invalid frag2", ret); return ret; } memcpy(fake_pad, frag2, iphfl + tcphfl); memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); - ret = fail4_packet(fake_pad, f2len); + ret = fail_packet(fake_pad, f2len); if (ret < 0) { lgerror("Failed to fail packet", ret); return ret; } - ret = send_tcp4_frags(fake_pad, f2len, NULL, 0, 0); + ret = send_tcp_frags(fake_pad, f2len, NULL, 0, 0); if (ret < 0) { return ret; } @@ -400,7 +445,7 @@ send_fake: send_frag2: { dvs += poses[0]; - ret = send_tcp4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); + ret = send_tcp_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { return ret; } @@ -413,28 +458,30 @@ out: return 0; } -int post_fake_sni(const struct iphdr *iph, unsigned int iph_len, +int post_fake_sni(const void *iph, unsigned int iph_len, const struct tcphdr *tcph, unsigned int tcph_len, unsigned char sequence_len) { - uint8_t rfsiph[60]; + uint8_t rfsiph[128]; uint8_t rfstcph[60]; int ret; memcpy(rfsiph, iph, iph_len); memcpy(rfstcph, tcph, tcph_len); - struct iphdr *fsiph = (void *)rfsiph; + void *fsiph = (void *)rfsiph; struct tcphdr *fstcph = (void *)rfstcph; for (int i = 0; i < sequence_len; i++) { uint8_t fake_sni[MAX_PACKET_SIZE]; uint32_t fsn_len = MAX_PACKET_SIZE; - ret = gen_fake_sni(fsiph, fstcph, fake_sni, &fsn_len); + ret = gen_fake_sni(fsiph, iph_len, fstcph, tcph_len, + fake_sni, &fsn_len); if (ret < 0) { lgerror("gen_fake_sni", ret); return ret; } + lgtrace_addp("post fake sni #%d", i + 1); ret = instance_config.send_raw_packet(fake_sni, fsn_len); if (ret < 0) { lgerror("send fake sni", ret); @@ -444,9 +491,10 @@ int post_fake_sni(const struct iphdr *iph, unsigned int iph_len, uint32_t iph_len; uint32_t tcph_len; uint32_t plen; - tcp4_payload_split( + tcp_payload_split( fake_sni, fsn_len, - &fsiph, &iph_len, &fstcph, &tcph_len, + &fsiph, &iph_len, + &fstcph, &tcph_len, NULL, &plen); @@ -692,42 +740,64 @@ brute: goto out; } - -int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, +int gen_fake_sni(const void *ipxh, uint32_t iph_len, + const struct tcphdr *tcph, uint32_t tcph_len, uint8_t *buf, uint32_t *buflen) { - if (!iph || !tcph || !buf || !buflen) + if (!ipxh || !tcph || !buf || !buflen) return -EINVAL; - int ip_len = iph->ihl * 4; - int tcph_len = tcph->doff * 4; + int ipxv = netproto_version(ipxh, iph_len); + + if (ipxv == IP4VERSION) { + const struct iphdr *iph = ipxh; + + memcpy(buf, iph, iph_len); + struct iphdr *niph = (struct iphdr *)buf; + + niph->protocol = IPPROTO_TCP; + } else if (ipxv == IP6VERSION) { + const struct ip6_hdr *iph = ipxh; + + iph_len = sizeof(struct ip6_hdr); + memcpy(buf, iph, iph_len); + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + + niph->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_TCP; + } else { + return -EINVAL; + } const char *data = config.fake_sni_pkt; size_t data_len = config.fake_sni_pkt_sz; - size_t dlen = ip_len + tcph_len + data_len; + size_t dlen = iph_len + tcph_len + data_len; if (*buflen < dlen) return -ENOMEM; - memcpy(buf, iph, ip_len); - memcpy(buf + ip_len, tcph, tcph_len); - memcpy(buf + ip_len + tcph_len, data, data_len); + memcpy(buf + iph_len, tcph, tcph_len); + memcpy(buf + iph_len + tcph_len, data, data_len); - struct iphdr *niph = (struct iphdr *)buf; - struct tcphdr *ntcph = (struct tcphdr *)(buf + ip_len); + struct tcphdr *ntcph = (struct tcphdr *)(buf + iph_len); - niph->protocol = IPPROTO_TCP; - niph->tot_len = htons(dlen); + if (ipxv == IP4VERSION) { + struct iphdr *niph = (struct iphdr *)buf; + niph->tot_len = htons(dlen); + } else if (ipxv == IP6VERSION) { + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + niph->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(dlen - iph_len); + } - fail4_packet(buf, *buflen); + fail_packet(buf, *buflen); *buflen = dlen; + return 0; } -int fail4_packet(uint8_t *payload, uint32_t plen) { - struct iphdr *iph; +int fail_packet(uint8_t *payload, uint32_t plen) { + void *iph; uint32_t iph_len; struct tcphdr *tcph; uint32_t tcph_len; @@ -735,7 +805,7 @@ int fail4_packet(uint8_t *payload, uint32_t plen) { uint32_t dlen; int ret; - ret = tcp4_payload_split(payload, plen, + ret = tcp_payload_split(payload, plen, &iph, &iph_len, &tcph, &tcph_len, &data, &dlen); @@ -743,6 +813,7 @@ int fail4_packet(uint8_t *payload, uint32_t plen) { return ret; } + if (config.faking_strategy == FAKE_STRAT_RAND_SEQ) { #ifdef KERNEL_SCOPE tcph->seq = 124; @@ -754,11 +825,19 @@ int fail4_packet(uint8_t *payload, uint32_t plen) { } else if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { tcph->seq = htonl(ntohl(tcph->seq) - dlen); } else if (config.faking_strategy == FAKE_STRAT_TTL) { - iph->ttl = config.faking_ttl; + uint32_t ipxv = netproto_version(payload, plen); + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->ttl = config.faking_ttl; + } else if (ipxv == IP6VERSION) { + ((struct ip6_hdr *)iph)->ip6_ctlun.ip6_un1.ip6_un1_hlim = config.faking_ttl; + } else { + lgerror("fail_packet: IP version is unsupported", -EINVAL); + return -EINVAL; + } } - ip4_set_checksum(iph); - tcp4_set_checksum(tcph, iph); + set_ip_checksum(iph, iph_len); + set_tcp_checksum(tcph, iph, iph_len); if (config.faking_strategy == FAKE_STRAT_TCP_CHECK) { tcph->check += 1; diff --git a/mangle.h b/mangle.h index 2196255..da0de99 100644 --- a/mangle.h +++ b/mangle.h @@ -22,14 +22,15 @@ struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); /** * Generates fake client hello message */ -int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, +int gen_fake_sni(const void *iph, uint32_t iph_len, + const struct tcphdr *tcph, uint32_t tcph_len, uint8_t *buf, uint32_t *buflen); /** * Invalidates the raw packet. The function aims to invalid the packet * in such way as it will be accepted by DPI, but dropped by target server */ -int fail4_packet(uint8_t *payload, uint32_t plen); +int fail_packet(uint8_t *payload, uint32_t plen); #define PKT_ACCEPT 0 #define PKT_DROP 1 @@ -45,7 +46,7 @@ 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); +int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len); /** @@ -57,7 +58,7 @@ int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen); /** * Sends fake client hello. */ -int post_fake_sni(const struct iphdr *iph, unsigned int iph_len, +int post_fake_sni(const void *iph, unsigned int iph_len, const struct tcphdr *tcph, unsigned int tcph_len, unsigned char sequence_len); @@ -66,7 +67,7 @@ int post_fake_sni(const struct iphdr *iph, unsigned int iph_len, * Poses are relative to start of TCP payload. * dvs used internally and should be zero. */ -int send_tcp4_frags( +int send_tcp_frags( const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_len, uint32_t dvs); diff --git a/types.h b/types.h index 8978cf6..b177e96 100644 --- a/types.h +++ b/types.h @@ -41,6 +41,7 @@ typedef __i64 int64_t; #define USER_SPACE #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export #endif diff --git a/utils.c b/utils.c index dee16d9..e727d61 100644 --- a/utils.c +++ b/utils.c @@ -1,11 +1,13 @@ #include "utils.h" #include "logging.h" +#include #ifdef KERNEL_SPACE #include #else #include #include +#include #include #endif @@ -34,6 +36,36 @@ void ip4_set_checksum(struct iphdr *iph) #endif } +void tcp6_set_checksum(struct tcphdr *tcph, struct ip6_hdr *iph) { + uint16_t old_check = ntohs(tcph->check); + + nfq_tcp_compute_checksum_ipv6(tcph, iph); +} + +int set_ip_checksum(void *iph, uint32_t iphb_len) { + int ipvx = netproto_version(iph, iphb_len); + + if (ipvx == IP4VERSION) { + ip4_set_checksum(iph); + } else if (ipvx == IP6VERSION) { // IP6 has no checksums + } else + return -1; + + return 0; +} + +int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len) { + int ipvx = netproto_version(iph, iphb_len); + + if (ipvx == IP4VERSION) { + tcp4_set_checksum(tcph, iph); + } else if (ipvx == IP6VERSION) { + tcp6_set_checksum(tcph, iph); + } else + return -1; + + return 0; +} int ip4_payload_split(uint8_t *pkt, uint32_t buflen, struct iphdr **iph, uint32_t *iph_len, @@ -44,7 +76,7 @@ int ip4_payload_split(uint8_t *pkt, uint32_t buflen, } struct iphdr *hdr = (struct iphdr *)pkt; - if (hdr->version != IPVERSION) { + if (netproto_version(pkt, buflen) != IP4VERSION) { lgerror("ip4_payload_split: ipversion", -EINVAL); return -EINVAL; } @@ -110,6 +142,97 @@ int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, return 0; } +int ip6_payload_split(uint8_t *pkt, uint32_t buflen, + struct ip6_hdr **iph, uint32_t *iph_len, + uint8_t **payload, uint32_t *plen) { + if (pkt == NULL || buflen < sizeof(struct ip6_hdr)) { + lgerror("ip6_payload_split: pkt|buflen", -EINVAL); + return -EINVAL; + } + + struct ip6_hdr *hdr = (struct ip6_hdr *)pkt; + if (netproto_version(pkt, buflen) != 6) { + lgerror("ip6_payload_split: ip6version", -EINVAL); + return -EINVAL; + } + + uint32_t hdr_len = sizeof(struct ip6_hdr); + uint32_t pktlen = ntohs(hdr->ip6_ctlun.ip6_un1.ip6_un1_plen); + if (buflen < pktlen) { + lgerror("ip6_payload_split: buflen cmp pktlen: %d %d", -EINVAL, buflen, pktlen); + return -EINVAL; + } + + if (iph) + *iph = hdr; + if (iph_len) + *iph_len = hdr_len; + if (payload) + *payload = pkt + hdr_len; + if (plen) + *plen = pktlen; + + return 0; +} + +int tcp6_payload_split(uint8_t *pkt, uint32_t buflen, + struct ip6_hdr **iph, uint32_t *iph_len, + struct tcphdr **tcph, uint32_t *tcph_len, + uint8_t **payload, uint32_t *plen) { + struct ip6_hdr *hdr; + uint32_t hdr_len; + struct tcphdr *thdr; + uint32_t thdr_len; + + uint8_t *tcph_pl; + uint32_t tcph_plen; + + if (ip6_payload_split(pkt, buflen, &hdr, &hdr_len, + &tcph_pl, &tcph_plen)){ + return -EINVAL; + } + + + if ( + hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt != 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 tcp_payload_split(uint8_t *pkt, uint32_t buflen, + void **iph, uint32_t *iph_len, + struct tcphdr **tcph, uint32_t *tcph_len, + uint8_t **payload, uint32_t *plen) { + int netvers = netproto_version(pkt, buflen); + if (netvers == IP4VERSION) { + return tcp4_payload_split(pkt, buflen, (struct iphdr **)iph, iph_len, tcph, tcph_len, payload, plen); + } else if (netvers == IP6VERSION) { + return tcp6_payload_split(pkt, buflen, (struct ip6_hdr **)iph, iph_len, tcph, tcph_len, payload, plen); + } else { + lgerror("Internet Protocol version is unsupported", -EINVAL); + return -EINVAL; + } +} + + int udp4_payload_split(uint8_t *pkt, uint32_t buflen, struct iphdr **iph, uint32_t *iph_len, struct udphdr **udph, @@ -222,9 +345,6 @@ int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, 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); @@ -232,11 +352,12 @@ int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, } // split packet to two tcp-on-ipv4 segments. -int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, +int tcp_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; + // struct ip6_hdr *hdr6; + void *hdr; uint32_t hdr_len; struct tcphdr *tcph; uint32_t tcph_len; @@ -247,23 +368,28 @@ int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, if (!seg1 || !s1len || !seg2 || !s2len) return -EINVAL; - if ((ret = tcp4_payload_split((uint8_t *)pkt, buflen, + if ((ret = tcp_payload_split((uint8_t *)pkt, buflen, &hdr, &hdr_len, &tcph, &tcph_len, (uint8_t **)&payload, &plen)) < 0) { - lgerror("tcp4_frag: tcp4_payload_split", ret); + lgerror("tcp_frag: tcp_payload_split", ret); return -EINVAL; } + int ipvx = netproto_version(pkt, buflen); - 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 (ipvx == IP4VERSION) { + struct iphdr *iphdr = hdr; + if ( + ntohs(iphdr->frag_off) & IP_MF || + ntohs(iphdr->frag_off) & IP_OFFMASK) { + lgdebugmsg("tcp_frag: ip4: frag value: %d", + ntohs(iphdr->frag_off)); + lgerror("tcp_frag: ip4: ip fragmentation is set", -EINVAL); + return -EINVAL; + } } @@ -292,21 +418,25 @@ int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, 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; + if (ipvx == IP4VERSION) { + struct iphdr *s1_hdr = (void *)seg1; + struct iphdr *s2_hdr = (void *)seg2; + s1_hdr->tot_len = htons(s1_dlen); + s2_hdr->tot_len = htons(s2_dlen); + } else { + struct ip6_hdr *s1_hdr = (void *)seg1; + struct ip6_hdr *s2_hdr = (void *)seg2; + s1_hdr->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(s1_dlen - hdr_len); + s2_hdr->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(s2_dlen - hdr_len); + } 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); + set_tcp_checksum(s1_tcph, seg1, hdr_len); + set_tcp_checksum(s2_tcph, seg2, hdr_len); return 0; } diff --git a/utils.h b/utils.h index e0f512e..f5b6c04 100644 --- a/utils.h +++ b/utils.h @@ -3,6 +3,9 @@ #include "types.h" +#define IP4VERSION 4 +#define IP6VERSION 6 + /** * Splits the packet to two IP fragments on position payload_offset. * payload_offset indicates the position relatively to start of IP payload @@ -17,11 +20,16 @@ int ip4_frag(const uint8_t *pkt, uint32_t pktlen, * 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, +// 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 tcp_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. */ @@ -29,6 +37,14 @@ int ip4_payload_split(uint8_t *pkt, uint32_t buflen, struct iphdr **iph, uint32_t *iph_len, uint8_t **payload, uint32_t *plen); +static inline int netproto_version(const uint8_t *pkt, uint32_t buflen) { + if (pkt == NULL || buflen == 0) + return -1; + + return (*pkt) >> 4; +} + + /** * Splits the raw packet payload to ip header, tcp header and tcp payload. */ @@ -37,6 +53,26 @@ int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, struct tcphdr **tcph, uint32_t *tcph_len, uint8_t **payload, uint32_t *plen); +/** + * Splits the raw packet payload to ip header and ip payload. + */ +int ip6_payload_split(uint8_t *pkt, uint32_t buflen, + struct ip6_hdr **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 tcp6_payload_split(uint8_t *pkt, uint32_t buflen, + struct ip6_hdr **iph, uint32_t *iph_len, + struct tcphdr **tcph, uint32_t *tcph_len, + uint8_t **payload, uint32_t *plen); + +int tcp_payload_split(uint8_t *pkt, uint32_t buflen, + void **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. */ @@ -47,5 +83,10 @@ int udp4_payload_split(uint8_t *pkt, uint32_t buflen, void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph); void ip4_set_checksum(struct iphdr *iph); +void ip6_set_checksum(struct ip6_hdr *iph); +void tcp6_set_checksum(struct tcphdr *tcph, struct ip6_hdr *iph); + +int set_ip_checksum(void *iph, uint32_t iphb_len); +int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len); #endif /* UTILS_H */ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 3c63db8..5876c5a 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -37,6 +37,9 @@ pthread_mutex_t rawsocket_lock; int rawsocket = -2; +pthread_mutex_t raw6socket_lock; +int raw6socket = -2; + static int open_socket(struct mnl_socket **_nl) { struct mnl_socket *nl = NULL; nl = mnl_socket_open(NETLINK_NETFILTER); @@ -123,6 +126,134 @@ static int close_raw_socket(void) { return 0; } +static int open_raw6_socket(void) { + if (raw6socket != -2) { + errno = EALREADY; + perror("Raw socket is already opened"); + return -1; + } + + raw6socket = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + if (rawsocket == -1) { + perror("Unable to create raw socket"); + return -1; + } + + int mark = RAWSOCKET_MARK; + if (setsockopt(raw6socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) + { + fprintf(stderr, "setsockopt(SO_MARK, %d) failed\n", mark); + return -1; + } + + int mst = pthread_mutex_init(&raw6socket_lock, NULL); + if (mst) { + fprintf(stderr, "Mutex err: %d\n", mst); + close(raw6socket); + errno = mst; + + return -1; + } + + + return raw6socket; +} + +static int close_raw6_socket(void) { + if (raw6socket < 0) { + errno = EALREADY; + perror("Raw socket is not set"); + return -1; + } + + if (close(raw6socket)) { + perror("Unable to close raw socket"); + pthread_mutex_destroy(&rawsocket_lock); + return -1; + } + + pthread_mutex_destroy(&raw6socket_lock); + + raw6socket = -2; + return 0; +} + +static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { + int ret; + if (pktlen > AVAILABLE_MTU) return -ENOMEM; + + struct iphdr *iph; + + if ((ret = ip4_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + errno = -ret; + return ret; + } + + struct sockaddr_in daddr = { + .sin_family = AF_INET, + /* Always 0 for raw socket */ + .sin_port = 0, + .sin_addr = { + .s_addr = iph->daddr + } + }; + + if (config.threads != 1) + pthread_mutex_lock(&rawsocket_lock); + + int sent = sendto(rawsocket, + pkt, pktlen, 0, + (struct sockaddr *)&daddr, sizeof(daddr)); + + if (config.threads != 1) + pthread_mutex_unlock(&rawsocket_lock); + + /* The function will return -errno on error as well as errno value set itself */ + if (sent < 0) sent = -errno; + + return sent; +} + +static int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { + int ret; + if (pktlen > AVAILABLE_MTU) return -ENOMEM; + + struct ip6_hdr *iph; + + if ((ret = ip6_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + errno = -ret; + return ret; + } + + struct sockaddr_in6 daddr = { + .sin6_family = AF_INET6, + /* Always 0 for raw socket */ + .sin6_port = 0, + .sin6_addr = iph->ip6_dst + }; + + tcp6_set_checksum((void *)(uint8_t *)pkt + sizeof(struct ip6_hdr), (void *)pkt); + + + if (config.threads != 1) + pthread_mutex_lock(&rawsocket_lock); + + int sent = sendto(raw6socket, + pkt, pktlen, 0, + (struct sockaddr *)&daddr, sizeof(daddr)); + + lgtrace_addp("rawsocket sent %d", sent); + + if (config.threads != 1) + pthread_mutex_unlock(&rawsocket_lock); + + /* The function will return -errno on error as well as errno value set itself */ + if (sent < 0) sent = -errno; + + return sent; +} static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { int ret; @@ -138,7 +269,7 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { switch (config.fragmentation_strategy) { case FRAG_STRAT_TCP: - if ((ret = tcp4_frag(pkt, pktlen, AVAILABLE_MTU-128, + if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, buff1, &buff1_size, buff2, &buff2_size)) < 0) { errno = -ret; @@ -175,39 +306,16 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { return sent; } + + int ipvx = netproto_version(pkt, pktlen); + if (ipvx == IP4VERSION) + return send_raw_ipv4(pkt, pktlen); + else if (ipvx == IP6VERSION) + return send_raw_ipv6(pkt, pktlen); - struct iphdr *iph; - - if ((ret = ip4_payload_split( - (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { - errno = -ret; - return ret; - } - - struct sockaddr_in daddr = { - .sin_family = AF_INET, - /* Always 0 for raw socket */ - .sin_port = 0, - .sin_addr = { - .s_addr = iph->daddr - } - }; - - if (config.threads != 1) - pthread_mutex_lock(&rawsocket_lock); - - int sent = sendto(rawsocket, - pkt, pktlen, 0, - (struct sockaddr *)&daddr, sizeof(daddr)); - - if (config.threads != 1) - pthread_mutex_unlock(&rawsocket_lock); - - /* The function will return -errno on error as well as errno value set itself */ - if (sent < 0) sent = -errno; - - return sent; + printf("proto version %d is unsupported\n", ipvx); + return -EINVAL; } @@ -471,6 +579,12 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } + if (open_raw6_socket() < 0) { + perror("Unable to open raw socket for ipv6"); + close_raw_socket(); + exit(EXIT_FAILURE); + } + struct queue_res *qres = &defqres; if (config.threads == 1) { @@ -503,10 +617,8 @@ int main(int argc, char *argv[]) { } } - if (close_raw_socket() < 0) { - perror("Unable to close raw socket"); - exit(EXIT_FAILURE); - } + close_raw_socket(); + close_raw6_socket(); return -qres->status; }