feature/conn25,ipn/ipnext,ipn/ipnlocal: add ExtraRouterConfigRoutes hook

conn25 needs to add routes to the operating system to direct handling
of the addresses in the magic IP range to the tailscale0 TUN and
tailscaled.

The way we do this for exit nodes and VIP services is that we add routes
to the Routes field of router.Config, and then the config is passed to
the WireGuard engine Reconfig.

conn25 is implemented as an ipnext.Extension and so this commit adds a
hook to ipnext.Hooks to allow any extension to provide routes to the
config. The hook if provided is called in routerConfigLocked, similarly
to exit nodes and VIP services.

Fixes tailscale/corp#38123

Signed-off-by: Fran Bull <fran@tailscale.com>
main
Fran Bull 3 weeks ago committed by franbull
parent 330a17b7d7
commit 2d5962f524
  1. 15
      feature/conn25/conn25.go
  2. 12
      ipn/ipnext/ipnext.go
  3. 5
      ipn/ipnlocal/local.go

@ -33,6 +33,7 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/types/appctype"
"tailscale.com/types/logger"
"tailscale.com/types/views"
"tailscale.com/util/dnsname"
"tailscale.com/util/mak"
"tailscale.com/util/set"
@ -123,12 +124,18 @@ func (e *extension) Init(host ipnext.Host) error {
}
e.host = host
host.Hooks().OnSelfChange.Add(e.onSelfChange)
host.Hooks().ExtraRouterConfigRoutes.Set(e.getMagicRange)
ctx, cancel := context.WithCancelCause(context.Background())
e.ctxCancel = cancel
go e.sendLoop(ctx)
return nil
}
func (e *extension) getMagicRange() views.Slice[netip.Prefix] {
cfg := e.conn25.client.getConfig()
return views.SliceOf(cfg.magicIPSet.Prefixes())
}
// Shutdown implements [ipnlocal.Extension].
func (e *extension) Shutdown() error {
if e.ctxCancel != nil {
@ -498,6 +505,12 @@ type client struct {
config config
}
func (c *client) getConfig() config {
c.mu.Lock()
defer c.mu.Unlock()
return c.config
}
// ClientTransitIPForMagicIP is part of the implementation of the IPMapper interface for dataflows lookups.
func (c *client) ClientTransitIPForMagicIP(magicIP netip.Addr) (netip.Addr, error) {
c.mu.Lock()
@ -673,7 +686,7 @@ func makePeerAPIReq(ctx context.Context, httpClient *http.Client, urlBase string
}
func (e *extension) sendAddressAssignment(ctx context.Context, as addrs) error {
app, ok := e.conn25.client.config.appsByName[as.app]
app, ok := e.conn25.client.getConfig().appsByName[as.app]
if !ok {
e.conn25.client.logf("App not found for app: %s (domain: %s)", as.app, as.domain)
return errors.New("app not found")

@ -418,6 +418,18 @@ type Hooks struct {
// new hooks that fit into the new architecture that make use of new
// WireGuard APIs.
ExtraWireGuardAllowedIPs feature.Hook[func(key.NodePublic) views.Slice[netip.Prefix]]
// ExtraRouterConfigRoutes returns a view of prefixes to append to [router.Config.Routes].
//
// Routes goes through the WireGuard engine which makes efforts to avoid
// unnecessary reconfiguration by checking that things have actually changed.
// So implementors should make sure that the order of the prefixes is stable
// and that we don't have duplicate entries.
//
// The returned slice should not be mutated by the extension after it is returned.
//
// The hook is called with LocalBackend's mutex locked.
ExtraRouterConfigRoutes feature.Hook[func() views.Slice[netip.Prefix]]
}
// FilterHooks contains hooks that extensions can use to customize the packet

@ -5667,6 +5667,11 @@ func (b *LocalBackend) routerConfigLocked(cfg *wgcfg.Config, prefs ipn.PrefsView
}
}
// Get any extra Routes an extension may want installed.
if extensionRoutesFx, ok := b.extHost.hooks.ExtraRouterConfigRoutes.GetOk(); ok {
rs.Routes = extensionRoutesFx().AppendTo(rs.Routes)
}
return rs
}

Loading…
Cancel
Save