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 <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Christof Meerwald
2026-01-06 21:25:16 +00:00
committed by waddlesplash
parent ed66661307
commit 68c1d4b3a8
2 changed files with 101 additions and 2 deletions
@@ -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);
@@ -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<IPv6Header> 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);
}