@@ -19,8 +19,11 @@ import (
1919 "github.com/slackhq/nebula/config"
2020 "github.com/slackhq/nebula/header"
2121 "github.com/slackhq/nebula/logging"
22+ "github.com/slackhq/nebula/overlay"
2223 "github.com/slackhq/nebula/udp"
2324 "github.com/slackhq/nebula/util"
25+ "golang.org/x/net/ipv4"
26+ "golang.org/x/net/ipv6"
2427)
2528
2629var ErrHostNotKnown = errors .New ("host not known" )
@@ -65,6 +68,18 @@ type LightHouse struct {
6568
6669 advertiseAddrs atomic.Pointer [[]netip.AddrPort ]
6770
71+ // tunMTU mirrors tun.mtu so locally discovered addrs can be checked for
72+ // links too small to carry a full-size nebula packet without fragmenting.
73+ tunMTU atomic.Int64
74+ // omitLowMTUAddrs drops such addrs from lighthouse updates (and demotes
75+ // the associated warnings to debug logs) instead of advertising them.
76+ omitLowMTUAddrs atomic.Bool
77+ // mtuWarned tracks the last classification logged per local addr so a
78+ // warning is only emitted when the classification changes, not on every
79+ // periodic update.
80+ mtuWarnLock sync.Mutex
81+ mtuWarned map [mtuWarnKey ]linkMTUTier
82+
6883 // Addr's of relays that can be used by peers to access me
6984 relaysForMe atomic.Pointer [[]netip.Addr ]
7085
@@ -105,6 +120,7 @@ func NewLightHouseFromConfig(ctx context.Context, l *slog.Logger, c *config.C, c
105120 punchy : p ,
106121 updateTrigger : make (chan struct {}, 1 ),
107122 queryChan : make (chan netip.Addr , c .GetUint32 ("handshakes.query_buffer" , 64 )),
123+ mtuWarned : make (map [mtuWarnKey ]linkMTUTier ),
108124 l : l ,
109125 }
110126 lighthouses := make ([]netip.Addr , 0 )
@@ -216,6 +232,23 @@ func (lh *LightHouse) reload(c *config.C, initial bool) error {
216232 }
217233 }
218234
235+ if initial || c .HasChanged ("tun.mtu" ) || c .HasChanged ("lighthouse.omit_low_mtu_addrs" ) {
236+ lh .tunMTU .Store (int64 (c .GetInt ("tun.mtu" , overlay .DefaultMTU )))
237+ lh .omitLowMTUAddrs .Store (c .GetBool ("lighthouse.omit_low_mtu_addrs" , false ))
238+
239+ // Re-log any addrs whose links are still too small under the new values
240+ lh .mtuWarnLock .Lock ()
241+ clear (lh .mtuWarned )
242+ lh .mtuWarnLock .Unlock ()
243+
244+ if ! initial {
245+ lh .l .Info ("tun.mtu and/or lighthouse.omit_low_mtu_addrs has changed" ,
246+ "tunMTU" , lh .tunMTU .Load (),
247+ "omitLowMTUAddrs" , lh .omitLowMTUAddrs .Load (),
248+ )
249+ }
250+ }
251+
219252 if initial || c .HasChanged ("lighthouse.interval" ) {
220253 lh .interval .Store (int64 (c .GetInt ("lighthouse.interval" , 10 )))
221254
@@ -905,6 +938,108 @@ func (lh *LightHouse) TriggerUpdate() {
905938 }
906939}
907940
941+ // linkMTUTier classifies how well a local addr's link MTU can carry
942+ // full-size nebula packets built from a tun packet of tun.mtu bytes.
943+ type linkMTUTier uint8
944+
945+ // mtuWarnKey identifies a local addr for MTU warning dedup purposes. The
946+ // interface name is included because the same addr can exist on multiple
947+ // links with different MTUs.
948+ type mtuWarnKey struct {
949+ ifName string
950+ addr netip.Addr
951+ }
952+
953+ const (
954+ // The link can carry both normal and relayed nebula traffic
955+ linkMTUOk linkMTUTier = iota
956+ // The link can carry normal nebula traffic, but relayed traffic (which
957+ // adds a second layer of encapsulation) will not fit
958+ linkMTUTooSmallForRelay
959+ // Even normal nebula traffic will not fit
960+ linkMTUTooSmall
961+ )
962+
963+ const (
964+ // Both AES-256-GCM and ChaCha20-Poly1305 append a 16 byte AEAD tag
965+ cipherTagLen = 16
966+ udpHeaderLen = 8
967+ )
968+
969+ // requiredLinkMTU returns the minimum underlay link MTU that can carry a
970+ // full-size tun packet to an addr of the given family without fragmentation,
971+ // both directly and via a relay (which wraps the packet in a second nebula
972+ // header and AEAD tag).
973+ func requiredLinkMTU (tunMTU int , is4 bool ) (direct , relayed int ) {
974+ ipHeaderLen := ipv6 .HeaderLen
975+ if is4 {
976+ ipHeaderLen = ipv4 .HeaderLen
977+ }
978+
979+ direct = tunMTU + header .Len + cipherTagLen + udpHeaderLen + ipHeaderLen
980+ relayed = direct + header .Len + cipherTagLen
981+ return direct , relayed
982+ }
983+
984+ // checkLocalLinkMTU classifies e's link MTU, logs when the classification
985+ // changes, and reports whether e should be advertised to lighthouses.
986+ func (lh * LightHouse ) checkLocalLinkMTU (e localAddr ) bool {
987+ tunMTU := int (lh .tunMTU .Load ())
988+ omit := lh .omitLowMTUAddrs .Load ()
989+
990+ tier := linkMTUOk
991+ direct , relayed := requiredLinkMTU (tunMTU , e .addr .Is4 ())
992+ if e .linkMTU > 0 { // links with an unknown MTU are advertised as-is
993+ if e .linkMTU < direct {
994+ tier = linkMTUTooSmall
995+ } else if e .linkMTU < relayed {
996+ tier = linkMTUTooSmallForRelay
997+ }
998+ }
999+ advertise := tier != linkMTUTooSmall || ! omit
1000+
1001+ key := mtuWarnKey {ifName : e .ifName , addr : e .addr }
1002+ lh .mtuWarnLock .Lock ()
1003+ changed := lh .mtuWarned [key ] != tier
1004+ if changed {
1005+ if tier == linkMTUOk {
1006+ delete (lh .mtuWarned , key )
1007+ } else {
1008+ lh .mtuWarned [key ] = tier
1009+ }
1010+ }
1011+ lh .mtuWarnLock .Unlock ()
1012+
1013+ if ! changed || tier == linkMTUOk {
1014+ return advertise
1015+ }
1016+
1017+ level := slog .LevelWarn
1018+ if omit {
1019+ level = slog .LevelDebug
1020+ }
1021+
1022+ if lh .l .Enabled (context .Background (), level ) {
1023+ msg := "Link MTU too small for nebula traffic, expect fragmentation or drops"
1024+ if ! advertise {
1025+ msg = "Omitting addr with too-small link MTU from lighthouse report"
1026+ } else if tier == linkMTUTooSmallForRelay {
1027+ msg = "Link MTU too small for relayed nebula traffic"
1028+ }
1029+
1030+ lh .l .Log (context .Background (), level , msg ,
1031+ "localAddr" , e .addr ,
1032+ "interface" , e .ifName ,
1033+ "linkMTU" , e .linkMTU ,
1034+ "requiredMTU" , direct ,
1035+ "requiredRelayMTU" , relayed ,
1036+ "tunMTU" , tunMTU ,
1037+ )
1038+ }
1039+
1040+ return advertise
1041+ }
1042+
9081043func (lh * LightHouse ) SendUpdate () {
9091044 var v4 []* V4AddrPort
9101045 var v6 []* V6AddrPort
@@ -919,15 +1054,19 @@ func (lh *LightHouse) SendUpdate() {
9191054
9201055 lal := lh .GetLocalAllowList ()
9211056 for _ , e := range localAddrs (lh .l , lal ) {
922- if lh .myVpnNetworksTable .Contains (e ) {
1057+ if lh .myVpnNetworksTable .Contains (e .addr ) {
1058+ continue
1059+ }
1060+
1061+ if ! lh .checkLocalLinkMTU (e ) {
9231062 continue
9241063 }
9251064
9261065 // Only add addrs that aren't my VPN/tun networks
927- if e .Is4 () {
928- v4 = append (v4 , netAddrToProtoV4AddrPort (e , uint16 (lh .nebulaPort )))
1066+ if e .addr . Is4 () {
1067+ v4 = append (v4 , netAddrToProtoV4AddrPort (e . addr , uint16 (lh .nebulaPort )))
9291068 } else {
930- v6 = append (v6 , netAddrToProtoV6AddrPort (e , uint16 (lh .nebulaPort )))
1069+ v6 = append (v6 , netAddrToProtoV6AddrPort (e . addr , uint16 (lh .nebulaPort )))
9311070 }
9321071 }
9331072
0 commit comments