ipn/ipnlocal: fix missing controlknobs.Knobs plumbing

I missed connecting some controlknobs.Knobs pieces in 4e91cf20a8
resulting in that breaking control knobs entirely.

Whoops.

The fix in ipn/ipnlocal (where it makes a new controlclient) but to
atone, I also added integration tests. Those integration tests use
a new "tailscale debug control-knobs" which by itself might be useful
for future debugging.

Updates #9351

Change-Id: Id9c89c8637746d879d5da67b9ac4e0d2367a3f0d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/9354/head
Brad Fitzpatrick 8 months ago committed by Brad Fitzpatrick
parent 12d4685328
commit 7c1ed38ab3

@ -392,6 +392,20 @@ func (lc *LocalClient) DebugAction(ctx context.Context, action string) error {
return nil
}
// DebugResultJSON invokes a debug action and returns its result as something JSON-able.
// These are development tools and subject to change or removal over time.
func (lc *LocalClient) DebugResultJSON(ctx context.Context, action string) (any, error) {
body, err := lc.send(ctx, "POST", "/localapi/v0/debug?action="+url.QueryEscape(action), 200, nil)
if err != nil {
return nil, fmt.Errorf("error %w: %s", err, body)
}
var x any
if err := json.Unmarshal(body, &x); err != nil {
return nil, err
}
return x, nil
}
// DebugPortmapOpts contains options for the DebugPortmap command.
type DebugPortmapOpts struct {
// Duration is how long the mapping should be created for. It defaults

@ -138,6 +138,11 @@ var debugCmd = &ffcli.Command{
Exec: localAPIAction("break-derp-conns"),
ShortHelp: "break any open DERP connections from the daemon",
},
{
Name: "control-knobs",
Exec: debugControlKnobs,
ShortHelp: "see current control knobs",
},
{
Name: "prefs",
Exec: runPrefs,
@ -915,3 +920,17 @@ func runPeerEndpointChanges(ctx context.Context, args []string) error {
fmt.Printf("%s", dst.String())
return nil
}
func debugControlKnobs(ctx context.Context, args []string) error {
if len(args) > 0 {
return errors.New("unexpected arguments")
}
v, err := localClient.DebugResultJSON(ctx, "control-knobs")
if err != nil {
return err
}
e := json.NewEncoder(os.Stdout)
e.SetIndent("", " ")
e.Encode(v)
return nil
}

@ -39,3 +39,19 @@ type Knobs struct {
// running in magicsock, even when idle.
ForceBackgroundSTUN atomic.Bool
}
// AsDebugJSON returns k as something that can be marshalled with json.Marshal
// for debug.
func (k *Knobs) AsDebugJSON() map[string]any {
if k == nil {
return nil
}
return map[string]any{
"DisableUPnP": k.DisableUPnP.Load(),
"DisableDRPO": k.DisableDRPO.Load(),
"KeepFullWGConfig": k.KeepFullWGConfig.Load(),
"RandomizeClientPort": k.RandomizeClientPort.Load(),
"OneCGNAT": k.OneCGNAT.Load(),
"ForceBackgroundSTUN": k.ForceBackgroundSTUN.Load(),
}
}

@ -1462,6 +1462,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
Observer: b,
C2NHandler: http.HandlerFunc(b.handleC2N),
DialPlan: &b.dialPlan, // pointer because it can't be copied
ControlKnobs: b.sys.ControlKnobs(),
// Don't warn about broken Linux IP forwarding when
// netstack is being used.

@ -557,6 +557,13 @@ func (h *Handler) serveDebug(w http.ResponseWriter, r *http.Request) {
err = h.b.DebugBreakTCPConns()
case "break-derp-conns":
err = h.b.DebugBreakDERPConns()
case "control-knobs":
k := h.b.ControlKnobs()
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(k.AsDebugJSON())
if err == nil {
return
}
case "":
err = fmt.Errorf("missing parameter 'action'")
default:

@ -119,6 +119,36 @@ func TestOneNodeExpiredKey(t *testing.T) {
d1.MustCleanShutdown(t)
}
func TestControlKnobs(t *testing.T) {
t.Parallel()
env := newTestEnv(t)
n1 := newTestNode(t, env)
d1 := n1.StartDaemon()
defer d1.MustCleanShutdown(t)
n1.AwaitResponding()
n1.MustUp()
t.Logf("Got IP: %v", n1.AwaitIP())
n1.AwaitRunning()
cmd := n1.Tailscale("debug", "control-knobs")
cmd.Stdout = nil // in case --verbose-tailscale was set
cmd.Stderr = nil // in case --verbose-tailscale was set
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatal(err)
}
t.Logf("control-knobs output:\n%s", out)
var m map[string]any
if err := json.Unmarshal(out, &m); err != nil {
t.Fatal(err)
}
if got, want := m["DisableUPnP"], true; got != want {
t.Errorf("control-knobs DisableUPnP = %v; want %v", got, want)
}
}
func TestCollectPanic(t *testing.T) {
t.Parallel()
env := newTestEnv(t)

Loading…
Cancel
Save