2023-05-21 12:37:59 -04:00
|
|
|
package db
|
2021-04-28 10:55:29 -04:00
|
|
|
|
|
|
|
import (
|
2022-11-25 10:29:45 -05:00
|
|
|
"errors"
|
2022-09-01 18:06:19 -04:00
|
|
|
"net/netip"
|
2022-11-23 13:50:30 -05:00
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
"github.com/juanfont/headscale/hscontrol/policy"
|
|
|
|
"github.com/juanfont/headscale/hscontrol/types"
|
2022-11-24 17:41:11 -05:00
|
|
|
"github.com/rs/zerolog/log"
|
2022-11-23 13:50:30 -05:00
|
|
|
"gorm.io/gorm"
|
2023-12-09 12:09:24 -05:00
|
|
|
"tailscale.com/types/key"
|
2021-04-28 10:55:29 -04:00
|
|
|
)
|
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
var ErrRouteIsNotAvailable = errors.New("route is not available")
|
2021-08-21 09:49:46 -04:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func GetRoutes(tx *gorm.DB) (types.Routes, error) {
|
2023-05-21 12:37:59 -04:00
|
|
|
var routes types.Routes
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.
|
2023-12-09 12:09:24 -05:00
|
|
|
Preload("Node").
|
|
|
|
Preload("Node.User").
|
|
|
|
Find(&routes).Error
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return routes, nil
|
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func getAdvertisedAndEnabledRoutes(tx *gorm.DB) (types.Routes, error) {
|
2023-12-09 12:09:24 -05:00
|
|
|
var routes types.Routes
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.
|
2023-12-09 12:09:24 -05:00
|
|
|
Preload("Node").
|
|
|
|
Preload("Node.User").
|
|
|
|
Where("advertised = ? AND enabled = ?", true, true).
|
|
|
|
Find(&routes).Error
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return routes, nil
|
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func getRoutesByPrefix(tx *gorm.DB, pref netip.Prefix) (types.Routes, error) {
|
2023-12-09 12:09:24 -05:00
|
|
|
var routes types.Routes
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.
|
2023-12-09 12:09:24 -05:00
|
|
|
Preload("Node").
|
|
|
|
Preload("Node.User").
|
|
|
|
Where("prefix = ?", types.IPPrefix(pref)).
|
|
|
|
Find(&routes).Error
|
2023-05-21 12:37:59 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2021-08-21 09:49:46 -04:00
|
|
|
}
|
2022-11-25 10:29:45 -05:00
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
return routes, nil
|
2021-08-21 09:49:46 -04:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func GetNodeAdvertisedRoutes(tx *gorm.DB, node *types.Node) (types.Routes, error) {
|
2023-05-21 12:37:59 -04:00
|
|
|
var routes types.Routes
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.
|
2023-09-24 07:42:05 -04:00
|
|
|
Preload("Node").
|
2023-12-09 12:09:24 -05:00
|
|
|
Preload("Node.User").
|
2023-09-24 07:42:05 -04:00
|
|
|
Where("node_id = ? AND advertised = true", node.ID).
|
2023-05-21 12:37:59 -04:00
|
|
|
Find(&routes).Error
|
2022-11-25 12:50:12 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return routes, nil
|
|
|
|
}
|
|
|
|
|
2023-09-24 07:42:05 -04:00
|
|
|
func (hsdb *HSDatabase) GetNodeRoutes(node *types.Node) (types.Routes, error) {
|
2024-02-08 11:28:19 -05:00
|
|
|
return Read(hsdb.DB, func(rx *gorm.DB) (types.Routes, error) {
|
|
|
|
return GetNodeRoutes(rx, node)
|
|
|
|
})
|
2023-07-17 07:35:05 -04:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func GetNodeRoutes(tx *gorm.DB, node *types.Node) (types.Routes, error) {
|
2023-05-21 12:37:59 -04:00
|
|
|
var routes types.Routes
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.
|
2023-09-24 07:42:05 -04:00
|
|
|
Preload("Node").
|
2023-12-09 12:09:24 -05:00
|
|
|
Preload("Node.User").
|
2023-09-24 07:42:05 -04:00
|
|
|
Where("node_id = ?", node.ID).
|
2022-11-25 12:50:12 -05:00
|
|
|
Find(&routes).Error
|
|
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return routes, nil
|
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func GetRoute(tx *gorm.DB, id uint64) (*types.Route, error) {
|
2023-05-21 12:37:59 -04:00
|
|
|
var route types.Route
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.
|
2023-12-09 12:09:24 -05:00
|
|
|
Preload("Node").
|
|
|
|
Preload("Node.User").
|
|
|
|
First(&route, id).Error
|
2022-11-25 12:50:12 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &route, nil
|
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func EnableRoute(tx *gorm.DB, id uint64) (*types.StateUpdate, error) {
|
|
|
|
route, err := GetRoute(tx, id)
|
2022-11-25 12:50:12 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|
|
|
|
|
2023-01-22 17:39:42 -05:00
|
|
|
// Tailscale requires both IPv4 and IPv6 exit routes to
|
|
|
|
// be enabled at the same time, as per
|
|
|
|
// https://github.com/juanfont/headscale/issues/804#issuecomment-1399314002
|
2023-05-21 12:37:59 -04:00
|
|
|
if route.IsExitRoute() {
|
2024-02-08 11:28:19 -05:00
|
|
|
return enableRoutes(
|
|
|
|
tx,
|
2023-09-24 07:42:05 -04:00
|
|
|
&route.Node,
|
2023-05-21 12:37:59 -04:00
|
|
|
types.ExitRouteV4.String(),
|
|
|
|
types.ExitRouteV6.String(),
|
|
|
|
)
|
2023-01-22 17:39:42 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return enableRoutes(tx, &route.Node, netip.Prefix(route.Prefix).String())
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func DisableRoute(tx *gorm.DB,
|
|
|
|
id uint64,
|
|
|
|
isConnected map[key.MachinePublic]bool,
|
|
|
|
) (*types.StateUpdate, error) {
|
|
|
|
route, err := GetRoute(tx, id)
|
2022-11-25 12:50:12 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
var routes types.Routes
|
|
|
|
node := route.Node
|
|
|
|
|
2023-05-07 06:17:16 -04:00
|
|
|
// Tailscale requires both IPv4 and IPv6 exit routes to
|
|
|
|
// be enabled at the same time, as per
|
|
|
|
// https://github.com/juanfont/headscale/issues/804#issuecomment-1399314002
|
2024-02-08 11:28:19 -05:00
|
|
|
var update *types.StateUpdate
|
2023-05-21 12:37:59 -04:00
|
|
|
if !route.IsExitRoute() {
|
2024-02-08 11:28:19 -05:00
|
|
|
update, err = failoverRouteReturnUpdate(tx, isConnected, route)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
|
|
|
|
2023-05-07 06:17:16 -04:00
|
|
|
route.Enabled = false
|
|
|
|
route.IsPrimary = false
|
2024-02-08 11:28:19 -05:00
|
|
|
err = tx.Save(route).Error
|
2023-05-07 06:17:16 -04:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-05-07 06:17:16 -04:00
|
|
|
}
|
2023-12-09 12:09:24 -05:00
|
|
|
} else {
|
2024-02-08 11:28:19 -05:00
|
|
|
routes, err = GetNodeRoutes(tx, &node)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
2023-05-07 06:17:16 -04:00
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
for i := range routes {
|
|
|
|
if routes[i].IsExitRoute() {
|
|
|
|
routes[i].Enabled = false
|
|
|
|
routes[i].IsPrimary = false
|
2024-02-08 11:28:19 -05:00
|
|
|
err = tx.Save(&routes[i]).Error
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-07 06:17:16 -04:00
|
|
|
}
|
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
if routes == nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
routes, err = GetNodeRoutes(tx, &node)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
node.Routes = routes
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
// If update is empty, it means that one was not created
|
|
|
|
// by failover (as a failover was not necessary), create
|
|
|
|
// one and return to the caller.
|
|
|
|
if update == nil {
|
|
|
|
update = &types.StateUpdate{
|
|
|
|
Type: types.StatePeerChanged,
|
|
|
|
ChangeNodes: types.Nodes{
|
|
|
|
&node,
|
|
|
|
},
|
|
|
|
Message: "called from db.DisableRoute",
|
|
|
|
}
|
2023-05-07 06:17:16 -04:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return update, nil
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func (hsdb *HSDatabase) DeleteRoute(
|
|
|
|
id uint64,
|
|
|
|
isConnected map[key.MachinePublic]bool,
|
|
|
|
) (*types.StateUpdate, error) {
|
|
|
|
return Write(hsdb.DB, func(tx *gorm.DB) (*types.StateUpdate, error) {
|
|
|
|
return DeleteRoute(tx, id, isConnected)
|
|
|
|
})
|
|
|
|
}
|
2023-07-17 07:35:05 -04:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func DeleteRoute(
|
|
|
|
tx *gorm.DB,
|
|
|
|
id uint64,
|
|
|
|
isConnected map[key.MachinePublic]bool,
|
|
|
|
) (*types.StateUpdate, error) {
|
|
|
|
route, err := GetRoute(tx, id)
|
2023-03-06 03:05:40 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-03-06 03:05:40 -05:00
|
|
|
}
|
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
var routes types.Routes
|
|
|
|
node := route.Node
|
|
|
|
|
2023-05-07 06:17:16 -04:00
|
|
|
// Tailscale requires both IPv4 and IPv6 exit routes to
|
|
|
|
// be enabled at the same time, as per
|
|
|
|
// https://github.com/juanfont/headscale/issues/804#issuecomment-1399314002
|
2024-02-08 11:28:19 -05:00
|
|
|
var update *types.StateUpdate
|
2023-05-21 12:37:59 -04:00
|
|
|
if !route.IsExitRoute() {
|
2024-02-08 11:28:19 -05:00
|
|
|
update, err = failoverRouteReturnUpdate(tx, isConnected, route)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, nil
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
if err := tx.Unscoped().Delete(&route).Error; err != nil {
|
|
|
|
return nil, err
|
2023-05-07 06:17:16 -04:00
|
|
|
}
|
2023-12-09 12:09:24 -05:00
|
|
|
} else {
|
2024-02-08 11:28:19 -05:00
|
|
|
routes, err := GetNodeRoutes(tx, &node)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
2023-05-07 06:17:16 -04:00
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
routesToDelete := types.Routes{}
|
|
|
|
for _, r := range routes {
|
|
|
|
if r.IsExitRoute() {
|
|
|
|
routesToDelete = append(routesToDelete, r)
|
|
|
|
}
|
|
|
|
}
|
2023-05-07 06:17:16 -04:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
if err := tx.Unscoped().Delete(&routesToDelete).Error; err != nil {
|
|
|
|
return nil, err
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
2023-05-07 06:17:16 -04:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
// If update is empty, it means that one was not created
|
|
|
|
// by failover (as a failover was not necessary), create
|
|
|
|
// one and return to the caller.
|
2023-12-09 12:09:24 -05:00
|
|
|
if routes == nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
routes, err = GetNodeRoutes(tx, &node)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-05-07 06:17:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
node.Routes = routes
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
if update == nil {
|
|
|
|
update = &types.StateUpdate{
|
|
|
|
Type: types.StatePeerChanged,
|
|
|
|
ChangeNodes: types.Nodes{
|
|
|
|
&node,
|
|
|
|
},
|
|
|
|
Message: "called from db.DeleteRoute",
|
|
|
|
}
|
2023-03-06 03:05:40 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return update, nil
|
2023-03-06 03:05:40 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func deleteNodeRoutes(tx *gorm.DB, node *types.Node, isConnected map[key.MachinePublic]bool) error {
|
|
|
|
routes, err := GetNodeRoutes(tx, node)
|
2023-03-06 03:05:40 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range routes {
|
2024-02-08 11:28:19 -05:00
|
|
|
if err := tx.Unscoped().Delete(&routes[i]).Error; err != nil {
|
2023-03-06 03:05:40 -05:00
|
|
|
return err
|
|
|
|
}
|
2023-12-09 12:09:24 -05:00
|
|
|
|
|
|
|
// TODO(kradalby): This is a bit too aggressive, we could probably
|
|
|
|
// figure out which routes needs to be failed over rather than all.
|
2024-02-08 11:28:19 -05:00
|
|
|
failoverRouteReturnUpdate(tx, isConnected, &routes[i])
|
2023-03-06 03:05:40 -05:00
|
|
|
}
|
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
return nil
|
2023-03-06 03:05:40 -05:00
|
|
|
}
|
|
|
|
|
2023-09-24 07:42:05 -04:00
|
|
|
// isUniquePrefix returns if there is another node providing the same route already.
|
2024-02-08 11:28:19 -05:00
|
|
|
func isUniquePrefix(tx *gorm.DB, route types.Route) bool {
|
2022-11-24 11:00:40 -05:00
|
|
|
var count int64
|
2024-02-08 11:28:19 -05:00
|
|
|
tx.Model(&types.Route{}).
|
2023-09-24 07:42:05 -04:00
|
|
|
Where("prefix = ? AND node_id != ? AND advertised = ? AND enabled = ?",
|
2022-11-24 11:00:40 -05:00
|
|
|
route.Prefix,
|
2023-09-24 07:42:05 -04:00
|
|
|
route.NodeID,
|
2022-11-24 11:00:40 -05:00
|
|
|
true, true).Count(&count)
|
2022-11-25 10:29:45 -05:00
|
|
|
|
2022-11-24 11:00:40 -05:00
|
|
|
return count == 0
|
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func getPrimaryRoute(tx *gorm.DB, prefix netip.Prefix) (*types.Route, error) {
|
2023-05-21 12:37:59 -04:00
|
|
|
var route types.Route
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.
|
2023-09-24 07:42:05 -04:00
|
|
|
Preload("Node").
|
2023-05-21 12:37:59 -04:00
|
|
|
Where("prefix = ? AND advertised = ? AND enabled = ? AND is_primary = ?", types.IPPrefix(prefix), true, true, true).
|
2022-11-24 17:41:11 -05:00
|
|
|
First(&route).Error
|
2022-11-25 10:29:45 -05:00
|
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
2022-11-24 17:41:11 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-11-25 10:29:45 -05:00
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
2022-11-24 17:41:11 -05:00
|
|
|
return nil, gorm.ErrRecordNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
return &route, nil
|
|
|
|
}
|
|
|
|
|
2023-09-24 07:42:05 -04:00
|
|
|
func (hsdb *HSDatabase) GetNodePrimaryRoutes(node *types.Node) (types.Routes, error) {
|
2024-02-08 11:28:19 -05:00
|
|
|
return Read(hsdb.DB, func(rx *gorm.DB) (types.Routes, error) {
|
|
|
|
return GetNodePrimaryRoutes(rx, node)
|
|
|
|
})
|
|
|
|
}
|
2023-07-17 07:35:05 -04:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
// getNodePrimaryRoutes returns the routes that are enabled and marked as primary (for subnet failover)
|
|
|
|
// Exit nodes are not considered for this, as they are never marked as Primary.
|
|
|
|
func GetNodePrimaryRoutes(tx *gorm.DB, node *types.Node) (types.Routes, error) {
|
2023-05-21 12:37:59 -04:00
|
|
|
var routes types.Routes
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.
|
2023-09-24 07:42:05 -04:00
|
|
|
Preload("Node").
|
|
|
|
Where("node_id = ? AND advertised = ? AND enabled = ? AND is_primary = ?", node.ID, true, true, true).
|
2022-11-23 13:50:30 -05:00
|
|
|
Find(&routes).Error
|
2021-08-21 09:49:46 -04:00
|
|
|
if err != nil {
|
2022-11-23 13:50:30 -05:00
|
|
|
return nil, err
|
2021-08-21 09:49:46 -04:00
|
|
|
}
|
|
|
|
|
2022-11-23 13:50:30 -05:00
|
|
|
return routes, nil
|
|
|
|
}
|
2021-08-21 09:49:46 -04:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func (hsdb *HSDatabase) SaveNodeRoutes(node *types.Node) (bool, error) {
|
|
|
|
return Write(hsdb.DB, func(tx *gorm.DB) (bool, error) {
|
|
|
|
return SaveNodeRoutes(tx, node)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-24 07:42:05 -04:00
|
|
|
// SaveNodeRoutes takes a node and updates the database with
|
2023-08-09 16:20:05 -04:00
|
|
|
// the new routes.
|
2024-01-16 10:04:03 -05:00
|
|
|
// It returns a bool whether an update should be sent as the
|
2023-12-09 12:09:24 -05:00
|
|
|
// saved route impacts nodes.
|
2024-02-08 11:28:19 -05:00
|
|
|
func SaveNodeRoutes(tx *gorm.DB, node *types.Node) (bool, error) {
|
2023-12-09 12:09:24 -05:00
|
|
|
sendUpdate := false
|
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
currentRoutes := types.Routes{}
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.Where("node_id = ?", node.ID).Find(¤tRoutes).Error
|
2021-08-21 09:49:46 -04:00
|
|
|
if err != nil {
|
2023-12-09 12:09:24 -05:00
|
|
|
return sendUpdate, err
|
2021-08-21 09:49:46 -04:00
|
|
|
}
|
2021-04-28 10:55:29 -04:00
|
|
|
|
2022-11-23 13:50:30 -05:00
|
|
|
advertisedRoutes := map[netip.Prefix]bool{}
|
2023-11-21 12:20:06 -05:00
|
|
|
for _, prefix := range node.Hostinfo.RoutableIPs {
|
2022-11-23 13:50:30 -05:00
|
|
|
advertisedRoutes[prefix] = false
|
2021-08-21 09:49:46 -04:00
|
|
|
}
|
|
|
|
|
2023-09-11 12:45:46 -04:00
|
|
|
log.Trace().
|
2023-09-24 07:42:05 -04:00
|
|
|
Str("node", node.Hostname).
|
2023-09-11 12:45:46 -04:00
|
|
|
Interface("advertisedRoutes", advertisedRoutes).
|
|
|
|
Interface("currentRoutes", currentRoutes).
|
|
|
|
Msg("updating routes")
|
|
|
|
|
2022-11-25 10:29:45 -05:00
|
|
|
for pos, route := range currentRoutes {
|
2022-11-23 13:50:30 -05:00
|
|
|
if _, ok := advertisedRoutes[netip.Prefix(route.Prefix)]; ok {
|
|
|
|
if !route.Advertised {
|
2022-11-25 10:29:45 -05:00
|
|
|
currentRoutes[pos].Advertised = true
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.Save(¤tRoutes[pos]).Error
|
2022-11-23 13:50:30 -05:00
|
|
|
if err != nil {
|
2023-12-09 12:09:24 -05:00
|
|
|
return sendUpdate, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a route that is newly "saved" is already
|
|
|
|
// enabled, set sendUpdate to true as it is now
|
|
|
|
// available.
|
|
|
|
if route.Enabled {
|
|
|
|
sendUpdate = true
|
2022-11-23 13:50:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
advertisedRoutes[netip.Prefix(route.Prefix)] = true
|
2022-11-25 10:29:45 -05:00
|
|
|
} else if route.Advertised {
|
|
|
|
currentRoutes[pos].Advertised = false
|
|
|
|
currentRoutes[pos].Enabled = false
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.Save(¤tRoutes[pos]).Error
|
2022-11-25 10:29:45 -05:00
|
|
|
if err != nil {
|
2023-12-09 12:09:24 -05:00
|
|
|
return sendUpdate, err
|
2021-04-28 10:55:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-21 09:49:46 -04:00
|
|
|
|
2022-11-23 13:50:30 -05:00
|
|
|
for prefix, exists := range advertisedRoutes {
|
|
|
|
if !exists {
|
2023-05-21 12:37:59 -04:00
|
|
|
route := types.Route{
|
2023-09-24 07:42:05 -04:00
|
|
|
NodeID: node.ID,
|
2023-05-21 12:37:59 -04:00
|
|
|
Prefix: types.IPPrefix(prefix),
|
2022-11-23 13:50:30 -05:00
|
|
|
Advertised: true,
|
|
|
|
Enabled: false,
|
|
|
|
}
|
2024-02-08 11:28:19 -05:00
|
|
|
err := tx.Create(&route).Error
|
2022-11-23 13:50:30 -05:00
|
|
|
if err != nil {
|
2023-12-09 12:09:24 -05:00
|
|
|
return sendUpdate, err
|
2022-11-23 13:50:30 -05:00
|
|
|
}
|
|
|
|
}
|
2022-05-30 09:31:06 -04:00
|
|
|
}
|
2021-08-21 09:49:46 -04:00
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
return sendUpdate, nil
|
2021-08-21 09:49:46 -04:00
|
|
|
}
|
2022-11-24 17:41:11 -05:00
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
// EnsureFailoverRouteIsAvailable takes a node and checks if the node's route
|
|
|
|
// currently have a functioning host that exposes the network.
|
2024-02-08 11:28:19 -05:00
|
|
|
func EnsureFailoverRouteIsAvailable(
|
|
|
|
tx *gorm.DB,
|
|
|
|
isConnected map[key.MachinePublic]bool,
|
|
|
|
node *types.Node,
|
|
|
|
) (*types.StateUpdate, error) {
|
|
|
|
nodeRoutes, err := GetNodeRoutes(tx, node)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, nil
|
2022-11-24 17:41:11 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
var changedNodes types.Nodes
|
2023-12-09 12:09:24 -05:00
|
|
|
for _, nodeRoute := range nodeRoutes {
|
2024-02-08 11:28:19 -05:00
|
|
|
routes, err := getRoutesByPrefix(tx, netip.Prefix(nodeRoute.Prefix))
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2022-11-24 17:41:11 -05:00
|
|
|
}
|
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
for _, route := range routes {
|
|
|
|
if route.IsPrimary {
|
|
|
|
// if we have a primary route, and the node is connected
|
|
|
|
// nothing needs to be done.
|
2024-02-08 11:28:19 -05:00
|
|
|
if isConnected[route.Node.MachineKey] {
|
2023-12-09 12:09:24 -05:00
|
|
|
continue
|
|
|
|
}
|
2022-11-24 17:41:11 -05:00
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
// if not, we need to failover the route
|
2024-02-08 11:28:19 -05:00
|
|
|
update, err := failoverRouteReturnUpdate(tx, isConnected, &route)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2022-11-24 17:41:11 -05:00
|
|
|
}
|
2022-11-25 10:29:45 -05:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
if update != nil {
|
|
|
|
changedNodes = append(changedNodes, update.ChangeNodes...)
|
|
|
|
}
|
|
|
|
}
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
2022-11-25 10:29:45 -05:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
if len(changedNodes) != 0 {
|
|
|
|
return &types.StateUpdate{
|
2023-12-09 12:09:24 -05:00
|
|
|
Type: types.StatePeerChanged,
|
2024-02-08 11:28:19 -05:00
|
|
|
ChangeNodes: changedNodes,
|
|
|
|
Message: "called from db.EnsureFailoverRouteIsAvailable",
|
|
|
|
}, nil
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
2022-11-24 17:41:11 -05:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, nil
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
2022-11-24 17:41:11 -05:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
func failoverRouteReturnUpdate(
|
|
|
|
tx *gorm.DB,
|
|
|
|
isConnected map[key.MachinePublic]bool,
|
|
|
|
r *types.Route,
|
|
|
|
) (*types.StateUpdate, error) {
|
|
|
|
changedKeys, err := failoverRoute(tx, isConnected, r)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
2022-11-24 17:41:11 -05:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
log.Trace().
|
|
|
|
Interface("isConnected", isConnected).
|
|
|
|
Interface("changedKeys", changedKeys).
|
|
|
|
Msg("building route failover")
|
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
if len(changedKeys) == 0 {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, nil
|
2023-12-09 12:09:24 -05:00
|
|
|
}
|
2022-11-24 17:41:11 -05:00
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
var nodes types.Nodes
|
|
|
|
for _, key := range changedKeys {
|
2024-02-08 11:28:19 -05:00
|
|
|
node, err := GetNodeByMachineKey(tx, key)
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2022-11-24 17:41:11 -05:00
|
|
|
}
|
2023-12-09 12:09:24 -05:00
|
|
|
|
|
|
|
nodes = append(nodes, node)
|
2022-11-24 17:41:11 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return &types.StateUpdate{
|
|
|
|
Type: types.StatePeerChanged,
|
|
|
|
ChangeNodes: nodes,
|
|
|
|
Message: "called from db.failoverRouteReturnUpdate",
|
|
|
|
}, nil
|
2022-11-24 17:41:11 -05:00
|
|
|
}
|
2022-11-25 12:50:12 -05:00
|
|
|
|
2023-12-09 12:09:24 -05:00
|
|
|
// failoverRoute takes a route that is no longer available,
|
|
|
|
// this can be either from:
|
|
|
|
// - being disabled
|
|
|
|
// - being deleted
|
|
|
|
// - host going offline
|
|
|
|
//
|
|
|
|
// and tries to find a new route to take over its place.
|
|
|
|
// If the given route was not primary, it returns early.
|
2024-02-08 11:28:19 -05:00
|
|
|
func failoverRoute(
|
|
|
|
tx *gorm.DB,
|
|
|
|
isConnected map[key.MachinePublic]bool,
|
|
|
|
r *types.Route,
|
|
|
|
) ([]key.MachinePublic, error) {
|
2023-12-09 12:09:24 -05:00
|
|
|
if r == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
// This route is not a primary route, and it is not
|
2023-12-09 12:09:24 -05:00
|
|
|
// being served to nodes.
|
|
|
|
if !r.IsPrimary {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// We do not have to failover exit nodes
|
|
|
|
if r.IsExitRoute() {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
routes, err := getRoutesByPrefix(tx, netip.Prefix(r.Prefix))
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var newPrimary *types.Route
|
|
|
|
|
|
|
|
// Find a new suitable route
|
|
|
|
for idx, route := range routes {
|
|
|
|
if r.ID == route.ID {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-02-03 09:30:15 -05:00
|
|
|
if !route.Enabled {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
if isConnected[route.Node.MachineKey] {
|
2023-12-09 12:09:24 -05:00
|
|
|
newPrimary = &routes[idx]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a new route was not found/available,
|
2024-02-08 11:28:19 -05:00
|
|
|
// return without an error.
|
2023-12-09 12:09:24 -05:00
|
|
|
// We do not want to update the database as
|
|
|
|
// the one currently marked as primary is the
|
|
|
|
// best we got.
|
|
|
|
if newPrimary == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Trace().
|
|
|
|
Str("hostname", newPrimary.Node.Hostname).
|
|
|
|
Msg("found new primary, updating db")
|
|
|
|
|
|
|
|
// Remove primary from the old route
|
|
|
|
r.IsPrimary = false
|
2024-02-08 11:28:19 -05:00
|
|
|
err = tx.Save(&r).Error
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("error disabling new primary route")
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Trace().
|
|
|
|
Str("hostname", newPrimary.Node.Hostname).
|
|
|
|
Msg("removed primary from old route")
|
|
|
|
|
|
|
|
// Set primary for the new primary
|
|
|
|
newPrimary.IsPrimary = true
|
2024-02-08 11:28:19 -05:00
|
|
|
err = tx.Save(&newPrimary).Error
|
2023-12-09 12:09:24 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("error enabling new primary route")
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Trace().
|
|
|
|
Str("hostname", newPrimary.Node.Hostname).
|
|
|
|
Msg("set primary to new route")
|
|
|
|
|
|
|
|
// Return a list of the machinekeys of the changed nodes.
|
|
|
|
return []key.MachinePublic{r.Node.MachineKey, newPrimary.Node.MachineKey}, nil
|
|
|
|
}
|
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
func (hsdb *HSDatabase) EnableAutoApprovedRoutes(
|
|
|
|
aclPolicy *policy.ACLPolicy,
|
2023-09-24 07:42:05 -04:00
|
|
|
node *types.Node,
|
2024-02-08 11:28:19 -05:00
|
|
|
) (*types.StateUpdate, error) {
|
|
|
|
return Write(hsdb.DB, func(tx *gorm.DB) (*types.StateUpdate, error) {
|
|
|
|
return EnableAutoApprovedRoutes(tx, aclPolicy, node)
|
|
|
|
})
|
|
|
|
}
|
2023-07-17 07:35:05 -04:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
// EnableAutoApprovedRoutes enables any routes advertised by a node that match the ACL autoApprovers policy.
|
|
|
|
func EnableAutoApprovedRoutes(
|
|
|
|
tx *gorm.DB,
|
|
|
|
aclPolicy *policy.ACLPolicy,
|
|
|
|
node *types.Node,
|
|
|
|
) (*types.StateUpdate, error) {
|
2023-09-24 07:42:05 -04:00
|
|
|
if len(node.IPAddresses) == 0 {
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, nil // This node has no IPAddresses, so can't possibly match any autoApprovers ACLs
|
2023-05-21 12:37:59 -04:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
routes, err := GetNodeAdvertisedRoutes(tx, node)
|
2023-05-21 12:37:59 -04:00
|
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
2023-09-24 07:42:05 -04:00
|
|
|
Str("node", node.Hostname).
|
|
|
|
Msg("Could not get advertised routes for node")
|
2023-05-21 12:37:59 -04:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-05-21 12:37:59 -04:00
|
|
|
}
|
|
|
|
|
2024-01-18 10:36:47 -05:00
|
|
|
log.Trace().Interface("routes", routes).Msg("routes for autoapproving")
|
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
approvedRoutes := types.Routes{}
|
|
|
|
|
|
|
|
for _, advertisedRoute := range routes {
|
|
|
|
if advertisedRoute.Enabled {
|
|
|
|
continue
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
routeApprovers, err := aclPolicy.AutoApprovers.GetRouteApprovers(
|
|
|
|
netip.Prefix(advertisedRoute.Prefix),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
log.Err(err).
|
|
|
|
Str("advertisedRoute", advertisedRoute.String()).
|
2023-09-24 07:42:05 -04:00
|
|
|
Uint64("nodeId", node.ID).
|
2023-05-21 12:37:59 -04:00
|
|
|
Msg("Failed to resolve autoApprovers for advertised route")
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-05-21 12:37:59 -04:00
|
|
|
}
|
|
|
|
|
2024-01-18 10:36:47 -05:00
|
|
|
log.Trace().
|
|
|
|
Str("node", node.Hostname).
|
|
|
|
Str("user", node.User.Name).
|
|
|
|
Strs("routeApprovers", routeApprovers).
|
|
|
|
Str("prefix", netip.Prefix(advertisedRoute.Prefix).String()).
|
|
|
|
Msg("looking up route for autoapproving")
|
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
for _, approvedAlias := range routeApprovers {
|
2023-09-24 07:42:05 -04:00
|
|
|
if approvedAlias == node.User.Name {
|
2023-05-21 12:37:59 -04:00
|
|
|
approvedRoutes = append(approvedRoutes, advertisedRoute)
|
|
|
|
} else {
|
|
|
|
// TODO(kradalby): figure out how to get this to depend on less stuff
|
2023-09-24 07:42:05 -04:00
|
|
|
approvedIps, err := aclPolicy.ExpandAlias(types.Nodes{node}, approvedAlias)
|
2023-05-21 12:37:59 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Err(err).
|
|
|
|
Str("alias", approvedAlias).
|
|
|
|
Msg("Failed to expand alias when processing autoApprovers policy")
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-05-21 12:37:59 -04:00
|
|
|
}
|
|
|
|
|
2023-09-24 07:42:05 -04:00
|
|
|
// approvedIPs should contain all of node's IPs if it matches the rule, so check for first
|
|
|
|
if approvedIps.Contains(node.IPAddresses[0]) {
|
2023-05-21 12:37:59 -04:00
|
|
|
approvedRoutes = append(approvedRoutes, advertisedRoute)
|
|
|
|
}
|
|
|
|
}
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|
2023-05-21 12:37:59 -04:00
|
|
|
}
|
2022-11-25 12:50:12 -05:00
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
update := &types.StateUpdate{
|
|
|
|
Type: types.StatePeerChanged,
|
|
|
|
ChangeNodes: types.Nodes{},
|
|
|
|
Message: "created in db.EnableAutoApprovedRoutes",
|
|
|
|
}
|
|
|
|
|
2023-05-21 12:37:59 -04:00
|
|
|
for _, approvedRoute := range approvedRoutes {
|
2024-02-08 11:28:19 -05:00
|
|
|
perHostUpdate, err := EnableRoute(tx, uint64(approvedRoute.ID))
|
2023-05-21 12:37:59 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Err(err).
|
|
|
|
Str("approvedRoute", approvedRoute.String()).
|
2023-09-24 07:42:05 -04:00
|
|
|
Uint64("nodeId", node.ID).
|
2023-05-21 12:37:59 -04:00
|
|
|
Msg("Failed to enable approved route")
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return nil, err
|
2023-05-21 12:37:59 -04:00
|
|
|
}
|
2024-02-08 11:28:19 -05:00
|
|
|
|
|
|
|
update.ChangeNodes = append(update.ChangeNodes, perHostUpdate.ChangeNodes...)
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 11:28:19 -05:00
|
|
|
return update, nil
|
2022-11-25 12:50:12 -05:00
|
|
|
}
|