2023-08-04 13:34:18 +02:00
|
|
|
package route
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2025-03-02 08:51:13 +01:00
|
|
|
"log/slog"
|
2024-10-15 15:44:47 +02:00
|
|
|
|
2025-02-28 08:29:40 +01:00
|
|
|
"github.com/h44z/wg-portal/internal/app"
|
|
|
|
"github.com/h44z/wg-portal/internal/config"
|
|
|
|
"github.com/h44z/wg-portal/internal/domain"
|
2023-08-04 13:34:18 +02:00
|
|
|
)
|
|
|
|
|
2025-03-23 23:09:47 +01:00
|
|
|
// region dependencies
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
type ControllerManager interface {
|
|
|
|
// GetController returns the controller for the given interface.
|
|
|
|
GetController(iface domain.Interface) domain.InterfaceController
|
|
|
|
}
|
|
|
|
|
2025-03-23 23:09:47 +01:00
|
|
|
type InterfaceAndPeerDatabaseRepo interface {
|
2025-10-06 22:17:39 +02:00
|
|
|
// GetInterface returns the interface with the given identifier.
|
|
|
|
GetInterface(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, error)
|
2025-03-23 23:09:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type EventBus interface {
|
|
|
|
// Subscribe subscribes to a topic
|
|
|
|
Subscribe(topic string, fn interface{}) error
|
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
type RoutesController interface {
|
|
|
|
// SetRoutes sets the routes for the given interface. If no routes are provided, the function is a no-op.
|
|
|
|
SetRoutes(
|
|
|
|
ctx context.Context,
|
|
|
|
interfaceId domain.InterfaceIdentifier,
|
|
|
|
table int,
|
|
|
|
fwMark uint32,
|
|
|
|
cidrs []domain.Cidr,
|
|
|
|
) error
|
|
|
|
// RemoveRoutes removes the routes for the given interface. If no routes are provided, the function is a no-op.
|
|
|
|
RemoveRoutes(
|
|
|
|
ctx context.Context,
|
|
|
|
interfaceId domain.InterfaceIdentifier,
|
|
|
|
table int,
|
|
|
|
fwMark uint32,
|
|
|
|
oldCidrs []domain.Cidr,
|
|
|
|
) error
|
|
|
|
}
|
|
|
|
|
2025-03-23 23:09:47 +01:00
|
|
|
// endregion dependencies
|
|
|
|
|
2023-08-04 13:34:18 +02:00
|
|
|
type routeRuleInfo struct {
|
|
|
|
ifaceId domain.InterfaceIdentifier
|
2024-10-15 15:44:47 +02:00
|
|
|
fwMark uint32
|
2023-08-04 13:34:18 +02:00
|
|
|
table int
|
|
|
|
family int
|
|
|
|
hasDefault bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Manager is try to mimic wg-quick behaviour (https://git.zx2c4.com/wireguard-tools/tree/src/wg-quick/linux.bash)
|
|
|
|
// for default routes.
|
|
|
|
type Manager struct {
|
|
|
|
cfg *config.Config
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
bus EventBus
|
|
|
|
db InterfaceAndPeerDatabaseRepo
|
|
|
|
wgController ControllerManager
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
2025-03-23 23:09:47 +01:00
|
|
|
// NewRouteManager creates a new route manager instance.
|
2025-10-06 22:17:39 +02:00
|
|
|
func NewRouteManager(
|
|
|
|
cfg *config.Config,
|
|
|
|
bus EventBus,
|
|
|
|
db InterfaceAndPeerDatabaseRepo,
|
|
|
|
wgController ControllerManager,
|
|
|
|
) (*Manager, error) {
|
2023-08-04 13:34:18 +02:00
|
|
|
m := &Manager{
|
|
|
|
cfg: cfg,
|
|
|
|
bus: bus,
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
db: db,
|
|
|
|
wgController: wgController,
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m.connectToMessageBus()
|
|
|
|
|
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m Manager) connectToMessageBus() {
|
|
|
|
_ = m.bus.Subscribe(app.TopicRouteUpdate, m.handleRouteUpdateEvent)
|
|
|
|
_ = m.bus.Subscribe(app.TopicRouteRemove, m.handleRouteRemoveEvent)
|
|
|
|
}
|
|
|
|
|
2025-03-23 23:09:47 +01:00
|
|
|
// StartBackgroundJobs starts background jobs for the route manager.
|
|
|
|
// This method is non-blocking and returns immediately.
|
2025-02-28 16:11:55 +01:00
|
|
|
func (m Manager) StartBackgroundJobs(_ context.Context) {
|
2025-03-23 23:09:47 +01:00
|
|
|
// this is a no-op for now
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
func (m Manager) handleRouteUpdateEvent(info domain.RoutingTableInfo) {
|
|
|
|
slog.Debug("handling route update event", "info", info.String())
|
|
|
|
|
|
|
|
if !info.ManagementEnabled() {
|
|
|
|
return // route management disabled
|
|
|
|
}
|
2023-08-04 13:34:18 +02:00
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
err := m.syncRoutes(context.Background(), info)
|
2023-08-04 13:34:18 +02:00
|
|
|
if err != nil {
|
2025-03-02 08:51:13 +01:00
|
|
|
slog.Error("failed to synchronize routes",
|
2025-10-06 22:17:39 +02:00
|
|
|
"info", info.String(), "error", err)
|
|
|
|
return
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
slog.Debug("routes synchronized", "info", info.String())
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m Manager) handleRouteRemoveEvent(info domain.RoutingTableInfo) {
|
2025-03-02 08:51:13 +01:00
|
|
|
slog.Debug("handling route remove event", "info", info.String())
|
2023-08-04 13:34:18 +02:00
|
|
|
|
|
|
|
if !info.ManagementEnabled() {
|
|
|
|
return // route management disabled
|
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
err := m.removeRoutes(context.Background(), info)
|
2023-08-04 13:34:18 +02:00
|
|
|
if err != nil {
|
2025-10-06 22:17:39 +02:00
|
|
|
slog.Error("failed to synchronize routes",
|
|
|
|
"info", info.String(), "error", err)
|
|
|
|
return
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
slog.Debug("routes removed", "info", info.String())
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
func (m Manager) syncRoutes(ctx context.Context, info domain.RoutingTableInfo) error {
|
|
|
|
rc, ok := m.wgController.GetController(info.Interface).(RoutesController)
|
|
|
|
if !ok {
|
|
|
|
slog.Warn("no capable routes-controller found for interface", "interface", info.Interface.Identifier)
|
2023-08-04 13:34:18 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
err := rc.SetRoutes(ctx, info.Interface.Identifier, info.Table, info.FwMark, info.AllowedIps)
|
2023-08-04 13:34:18 +02:00
|
|
|
if err != nil {
|
2025-10-06 22:17:39 +02:00
|
|
|
return fmt.Errorf("failed to set routes for interface %s: %w", info.Interface.Identifier, err)
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
func (m Manager) removeRoutes(ctx context.Context, info domain.RoutingTableInfo) error {
|
|
|
|
rc, ok := m.wgController.GetController(info.Interface).(RoutesController)
|
|
|
|
if !ok {
|
|
|
|
slog.Warn("no capable routes-controller found for interface", "interface", info.Interface.Identifier)
|
|
|
|
return nil
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
|
2025-10-06 22:17:39 +02:00
|
|
|
err := rc.RemoveRoutes(ctx, info.Interface.Identifier, info.Table, info.FwMark, info.AllowedIps)
|
2023-08-04 13:34:18 +02:00
|
|
|
if err != nil {
|
2025-10-06 22:17:39 +02:00
|
|
|
return fmt.Errorf("failed to remove routes for interface %s: %w", info.Interface.Identifier, err)
|
2023-08-04 13:34:18 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|