From d1a23fdb5f39216de4910e3849bd96d0754baf7b Mon Sep 17 00:00:00 2001 From: xq9mend Date: Fri, 24 Apr 2026 00:33:47 +0000 Subject: [PATCH] Fix IPv6 extension header length calculation in dhcp6relay Per RFC 2460/8200, ip6e_len is in units of 8 octets, not counting the first 8 bytes. The correct advance is (ip6e_len + 1) * 8, not the raw byte value. Add a unit test with a Hop-by-Hop extension header (ip6e_len=1) to cover the nonzero case, which was previously untested. Signed-off-by: xq9mend --- dhcp6relay/src/relay.cpp | 2 +- dhcp6relay/test/mock_relay.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/dhcp6relay/src/relay.cpp b/dhcp6relay/src/relay.cpp index d2b29b7..ebb27de 100644 --- a/dhcp6relay/src/relay.cpp +++ b/dhcp6relay/src/relay.cpp @@ -947,7 +947,7 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config const struct ip6_ext *ext_header; do { ext_header = (const struct ip6_ext *)current_position; - current_position += ext_header->ip6e_len; + current_position += (ext_header->ip6e_len + 1) * 8; if((current_position == prev) || (current_position + sizeof(*ext_header) >= buffer_end)) { return; diff --git a/dhcp6relay/test/mock_relay.cpp b/dhcp6relay/test/mock_relay.cpp index 6fe7a56..2a75578 100644 --- a/dhcp6relay/test/mock_relay.cpp +++ b/dhcp6relay/test/mock_relay.cpp @@ -721,6 +721,18 @@ TEST(relay, client_packet_handler) { 0x00, 0x00, 0x00, 0x19, 0x00, 0x0c, 0x27, 0xfe, 0x8f, 0x95, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x15, 0x18 }; + // Packet with Hop-by-Hop extension header (ip6e_len=1 => 16 bytes per RFC 2460: (1+1)*8=16). + // Tests that the extension header pointer arithmetic is correct for nonzero ip6e_len. + // IPv6 next_header=0x00 (HbH), ext: nxt=0x11(UDP), len=0x01 -> advance 16 bytes -> UDP dst=547 + uint8_t client_raw_solicit_with_hbh_extension[] = { + 0x33, 0x33, 0x00, 0x01, 0x00, 0x02, 0x08, 0x00, 0x27, 0xfe, 0x8f, 0x95, 0x86, 0xdd, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x27, 0xff, 0xfe, 0xfe, 0x8f, 0x95, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x22, 0x02, 0x23, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x10, + 0x08, 0x74 + }; + uint8_t non_udp_with_externsion[] = { 0x33, 0x33, 0x00, 0x01, 0x00, 0x02, 0x08, 0x00, 0x27, 0xfe, 0x8f, 0x95, 0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2c, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, @@ -745,6 +757,9 @@ TEST(relay, client_packet_handler) { ASSERT_NO_THROW(client_packet_handler(client_raw_solicit_with_externsion, sizeof(client_raw_solicit_with_externsion), &config, ifname)); ASSERT_NO_THROW(client_packet_handler(non_udp_with_externsion, sizeof(non_udp_with_externsion), &config, ifname)); + + // nonzero ip6e_len=1: must advance (1+1)*8=16 bytes per RFC 2460, not 1 byte + ASSERT_NO_THROW(client_packet_handler(client_raw_solicit_with_hbh_extension, sizeof(client_raw_solicit_with_hbh_extension), &config, ifname)); } MOCK_GLOBAL_FUNC6(recvfrom, ssize_t(int, void *, size_t, int, struct sockaddr *, socklen_t *));