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 }