mirror of
https://github.com/tailscale/tailscale.git
synced 2025-05-03 14:11:02 +00:00
wip
This commit is contained in:
parent
9258bcc360
commit
43fbc0d588
@ -18,6 +18,7 @@ import (
|
|||||||
|
|
||||||
xmaps "golang.org/x/exp/maps"
|
xmaps "golang.org/x/exp/maps"
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
|
"tailscale.com/appc/routeinfo"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/views"
|
"tailscale.com/types/views"
|
||||||
"tailscale.com/util/dnsname"
|
"tailscale.com/util/dnsname"
|
||||||
@ -34,6 +35,10 @@ type RouteAdvertiser interface {
|
|||||||
|
|
||||||
// UnadvertiseRoute removes any matching route advertisements.
|
// UnadvertiseRoute removes any matching route advertisements.
|
||||||
UnadvertiseRoute(...netip.Prefix) error
|
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
|
// AppConnector is an implementation of an AppConnector that performs
|
||||||
|
@ -6,6 +6,8 @@ package appctest
|
|||||||
import (
|
import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
"tailscale.com/appc/routeinfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RouteCollector is a test helper that collects the list of routes advertised
|
// 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
|
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.
|
// RemovedRoutes returns the list of routes that were removed.
|
||||||
func (rc *RouteCollector) RemovedRoutes() []netip.Prefix {
|
func (rc *RouteCollector) RemovedRoutes() []netip.Prefix {
|
||||||
return rc.removedRoutes
|
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"
|
xmaps "golang.org/x/exp/maps"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"tailscale.com/appc"
|
"tailscale.com/appc"
|
||||||
|
"tailscale.com/appc/routeinfo"
|
||||||
"tailscale.com/client/tailscale/apitype"
|
"tailscale.com/client/tailscale/apitype"
|
||||||
"tailscale.com/clientupdate"
|
"tailscale.com/clientupdate"
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
@ -6250,6 +6251,49 @@ func (b *LocalBackend) UnadvertiseRoute(toRemove ...netip.Prefix) error {
|
|||||||
return err
|
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
|
// seamlessRenewalEnabled reports whether seamless key renewals are enabled
|
||||||
// (i.e. we saw our self node with the SeamlessKeyRenewal attr in a netmap).
|
// (i.e. we saw our self node with the SeamlessKeyRenewal attr in a netmap).
|
||||||
// This enables beta functionality of renewing node keys without breaking
|
// This enables beta functionality of renewing node keys without breaking
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"golang.org/x/net/dns/dnsmessage"
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
"tailscale.com/appc"
|
"tailscale.com/appc"
|
||||||
"tailscale.com/appc/appctest"
|
"tailscale.com/appc/appctest"
|
||||||
|
"tailscale.com/appc/routeinfo"
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
"tailscale.com/drive"
|
"tailscale.com/drive"
|
||||||
"tailscale.com/drive/driveimpl"
|
"tailscale.com/drive/driveimpl"
|
||||||
@ -2634,3 +2635,49 @@ func (b *LocalBackend) SetPrefsForTest(newp *ipn.Prefs) {
|
|||||||
defer unlock()
|
defer unlock()
|
||||||
b.setPrefsLockedOnEntry(newp, 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