netinet/in.h: Add IP_DONTFRAG socket option, and implement for IPv4.

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 <[email protected]>
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
Augustin Cavalier
2025-06-28 17:22:47 +00:00
committed by waddlesplash
parent d31febdefa
commit 83aa27ada6
2 changed files with 28 additions and 1 deletions
+2
View File
@@ -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)
@@ -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);
}