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

     1  package nebula
     2  
     3  import (
     4  	"github.com/sirupsen/logrus"
     5  	"github.com/slackhq/nebula/firewall"
     6  	"github.com/slackhq/nebula/header"
     7  	"github.com/slackhq/nebula/iputil"
     8  	"github.com/slackhq/nebula/noiseutil"
     9  	"github.com/slackhq/nebula/udp"
    10  )
    11  
    12  func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet, nb, out []byte, q int, localCache firewall.ConntrackCache) {
    13  	err := newPacket(packet, false, fwPacket)
    14  	if err != nil {
    15  		if f.l.Level >= logrus.DebugLevel {
    16  			f.l.WithField("packet", packet).Debugf("Error while validating outbound packet: %s", err)
    17  		}
    18  		return
    19  	}
    20  
    21  	// Ignore local broadcast packets
    22  	if f.dropLocalBroadcast && fwPacket.RemoteIP == f.localBroadcast {
    23  		return
    24  	}
    25  
    26  	if fwPacket.RemoteIP == f.myVpnIp {
    27  		// Immediately forward packets from self to self.
    28  		// This should only happen on Darwin-based and FreeBSD hosts, which
    29  		// routes packets from the Nebula IP to the Nebula IP through the Nebula
    30  		// TUN device.
    31  		if immediatelyForwardToSelf {
    32  			_, err := f.readers[q].Write(packet)
    33  			if err != nil {
    34  				f.l.WithError(err).Error("Failed to forward to tun")
    35  			}
    36  		}
    37  		// Otherwise, drop. On linux, we should never see these packets - Linux
    38  		// routes packets from the nebula IP to the nebula IP through the loopback device.
    39  		return
    40  	}
    41  
    42  	// Ignore broadcast packets
    43  	if f.dropMulticast && isMulticast(fwPacket.RemoteIP) {
    44  		return
    45  	}
    46  
    47  	hostinfo, ready := f.getOrHandshake(fwPacket.RemoteIP, func(hh *HandshakeHostInfo) {
    48  		hh.cachePacket(f.l, header.Message, 0, packet, f.sendMessageNow, f.cachedPacketMetrics)
    49  	})
    50  
    51  	if hostinfo == nil {
    52  		f.rejectInside(packet, out, q)
    53  		if f.l.Level >= logrus.DebugLevel {
    54  			f.l.WithField("vpnIp", fwPacket.RemoteIP).
    55  				WithField("fwPacket", fwPacket).
    56  				Debugln("dropping outbound packet, vpnIp not in our CIDR or in unsafe routes")
    57  		}
    58  		return
    59  	}
    60  
    61  	if !ready {
    62  		return
    63  	}
    64  
    65  	dropReason := f.firewall.Drop(*fwPacket, false, hostinfo, f.pki.GetCAPool(), localCache)
    66  	if dropReason == nil {
    67  		f.sendNoMetrics(header.Message, 0, hostinfo.ConnectionState, hostinfo, nil, packet, nb, out, q)
    68  
    69  	} else {
    70  		f.rejectInside(packet, out, q)
    71  		if f.l.Level >= logrus.DebugLevel {
    72  			hostinfo.logger(f.l).
    73  				WithField("fwPacket", fwPacket).
    74  				WithField("reason", dropReason).
    75  				Debugln("dropping outbound packet")
    76  		}
    77  	}
    78  }
    79  
    80  func (f *Interface) rejectInside(packet []byte, out []byte, q int) {
    81  	if !f.firewall.InSendReject {
    82  		return
    83  	}
    84  
    85  	out = iputil.CreateRejectPacket(packet, out)
    86  	if len(out) == 0 {
    87  		return
    88  	}
    89  
    90  	_, err := f.readers[q].Write(out)
    91  	if err != nil {
    92  		f.l.WithError(err).Error("Failed to write to tun")
    93  	}
    94  }
    95  
    96  func (f *Interface) rejectOutside(packet []byte, ci *ConnectionState, hostinfo *HostInfo, nb, out []byte, q int) {
    97  	if !f.firewall.OutSendReject {
    98  		return
    99  	}
   100  
   101  	out = iputil.CreateRejectPacket(packet, out)
   102  	if len(out) == 0 {
   103  		return
   104  	}
   105  
   106  	if len(out) > iputil.MaxRejectPacketSize {
   107  		if f.l.GetLevel() >= logrus.InfoLevel {
   108  			f.l.
   109  				WithField("packet", packet).
   110  				WithField("outPacket", out).
   111  				Info("rejectOutside: packet too big, not sending")
   112  		}
   113  		return
   114  	}
   115  
   116  	f.sendNoMetrics(header.Message, 0, ci, hostinfo, nil, out, nb, packet, q)
   117  }
   118  
   119  func (f *Interface) Handshake(vpnIp iputil.VpnIp) {
   120  	f.getOrHandshake(vpnIp, nil)
   121  }
   122  
   123  // getOrHandshake returns nil if the vpnIp is not routable.
   124  // If the 2nd return var is false then the hostinfo is not ready to be used in a tunnel
   125  func (f *Interface) getOrHandshake(vpnIp iputil.VpnIp, cacheCallback func(*HandshakeHostInfo)) (*HostInfo, bool) {
   126  	if !ipMaskContains(f.lightHouse.myVpnIp, f.lightHouse.myVpnZeros, vpnIp) {
   127  		vpnIp = f.inside.RouteFor(vpnIp)
   128  		if vpnIp == 0 {
   129  			return nil, false
   130  		}
   131  	}
   132  
   133  	return f.handshakeManager.GetOrHandshake(vpnIp, cacheCallback)
   134  }
   135  
   136  func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubType, hostinfo *HostInfo, p, nb, out []byte) {
   137  	fp := &firewall.Packet{}
   138  	err := newPacket(p, false, fp)
   139  	if err != nil {
   140  		f.l.Warnf("error while parsing outgoing packet for firewall check; %v", err)
   141  		return
   142  	}
   143  
   144  	// check if packet is in outbound fw rules
   145  	dropReason := f.firewall.Drop(*fp, false, hostinfo, f.pki.GetCAPool(), nil)
   146  	if dropReason != nil {
   147  		if f.l.Level >= logrus.DebugLevel {
   148  			f.l.WithField("fwPacket", fp).
   149  				WithField("reason", dropReason).
   150  				Debugln("dropping cached packet")
   151  		}
   152  		return
   153  	}
   154  
   155  	f.sendNoMetrics(header.Message, st, hostinfo.ConnectionState, hostinfo, nil, p, nb, out, 0)
   156  }
   157  
   158  // SendMessageToVpnIp handles real ip:port lookup and sends to the current best known address for vpnIp
   159  func (f *Interface) SendMessageToVpnIp(t header.MessageType, st header.MessageSubType, vpnIp iputil.VpnIp, p, nb, out []byte) {
   160  	hostInfo, ready := f.getOrHandshake(vpnIp, func(hh *HandshakeHostInfo) {
   161  		hh.cachePacket(f.l, t, st, p, f.SendMessageToHostInfo, f.cachedPacketMetrics)
   162  	})
   163  
   164  	if hostInfo == nil {
   165  		if f.l.Level >= logrus.DebugLevel {
   166  			f.l.WithField("vpnIp", vpnIp).
   167  				Debugln("dropping SendMessageToVpnIp, vpnIp not in our CIDR or in unsafe routes")
   168  		}
   169  		return
   170  	}
   171  
   172  	if !ready {
   173  		return
   174  	}
   175  
   176  	f.SendMessageToHostInfo(t, st, hostInfo, p, nb, out)
   177  }
   178  
   179  func (f *Interface) SendMessageToHostInfo(t header.MessageType, st header.MessageSubType, hi *HostInfo, p, nb, out []byte) {
   180  	f.send(t, st, hi.ConnectionState, hi, p, nb, out)
   181  }
   182  
   183  func (f *Interface) send(t header.MessageType, st header.MessageSubType, ci *ConnectionState, hostinfo *HostInfo, p, nb, out []byte) {
   184  	f.messageMetrics.Tx(t, st, 1)
   185  	f.sendNoMetrics(t, st, ci, hostinfo, nil, p, nb, out, 0)
   186  }
   187  
   188  func (f *Interface) sendTo(t header.MessageType, st header.MessageSubType, ci *ConnectionState, hostinfo *HostInfo, remote *udp.Addr, p, nb, out []byte) {
   189  	f.messageMetrics.Tx(t, st, 1)
   190  	f.sendNoMetrics(t, st, ci, hostinfo, remote, p, nb, out, 0)
   191  }
   192  
   193  // SendVia sends a payload through a Relay tunnel. No authentication or encryption is done
   194  // to the payload for the ultimate target host, making this a useful method for sending
   195  // handshake messages to peers through relay tunnels.
   196  // via is the HostInfo through which the message is relayed.
   197  // ad is the plaintext data to authenticate, but not encrypt
   198  // nb is a buffer used to store the nonce value, re-used for performance reasons.
   199  // out is a buffer used to store the result of the Encrypt operation
   200  // q indicates which writer to use to send the packet.
   201  func (f *Interface) SendVia(via *HostInfo,
   202  	relay *Relay,
   203  	ad,
   204  	nb,
   205  	out []byte,
   206  	nocopy bool,
   207  ) {
   208  	if noiseutil.EncryptLockNeeded {
   209  		// NOTE: for goboring AESGCMTLS we need to lock because of the nonce check
   210  		via.ConnectionState.writeLock.Lock()
   211  	}
   212  	c := via.ConnectionState.messageCounter.Add(1)
   213  
   214  	out = header.Encode(out, header.Version, header.Message, header.MessageRelay, relay.RemoteIndex, c)
   215  	f.connectionManager.Out(via.localIndexId)
   216  
   217  	// Authenticate the header and payload, but do not encrypt for this message type.
   218  	// The payload consists of the inner, unencrypted Nebula header, as well as the end-to-end encrypted payload.
   219  	if len(out)+len(ad)+via.ConnectionState.eKey.Overhead() > cap(out) {
   220  		if noiseutil.EncryptLockNeeded {
   221  			via.ConnectionState.writeLock.Unlock()
   222  		}
   223  		via.logger(f.l).
   224  			WithField("outCap", cap(out)).
   225  			WithField("payloadLen", len(ad)).
   226  			WithField("headerLen", len(out)).
   227  			WithField("cipherOverhead", via.ConnectionState.eKey.Overhead()).
   228  			Error("SendVia out buffer not large enough for relay")
   229  		return
   230  	}
   231  
   232  	// The header bytes are written to the 'out' slice; Grow the slice to hold the header and associated data payload.
   233  	offset := len(out)
   234  	out = out[:offset+len(ad)]
   235  
   236  	// In one call path, the associated data _is_ already stored in out. In other call paths, the associated data must
   237  	// be copied into 'out'.
   238  	if !nocopy {
   239  		copy(out[offset:], ad)
   240  	}
   241  
   242  	var err error
   243  	out, err = via.ConnectionState.eKey.EncryptDanger(out, out, nil, c, nb)
   244  	if noiseutil.EncryptLockNeeded {
   245  		via.ConnectionState.writeLock.Unlock()
   246  	}
   247  	if err != nil {
   248  		via.logger(f.l).WithError(err).Info("Failed to EncryptDanger in sendVia")
   249  		return
   250  	}
   251  	err = f.writers[0].WriteTo(out, via.remote)
   252  	if err != nil {
   253  		via.logger(f.l).WithError(err).Info("Failed to WriteTo in sendVia")
   254  	}
   255  	f.connectionManager.RelayUsed(relay.LocalIndex)
   256  }
   257  
   258  func (f *Interface) sendNoMetrics(t header.MessageType, st header.MessageSubType, ci *ConnectionState, hostinfo *HostInfo, remote *udp.Addr, p, nb, out []byte, q int) {
   259  	if ci.eKey == nil {
   260  		//TODO: log warning
   261  		return
   262  	}
   263  	useRelay := remote == nil && hostinfo.remote == nil
   264  	fullOut := out
   265  
   266  	if useRelay {
   267  		if len(out) < header.Len {
   268  			// out always has a capacity of mtu, but not always a length greater than the header.Len.
   269  			// Grow it to make sure the next operation works.
   270  			out = out[:header.Len]
   271  		}
   272  		// Save a header's worth of data at the front of the 'out' buffer.
   273  		out = out[header.Len:]
   274  	}
   275  
   276  	if noiseutil.EncryptLockNeeded {
   277  		// NOTE: for goboring AESGCMTLS we need to lock because of the nonce check
   278  		ci.writeLock.Lock()
   279  	}
   280  	c := ci.messageCounter.Add(1)
   281  
   282  	//l.WithField("trace", string(debug.Stack())).Error("out Header ", &Header{Version, t, st, 0, hostinfo.remoteIndexId, c}, p)
   283  	out = header.Encode(out, header.Version, t, st, hostinfo.remoteIndexId, c)
   284  	f.connectionManager.Out(hostinfo.localIndexId)
   285  
   286  	// Query our LH if we haven't since the last time we've been rebound, this will cause the remote to punch against
   287  	// all our IPs and enable a faster roaming.
   288  	if t != header.CloseTunnel && hostinfo.lastRebindCount != f.rebindCount {
   289  		//NOTE: there is an update hole if a tunnel isn't used and exactly 256 rebinds occur before the tunnel is
   290  		// finally used again. This tunnel would eventually be torn down and recreated if this action didn't help.
   291  		f.lightHouse.QueryServer(hostinfo.vpnIp)
   292  		hostinfo.lastRebindCount = f.rebindCount
   293  		if f.l.Level >= logrus.DebugLevel {
   294  			f.l.WithField("vpnIp", hostinfo.vpnIp).Debug("Lighthouse update triggered for punch due to rebind counter")
   295  		}
   296  	}
   297  
   298  	var err error
   299  	out, err = ci.eKey.EncryptDanger(out, out, p, c, nb)
   300  	if noiseutil.EncryptLockNeeded {
   301  		ci.writeLock.Unlock()
   302  	}
   303  	if err != nil {
   304  		hostinfo.logger(f.l).WithError(err).
   305  			WithField("udpAddr", remote).WithField("counter", c).
   306  			WithField("attemptedCounter", c).
   307  			Error("Failed to encrypt outgoing packet")
   308  		return
   309  	}
   310  
   311  	if remote != nil {
   312  		err = f.writers[q].WriteTo(out, remote)
   313  		if err != nil {
   314  			hostinfo.logger(f.l).WithError(err).
   315  				WithField("udpAddr", remote).Error("Failed to write outgoing packet")
   316  		}
   317  	} else if hostinfo.remote != nil {
   318  		err = f.writers[q].WriteTo(out, hostinfo.remote)
   319  		if err != nil {
   320  			hostinfo.logger(f.l).WithError(err).
   321  				WithField("udpAddr", remote).Error("Failed to write outgoing packet")
   322  		}
   323  	} else {
   324  		// Try to send via a relay
   325  		for _, relayIP := range hostinfo.relayState.CopyRelayIps() {
   326  			relayHostInfo, relay, err := f.hostMap.QueryVpnIpRelayFor(hostinfo.vpnIp, relayIP)
   327  			if err != nil {
   328  				hostinfo.relayState.DeleteRelay(relayIP)
   329  				hostinfo.logger(f.l).WithField("relay", relayIP).WithError(err).Info("sendNoMetrics failed to find HostInfo")
   330  				continue
   331  			}
   332  			f.SendVia(relayHostInfo, relay, out, nb, fullOut[:header.Len+len(out)], true)
   333  			break
   334  		}
   335  	}
   336  }
   337  
   338  func isMulticast(ip iputil.VpnIp) bool {
   339  	// Class D multicast
   340  	return (((ip >> 24) & 0xff) & 0xf0) == 0xe0
   341  }