Skip to content

Commit c10aca7

Browse files
authored
android: don't set vpnService to nil when state is Stopped (#523)
We are currently setting vpnService.service to nil: -any time there’s an error with updateTUN -when we exit out of runBackend -if the config is the default config (aka when the ipn state is Stopped) When it gets set to nil, we don’t handle state or config updates by calling updateTUN until after startVPN is called again. The second case never happens because there’s no condition to break out of the loop in runBackend and ctx is uncancelable per the doc for context.Background() In the third case, we should not establish the VPN; the state is already in the correct Stopped state, but there’s no need to set the service to nil and prevent updateTUN from being called. The quick settings tile bug is caused by this third case, where because the saved prefs starts the app up in the Stopped state, the config is set to the default config, and the service is set to nil, and we can't updateTUN until there’s another startVPN call. This PR: -cleans up the updateTUN error handling to be more consistent -removes the IPNService parameter from updateTUN so that vpnService.service is not set to nil in the third case -updates IPNService to use stopSelf and not stopForeground when we disconnect the VPN; the latter only disconnects if there is a memory need Fixes tailscale/tailscale#12489 Signed-off-by: kari-ts <[email protected]>
1 parent 25e7681 commit c10aca7

File tree

4 files changed

+44
-36
lines changed

4 files changed

+44
-36
lines changed

android/src/main/java/com/tailscale/ipn/IPNService.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,14 @@ open class IPNService : VpnService(), libtailscale.IPNService {
7373
override fun close() {
7474
app.setWantRunning(false) {}
7575
Notifier.setState(Ipn.State.Stopping)
76-
stopForeground(STOP_FOREGROUND_REMOVE)
76+
disconnectVPN()
7777
Libtailscale.serviceDisconnect(this)
7878
}
7979

80+
override fun disconnectVPN(){
81+
stopSelf()
82+
}
83+
8084
override fun onDestroy() {
8185
close()
8286
updateVpnStatus(false)

libtailscale/backend.go

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"net/http"
1212
"os"
1313
"path/filepath"
14+
"reflect"
1415
"runtime/debug"
1516
"sync"
1617
"sync/atomic"
@@ -165,36 +166,24 @@ func (a *App) runBackend(ctx context.Context) error {
165166
select {
166167
case s := <-stateCh:
167168
state = s
168-
if cfg.rcfg != nil && state >= ipn.Starting && vpnService.service != nil {
169+
if state >= ipn.Starting && vpnService.service != nil && b.isConfigNonNilAndDifferent(cfg.rcfg, cfg.dcfg) {
169170
// On state change, check if there are router or config changes requiring an update to VPNBuilder
170-
if err := b.updateTUN(vpnService.service, cfg.rcfg, cfg.dcfg); err != nil {
171+
if err := b.updateTUN(cfg.rcfg, cfg.dcfg); err != nil {
171172
if errors.Is(err, errMultipleUsers) {
172173
// TODO: surface error to user
173174
}
174-
log.Printf("VPN update failed: %v", err)
175-
176-
mp := new(ipn.MaskedPrefs)
177-
mp.WantRunning = false
178-
mp.WantRunningSet = true
179-
180-
_, err := a.EditPrefs(*mp)
181-
if err != nil {
182-
log.Printf("localapi edit prefs error %v", err)
183-
}
184-
185-
b.lastCfg = nil
186-
b.CloseTUNs()
175+
a.closeVpnService(err, b)
187176
}
188177
}
189178
case n := <-netmapCh:
190179
networkMap = n
191180
case c := <-configs:
192181
cfg = c
193-
if b == nil || vpnService.service == nil || cfg.rcfg == nil {
182+
if vpnService.service == nil || !b.isConfigNonNilAndDifferent(cfg.rcfg, cfg.dcfg) {
194183
configErrs <- nil
195184
break
196185
}
197-
configErrs <- b.updateTUN(vpnService.service, cfg.rcfg, cfg.dcfg)
186+
configErrs <- b.updateTUN(cfg.rcfg, cfg.dcfg)
198187
case s := <-onVPNRequested:
199188
if vpnService.service != nil && vpnService.service.ID() == s.ID() {
200189
// Still the same VPN instance, do nothing
@@ -230,12 +219,9 @@ func (a *App) runBackend(ctx context.Context) error {
230219
if networkMap != nil {
231220
// TODO
232221
}
233-
if cfg.rcfg != nil && state >= ipn.Starting {
234-
if err := b.updateTUN(vpnService.service, cfg.rcfg, cfg.dcfg); err != nil {
235-
log.Printf("VPN update failed: %v", err)
236-
vpnService.service.Close()
237-
b.lastCfg = nil
238-
b.CloseTUNs()
222+
if state >= ipn.Starting && b.isConfigNonNilAndDifferent(cfg.rcfg, cfg.dcfg) {
223+
if err := b.updateTUN(cfg.rcfg, cfg.dcfg); err != nil {
224+
a.closeVpnService(err, b)
239225
}
240226
}
241227
case s := <-onDisconnect:
@@ -245,9 +231,7 @@ func (a *App) runBackend(ctx context.Context) error {
245231
vpnService.service = nil
246232
}
247233
case i := <-onDNSConfigChanged:
248-
if b != nil {
249-
go b.NetworkChanged(i)
250-
}
234+
go b.NetworkChanged(i)
251235
}
252236
}
253237
}
@@ -346,3 +330,29 @@ func (a *App) newBackend(dataDir, directFileRoot string, appCtx AppContext, stor
346330
}()
347331
return b, nil
348332
}
333+
334+
func (b *backend) isConfigNonNilAndDifferent(rcfg *router.Config, dcfg *dns.OSConfig) bool {
335+
if reflect.DeepEqual(rcfg, b.lastCfg) && reflect.DeepEqual(dcfg, b.lastDNSCfg) {
336+
b.logger.Logf("isConfigNonNilAndDifferent: no change to Routes or DNS, ignore")
337+
return false
338+
}
339+
return rcfg != nil
340+
}
341+
342+
func (a *App) closeVpnService(err error, b *backend) {
343+
log.Printf("VPN update failed: %v", err)
344+
345+
mp := new(ipn.MaskedPrefs)
346+
mp.WantRunning = false
347+
mp.WantRunningSet = true
348+
349+
if _, localApiErr := a.EditPrefs(*mp); localApiErr != nil {
350+
log.Printf("localapi edit prefs error %v", localApiErr)
351+
}
352+
353+
b.lastCfg = nil
354+
b.CloseTUNs()
355+
356+
vpnService.service.DisconnectVPN()
357+
vpnService.service = nil
358+
}

libtailscale/interfaces.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ type IPNService interface {
8080

8181
Close()
8282

83+
DisconnectVPN()
84+
8385
UpdateVpnStatus(bool)
8486
}
8587

libtailscale/net.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"log"
1010
"net"
1111
"net/netip"
12-
"reflect"
1312
"runtime/debug"
1413
"strings"
1514
"syscall"
@@ -125,12 +124,7 @@ var googleDNSServers = []netip.Addr{
125124
netip.MustParseAddr("2001:4860:4860::8844"),
126125
}
127126

128-
func (b *backend) updateTUN(service IPNService, rcfg *router.Config, dcfg *dns.OSConfig) error {
129-
if reflect.DeepEqual(rcfg, b.lastCfg) && reflect.DeepEqual(dcfg, b.lastDNSCfg) {
130-
b.logger.Logf("updateTUN: no change to Routes or DNS, ignore")
131-
return nil
132-
}
133-
127+
func (b *backend) updateTUN(rcfg *router.Config, dcfg *dns.OSConfig) error {
134128
b.logger.Logf("updateTUN: changed")
135129
defer b.logger.Logf("updateTUN: finished")
136130

@@ -147,7 +141,6 @@ func (b *backend) updateTUN(service IPNService, rcfg *router.Config, dcfg *dns.O
147141
if len(rcfg.LocalAddrs) == 0 {
148142
return nil
149143
}
150-
vpnService.service = service
151144
builder := vpnService.service.NewBuilder()
152145
b.logger.Logf("updateTUN: got new builder")
153146

@@ -258,7 +251,6 @@ func closeFileDescriptor() error {
258251
func (b *backend) CloseTUNs() {
259252
b.lastCfg = nil
260253
b.devices.Shutdown()
261-
vpnService.service = nil
262254
}
263255

264256
// ifname is the interface name retrieved from LinkProperties on network change. If a network is lost, an empty string is passed in.

0 commit comments

Comments
 (0)