gluetun/internal/routing/outbound.go
2024-10-11 19:27:29 +00:00

112 lines
2.8 KiB
Go

package routing
import (
"fmt"
"net/netip"
"github.com/qdm12/gluetun/internal/netlink"
"github.com/qdm12/gluetun/internal/subnet"
)
const (
outboundTable = 199
outboundPriority = 99
)
func (r *Routing) SetOutboundRoutes(outboundSubnets []netip.Prefix) error {
defaultRoutes, err := r.DefaultRoutes()
if err != nil {
return err
}
return r.setOutboundRoutes(outboundSubnets, defaultRoutes)
}
func (r *Routing) setOutboundRoutes(outboundSubnets []netip.Prefix,
defaultRoutes []DefaultRoute,
) (err error) {
r.stateMutex.Lock()
defer r.stateMutex.Unlock()
subnetsToAdd, subnetsToRemove := subnet.FindSubnetsToChange(
r.outboundSubnets, outboundSubnets)
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
return nil
}
warnings := r.removeOutboundSubnets(subnetsToRemove, defaultRoutes)
for _, warning := range warnings {
r.logger.Warn("cannot remove outdated outbound subnet from routing: " + warning)
}
err = r.addOutboundSubnets(subnetsToAdd, defaultRoutes)
if err != nil {
return fmt.Errorf("adding outbound subnet to routes: %w", err)
}
return nil
}
func (r *Routing) removeOutboundSubnets(subnets []netip.Prefix,
defaultRoutes []DefaultRoute,
) (warnings []string) {
for i, subNet := range subnets {
for _, defaultRoute := range defaultRoutes {
err := r.deleteRouteVia(subNet, defaultRoute.Gateway, defaultRoute.NetInterface, outboundTable)
if err != nil {
warnings = append(warnings, err.Error())
continue
}
}
ruleSrcNet := netip.Prefix{}
ruleDstNet := subnets[i]
err := r.deleteIPRule(ruleSrcNet, ruleDstNet, outboundTable, outboundPriority)
if err != nil {
warnings = append(warnings,
"cannot delete rule: for subnet "+subNet.String()+": "+err.Error())
continue
}
r.outboundSubnets = subnet.RemoveSubnetFromSubnets(r.outboundSubnets, subNet)
}
return warnings
}
func (r *Routing) addOutboundSubnets(subnets []netip.Prefix,
defaultRoutes []DefaultRoute,
) (err error) {
for i, subnet := range subnets {
subnetIsIPv6 := subnet.Addr().Is6()
subnetRouteAdded := false
for _, defaultRoute := range defaultRoutes {
defaultRouteIsIPv6 := defaultRoute.Family == netlink.FamilyV6
ipFamilyMatch := subnetIsIPv6 == defaultRouteIsIPv6
if !ipFamilyMatch {
continue
}
err = r.addRouteVia(subnet, defaultRoute.Gateway, defaultRoute.NetInterface, outboundTable)
if err != nil {
return fmt.Errorf("adding route for subnet %s: %w", subnet, err)
}
subnetRouteAdded = true
}
if !subnetRouteAdded {
continue
}
ruleSrcNet := netip.Prefix{}
ruleDstNet := subnets[i]
err = r.addIPRule(ruleSrcNet, ruleDstNet, outboundTable, outboundPriority)
if err != nil {
return fmt.Errorf("adding rule: for subnet %s: %w", subnet, err)
}
r.outboundSubnets = append(r.outboundSubnets, subnet)
}
return nil
}