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 }