mirror of
https://github.com/slackhq/nebula.git
synced 2024-11-14 20:05:53 +08:00
2a4beb41b9
Previously, every packet we see gets a lock on the conntrack table and updates it. When running with multiple routines, this can cause heavy lock contention and limit our ability for the threads to run independently. This change caches reads from the conntrack table for a very short period of time to reduce this lock contention. This cache will currently default to disabled unless you are running with multiple routines, in which case the default cache delay will be 1 second. This means that entries in the conntrack table may be up to 1 second out of date and remain in a routine local cache for up to 1 second longer than the global table. Instead of calling time.Now() for every packet, this cache system relies on a tick thread that updates the current cache "version" each tick. Every packet we check if the cache version is out of date, and reset the cache if so.
143 lines
2.8 KiB
Go
143 lines
2.8 KiB
Go
// +build !linux android
|
|
|
|
// udp_generic implements the nebula UDP interface in pure Go stdlib. This
|
|
// means it can be used on platforms like Darwin and Windows.
|
|
|
|
package nebula
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type udpAddr struct {
|
|
net.UDPAddr
|
|
}
|
|
|
|
type udpConn struct {
|
|
*net.UDPConn
|
|
}
|
|
|
|
func NewUDPAddr(ip uint32, port uint16) *udpAddr {
|
|
return &udpAddr{
|
|
UDPAddr: net.UDPAddr{
|
|
IP: int2ip(ip),
|
|
Port: int(port),
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewUDPAddrFromString(s string) *udpAddr {
|
|
p := strings.Split(s, ":")
|
|
if len(p) < 2 {
|
|
return nil
|
|
}
|
|
|
|
port, _ := strconv.Atoi(p[1])
|
|
return &udpAddr{
|
|
UDPAddr: net.UDPAddr{
|
|
IP: net.ParseIP(p[0]),
|
|
Port: port,
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewListener(ip string, port int, multi bool) (*udpConn, error) {
|
|
lc := NewListenConfig(multi)
|
|
pc, err := lc.ListenPacket(context.TODO(), "udp4", fmt.Sprintf("%s:%d", ip, port))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if uc, ok := pc.(*net.UDPConn); ok {
|
|
return &udpConn{UDPConn: uc}, nil
|
|
}
|
|
return nil, fmt.Errorf("Unexpected PacketConn: %T %#v", pc, pc)
|
|
}
|
|
|
|
func (ua *udpAddr) Equals(t *udpAddr) bool {
|
|
if t == nil || ua == nil {
|
|
return t == nil && ua == nil
|
|
}
|
|
return ua.IP.Equal(t.IP) && ua.Port == t.Port
|
|
}
|
|
|
|
func (ua *udpAddr) Copy() udpAddr {
|
|
nu := udpAddr{net.UDPAddr{
|
|
Port: ua.Port,
|
|
Zone: ua.Zone,
|
|
IP: make(net.IP, len(ua.IP)),
|
|
}}
|
|
|
|
copy(nu.IP, ua.IP)
|
|
return nu
|
|
}
|
|
|
|
func (uc *udpConn) WriteTo(b []byte, addr *udpAddr) error {
|
|
_, err := uc.UDPConn.WriteToUDP(b, &addr.UDPAddr)
|
|
return err
|
|
}
|
|
|
|
func (uc *udpConn) LocalAddr() (*udpAddr, error) {
|
|
a := uc.UDPConn.LocalAddr()
|
|
|
|
switch v := a.(type) {
|
|
case *net.UDPAddr:
|
|
return &udpAddr{UDPAddr: *v}, nil
|
|
default:
|
|
return nil, fmt.Errorf("LocalAddr returned: %#v", a)
|
|
}
|
|
}
|
|
|
|
func (u *udpConn) reloadConfig(c *Config) {
|
|
// TODO
|
|
}
|
|
|
|
func NewUDPStatsEmitter(udpConns []*udpConn) func() {
|
|
// No UDP stats for non-linux
|
|
return func() {}
|
|
}
|
|
|
|
type rawMessage struct {
|
|
Len uint32
|
|
}
|
|
|
|
func (u *udpConn) ListenOut(f *Interface, q int) {
|
|
plaintext := make([]byte, mtu)
|
|
buffer := make([]byte, mtu)
|
|
header := &Header{}
|
|
fwPacket := &FirewallPacket{}
|
|
udpAddr := &udpAddr{}
|
|
nb := make([]byte, 12, 12)
|
|
|
|
lhh := f.lightHouse.NewRequestHandler()
|
|
|
|
conntrackCache := NewConntrackCacheTicker(f.conntrackCacheTimeout)
|
|
|
|
for {
|
|
// Just read one packet at a time
|
|
n, rua, err := u.ReadFromUDP(buffer)
|
|
if err != nil {
|
|
l.WithError(err).Error("Failed to read packets")
|
|
continue
|
|
}
|
|
|
|
udpAddr.UDPAddr = *rua
|
|
f.readOutsidePackets(udpAddr, plaintext[:0], buffer[:n], header, fwPacket, lhh, nb, q, conntrackCache.Get())
|
|
}
|
|
}
|
|
|
|
func udp2ip(addr *udpAddr) net.IP {
|
|
return addr.IP
|
|
}
|
|
|
|
func udp2ipInt(addr *udpAddr) uint32 {
|
|
return binary.BigEndian.Uint32(addr.IP.To4())
|
|
}
|
|
|
|
func hostDidRoam(addr *udpAddr, newaddr *udpAddr) bool {
|
|
return !addr.Equals(newaddr)
|
|
}
|