mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	fix deletion of exit routes without nodes (#2286)
Fixes #2259 Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
		
							parent
							
								
									76d26a7eec
								
							
						
					
					
						commit
						58d089ce0a
					
				| @ -117,13 +117,13 @@ func EnableRoute(tx *gorm.DB, id uint64) (*types.StateUpdate, error) { | |||||||
| 	if route.IsExitRoute() { | 	if route.IsExitRoute() { | ||||||
| 		return enableRoutes( | 		return enableRoutes( | ||||||
| 			tx, | 			tx, | ||||||
| 			&route.Node, | 			route.Node, | ||||||
| 			tsaddr.AllIPv4(), | 			tsaddr.AllIPv4(), | ||||||
| 			tsaddr.AllIPv6(), | 			tsaddr.AllIPv6(), | ||||||
| 		) | 		) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return enableRoutes(tx, &route.Node, netip.Prefix(route.Prefix)) | 	return enableRoutes(tx, route.Node, netip.Prefix(route.Prefix)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func DisableRoute(tx *gorm.DB, | func DisableRoute(tx *gorm.DB, | ||||||
| @ -154,7 +154,7 @@ func DisableRoute(tx *gorm.DB, | |||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		routes, err = GetNodeRoutes(tx, &node) | 		routes, err = GetNodeRoutes(tx, node) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| @ -201,24 +201,26 @@ func DeleteRoute( | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if route.Node == nil { | ||||||
|  | 		// If the route is not assigned to a node, just delete it,
 | ||||||
|  | 		// there are no updates to be sent as no nodes are
 | ||||||
|  | 		// dependent on it
 | ||||||
|  | 		if err := tx.Unscoped().Delete(&route).Error; err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	var routes types.Routes | 	var routes types.Routes | ||||||
| 	node := route.Node | 	node := route.Node | ||||||
| 
 | 
 | ||||||
| 	// Tailscale requires both IPv4 and IPv6 exit routes to
 | 	// Tailscale requires both IPv4 and IPv6 exit routes to
 | ||||||
| 	// be enabled at the same time, as per
 | 	// be enabled at the same time, as per
 | ||||||
| 	// https://github.com/juanfont/headscale/issues/804#issuecomment-1399314002
 | 	// https://github.com/juanfont/headscale/issues/804#issuecomment-1399314002
 | ||||||
|  | 	// This means that if we delete a route which is an exit route, delete both.
 | ||||||
| 	var update []types.NodeID | 	var update []types.NodeID | ||||||
| 	if !route.IsExitRoute() { | 	if route.IsExitRoute() { | ||||||
| 		update, err = failoverRouteTx(tx, isLikelyConnected, route) | 		routes, err = GetNodeRoutes(tx, node) | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, nil |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if err := tx.Unscoped().Delete(&route).Error; err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		routes, err = GetNodeRoutes(tx, &node) |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| @ -233,13 +235,22 @@ func DeleteRoute( | |||||||
| 		if err := tx.Unscoped().Delete(&routesToDelete).Error; err != nil { | 		if err := tx.Unscoped().Delete(&routesToDelete).Error; err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
|  | 	} else { | ||||||
|  | 		update, err = failoverRouteTx(tx, isLikelyConnected, route) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err := tx.Unscoped().Delete(&route).Error; err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// If update is empty, it means that one was not created
 | 	// If update is empty, it means that one was not created
 | ||||||
| 	// by failover (as a failover was not necessary), create
 | 	// by failover (as a failover was not necessary), create
 | ||||||
| 	// one and return to the caller.
 | 	// one and return to the caller.
 | ||||||
| 	if routes == nil { | 	if routes == nil { | ||||||
| 		routes, err = GetNodeRoutes(tx, &node) | 		routes, err = GetNodeRoutes(tx, node) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -290,23 +290,18 @@ func (s *Suite) TestDeleteRoutes(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	ipp    = func(s string) netip.Prefix { return netip.MustParsePrefix(s) } | 	ipp = func(s string) netip.Prefix { return netip.MustParsePrefix(s) } | ||||||
| 	mkNode = func(nid types.NodeID) types.Node { | 	np  = func(nid types.NodeID) *types.Node { | ||||||
| 		return types.Node{ID: nid} | 		return &types.Node{ID: nid} | ||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var np = func(nid types.NodeID) *types.Node { |  | ||||||
| 	no := mkNode(nid) |  | ||||||
| 	return &no |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var r = func(id uint, nid types.NodeID, prefix netip.Prefix, enabled, primary bool) types.Route { | var r = func(id uint, nid types.NodeID, prefix netip.Prefix, enabled, primary bool) types.Route { | ||||||
| 	return types.Route{ | 	return types.Route{ | ||||||
| 		Model: gorm.Model{ | 		Model: gorm.Model{ | ||||||
| 			ID: id, | 			ID: id, | ||||||
| 		}, | 		}, | ||||||
| 		Node:      mkNode(nid), | 		Node:      np(nid), | ||||||
| 		Prefix:    prefix, | 		Prefix:    prefix, | ||||||
| 		Enabled:   enabled, | 		Enabled:   enabled, | ||||||
| 		IsPrimary: primary, | 		IsPrimary: primary, | ||||||
| @ -693,7 +688,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix:    ipp("10.0.0.0/24"), | 				Prefix:    ipp("10.0.0.0/24"), | ||||||
| 				Node:      types.Node{}, | 				Node:      &types.Node{}, | ||||||
| 				IsPrimary: false, | 				IsPrimary: false, | ||||||
| 			}, | 			}, | ||||||
| 			routes:  types.Routes{}, | 			routes:  types.Routes{}, | ||||||
| @ -707,7 +702,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix:    ipp("0.0.0.0/0"), | 				Prefix:    ipp("0.0.0.0/0"), | ||||||
| 				Node:      types.Node{}, | 				Node:      &types.Node{}, | ||||||
| 				IsPrimary: true, | 				IsPrimary: true, | ||||||
| 			}, | 			}, | ||||||
| 			routes:  types.Routes{}, | 			routes:  types.Routes{}, | ||||||
| @ -721,7 +716,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix: ipp("10.0.0.0/24"), | 				Prefix: ipp("10.0.0.0/24"), | ||||||
| 				Node: types.Node{ | 				Node: &types.Node{ | ||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				IsPrimary: true, | 				IsPrimary: true, | ||||||
| @ -732,7 +727,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: true, | 					IsPrimary: true, | ||||||
| @ -748,7 +743,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix: ipp("10.0.0.0/24"), | 				Prefix: ipp("10.0.0.0/24"), | ||||||
| 				Node: types.Node{ | 				Node: &types.Node{ | ||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				IsPrimary: true, | 				IsPrimary: true, | ||||||
| @ -760,7 +755,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: true, | 					IsPrimary: true, | ||||||
| @ -771,7 +766,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: false, | 					IsPrimary: false, | ||||||
| @ -795,7 +790,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix: ipp("10.0.0.0/24"), | 				Prefix: ipp("10.0.0.0/24"), | ||||||
| 				Node: types.Node{ | 				Node: &types.Node{ | ||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				IsPrimary: false, | 				IsPrimary: false, | ||||||
| @ -807,7 +802,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: true, | 					IsPrimary: true, | ||||||
| @ -818,7 +813,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: false, | 					IsPrimary: false, | ||||||
| @ -835,7 +830,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 2, | 					ID: 2, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix: ipp("10.0.0.0/24"), | 				Prefix: ipp("10.0.0.0/24"), | ||||||
| 				Node: types.Node{ | 				Node: &types.Node{ | ||||||
| 					ID: 2, | 					ID: 2, | ||||||
| 				}, | 				}, | ||||||
| 				IsPrimary: true, | 				IsPrimary: true, | ||||||
| @ -847,7 +842,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: false, | 					IsPrimary: false, | ||||||
| @ -858,7 +853,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: true, | 					IsPrimary: true, | ||||||
| @ -869,7 +864,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: false, | 					IsPrimary: false, | ||||||
| @ -893,7 +888,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix: ipp("10.0.0.0/24"), | 				Prefix: ipp("10.0.0.0/24"), | ||||||
| 				Node: types.Node{ | 				Node: &types.Node{ | ||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				IsPrimary: true, | 				IsPrimary: true, | ||||||
| @ -905,7 +900,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: true, | 					IsPrimary: true, | ||||||
| @ -917,7 +912,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 4, | 						ID: 4, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: false, | 					IsPrimary: false, | ||||||
| @ -938,7 +933,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix: ipp("10.0.0.0/24"), | 				Prefix: ipp("10.0.0.0/24"), | ||||||
| 				Node: types.Node{ | 				Node: &types.Node{ | ||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				IsPrimary: true, | 				IsPrimary: true, | ||||||
| @ -950,7 +945,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: true, | 					IsPrimary: true, | ||||||
| @ -962,7 +957,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 4, | 						ID: 4, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: false, | 					IsPrimary: false, | ||||||
| @ -973,7 +968,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: true, | 					IsPrimary: true, | ||||||
| @ -998,7 +993,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				Prefix: ipp("10.0.0.0/24"), | 				Prefix: ipp("10.0.0.0/24"), | ||||||
| 				Node: types.Node{ | 				Node: &types.Node{ | ||||||
| 					ID: 1, | 					ID: 1, | ||||||
| 				}, | 				}, | ||||||
| 				IsPrimary: true, | 				IsPrimary: true, | ||||||
| @ -1010,7 +1005,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 1, | 						ID: 1, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: true, | 					IsPrimary: true, | ||||||
| @ -1022,7 +1017,7 @@ func TestFailoverRouteTx(t *testing.T) { | |||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					Prefix: ipp("10.0.0.0/24"), | 					Prefix: ipp("10.0.0.0/24"), | ||||||
| 					Node: types.Node{ | 					Node: &types.Node{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 					}, | 					}, | ||||||
| 					IsPrimary: false, | 					IsPrimary: false, | ||||||
| @ -1075,7 +1070,7 @@ func TestFailoverRoute(t *testing.T) { | |||||||
| 			Model: gorm.Model{ | 			Model: gorm.Model{ | ||||||
| 				ID: id, | 				ID: id, | ||||||
| 			}, | 			}, | ||||||
| 			Node: types.Node{ | 			Node: &types.Node{ | ||||||
| 				ID: nid, | 				ID: nid, | ||||||
| 			}, | 			}, | ||||||
| 			Prefix:    prefix, | 			Prefix:    prefix, | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ type Route struct { | |||||||
| 	gorm.Model | 	gorm.Model | ||||||
| 
 | 
 | ||||||
| 	NodeID uint64 | 	NodeID uint64 | ||||||
| 	Node   Node | 	Node   *Node | ||||||
| 
 | 
 | ||||||
| 	// TODO(kradalby): change this custom type to netip.Prefix
 | 	// TODO(kradalby): change this custom type to netip.Prefix
 | ||||||
| 	Prefix netip.Prefix `gorm:"serializer:text"` | 	Prefix netip.Prefix `gorm:"serializer:text"` | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user