mirror of https://github.com/h44z/wg-portal.git
cleanup route handling for local backend
This commit is contained in:
parent
c00b34ac31
commit
e8965bb65e
|
|
@ -658,7 +658,12 @@ func (c LocalController) SetRoutes(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get or create routing table and fwmark for %s: %w", interfaceId, err)
|
return fmt.Errorf("failed to get or create routing table and fwmark for %s: %w", interfaceId, err)
|
||||||
}
|
}
|
||||||
if realFwMark != fwMark {
|
wgDev, err := c.wg.Device(string(interfaceId))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get wg device for %s: %w", interfaceId, err)
|
||||||
|
}
|
||||||
|
currentFwMark := wgDev.FirewallMark
|
||||||
|
if int(realFwMark) != currentFwMark {
|
||||||
slog.Debug("updating fwmark for interface", "interface", interfaceId, "oldFwMark", fwMark,
|
slog.Debug("updating fwmark for interface", "interface", interfaceId, "oldFwMark", fwMark,
|
||||||
"newFwMark", realFwMark, "oldTable", table, "newTable", realTable)
|
"newFwMark", realFwMark, "oldTable", table, "newTable", realTable)
|
||||||
if err := c.updateFwMarkOnInterface(interfaceId, int(realFwMark)); err != nil {
|
if err := c.updateFwMarkOnInterface(interfaceId, int(realFwMark)); err != nil {
|
||||||
|
|
@ -878,9 +883,26 @@ func (c LocalController) RemoveRoutes(
|
||||||
) error {
|
) error {
|
||||||
slog.Debug("removing linux routes", "interface", interfaceId, "table", table, "fwMark", fwMark, "cidrs", oldCidrs)
|
slog.Debug("removing linux routes", "interface", interfaceId, "table", table, "fwMark", fwMark, "cidrs", oldCidrs)
|
||||||
|
|
||||||
|
wgDev, err := c.wg.Device(string(interfaceId))
|
||||||
|
if err != nil {
|
||||||
|
slog.Debug("wg device already removed, route cleanup might be incomplete", "interface", interfaceId)
|
||||||
|
wgDev = nil
|
||||||
|
}
|
||||||
link, err := c.nl.LinkByName(string(interfaceId))
|
link, err := c.nl.LinkByName(string(interfaceId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find physical link for %s: %w", interfaceId, err)
|
slog.Debug("physical link already removed, route cleanup might be incomplete", "interface", interfaceId)
|
||||||
|
link = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if wgDev != nil && fwMark == 0 {
|
||||||
|
fwMark = uint32(wgDev.FirewallMark)
|
||||||
|
}
|
||||||
|
if wgDev != nil && table == 0 {
|
||||||
|
table = wgDev.FirewallMark // use the fwMark as table, this is the default behavior
|
||||||
|
}
|
||||||
|
linkIndex := -1
|
||||||
|
if link != nil {
|
||||||
|
linkIndex = link.Attrs().Index
|
||||||
}
|
}
|
||||||
|
|
||||||
cidrsV4, cidrsV6 := domain.CidrsPerFamily(oldCidrs)
|
cidrsV4, cidrsV6 := domain.CidrsPerFamily(oldCidrs)
|
||||||
|
|
@ -889,6 +911,7 @@ func (c LocalController) RemoveRoutes(
|
||||||
return fmt.Errorf("failed to get or create routing table and fwmark for %s: %w", interfaceId, err)
|
return fmt.Errorf("failed to get or create routing table and fwmark for %s: %w", interfaceId, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if linkIndex > 0 {
|
||||||
err = c.removeRoutesForFamily(interfaceId, link, netlink.FAMILY_V4, realTable, realFwMark, cidrsV4)
|
err = c.removeRoutesForFamily(interfaceId, link, netlink.FAMILY_V4, realTable, realFwMark, cidrsV4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to remove v4 routes: %w", err)
|
return fmt.Errorf("failed to remove v4 routes: %w", err)
|
||||||
|
|
@ -897,6 +920,18 @@ func (c LocalController) RemoveRoutes(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to remove v6 routes: %w", err)
|
return fmt.Errorf("failed to remove v6 routes: %w", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if table > 0 {
|
||||||
|
err = c.removeRouteRulesForTable(netlink.FAMILY_V4, realTable)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to remove v4 route rules for %s: %w", interfaceId, err)
|
||||||
|
}
|
||||||
|
err = c.removeRouteRulesForTable(netlink.FAMILY_V6, realTable)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to remove v6 route rules for %s: %w", interfaceId, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -958,6 +993,25 @@ func (c LocalController) removeRoutesForFamily(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c LocalController) removeRouteRulesForTable(
|
||||||
|
family int,
|
||||||
|
table int,
|
||||||
|
) error {
|
||||||
|
existingRules, err := c.nl.RuleList(family)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get existing route rules for family-id %d: %w", family, err)
|
||||||
|
}
|
||||||
|
for _, existingRule := range existingRules {
|
||||||
|
if existingRule.Table == table {
|
||||||
|
err := c.nl.RuleDel(&existingRule)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete old rule for table %d and family-id %d: %w", table, family, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// endregion routing-related
|
// endregion routing-related
|
||||||
|
|
||||||
// region statistics-related
|
// region statistics-related
|
||||||
|
|
|
||||||
|
|
@ -462,12 +462,17 @@ func (m Manager) DeleteInterface(ctx context.Context, id domain.InterfaceIdentif
|
||||||
return fmt.Errorf("deletion not allowed: %w", err)
|
return fmt.Errorf("deletion not allowed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.bus.Publish(app.TopicRouteRemove, domain.RoutingTableInfo{
|
||||||
|
Interface: *existingInterface,
|
||||||
|
AllowedIps: existingInterface.GetAllowedIPs(existingPeers),
|
||||||
|
FwMark: existingInterface.FirewallMark,
|
||||||
|
Table: existingInterface.GetRoutingTable(),
|
||||||
|
})
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
existingInterface.Disabled = &now // simulate a disabled interface
|
existingInterface.Disabled = &now // simulate a disabled interface
|
||||||
existingInterface.DisabledReason = domain.DisabledReasonDeleted
|
existingInterface.DisabledReason = domain.DisabledReasonDeleted
|
||||||
|
|
||||||
physicalInterface, _ := m.wg.GetController(*existingInterface).GetInterface(ctx, id)
|
|
||||||
|
|
||||||
if err := m.handleInterfacePreSaveHooks(ctx, existingInterface, !existingInterface.IsDisabled(),
|
if err := m.handleInterfacePreSaveHooks(ctx, existingInterface, !existingInterface.IsDisabled(),
|
||||||
false); err != nil {
|
false); err != nil {
|
||||||
return fmt.Errorf("pre-delete hooks failed: %w", err)
|
return fmt.Errorf("pre-delete hooks failed: %w", err)
|
||||||
|
|
@ -489,17 +494,6 @@ func (m Manager) DeleteInterface(ctx context.Context, id domain.InterfaceIdentif
|
||||||
return fmt.Errorf("deletion failure: %w", err)
|
return fmt.Errorf("deletion failure: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fwMark := existingInterface.FirewallMark
|
|
||||||
if physicalInterface != nil && fwMark == 0 {
|
|
||||||
fwMark = physicalInterface.FirewallMark
|
|
||||||
}
|
|
||||||
m.bus.Publish(app.TopicRouteRemove, domain.RoutingTableInfo{
|
|
||||||
Interface: *existingInterface,
|
|
||||||
AllowedIps: existingInterface.GetAllowedIPs(existingPeers),
|
|
||||||
FwMark: fwMark,
|
|
||||||
Table: existingInterface.GetRoutingTable(),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := m.handleInterfacePostSaveHooks(
|
if err := m.handleInterfacePostSaveHooks(
|
||||||
ctx,
|
ctx,
|
||||||
existingInterface,
|
existingInterface,
|
||||||
|
|
@ -577,15 +571,10 @@ func (m Manager) saveInterface(ctx context.Context, iface *domain.Interface) (
|
||||||
}
|
}
|
||||||
|
|
||||||
if iface.IsDisabled() {
|
if iface.IsDisabled() {
|
||||||
physicalInterface, _ := m.wg.GetController(*iface).GetInterface(ctx, iface.Identifier)
|
|
||||||
fwMark := iface.FirewallMark
|
|
||||||
if physicalInterface != nil && fwMark == 0 {
|
|
||||||
fwMark = physicalInterface.FirewallMark
|
|
||||||
}
|
|
||||||
m.bus.Publish(app.TopicRouteRemove, domain.RoutingTableInfo{
|
m.bus.Publish(app.TopicRouteRemove, domain.RoutingTableInfo{
|
||||||
Interface: *iface,
|
Interface: *iface,
|
||||||
AllowedIps: iface.GetAllowedIPs(peers),
|
AllowedIps: iface.GetAllowedIPs(peers),
|
||||||
FwMark: fwMark,
|
FwMark: iface.FirewallMark,
|
||||||
Table: iface.GetRoutingTable(),
|
Table: iface.GetRoutingTable(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -449,7 +449,6 @@ func (m Manager) GetUserPeerStats(ctx context.Context, id domain.UserIdentifier)
|
||||||
|
|
||||||
func (m Manager) savePeers(ctx context.Context, peers ...*domain.Peer) error {
|
func (m Manager) savePeers(ctx context.Context, peers ...*domain.Peer) error {
|
||||||
interfaces := make(map[domain.InterfaceIdentifier]domain.Interface)
|
interfaces := make(map[domain.InterfaceIdentifier]domain.Interface)
|
||||||
interfacePeers := make(map[domain.InterfaceIdentifier][]domain.Peer)
|
|
||||||
|
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
// get interface from db if it is not yet in the map
|
// get interface from db if it is not yet in the map
|
||||||
|
|
@ -462,7 +461,6 @@ func (m Manager) savePeers(ctx context.Context, peers ...*domain.Peer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
iface := interfaces[peer.InterfaceIdentifier]
|
iface := interfaces[peer.InterfaceIdentifier]
|
||||||
interfacePeers[iface.Identifier] = append(interfacePeers[iface.Identifier], *peer)
|
|
||||||
|
|
||||||
// Always save the peer to the backend, regardless of disabled/expired state
|
// Always save the peer to the backend, regardless of disabled/expired state
|
||||||
// The backend will handle the disabled state appropriately
|
// The backend will handle the disabled state appropriately
|
||||||
|
|
@ -497,9 +495,14 @@ func (m Manager) savePeers(ctx context.Context, peers ...*domain.Peer) error {
|
||||||
|
|
||||||
// Update routes after peers have changed
|
// Update routes after peers have changed
|
||||||
for id, iface := range interfaces {
|
for id, iface := range interfaces {
|
||||||
|
interfacePeers, err := m.db.GetInterfacePeers(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to re-load peers for interface %s: %w", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
m.bus.Publish(app.TopicRouteUpdate, domain.RoutingTableInfo{
|
m.bus.Publish(app.TopicRouteUpdate, domain.RoutingTableInfo{
|
||||||
Interface: iface,
|
Interface: iface,
|
||||||
AllowedIps: iface.GetAllowedIPs(interfacePeers[id]),
|
AllowedIps: iface.GetAllowedIPs(interfacePeers),
|
||||||
FwMark: iface.FirewallMark,
|
FwMark: iface.FirewallMark,
|
||||||
Table: iface.GetRoutingTable(),
|
Table: iface.GetRoutingTable(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -132,9 +132,14 @@ func (i *Interface) GetConfigFileName() string {
|
||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllowedIPs returns the allowed IPs for the interface depending on the interface type and peers.
|
||||||
|
// For example, if the interface type is Server, the allowed IPs are the IPs of the peers.
|
||||||
|
// If the interface type is Client, the allowed IPs correspond to the AllowedIPsStr of the peers.
|
||||||
func (i *Interface) GetAllowedIPs(peers []Peer) []Cidr {
|
func (i *Interface) GetAllowedIPs(peers []Peer) []Cidr {
|
||||||
var allowedCidrs []Cidr
|
var allowedCidrs []Cidr
|
||||||
|
|
||||||
|
switch i.Type {
|
||||||
|
case InterfaceTypeServer, InterfaceTypeAny:
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
for _, ip := range peer.Interface.Addresses {
|
for _, ip := range peer.Interface.Addresses {
|
||||||
allowedCidrs = append(allowedCidrs, ip.HostAddr())
|
allowedCidrs = append(allowedCidrs, ip.HostAddr())
|
||||||
|
|
@ -146,6 +151,14 @@ func (i *Interface) GetAllowedIPs(peers []Peer) []Cidr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case InterfaceTypeClient:
|
||||||
|
for _, peer := range peers {
|
||||||
|
allowedIPs, err := CidrsFromString(peer.AllowedIPsStr.GetValue())
|
||||||
|
if err == nil {
|
||||||
|
allowedCidrs = append(allowedCidrs, allowedIPs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return allowedCidrs
|
return allowedCidrs
|
||||||
}
|
}
|
||||||
|
|
@ -315,7 +328,8 @@ type RoutingTableInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r RoutingTableInfo) String() string {
|
func (r RoutingTableInfo) String() string {
|
||||||
return fmt.Sprintf("%s: %d -> %d", r.Interface.Identifier, r.FwMark, r.Table)
|
v4, v6 := CidrsPerFamily(r.AllowedIps)
|
||||||
|
return fmt.Sprintf("%s: fwmark=%d; table=%d; routes_4=%d; routes_6=%d", r.Interface.Identifier, r.FwMark, r.Table, len(v4), len(v6))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r RoutingTableInfo) ManagementEnabled() bool {
|
func (r RoutingTableInfo) ManagementEnabled() bool {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue