From 10511fad863f95a08f483400d20ce9122655444e Mon Sep 17 00:00:00 2001 Message-Id: <10511fad863f95a08f483400d20ce9122655444e.1715893609.git.mdw@distorted.org.uk> From: Mark Wooding Date: Wed, 3 Jun 2020 21:01:15 +0100 Subject: [PATCH] lib/configuration.c, lib/uaudio-rtp.c: Allow tweaking MTU-discovery. Organization: Straylight/Edgeware From: Mark Wooding Apparently under some circumstances, Linux tries to do path MTU discovery with RTP transmissions, so it (a) sets DF on outgoing packets and then (b) ignores the `fragmentation-needed' errors coming back! This option attempts to work around this foolishness. Configuration is accepted on all platforms for portability's sake, but is only effective on Linux. --- doc/disorder_config.5.in | 22 ++++++++++++++++++++++ lib/configuration.c | 20 ++++++++++++++++++++ lib/configuration.h | 9 +++++++++ lib/uaudio-rtp.c | 19 +++++++++++++++++++ 4 files changed, 70 insertions(+) diff --git a/doc/disorder_config.5.in b/doc/disorder_config.5.in index 0822669..c4f3bd4 100644 --- a/doc/disorder_config.5.in +++ b/doc/disorder_config.5.in @@ -736,6 +736,28 @@ Choose one of the above based on the destination address. This is the default, for backwards compatibility reasons. .RE .TP +.B rtp_mtu_discovery \fIOPTION\fR +Control whether the system attemps path-MTU discovery using RTP packets +transmitted over IPv4. (This is not configurable in IPv6.) Possible values +are: +.RS +.TP +.B default +Do whatever the kernel usually does with UDP packets. This is, err, the +default. +.TP +.B yes +Force path-MTU disocvery. The `don't fragment' bit is set on outgoing packets +and we assume that the kernel will handle ICMP `fragmentation needed' errors +coming back and fragment accordingly. +.TP +.B no +Disable path-MTU discovery. Packets will be sent without the `don't fragment' +bit, and routers will be expected to fragment packets as necessary. +.RE +.IP +This option is experimental, and may change or be removed in a future release. +.TP .B rtp_rcvbuf \fISIZE\fR Set .BR disorder-playrtp (1)'s diff --git a/lib/configuration.c b/lib/configuration.c index a87957e..bb75173 100644 --- a/lib/configuration.c +++ b/lib/configuration.c @@ -984,6 +984,24 @@ static int validate_pausemode(const struct config_state attribute((unused)) *cs, return -1; } +/** @brief Validate an MTU-discovery setting + * @param cs Configuration state + * @param nvec Length of (proposed) new value + * @param vec Elements of new value + * @return 0 on success, non-0 on error + */ +static int validate_mtu_discovery(const struct config_state attribute((unused)) *cs, + int nvec, + char **vec) { + if (nvec == 1 && + (!strcmp(vec[0], "default") || + !strcmp(vec[0], "yes") || + !strcmp(vec[0], "no"))) + return 0; + disorder_error(0, "%s:%d: invalid MTU-discovery setting", cs->path, cs->line); + return -1; +} + /** @brief Validate a destination network address * @param cs Configuration state * @param nvec Length of (proposed) new value @@ -1097,6 +1115,7 @@ static const struct conf conf[] = { { C(rtp_maxbuffer), &type_integer, validate_non_negative }, { C(rtp_minbuffer), &type_integer, validate_non_negative }, { C(rtp_mode), &type_string, validate_any }, + { C(rtp_mtu_discovery), &type_string, validate_mtu_discovery }, { C(rtp_rcvbuf), &type_integer, validate_non_negative }, { C(rtp_request_address), &type_netaddress, validate_inetaddr }, { C(rtp_verbose), &type_boolean, validate_any }, @@ -1411,6 +1430,7 @@ static struct config *config_default(void) { c->connect.af = -1; c->rtp_mode = xstrdup("auto"); c->rtp_max_payload = -1; + c->rtp_mtu_discovery = xstrdup("default"); return c; } diff --git a/lib/configuration.h b/lib/configuration.h index 2b0db52..3811b34 100644 --- a/lib/configuration.h +++ b/lib/configuration.h @@ -269,6 +269,15 @@ struct config { */ long rtp_max_payload; + /** @brief Whether to allow MTU discovery + * + * This is `yes' to force it on, `no' to force it off, or `default' to do + * whatever the system is configured to do. Note that this only has a + * useful effect in IPv4, since IPv6 doesn't permit hop-by-hop + * fragmentation. + */ + char *rtp_mtu_discovery; + /** @brief Login lifetime in seconds */ long cookie_login_lifetime; diff --git a/lib/uaudio-rtp.c b/lib/uaudio-rtp.c index 73677d5..9e7abde 100644 --- a/lib/uaudio-rtp.c +++ b/lib/uaudio-rtp.c @@ -101,6 +101,7 @@ static const char *const rtp_options[] = { "multicast-loop", "rtp-mode", "rtp-max-payload", + "rtp-mtu-discovery", NULL }; @@ -264,6 +265,10 @@ static void rtp_open(void) { static const int one = 1; struct netaddress dst[1], src[1]; const char *mode; +#ifdef IP_MTU_DISCOVER + const char *mtu_disc; + int opt; +#endif /* Get the mode */ mode = uaudio_get("rtp-mode", "auto"); @@ -396,6 +401,19 @@ static void rtp_open(void) { disorder_fatal(errno, "error connecting broadcast socket to %s", format_sockaddr(dres->ai_addr)); } +#ifdef IP_MTU_DISCOVER + mtu_disc = uaudio_get("rtp-mtu-discovery", "default"); + do { + if(!strcmp(mtu_disc, "yes")) opt = IP_PMTUDISC_DO; + else if(!strcmp(mtu_disc, "no")) opt = IP_PMTUDISC_DONT; + else break; + if(setsockopt(rtp_fd4, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof opt)) + disorder_fatal(errno, "error setting MTU discovery"); + if(sres->ai_family == AF_INET && + setsockopt(rtp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof opt)) + disorder_fatal(errno, "error setting MTU discovery"); + } while (0); +#endif if(config->rtp_verbose) disorder_info("RTP: prepared socket"); } @@ -463,6 +481,7 @@ static void rtp_configure(void) { uaudio_set("multicast-loop", config->multicast_loop ? "yes" : "no"); snprintf(buffer, sizeof buffer, "%ld", config->rtp_max_payload); uaudio_set("rtp-max-payload", buffer); + uaudio_set("rtp-mtu-discovery", config->rtp_mtu_discovery); if(config->rtp_verbose) disorder_info("RTP: configured"); } -- [mdw]