diff --git a/includes/icmp.h b/includes/icmp.h index a193dcf..4b7535f 100644 --- a/includes/icmp.h +++ b/includes/icmp.h @@ -44,6 +44,10 @@ int icmp_send_echo(icmp_handle_t *h, struct in_addr dest, uint16_t id, int icmp_process(icmp_handle_t *h, icmp_callback_t cb, void *userdata, size_t max_packets); +/* ID/Sequence helpers */ +int icmp_reply_id_seq(const icmp_reply_t *reply, uint16_t *id, + uint16_t *seq); + /* Error handling */ const char *icmp_strerror(const icmp_handle_t *h); int icmp_should_retry(const icmp_handle_t *h); diff --git a/sources.mk b/sources.mk index 254a253..77ae9f9 100644 --- a/sources.mk +++ b/sources.mk @@ -28,6 +28,7 @@ SRCS += $(SRC_DIR)/recv/core/receive_packet.c SRCS += $(SRC_DIR)/recv/core/parse_packet.c SRCS += $(SRC_DIR)/recv/core/process_single_packet.c SRCS += $(SRC_DIR)/recv/api/process.c +SRCS += $(SRC_DIR)/recv/api/extract_id_seq.c TESTS_DIR = tests TESTS += $(TESTS_DIR)/utils/test_checksum.c @@ -50,3 +51,4 @@ TESTS += $(TESTS_DIR)/recv/test_build_reply.c TESTS += $(TESTS_DIR)/recv/test_receive_packet.c TESTS += $(TESTS_DIR)/recv/test_parse_packet.c TESTS += $(TESTS_DIR)/recv/test_process.c +TESTS += $(TESTS_DIR)/recv/test_extract_id_seq.c diff --git a/src/recv/api/extract_id_seq.c b/src/recv/api/extract_id_seq.c new file mode 100644 index 0000000..efeab19 --- /dev/null +++ b/src/recv/api/extract_id_seq.c @@ -0,0 +1,27 @@ +#include +#include "icmp.h" +#include "icmp_types.h" +#include "internal/icmp_packet.h" + +int +icmp_reply_id_seq(const icmp_reply_t *reply, uint16_t *id, uint16_t *seq) +{ + const struct icmp_header *hdr; + + if (NULL == reply) + return -1; + if (reply->type != ICMP_TYPE_ECHO_REPLY && + reply->type != ICMP_TYPE_ECHO_REQUEST && + reply->type != ICMP_TYPE_TIMESTAMP_REPLY && + reply->type != ICMP_TYPE_TIMESTAMP_REQUEST) + return -1; + if (NULL == reply->ip_payload || reply->ip_payload_len < 8) + return -1; + + hdr = reply->ip_payload; + if (NULL != id) + *id = ntohs(hdr->un.echo.id); + if (NULL != seq) + *seq = ntohs(hdr->un.echo.seq); + return 0; +} diff --git a/tests/recv/test_extract_id_seq.c b/tests/recv/test_extract_id_seq.c new file mode 100644 index 0000000..81bb796 --- /dev/null +++ b/tests/recv/test_extract_id_seq.c @@ -0,0 +1,139 @@ +#include +#include +#include "icmp.h" + +static icmp_reply_t +make_echo_reply(uint8_t type, uint16_t id, uint16_t seq, uint8_t *buffer) +{ + icmp_reply_t reply; + + memset(&reply, 0, sizeof(reply)); + memset(buffer, 0, 8); + reply.type = type; + reply.code = 0; + buffer[0] = type; + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + buffer[4] = (id >> 8) & 0xFF; + buffer[5] = id & 0xFF; + buffer[6] = (seq >> 8) & 0xFF; + buffer[7] = seq & 0xFF; + reply.ip_payload = buffer; + reply.ip_payload_len = 8; + return reply; +} + +Test(extract_echo, echo_reply_type_0) +{ + uint8_t buffer[8]; + icmp_reply_t reply = make_echo_reply(0, 0x1234, 0x5678, buffer); + uint16_t id; + uint16_t seq; + int ret; + + ret = icmp_reply_id_seq(&reply, &id, &seq); + cr_assert_eq(ret, 0); + cr_assert_eq(id, 0x1234); + cr_assert_eq(seq, 0x5678); +} + +Test(extract_echo, echo_request_type_8) +{ + uint8_t buffer[8]; + icmp_reply_t reply = make_echo_reply(8, 0xABCD, 0xEF01, buffer); + uint16_t id; + uint16_t seq; + int ret; + + ret = icmp_reply_id_seq(&reply, &id, &seq); + cr_assert_eq(ret, 0); + cr_assert_eq(id, 0xABCD); + cr_assert_eq(seq, 0xEF01); +} + +Test(extract_echo, non_echo_type_returns_error) +{ + uint8_t buffer[8]; + icmp_reply_t reply = make_echo_reply(3, 0x1234, 0x5678, buffer); + uint16_t id = 0xFFFF; + uint16_t seq = 0xFFFF; + int ret; + + ret = icmp_reply_id_seq(&reply, &id, &seq); + cr_assert_eq(ret, -1); + cr_assert_eq(id, 0xFFFF, "id should be unchanged on error"); + cr_assert_eq(seq, 0xFFFF, "seq should be unchanged on error"); +} + +Test(extract_echo, null_reply_returns_error) +{ + uint16_t id; + uint16_t seq; + int ret; + + ret = icmp_reply_id_seq(NULL, &id, &seq); + cr_assert_eq(ret, -1); +} + +Test(extract_echo, null_id_allowed) +{ + uint8_t buffer[8]; + icmp_reply_t reply = make_echo_reply(0, 0x1234, 0x5678, buffer); + uint16_t seq; + int ret; + + ret = icmp_reply_id_seq(&reply, NULL, &seq); + cr_assert_eq(ret, 0); + cr_assert_eq(seq, 0x5678); +} + +Test(extract_echo, null_seq_allowed) +{ + uint8_t buffer[8]; + icmp_reply_t reply = make_echo_reply(0, 0x1234, 0x5678, buffer); + uint16_t id; + int ret; + + ret = icmp_reply_id_seq(&reply, &id, NULL); + cr_assert_eq(ret, 0); + cr_assert_eq(id, 0x1234); +} + +Test(extract_echo, both_null_allowed) +{ + uint8_t buffer[8]; + icmp_reply_t reply = make_echo_reply(0, 0x1234, 0x5678, buffer); + int ret; + + ret = icmp_reply_id_seq(&reply, NULL, NULL); + cr_assert_eq(ret, 0); +} + +Test(extract_echo, payload_too_short_returns_error) +{ + uint8_t buffer[8]; + icmp_reply_t reply = make_echo_reply(0, 0x1234, 0x5678, buffer); + uint16_t id; + uint16_t seq; + int ret; + + reply.ip_payload_len = 7; + ret = icmp_reply_id_seq(&reply, &id, &seq); + cr_assert_eq(ret, -1); +} + +Test(extract_echo, null_ip_payload_returns_error) +{ + icmp_reply_t reply; + uint16_t id; + uint16_t seq; + int ret; + + memset(&reply, 0, sizeof(reply)); + reply.type = 0; + reply.ip_payload = NULL; + reply.ip_payload_len = 8; + ret = icmp_reply_id_seq(&reply, &id, &seq); + cr_assert_eq(ret, -1); +}