From ad7698d530ffaf2f3e603e8e94cce9b7591ab3dd Mon Sep 17 00:00:00 2001 From: lohhiiccc <96543753+lohhiiccc@users.noreply.github.com> Date: Fri, 13 Mar 2026 03:21:35 +0100 Subject: [PATCH] feat: add icmp_set_dont_fragment and next_mtu extraction - Add icmp_set_dont_fragment() using IP_MTU_DISCOVER/IP_PMTUDISC_DO - Add next_mtu field to icmp_offending_packet_t, populated from the outer ICMP header on type=3/code=4 (frag needed) replies - Fix extract_offending: read MTU from outer ICMP, not embedded packet - Update test to match corrected extraction semantics - Clean up checksum odd-byte handling with a static zero sentinel --- includes/icmp.h | 12 ++++++--- sources.mk | 1 + src/recv/api/extract_offending.c | 10 +++++--- src/socket/set_dont_fragment.c | 26 ++++++++++++++++++++ src/utils/checksum.c | 6 +++-- tests/recv/test_extract_offending.c | 38 ++++++++++++++++++----------- 6 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 src/socket/set_dont_fragment.c diff --git a/includes/icmp.h b/includes/icmp.h index ad2bcd3..947d6a3 100644 --- a/includes/icmp.h +++ b/includes/icmp.h @@ -27,9 +27,10 @@ typedef struct icmp_reply { typedef struct icmp_offending_packet { struct in_addr src; struct in_addr dst; - uint8_t protocol; - uint8_t icmp_type; - uint8_t icmp_code; + uint8_t protocol; + uint8_t icmp_type; + uint8_t icmp_code; + uint16_t next_mtu; /* MTU from outer ICMP (type=3, code=4) */ union { struct { uint16_t id; @@ -40,7 +41,7 @@ typedef struct icmp_offending_packet { uint16_t unused; uint16_t mtu; } frag; - uint8_t raw[4]; + uint8_t raw[4]; } rest; } icmp_offending_packet_t; @@ -74,6 +75,9 @@ int icmp_reply_id_seq(const icmp_reply_t *reply, uint16_t *id, int icmp_error_extract_offending(const icmp_reply_t *reply, icmp_offending_packet_t *offending); +/* Socket options */ +int icmp_set_dont_fragment(icmp_handle_t *h); + /* 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 7d28607..a1c402b 100644 --- a/sources.mk +++ b/sources.mk @@ -8,6 +8,7 @@ SRCS += $(SRC_DIR)/error/strerror.c SRCS += $(SRC_DIR)/error/should_retry.c SRCS += $(SRC_DIR)/socket/create.c SRCS += $(SRC_DIR)/socket/configure.c +SRCS += $(SRC_DIR)/socket/set_dont_fragment.c SRCS += $(SRC_DIR)/handle/create.c SRCS += $(SRC_DIR)/handle/destroy.c SRCS += $(SRC_DIR)/handle/get_fd.c diff --git a/src/recv/api/extract_offending.c b/src/recv/api/extract_offending.c index dee080c..272a865 100644 --- a/src/recv/api/extract_offending.c +++ b/src/recv/api/extract_offending.c @@ -37,6 +37,12 @@ icmp_error_extract_offending(const icmp_reply_t *reply, hdr = get_embedded_icmp(reply, ip_hdr_len); extract_icmp_fields(hdr, offending); + offending->next_mtu = 0; + if (is_frag_needed(reply->type, reply->code) && NULL != reply->ip_payload) { + const struct icmp_header *outer; + outer = (const struct icmp_header *)reply->ip_payload; + offending->next_mtu = ntohs(outer->un.frag.mtu); + } return 0; } @@ -92,8 +98,4 @@ extract_icmp_fields(const struct icmp_header *hdr, icmp_offending_packet_t *offe offending->rest.echo.id = ntohs(hdr->un.echo.id); offending->rest.echo.seq = ntohs(hdr->un.echo.seq); } - else if (is_frag_needed(hdr->type, hdr->code)) - { - offending->rest.frag.mtu = ntohs(hdr->un.frag.mtu); - } } diff --git a/src/socket/set_dont_fragment.c b/src/socket/set_dont_fragment.c new file mode 100644 index 0000000..a35a4b0 --- /dev/null +++ b/src/socket/set_dont_fragment.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +#include "internal/icmp_internal.h" + +int +icmp_set_dont_fragment(struct icmp_handle *h) +{ + int val; + int saved_errno; + + if (NULL == h) + return -1; + val = IP_PMTUDISC_DO; + if (0 > setsockopt(h->fd, IPPROTO_IP, + IP_MTU_DISCOVER, &val, sizeof(val))) + { + saved_errno = errno; + icmp_set_error_fmt(h, ICMP_ERR_SOCKET, + "Failed to set DF bit: %s", strerror(saved_errno)); + return -1; + } + return 0; +} diff --git a/src/utils/checksum.c b/src/utils/checksum.c index e615112..cc45fbc 100644 --- a/src/utils/checksum.c +++ b/src/utils/checksum.c @@ -41,8 +41,10 @@ sum_words(const uint8_t *data, size_t len) } } - /* Handle odd byte if present */ - sum += ((uint32_t)(*ptr << 8)) & (uint32_t)(-(len & 1)); + /* Handle odd byte if present: point to last byte or a zero sentinel */ + static const uint8_t zero = 0; + ptr = (len & 1) ? ptr : &zero; + sum += (uint32_t)(*ptr << 8); return sum; } diff --git a/tests/recv/test_extract_offending.c b/tests/recv/test_extract_offending.c index bcfe53f..d72309f 100644 --- a/tests/recv/test_extract_offending.c +++ b/tests/recv/test_extract_offending.c @@ -213,27 +213,37 @@ Test(extract_offending, ip_header_with_options) cr_assert_eq(offending.rest.echo.seq, 0xAAAA); } -Test(extract_offending, embedded_frag_needed_mtu) +Test(extract_offending, frag_needed_mtu_from_outer_icmp) { - uint8_t buffer[28]; - icmp_reply_t reply = make_error_reply(ICMP_TYPE_TIME_EXCEEDED, buffer, sizeof(buffer)); + /* outer ICMP header (8 bytes): type=3, code=4, unused=0, mtu=1400 */ + uint8_t outer[8]; + /* embedded IP (20 bytes) + embedded ICMP echo request (8 bytes) */ + uint8_t embedded[28]; + icmp_reply_t reply; - fill_ip_header(buffer, "10.0.0.1", "10.0.0.2", 1); - /* Embedded packet is itself a Dest Unreachable / Frag Needed */ - buffer[20] = ICMP_TYPE_DEST_UNREACHABLE; - buffer[21] = ICMP_CODE_FRAG_NEEDED; - buffer[22] = 0; - buffer[23] = 0; - *(uint16_t *)(buffer + 24) = 0; /* unused */ - *(uint16_t *)(buffer + 26) = htons(1400); /* mtu in network order */ + memset(outer, 0, sizeof(outer)); + outer[0] = ICMP_TYPE_DEST_UNREACHABLE; + outer[1] = ICMP_CODE_FRAG_NEEDED; + *(uint16_t *)(outer + 6) = htons(1400); /* next-hop MTU */ + + fill_ip_header(embedded, "10.0.0.1", "10.0.0.2", 1); + fill_icmp_header(embedded, ICMP_TYPE_ECHO_REQUEST, 0, 0x1234, 0x0001); + + memset(&reply, 0, sizeof(reply)); + reply.type = ICMP_TYPE_DEST_UNREACHABLE; + reply.code = ICMP_CODE_FRAG_NEEDED; + reply.payload = embedded; + reply.payload_len = sizeof(embedded); + reply.ip_payload = outer; icmp_offending_packet_t offending; int ret = icmp_error_extract_offending(&reply, &offending); cr_assert_eq(ret, 0); - cr_assert_eq(offending.icmp_type, ICMP_TYPE_DEST_UNREACHABLE); - cr_assert_eq(offending.icmp_code, ICMP_CODE_FRAG_NEEDED); - cr_assert_eq(offending.rest.frag.mtu, 1400, "MTU should be in host byte order"); + cr_assert_eq(offending.icmp_type, ICMP_TYPE_ECHO_REQUEST); + cr_assert_eq(offending.rest.echo.id, 0x1234); + cr_assert_eq(offending.rest.echo.seq, 0x0001); + cr_assert_eq(offending.next_mtu, 1400, "MTU should be in host byte order"); } Test(extract_offending, redirect_with_tcp_packet)