lib/configuration.c, lib/uaudio-rtp.c: Allow tweaking MTU-discovery.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 3 Jun 2020 20:01:15 +0000 (21:01 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 6 Jun 2020 17:07:12 +0000 (18:07 +0100)
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
lib/configuration.c
lib/configuration.h
lib/uaudio-rtp.c

index 0822669..c4f3bd4 100644 (file)
@@ -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
index a87957e..bb75173 100644 (file)
@@ -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;
 }
 
index 2b0db52..3811b34 100644 (file)
@@ -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;
 
index 73677d5..9e7abde 100644 (file)
@@ -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");
 }