This was driving me up the wall. I was using OpenVPN in an environment where it wasn't very convenient to manually tune the MTU at deployment time, and as you pointed out this value in theory could drift over time. I kept assuming that some combination of the billions of MTU features would work, to no avail. I discovered that unless you were willing to accept fragmentation (either by the network--always a bad idea and with IPv6 an impossible idea--or by OpenVPN itself at the cost of 4 bytes per packet) you simply cannot get a properly auto-tuning MTU configuration and that network connectivity would often simply break.
This was also complicated by the fact that with TAP devices, the tun-mtu parameter was calculated incorrectly by OpenVPN. Specifically, the MTU as set by the interface device does NOT include the size of the ethernet header, whereas the tun-mtu provided by OpenVPN does include the size (presumably legacy from the more popular TUN devices).
I discovered while researching the various options that the mtu-disc option actually discovers the correct Path-MTU between the client and server: OpenVPN just wasn't doing anything very useful with that information (unless you had OpenVPN internal fragmentation enabled). So I just inserted some code to detect this case and manually run `ifconfig $device mtu $tun_mtu` as necessary.
I read about an idea from mailing list to forge an ICMP can't fragment error packet. This unfortunately doesn't help non-TCP packets whereas adjusting the MTU on the interface does (by allowing fragmentation or by causing a legitimate ICMP packet to be sent as necessary).
However, the patch I made to support this is not necessarily entirely ready to be stuffed into the next release. Specifically I didn't attempt to integrate the mtu setting into the tun.c style generic framework with route2 and ifconfig support, but rather just hardcoded a pathname. Possibly I didn't even put the check in the right place. However, the hardcoded command and the test location were both very convenient.
Another concern is that you are guaranteed to drop packets (one per Path MTU shrinkage) every time you restart OpenVPN as Path MTU discovery takes place. Due to the poor initial configuration of tun-mtu, this is going to happen at least one time per restart. If there are actually multiple MTUs in use in your network, it will occur several times. However, Path MTU discovery and the dropped packets associated with it are pretty standard fare on the internet.
More seriously is the problem that multi-client server will use the lowest MTU to any of the clients that have ever attached to it. I don't really see any good way around this problem without having individual per-client interfaces, though in theory at least we could detect when that client went away and try to raise the MTU back up.
Anyway, I hope this is useful for someone.
Code: Select all
--- a/forward.c
+++ b/forward.c
@@ -1156,6 +1156,41 @@ process_outgoing_link (struct context *c)
BLEN (&c->c2.to_link),
size);
}
+ else if (size < 0 && c->c2.link_socket->info.mtu_changed)
+ {
+ int newmtu = c->c2.link_socket->mtu - datagram_overhead(c->c2.link_socket->info.proto) - c->c2.frame.extra_frame;
+
+ if (c->c1.tuntap->type == DEV_TYPE_TAP)
+ newmtu -= ETH_HLEN;
+
+ msg (D_LINK_ERRORS,
+ "MTU CHANGE EVENT: "
+ "actual_name=%s, "
+ "post_open_mtu=%d, "
+ "link_mtu=%d, "
+ "link_mtu_dynamic=%d, "
+ "new_link_mtu=%d, "
+ "old_tun_mtu=%d, "
+ "new_tun_mtu=%d\n",
+
+ c->c1.tuntap->actual_name,
+ c->c1.tuntap->post_open_mtu,
+ c->c2.frame.link_mtu,
+ c->c2.frame.link_mtu_dynamic,
+ c->c2.link_socket->mtu,
+ c->c2.frame.discovered_tun_mtu,
+ newmtu);
+
+ if (c->c2.frame.discovered_tun_mtu > TUN_MTU_MIN && newmtu >= c->c2.frame.discovered_tun_mtu)
+ newmtu = MAX(TUN_MTU_MIN,c->c2.frame.discovered_tun_mtu-1);
+
+ char buf[1024];
+ snprintf(buf,sizeof(buf),"/sbin/ifconfig %s mtu %d",c->c1.tuntap->actual_name,newmtu);
+ system(buf);
+ c->c2.frame.discovered_tun_mtu = newmtu;
+
+ c->c2.link_socket->info.mtu_changed = false;
+ }
/* indicate activity regarding --inactive parameter */
register_activity (c, size);
diff --git a/mtu.h b/mtu.h
index 6079bf8..6d0322a 100644
--- a/mtu.h
+++ b/mtu.h
@@ -92,6 +92,7 @@ struct frame {
*/
int link_mtu;
int link_mtu_dynamic;
+ int discovered_tun_mtu;
/*