Preserve IPv4 UDP checksum encoding through NAT
This commit is contained in:
1 parent
52a73ea6a7
commit
a9820bea42
3 files changed
+50
No files matched your search
@@ -55,10 +55,12 @@ static void csum_update_transport(uint8_t* transport, int csum_off,
|
||||
uint32_t old_ip_host, uint32_t new_ip_host,
|
||||
uint16_t old_port_net, uint16_t new_port_net) {
|
||||
uint16_t old_csum; memcpy(&old_csum, transport + csum_off, 2);
|
||||
if (csum_off == UDP_CHECKSUM_OFFSET && old_csum == 0) return; // IPv4 UDP: проверка отключена отправителем.
|
||||
uint32_t old_ip_net = htonl(old_ip_host), new_ip_net = htonl(new_ip_host);
|
||||
uint16_t old_w[3] = {(uint16_t)(old_ip_net & 0xFFFF), (uint16_t)(old_ip_net >> 16), old_port_net};
|
||||
uint16_t new_w[3] = {(uint16_t)(new_ip_net & 0xFFFF), (uint16_t)(new_ip_net >> 16), new_port_net};
|
||||
uint16_t new_csum = csum_update_n(old_csum, old_w, new_w, 3);
|
||||
if (csum_off == UDP_CHECKSUM_OFFSET && new_csum == 0) new_csum = 0xffff;
|
||||
memcpy(transport + csum_off, &new_csum, 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eim_nat — таблица Endpoint-Independent Mapping для IPv4 TCP/UDP и ICMP echo.
|
||||
* init_ctx создаёт состояние из global_config; при отключённом NAT оставляет initialized=0.
|
||||
* egress/ingress меняют IP/порты и checksums на месте, не отправляют пакеты и не владеют TUN/ETCP.
|
||||
* IPv4 UDP: отключённая checksum=0 сохраняется; вычисленный ноль записывается как 0xffff.
|
||||
* Перед обработкой нужен инициализированный ctx; вызовы последовательны в uasync-потоке.
|
||||
* gateway/internal IP — host order, internal_port — network order, внешний порт — host order.
|
||||
* src_conn заимствован; срок жизни и выбор транспорта обратной отправки контролирует nat_transport. */
|
||||
|
||||
@@ -892,6 +892,51 @@ static void test_checksum_update(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* UDP: отключённая checksum остаётся нулевой; вычисленный ноль передаётся как 0xffff. */
|
||||
static void test_udp_checksum_encoding(int computed_zero) {
|
||||
for (int ingress = 0; ingress < 2; ingress++) {
|
||||
TEST(computed_zero ? (ingress ? "udp_zero_result_ingress" : "udp_zero_result_egress") :
|
||||
(ingress ? "udp_disabled_checksum_ingress" : "udp_disabled_checksum_egress"));
|
||||
struct global_config g = make_global_config();
|
||||
struct eim_nat_ctx ctx;
|
||||
ASSERT_EQ(eim_nat_init_ctx(&ctx, &g), 0, "UDP checksum context initialized");
|
||||
ctx.table[TEST_PORT_START] = (struct eim_nat_entry){.internal_ip = TEST_IP_SRC_HOST,
|
||||
.internal_port = htobe16(TEST_SRC_PORT), .proto = IPPROTO_UDP_UINT8,
|
||||
.state = EIM_NAT_ENTRY_ACTIVE, .src_node_id = 1};
|
||||
const size_t len = 28 + TEST_PAYLOAD_LEN;
|
||||
uint8_t* pkt = ingress ? make_udp_pkt(TEST_IP_DST_HOST, TEST_DST_PORT, TEST_GW_HOST, TEST_PORT_START) :
|
||||
make_udp_pkt(TEST_IP_SRC_HOST, TEST_SRC_PORT, TEST_IP_DST_HOST, TEST_DST_PORT);
|
||||
if (computed_zero) {
|
||||
size_t ip_offset = ingress ? 16 : 12;
|
||||
size_t port_offset = ingress ? 22 : 20;
|
||||
uint32_t original_ip; memcpy(&original_ip, pkt + ip_offset, 4);
|
||||
uint16_t original_port; memcpy(&original_port, pkt + port_offset, 2);
|
||||
uint32_t target_ip = htobe32(ingress ? TEST_IP_SRC_HOST : TEST_GW_HOST);
|
||||
uint16_t target_port = htobe16(ingress ? TEST_SRC_PORT : TEST_PORT_START);
|
||||
memcpy(pkt + ip_offset, &target_ip, 4); memcpy(pkt + port_offset, &target_port, 2);
|
||||
memset(pkt + 28, 0, 2);
|
||||
uint16_t payload_word = htobe16(full_transport_checksum(pkt, len));
|
||||
memcpy(pkt + 28, &payload_word, 2);
|
||||
ASSERT_EQ(full_transport_checksum(pkt, len), 0, "translated packet requires zero checksum");
|
||||
memcpy(pkt + ip_offset, &original_ip, 4); memcpy(pkt + port_offset, &original_port, 2);
|
||||
set_packet_checksums(pkt, len, 26);
|
||||
ASSERT_EQ(full_transport_checksum(pkt, len), 0, "valid nonzero input UDP checksum");
|
||||
}
|
||||
struct eim_nat_entry* entry = NULL;
|
||||
int r = ingress ? eim_nat_ingress(&ctx, pkt, len, &entry) :
|
||||
eim_nat_egress(&ctx, pkt, len, 1, get_mock_conn());
|
||||
uint16_t checksum; memcpy(&checksum, pkt + 26, 2);
|
||||
int valid = !computed_zero || full_transport_checksum(pkt, len) == 0;
|
||||
free(pkt);
|
||||
ASSERT_EQ(r, 0, "UDP checksum packet accepted");
|
||||
ASSERT(!ingress || entry == &ctx.table[TEST_PORT_START], "ingress used expected UDP mapping");
|
||||
ASSERT_EQ(checksum, computed_zero ? 0xffff : 0, "correct UDP checksum encoding");
|
||||
ASSERT(valid, "encoded UDP checksum remains valid");
|
||||
eim_nat_destroy_ctx(&ctx);
|
||||
PASS();
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Main
|
||||
* ================================================================ */
|
||||
@@ -912,6 +957,8 @@ int main(void) {
|
||||
test_port_forward();
|
||||
test_bypass_flows();
|
||||
test_checksum_update();
|
||||
test_udp_checksum_encoding(0);
|
||||
test_udp_checksum_encoding(1);
|
||||
|
||||
printf("\n=== Results: %d run, %d passed, %d failed ===\n",
|
||||
stats.run, stats.passed, stats.failed);
|
||||
|
||||
Reference in new issue
Block a user