mirror of
https://github.com/tailscale/tailscale.git
synced 2025-05-02 13:41:03 +00:00
wip
This commit is contained in:
parent
9258bcc360
commit
43fbc0d588
@ -18,6 +18,7 @@ import (
|
||||
|
||||
xmaps "golang.org/x/exp/maps"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"tailscale.com/appc/routeinfo"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/util/dnsname"
|
||||
@ -34,6 +35,10 @@ type RouteAdvertiser interface {
|
||||
|
||||
// UnadvertiseRoute removes any matching route advertisements.
|
||||
UnadvertiseRoute(...netip.Prefix) error
|
||||
|
||||
// Store/ReadRouteInfo persists and retreives RouteInfo to stable storage
|
||||
StoreRouteInfo(*routeinfo.RouteInfo) error
|
||||
ReadRouteInfo() (*routeinfo.RouteInfo, error)
|
||||
}
|
||||
|
||||
// AppConnector is an implementation of an AppConnector that performs
|
||||
|
@ -6,6 +6,8 @@ package appctest
|
||||
import (
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"tailscale.com/appc/routeinfo"
|
||||
)
|
||||
|
||||
// RouteCollector is a test helper that collects the list of routes advertised
|
||||
@ -32,6 +34,14 @@ func (rc *RouteCollector) UnadvertiseRoute(toRemove ...netip.Prefix) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *RouteCollector) StoreRouteInfo(ri *routeinfo.RouteInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *RouteCollector) ReadRouteInfo() (*routeinfo.RouteInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// RemovedRoutes returns the list of routes that were removed.
|
||||
func (rc *RouteCollector) RemovedRoutes() []netip.Prefix {
|
||||
return rc.removedRoutes
|
||||
|
25
appc/routeinfo/routeinfo.go
Normal file
25
appc/routeinfo/routeinfo.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package routeinfo
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RouteInfo struct {
|
||||
// routes set with --advertise-routes
|
||||
Local []netip.Prefix
|
||||
// routes from the 'routes' section of an app connector acl
|
||||
Control []netip.Prefix
|
||||
// routes discovered by observing dns lookups for configured domains
|
||||
Discovered map[string]*DatedRoutes
|
||||
}
|
||||
|
||||
type DatedRoutes struct {
|
||||
// routes discovered for a domain, and when they were last seen in a dns query
|
||||
Routes map[netip.Prefix]time.Time
|
||||
// the time at which we last expired old routes
|
||||
LastCleanup time.Time
|
||||
}
|
@ -35,6 +35,7 @@ import (
|
||||
xmaps "golang.org/x/exp/maps"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"tailscale.com/appc"
|
||||
"tailscale.com/appc/routeinfo"
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/control/controlclient"
|
||||
@ -6250,6 +6251,49 @@ func (b *LocalBackend) UnadvertiseRoute(toRemove ...netip.Prefix) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// namespace a key with the profile manager's current profile key, if any
|
||||
func namespaceKeyForCurrentProfile(pm *profileManager, key ipn.StateKey) ipn.StateKey {
|
||||
return pm.CurrentProfile().Key + "||" + key
|
||||
}
|
||||
|
||||
const routeInfoStateStoreKey ipn.StateKey = "_routeInfo"
|
||||
|
||||
// StoreRouteInfo implements the appc.RouteAdvertiser interface. It stores
|
||||
// RouteInfo to StateStore per profile.
|
||||
func (b *LocalBackend) StoreRouteInfo(ri *routeinfo.RouteInfo) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.pm.CurrentProfile().ID == "" {
|
||||
return nil
|
||||
}
|
||||
key := namespaceKeyForCurrentProfile(b.pm, routeInfoStateStoreKey)
|
||||
bs, err := json.Marshal(ri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.pm.WriteState(key, bs)
|
||||
}
|
||||
|
||||
// ReadRouteInfo implements the appc.RouteAdvertiser interface. It reads
|
||||
// RouteInfo from StateStore per profile.
|
||||
func (b *LocalBackend) ReadRouteInfo() (*routeinfo.RouteInfo, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.pm.CurrentProfile().ID == "" {
|
||||
return &routeinfo.RouteInfo{}, nil
|
||||
}
|
||||
key := namespaceKeyForCurrentProfile(b.pm, routeInfoStateStoreKey)
|
||||
bs, err := b.pm.Store().ReadState(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ri := &routeinfo.RouteInfo{}
|
||||
if err := json.Unmarshal(bs, ri); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ri, nil
|
||||
}
|
||||
|
||||
// seamlessRenewalEnabled reports whether seamless key renewals are enabled
|
||||
// (i.e. we saw our self node with the SeamlessKeyRenewal attr in a netmap).
|
||||
// This enables beta functionality of renewing node keys without breaking
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"tailscale.com/appc"
|
||||
"tailscale.com/appc/appctest"
|
||||
"tailscale.com/appc/routeinfo"
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/drive"
|
||||
"tailscale.com/drive/driveimpl"
|
||||
@ -2634,3 +2635,49 @@ func (b *LocalBackend) SetPrefsForTest(newp *ipn.Prefs) {
|
||||
defer unlock()
|
||||
b.setPrefsLockedOnEntry(newp, unlock)
|
||||
}
|
||||
func TestReadWriteRouteInfo(t *testing.T) {
|
||||
// test can read what's written
|
||||
prefix1 := netip.MustParsePrefix("1.2.3.4/32")
|
||||
prefix2 := netip.MustParsePrefix("1.2.3.5/32")
|
||||
prefix3 := netip.MustParsePrefix("1.2.3.6/32")
|
||||
now := time.Now()
|
||||
discovered := make(map[string]*routeinfo.DatedRoutes)
|
||||
routes := make(map[netip.Prefix]time.Time)
|
||||
routes[prefix3] = now
|
||||
discovered["example.com"] = &routeinfo.DatedRoutes{
|
||||
LastCleanup: now,
|
||||
Routes: routes,
|
||||
}
|
||||
b := newTestBackend(t)
|
||||
ri := routeinfo.RouteInfo{
|
||||
Local: []netip.Prefix{prefix1},
|
||||
Control: []netip.Prefix{prefix2},
|
||||
Discovered: discovered,
|
||||
}
|
||||
if err := b.StoreRouteInfo(&ri); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
readRi, err := b.ReadRouteInfo()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(readRi.Local) != 1 || len(readRi.Control) != 1 || len(readRi.Discovered) != 1 {
|
||||
t.Fatal("read Ri expected to be same shape as ri")
|
||||
}
|
||||
if readRi.Local[0] != ri.Local[0] {
|
||||
t.Fatalf("wanted %v, got %v", ri.Local[0], readRi.Local[0])
|
||||
}
|
||||
if readRi.Control[0] != ri.Control[0] {
|
||||
t.Fatalf("wanted %v, got %v", ri.Control[0], readRi.Control[0])
|
||||
}
|
||||
dr := readRi.Discovered["example.com"]
|
||||
if dr.LastCleanup.Compare(now) != 0 {
|
||||
t.Fatalf("wanted %v, got %v", now, dr.LastCleanup)
|
||||
}
|
||||
if len(dr.Routes) != 1 {
|
||||
t.Fatalf("read Ri expected to be same shape as ri")
|
||||
}
|
||||
if dr.Routes[prefix3].Compare(routes[prefix3]) != 0 {
|
||||
t.Fatalf("wanted %v, got %v", routes[prefix3], dr.Routes[prefix3])
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user