From 68c1d4b3a8f014b9366ae19c32c063e89d991b7c Mon Sep 17 00:00:00 2001 From: Christof Meerwald Date: Mon, 29 Dec 2025 21:52:55 +0000 Subject: [PATCH] IPv6: Path MTU Discovery * Copied and adapted IPv4 error handling code for IPv6 (to handle ICMP6_PACKET_TOO_BIG) Change-Id: Id14cf6221626b5c723fbe77f19aa0d6b9d25f36a Reviewed-on: https://review.haiku-os.org/c/haiku/+/10170 Tested-by: Commit checker robot Reviewed-by: waddlesplash --- .../kernel/network/protocols/icmp6/icmp6.cpp | 50 +++++++++++++++++ .../kernel/network/protocols/ipv6/ipv6.cpp | 53 ++++++++++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/add-ons/kernel/network/protocols/icmp6/icmp6.cpp b/src/add-ons/kernel/network/protocols/icmp6/icmp6.cpp index def182d54b..ae75d03240 100644 --- a/src/add-ons/kernel/network/protocols/icmp6/icmp6.cpp +++ b/src/add-ons/kernel/network/protocols/icmp6/icmp6.cpp @@ -39,6 +39,24 @@ static net_stack_module_info *sStackModule; static net_ndp_module_info *sIPv6NDPModule; +static net_error +icmp6_to_net_error(uint8 type, uint8 code) +{ + switch (type) { + case ICMP6_PARAM_PROB: + return B_NET_ERROR_PARAMETER_PROBLEM; + + case ICMP6_PACKET_TOO_BIG: + return B_NET_ERROR_MESSAGE_SIZE; + + default: + break; + } + + return (net_error)0; +} + + net_protocol * icmp6_init_protocol(net_socket *socket) { @@ -280,6 +298,38 @@ icmp6_receive_data(net_buffer *buffer) } } + case ICMP6_DST_UNREACH: + case ICMP6_PACKET_TOO_BIG: + case ICMP6_TIME_EXCEEDED: + case ICMP6_PARAM_PROB: + { + net_domain* domain = get_domain(buffer); + if (domain == NULL) + break; + + net_error error = icmp6_to_net_error(header.icmp6_type, + header.icmp6_code); + if (error == 0) + break; + + net_error_data dataStorage = {}; + net_error_data* data = NULL; + if (error == B_NET_ERROR_MESSAGE_SIZE) { + data = &dataStorage; + data->mtu = ntohl(header.icmp6_mtu); + + // IPv6 minimum fragment size is 1280 bytes, so if the "next MTU" + // is smaller than that, we can be sure it's invalid. + if (data->mtu < 1280) + data = NULL; + } + + // Deliver the error to the domain protocol which will + // propagate the error to the upper protocols + bufferHeader.Remove(); + return domain->module->error_received(error, data, buffer); + } + default: // unrecognized messages go to neighbor discovery protocol handler return sIPv6NDPModule->receive_data(buffer); diff --git a/src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp b/src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp index 386cd47088..08e7c4d05c 100644 --- a/src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp +++ b/src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp @@ -45,9 +45,11 @@ #define TRACE_SK(protocol, format, args...) \ dprintf("IPv6 [%" B_PRIdBIGTIME "] %p " format "\n", system_time(), \ protocol, ##args) + #define TRACE_ONLY(x) x #else #define TRACE(args...) #define TRACE_SK(args...) + #define TRACE_ONLY(x) #endif @@ -1579,9 +1581,56 @@ ipv6_deliver_data(net_protocol* _protocol, net_buffer* buffer) status_t -ipv6_error_received(net_error error, net_error_data* errorData, net_buffer* data) +ipv6_error_received(net_error error, net_error_data* errorData, net_buffer* buffer) { - return B_ERROR; + TRACE(" ipv6_error_received(error %d, buffer %p [%" B_PRIu32 " bytes])", + (int)error, buffer, buffer->size); + + NetBufferHeaderReader bufferHeader(buffer); + if (bufferHeader.Status() != B_OK) + return bufferHeader.Status(); + + IPv6Header& header = bufferHeader.Data(); + TRACE_ONLY(dump_ipv6_header(header)); + + // We do not check the packet length, as we usually only get a part of it + if (header.ProtocolVersion() != IPV6_VERSION) + return B_BAD_DATA; + + // Restore addresses of the original buffer + + // lower layers notion of broadcast or multicast have no relevance to us + // TODO: they actually have when deciding whether to send an ICMP error + buffer->msg_flags &= ~(MSG_BCAST | MSG_MCAST); + + fill_sockaddr_in6((struct sockaddr_in6*)buffer->source, header.Src()); + fill_sockaddr_in6((struct sockaddr_in6*)buffer->destination, + header.Dst()); + + // test if the packet is really from us + if (!sDatalinkModule->is_local_address(sDomain, buffer->source, NULL, + NULL)) { + TRACE(" ipv6_error_received(): packet was not for us"); + return B_ERROR; + } + + if (error == B_NET_ERROR_MESSAGE_SIZE) { + if (errorData != NULL) + errorData->mtu -= sizeof(IPv6Header); + } else if (error == B_NET_ERROR_REDIRECT_HOST) { + // TODO: Update the routing table! + } + + buffer->protocol = header.NextHeader(); + + bufferHeader.Remove(sizeof(IPv6Header)); + + net_protocol_module_info* protocol = receiving_protocol(buffer->protocol); + if (protocol == NULL) + return B_ERROR; + + // propagate error + return protocol->error_received(error, errorData, buffer); }