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  }