Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1079,21 +1079,6 @@ test-wolfguard-loopback-ubsan: CFLAGS+=-fsanitize=undefined -fno-sanitize-recove
test-wolfguard-loopback-ubsan: LDFLAGS+=-fsanitize=undefined $(UNIT_LIBS)
test-wolfguard-loopback-ubsan: clean-test-wolfguard-loopback build/test/test-wolfguard-loopback

# wolfGuard benchmark
bench-wolfguard: build/test/bench-wolfguard

build/test/bench-wolfguard: src/test/bench_wolfguard.c
@mkdir -p build/test/
@echo "[CC] bench_wolfguard.c"
@$(CC) $(CFLAGS) -O2 $(WOLFGUARD_CFLAGS) \
-c src/test/bench_wolfguard.c -o build/test/bench_wolfguard.o
@echo "[LD] $@"
@$(CC) build/test/bench_wolfguard.o -o $@ \
$(LDFLAGS) -lwolfssl

clean-bench-wolfguard:
@rm -f build/test/bench-wolfguard build/test/bench_wolfguard.o

# wolfGuard interop test (wolfIP <-> kernel wolfGuard via TUN)
test-wolfguard-interop: build/test/test-wolfguard-interop
Comment thread
gasbytes marked this conversation as resolved.

Expand All @@ -1117,7 +1102,6 @@ clean-test-wolfguard-interop:
unit-wolfguard unit-wolfguard-asan unit-wolfguard-ubsan clean-unit-wolfguard \
test-wolfguard-loopback test-wolfguard-loopback-asan test-wolfguard-loopback-ubsan \
clean-test-wolfguard-loopback \
bench-wolfguard clean-bench-wolfguard \
test-wolfguard-interop clean-test-wolfguard-interop

cppcheck:
Expand Down
1 change: 0 additions & 1 deletion docs/wolfguard_howto.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ switch. The pre-wired Makefile targets build and exercise it:
```sh
make unit-wolfguard # unit tests
make test-wolfguard-loopback # two-stack loopback integration test
make bench-wolfguard # micro-benchmarks
make test-wolfguard-interop # interop binary (driven by the script in §8)
```

Expand Down
220 changes: 220 additions & 0 deletions src/test/test_wolfguard_loopback.c
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,214 @@ START_TEST(test_multi_peer)
}
END_TEST

/*
* MTU boundary sweep
*
* Sweeps the inner UDP payload across the wg0 MTU in 1-byte
* steps. wolfguard_init() sets wg0 MTU = LINK_MTU - 60, and IPv4(20)+UDP(8)
* headers eat 28 more, so payloads up to (LINK_MTU - 88) fit and larger ones
* exceed the tunnel MTU.
*/
START_TEST(test_mtu_boundary_sweep)
{
uint64_t now;
int app_sock_a, app_sock_b;
struct wolfIP_sockaddr_in bind_addr, dst_addr;
uint8_t sndbuf[LINK_MTU];
const int wg0_mtu = LINK_MTU - 60; /* set by wolfguard_init() */
const int max_payload = wg0_mtu - 28; /* wg0 MTU minus IPv4(20)+UDP(8) */
const int anchor = 1000; /* known-deliverable (cf. flood) */
const int top = max_payload + 16; /* just above the wg0 MTU */
int max_delivered = -1;
int anchor_delivered = 0;
int p, i, ret;

setup_loopback_stacks(&now);

/* B listens on 7777, A binds a source port on 9999 */
app_sock_b = wolfIP_sock_socket(&stack_b, AF_INET, SOCK_DGRAM, 0);
ck_assert_int_ge(app_sock_b, 0);
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = ee16(7777);
bind_addr.sin_addr.s_addr = ee32(MAKE_IP4(10,0,0,2));
ck_assert_int_ge(wolfIP_sock_bind(&stack_b, app_sock_b,
(struct wolfIP_sockaddr *)&bind_addr, sizeof(bind_addr)), 0);
wolfIP_register_callback(&stack_b, app_sock_b, app_udp_callback, &stack_b);

app_sock_a = wolfIP_sock_socket(&stack_a, AF_INET, SOCK_DGRAM, 0);
ck_assert_int_ge(app_sock_a, 0);
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = ee16(9999);
bind_addr.sin_addr.s_addr = ee32(MAKE_IP4(10,0,0,1));
ck_assert_int_ge(wolfIP_sock_bind(&stack_a, app_sock_a,
(struct wolfIP_sockaddr *)&bind_addr, sizeof(bind_addr)), 0);

memset(&dst_addr, 0, sizeof(dst_addr));
dst_addr.sin_family = AF_INET;
dst_addr.sin_port = ee16(7777);
dst_addr.sin_addr.s_addr = ee32(MAKE_IP4(10,0,0,2));

/* Bring the session up with a small packet so the sweep tests
* the pure data-plane MTU path, not a handshake+size interaction. */
for (i = 0; i < 64; i++)
sndbuf[i] = (uint8_t)(i & 0xff);
ret = wolfIP_sock_sendto(&stack_a, app_sock_a, sndbuf, 64, 0,
(const struct wolfIP_sockaddr *)&dst_addr,
sizeof(dst_addr));
ck_assert_int_ge(ret, 0);
pump_stacks(&now, 200, 10);
ck_assert_int_gt(app_recv_count, 0);
ck_assert_ptr_nonnull(wg_dev_a.peers[0].keypairs.current);

/* Sweep the inner payload up to just past the tunnel MTU, one byte at a
* time. The loopback ring caps the outer frame below the wg0 MTU, so we
* don't hardcode where delivery stops; instead we require that every
* delivered packet is byte-exact and that delivery never exceeds the wg0
* MTU. Timers are frozen (step_ms = 0) so a single session persists, a
* live clock would trip the spec's stale-receive rekey, since B is a pure
* sink that never replies. */
for (p = anchor; p <= top; p++) {
for (i = 0; i < p; i++)
sndbuf[i] = (uint8_t)((i * 31 + p) & 0xff);

app_recv_count = 0;
app_recv_len = 0;

/* sendto may reject an over-MTU payload (no fragmentation), so
* either a rejection here or a silent drop downstream is acceptable. */
(void)wolfIP_sock_sendto(&stack_a, app_sock_a, sndbuf, p, 0,
(const struct wolfIP_sockaddr *)&dst_addr,
sizeof(dst_addr));
pump_stacks(&now, 20, 0);

if (app_recv_count > 0) {
/* Any delivered packet must be intact: exact length and bytes.
* This is what catches padding / truncation / buffer bugs. */
ck_assert_int_eq(app_recv_len, p);
for (i = 0; i < p; i++)
ck_assert_uint_eq(app_recv_buf[i],
(uint8_t)((i * 31 + p) & 0xff));
if (p == anchor)
anchor_delivered = 1;
max_delivered = p;
}
}

/* ~1 KB packets flow intact, and delivery never exceeds the wg0 MTU
* (an over-MTU inner packet is dropped, not truncated, since wolfIP has no
* fragmentation, a larger max_delivered would mean a buffer overrun). */
ck_assert_int_eq(anchor_delivered, 1);
ck_assert_int_ge(max_delivered, anchor);
ck_assert_int_le(max_delivered, max_payload);

wolfIP_sock_close(&stack_a, app_sock_a);
wolfIP_sock_close(&stack_b, app_sock_b);
teardown_stacks();
}
END_TEST

/*
* Sustained flood
* This test floods 256 packets to stress the transport path. Each packet
* is uniquely tagged, exercising the replay-counter sliding window implemented
* by wg_counter_validate().
* */
START_TEST(test_sustained_flood)
{
uint64_t now;
int app_sock_a, app_sock_b;
struct wolfIP_sockaddr_in bind_addr, dst_addr;
uint8_t sndbuf[1024];
const int N = 256; /* crosses several replay-bitmap words */
const int payload_len = 1000;
int i, j, ret, delivered = 0;

setup_loopback_stacks(&now);

app_sock_b = wolfIP_sock_socket(&stack_b, AF_INET, SOCK_DGRAM, 0);
ck_assert_int_ge(app_sock_b, 0);
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = ee16(7777);
bind_addr.sin_addr.s_addr = ee32(MAKE_IP4(10,0,0,2));
ck_assert_int_ge(wolfIP_sock_bind(&stack_b, app_sock_b,
(struct wolfIP_sockaddr *)&bind_addr, sizeof(bind_addr)), 0);
wolfIP_register_callback(&stack_b, app_sock_b, app_udp_callback, &stack_b);

app_sock_a = wolfIP_sock_socket(&stack_a, AF_INET, SOCK_DGRAM, 0);
ck_assert_int_ge(app_sock_a, 0);
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = ee16(9999);
bind_addr.sin_addr.s_addr = ee32(MAKE_IP4(10,0,0,1));
ck_assert_int_ge(wolfIP_sock_bind(&stack_a, app_sock_a,
(struct wolfIP_sockaddr *)&bind_addr, sizeof(bind_addr)), 0);

memset(&dst_addr, 0, sizeof(dst_addr));
dst_addr.sin_family = AF_INET;
dst_addr.sin_port = ee16(7777);
dst_addr.sin_addr.s_addr = ee32(MAKE_IP4(10,0,0,2));

/* Establish the session before flooding. */
for (j = 0; j < payload_len; j++)
sndbuf[j] = (uint8_t)(j & 0xff);
ret = wolfIP_sock_sendto(&stack_a, app_sock_a, sndbuf, payload_len, 0,
(const struct wolfIP_sockaddr *)&dst_addr,
sizeof(dst_addr));
ck_assert_int_ge(ret, 0);
pump_stacks(&now, 200, 10);
ck_assert_int_gt(app_recv_count, 0);
ck_assert_ptr_nonnull(wg_dev_a.peers[0].keypairs.current);

/* This is the flood logic, where each
* packet carries its sequence number in the first two bytes so
* delivery, ordering, and integrity are checked per packet. */
for (i = 0; i < N; i++) {
for (j = 0; j < payload_len; j++)
sndbuf[j] = (uint8_t)((j + i) & 0xff);
sndbuf[0] = (uint8_t)(i & 0xff);
sndbuf[1] = (uint8_t)((i >> 8) & 0xff);

app_recv_count = 0;
app_recv_len = 0;

ret = wolfIP_sock_sendto(&stack_a, app_sock_a, sndbuf, payload_len, 0,
(const struct wolfIP_sockaddr *)&dst_addr,
sizeof(dst_addr));
ck_assert_int_ge(ret, 0);
/* Freeze timers (step_ms = 0): keeps a single session for the whole
* flood so the replay counter advances monotonically. With a live
* clock the spec's stale-receive rekey would fire (B never replies),
* resetting the counter mid-flood. */
pump_stacks(&now, 16, 0);

if (app_recv_count > 0) {
ck_assert_int_eq(app_recv_len, payload_len);
ck_assert_uint_eq(app_recv_buf[0], (uint8_t)(i & 0xff));
ck_assert_uint_eq(app_recv_buf[1], (uint8_t)((i >> 8) & 0xff));
delivered++;
}
}

/* Nearly all delivered (small slack for pump-timing stragglers). */
ck_assert_int_ge(delivered, N - 4);

/* Receiver's replay window advanced across the whole flood, crossing many
* 32-bit bitmap words in wg_counter_validate without false rejections. */
ck_assert_ptr_nonnull(wg_dev_b.peers[0].keypairs.current);
ck_assert_uint_ge(wg_dev_b.peers[0].keypairs.current->receiving_counter_max,
(uint64_t)(N - 4));
ck_assert_uint_gt(wg_dev_a.peers[0].tx_bytes,
(uint64_t)(N - 4) * (uint64_t)payload_len);

wolfIP_sock_close(&stack_a, app_sock_a);
wolfIP_sock_close(&stack_b, app_sock_b);
teardown_stacks();
}
END_TEST

/*
* Test suite assembly
* */
Expand Down Expand Up @@ -1027,6 +1235,18 @@ static Suite *wolfguard_integration_suite(void)
tcase_add_test(tc, test_multi_peer);
suite_add_tcase(s, tc);

/* MTU boundary: 1-byte payload sweep across the wg0 MTU */
tc = tcase_create("mtu_boundary");
tcase_set_timeout(tc, 120);
tcase_add_test(tc, test_mtu_boundary_sweep);
suite_add_tcase(s, tc);

/* Sustained flood: replay-window advance under volume */
tc = tcase_create("flood");
tcase_set_timeout(tc, 120);
tcase_add_test(tc, test_sustained_flood);
suite_add_tcase(s, tc);

return s;
}

Expand Down
4 changes: 0 additions & 4 deletions src/wolfguard/wg_allowedips.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
/*
* Compute network mask from CIDR prefix length
* */

static uint32_t cidr_to_mask(uint8_t cidr)
{
if (cidr == 0)
Expand All @@ -29,7 +28,6 @@ static uint32_t cidr_to_mask(uint8_t cidr)
/*
* Insert an allowed IP entry
* */

int wg_allowedips_insert(struct wg_device *dev, uint32_t ip, uint8_t cidr,
uint8_t peer_idx)
{
Expand Down Expand Up @@ -67,7 +65,6 @@ int wg_allowedips_insert(struct wg_device *dev, uint32_t ip, uint8_t cidr,
*
* Returns peer_idx or -1 if no match.
* */

int wg_allowedips_lookup(struct wg_device *dev, uint32_t ip)
{
int i;
Expand All @@ -94,7 +91,6 @@ int wg_allowedips_lookup(struct wg_device *dev, uint32_t ip)
/*
* Remove all entries for a given peer
* */

void wg_allowedips_remove_by_peer(struct wg_device *dev, uint8_t peer_idx)
{
int i;
Expand Down
6 changes: 0 additions & 6 deletions src/wolfguard/wg_cookie.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
* message_mac1_key = Hash("mac1----" || device_public_key)
* cookie_encryption_key = Hash("cookie--" || device_public_key)
* */

void wg_cookie_checker_init(struct wg_cookie_checker *checker,
const uint8_t *device_public_key)
{
Expand All @@ -43,7 +42,6 @@ void wg_cookie_checker_init(struct wg_cookie_checker *checker,
*
* Keys are derived from the remote peer's public key.
* */

void wg_cookie_init(struct wg_cookie *cookie,
const uint8_t *peer_public_key)
{
Expand All @@ -64,7 +62,6 @@ void wg_cookie_init(struct wg_cookie *cookie,
* mac1 = Mac(message_mac1_key, msg[0..mac_offset))
* mac2 = Mac(cookie, msg[0..mac_offset+16)) if cookie is valid
* */

int wg_cookie_add_macs(struct wg_peer *peer, void *msg, size_t msg_len,
size_t mac_offset, uint64_t now)
{
Expand Down Expand Up @@ -107,7 +104,6 @@ int wg_cookie_add_macs(struct wg_peer *peer, void *msg, size_t msg_len,
/*
* Validate mac1 (and optionally mac2) on incoming handshake message
* */

enum wg_cookie_mac_state wg_cookie_validate(
struct wg_cookie_checker *checker, void *msg, size_t msg_len,
size_t mac_offset, uint32_t src_ip, uint16_t src_port, uint64_t now)
Expand Down Expand Up @@ -167,7 +163,6 @@ enum wg_cookie_mac_state wg_cookie_validate(
/*
* Create cookie reply message
* */

int wg_cookie_create_reply(struct wg_device *dev, struct wg_msg_cookie *reply,
const void *triggering_msg, size_t mac_offset,
uint32_t sender_index,
Expand Down Expand Up @@ -235,7 +230,6 @@ int wg_cookie_create_reply(struct wg_device *dev, struct wg_msg_cookie *reply,
/*
* Consume cookie reply message
* */

int wg_cookie_consume_reply(struct wg_peer *peer, struct wg_msg_cookie *msg,
uint64_t now)
{
Expand Down
4 changes: 2 additions & 2 deletions src/wolfguard/wg_noise.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

/* Helper: generate a new sender index.
* Per spec 5.4.2: "Ii (sender index 4 bytes) is generated randomly (p4)
* p^n represents a random bitstring of length n bytes.
* p^n represents a random bitstring of length n bytes."
* */
static uint32_t wg_new_index(struct wg_device *dev)
{
Expand Down Expand Up @@ -65,7 +65,7 @@ void wg_noise_handshake_init(struct wg_handshake *hs,
const uint8_t *preshared_key,
WC_RNG *rng)
{
/* Save PSK before memset preshared_key may alias hs->preshared_key */
/* Save PSK before memset, preshared_key may alias hs->preshared_key */
uint8_t psk_buf[WG_SYMMETRIC_KEY_LEN];
if (preshared_key != NULL)
memcpy(psk_buf, preshared_key, WG_SYMMETRIC_KEY_LEN);
Expand Down
Loading
Loading