diff --git a/ipn/prefs.go b/ipn/prefs.go index 0d341ae1a..297d94d2e 100644 --- a/ipn/prefs.go +++ b/ipn/prefs.go @@ -426,6 +426,47 @@ func (p *Prefs) AdminPageURL() string { return url + "/admin/machines" } +// AdvertisesExitNode reports whether p is advertising both the v4 and +// v6 /0 exit node routes. +func (p *Prefs) AdvertisesExitNode() bool { + if p == nil { + return false + } + var v4, v6 bool + for _, r := range p.AdvertiseRoutes { + if r.Bits() != 0 { + continue + } + if r.IP().Is4() { + v4 = true + } else if r.IP().Is6() { + v6 = true + } + } + return v4 && v6 +} + +// SetRunExitNode mutates p (if non-nil) to add or remove the two +// /0 exit node routes. +func (p *Prefs) SetRunExitNode(runExit bool) { + if p == nil { + return + } + all := p.AdvertiseRoutes + p.AdvertiseRoutes = p.AdvertiseRoutes[:0] + for _, r := range all { + if r.Bits() != 0 { + p.AdvertiseRoutes = append(p.AdvertiseRoutes, r) + } + } + if !runExit { + return + } + p.AdvertiseRoutes = append(p.AdvertiseRoutes, + netaddr.IPPrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0), + netaddr.IPPrefixFrom(netaddr.IPv6Unspecified(), 0)) +} + // PrefsFromBytes deserializes Prefs from a JSON blob. If // enforceDefaults is true, Prefs.RouteAll and Prefs.AllowSingleHosts // are forced on. diff --git a/ipn/prefs_test.go b/ipn/prefs_test.go index 252ecce76..23bc4f31f 100644 --- a/ipn/prefs_test.go +++ b/ipn/prefs_test.go @@ -646,3 +646,35 @@ func TestMaskedPrefsPretty(t *testing.T) { } } } + +func TestPrefsExitNode(t *testing.T) { + var p *Prefs + if p.AdvertisesExitNode() { + t.Errorf("nil shouldn't advertise exit node") + } + p = NewPrefs() + if p.AdvertisesExitNode() { + t.Errorf("default shouldn't advertise exit node") + } + p.AdvertiseRoutes = []netaddr.IPPrefix{ + netaddr.MustParseIPPrefix("10.0.0.0/16"), + } + p.SetRunExitNode(true) + if got, want := len(p.AdvertiseRoutes), 3; got != want { + t.Errorf("routes = %d; want %d", got, want) + } + p.SetRunExitNode(true) + if got, want := len(p.AdvertiseRoutes), 3; got != want { + t.Errorf("routes = %d; want %d", got, want) + } + if !p.AdvertisesExitNode() { + t.Errorf("not advertising after enable") + } + p.SetRunExitNode(false) + if p.AdvertisesExitNode() { + t.Errorf("advertising after disable") + } + if got, want := len(p.AdvertiseRoutes), 1; got != want { + t.Errorf("routes = %d; want %d", got, want) + } +}