github.com/slackhq/nebula@v1.9.0/relay_manager.go (about) 1 package nebula 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sync/atomic" 8 9 "github.com/sirupsen/logrus" 10 "github.com/slackhq/nebula/config" 11 "github.com/slackhq/nebula/header" 12 "github.com/slackhq/nebula/iputil" 13 ) 14 15 type relayManager struct { 16 l *logrus.Logger 17 hostmap *HostMap 18 amRelay atomic.Bool 19 } 20 21 func NewRelayManager(ctx context.Context, l *logrus.Logger, hostmap *HostMap, c *config.C) *relayManager { 22 rm := &relayManager{ 23 l: l, 24 hostmap: hostmap, 25 } 26 rm.reload(c, true) 27 c.RegisterReloadCallback(func(c *config.C) { 28 err := rm.reload(c, false) 29 if err != nil { 30 l.WithError(err).Error("Failed to reload relay_manager") 31 } 32 }) 33 return rm 34 } 35 36 func (rm *relayManager) reload(c *config.C, initial bool) error { 37 if initial || c.HasChanged("relay.am_relay") { 38 rm.setAmRelay(c.GetBool("relay.am_relay", false)) 39 } 40 return nil 41 } 42 43 func (rm *relayManager) GetAmRelay() bool { 44 return rm.amRelay.Load() 45 } 46 47 func (rm *relayManager) setAmRelay(v bool) { 48 rm.amRelay.Store(v) 49 } 50 51 // AddRelay finds an available relay index on the hostmap, and associates the relay info with it. 52 // relayHostInfo is the Nebula peer which can be used as a relay to access the target vpnIp. 53 func AddRelay(l *logrus.Logger, relayHostInfo *HostInfo, hm *HostMap, vpnIp iputil.VpnIp, remoteIdx *uint32, relayType int, state int) (uint32, error) { 54 hm.Lock() 55 defer hm.Unlock() 56 for i := 0; i < 32; i++ { 57 index, err := generateIndex(l) 58 if err != nil { 59 return 0, err 60 } 61 62 _, inRelays := hm.Relays[index] 63 if !inRelays { 64 // Avoid standing up a relay that can't be used since only the primary hostinfo 65 // will be pointed to by the relay logic 66 //TODO: if there was an existing primary and it had relay state, should we merge? 67 hm.unlockedMakePrimary(relayHostInfo) 68 69 hm.Relays[index] = relayHostInfo 70 newRelay := Relay{ 71 Type: relayType, 72 State: state, 73 LocalIndex: index, 74 PeerIp: vpnIp, 75 } 76 77 if remoteIdx != nil { 78 newRelay.RemoteIndex = *remoteIdx 79 } 80 relayHostInfo.relayState.InsertRelay(vpnIp, index, &newRelay) 81 82 return index, nil 83 } 84 } 85 86 return 0, errors.New("failed to generate unique localIndexId") 87 } 88 89 // EstablishRelay updates a Requested Relay to become an Established Relay, which can pass traffic. 90 func (rm *relayManager) EstablishRelay(relayHostInfo *HostInfo, m *NebulaControl) (*Relay, error) { 91 relay, ok := relayHostInfo.relayState.CompleteRelayByIdx(m.InitiatorRelayIndex, m.ResponderRelayIndex) 92 if !ok { 93 rm.l.WithFields(logrus.Fields{"relay": relayHostInfo.vpnIp, 94 "initiatorRelayIndex": m.InitiatorRelayIndex, 95 "relayFrom": m.RelayFromIp, 96 "relayTo": m.RelayToIp}).Info("relayManager failed to update relay") 97 return nil, fmt.Errorf("unknown relay") 98 } 99 100 return relay, nil 101 } 102 103 func (rm *relayManager) HandleControlMsg(h *HostInfo, m *NebulaControl, f *Interface) { 104 105 switch m.Type { 106 case NebulaControl_CreateRelayRequest: 107 rm.handleCreateRelayRequest(h, f, m) 108 case NebulaControl_CreateRelayResponse: 109 rm.handleCreateRelayResponse(h, f, m) 110 } 111 112 } 113 114 func (rm *relayManager) handleCreateRelayResponse(h *HostInfo, f *Interface, m *NebulaControl) { 115 rm.l.WithFields(logrus.Fields{ 116 "relayFrom": iputil.VpnIp(m.RelayFromIp), 117 "relayTo": iputil.VpnIp(m.RelayToIp), 118 "initiatorRelayIndex": m.InitiatorRelayIndex, 119 "responderRelayIndex": m.ResponderRelayIndex, 120 "vpnIp": h.vpnIp}). 121 Info("handleCreateRelayResponse") 122 target := iputil.VpnIp(m.RelayToIp) 123 124 relay, err := rm.EstablishRelay(h, m) 125 if err != nil { 126 rm.l.WithError(err).Error("Failed to update relay for relayTo") 127 return 128 } 129 // Do I need to complete the relays now? 130 if relay.Type == TerminalType { 131 return 132 } 133 // I'm the middle man. Let the initiator know that the I've established the relay they requested. 134 peerHostInfo := rm.hostmap.QueryVpnIp(relay.PeerIp) 135 if peerHostInfo == nil { 136 rm.l.WithField("relayTo", relay.PeerIp).Error("Can't find a HostInfo for peer") 137 return 138 } 139 peerRelay, ok := peerHostInfo.relayState.QueryRelayForByIp(target) 140 if !ok { 141 rm.l.WithField("relayTo", peerHostInfo.vpnIp).Error("peerRelay does not have Relay state for relayTo") 142 return 143 } 144 if peerRelay.State == PeerRequested { 145 peerRelay.State = Established 146 resp := NebulaControl{ 147 Type: NebulaControl_CreateRelayResponse, 148 ResponderRelayIndex: peerRelay.LocalIndex, 149 InitiatorRelayIndex: peerRelay.RemoteIndex, 150 RelayFromIp: uint32(peerHostInfo.vpnIp), 151 RelayToIp: uint32(target), 152 } 153 msg, err := resp.Marshal() 154 if err != nil { 155 rm.l. 156 WithError(err).Error("relayManager Failed to marshal Control CreateRelayResponse message to create relay") 157 } else { 158 f.SendMessageToHostInfo(header.Control, 0, peerHostInfo, msg, make([]byte, 12), make([]byte, mtu)) 159 rm.l.WithFields(logrus.Fields{ 160 "relayFrom": iputil.VpnIp(resp.RelayFromIp), 161 "relayTo": iputil.VpnIp(resp.RelayToIp), 162 "initiatorRelayIndex": resp.InitiatorRelayIndex, 163 "responderRelayIndex": resp.ResponderRelayIndex, 164 "vpnIp": peerHostInfo.vpnIp}). 165 Info("send CreateRelayResponse") 166 } 167 } 168 } 169 170 func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *NebulaControl) { 171 172 from := iputil.VpnIp(m.RelayFromIp) 173 target := iputil.VpnIp(m.RelayToIp) 174 175 logMsg := rm.l.WithFields(logrus.Fields{ 176 "relayFrom": from, 177 "relayTo": target, 178 "initiatorRelayIndex": m.InitiatorRelayIndex, 179 "vpnIp": h.vpnIp}) 180 181 logMsg.Info("handleCreateRelayRequest") 182 // Is the source of the relay me? This should never happen, but did happen due to 183 // an issue migrating relays over to newly re-handshaked host info objects. 184 if from == f.myVpnIp { 185 logMsg.WithField("myIP", f.myVpnIp).Error("Discarding relay request from myself") 186 return 187 } 188 // Is the target of the relay me? 189 if target == f.myVpnIp { 190 existingRelay, ok := h.relayState.QueryRelayForByIp(from) 191 if ok { 192 switch existingRelay.State { 193 case Requested: 194 ok = h.relayState.CompleteRelayByIP(from, m.InitiatorRelayIndex) 195 if !ok { 196 logMsg.Error("Relay State not found") 197 return 198 } 199 case Established: 200 if existingRelay.RemoteIndex != m.InitiatorRelayIndex { 201 // We got a brand new Relay request, because its index is different than what we saw before. 202 // This should never happen. The peer should never change an index, once created. 203 logMsg.WithFields(logrus.Fields{ 204 "existingRemoteIndex": existingRelay.RemoteIndex}).Error("Existing relay mismatch with CreateRelayRequest") 205 return 206 } 207 } 208 } else { 209 _, err := AddRelay(rm.l, h, f.hostMap, from, &m.InitiatorRelayIndex, TerminalType, Established) 210 if err != nil { 211 logMsg.WithError(err).Error("Failed to add relay") 212 return 213 } 214 } 215 216 relay, ok := h.relayState.QueryRelayForByIp(from) 217 if !ok { 218 logMsg.Error("Relay State not found") 219 return 220 } 221 222 resp := NebulaControl{ 223 Type: NebulaControl_CreateRelayResponse, 224 ResponderRelayIndex: relay.LocalIndex, 225 InitiatorRelayIndex: relay.RemoteIndex, 226 RelayFromIp: uint32(from), 227 RelayToIp: uint32(target), 228 } 229 msg, err := resp.Marshal() 230 if err != nil { 231 logMsg. 232 WithError(err).Error("relayManager Failed to marshal Control CreateRelayResponse message to create relay") 233 } else { 234 f.SendMessageToHostInfo(header.Control, 0, h, msg, make([]byte, 12), make([]byte, mtu)) 235 rm.l.WithFields(logrus.Fields{ 236 "relayFrom": iputil.VpnIp(resp.RelayFromIp), 237 "relayTo": iputil.VpnIp(resp.RelayToIp), 238 "initiatorRelayIndex": resp.InitiatorRelayIndex, 239 "responderRelayIndex": resp.ResponderRelayIndex, 240 "vpnIp": h.vpnIp}). 241 Info("send CreateRelayResponse") 242 } 243 return 244 } else { 245 // the target is not me. Create a relay to the target, from me. 246 if !rm.GetAmRelay() { 247 return 248 } 249 peer := rm.hostmap.QueryVpnIp(target) 250 if peer == nil { 251 // Try to establish a connection to this host. If we get a future relay request, 252 // we'll be ready! 253 f.Handshake(target) 254 return 255 } 256 if peer.remote == nil { 257 // Only create relays to peers for whom I have a direct connection 258 return 259 } 260 sendCreateRequest := false 261 var index uint32 262 var err error 263 targetRelay, ok := peer.relayState.QueryRelayForByIp(from) 264 if ok { 265 index = targetRelay.LocalIndex 266 if targetRelay.State == Requested { 267 sendCreateRequest = true 268 } 269 } else { 270 // Allocate an index in the hostMap for this relay peer 271 index, err = AddRelay(rm.l, peer, f.hostMap, from, nil, ForwardingType, Requested) 272 if err != nil { 273 return 274 } 275 sendCreateRequest = true 276 } 277 if sendCreateRequest { 278 // Send a CreateRelayRequest to the peer. 279 req := NebulaControl{ 280 Type: NebulaControl_CreateRelayRequest, 281 InitiatorRelayIndex: index, 282 RelayFromIp: uint32(h.vpnIp), 283 RelayToIp: uint32(target), 284 } 285 msg, err := req.Marshal() 286 if err != nil { 287 logMsg. 288 WithError(err).Error("relayManager Failed to marshal Control message to create relay") 289 } else { 290 f.SendMessageToHostInfo(header.Control, 0, peer, msg, make([]byte, 12), make([]byte, mtu)) 291 rm.l.WithFields(logrus.Fields{ 292 "relayFrom": iputil.VpnIp(req.RelayFromIp), 293 "relayTo": iputil.VpnIp(req.RelayToIp), 294 "initiatorRelayIndex": req.InitiatorRelayIndex, 295 "responderRelayIndex": req.ResponderRelayIndex, 296 "vpnIp": target}). 297 Info("send CreateRelayRequest") 298 } 299 } 300 // Also track the half-created Relay state just received 301 relay, ok := h.relayState.QueryRelayForByIp(target) 302 if !ok { 303 // Add the relay 304 state := PeerRequested 305 if targetRelay != nil && targetRelay.State == Established { 306 state = Established 307 } 308 _, err := AddRelay(rm.l, h, f.hostMap, target, &m.InitiatorRelayIndex, ForwardingType, state) 309 if err != nil { 310 logMsg. 311 WithError(err).Error("relayManager Failed to allocate a local index for relay") 312 return 313 } 314 } else { 315 switch relay.State { 316 case Established: 317 if relay.RemoteIndex != m.InitiatorRelayIndex { 318 // We got a brand new Relay request, because its index is different than what we saw before. 319 // This should never happen. The peer should never change an index, once created. 320 logMsg.WithFields(logrus.Fields{ 321 "existingRemoteIndex": relay.RemoteIndex}).Error("Existing relay mismatch with CreateRelayRequest") 322 return 323 } 324 resp := NebulaControl{ 325 Type: NebulaControl_CreateRelayResponse, 326 ResponderRelayIndex: relay.LocalIndex, 327 InitiatorRelayIndex: relay.RemoteIndex, 328 RelayFromIp: uint32(h.vpnIp), 329 RelayToIp: uint32(target), 330 } 331 msg, err := resp.Marshal() 332 if err != nil { 333 rm.l. 334 WithError(err).Error("relayManager Failed to marshal Control CreateRelayResponse message to create relay") 335 } else { 336 f.SendMessageToHostInfo(header.Control, 0, h, msg, make([]byte, 12), make([]byte, mtu)) 337 rm.l.WithFields(logrus.Fields{ 338 "relayFrom": iputil.VpnIp(resp.RelayFromIp), 339 "relayTo": iputil.VpnIp(resp.RelayToIp), 340 "initiatorRelayIndex": resp.InitiatorRelayIndex, 341 "responderRelayIndex": resp.ResponderRelayIndex, 342 "vpnIp": h.vpnIp}). 343 Info("send CreateRelayResponse") 344 } 345 346 case Requested: 347 // Keep waiting for the other relay to complete 348 } 349 } 350 } 351 } 352 353 func (rm *relayManager) RemoveRelay(localIdx uint32) { 354 rm.hostmap.RemoveRelay(localIdx) 355 }