From 518df33b0e9eb0b97a78855ab13b3328d389d913 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 29 Sep 2026 03:56:55 +0300 Subject: [PATCH] Fix UDP and ICMP packet construction at the TUN boundary --- src/proxy/icmp_proxy.c | 20 +++++++------ src/proxy/udp_proxy.c | 5 ++-- tests/Makefile.am | 4 +++ tests/test_proxy_packets.c | 58 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 tests/test_proxy_packets.c diff --git a/src/proxy/icmp_proxy.c b/src/proxy/icmp_proxy.c index 40d662f4..a24a8f65 100644 --- a/src/proxy/icmp_proxy.c +++ b/src/proxy/icmp_proxy.c @@ -53,6 +53,14 @@ struct icmp_proxy_ctx* g_icmp_ctx = NULL; static void req_expire_timer_cb(void* arg); static void req_expire(struct icmp_proxy_ctx* ctx); +static uint16_t icmp_checksum(const uint8_t* data, size_t len) { + uint32_t sum = 0; + while (len >= 2) { sum += ((uint16_t)data[0] << 8) | data[1]; data += 2; len -= 2; } + if (len) sum += (uint16_t)data[0] << 8; + while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); + return htons((uint16_t)~sum); +} + static struct icmp_request* req_find_by_id(struct icmp_request* head, uint16_t echo_id, uint16_t echo_seq) { struct icmp_request* r; for (r = head; r; r = r->next) if (r->echo_id == echo_id && r->echo_seq == echo_seq) return r; @@ -76,10 +84,7 @@ static int exit_send_echo(struct UTUN_INSTANCE* inst, uint64_t client_node_id, icmp_hdr->icmp_seq = echo_seq; if (payload_len > 0) memcpy(icmp_hdr->icmp_data, payload, payload_len); icmp_hdr->icmp_cksum = 0; - uint32_t sum = 0; uint16_t* w = (uint16_t*)icmp_hdr; - for (size_t i = 0; i < (icmp_len + 1) / 2; i++) sum += w[i]; - while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); - icmp_hdr->icmp_cksum = ~(uint16_t)sum; + icmp_hdr->icmp_cksum = icmp_checksum((const uint8_t*)icmp_hdr, icmp_len); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto dst=0x%08x id=0x%04x seq=%u len=%zu", dst_ip, echo_id, echo_seq, icmp_len); @@ -260,6 +265,7 @@ int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, } struct tun_if* tun = inst->tcp_proxy_client->tun; + if (payload_len > 65506) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: payload too large len=%zu", payload_len); return -1; } size_t icmp_len = ICMP_MINLEN + payload_len; size_t pkt_len = 20 + icmp_len; uint8_t* pkt = u_malloc(pkt_len); @@ -281,14 +287,12 @@ int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, icmp_hdr->icmp_id = echo_id; icmp_hdr->icmp_seq = echo_seq; if (payload_len > 0) memcpy(icmp_hdr->icmp_data, payload, payload_len); icmp_hdr->icmp_cksum = 0; - uint32_t sum = 0; uint16_t* w = (uint16_t*)icmp_hdr; - for (size_t i = 0; i < (icmp_len + 1) / 2; i++) sum += w[i]; - while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); - icmp_hdr->icmp_cksum = ~(uint16_t)sum; + icmp_hdr->icmp_cksum = icmp_checksum((const uint8_t*)icmp_hdr, icmp_len); struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(pkt); return -1; } e->dgram = u_malloc(1 + pkt_len); + if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply allocation failed"); u_free(pkt); queue_entry_free(e); return -1; } e->dgram[0] = 4; memcpy(e->dgram + 1, pkt, pkt_len); e->len = 1 + pkt_len; u_free(pkt); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply delivered to TUN id=0x%04x seq=%u", echo_id, echo_seq); diff --git a/src/proxy/udp_proxy.c b/src/proxy/udp_proxy.c index da67481c..70bdae6b 100644 --- a/src/proxy/udp_proxy.c +++ b/src/proxy/udp_proxy.c @@ -178,14 +178,14 @@ int udp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, uint32_t src_ip, uint16_t src_port, uint32_t dst_ip, uint16_t dst_port, const uint8_t* payload, size_t payload_len) { - if (!inst || !inst->tcp_proxy_client || !inst->tcp_proxy_client->tun) return -1; + if (!inst || !inst->tcp_proxy_client || !inst->tcp_proxy_client->tun || payload_len > 65506) return -1; // Собрать IP/UDP ответный пакет size_t ip_len = 20 + 8 + payload_len; uint8_t* pkt = u_malloc(ip_len); if (!pkt) return -1; - memset(pkt, 0, 20); + memset(pkt, 0, 28); // IPv4 UDP checksum=0: проверка отключена явно pkt[0] = 0x45; pkt[8] = 64; pkt[9] = IPPROTO_UDP; memcpy(pkt + 12, &src_ip, 4); memcpy(pkt + 16, &dst_ip, 4); @@ -205,6 +205,7 @@ int udp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(pkt); return -1; } e->dgram = u_malloc(1 + ip_len); + if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "udp_proxy: reply allocation failed"); u_free(pkt); queue_entry_free(e); return -1; } e->dgram[0] = 4; memcpy(e->dgram + 1, pkt, ip_len); e->len = 1 + ip_len; u_free(pkt); queue_data_put(inst->tcp_proxy_client->tun->input_queue, e); diff --git a/tests/Makefile.am b/tests/Makefile.am index b6b06b2c..ff80fe30 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -632,3 +632,7 @@ test_etcp_router_tcp_LDADD = $(LIBUTUN) $(COMMON_LIBS) test_services_SOURCES = test_services.c test_services_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/routing_layer -I$(top_srcdir)/src/transport_layer -I$(top_srcdir)/lib test_services_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) + +check_PROGRAMS += test_proxy_packets +test_proxy_packets_SOURCES = test_proxy_packets.c +test_proxy_packets_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) diff --git a/tests/test_proxy_packets.c b/tests/test_proxy_packets.c new file mode 100644 index 00000000..a19edf80 --- /dev/null +++ b/tests/test_proxy_packets.c @@ -0,0 +1,58 @@ +// Проверяем пакеты на границе proxy → TUN: длины, адреса, payload и checksum. +#include +#include +#include "utun_instance.h" +#include "tun_if.h" +#include "proxy/udp_proxy.h" +#include "proxy/icmp_proxy.h" +#include "debug_config.h" +#include "mem.h" + +#define CHECK(x) do { if (!(x)) { fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #x); return 1; } } while (0) + +static unsigned checksum_sum(const uint8_t* p, size_t n) { + unsigned sum = 0; + while (n >= 2) { sum += ((unsigned)p[0] << 8) | p[1]; p += 2; n -= 2; } + if (n) sum += (unsigned)p[0] << 8; + while (sum >> 16) sum = (sum & 65535) + (sum >> 16); + return sum; +} + +int main(void) { + debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); + struct UASYNC* ua = uasync_create(); + struct UTUN_INSTANCE* inst = u_calloc(1, sizeof(*inst)); + struct tcp_proxy_client client = {0}; + struct tun_if tun = {0}; + CHECK(ua && inst); + inst->tcp_proxy_client = &client; client.tun = &tun; + tun.input_queue = queue_new(ua, 0, 0, 0, "packet_test"); + CHECK(tun.input_queue); + uint8_t payload[1501]; + for (size_t i = 0; i < sizeof(payload); i++) payload[i] = (uint8_t)(i * 17 + 3); + const uint8_t src[4] = {192, 0, 2, 1}, dst[4] = {10, 0, 0, 2}; + uint32_t src_ip, dst_ip; memcpy(&src_ip, src, 4); memcpy(&dst_ip, dst, 4); + for (size_t len = 0; len <= sizeof(payload); len++) { + CHECK(udp_proxy_deliver_reply(inst, src_ip, htons(53), dst_ip, htons(23456), payload, len) == 0); + struct ll_entry* e = queue_data_get(tun.input_queue); + CHECK(e && e->len == 29 + len); + const uint8_t* ip = e->dgram + 1; + CHECK(e->dgram[0] == 4 && ip[9] == 17 && checksum_sum(ip, 20) == 65535); + CHECK(memcmp(ip + 12, src, 4) == 0 && memcmp(ip + 16, dst, 4) == 0); + CHECK(ip[26] == 0 && ip[27] == 0); + CHECK(memcmp(ip + 28, payload, len) == 0); + queue_dgram_free(e); queue_entry_free(e); + CHECK(icmp_proxy_deliver_reply(inst, dst_ip, src_ip, htons(123), htons(5), payload, len) == 0); + e = queue_data_get(tun.input_queue); + CHECK(e && e->len == 29 + len); + ip = e->dgram + 1; + CHECK(ip[9] == 1 && checksum_sum(ip, 20) == 65535 && checksum_sum(ip + 20, 8 + len) == 65535); + CHECK(ip[20] == 0 && ip[24] == 0 && ip[25] == 123 && ip[27] == 5); + CHECK(memcmp(ip + 28, payload, len) == 0); + queue_dgram_free(e); queue_entry_free(e); + } + queue_free(tun.input_queue); u_free(inst); uasync_destroy(ua, 0); + CHECK(u_get_allocated_count() == 0); + puts("[PASS] proxy UDP/ICMP packets: payload lengths 0..1501"); + return 0; +}