|
|
@@ -0,0 +1,99 @@
|
|
|
+// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
|
+
|
|
|
+//go:build (darwin && !ios) || (linux && !android)
|
|
|
+
|
|
|
+package magicsock
|
|
|
+
|
|
|
+// Peer path MTU routines shared by platforms that implement it.
|
|
|
+
|
|
|
+// DontFragSetting returns true if at least one of the underlying sockets of
|
|
|
+// this connection is a UDP socket with the don't fragment bit set, otherwise it
|
|
|
+// returns false. It also returns an error if either connection returned an error
|
|
|
+// other than errUnsupportedConnType.
|
|
|
+func (c *Conn) DontFragSetting() (bool, error) {
|
|
|
+ df4, err4 := c.getDontFragment("udp4")
|
|
|
+ df6, err6 := c.getDontFragment("udp6")
|
|
|
+ df := df4 || df6
|
|
|
+ err := err4
|
|
|
+ if err4 != nil && err4 != errUnsupportedConnType {
|
|
|
+ err = err6
|
|
|
+ }
|
|
|
+ if err == errUnsupportedConnType {
|
|
|
+ err = nil
|
|
|
+ }
|
|
|
+ return df, err
|
|
|
+}
|
|
|
+
|
|
|
+// ShouldPMTUD returns true if this client should try to enable peer MTU
|
|
|
+// discovery, false otherwise.
|
|
|
+func (c *Conn) ShouldPMTUD() bool {
|
|
|
+ if v, ok := debugEnablePMTUD().Get(); ok {
|
|
|
+ if debugPMTUD() {
|
|
|
+ c.logf("magicsock: peermtu: peer path MTU discovery set via envknob to %v", v)
|
|
|
+ }
|
|
|
+ return v
|
|
|
+ }
|
|
|
+ if debugPMTUD() {
|
|
|
+ c.logf("magicsock: peermtu: peer path MTU discovery set by default to false")
|
|
|
+ }
|
|
|
+ return false // Until we feel confident PMTUD is solid.
|
|
|
+}
|
|
|
+
|
|
|
+// PeerMTUEnabled returns true if this Conn is has peer path MTU discovery enabled.
|
|
|
+func (c *Conn) PeerMTUEnabled() bool {
|
|
|
+ return c.peerMTUEnabled.Load()
|
|
|
+}
|
|
|
+
|
|
|
+// UpdatePMTUD configures underlying sockets of this Conn to enable or disable
|
|
|
+// peer path MTU discovery according to the current configuration.
|
|
|
+//
|
|
|
+// Enabling or disabling peer path MTU discovery requires setting the don't
|
|
|
+// fragment bit on its two underlying pconns. There are three distinct results
|
|
|
+// for this operation on each pconn:
|
|
|
+//
|
|
|
+// 1. Success
|
|
|
+// 2. Failure (not supported on this platform, or supported but failed)
|
|
|
+// 3. Not a UDP socket (most likely one of IPv4 or IPv6 couldn't be used)
|
|
|
+//
|
|
|
+// To simplify the fast path for the most common case, we set the PMTUD status
|
|
|
+// of the overall Conn according to the results of setting the sockopt on pconn
|
|
|
+// as follows:
|
|
|
+//
|
|
|
+// 1. Both setsockopts succeed: PMTUD status update succeeds
|
|
|
+// 2. One succeeds, one returns not a UDP socket: PMTUD status update succeeds
|
|
|
+// 4. Neither setsockopt succeeds: PMTUD disabled
|
|
|
+// 3. Either setsockopt fails: PMTUD disabled
|
|
|
+//
|
|
|
+// If the PMTUD settings changed, it resets the endpoint state so that it will
|
|
|
+// re-probe path MTUs to this peer.
|
|
|
+func (c *Conn) UpdatePMTUD() {
|
|
|
+ if debugPMTUD() {
|
|
|
+ df4, err4 := c.getDontFragment("udp4")
|
|
|
+ df6, err6 := c.getDontFragment("udp6")
|
|
|
+ c.logf("magicsock: peermtu: peer MTU status %v DF bit status: v4: %v (%v) v6: %v (%v)", c.peerMTUEnabled.Load(), df4, err4, df6, err6)
|
|
|
+ }
|
|
|
+
|
|
|
+ enable := c.ShouldPMTUD()
|
|
|
+ if c.peerMTUEnabled.Load() == enable {
|
|
|
+ c.logf("magicsock: peermtu: peer MTU status is %v", enable)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ newStatus := enable
|
|
|
+ err4 := c.setDontFragment("udp4", enable)
|
|
|
+ err6 := c.setDontFragment("udp6", enable)
|
|
|
+ anySuccess := err4 == nil || err6 == nil
|
|
|
+ noFailures := (err4 == nil || err4 == errUnsupportedConnType) && (err6 == nil || err6 == errUnsupportedConnType)
|
|
|
+
|
|
|
+ if anySuccess && noFailures {
|
|
|
+ c.logf("magicsock: peermtu: peer MTU status updated to %v", newStatus)
|
|
|
+ } else {
|
|
|
+ c.logf("[unexpected] magicsock: peermtu: updating peer MTU status to %v failed (v4: %v, v6: %v), disabling", enable, err4, err6)
|
|
|
+ _ = c.setDontFragment("udp4", false)
|
|
|
+ _ = c.setDontFragment("udp6", false)
|
|
|
+ newStatus = false
|
|
|
+ }
|
|
|
+ c.peerMTUEnabled.Store(newStatus)
|
|
|
+ c.resetEndpointStates()
|
|
|
+}
|