Browse code

Allow tun-mtu to be pushed

This allows tun-mtu to pushed but only up to the size of the preallocated
buffers. This is not a perfect solution but should allow most of the use
cases where the mtu is close enough to 1500 (or smaller).

Signed-off-by: Arne Schwabe <arne@rfc2549.org>

Patch v4: rebase for check_session_cipher name change
Patch v5: remove mention of change of default mtu, remove leftover code
from an earlier approach.

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20221109154810.1268403-1-arne@rfc2549.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg25498.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Arne Schwabe authored on 2022/11/10 00:48:09
Showing 9 changed files
... ...
@@ -99,6 +99,11 @@ Inline auth username and password
99 99
     missing OpenVPN will prompt for input via stdin. This applies to inline'd
100 100
     http-proxy-user-pass too.
101 101
 
102
+Tun MTU can be pushed
103
+    The  client can now also dynamically configure its MTU and the server
104
+    will try to push the client MTU when the client supports it. The
105
+    directive ``--tun-mtu-max`` has been introduced to increase the maximum
106
+    pushable MTU size (defaults to 1600).
102 107
 
103 108
 Improved control channel packet size control (``max-packet-size``)
104 109
     The size of control channel is no longer tied to
... ...
@@ -363,6 +363,10 @@ configuration.
363 363
         The client announces the list of supported ciphers configured with the
364 364
         ``--data-ciphers`` option to the server.
365 365
 
366
+  :code:`IV_MTU=<max_mtu>`
367
+        The client announces the support of pushable MTU and the maximum MTU
368
+        it is willing to accept.
369
+
366 370
   :code:`IV_GUI_VER=<gui_id> <version>`
367 371
         The UI version of a UI if one is running, for example
368 372
         :code:`de.blinkt.openvpn 0.5.47` for the Android app.
... ...
@@ -540,6 +540,12 @@ routing.
540 540
   It's best to use the ``--fragment`` and/or ``--mssfix`` options to deal
541 541
   with MTU sizing issues.
542 542
 
543
+--tun-max-mtu maxmtu
544
+  This configures the maximum MTU size that a server can push to ``maxmtu``,
545
+  by configuring the internal buffers to allow at least this packet size.
546
+  The default for ``maxmtu`` is 1600. Currently, only increasing beyond 1600
547
+  is possible, and attempting to reduce max-mtu below 1600 will be ignored.
548
+
543 549
 --tun-mtu-extra n
544 550
   Assume that the TUN/TAP device might return as many as ``n`` bytes more
545 551
   than the ``--tun-mtu`` size on read. This parameter defaults to 0, which
... ...
@@ -2312,7 +2312,8 @@ pull_permission_mask(const struct context *c)
2312 2312
         | OPT_P_ECHO
2313 2313
         | OPT_P_PULL_MODE
2314 2314
         | OPT_P_PEER_ID
2315
-        | OPT_P_NCP;
2315
+        | OPT_P_NCP
2316
+        | OPT_P_PUSH_MTU;
2316 2317
 
2317 2318
     if (!c->options.route_nopull)
2318 2319
     {
... ...
@@ -2475,6 +2476,23 @@ do_deferred_options(struct context *c, const unsigned int found)
2475 2475
         }
2476 2476
     }
2477 2477
 
2478
+    if (found & OPT_P_PUSH_MTU)
2479
+    {
2480
+        /* MTU has changed, check that the pushed MTU is small enough to
2481
+         * be able to change it */
2482
+        msg(D_PUSH, "OPTIONS IMPORT: tun-mtu set to %d", c->options.ce.tun_mtu);
2483
+
2484
+        struct frame *frame = &c->c2.frame;
2485
+
2486
+        if (c->options.ce.tun_mtu > frame->tun_max_mtu)
2487
+        {
2488
+            msg(D_PUSH_ERRORS, "Server-pushed tun-mtu is too large, please add "
2489
+                "tun-mtu-max %d in the client configuration",
2490
+                c->options.ce.tun_mtu);
2491
+        }
2492
+        frame->tun_mtu = min_int(frame->tun_max_mtu, c->options.ce.tun_mtu);
2493
+    }
2494
+
2478 2495
     return true;
2479 2496
 }
2480 2497
 
... ...
@@ -2635,10 +2653,16 @@ frame_finalize_options(struct context *c, const struct options *o)
2635 2635
     struct frame *frame = &c->c2.frame;
2636 2636
 
2637 2637
     frame->tun_mtu = get_frame_mtu(c, o);
2638
+    frame->tun_max_mtu = o->ce.tun_mtu_max;
2639
+
2640
+    /* max mtu needs to be at least as large as the tun mtu */
2641
+    frame->tun_max_mtu = max_int(frame->tun_mtu, frame->tun_max_mtu);
2638 2642
 
2639
-    /* We always allow at least 1500 MTU packets to be received in our buffer
2640
-     * space */
2641
-    size_t payload_size = max_int(1500, frame->tun_mtu);
2643
+    /* We always allow at least 1600 MTU packets to be received in our buffer
2644
+     * space to allow server to push "baby giant" MTU sizes */
2645
+    frame->tun_max_mtu = max_int(1600, frame->tun_max_mtu);
2646
+
2647
+    size_t payload_size = frame->tun_max_mtu;
2642 2648
 
2643 2649
     /* we need to be also large enough to hold larger control channel packets
2644 2650
      * if configured */
... ...
@@ -2650,9 +2674,9 @@ frame_finalize_options(struct context *c, const struct options *o)
2650 2650
         payload_size += o->ce.tun_mtu_extra;
2651 2651
     }
2652 2652
 
2653
-    /* Add 100 byte of extra space in the buffer to account for slightly
2654
-     * mismatched MUTs between peers */
2655
-    payload_size += 100;
2653
+    /* Add 32 byte of extra space in the buffer to account for small errors
2654
+     * in the calculation */
2655
+    payload_size += 32;
2656 2656
 
2657 2657
 
2658 2658
     /* the space that is reserved before the payload to add extra headers to it
... ...
@@ -3211,6 +3235,10 @@ do_init_frame_tls(struct context *c)
3211 3211
                c->c2.frame.buf.payload_size);
3212 3212
         frame_print(&c->c2.tls_multi->opt.frame, D_MTU_INFO,
3213 3213
                     "Control Channel MTU parms");
3214
+
3215
+        /* Keep the max mtu also in the frame of tls multi so it can access
3216
+         * it in push_peer_info */
3217
+        c->c2.tls_multi->opt.frame.tun_max_mtu = c->c2.frame.tun_max_mtu;
3214 3218
     }
3215 3219
     if (c->c2.tls_auth_standalone)
3216 3220
     {
... ...
@@ -222,6 +222,7 @@ frame_print(const struct frame *frame,
222 222
     buf_printf(&out, " max_frag:%d", frame->max_fragment_size);
223 223
 #endif
224 224
     buf_printf(&out, " tun_mtu:%d", frame->tun_mtu);
225
+    buf_printf(&out, " tun_max_mtu:%d", frame->tun_max_mtu);
225 226
     buf_printf(&out, " headroom:%d", frame->buf.headroom);
226 227
     buf_printf(&out, " payload:%d", frame->buf.payload_size);
227 228
     buf_printf(&out, " tailroom:%d", frame->buf.tailroom);
... ...
@@ -138,6 +138,9 @@ struct frame {
138 138
                                  *   control frame payload (although most of
139 139
                                  *   code ignores it)
140 140
                                  */
141
+    int tun_max_mtu;            /**< the maximum tun-mtu size the buffers are
142
+                                 *   are sized for. This is the upper bound that
143
+                                 *   a server can push as MTU */
141 144
 
142 145
     int extra_tun;              /**< Maximum number of bytes in excess of
143 146
                                  *   the tun/tap MTU that might be read
... ...
@@ -6449,10 +6449,23 @@ add_option(struct options *options,
6449 6449
     }
6450 6450
     else if (streq(p[0], "tun-mtu") && p[1] && !p[2])
6451 6451
     {
6452
-        VERIFY_PERMISSION(OPT_P_MTU|OPT_P_CONNECTION);
6452
+        VERIFY_PERMISSION(OPT_P_PUSH_MTU|OPT_P_CONNECTION);
6453 6453
         options->ce.tun_mtu = positive_atoi(p[1]);
6454 6454
         options->ce.tun_mtu_defined = true;
6455 6455
     }
6456
+    else if (streq(p[0], "tun-mtu-max") && p[1] && !p[3])
6457
+    {
6458
+        VERIFY_PERMISSION(OPT_P_MTU|OPT_P_CONNECTION);
6459
+        int max_mtu = positive_atoi(p[1]);
6460
+        if (max_mtu < 68 || max_mtu > 65536)
6461
+        {
6462
+            msg(msglevel, "--tun-mtu-max value '%s' is invalid", p[1]);
6463
+        }
6464
+        else
6465
+        {
6466
+            options->ce.tun_mtu_max = max_mtu;
6467
+        }
6468
+    }
6456 6469
     else if (streq(p[0], "tun-mtu-extra") && p[1] && !p[2])
6457 6470
     {
6458 6471
         VERIFY_PERMISSION(OPT_P_MTU|OPT_P_CONNECTION);
... ...
@@ -118,6 +118,8 @@ struct connection_entry
118 118
     const char *socks_proxy_authfile;
119 119
 
120 120
     int tun_mtu;         /* MTU of tun device */
121
+    int tun_mtu_max;     /* maximum MTU that can be pushed */
122
+
121 123
     bool tun_mtu_defined; /* true if user overriding parm with command line option */
122 124
     int tun_mtu_extra;
123 125
     bool tun_mtu_extra_defined;
... ...
@@ -730,6 +732,7 @@ struct options
730 730
 #define OPT_P_CONNECTION      (1<<27)
731 731
 #define OPT_P_PEER_ID         (1<<28)
732 732
 #define OPT_P_INLINE          (1<<29)
733
+#define OPT_P_PUSH_MTU        (1<<30)
733 734
 
734 735
 #define OPT_P_DEFAULT   (~(OPT_P_INSTANCE|OPT_P_PULL_MODE))
735 736
 
... ...
@@ -2048,6 +2048,9 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
2048 2048
 
2049 2049
             /* support for AUTH_FAIL,TEMP control message */
2050 2050
             iv_proto |= IV_PROTO_AUTH_FAIL_TEMP;
2051
+
2052
+            /* support for tun-mtu as part of the push message */
2053
+            buf_printf(&out, "IV_MTU=%d\n", session->opt->frame.tun_max_mtu);
2051 2054
         }
2052 2055
 
2053 2056
         /* support for Negotiable Crypto Parameters */