github.com/slackhq/nebula@v1.9.0/control.go (about)

     1  package nebula
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"os"
     7  	"os/signal"
     8  	"syscall"
     9  
    10  	"github.com/sirupsen/logrus"
    11  	"github.com/slackhq/nebula/cert"
    12  	"github.com/slackhq/nebula/header"
    13  	"github.com/slackhq/nebula/iputil"
    14  	"github.com/slackhq/nebula/overlay"
    15  	"github.com/slackhq/nebula/udp"
    16  )
    17  
    18  // Every interaction here needs to take extra care to copy memory and not return or use arguments "as is" when touching
    19  // core. This means copying IP objects, slices, de-referencing pointers and taking the actual value, etc
    20  
    21  type controlEach func(h *HostInfo)
    22  
    23  type controlHostLister interface {
    24  	QueryVpnIp(vpnIp iputil.VpnIp) *HostInfo
    25  	ForEachIndex(each controlEach)
    26  	ForEachVpnIp(each controlEach)
    27  	GetPreferredRanges() []*net.IPNet
    28  }
    29  
    30  type Control struct {
    31  	f               *Interface
    32  	l               *logrus.Logger
    33  	ctx             context.Context
    34  	cancel          context.CancelFunc
    35  	sshStart        func()
    36  	statsStart      func()
    37  	dnsStart        func()
    38  	lighthouseStart func()
    39  }
    40  
    41  type ControlHostInfo struct {
    42  	VpnIp                  net.IP                  `json:"vpnIp"`
    43  	LocalIndex             uint32                  `json:"localIndex"`
    44  	RemoteIndex            uint32                  `json:"remoteIndex"`
    45  	RemoteAddrs            []*udp.Addr             `json:"remoteAddrs"`
    46  	Cert                   *cert.NebulaCertificate `json:"cert"`
    47  	MessageCounter         uint64                  `json:"messageCounter"`
    48  	CurrentRemote          *udp.Addr               `json:"currentRemote"`
    49  	CurrentRelaysToMe      []iputil.VpnIp          `json:"currentRelaysToMe"`
    50  	CurrentRelaysThroughMe []iputil.VpnIp          `json:"currentRelaysThroughMe"`
    51  }
    52  
    53  // Start actually runs nebula, this is a nonblocking call. To block use Control.ShutdownBlock()
    54  func (c *Control) Start() {
    55  	// Activate the interface
    56  	c.f.activate()
    57  
    58  	// Call all the delayed funcs that waited patiently for the interface to be created.
    59  	if c.sshStart != nil {
    60  		go c.sshStart()
    61  	}
    62  	if c.statsStart != nil {
    63  		go c.statsStart()
    64  	}
    65  	if c.dnsStart != nil {
    66  		go c.dnsStart()
    67  	}
    68  	if c.lighthouseStart != nil {
    69  		c.lighthouseStart()
    70  	}
    71  
    72  	// Start reading packets.
    73  	c.f.run()
    74  }
    75  
    76  func (c *Control) Context() context.Context {
    77  	return c.ctx
    78  }
    79  
    80  // Stop signals nebula to shutdown and close all tunnels, returns after the shutdown is complete
    81  func (c *Control) Stop() {
    82  	// Stop the handshakeManager (and other services), to prevent new tunnels from
    83  	// being created while we're shutting them all down.
    84  	c.cancel()
    85  
    86  	c.CloseAllTunnels(false)
    87  	if err := c.f.Close(); err != nil {
    88  		c.l.WithError(err).Error("Close interface failed")
    89  	}
    90  	c.l.Info("Goodbye")
    91  }
    92  
    93  // ShutdownBlock will listen for and block on term and interrupt signals, calling Control.Stop() once signalled
    94  func (c *Control) ShutdownBlock() {
    95  	sigChan := make(chan os.Signal, 1)
    96  	signal.Notify(sigChan, syscall.SIGTERM)
    97  	signal.Notify(sigChan, syscall.SIGINT)
    98  
    99  	rawSig := <-sigChan
   100  	sig := rawSig.String()
   101  	c.l.WithField("signal", sig).Info("Caught signal, shutting down")
   102  	c.Stop()
   103  }
   104  
   105  // RebindUDPServer asks the UDP listener to rebind it's listener. Mainly used on mobile clients when interfaces change
   106  func (c *Control) RebindUDPServer() {
   107  	_ = c.f.outside.Rebind()
   108  
   109  	// Trigger a lighthouse update, useful for mobile clients that should have an update interval of 0
   110  	c.f.lightHouse.SendUpdate()
   111  
   112  	// Let the main interface know that we rebound so that underlying tunnels know to trigger punches from their remotes
   113  	c.f.rebindCount++
   114  }
   115  
   116  // ListHostmapHosts returns details about the actual or pending (handshaking) hostmap by vpn ip
   117  func (c *Control) ListHostmapHosts(pendingMap bool) []ControlHostInfo {
   118  	if pendingMap {
   119  		return listHostMapHosts(c.f.handshakeManager)
   120  	} else {
   121  		return listHostMapHosts(c.f.hostMap)
   122  	}
   123  }
   124  
   125  // ListHostmapIndexes returns details about the actual or pending (handshaking) hostmap by local index id
   126  func (c *Control) ListHostmapIndexes(pendingMap bool) []ControlHostInfo {
   127  	if pendingMap {
   128  		return listHostMapIndexes(c.f.handshakeManager)
   129  	} else {
   130  		return listHostMapIndexes(c.f.hostMap)
   131  	}
   132  }
   133  
   134  // GetHostInfoByVpnIp returns a single tunnels hostInfo, or nil if not found
   135  func (c *Control) GetHostInfoByVpnIp(vpnIp iputil.VpnIp, pending bool) *ControlHostInfo {
   136  	var hl controlHostLister
   137  	if pending {
   138  		hl = c.f.handshakeManager
   139  	} else {
   140  		hl = c.f.hostMap
   141  	}
   142  
   143  	h := hl.QueryVpnIp(vpnIp)
   144  	if h == nil {
   145  		return nil
   146  	}
   147  
   148  	ch := copyHostInfo(h, c.f.hostMap.GetPreferredRanges())
   149  	return &ch
   150  }
   151  
   152  // SetRemoteForTunnel forces a tunnel to use a specific remote
   153  func (c *Control) SetRemoteForTunnel(vpnIp iputil.VpnIp, addr udp.Addr) *ControlHostInfo {
   154  	hostInfo := c.f.hostMap.QueryVpnIp(vpnIp)
   155  	if hostInfo == nil {
   156  		return nil
   157  	}
   158  
   159  	hostInfo.SetRemote(addr.Copy())
   160  	ch := copyHostInfo(hostInfo, c.f.hostMap.GetPreferredRanges())
   161  	return &ch
   162  }
   163  
   164  // CloseTunnel closes a fully established tunnel. If localOnly is false it will notify the remote end as well.
   165  func (c *Control) CloseTunnel(vpnIp iputil.VpnIp, localOnly bool) bool {
   166  	hostInfo := c.f.hostMap.QueryVpnIp(vpnIp)
   167  	if hostInfo == nil {
   168  		return false
   169  	}
   170  
   171  	if !localOnly {
   172  		c.f.send(
   173  			header.CloseTunnel,
   174  			0,
   175  			hostInfo.ConnectionState,
   176  			hostInfo,
   177  			[]byte{},
   178  			make([]byte, 12, 12),
   179  			make([]byte, mtu),
   180  		)
   181  	}
   182  
   183  	c.f.closeTunnel(hostInfo)
   184  	return true
   185  }
   186  
   187  // CloseAllTunnels is just like CloseTunnel except it goes through and shuts them all down, optionally you can avoid shutting down lighthouse tunnels
   188  // the int returned is a count of tunnels closed
   189  func (c *Control) CloseAllTunnels(excludeLighthouses bool) (closed int) {
   190  	//TODO: this is probably better as a function in ConnectionManager or HostMap directly
   191  	lighthouses := c.f.lightHouse.GetLighthouses()
   192  
   193  	shutdown := func(h *HostInfo) {
   194  		if excludeLighthouses {
   195  			if _, ok := lighthouses[h.vpnIp]; ok {
   196  				return
   197  			}
   198  		}
   199  		c.f.send(header.CloseTunnel, 0, h.ConnectionState, h, []byte{}, make([]byte, 12, 12), make([]byte, mtu))
   200  		c.f.closeTunnel(h)
   201  
   202  		c.l.WithField("vpnIp", h.vpnIp).WithField("udpAddr", h.remote).
   203  			Debug("Sending close tunnel message")
   204  		closed++
   205  	}
   206  
   207  	// Learn which hosts are being used as relays, so we can shut them down last.
   208  	relayingHosts := map[iputil.VpnIp]*HostInfo{}
   209  	// Grab the hostMap lock to access the Relays map
   210  	c.f.hostMap.Lock()
   211  	for _, relayingHost := range c.f.hostMap.Relays {
   212  		relayingHosts[relayingHost.vpnIp] = relayingHost
   213  	}
   214  	c.f.hostMap.Unlock()
   215  
   216  	hostInfos := []*HostInfo{}
   217  	// Grab the hostMap lock to access the Hosts map
   218  	c.f.hostMap.Lock()
   219  	for _, relayHost := range c.f.hostMap.Indexes {
   220  		if _, ok := relayingHosts[relayHost.vpnIp]; !ok {
   221  			hostInfos = append(hostInfos, relayHost)
   222  		}
   223  	}
   224  	c.f.hostMap.Unlock()
   225  
   226  	for _, h := range hostInfos {
   227  		shutdown(h)
   228  	}
   229  	for _, h := range relayingHosts {
   230  		shutdown(h)
   231  	}
   232  	return
   233  }
   234  
   235  func (c *Control) Device() overlay.Device {
   236  	return c.f.inside
   237  }
   238  
   239  func copyHostInfo(h *HostInfo, preferredRanges []*net.IPNet) ControlHostInfo {
   240  
   241  	chi := ControlHostInfo{
   242  		VpnIp:                  h.vpnIp.ToIP(),
   243  		LocalIndex:             h.localIndexId,
   244  		RemoteIndex:            h.remoteIndexId,
   245  		RemoteAddrs:            h.remotes.CopyAddrs(preferredRanges),
   246  		CurrentRelaysToMe:      h.relayState.CopyRelayIps(),
   247  		CurrentRelaysThroughMe: h.relayState.CopyRelayForIps(),
   248  	}
   249  
   250  	if h.ConnectionState != nil {
   251  		chi.MessageCounter = h.ConnectionState.messageCounter.Load()
   252  	}
   253  
   254  	if c := h.GetCert(); c != nil {
   255  		chi.Cert = c.Copy()
   256  	}
   257  
   258  	if h.remote != nil {
   259  		chi.CurrentRemote = h.remote.Copy()
   260  	}
   261  
   262  	return chi
   263  }
   264  
   265  func listHostMapHosts(hl controlHostLister) []ControlHostInfo {
   266  	hosts := make([]ControlHostInfo, 0)
   267  	pr := hl.GetPreferredRanges()
   268  	hl.ForEachVpnIp(func(hostinfo *HostInfo) {
   269  		hosts = append(hosts, copyHostInfo(hostinfo, pr))
   270  	})
   271  	return hosts
   272  }
   273  
   274  func listHostMapIndexes(hl controlHostLister) []ControlHostInfo {
   275  	hosts := make([]ControlHostInfo, 0)
   276  	pr := hl.GetPreferredRanges()
   277  	hl.ForEachIndex(func(hostinfo *HostInfo) {
   278  		hosts = append(hosts, copyHostInfo(hostinfo, pr))
   279  	})
   280  	return hosts
   281  }