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.
This commit is contained in:
parent
9bd55cf24a
commit
2e509336c7
8 changed files with 200 additions and 1 deletions
2
Makefile
2
Makefile
|
|
@ -18,7 +18,7 @@ MAKEFLAGS += --no-print-directory
|
||||||
include sources.mk
|
include sources.mk
|
||||||
|
|
||||||
CC = clang
|
CC = clang
|
||||||
CPPFLAGS = -std=c99 -I includes
|
CPPFLAGS = -std=c99 -I includes -D_POSIX_C_SOURCE=199309L
|
||||||
CFLAGS = -Wall -Wextra -Werror -pipe
|
CFLAGS = -Wall -Wextra -Werror -pipe
|
||||||
CFLAGS_SHARED = $(CFLAGS) -fPIC
|
CFLAGS_SHARED = $(CFLAGS) -fPIC
|
||||||
LDFLAGS =
|
LDFLAGS =
|
||||||
|
|
|
||||||
16
includes/internal/icmp_utils.h
Normal file
16
includes/internal/icmp_utils.h
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef ICMP_UTILS_H
|
||||||
|
#define ICMP_UTILS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* 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
|
||||||
64
src/utils/checksum.c
Normal file
64
src/utils/checksum.c
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* 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];
|
||||||
|
}
|
||||||
17
src/utils/time.c
Normal file
17
src/utils/time.c
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include "internal/icmp_utils.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
58
tests/utils/test_checksum.c
Normal file
58
tests/utils/test_checksum.c
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
44
tests/utils/test_time.c
Normal file
44
tests/utils/test_time.c
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue