package aerotun import ( "fmt" "net" "github.com/vishvananda/netlink" ) // Account for our authenticated header/tag plus UDP and the outer IP header. // Route MTU and link MTU are local upper bounds; a smaller remote path MTU can // still require an explicitly lower configured MTU. func packetHeaderSize(version int) int { if version == 3 { return secureHeaderSize } return headerSize } func underlayMTU(requested int, remote net.IP) (int, error) { return underlayMTUForHeader(requested, remote, headerSize) } func underlayMTUForHeader(requested int, remote net.IP, header int) (int, error) { routes, err := netlink.RouteGet(remote) if err != nil { return 0, fmt.Errorf("remote underlay route: %w", err) } if len(routes) == 0 { return 0, fmt.Errorf("no underlay route to %s", remote) } link, err := netlink.LinkByIndex(routes[0].LinkIndex) if err != nil { return 0, err } limit := link.Attrs().MTU if routes[0].MTU > 0 && routes[0].MTU < limit { limit = routes[0].MTU } overhead := header + 16 + 8 + 20 if remote.To4() == nil { overhead += 20 } limit -= overhead if limit < 576 { return 0, fmt.Errorf("underlay MTU too small for AeroTUN") } if requested > limit { return limit, nil } return requested, nil } func (t *TunnelRunner) adjustMTU(remote net.IP, peerMTU int) error { mtu, err := underlayMTUForHeader(int(t.effectiveMTU.Load()), remote, packetHeaderSize(t.cfg.Protocol)) if err != nil { return err } if peerMTU < mtu { mtu = peerMTU } if mtu == int(t.effectiveMTU.Load()) { return nil } ip, _, _ := net.ParseCIDR(t.cfg.LocalAddress) if ip.To4() == nil && mtu < 1280 { return fmt.Errorf("underlay cannot carry IPv6 tunnel MTU 1280") } names := []string{t.dev.Name()} if v, ok := t.dev.(*vethTun); ok { names = append(names, v.epName) } for _, name := range names { if _, err := runIP("link", "set", name, "mtu", fmt.Sprint(mtu)); err != nil { return err } } t.effectiveMTU.Store(int64(mtu)) return nil }