From 83aa27ada6252e4df19feca391f824ae9dc7492d Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Thu, 26 Jun 2025 22:02:08 -0400 Subject: [PATCH] netinet/in.h: Add IP_DONTFRAG socket option, and implement for IPv4. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow consumers to cause the "don't fragment" bit to be set in all IPv4 packet headers. There is no standard way of doing this, and different OSes expose this option in different ways. Linux has "IP_MTU_DISCOVER", but it takes an enum, not a boolean. NetBSD and OpenBSD appear to have no socket option, instead they have "IP_MTUDISC", an option for the "ip_output()" kernel-level network stack method. "IP_DONTFRAG" sockopt originates on FreeBSD, and it seems macOS now also supports it in version 11+. Windows has "IP_DONTFRAGMENT", which, at a glance, appears to do the same thing. So this looks like the one that makes the most sense to adopt. This doesn't add any code to process MTU changes yet, though. Change-Id: I492d22dbd0ee5f4ab35c600396ad3d3ec9f4f200 Reviewed-on: https://review.haiku-os.org/c/haiku/+/9401 Reviewed-by: waddlesplash Tested-by: Commit checker robot Reviewed-by: Jérôme Duval --- headers/posix/netinet/in.h | 2 ++ .../kernel/network/protocols/ipv4/ipv4.cpp | 27 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/headers/posix/netinet/in.h b/headers/posix/netinet/in.h index f2e2dc1db7..2bb0fc5201 100644 --- a/headers/posix/netinet/in.h +++ b/headers/posix/netinet/in.h @@ -164,6 +164,8 @@ struct group_source_req { #define IPV6_DSTOPTS 36 /* struct ip6_dest */ #define IPV6_RTHDR 37 /* struct ip6_rthdr */ +#define IP_DONTFRAG 38 /* bool; don't fragment */ + #define INADDR_ANY ((in_addr_t)0x00000000) #define INADDR_LOOPBACK ((in_addr_t)0x7f000001) diff --git a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp index 101b285a86..41a195ee1a 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp @@ -188,6 +188,7 @@ struct ipv4_protocol : net_protocol { // protocol flags #define IP_FLAG_HEADER_INCLUDED 0x01 #define IP_FLAG_RECEIVE_DEST_ADDR 0x02 +#define IP_FLAG_DONT_FRAGMENT 0x04 static const int kDefaultTTL = 254; @@ -1203,6 +1204,10 @@ ipv4_getsockopt(net_protocol* _protocol, int level, int option, void* value, return get_int_option(value, *_length, (protocol->flags & IP_FLAG_RECEIVE_DEST_ADDR) != 0); } + if (option == IP_DONTFRAG) { + return get_int_option(value, *_length, + (protocol->flags & IP_FLAG_DONT_FRAGMENT) != 0); + } if (option == IP_TTL) return get_int_option(value, *_length, protocol->time_to_live); if (option == IP_TOS) @@ -1302,6 +1307,21 @@ ipv4_setsockopt(net_protocol* _protocol, int level, int option, return B_OK; } + if (option == IP_DONTFRAG) { + int headerIncluded; + if (length != sizeof(int)) + return B_BAD_VALUE; + if (user_memcpy(&headerIncluded, value, sizeof(headerIncluded)) + != B_OK) + return B_BAD_ADDRESS; + + if (headerIncluded) + protocol->flags |= IP_FLAG_DONT_FRAGMENT; + else + protocol->flags &= ~IP_FLAG_DONT_FRAGMENT; + + return B_OK; + } if (option == IP_TTL) return set_int_option(protocol->time_to_live, value, length); if (option == IP_TOS) @@ -1521,7 +1541,9 @@ ipv4_send_routed_data(net_protocol* _protocol, struct net_route* route, header->total_length = htons(buffer->size); header->id = htons(atomic_add(&sPacketID, 1)); header->fragment_offset = 0; - if (protocol) { + if (protocol != NULL) { + if ((protocol->flags & IP_FLAG_DONT_FRAGMENT) != 0) + header->fragment_offset |= htons(IP_DONT_FRAGMENT); header->time_to_live = (buffer->msg_flags & MSG_MCAST) != 0 ? protocol->multicast_time_to_live : protocol->time_to_live; } else { @@ -1602,6 +1624,9 @@ ipv4_send_routed_data(net_protocol* _protocol, struct net_route* route, uint32 mtu = route->mtu ? route->mtu : interface->device->mtu; if (buffer->size > mtu) { + if (protocol != NULL && (protocol->flags & IP_FLAG_DONT_FRAGMENT) != 0) + return EMSGSIZE; + // we need to fragment the packet return send_fragments(protocol, route, buffer, mtu); }