#define _GNU_SOURCE #include "types.h" // IWYU pragma: keep #include "mangle.h" #include "config.h" #include "utils.h" #include "quic.h" #include "logging.h" #ifndef KERNEL_SCOPE #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; 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; 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 && config.use_ipv6) { 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; } else { lgtracemsg("Unknown layer 3 protocol version: %d", ipver); goto accept; } switch (transport_proto) { case IPPROTO_TCP: return process_tcp_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_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { const void *ipxh; uint32_t iph_len; const struct tcphdr *tcph; uint32_t tcph_len; const uint8_t *data; uint32_t dlen; 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, (void *)&ipxh, &iph_len, (struct tcphdr **)&tcph, &tcph_len, (uint8_t **)&data, &dlen); if (ret < 0) { goto accept; } if (tcph->syn && config.synfake) { lgtrace_addp("TCP syn alter"); uint8_t payload[MAX_PACKET_SIZE]; memcpy(payload, ipxh, iph_len); memcpy(payload + iph_len, tcph, tcph_len); memcpy(payload + iph_len + tcph_len, config.fake_sni_pkt, config.fake_sni_pkt_sz); struct tcphdr *tcph = (struct tcphdr *)(payload + iph_len); if (ipxv == IP4VERSION) { struct iphdr *iph = (struct iphdr *)payload; iph->tot_len = htons(iph_len + tcph_len + config.fake_sni_pkt_sz); set_ip_checksum(payload, iph_len); set_tcp_checksum(tcph, iph, iph_len); } else if (ipxv == IP6VERSION) { struct ip6_hdr *ip6h = (struct ip6_hdr *)payload; ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen = ntohs(tcph_len + config.fake_sni_pkt_sz); set_ip_checksum(ip6h, iph_len); set_tcp_checksum(tcph, ip6h, iph_len); } instance_config.send_raw_packet(payload, iph_len + tcph_len + config.fake_sni_pkt_sz); goto drop; } struct tls_verdict vrd = analyze_tls_data(data, dlen); if (vrd.target_sni) { 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; memcpy(payload, raw_payload, raw_payload_len); void *iph; uint32_t iph_len; struct tcphdr *tcph; uint32_t tcph_len; uint8_t *data; uint32_t dlen; 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); } 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!"); } if (config.fake_sni) { post_fake_sni(iph, iph_len, tcph, tcph_len, config.fake_sni_seq_len); } size_t ipd_offset; size_t mid_offset; switch (config.fragmentation_strategy) { case FRAG_STRAT_TCP: { ipd_offset = vrd.sni_offset; mid_offset = ipd_offset + vrd.sni_len / 2; uint32_t poses[2]; int cnt = 0; if (config.frag_sni_pos && dlen > config.frag_sni_pos) { poses[cnt++] = config.frag_sni_pos; } if (config.frag_middle_sni) { poses[cnt++] = mid_offset; } if (cnt > 1 && poses[0] > poses[1]) { uint32_t tmp = poses[0]; poses[0] = poses[1]; poses[1] = tmp; } ret = send_tcp_frags(payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("tcp4 send frags", ret); goto accept; } goto drop; } break; 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; uint32_t poses[2]; int cnt = 0; if (config.frag_sni_pos && dlen > config.frag_sni_pos) { poses[cnt] = config.frag_sni_pos + ((char *)data - (char *)tcph); poses[cnt] += 8 - poses[cnt] % 8; cnt++; } if (config.frag_middle_sni) { poses[cnt++] = mid_offset; } if (cnt > 1 && poses[0] > poses[1]) { uint32_t tmp = poses[0]; poses[0] = poses[1]; poses[1] = tmp; } ret = send_ip4_frags(payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("ip4 send frags", ret); goto accept; } goto drop; break; } else { printf("WARNING: IP fragmentation is supported only for IPv4\n"); } default: ret = instance_config.send_raw_packet(payload, payload_len); if (ret < 0) { lgerror("raw pack send", ret); goto accept; } goto drop; } goto drop; } accept: lgtrace_addp("accept"); lgtrace_end(); return PKT_ACCEPT; drop: lgtrace_addp("drop"); lgtrace_end(); 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)) { if (!instance_config.send_delayed_packet) { return -EINVAL; } instance_config.send_delayed_packet( packet, pktlen, config.seg2_delay); return 0; } else { return instance_config.send_raw_packet( packet, pktlen); } } else { uint8_t frag1[MAX_PACKET_SIZE]; uint8_t frag2[MAX_PACKET_SIZE]; uint32_t f1len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE; int ret; if (dvs > poses[0]) { lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]); return -EINVAL; } ret = ip4_frag(packet, pktlen, poses[0] - dvs, frag1, &f1len, frag2, &f2len); if (ret < 0) { lgerror("send_frags: 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_ip4_frags(frag1, f1len, NULL, 0, 0); if (ret < 0) { return ret; } if (config.frag_sni_reverse) goto out; send_frag2: dvs += poses[0]; ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { return ret; } if (config.frag_sni_reverse) goto send_frag1; } out: return 0; } 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) { return -EINVAL; } instance_config.send_delayed_packet( packet, pktlen, config.seg2_delay); return 0; } else { lgtrace_addp("raw send packet of %d bytes with %d dvs", pktlen, dvs); return instance_config.send_raw_packet( packet, pktlen); } } else { uint8_t frag1[MAX_PACKET_SIZE]; uint8_t frag2[MAX_PACKET_SIZE]; uint8_t fake_pad[MAX_PACKET_SIZE]; uint32_t f1len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE; int ret; if (dvs > poses[0]) { lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]); return -EINVAL; } 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: 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_tcp_frags(frag1, f1len, NULL, 0, 0); if (ret < 0) { return ret; } if (config.frag_sni_reverse) goto out; } send_fake: // TODO if (config.frag_sni_faked) { uint32_t iphfl, tcphfl; 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); struct tcphdr *fakethdr = (void *)(fake_pad + iphfl); if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { lgtrace("frag fake sent with %u -> ", ntohl(fakethdr->seq)); fakethdr->seq = htonl(ntohl(fakethdr->seq) - dvs); lgtrace_addp("%u, ", ntohl(fakethdr->seq)); } ret = fail_packet(fake_pad, f2len); if (ret < 0) { lgerror("Failed to fail packet", ret); return ret; } ret = send_tcp_frags(fake_pad, f2len, NULL, 0, 0); if (ret < 0) { return ret; } } if (config.frag_sni_reverse) goto send_frag1; send_frag2: { dvs += poses[0]; ret = send_tcp_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { return ret; } if (config.frag_sni_reverse) goto send_fake; } } out: return 0; } 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[128]; uint8_t rfstcph[60]; int ret; memcpy(rfsiph, iph, iph_len); memcpy(rfstcph, tcph, tcph_len); 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, 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); return ret; } uint32_t iph_len; uint32_t tcph_len; uint32_t plen; tcp_payload_split( fake_sni, fsn_len, &fsiph, &iph_len, &fstcph, &tcph_len, NULL, &plen); fstcph->seq = htonl(ntohl(fstcph->seq) + plen); memcpy(rfsiph, fsiph, iph_len); memcpy(rfstcph, fstcph, tcph_len); fsiph = (void *)rfsiph; fstcph = (void *)rfstcph; } return 0; } void z_function(const char *str, int *zbuf, size_t len) { zbuf[0] = len; ssize_t lh = 0, rh = 1; for (ssize_t i = 1; i < len; i++) { zbuf[i] = 0; if (i < rh) { zbuf[i] = min(zbuf[i - lh], rh - i); } while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]]) zbuf[i]++; if (i + zbuf[i] > rh) { lh = i; rh = i + zbuf[i]; } } } #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 #define TLS_EXTENSION_SNI 0x0000 #define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d typedef uint8_t uint8_t; typedef uint32_t uint32_t; typedef uint16_t uint16_t; /** * Processes tls payload of the tcp request. * * data Payload data of TCP. * dlen Length of `data`. */ struct tls_verdict analyze_tls_data( const uint8_t *data, uint32_t dlen) { struct tls_verdict vrd = {0}; size_t i = 0; const uint8_t *data_end = data + dlen; while (i + 4 < dlen) { const uint8_t *msgData = data + i; uint8_t tls_content_type = *msgData; uint8_t tls_vmajor = *(msgData + 1); uint8_t tls_vminor = *(msgData + 2); uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3)); const uint8_t *message_length_ptr = msgData + 3; if (tls_vmajor != 0x03) goto nextMessage; if (i + 5 > dlen) break; if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) goto nextMessage; if (config.sni_detection == SNI_DETECTION_BRUTE) { goto brute; } const uint8_t *handshakeProto = msgData + 5; if (handshakeProto + 1 >= data_end) break; uint8_t handshakeType = *handshakeProto; if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) goto nextMessage; const uint8_t *msgPtr = handshakeProto; msgPtr += 1; const uint8_t *handshakeProto_length_ptr = msgPtr + 1; msgPtr += 3 + 2 + 32; if (msgPtr + 1 >= data_end) break; uint8_t sessionIdLength = *msgPtr; msgPtr++; msgPtr += sessionIdLength; if (msgPtr + 2 >= data_end) break; uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr); msgPtr += 2; msgPtr += ciphersLength; if (msgPtr + 1 >= data_end) break; uint8_t compMethodsLen = *msgPtr; msgPtr++; msgPtr += compMethodsLen; if (msgPtr + 2 >= data_end) break; uint16_t extensionsLen = ntohs(*(uint16_t *)msgPtr); const uint8_t *extensionsLen_ptr = msgPtr; msgPtr += 2; const uint8_t *extensionsPtr = msgPtr; const uint8_t *extensions_end = extensionsPtr + extensionsLen; if (extensions_end > data_end) extensions_end = data_end; while (extensionsPtr < extensions_end) { const uint8_t *extensionPtr = extensionsPtr; if (extensionPtr + 4 >= extensions_end) break; uint16_t extensionType = ntohs(*(uint16_t *)extensionPtr); extensionPtr += 2; uint16_t extensionLen = ntohs(*(uint16_t *)extensionPtr); const uint8_t *extensionLen_ptr = extensionPtr; extensionPtr += 2; if (extensionPtr + extensionLen > extensions_end) break; if (extensionType != TLS_EXTENSION_SNI) goto nextExtension; const uint8_t *sni_ext_ptr = extensionPtr; if (sni_ext_ptr + 2 >= extensions_end) break; uint16_t sni_ext_dlen = ntohs(*(uint16_t *)sni_ext_ptr); const uint8_t *sni_ext_dlen_ptr = sni_ext_ptr; sni_ext_ptr += 2; const uint8_t *sni_ext_end = sni_ext_ptr + sni_ext_dlen; if (sni_ext_end >= extensions_end) break; if (sni_ext_ptr + 3 >= sni_ext_end) break; uint8_t sni_type = *sni_ext_ptr++; uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr); sni_ext_ptr += 2; if (sni_ext_ptr + sni_len > sni_ext_end) break; char *sni_name = (char *)sni_ext_ptr; vrd.sni_offset = (uint8_t *)sni_name - data; vrd.sni_len = sni_len; if (config.all_domains) { vrd.target_sni = 1; goto out; } unsigned int j = 0; for (unsigned int i = 0; i <= config.domains_strlen; i++) { if ( i > j && (i == config.domains_strlen || config.domains_str[i] == '\0' || config.domains_str[i] == ',' || config.domains_str[i] == '\n' )) { unsigned int domain_len = (i - j); const char *sni_startp = sni_name + sni_len - domain_len; const char *domain_startp = config.domains_str + j; if (sni_len >= domain_len && sni_len < 128 && !strncmp(sni_startp, domain_startp, domain_len)) { vrd.target_sni = 1; } j = i + 1; } } nextExtension: extensionsPtr += 2 + 2 + extensionLen; } nextMessage: i += 5 + message_length; } out: return vrd; brute: if (config.all_domains) { vrd.target_sni = 1; vrd.sni_len = 0; vrd.sni_offset = dlen / 2; goto out; } unsigned int j = 0; for (unsigned int i = 0; i <= config.domains_strlen; i++) { if ( i > j && (i == config.domains_strlen || config.domains_str[i] == '\0' || config.domains_str[i] == ',' || config.domains_str[i] == '\n' )) { uint8_t buf[MAX_PACKET_SIZE]; int zbuf[MAX_PACKET_SIZE]; unsigned int domain_len = (i - j); const char *domain_startp = config.domains_str + j; if (domain_len + dlen + 1> MAX_PACKET_SIZE) continue; memcpy(buf, domain_startp, domain_len); memcpy(buf + domain_len, "#", 1); memcpy(buf + domain_len + 1, data, dlen); z_function((char *)buf, zbuf, domain_len + 1 + dlen); for (unsigned int k = 0; k < dlen; k++) { if (zbuf[k] == domain_len) { vrd.target_sni = 1; vrd.sni_len = domain_len; vrd.sni_offset = (k - domain_len - 1); goto out; } } j = i + 1; } } goto out; } 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 (!ipxh || !tcph || !buf || !buflen) return -EINVAL; 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 = iph_len + tcph_len + data_len; if (*buflen < dlen) return -ENOMEM; memcpy(buf + iph_len, tcph, tcph_len); memcpy(buf + iph_len + tcph_len, data, data_len); struct tcphdr *ntcph = (struct tcphdr *)(buf + iph_len); 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); } fail_packet(buf, *buflen); *buflen = dlen; return 0; } int fail_packet(uint8_t *payload, uint32_t plen) { void *iph; uint32_t iph_len; struct tcphdr *tcph; uint32_t tcph_len; uint8_t *data; uint32_t dlen; int ret; ret = tcp_payload_split(payload, plen, &iph, &iph_len, &tcph, &tcph_len, &data, &dlen); if (ret < 0) { return ret; } if (config.faking_strategy == FAKE_STRAT_RAND_SEQ) { lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); if (config.fakeseq_offset) { tcph->seq = htonl(ntohl(tcph->seq) - config.fakeseq_offset); } else { #ifdef KERNEL_SCOPE tcph->seq = 124; #else tcph->seq = random(); #endif } lgtrace_addp("%u", ntohl(tcph->seq)); } else if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); tcph->seq = htonl(ntohl(tcph->seq) - dlen); lgtrace_addp("%u", ntohl(tcph->seq)); } else if (config.faking_strategy == FAKE_STRAT_TTL) { lgtrace_addp("set fake ttl to %d", 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; } } set_ip_checksum(iph, iph_len); set_tcp_checksum(tcph, iph, iph_len); if (config.faking_strategy == FAKE_STRAT_TCP_CHECK) { lgtrace_addp("break fake tcp checksum"); tcph->check += 1; } return 0; }