From 1ea88470857e09d0457f3d719c61697cb638be7d Mon Sep 17 00:00:00 2001 From: Wade Simmons Date: Fri, 26 Jun 2020 13:47:21 -0400 Subject: [PATCH] linux: set advmss correctly when route MTU is used (#245) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If different mtus are specified for different routes, we should set advmss on each route because Linux does a poor job of selecting the default (from ip-route(8)): advmss NUMBER (Linux 2.3.15+ only) the MSS ('Maximal Segment Size') to advertise to these destinations when estab‐ lishing TCP connections. If it is not given, Linux uses a default value calcu‐ lated from the first hop device MTU. (If the path to these destination is asym‐ metric, this guess may be wrong.) Note that the default value is calculated from the first hop *device MTU*, not the *route MTU*. In practice this is usually ok as long as the other side of the tunnel has the mtu configured exactly the same, but we should probably just set advmss correctly on these routes. --- tun_linux.go | 16 ++++++++++++++++ tun_linux_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 tun_linux_test.go diff --git a/tun_linux.go b/tun_linux.go index cf66a8c..6a9cb09 100644 --- a/tun_linux.go +++ b/tun_linux.go @@ -216,6 +216,7 @@ func (c Tun) Activate() error { LinkIndex: link.Attrs().Index, Dst: dr, MTU: c.DefaultMTU, + AdvMSS: c.advMSS(route{}), Scope: unix.RT_SCOPE_LINK, Src: c.Cidr.IP, Protocol: unix.RTPROT_KERNEL, @@ -233,6 +234,7 @@ func (c Tun) Activate() error { LinkIndex: link.Attrs().Index, Dst: r.route, MTU: r.mtu, + AdvMSS: c.advMSS(r), Scope: unix.RT_SCOPE_LINK, } @@ -248,6 +250,7 @@ func (c Tun) Activate() error { LinkIndex: link.Attrs().Index, Dst: r.route, MTU: r.mtu, + AdvMSS: c.advMSS(r), Scope: unix.RT_SCOPE_LINK, } @@ -265,3 +268,16 @@ func (c Tun) Activate() error { return nil } + +func (c Tun) advMSS(r route) int { + mtu := r.mtu + if r.mtu == 0 { + mtu = c.DefaultMTU + } + + // We only need to set advmss if the route MTU does not match the device MTU + if mtu != c.MaxMTU { + return mtu - 40 + } + return 0 +} diff --git a/tun_linux_test.go b/tun_linux_test.go new file mode 100644 index 0000000..4e70aa0 --- /dev/null +++ b/tun_linux_test.go @@ -0,0 +1,31 @@ +package nebula + +import "testing" + +var runAdvMSSTests = []struct { + name string + tun Tun + r route + expected int +}{ + // Standard case, default MTU is the device max MTU + {"default", Tun{DefaultMTU: 1440, MaxMTU: 1440}, route{}, 0}, + {"default-min", Tun{DefaultMTU: 1440, MaxMTU: 1440}, route{mtu: 1440}, 0}, + {"default-low", Tun{DefaultMTU: 1440, MaxMTU: 1440}, route{mtu: 1200}, 1160}, + + // Case where we have a route MTU set higher than the default + {"route", Tun{DefaultMTU: 1440, MaxMTU: 8941}, route{}, 1400}, + {"route-min", Tun{DefaultMTU: 1440, MaxMTU: 8941}, route{mtu: 1440}, 1400}, + {"route-high", Tun{DefaultMTU: 1440, MaxMTU: 8941}, route{mtu: 8941}, 0}, +} + +func TestTunAdvMSS(t *testing.T) { + for _, tt := range runAdvMSSTests { + t.Run(tt.name, func(t *testing.T) { + o := tt.tun.advMSS(tt.r) + if o != tt.expected { + t.Errorf("got %d, want %d", o, tt.expected) + } + }) + } +}