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
This commit is contained in:
parent
f85b342f8c
commit
ad7698d530
6 changed files with 69 additions and 24 deletions
|
|
@ -27,9 +27,10 @@ typedef struct icmp_reply {
|
||||||
typedef struct icmp_offending_packet {
|
typedef struct icmp_offending_packet {
|
||||||
struct in_addr src;
|
struct in_addr src;
|
||||||
struct in_addr dst;
|
struct in_addr dst;
|
||||||
uint8_t protocol;
|
uint8_t protocol;
|
||||||
uint8_t icmp_type;
|
uint8_t icmp_type;
|
||||||
uint8_t icmp_code;
|
uint8_t icmp_code;
|
||||||
|
uint16_t next_mtu; /* MTU from outer ICMP (type=3, code=4) */
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
|
|
@ -40,7 +41,7 @@ typedef struct icmp_offending_packet {
|
||||||
uint16_t unused;
|
uint16_t unused;
|
||||||
uint16_t mtu;
|
uint16_t mtu;
|
||||||
} frag;
|
} frag;
|
||||||
uint8_t raw[4];
|
uint8_t raw[4];
|
||||||
} rest;
|
} rest;
|
||||||
} icmp_offending_packet_t;
|
} 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,
|
int icmp_error_extract_offending(const icmp_reply_t *reply,
|
||||||
icmp_offending_packet_t *offending);
|
icmp_offending_packet_t *offending);
|
||||||
|
|
||||||
|
/* Socket options */
|
||||||
|
int icmp_set_dont_fragment(icmp_handle_t *h);
|
||||||
|
|
||||||
/* Error handling */
|
/* Error handling */
|
||||||
const char *icmp_strerror(const icmp_handle_t *h);
|
const char *icmp_strerror(const icmp_handle_t *h);
|
||||||
int icmp_should_retry(const icmp_handle_t *h);
|
int icmp_should_retry(const icmp_handle_t *h);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ SRCS += $(SRC_DIR)/error/strerror.c
|
||||||
SRCS += $(SRC_DIR)/error/should_retry.c
|
SRCS += $(SRC_DIR)/error/should_retry.c
|
||||||
SRCS += $(SRC_DIR)/socket/create.c
|
SRCS += $(SRC_DIR)/socket/create.c
|
||||||
SRCS += $(SRC_DIR)/socket/configure.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/create.c
|
||||||
SRCS += $(SRC_DIR)/handle/destroy.c
|
SRCS += $(SRC_DIR)/handle/destroy.c
|
||||||
SRCS += $(SRC_DIR)/handle/get_fd.c
|
SRCS += $(SRC_DIR)/handle/get_fd.c
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ icmp_error_extract_offending(const icmp_reply_t *reply,
|
||||||
|
|
||||||
hdr = get_embedded_icmp(reply, ip_hdr_len);
|
hdr = get_embedded_icmp(reply, ip_hdr_len);
|
||||||
extract_icmp_fields(hdr, offending);
|
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;
|
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.id = ntohs(hdr->un.echo.id);
|
||||||
offending->rest.echo.seq = ntohs(hdr->un.echo.seq);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
src/socket/set_dont_fragment.c
Normal file
26
src/socket/set_dont_fragment.c
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
@ -41,8 +41,10 @@ sum_words(const uint8_t *data, size_t len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle odd byte if present */
|
/* Handle odd byte if present: point to last byte or a zero sentinel */
|
||||||
sum += ((uint32_t)(*ptr << 8)) & (uint32_t)(-(len & 1));
|
static const uint8_t zero = 0;
|
||||||
|
ptr = (len & 1) ? ptr : &zero;
|
||||||
|
sum += (uint32_t)(*ptr << 8);
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -213,27 +213,37 @@ Test(extract_offending, ip_header_with_options)
|
||||||
cr_assert_eq(offending.rest.echo.seq, 0xAAAA);
|
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];
|
/* outer ICMP header (8 bytes): type=3, code=4, unused=0, mtu=1400 */
|
||||||
icmp_reply_t reply = make_error_reply(ICMP_TYPE_TIME_EXCEEDED, buffer, sizeof(buffer));
|
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);
|
memset(outer, 0, sizeof(outer));
|
||||||
/* Embedded packet is itself a Dest Unreachable / Frag Needed */
|
outer[0] = ICMP_TYPE_DEST_UNREACHABLE;
|
||||||
buffer[20] = ICMP_TYPE_DEST_UNREACHABLE;
|
outer[1] = ICMP_CODE_FRAG_NEEDED;
|
||||||
buffer[21] = ICMP_CODE_FRAG_NEEDED;
|
*(uint16_t *)(outer + 6) = htons(1400); /* next-hop MTU */
|
||||||
buffer[22] = 0;
|
|
||||||
buffer[23] = 0;
|
fill_ip_header(embedded, "10.0.0.1", "10.0.0.2", 1);
|
||||||
*(uint16_t *)(buffer + 24) = 0; /* unused */
|
fill_icmp_header(embedded, ICMP_TYPE_ECHO_REQUEST, 0, 0x1234, 0x0001);
|
||||||
*(uint16_t *)(buffer + 26) = htons(1400); /* mtu in network order */
|
|
||||||
|
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;
|
icmp_offending_packet_t offending;
|
||||||
int ret = icmp_error_extract_offending(&reply, &offending);
|
int ret = icmp_error_extract_offending(&reply, &offending);
|
||||||
|
|
||||||
cr_assert_eq(ret, 0);
|
cr_assert_eq(ret, 0);
|
||||||
cr_assert_eq(offending.icmp_type, ICMP_TYPE_DEST_UNREACHABLE);
|
cr_assert_eq(offending.icmp_type, ICMP_TYPE_ECHO_REQUEST);
|
||||||
cr_assert_eq(offending.icmp_code, ICMP_CODE_FRAG_NEEDED);
|
cr_assert_eq(offending.rest.echo.id, 0x1234);
|
||||||
cr_assert_eq(offending.rest.frag.mtu, 1400, "MTU should be in host byte order");
|
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)
|
Test(extract_offending, redirect_with_tcp_packet)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue