From 2e509336c70e95beeb2a84d82859038a72d6811c Mon Sep 17 00:00:00 2001 From: lohhiiccc <96543753+lohhiiccc@users.noreply.github.com> Date: Sat, 24 Jan 2026 22:00:24 +0100 Subject: [PATCH] feat(utils): add ICMP checksum and time utilities with tests - Added `icmp_checksum`. - Added `icmp_get_time`. - Created unit tests for checksum and time. - Set `-D_POSIX_C_SOURCE=199309L` in CPPFLAGS for clock_gettime support. --- Makefile | 2 +- includes/internal/icmp_utils.h | 16 +++++++++ src/utils/.gitkeep | 0 src/utils/checksum.c | 64 ++++++++++++++++++++++++++++++++++ src/utils/time.c | 17 +++++++++ tests/utils/.gitkeep | 0 tests/utils/test_checksum.c | 58 ++++++++++++++++++++++++++++++ tests/utils/test_time.c | 44 +++++++++++++++++++++++ 8 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 includes/internal/icmp_utils.h delete mode 100644 src/utils/.gitkeep create mode 100644 src/utils/checksum.c create mode 100644 src/utils/time.c delete mode 100644 tests/utils/.gitkeep create mode 100644 tests/utils/test_checksum.c create mode 100644 tests/utils/test_time.c diff --git a/Makefile b/Makefile index 47a8f9a..14ffe03 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ MAKEFLAGS += --no-print-directory include sources.mk CC = clang -CPPFLAGS = -std=c99 -I includes +CPPFLAGS = -std=c99 -I includes -D_POSIX_C_SOURCE=199309L CFLAGS = -Wall -Wextra -Werror -pipe CFLAGS_SHARED = $(CFLAGS) -fPIC LDFLAGS = diff --git a/includes/internal/icmp_utils.h b/includes/internal/icmp_utils.h new file mode 100644 index 0000000..2162bbe --- /dev/null +++ b/includes/internal/icmp_utils.h @@ -0,0 +1,16 @@ +#ifndef ICMP_UTILS_H +#define ICMP_UTILS_H + +#include +#include +#include + +/* Checksum calculation (RFC 1071) */ +uint16_t icmp_checksum(const void *data, size_t len); + +/* Time helpers - return 0 on success, -1 on error */ +int icmp_get_time(struct timespec *ts); +int64_t icmp_time_diff_ns(const struct timespec *start, + const struct timespec *end); + +#endif diff --git a/src/utils/.gitkeep b/src/utils/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils/checksum.c b/src/utils/checksum.c new file mode 100644 index 0000000..fc696d7 --- /dev/null +++ b/src/utils/checksum.c @@ -0,0 +1,64 @@ +#include +#include + +/* Forward Declarations */ +static inline uint16_t read_be16(const uint8_t *ptr); +static uint32_t sum_words(const uint8_t *data, size_t len); +static inline uint16_t fold_carries(uint32_t sum); +/* -------------------- */ + +/* RFC 1071: Compute ICMP checksum (16-bit one's complement sum) */ +uint16_t +icmp_checksum(const void *data, size_t len) +{ + uint32_t sum; + + sum = sum_words(data, len); + sum = fold_carries(sum); + + return ~sum; +} + +/* Sum all 16-bit words */ +static uint32_t +sum_words(const uint8_t *data, size_t len) +{ + const uint8_t *ptr = data; + uint32_t sum = 0; + size_t words = len >> 1; + + /* Duff's device: unroll loop by 4 */ + if (words > 0) + { + size_t n = (words + 3) >> 2; + switch (words & 3) { + case 0: do { sum += read_be16(ptr); ptr += 2; + case 3: sum += read_be16(ptr); ptr += 2; + case 2: sum += read_be16(ptr); ptr += 2; + case 1: sum += read_be16(ptr); ptr += 2; + } while (--n > 0); + } + } + + /* Handle odd byte if present */ + if (len & 1) + sum += *ptr << 8; + + return sum; +} + +/* Fold 32-bit sum to 16 bits by adding carry bits back */ +static inline uint16_t +fold_carries(uint32_t sum) +{ + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + return sum; +} + +/* Read 16-bit word in network byte order (big-endian) */ +static inline uint16_t +read_be16(const uint8_t *ptr) +{ + return (*ptr << 8) | ptr[1]; +} diff --git a/src/utils/time.c b/src/utils/time.c new file mode 100644 index 0000000..0e8c123 --- /dev/null +++ b/src/utils/time.c @@ -0,0 +1,17 @@ +#include "internal/icmp_utils.h" +#include + +int +icmp_get_time(struct timespec *ts) +{ + if (0 != clock_gettime(CLOCK_REALTIME, ts)) + return -1; + return 0; +} + +int64_t +icmp_time_diff_ns(const struct timespec *start, const struct timespec *end) +{ + return (end->tv_sec - start->tv_sec) * 1000000000LL + + (end->tv_nsec - start->tv_nsec); +} diff --git a/tests/utils/.gitkeep b/tests/utils/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/utils/test_checksum.c b/tests/utils/test_checksum.c new file mode 100644 index 0000000..10cedcf --- /dev/null +++ b/tests/utils/test_checksum.c @@ -0,0 +1,58 @@ +#include +#include +#include "internal/icmp_utils.h" + +/* Test 1: Simple ICMP Echo Request header (8 bytes, even length) */ +Test(checksum, echo_request_header) +{ + /* ICMP Echo Request: type=8, code=0, checksum=0, id=0x1234, seq=0x0001 */ + uint8_t packet[] = { + 0x08, 0x00, 0x00, 0x00, /* type, code, checksum (zeroed) */ + 0x12, 0x34, 0x00, 0x01 /* id=0x1234, seq=0x0001 */ + }; + + uint16_t result = icmp_checksum(packet, sizeof(packet)); + + /* Expected checksum calculated manually (network byte order): + * Words: 0x0800, 0x0000, 0x1234, 0x0001 + * Sum: 0x0800 + 0x0000 + 0x1234 + 0x0001 = 0x1A35 + * Checksum: ~0x1A35 = 0xE5CA */ + cr_assert_eq(result, 0xE5CA, + "Expected checksum 0xE5CA, got 0x%04X", result); +} + +/* Test 2: Odd length packet (9 bytes) */ +Test(checksum, odd_length) +{ + /* ICMP packet with 9 bytes (last byte should be treated as 0xAB00) */ + uint8_t packet[] = { + 0x08, 0x00, 0x00, 0x00, /* type, code, checksum */ + 0x00, 0x00, 0x00, 0x00, /* id, seq */ + 0xAB /* 1 byte payload */ + }; + + uint16_t result = icmp_checksum(packet, sizeof(packet)); + + /* Words: 0x0800, 0x0000, 0x0000, 0x0000, 0xAB00 (last byte in high) + * Sum: 0x0800 + 0x0000 + 0x0000 + 0x0000 + 0xAB00 = 0xB300 + * Checksum: ~0xB300 = 0x4CFF */ + cr_assert_eq(result, 0x4CFF, + "Expected checksum 0x4CFF, got 0x%04X", result); +} + +/* Test 3: Verify checksum - a packet with correct checksum + * should give 0x0000 when checksummed (including the checksum field) */ +Test(checksum, verify_correct_checksum) +{ + /* ICMP Echo Request with correct checksum already filled in */ + uint8_t packet[] = { + 0x08, 0x00, 0xE5, 0xCA, /* type, code, checksum=0xE5CA */ + 0x12, 0x34, 0x00, 0x01 /* id=0x1234, seq=0x0001 */ + }; + + /* When checksum is included in calculation, result should be 0 */ + uint16_t result = icmp_checksum(packet, sizeof(packet)); + + cr_assert_eq(result, 0x0000, + "Valid packet should checksum to 0x0000, got 0x%04X", result); +} diff --git a/tests/utils/test_time.c b/tests/utils/test_time.c new file mode 100644 index 0000000..b215a9e --- /dev/null +++ b/tests/utils/test_time.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include "internal/icmp_utils.h" + +/* Test 1: icmp_get_time() fills the timespec structure */ +Test(time, get_time_fills_struct) +{ + struct timespec ts = {0}; + + int ret = icmp_get_time(&ts); + + cr_assert_eq(ret, 0, "Expected 0 (success), got %d", ret); + /* Should have non-zero seconds */ + cr_assert_neq(ts.tv_sec, 0, + "Expected non-zero timestamp, got tv_sec=%ld", ts.tv_sec); +} + +/* Test 2: Time difference with same timestamp should be zero */ +Test(time, diff_same_time_is_zero) +{ + struct timespec ts; + int ret = icmp_get_time(&ts); + + cr_assert_eq(ret, 0, "Expected 0 (success), got %d", ret); + + int64_t diff = icmp_time_diff_ns(&ts, &ts); + + cr_assert_eq(diff, 0, "Same timestamp should give 0ns diff, got %ld", diff); +} + +/* Test 3: Time difference calculation is correct */ +Test(time, diff_calculation) +{ + struct timespec start = {.tv_sec = 10, .tv_nsec = 500000000}; /* 10.5s */ + struct timespec end = {.tv_sec = 12, .tv_nsec = 750000000}; /* 12.75s */ + + int64_t diff = icmp_time_diff_ns(&start, &end); + + /* Expected: (12 - 10) * 1e9 + (750000000 - 500000000) + * = 2000000000 + 250000000 = 2250000000 ns = 2.25s */ + cr_assert_eq(diff, 2250000000LL, + "Expected 2250000000ns, got %ld", diff); +}