88 "github.com/slackhq/nebula/header"
99 "github.com/slackhq/nebula/iputil"
1010 "github.com/slackhq/nebula/noiseutil"
11+ "github.com/slackhq/nebula/routing"
1112)
1213
1314func (f * Interface ) consumeInsidePacket (packet []byte , fwPacket * firewall.Packet , nb , out []byte , q int , localCache firewall.ConntrackCache ) {
@@ -49,7 +50,7 @@ func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet
4950 return
5051 }
5152
52- hostinfo , ready := f .getOrHandshake (fwPacket . RemoteAddr , func (hh * HandshakeHostInfo ) {
53+ hostinfo , ready := f .getOrHandshakeConsiderRouting (fwPacket , func (hh * HandshakeHostInfo ) {
5354 hh .cachePacket (f .l , header .Message , 0 , packet , f .sendMessageNow , f .cachedPacketMetrics )
5455 })
5556
@@ -121,22 +122,94 @@ func (f *Interface) rejectOutside(packet []byte, ci *ConnectionState, hostinfo *
121122 f .sendNoMetrics (header .Message , 0 , ci , hostinfo , netip.AddrPort {}, out , nb , packet , q )
122123}
123124
125+ // Handshake will attempt to initiate a tunnel with the provided vpn address if it is within our vpn networks. This is a no-op if the tunnel is already established or being established
124126func (f * Interface ) Handshake (vpnAddr netip.Addr ) {
125- f .getOrHandshake (vpnAddr , nil )
127+ f .getOrHandshakeNoRouting (vpnAddr , nil )
126128}
127129
128- // getOrHandshake returns nil if the vpnAddr is not routable.
130+ // getOrHandshakeNoRouting returns nil if the vpnAddr is not routable.
129131// If the 2nd return var is false then the hostinfo is not ready to be used in a tunnel
130- func (f * Interface ) getOrHandshake (vpnAddr netip.Addr , cacheCallback func (* HandshakeHostInfo )) (* HostInfo , bool ) {
132+ func (f * Interface ) getOrHandshakeNoRouting (vpnAddr netip.Addr , cacheCallback func (* HandshakeHostInfo )) (* HostInfo , bool ) {
131133 _ , found := f .myVpnNetworksTable .Lookup (vpnAddr )
132- if ! found {
133- vpnAddr = f .inside .RouteFor (vpnAddr )
134- if ! vpnAddr .IsValid () {
135- return nil , false
134+ if found {
135+ return f .handshakeManager .GetOrHandshake (vpnAddr , cacheCallback )
136+ }
137+
138+ return nil , false
139+ }
140+
141+ // getOrHandshakeConsiderRouting will try to find the HostInfo to handle this packet, starting a handshake if necessary.
142+ // If the 2nd return var is false then the hostinfo is not ready to be used in a tunnel.
143+ func (f * Interface ) getOrHandshakeConsiderRouting (fwPacket * firewall.Packet , cacheCallback func (* HandshakeHostInfo )) (* HostInfo , bool ) {
144+
145+ destinationAddr := fwPacket .RemoteAddr
146+
147+ hostinfo , ready := f .getOrHandshakeNoRouting (destinationAddr , cacheCallback )
148+
149+ // Host is inside the mesh, no routing required
150+ if hostinfo != nil {
151+ return hostinfo , ready
152+ }
153+
154+ gateways := f .inside .RoutesFor (destinationAddr )
155+
156+ switch len (gateways ) {
157+ case 0 :
158+ return nil , false
159+ case 1 :
160+ // Single gateway route
161+ return f .handshakeManager .GetOrHandshake (gateways [0 ].Addr (), cacheCallback )
162+ default :
163+ // Multi gateway route, perform ECMP categorization
164+ gatewayAddr , balancingOk := routing .BalancePacket (fwPacket , gateways )
165+
166+ if ! balancingOk {
167+ // This happens if the gateway buckets were not calculated, this _should_ never happen
168+ f .l .Error ("Gateway buckets not calculated, fallback from ECMP to random routing. Please report this bug." )
136169 }
170+
171+ var handshakeInfoForChosenGateway * HandshakeHostInfo
172+ var hhReceiver = func (hh * HandshakeHostInfo ) {
173+ handshakeInfoForChosenGateway = hh
174+ }
175+
176+ // Store the handshakeHostInfo for later.
177+ // If this node is not reachable we will attempt other nodes, if none are reachable we will
178+ // cache the packet for this gateway.
179+ if hostinfo , ready = f .handshakeManager .GetOrHandshake (gatewayAddr , hhReceiver ); ready {
180+ return hostinfo , true
181+ }
182+
183+ // It appears the selected gateway cannot be reached, find another gateway to fallback on.
184+ // The current implementation breaks ECMP but that seems better than no connectivity.
185+ // If ECMP is also required when a gateway is down then connectivity status
186+ // for each gateway needs to be kept and the weights recalculated when they go up or down.
187+ // This would also need to interact with unsafe_route updates through reloading the config or
188+ // use of the use_system_route_table option
189+
190+ if f .l .Level >= logrus .DebugLevel {
191+ f .l .WithField ("destination" , destinationAddr ).
192+ WithField ("originalGateway" , gatewayAddr ).
193+ Debugln ("Calculated gateway for ECMP not available, attempting other gateways" )
194+ }
195+
196+ for i := range gateways {
197+ // Skip the gateway that failed previously
198+ if gateways [i ].Addr () == gatewayAddr {
199+ continue
200+ }
201+
202+ // We do not need the HandshakeHostInfo since we cache the packet in the originally chosen gateway
203+ if hostinfo , ready = f .handshakeManager .GetOrHandshake (gateways [i ].Addr (), nil ); ready {
204+ return hostinfo , true
205+ }
206+ }
207+
208+ // No gateways reachable, cache the packet in the originally chosen gateway
209+ cacheCallback (handshakeInfoForChosenGateway )
210+ return hostinfo , false
137211 }
138212
139- return f .handshakeManager .GetOrHandshake (vpnAddr , cacheCallback )
140213}
141214
142215func (f * Interface ) sendMessageNow (t header.MessageType , st header.MessageSubType , hostinfo * HostInfo , p , nb , out []byte ) {
@@ -163,7 +236,7 @@ func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubTyp
163236
164237// SendMessageToVpnAddr handles real addr:port lookup and sends to the current best known address for vpnAddr
165238func (f * Interface ) SendMessageToVpnAddr (t header.MessageType , st header.MessageSubType , vpnAddr netip.Addr , p , nb , out []byte ) {
166- hostInfo , ready := f .getOrHandshake (vpnAddr , func (hh * HandshakeHostInfo ) {
239+ hostInfo , ready := f .getOrHandshakeNoRouting (vpnAddr , func (hh * HandshakeHostInfo ) {
167240 hh .cachePacket (f .l , t , st , p , f .SendMessageToHostInfo , f .cachedPacketMetrics )
168241 })
169242
0 commit comments