Files
cunicu/pkg/daemon.go
Steffen Vogel 3fca8e23dc watcher: do not run initial sync in background
Signed-off-by: Steffen Vogel <post@steffenvogel.de>
2022-09-06 03:34:08 +02:00

208 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package wice
import (
"errors"
"fmt"
"net/url"
"go.uber.org/zap"
"golang.zx2c4.com/wireguard/wgctrl"
"riasc.eu/wice/pkg/config"
"riasc.eu/wice/pkg/device"
"riasc.eu/wice/pkg/feat"
"riasc.eu/wice/pkg/feat/epdisc"
"riasc.eu/wice/pkg/util"
"riasc.eu/wice/pkg/watcher"
"riasc.eu/wice/pkg/wg"
"riasc.eu/wice/pkg/signaling"
)
type Daemon struct {
*watcher.Watcher
Features []feat.Feature
EndpointDiscovery *epdisc.EndpointDiscovery
// Shared
Backend *signaling.MultiBackend
Client *wgctrl.Client
Config *config.Config
devices []device.Device
stop chan any
reexecOnClose bool
logger *zap.Logger
}
func NewDaemon(cfg *config.Config) (*Daemon, error) {
var err error
// Check permissions
if !util.HasAdminPrivileges() {
return nil, errors.New("insufficient privileges. Please run ɯice as root user or with NET_ADMIN capabilities")
}
d := &Daemon{
Config: cfg,
devices: []device.Device{},
stop: make(chan any),
}
d.logger = zap.L().Named("daemon")
// Create WireGuard netlink socket
d.Client, err = wgctrl.New()
if err != nil {
return nil, fmt.Errorf("failed to create WireGuard client: %w", err)
}
// Create watcher
if d.Watcher, err = watcher.New(d.Client, cfg.WatchInterval, cfg.WireGuard.InterfaceFilter.Regexp.MatchString); err != nil {
return nil, fmt.Errorf("failed to initialize watcher: %w", err)
}
// Create backend
urls := []*url.URL{}
for _, u := range cfg.Backends {
urls = append(urls, &u.URL)
}
d.Backend, err = signaling.NewMultiBackend(urls, &signaling.BackendConfig{
OnReady: []signaling.BackendReadyHandler{},
})
if err != nil {
return nil, fmt.Errorf("failed to initialize signaling backend: %w", err)
}
// Check if WireGuard interface can be created by the kernel
if !cfg.WireGuard.Userspace {
cfg.WireGuard.Userspace = !wg.KernelModuleExists()
}
d.Features, d.EndpointDiscovery = feat.NewFeatures(d.Watcher, d.Config, d.Client, d.Backend)
for _, feat := range d.Features {
if err := feat.Start(); err != nil {
return nil, err
}
}
return d, nil
}
func (d *Daemon) Run() error {
if err := wg.CleanupUserSockets(); err != nil {
return fmt.Errorf("failed to cleanup stale userspace sockets: %w", err)
}
if err := d.CreateDevicesFromArgs(); err != nil {
return fmt.Errorf("failed to create devices: %w", err)
}
signals := util.SetupSignals(util.SigUpdate)
d.logger.Debug("Started initial synchronization")
if err := d.Watcher.Sync(); err != nil {
d.logger.Fatal("Initial synchronization failed", zap.Error(err))
}
d.logger.Debug("Finished initial synchronization")
go d.Watcher.Watch()
out:
for {
select {
case sig := <-signals:
d.logger.Debug("Received signal", zap.String("signal", sig.String()))
switch sig {
case util.SigUpdate:
if err := d.Sync(); err != nil {
d.logger.Error("Failed to synchronize interfaces", zap.Error(err))
}
default:
break out
}
case <-d.stop:
break out
}
}
return nil
}
func (d *Daemon) Stop() {
close(d.stop)
d.logger.Debug("Stopping daemon")
}
func (d *Daemon) Restart() {
d.reexecOnClose = true
close(d.stop)
d.logger.Debug("Restarting daemon")
}
func (d *Daemon) Close() error {
for _, dev := range d.devices {
if err := dev.Close(); err != nil {
return fmt.Errorf("failed to delete device: %w", err)
}
}
if err := d.Watcher.Close(); err != nil {
return fmt.Errorf("failed to close interface: %w", err)
}
for _, feat := range d.Features {
if err := feat.Close(); err != nil {
return err
}
}
if err := d.Client.Close(); err != nil {
return fmt.Errorf("failed to close WireGuard client: %w", err)
}
d.logger.Debug("Closed daemon")
if d.reexecOnClose {
return util.ReexecSelf()
}
return nil
}
func (d *Daemon) CreateDevicesFromArgs() error {
var devs wg.Devices
var err error
if devs, err = d.Client.Devices(); err != nil {
return fmt.Errorf("failed to get existing WireGuard devices: %w", err)
}
for _, devName := range d.Config.WireGuard.Interfaces {
if !d.Config.WireGuard.InterfaceFilter.MatchString(devName) {
return fmt.Errorf("device '%s' is not matched by WireGuard interface filter '%s'",
devName, d.Config.WireGuard.InterfaceFilter.String())
}
if wgdev := devs.GetByName(devName); wgdev != nil {
return fmt.Errorf("device '%s' already exists", devName)
}
dev, err := device.NewDevice(devName, d.Config.WireGuard.Userspace)
if err != nil {
return fmt.Errorf("failed to create WireGuard device: %w", err)
}
d.devices = append(d.devices, dev)
}
return nil
}