|
|
|
|
@ -36,19 +36,19 @@ static struct {
|
|
|
|
|
#define ASSERT_EQ(a,b,m) ASSERT((a)==(b),m) |
|
|
|
|
|
|
|
|
|
/* ================================================================
|
|
|
|
|
* Helpers: build IP packets (uses htonl/htons + memcpy: network byte order in wire) |
|
|
|
|
* Helpers: build IP packets (uses tc_htonl/tc_htons + memcpy: network byte order in wire) |
|
|
|
|
* ================================================================ */ |
|
|
|
|
|
|
|
|
|
static void build_ip_hdr(uint8_t* buf, uint8_t proto, uint32_t src_host, uint32_t dst_host, uint16_t total_len) { |
|
|
|
|
buf[0] = 0x45; |
|
|
|
|
buf[1] = 0x00; |
|
|
|
|
uint16_t n = htons(total_len); memcpy(buf + 2, &n, 2); |
|
|
|
|
uint16_t n = tc_htons(total_len); memcpy(buf + 2, &n, 2); |
|
|
|
|
buf[4] = 0x12; buf[5] = 0x34; |
|
|
|
|
memset(buf + 6, 0, 2); |
|
|
|
|
buf[8] = 64; |
|
|
|
|
buf[9] = proto; |
|
|
|
|
memset(buf + 10, 0, 2); // checksum slot = 0 for now
|
|
|
|
|
uint32_t s_net = htonl(src_host), d_net = htonl(dst_host); |
|
|
|
|
uint32_t s_net = tc_htonl(src_host), d_net = tc_htonl(dst_host); |
|
|
|
|
memcpy(buf + 12, &s_net, 4); |
|
|
|
|
memcpy(buf + 16, &d_net, 4); |
|
|
|
|
} |
|
|
|
|
@ -98,7 +98,7 @@ static struct global_config make_global_config(void) {
|
|
|
|
|
g.nat_port_start = TEST_PORT_START; |
|
|
|
|
g.nat_port_end = TEST_PORT_END; |
|
|
|
|
g.nat_tun_ip.family = AF_INET; |
|
|
|
|
g.nat_tun_ip.addr.v4.s_addr = htonl(TEST_GW_HOST); |
|
|
|
|
g.nat_tun_ip.addr.v4.s_addr = tc_htonl(TEST_GW_HOST); |
|
|
|
|
return g; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -181,10 +181,10 @@ static uint8_t* make_udp_pkt(uint32_t src_host, uint16_t src_port_host, uint32_t
|
|
|
|
|
uint8_t* pkt = calloc(1, ip_udp_len); |
|
|
|
|
build_ip_hdr(pkt, IPPROTO_UDP_UINT8, src_host, dst_host, ip_udp_len); |
|
|
|
|
// UDP header
|
|
|
|
|
uint16_t sp = htons(src_port_host), dp = htons(dst_port_host); |
|
|
|
|
uint16_t sp = tc_htons(src_port_host), dp = tc_htons(dst_port_host); |
|
|
|
|
memcpy(pkt + 20, &sp, 2); // src port
|
|
|
|
|
memcpy(pkt + 22, &dp, 2); // dst port
|
|
|
|
|
uint16_t udp_len = htons(8 + TEST_PAYLOAD_LEN); |
|
|
|
|
uint16_t udp_len = tc_htons(8 + TEST_PAYLOAD_LEN); |
|
|
|
|
memcpy(pkt + 24, &udp_len, 2); // length
|
|
|
|
|
memset(pkt + 26, 0, 2); // checksum = 0 (no UDP csum)
|
|
|
|
|
memset(pkt + 28, 0xAB, TEST_PAYLOAD_LEN); // payload
|
|
|
|
|
@ -208,7 +208,7 @@ static void test_port_alloc(void) {
|
|
|
|
|
// Check port was allocated from table index
|
|
|
|
|
struct eim_nat_entry* e = &ctx.table[10000 + i]; |
|
|
|
|
ASSERT(e->state == EIM_NAT_ENTRY_ACTIVE, "entry active"); |
|
|
|
|
ASSERT_EQ(e->internal_port, htons(50000 + i), "internal port stored"); |
|
|
|
|
ASSERT_EQ(e->internal_port, tc_htons(50000 + i), "internal port stored"); |
|
|
|
|
free(pkt); |
|
|
|
|
} |
|
|
|
|
ASSERT(ctx.next_port == 10003, "next_port advanced"); |
|
|
|
|
@ -258,7 +258,7 @@ static void test_port_alloc(void) {
|
|
|
|
|
r = eim_nat_egress(&ctx, p3, 20 + 8 + TEST_PAYLOAD_LEN, 1, get_mock_conn()); |
|
|
|
|
ASSERT_EQ(r, 0, "third egress ok after wrap"); |
|
|
|
|
ASSERT(ctx.table[10000].state == EIM_NAT_ENTRY_ACTIVE, "port 10000 reused"); |
|
|
|
|
ASSERT_EQ(ctx.table[10000].internal_port, htons(50003), "new entry for reused port"); |
|
|
|
|
ASSERT_EQ(ctx.table[10000].internal_port, tc_htons(50003), "new entry for reused port"); |
|
|
|
|
free(p3); |
|
|
|
|
|
|
|
|
|
eim_nat_destroy_ctx(&ctx); |
|
|
|
|
@ -316,13 +316,13 @@ static void test_egress_udp(void) {
|
|
|
|
|
|
|
|
|
|
// Check src IP = gateway
|
|
|
|
|
uint32_t new_src_ip_net; memcpy(&new_src_ip_net, pkt + 12, 4); |
|
|
|
|
ASSERT_EQ(ntohl(new_src_ip_net), TEST_GW_HOST, "src IP = gateway"); |
|
|
|
|
ASSERT_EQ(tc_ntohl(new_src_ip_net), TEST_GW_HOST, "src IP = gateway"); |
|
|
|
|
// Check dst IP unchanged
|
|
|
|
|
uint32_t dst_ip_net; memcpy(&dst_ip_net, pkt + 16, 4); |
|
|
|
|
ASSERT_EQ(dst_ip_net, orig_dst_ip_net, "dst IP unchanged"); |
|
|
|
|
// Check src port changed
|
|
|
|
|
uint16_t new_src_port_net; memcpy(&new_src_port_net, pkt + 20, 2); |
|
|
|
|
ASSERT(ntohs(new_src_port_net) == TEST_PORT_START, "src port = port_start"); |
|
|
|
|
ASSERT(tc_ntohs(new_src_port_net) == TEST_PORT_START, "src port = port_start"); |
|
|
|
|
// Check dst port unchanged
|
|
|
|
|
uint16_t dst_port_net; memcpy(&dst_port_net, pkt + 22, 2); |
|
|
|
|
ASSERT_EQ(dst_port_net, orig_dst_port_net, "dst port unchanged"); |
|
|
|
|
@ -332,7 +332,7 @@ static void test_egress_udp(void) {
|
|
|
|
|
struct eim_nat_entry* e = &ctx.table[TEST_PORT_START]; |
|
|
|
|
ASSERT_EQ(e->state, EIM_NAT_ENTRY_ACTIVE, "entry active"); |
|
|
|
|
ASSERT_EQ(e->internal_ip, TEST_IP_SRC_HOST, "internal_ip stored"); |
|
|
|
|
ASSERT_EQ(e->internal_port, htons(TEST_SRC_PORT), "internal_port stored"); |
|
|
|
|
ASSERT_EQ(e->internal_port, tc_htons(TEST_SRC_PORT), "internal_port stored"); |
|
|
|
|
ASSERT_EQ(e->proto, IPPROTO_UDP_UINT8, "proto=UDP"); |
|
|
|
|
ASSERT_EQ(e->src_node_id, 0x5555ULL, "src_node_id stored"); |
|
|
|
|
ASSERT(e->src_conn == get_mock_conn(), "src_conn stored"); |
|
|
|
|
@ -350,7 +350,7 @@ static uint8_t* make_tcp_pkt(uint32_t src_host, uint16_t src_port_host, uint32_t
|
|
|
|
|
const size_t ip_tcp_len = 20 + 20 + TEST_PAYLOAD_LEN; // 20 TCP hdr min
|
|
|
|
|
uint8_t* pkt = calloc(1, ip_tcp_len); |
|
|
|
|
build_ip_hdr(pkt, IPPROTO_TCP_UINT8, src_host, dst_host, ip_tcp_len); |
|
|
|
|
uint16_t sp = htons(src_port_host), dp = htons(dst_port_host); |
|
|
|
|
uint16_t sp = tc_htons(src_port_host), dp = tc_htons(dst_port_host); |
|
|
|
|
memcpy(pkt + 20, &sp, 2); |
|
|
|
|
memcpy(pkt + 22, &dp, 2); |
|
|
|
|
// seq, ack, offset+flags, window
|
|
|
|
|
@ -374,10 +374,10 @@ static void test_egress_tcp(void) {
|
|
|
|
|
ASSERT_EQ(r, 0, "egress TCP ok"); |
|
|
|
|
// Check src IP = gateway
|
|
|
|
|
uint32_t new_src; memcpy(&new_src, pkt + 12, 4); |
|
|
|
|
ASSERT_EQ(ntohl(new_src), TEST_GW_HOST, "src IP=gw"); |
|
|
|
|
ASSERT_EQ(tc_ntohl(new_src), TEST_GW_HOST, "src IP=gw"); |
|
|
|
|
// Check src port
|
|
|
|
|
uint16_t new_sport; memcpy(&new_sport, pkt + 20, 2); |
|
|
|
|
ASSERT_EQ(ntohs(new_sport), TEST_PORT_START, "src port=start"); |
|
|
|
|
ASSERT_EQ(tc_ntohs(new_sport), TEST_PORT_START, "src port=start"); |
|
|
|
|
// IP checksum valid
|
|
|
|
|
ASSERT(verify_ip_checksum(pkt) == 1, "IP checksum valid"); |
|
|
|
|
// Entry proto = TCP
|
|
|
|
|
@ -399,7 +399,7 @@ static uint8_t* make_icmp_echo_pkt(uint32_t src_host, uint32_t dst_host, uint8_t
|
|
|
|
|
pkt[21] = 0x00; // code
|
|
|
|
|
// checksum = 0 for now
|
|
|
|
|
memset(pkt + 22, 0, 2); |
|
|
|
|
uint16_t id_n = htons(id_host), seq_n = htons(seq_host); |
|
|
|
|
uint16_t id_n = tc_htons(id_host), seq_n = tc_htons(seq_host); |
|
|
|
|
memcpy(pkt + 24, &id_n, 2); |
|
|
|
|
memcpy(pkt + 26, &seq_n, 2); |
|
|
|
|
memset(pkt + 28, 0xDD, TEST_PAYLOAD_LEN); |
|
|
|
|
@ -420,12 +420,12 @@ static void test_egress_icmp(void) {
|
|
|
|
|
ASSERT_EQ(r, 0, "egress ICMP echo ok"); |
|
|
|
|
// ICMP ID should be overwritten with allocated port
|
|
|
|
|
uint16_t new_id_net; memcpy(&new_id_net, pkt + 24, 2); |
|
|
|
|
ASSERT_EQ(ntohs(new_id_net), TEST_PORT_START, "ICMP ID = allocated port"); |
|
|
|
|
ASSERT_EQ(tc_ntohs(new_id_net), TEST_PORT_START, "ICMP ID = allocated port"); |
|
|
|
|
// IP checksum valid
|
|
|
|
|
ASSERT(verify_ip_checksum(pkt) == 1, "IP checksum valid"); |
|
|
|
|
// Entry proto = ICMP
|
|
|
|
|
ASSERT_EQ(ctx.table[TEST_PORT_START].proto, IPPROTO_ICMP_UINT8, "proto=ICMP"); |
|
|
|
|
ASSERT_EQ(ctx.table[TEST_PORT_START].internal_port, htons(icmp_id), "internal port = ICMP ID"); |
|
|
|
|
ASSERT_EQ(ctx.table[TEST_PORT_START].internal_port, tc_htons(icmp_id), "internal port = ICMP ID"); |
|
|
|
|
free(pkt); |
|
|
|
|
eim_nat_destroy_ctx(&ctx); |
|
|
|
|
} |
|
|
|
|
@ -538,7 +538,7 @@ static void test_egress_invalid(void) {
|
|
|
|
|
uint8_t pkt[20 + 8 + TEST_PAYLOAD_LEN]; |
|
|
|
|
build_ip_hdr(pkt, 0x63, TEST_IP_SRC_HOST, TEST_IP_DST_HOST, sizeof(pkt)); |
|
|
|
|
// Fill fake UDP header
|
|
|
|
|
uint16_t sp = htons(TEST_SRC_PORT), dp = htons(TEST_DST_PORT); |
|
|
|
|
uint16_t sp = tc_htons(TEST_SRC_PORT), dp = tc_htons(TEST_DST_PORT); |
|
|
|
|
memcpy(pkt + 20, &sp, 2); memcpy(pkt + 22, &dp, 2); |
|
|
|
|
compute_ip_checksum(pkt); |
|
|
|
|
int r = eim_nat_egress(&ctx, pkt, sizeof(pkt), 1, get_mock_conn()); |
|
|
|
|
@ -582,13 +582,13 @@ static void test_ingress_udp(void) {
|
|
|
|
|
ASSERT(entry == &ctx.table[TEST_PORT_START], "correct entry"); |
|
|
|
|
// Check dst IP → internal IP
|
|
|
|
|
uint32_t new_dst_net; memcpy(&new_dst_net, resp + 16, 4); |
|
|
|
|
ASSERT_EQ(ntohl(new_dst_net), internal_ip, "dst IP = internal IP"); |
|
|
|
|
ASSERT_EQ(tc_ntohl(new_dst_net), internal_ip, "dst IP = internal IP"); |
|
|
|
|
// Check dst port → internal port
|
|
|
|
|
uint16_t new_dst_port_net; memcpy(&new_dst_port_net, resp + 22, 2); |
|
|
|
|
ASSERT_EQ(new_dst_port_net, internal_port_net, "dst port = internal port"); |
|
|
|
|
// Check src IP unchanged
|
|
|
|
|
uint32_t src_net; memcpy(&src_net, resp + 12, 4); |
|
|
|
|
ASSERT_EQ(ntohl(src_net), TEST_IP_DST_HOST, "src IP unchanged"); |
|
|
|
|
ASSERT_EQ(tc_ntohl(src_net), TEST_IP_DST_HOST, "src IP unchanged"); |
|
|
|
|
ASSERT(verify_ip_checksum(resp) == 1, "IP checksum valid"); |
|
|
|
|
|
|
|
|
|
free(resp); |
|
|
|
|
@ -654,8 +654,8 @@ static void test_ingress_icmp(void) {
|
|
|
|
|
reply[20] = 0; // Echo Reply
|
|
|
|
|
reply[21] = 0; |
|
|
|
|
memset(reply + 22, 0, 2); // checksum
|
|
|
|
|
uint16_t alloc_id_net = htons(TEST_PORT_START); // the port allocated by egress
|
|
|
|
|
uint16_t seq = htons(1); |
|
|
|
|
uint16_t alloc_id_net = tc_htons(TEST_PORT_START); // the port allocated by egress
|
|
|
|
|
uint16_t seq = tc_htons(1); |
|
|
|
|
memcpy(reply + 24, &alloc_id_net, 2); |
|
|
|
|
memcpy(reply + 26, &seq, 2); |
|
|
|
|
memset(reply + 28, 0xDD, TEST_PAYLOAD_LEN); |
|
|
|
|
@ -666,7 +666,7 @@ static void test_ingress_icmp(void) {
|
|
|
|
|
int r = eim_nat_ingress(&ctx, reply, len, &entry); |
|
|
|
|
ASSERT_EQ(r, 0, "ingress icmp reply ok"); |
|
|
|
|
uint16_t new_id_net; memcpy(&new_id_net, reply + 24, 2); |
|
|
|
|
ASSERT_EQ(ntohs(new_id_net), icmp_id, "ICMP ID restored to original"); |
|
|
|
|
ASSERT_EQ(tc_ntohs(new_id_net), icmp_id, "ICMP ID restored to original"); |
|
|
|
|
ASSERT(entry != NULL, "entry returned"); |
|
|
|
|
ASSERT(verify_ip_checksum(reply) == 1, "IP checksum valid"); |
|
|
|
|
free(reply); |
|
|
|
|
@ -686,12 +686,12 @@ static void test_port_forward(void) {
|
|
|
|
|
eim_nat_init_ctx(&ctx, &g); |
|
|
|
|
|
|
|
|
|
int r = eim_nat_add_forward(&ctx, IPPROTO_TCP_UINT8, |
|
|
|
|
0x0A0000FE, htons(8080), // internal 10.0.0.254:8080
|
|
|
|
|
0x0A0000FE, tc_htons(8080), // internal 10.0.0.254:8080
|
|
|
|
|
10050); |
|
|
|
|
ASSERT_EQ(r, 0, "add_forward ok"); |
|
|
|
|
ASSERT(ctx.table[10050].state == EIM_NAT_ENTRY_STATIC, "entry static"); |
|
|
|
|
ASSERT_EQ(ctx.table[10050].internal_ip, 0x0A0000FE, "internal_ip"); |
|
|
|
|
ASSERT_EQ(ctx.table[10050].internal_port, htons(8080), "internal_port"); |
|
|
|
|
ASSERT_EQ(ctx.table[10050].internal_port, tc_htons(8080), "internal_port"); |
|
|
|
|
ASSERT_EQ(ctx.table[10050].proto, IPPROTO_TCP_UINT8, "proto=TCP"); |
|
|
|
|
eim_nat_destroy_ctx(&ctx); |
|
|
|
|
} |
|
|
|
|
@ -703,8 +703,8 @@ static void test_port_forward(void) {
|
|
|
|
|
struct eim_nat_ctx ctx; |
|
|
|
|
eim_nat_init_ctx(&ctx, &g); |
|
|
|
|
|
|
|
|
|
eim_nat_add_forward(&ctx, IPPROTO_TCP_UINT8, 0x0A0000FE, htons(8080), 10050); |
|
|
|
|
int r = eim_nat_add_forward(&ctx, IPPROTO_UDP_UINT8, 0x0A0000FF, htons(9090), 10050); |
|
|
|
|
eim_nat_add_forward(&ctx, IPPROTO_TCP_UINT8, 0x0A0000FE, tc_htons(8080), 10050); |
|
|
|
|
int r = eim_nat_add_forward(&ctx, IPPROTO_UDP_UINT8, 0x0A0000FF, tc_htons(9090), 10050); |
|
|
|
|
ASSERT_EQ(r, -1, "duplicate rejects"); |
|
|
|
|
eim_nat_destroy_ctx(&ctx); |
|
|
|
|
} |
|
|
|
|
@ -717,7 +717,7 @@ static void test_port_forward(void) {
|
|
|
|
|
eim_nat_init_ctx(&ctx, &g); |
|
|
|
|
|
|
|
|
|
// Static forward: external port 10050 → internal 10.0.0.254:8080 TCP
|
|
|
|
|
eim_nat_add_forward(&ctx, IPPROTO_TCP_UINT8, 0x0A0000FE, htons(8080), 10050); |
|
|
|
|
eim_nat_add_forward(&ctx, IPPROTO_TCP_UINT8, 0x0A0000FE, tc_htons(8080), 10050); |
|
|
|
|
|
|
|
|
|
// Ingress TCP packet to gateway:10050
|
|
|
|
|
uint8_t* resp = make_tcp_pkt(TEST_IP_DST_HOST, 443, TEST_GW_HOST, 10050); |
|
|
|
|
@ -728,10 +728,10 @@ static void test_port_forward(void) {
|
|
|
|
|
ASSERT_EQ(entry->state, EIM_NAT_ENTRY_STATIC, "static entry"); |
|
|
|
|
// Check dst IP → 10.0.0.254
|
|
|
|
|
uint32_t dst_net; memcpy(&dst_net, resp + 16, 4); |
|
|
|
|
ASSERT_EQ(ntohl(dst_net), 0x0A0000FE, "dst IP = 10.0.0.254"); |
|
|
|
|
ASSERT_EQ(tc_ntohl(dst_net), 0x0A0000FE, "dst IP = 10.0.0.254"); |
|
|
|
|
// Check dst port → 8080
|
|
|
|
|
uint16_t dst_port; memcpy(&dst_port, resp + 22, 2); |
|
|
|
|
ASSERT_EQ(dst_port, htons(8080), "dst port = 8080"); |
|
|
|
|
ASSERT_EQ(dst_port, tc_htons(8080), "dst port = 8080"); |
|
|
|
|
ASSERT(verify_ip_checksum(resp) == 1, "IP checksum valid"); |
|
|
|
|
free(resp); |
|
|
|
|
eim_nat_destroy_ctx(&ctx); |
|
|
|
|
@ -758,7 +758,7 @@ static void test_bypass_flows(void) {
|
|
|
|
|
eim_nat_egress(&ctx, p2, 20 + 8 + TEST_PAYLOAD_LEN, 0x1111, get_mock_conn()); |
|
|
|
|
// Should reuse same port (matching internal_ip:port:proto)
|
|
|
|
|
uint16_t sp; memcpy(&sp, p2 + 20, 2); |
|
|
|
|
ASSERT_EQ(ntohs(sp), TEST_PORT_START, "same port reused"); |
|
|
|
|
ASSERT_EQ(tc_ntohs(sp), TEST_PORT_START, "same port reused"); |
|
|
|
|
free(p2); |
|
|
|
|
eim_nat_destroy_ctx(&ctx); |
|
|
|
|
} |
|
|
|
|
|