From: Alan T. DeKok Date: Wed, 16 Apr 2025 15:58:19 +0000 (-0400) Subject: add "exceed_mtu" configuration flag for UDP sockets. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fb5942214adc2f1fcd7a8906ec88f2e687d2a20f;p=thirdparty%2Ffreeradius-server.git add "exceed_mtu" configuration flag for UDP sockets. which defaults to "yes". When set to "yes", clears the "don't fragment" bit in the IP packet header. When set to "no", it uses the OS definition for the DF bit. Note that we do NOT set the DF bit. OSX supports IP_DONTFRAG only for raw sockets. Linux always sets the DF flag for UDP sockets. No matter what this flag is set to, there are some situations where UDP packets will silently disappear in the network. When DF is set, "too large" packets might get an ICMP error to the OS, which the server will currently ignore. When DF is clear, "too large" packets might be silently discarded by some other network element. We can later add code to do actual PMTU discovery --- diff --git a/src/lib/bio/fd.h b/src/lib/bio/fd.h index d5baa7e3595..81ed87de929 100644 --- a/src/lib/bio/fd.h +++ b/src/lib/bio/fd.h @@ -111,6 +111,7 @@ typedef struct { bool async; //!< is it async bool tcp_delay; //!< We do tcp_nodelay by default. + bool exceed_mtu; //!< Whether we allow packets which exceed the local MTU /* * Extra fields for conf_parser_t diff --git a/src/lib/bio/fd_config.c b/src/lib/bio/fd_config.c index 2385740290d..97b2dc10114 100644 --- a/src/lib/bio/fd_config.c +++ b/src/lib/bio/fd_config.c @@ -118,6 +118,10 @@ static const conf_parser_t client_udp_sub_config[] = { { FR_CONF_OFFSET("interface", fr_bio_fd_config_t, interface) }, +#if (defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)) || defined(IP_DONTFRAG) + { FR_CONF_OFFSET("exceed_mtu", fr_bio_fd_config_t, exceed_mtu), .dflt = "yes" }, +#endif + { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, fr_bio_fd_config_t, recv_buff) }, { FR_CONF_OFFSET_IS_SET("send_buff", FR_TYPE_UINT32, 0, fr_bio_fd_config_t, send_buff) }, @@ -141,6 +145,10 @@ static const conf_parser_t client_udp_unconnected_sub_config[] = { { FR_CONF_OFFSET("src_port_start", fr_bio_fd_config_t, src_port_start) }, { FR_CONF_OFFSET("src_port_end", fr_bio_fd_config_t, src_port_end) }, +#if (defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)) || defined(IP_DONTFRAG) + { FR_CONF_OFFSET("exceed_mtu", fr_bio_fd_config_t, exceed_mtu), .dflt = "yes" }, +#endif + { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, fr_bio_fd_config_t, recv_buff) }, { FR_CONF_OFFSET_IS_SET("send_buff", FR_TYPE_UINT32, 0, fr_bio_fd_config_t, send_buff) }, @@ -289,6 +297,10 @@ static const conf_parser_t server_udp_sub_config[] = { { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, fr_bio_fd_config_t, recv_buff) }, { FR_CONF_OFFSET_IS_SET("send_buff", FR_TYPE_UINT32, 0, fr_bio_fd_config_t, send_buff) }, +#if (defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)) || defined(IP_DONTFRAG) + { FR_CONF_OFFSET("exceed_mtu", fr_bio_fd_config_t, exceed_mtu), .dflt = "yes" }, +#endif + CONF_PARSER_TERMINATOR }; diff --git a/src/lib/bio/fd_open.c b/src/lib/bio/fd_open.c index bc28792a88c..9f291e91391 100644 --- a/src/lib/bio/fd_open.c +++ b/src/lib/bio/fd_open.c @@ -179,15 +179,14 @@ static int fr_bio_fd_common_udp(int fd, fr_socket_t const *sock, fr_bio_fd_confi * The wider Internet does not support UDP fragmentation. This means that fragmented packets are * silently discarded. * - * @todo - the correct way to handle this is to have a configuration flag which controls whether - * or not the "DF" bit is cleared. The documentation can make it clear that this only works on - * local networks, AND only on connected datagrams. The default should be to clear the DF bit. + * @todo - add more code to properly handle EMSGSIZE for PMTUD. We can then also do + * getsockopt(fd,IP_MTU,&integer) to get the current path MTU. It can only be used after the + * socket has been connected. It should also be called after an EMSGSIZE error is returned. * - * We then need to update the UDP reading code to handle EMSGSIZE as a non-fatal error. Since - * IPv6 will _always_ do PMTU discovery, we definitely need to handle this situation there. So - * we might as well handle it for IPv4, too. + * getsockopt(fd,IP_MTU,&integer) can also be used for unconnected sockets, if we also set + * IP_RECVERR. In which case the errors are put into an error queue. This is only for Linux. */ - if (sock->af == AF_INET) { + if ((sock->af == AF_INET) && cfg->exceed_mtu) { #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) /* * Disable PMTU discovery. On Linux, this also makes sure that the "don't