package types import ( "fmt" "net/netip" v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "google.golang.org/protobuf/types/known/timestamppb" "gorm.io/gorm" "tailscale.com/net/tsaddr" ) type Route struct { gorm.Model NodeID uint64 Node Node // TODO(kradalby): change this custom type to netip.Prefix Prefix netip.Prefix `gorm:"serializer:text"` Advertised bool Enabled bool IsPrimary bool } type Routes []Route func (r *Route) String() string { return fmt.Sprintf("%s:%s", r.Node.Hostname, netip.Prefix(r.Prefix).String()) } func (r *Route) IsExitRoute() bool { return tsaddr.IsExitRoute(r.Prefix) } func (r *Route) IsAnnouncable() bool { return r.Advertised && r.Enabled } func (rs Routes) Prefixes() []netip.Prefix { prefixes := make([]netip.Prefix, len(rs)) for i, r := range rs { prefixes[i] = netip.Prefix(r.Prefix) } return prefixes } // Primaries returns Primary routes from a list of routes. func (rs Routes) Primaries() Routes { res := make(Routes, 0) for _, route := range rs { if route.IsPrimary { res = append(res, route) } } return res } func (rs Routes) PrefixMap() map[netip.Prefix][]Route { res := map[netip.Prefix][]Route{} for _, route := range rs { if _, ok := res[route.Prefix]; ok { res[route.Prefix] = append(res[route.Prefix], route) } else { res[route.Prefix] = []Route{route} } } return res } func (rs Routes) Proto() []*v1.Route { protoRoutes := []*v1.Route{} for _, route := range rs { protoRoute := v1.Route{ Id: uint64(route.ID), Node: route.Node.Proto(), Prefix: route.Prefix.String(), Advertised: route.Advertised, Enabled: route.Enabled, IsPrimary: route.IsPrimary, CreatedAt: timestamppb.New(route.CreatedAt), UpdatedAt: timestamppb.New(route.UpdatedAt), } if route.DeletedAt.Valid { protoRoute.DeletedAt = timestamppb.New(route.DeletedAt.Time) } protoRoutes = append(protoRoutes, &protoRoute) } return protoRoutes }