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 <no-reply+buildbot@haiku-os.org>
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
Diff
src/add-ons/kernel/network/protocols/icmp6/icmp6.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 101 insertions(+), 2 deletions(-)
@@ -39,6 +39,24 @@
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)
{
@@ -278,6 +296,38 @@
gBufferModule->free(reply);
return status;
}
}
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);
if (data->mtu < 1280)
data = NULL;
}
bufferHeader.Remove();
return domain->module->error_received(error, data, buffer);
}
default:
@@ -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 @@
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));
if (header.ProtocolVersion() != IPV6_VERSION)
return B_BAD_DATA;
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());
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) {
}
buffer->protocol = header.NextHeader();
bufferHeader.Remove(sizeof(IPv6Header));
net_protocol_module_info* protocol = receiving_protocol(buffer->protocol);
if (protocol == NULL)
return B_ERROR;
return protocol->error_received(error, errorData, buffer);
}