mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 00:01:40 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			131 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| //go:build (darwin && !ios) || (linux && !android)
 | |
| 
 | |
| package magicsock
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 
 | |
| 	"golang.org/x/sys/unix"
 | |
| 	"tailscale.com/disco"
 | |
| 	"tailscale.com/net/tstun"
 | |
| )
 | |
| 
 | |
| // 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 c.controlKnobs != nil {
 | |
| 		if v := c.controlKnobs.PeerMTUEnable.Load(); v {
 | |
| 			if debugPMTUD() {
 | |
| 				c.logf("magicsock: peermtu: peer path MTU discovery enabled by control")
 | |
| 			}
 | |
| 			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 reports whether peer path MTU discovery is enabled.
 | |
| func (c *Conn) PeerMTUEnabled() bool {
 | |
| 	return c.peerMTUEnabled.Load()
 | |
| }
 | |
| 
 | |
| // UpdatePMTUD configures the 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("[v1] 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
 | |
| 	}
 | |
| 	if debugPMTUD() {
 | |
| 		c.logf("magicsock: peermtu: peer MTU probes are %v", tstun.WireMTUsToProbe)
 | |
| 	}
 | |
| 	c.peerMTUEnabled.Store(newStatus)
 | |
| 	c.resetEndpointStates()
 | |
| }
 | |
| 
 | |
| var errEMSGSIZE error = unix.EMSGSIZE
 | |
| 
 | |
| func pmtuShouldLogDiscoTxErr(m disco.Message, err error) bool {
 | |
| 	// Large disco.Ping packets used to probe path MTU may result in
 | |
| 	// an EMSGSIZE error fairly regularly which can pollute logs.
 | |
| 	p, ok := m.(*disco.Ping)
 | |
| 	if !ok || p.Padding == 0 || !errors.Is(err, errEMSGSIZE) || debugPMTUD() {
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 |