Add initial support for QUIC, improve logging capabilities.

Add TRACE logging mode
This commit is contained in:
Vadim Vetrov 2024-08-13 20:48:35 +03:00
parent 4a8f0d18a9
commit f3db464b97
No known key found for this signature in database
GPG Key ID: E8A308689D7A73A5
12 changed files with 628 additions and 379 deletions

24
args.c
View File

@ -32,10 +32,11 @@ struct config_t config = {
#endif #endif
#ifdef DEBUG #ifdef DEBUG
.verbose = true, .verbose = 1,
#else #else
.verbose = false, .verbose = 0,
#endif #endif
.domains_str = defaul_snistr, .domains_str = defaul_snistr,
.domains_strlen = sizeof(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_REVERSE 12
#define OPT_FRAG_SNI_FAKED 13 #define OPT_FRAG_SNI_FAKED 13
#define OPT_FK_WINSIZE 14 #define OPT_FK_WINSIZE 14
#define OPT_TRACE 15
#define OPT_QUIC_DROP 16
#define OPT_SEG2DELAY 5 #define OPT_SEG2DELAY 5
#define OPT_THREADS 6 #define OPT_THREADS 6
#define OPT_SILENT 7 #define OPT_SILENT 7
#define OPT_NO_GSO 8 #define OPT_NO_GSO 8
#define OPT_QUEUE_NUM 9 #define OPT_QUEUE_NUM 9
#define OPT_MAX OPT_FRAG_SNI_FAKED #define OPT_MAX OPT_QUIC_DROP
static struct option long_opt[] = { static struct option long_opt[] = {
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
@ -73,9 +76,11 @@ static struct option long_opt[] = {
{"frag-sni-reverse", 1, 0, OPT_FRAG_SNI_REVERSE}, {"frag-sni-reverse", 1, 0, OPT_FRAG_SNI_REVERSE},
{"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED}, {"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED},
{"fk-winsize", 1, 0, OPT_FK_WINSIZE}, {"fk-winsize", 1, 0, OPT_FK_WINSIZE},
{"quic-drop", 0, 0, OPT_QUIC_DROP},
{"seg2delay", 1, 0, OPT_SEG2DELAY}, {"seg2delay", 1, 0, OPT_SEG2DELAY},
{"threads", 1, 0, OPT_THREADS}, {"threads", 1, 0, OPT_THREADS},
{"silent", 0, 0, OPT_SILENT}, {"silent", 0, 0, OPT_SILENT},
{"trace", 0, 0, OPT_TRACE},
{"no-gso", 0, 0, OPT_NO_GSO}, {"no-gso", 0, 0, OPT_NO_GSO},
{"queue-num", 1, 0, OPT_QUEUE_NUM}, {"queue-num", 1, 0, OPT_QUEUE_NUM},
{0,0,0,0} {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-reverse={0|1}\n");
printf("\t--frag-sni-faked={0|1}\n"); printf("\t--frag-sni-faked={0|1}\n");
printf("\t--fk-winsize=<winsize>\n"); printf("\t--fk-winsize=<winsize>\n");
printf("\t--quic-drop\n");
printf("\t--seg2delay=<delay>\n"); printf("\t--seg2delay=<delay>\n");
printf("\t--threads=<threads number>\n"); printf("\t--threads=<threads number>\n");
printf("\t--silent\n"); printf("\t--silent\n");
printf("\t--trace\n");
printf("\t--no-gso\n"); printf("\t--no-gso\n");
printf("\n"); printf("\n");
} }
@ -140,12 +147,18 @@ int parse_args(int argc, char *argv[]) {
case 'v': case 'v':
print_version(); print_version();
goto stop_exec; goto stop_exec;
case OPT_TRACE:
config.verbose = 2;
break;
case OPT_SILENT: case OPT_SILENT:
config.verbose = 0; config.verbose = 0;
break; break;
case OPT_NO_GSO: case OPT_NO_GSO:
config.use_gso = 0; config.use_gso = 0;
break; break;
case OPT_QUIC_DROP:
config.quic_drop = 1;
break;
case OPT_SNI_DOMAINS: case OPT_SNI_DOMAINS:
if (!strcmp(optarg, "all")) { if (!strcmp(optarg, "all")) {
config.all_domains = 1; config.all_domains = 1;
@ -329,7 +342,12 @@ void print_welcome() {
printf("GSO is enabled\n"); printf("GSO is enabled\n");
} }
if (config.quic_drop) {
printf("All QUIC packets will be dropped\n");
}
if (config.all_domains) { if (config.all_domains) {
printf("All Client Hello will be targetted by youtubeUnblock!\n"); printf("All Client Hello will be targetted by youtubeUnblock!\n");
} }
} }

View File

@ -25,7 +25,11 @@ struct config_t {
unsigned char faking_ttl; unsigned char faking_ttl;
int fake_sni; int fake_sni;
unsigned int fake_sni_seq_len; unsigned int fake_sni_seq_len;
#define VERBOSE_INFO 0
#define VERBOSE_DEBUG 1
#define VERBOSE_TRACE 2
int verbose; int verbose;
int quic_drop;
/* In milliseconds */ /* In milliseconds */
unsigned int seg2_delay; unsigned int seg2_delay;
const char *domains_str; const char *domains_str;

39
logging.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef LOGGING_H
#define LOGGING_H
#include "config.h"
#define LOG_LEVEL (config.verbose)
#ifdef KERNEL_SPACE
#include <linux/printk.h>
#define printf pr_info
#define perror pr_err
#define lgerror(msg, ret, ...) __extension__ ({ \
printf(msg ": %d\n", ##__VA_ARGS__, ret); \
})
#else
#include <stdio.h> // IWYU pragma: export
#include <errno.h>
#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 */

397
mangle.c
View File

@ -2,22 +2,50 @@
#include "types.h" // IWYU pragma: keep #include "types.h" // IWYU pragma: keep
#include "mangle.h" #include "mangle.h"
#include "config.h" #include "config.h"
#include "utils.h"
#include "quic.h"
#include "logging.h"
#ifdef KERNEL_SPACE #ifndef KERNEL_SCOPE
#include <linux/ip.h>
#else
#include <stdlib.h> #include <stdlib.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#endif #endif
int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) {
if (raw_payload_len > MAX_PACKET_SIZE) { if (raw_payload_len > MAX_PACKET_SIZE) {
return PKT_ACCEPT; 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; const struct iphdr *iph;
uint32_t iph_len; uint32_t iph_len;
const struct tcphdr *tcph; 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); struct tls_verdict vrd = analyze_tls_data(data, dlen);
if (vrd.target_sni) { if (vrd.target_sni) {
if (config.verbose) lgdebugmsg("Target SNI detected: %.*s", vrd.sni_len, data + vrd.sni_offset);
printf("Target SNI detected: %.*s\n", vrd.sni_len, data + vrd.sni_offset);
uint8_t payload[MAX_PACKET_SIZE]; uint8_t payload[MAX_PACKET_SIZE];
uint32_t payload_len = raw_payload_len; 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) { 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) { if (config.fake_sni) {
@ -126,6 +153,79 @@ drop:
return PKT_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) { 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 (poses_sz == 0) {
if (config.seg2_delay && ((dvs > 0) ^ config.frag_sni_reverse)) { 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; int ret;
if (dvs > poses[0]) { 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; 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); frag1, &f1len, frag2, &f2len);
if (ret < 0) { if (ret < 0) {
lgerror("send_frags: frag", ret); lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs);
printf("Error context: packet with size %d, position: %d, recursive dvs: %d\n", pktlen, poses[0], dvs);
return ret; return ret;
} }
@ -214,7 +313,7 @@ int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *pose
int ret; int ret;
if (dvs > poses[0]) { 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; 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); frag1, &f1len, frag2, &f2len);
if (ret < 0) { if (ret < 0) {
lgerror("send_frags: frag", ret); lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs);
printf("Error context: packet with size %d, position: %d, recursive dvs: %d\n", pktlen, poses[0], dvs);
return ret; return ret;
} }
@ -330,269 +428,7 @@ int post_fake_sni(const struct iphdr *iph, unsigned int iph_len,
return 0; 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_CONTENT_TYPE_HANDSHAKE 0x16
#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 #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) { if (config.faking_strategy == FAKE_STRAT_ACK_SEQ) {
#ifdef KERNEL_SCOPE
tcph->seq = 124;
tcph->ack_seq = 124;
#else
tcph->seq = random(); tcph->seq = random();
tcph->ack_seq = random(); tcph->ack_seq = random();
#endif
} else if (config.faking_strategy == FAKE_STRAT_TTL) { } else if (config.faking_strategy == FAKE_STRAT_TTL) {
iph->ttl = config.faking_ttl; iph->ttl = config.faking_ttl;
} }

View File

@ -3,30 +3,6 @@
#include "types.h" #include "types.h"
#ifdef KERNEL_SPACE
#include <linux/string.h>
#include <linux/stddef.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <asm/byteorder.h>
/* from <netinet/ip.h> */
#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 <stdint.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
/** /**
* Result of analyze_tls_data function * 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); 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 * 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); 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. * Sends fake client hello.
*/ */

17
quic.c
View File

@ -1,9 +1,6 @@
#include "quic.h" #include "quic.h"
#include "logging.h"
static const uint32_t supported_versions[] = {
1, // version 1, RFC 9000
0x6b3343cf, // version 2, RFC 9369
};
/** /**
* Packet number. * 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; struct quic_lhdr *nqch = (struct quic_lhdr *)raw_payload;
uint32_t left_len = raw_payload_len - sizeof(struct quic_lhdr); uint32_t left_len = raw_payload_len - sizeof(struct quic_lhdr);
uint8_t *cur_rawptr = raw_payload + 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; return -EPROTO;
}
uint8_t found = 0; uint8_t found = 0;
for (uint8_t i = 0; i < sizeof(supported_versions); i++) { 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; found = 1;
} }
} }
if (!found) if (!found) {
lgtrace_addp("quic version undefined %d", ntohl(nqch->version));
return -EPROTO; return -EPROTO;
}
lgtrace_addp("quic version valid %d", ntohl(nqch->version));
if (left_len < 2) goto invalid_packet; if (left_len < 2) goto invalid_packet;
struct quic_cids nqci = {0}; struct quic_cids nqci = {0};

55
quic.h
View File

@ -2,6 +2,7 @@
#define QUIC_H #define QUIC_H
#include "types.h" #include "types.h"
/** /**
* @macro * @macro
* *
@ -22,38 +23,52 @@
"\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb" \ "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb" \
"\xf9\xbd\x2e\xd9" "\xf9\xbd\x2e\xd9"
#define QUIC_INITIAL_TYPE_V1 0x00 #define QUIC_INITIAL_TYPE 0
#define QUIC_0_RTT_TYPE_V1 0x01 #define QUIC_0_RTT_TYPE 1
#define QUIC_HANDSHAKE_TYPE_V1 0x02 #define QUIC_HANDSHAKE_TYPE 2
#define QUIC_RETRY_TYPE_V1 0x03 #define QUIC_RETRY_TYPE 3
#define QUIC_INITIAL_TYPE_V2 0b01 #define QUIC_INITIAL_TYPE_V1 0b00
#define QUIC_0_RTT_TYPE_V2 0b10 #define QUIC_0_RTT_TYPE_V1 0b01
#define QUIC_HANDSHAKE_TYPE_V2 0b11 #define QUIC_HANDSHAKE_TYPE_V1 0b10
#define QUIC_RETRY_TYPE_V2 0b00 #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 * Quic Large Header
*/ */
struct quic_lhdr { struct quic_lhdr {
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t number_length: 2; uint8_t number_length:2;
uint8_t reserved: 2; uint8_t reserved:2;
uint8_t type: 2; uint8_t type:2;
uint8_t fixed: 1; uint8_t fixed:1;
uint8_t form: 1; uint8_t form:1;
#elif __BYTE_ORDER == __BIG_ENDIAN #elif __BYTE_ORDER == __BIG_ENDIAN
uint8_t form: 1; uint8_t form:1;
uint8_t fixed: 1; uint8_t fixed:1;
uint8_t type: 2; uint8_t type:2;
uint8_t reserved: 2; uint8_t reserved:2;
uint8_t number_length: 2; uint8_t number_length:2;
#else #else
#error "Undefined endian" #error "Undefined endian"
#endif #endif
uint32_t version; uint32_t version;
}; }__attribute__((packed));
/** /**
* Quic Large Header Ids * Quic Large Header Ids

31
types.h
View File

@ -15,22 +15,33 @@ typedef __i8 int8_t;
typedef __i16 int16_t; typedef __i16 int16_t;
typedef __i32 int32_t; typedef __i32 int32_t;
typedef __i64 int64_t; typedef __i64 int64_t;
#include <linux/printk.h>
#define printf pr_info
#define perror pr_err
#define lgerror(msg, ret) (pr_err(msg ": %d\n", ret))
#else /* USERSPACE_SCOPE */ #else /* USERSPACE_SCOPE */
#include <errno.h> // IWYU pragma: export #include <errno.h> // IWYU pragma: export
#include <stdint.h> // IWYU pragma: export #include <stdint.h> // IWYU pragma: export
#include <string.h> // IWYU pragma: export #include <string.h> // IWYU pragma: export
#include <stdio.h> // IWYU pragma: export
#define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);})
#endif /* SCOPES */ #endif /* SCOPES */
// Network specific structures
#ifdef KERNEL_SPACE
#include <linux/stddef.h> // IWYU pragma: export
#include <linux/net.h> // IWYU pragma: export
#include <linux/in.h> // IWYU pragma: export
#include <linux/ip.h> // IWYU pragma: export
#include <linux/tcp.h> // IWYU pragma: export
/* from <netinet/ip.h> */
#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 <arpa/inet.h> // IWYU pragma: export
#include <netinet/ip.h> // IWYU pragma: export
#include <netinet/tcp.h> // IWYU pragma: export
#include <netinet/udp.h> // IWYU pragma: export
#endif
#endif /* TYPES_H */ #endif /* TYPES_H */

View File

@ -22,7 +22,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF
APP:=$(BUILD_DIR)/youtubeUnblock 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) OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a

312
utils.c Normal file
View File

@ -0,0 +1,312 @@
#include "utils.h"
#include "logging.h"
#ifdef KERNEL_SPACE
#include <linux/ip.h>
#else
#include <stdlib.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#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;
}

51
utils.h Normal file
View File

@ -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 */

View File

@ -31,6 +31,7 @@
#include "config.h" #include "config.h"
#include "mangle.h" #include "mangle.h"
#include "args.h" #include "args.h"
#include "utils.h"
pthread_mutex_t rawsocket_lock; pthread_mutex_t rawsocket_lock;
int rawsocket = -2; int rawsocket = -2;