mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-13 17:09:07 +02:00
* [client] Reuse one wgctrl client in the kernel configurer The kernel configurer opened a new wgctrl client for every request, paying a socket open, a generic netlink family lookup and a close on each peer update. With 10000 peers a single update dropped from about 37us to 10us per call once the client is kept open. The configurer now opens the client lazily on the first request and keeps it. A failed request drops the client so the next one opens a fresh socket: a reply cut short mid-dump leaves stale messages in the netlink buffer, and reusing that socket would fail every later request on a sequence mismatch. The failed request is not retried. Close releases the client and rejects later requests instead of reopening a socket nobody would close. The kernel device never closed its configurer, so it now keeps it and closes it on teardown.
194 lines
4.2 KiB
Go
194 lines
4.2 KiB
Go
//go:build (linux && !android) || freebsd
|
|
|
|
package device
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/pion/transport/v3"
|
|
log "github.com/sirupsen/logrus"
|
|
"golang.zx2c4.com/wireguard/device"
|
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
|
|
|
"github.com/netbirdio/netbird/client/iface/configurer"
|
|
"github.com/netbirdio/netbird/client/iface/udpmux"
|
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
|
nbnet "github.com/netbirdio/netbird/client/net"
|
|
"github.com/netbirdio/netbird/sharedsock"
|
|
)
|
|
|
|
type TunKernelDevice struct {
|
|
name string
|
|
address wgaddr.Address
|
|
wgPort int
|
|
key string
|
|
mtu uint16
|
|
ctx context.Context
|
|
ctxCancel context.CancelFunc
|
|
transportNet transport.Net
|
|
|
|
link *wgLink
|
|
udpMuxConn net.PacketConn
|
|
udpMux *udpmux.UniversalUDPMuxDefault
|
|
configurer WGConfigurer
|
|
}
|
|
|
|
func NewKernelDevice(name string, address wgaddr.Address, wgPort int, key string, mtu uint16, transportNet transport.Net) *TunKernelDevice {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
return &TunKernelDevice{
|
|
ctx: ctx,
|
|
ctxCancel: cancel,
|
|
name: name,
|
|
address: address,
|
|
wgPort: wgPort,
|
|
key: key,
|
|
mtu: mtu,
|
|
transportNet: transportNet,
|
|
}
|
|
}
|
|
|
|
func (t *TunKernelDevice) Create() (WGConfigurer, error) {
|
|
link := newWGLink(t.name)
|
|
|
|
if err := link.recreate(); err != nil {
|
|
return nil, fmt.Errorf("recreate: %w", err)
|
|
}
|
|
|
|
t.link = link
|
|
|
|
if err := t.assignAddr(); err != nil {
|
|
return nil, fmt.Errorf("assign addr: %w", err)
|
|
}
|
|
|
|
// TODO: do a MTU discovery
|
|
log.Debugf("setting MTU: %d interface: %s", t.mtu, t.name)
|
|
|
|
if err := link.setMTU(int(t.mtu)); err != nil {
|
|
return nil, fmt.Errorf("set mtu: %w", err)
|
|
}
|
|
|
|
cfg := configurer.NewKernelConfigurer(t.name)
|
|
|
|
if err := cfg.ConfigureInterface(t.key, t.wgPort); err != nil {
|
|
cfg.Close()
|
|
return nil, fmt.Errorf("error configuring interface: %s", err)
|
|
}
|
|
|
|
if t.configurer != nil {
|
|
t.configurer.Close()
|
|
}
|
|
t.configurer = cfg
|
|
return cfg, nil
|
|
}
|
|
|
|
func (t *TunKernelDevice) Up() (*udpmux.UniversalUDPMuxDefault, error) {
|
|
if t.udpMux != nil {
|
|
return t.udpMux, nil
|
|
}
|
|
|
|
if t.link == nil {
|
|
return nil, fmt.Errorf("device is not ready yet")
|
|
}
|
|
|
|
log.Debugf("bringing up interface: %s", t.name)
|
|
|
|
if err := t.link.up(); err != nil {
|
|
log.Errorf("error bringing up interface: %s", t.name)
|
|
|
|
return nil, err
|
|
}
|
|
|
|
rawSock, err := sharedsock.Listen(t.wgPort, sharedsock.NewIncomingSTUNFilter(), t.mtu)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bindParams := udpmux.UniversalUDPMuxParams{
|
|
UDPConn: nbnet.WrapPacketConn(rawSock),
|
|
Net: t.transportNet,
|
|
WGAddress: t.address,
|
|
MTU: t.mtu,
|
|
}
|
|
mux := udpmux.NewUniversalUDPMuxDefault(bindParams)
|
|
go mux.ReadFromConn(t.ctx)
|
|
t.udpMuxConn = rawSock
|
|
t.udpMux = mux
|
|
|
|
log.Debugf("device is ready to use: %s", t.name)
|
|
return t.udpMux, nil
|
|
}
|
|
|
|
func (t *TunKernelDevice) UpdateAddr(address wgaddr.Address) error {
|
|
t.address = address
|
|
return t.assignAddr()
|
|
}
|
|
|
|
func (t *TunKernelDevice) Close() error {
|
|
if t.link == nil {
|
|
return nil
|
|
}
|
|
|
|
t.ctxCancel()
|
|
|
|
var closErr error
|
|
if err := t.link.Close(); err != nil {
|
|
log.Debugf("failed to close link: %s", err)
|
|
closErr = err
|
|
}
|
|
|
|
if t.configurer != nil {
|
|
t.configurer.Close()
|
|
}
|
|
|
|
if t.udpMux != nil {
|
|
if err := t.udpMux.Close(); err != nil {
|
|
log.Debugf("failed to close udp mux: %s", err)
|
|
closErr = err
|
|
}
|
|
|
|
if err := t.udpMuxConn.Close(); err != nil {
|
|
log.Debugf("failed to close udp mux connection: %s", err)
|
|
closErr = err
|
|
}
|
|
}
|
|
|
|
return closErr
|
|
}
|
|
|
|
func (t *TunKernelDevice) WgAddress() wgaddr.Address {
|
|
return t.address
|
|
}
|
|
|
|
func (t *TunKernelDevice) MTU() uint16 {
|
|
return t.mtu
|
|
}
|
|
|
|
func (t *TunKernelDevice) DeviceName() string {
|
|
return t.name
|
|
}
|
|
|
|
// Device returns the wireguard device, not applicable for kernel devices
|
|
func (t *TunKernelDevice) Device() *device.Device {
|
|
return nil
|
|
}
|
|
|
|
func (t *TunKernelDevice) FilteredDevice() *FilteredDevice {
|
|
return nil
|
|
}
|
|
|
|
// assignAddr Adds IP address to the tunnel interface
|
|
func (t *TunKernelDevice) assignAddr() error {
|
|
return t.link.assignAddr(&t.address)
|
|
}
|
|
|
|
func (t *TunKernelDevice) GetNet() *netstack.Net {
|
|
return nil
|
|
}
|
|
|
|
// GetICEBind returns nil for kernel mode devices
|
|
func (t *TunKernelDevice) GetICEBind() EndpointManager {
|
|
return nil
|
|
}
|