8
8
"github.com/slackhq/nebula/header"
9
9
"github.com/slackhq/nebula/iputil"
10
10
"github.com/slackhq/nebula/noiseutil"
11
+ "github.com/slackhq/nebula/routing"
11
12
)
12
13
13
14
func (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
49
50
return
50
51
}
51
52
52
- hostinfo , ready := f .getOrHandshake (fwPacket . RemoteAddr , func (hh * HandshakeHostInfo ) {
53
+ hostinfo , ready := f .getOrHandshakeConsiderRouting (fwPacket , func (hh * HandshakeHostInfo ) {
53
54
hh .cachePacket (f .l , header .Message , 0 , packet , f .sendMessageNow , f .cachedPacketMetrics )
54
55
})
55
56
@@ -121,22 +122,94 @@ func (f *Interface) rejectOutside(packet []byte, ci *ConnectionState, hostinfo *
121
122
f .sendNoMetrics (header .Message , 0 , ci , hostinfo , netip.AddrPort {}, out , nb , packet , q )
122
123
}
123
124
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
124
126
func (f * Interface ) Handshake (vpnAddr netip.Addr ) {
125
- f .getOrHandshake (vpnAddr , nil )
127
+ f .getOrHandshakeNoRouting (vpnAddr , nil )
126
128
}
127
129
128
- // getOrHandshake returns nil if the vpnAddr is not routable.
130
+ // getOrHandshakeNoRouting returns nil if the vpnAddr is not routable.
129
131
// 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 ) {
131
133
_ , 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." )
136
169
}
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
137
211
}
138
212
139
- return f .handshakeManager .GetOrHandshake (vpnAddr , cacheCallback )
140
213
}
141
214
142
215
func (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
163
236
164
237
// SendMessageToVpnAddr handles real addr:port lookup and sends to the current best known address for vpnAddr
165
238
func (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 ) {
167
240
hh .cachePacket (f .l , t , st , p , f .SendMessageToHostInfo , f .cachedPacketMetrics )
168
241
})
169
242
0 commit comments