@@ -21,7 +21,21 @@ object NetworkChangeCallback {
2121
2222 private val lock = ReentrantLock ()
2323
24- private val activeNetworks = mutableMapOf<Network , NetworkInfo >() // keyed by Network
24+ // All currently active non-VPN networks we know about.
25+ private val activeNetworks = mutableMapOf<Network , NetworkInfo >()
26+
27+ // Cached chosen default network for outbound sockets.
28+ @Volatile
29+ var cachedDefaultNetwork: Network ? = null
30+ private set
31+
32+ // Cached info for the chosen default network.
33+ @Volatile private var cachedDefaultNetworkInfo: NetworkInfo ? = null
34+
35+ // Convenience: cached interface name for logging.
36+ @Volatile
37+ var cachedDefaultInterfaceName: String? = null
38+ private set
2539
2640 // monitorDnsChanges sets up a network callback to monitor changes to the
2741 // system's network state and update the DNS configuration when interfaces
@@ -45,34 +59,45 @@ object NetworkChangeCallback {
4559 connectivityManager.registerNetworkCallback(
4660 networkConnectivityRequest,
4761 object : ConnectivityManager .NetworkCallback () {
62+
4863 override fun onAvailable (network : Network ) {
4964 super .onAvailable(network)
5065
51- TSLog .d(TAG , " onAvailable: network ${network} " )
66+ TSLog .d(TAG , " onAvailable: network $network " )
67+
5268 lock.withLock {
5369 activeNetworks[network] = NetworkInfo (NetworkCapabilities (), LinkProperties ())
70+ recomputeDefaultNetworkLocked(" onAvailable" )
5471 }
5572 }
5673
5774 override fun onCapabilitiesChanged (network : Network , capabilities : NetworkCapabilities ) {
5875 super .onCapabilitiesChanged(network, capabilities)
59- lock.withLock { activeNetworks[network]?.caps = capabilities }
76+
77+ lock.withLock {
78+ activeNetworks[network]?.caps = capabilities
79+ recomputeDefaultNetworkLocked(" onCapabilitiesChanged" )
80+ }
6081 }
6182
6283 override fun onLinkPropertiesChanged (network : Network , linkProperties : LinkProperties ) {
6384 super .onLinkPropertiesChanged(network, linkProperties)
85+
6486 lock.withLock {
6587 activeNetworks[network]?.linkProps = linkProperties
88+ recomputeDefaultNetworkLocked(" onLinkPropertiesChanged" )
6689 maybeUpdateDNSConfig(" onLinkPropertiesChanged" , dns)
6790 }
6891 }
6992
7093 override fun onLost (network : Network ) {
7194 super .onLost(network)
7295
73- TSLog .d(TAG , " onLost: network ${network} " )
96+ TSLog .d(TAG , " onLost: network $network " )
97+
7498 lock.withLock {
7599 activeNetworks.remove(network)
100+ recomputeDefaultNetworkLocked(" onLost" )
76101 maybeUpdateDNSConfig(" onLost" , dns)
77102 }
78103 }
@@ -101,7 +126,7 @@ object NetworkChangeCallback {
101126 activeNetworks.filter { (_, info) ->
102127 info.caps.hasCapability(NetworkCapabilities .NET_CAPABILITY_INTERNET ) &&
103128 info.caps.hasCapability(NetworkCapabilities .NET_CAPABILITY_NOT_VPN ) &&
104- info.linkProps.dnsServers.isNotEmpty() == true
129+ info.linkProps.dnsServers.isNotEmpty()
105130 }
106131
107132 // If we have one; just return it; otherwise, prefer networks that are also
@@ -119,49 +144,59 @@ object NetworkChangeCallback {
119144 for ((network, info) in activeNetworks) {
120145 if (info.caps.hasCapability(NetworkCapabilities .NET_CAPABILITY_INTERNET ) &&
121146 info.caps.hasCapability(NetworkCapabilities .NET_CAPABILITY_NOT_VPN )) {
122- Log .w(
123- TAG ,
124- " no networks available that also have DNS servers set; falling back to first network ${network} " )
147+ Log .w(TAG , " no networks with DNS; falling back to first network $network " )
125148 return network
126149 }
127150 }
128151
129152 // Otherwise, return nothing; we don't want to return a VPN network since
130153 // it could result in a routing loop, and a non-INTERNET network isn't
131154 // helpful.
132- Log .w(TAG , " no networks available to pick a default network" )
155+ Log .w(TAG , " no networks available to pick default network" )
133156 return null
134157 }
135158
159+ // Update cached default network + log interface name.
160+ private fun recomputeDefaultNetworkLocked (why : String ) {
161+ val newNetwork = pickDefaultNetwork()
162+ cachedDefaultNetwork = newNetwork
163+
164+ val info = if (newNetwork != null ) activeNetworks[newNetwork] else null
165+ cachedDefaultNetworkInfo = info
166+ cachedDefaultInterfaceName = info?.linkProps?.interfaceName
167+
168+ TSLog .d(
169+ TAG , " $why : cachedDefaultNetwork=$newNetwork iface=${cachedDefaultInterfaceName ? : " none" } " )
170+ }
171+
136172 // maybeUpdateDNSConfig will maybe update our DNS configuration based on the
137173 // current set of active Networks.
138174 private fun maybeUpdateDNSConfig (why : String , dns : DnsConfig ) {
139- val defaultNetwork = pickDefaultNetwork()
175+ val defaultNetwork = cachedDefaultNetwork
140176 if (defaultNetwork == null ) {
141- TSLog .d(TAG , " ${ why} : no default network available; not updating DNS config " )
177+ TSLog .d(TAG , " $why : no default network available; not updating DNS" )
142178 return
143179 }
144- val info = activeNetworks[defaultNetwork]
180+
181+ val info = cachedDefaultNetworkInfo
145182 if (info == null ) {
146- Log .w(
147- TAG ,
148- " ${why} : [unexpected] no info available for default network; not updating DNS config" )
183+ Log .w(TAG , " $why : no info for default network; not updating DNS" )
149184 return
150185 }
151186
152187 val sb = StringBuilder ()
153188 for (ip in info.linkProps.dnsServers) {
154189 sb.append(ip.hostAddress).append(" " )
155190 }
191+
156192 val searchDomains: String? = info.linkProps.domains
157193 if (searchDomains != null ) {
158194 sb.append(" \n " )
159195 sb.append(searchDomains)
160196 }
197+
161198 if (dns.updateDNSFromNetwork(sb.toString())) {
162- TSLog .d(
163- TAG ,
164- " ${why} : updated DNS config for network ${defaultNetwork} (${info.linkProps.interfaceName} )" )
199+ TSLog .d(TAG , " $why : updated DNS config for iface=${info.linkProps.interfaceName} " )
165200 Libtailscale .onDNSConfigChanged(info.linkProps.interfaceName)
166201 }
167202 }
0 commit comments