@@ -17,11 +17,30 @@ object NetworkChangeCallback {
1717
1818 private const val TAG = " NetworkChangeCallback"
1919
20- private data class NetworkInfo (var caps : NetworkCapabilities , var linkProps : LinkProperties )
20+ private data class NetworkInfo (
21+ var caps : NetworkCapabilities ,
22+ var linkProps : LinkProperties
23+ )
2124
2225 private val lock = ReentrantLock ()
2326
24- private val activeNetworks = mutableMapOf<Network , NetworkInfo >() // keyed by Network
27+ // All currently active non-VPN networks we know about.
28+ private val activeNetworks = mutableMapOf<Network , NetworkInfo >()
29+
30+ // Cached chosen default network for outbound sockets.
31+ @Volatile
32+ var cachedDefaultNetwork: Network ? = null
33+ private set
34+
35+ // Cached info for the chosen default network.
36+ @Volatile
37+ var cachedDefaultNetworkInfo: NetworkInfo ? = null
38+ private set
39+
40+ // Convenience: cached interface name for logging.
41+ @Volatile
42+ var cachedDefaultInterfaceName: String? = null
43+ private set
2544
2645 // monitorDnsChanges sets up a network callback to monitor changes to the
2746 // system's network state and update the DNS configuration when interfaces
@@ -45,34 +64,45 @@ object NetworkChangeCallback {
4564 connectivityManager.registerNetworkCallback(
4665 networkConnectivityRequest,
4766 object : ConnectivityManager .NetworkCallback () {
67+
4868 override fun onAvailable (network : Network ) {
4969 super .onAvailable(network)
5070
51- TSLog .d(TAG , " onAvailable: network ${network} " )
71+ TSLog .d(TAG , " onAvailable: network $network " )
72+
5273 lock.withLock {
5374 activeNetworks[network] = NetworkInfo (NetworkCapabilities (), LinkProperties ())
75+ recomputeDefaultNetworkLocked(" onAvailable" )
5476 }
5577 }
5678
5779 override fun onCapabilitiesChanged (network : Network , capabilities : NetworkCapabilities ) {
5880 super .onCapabilitiesChanged(network, capabilities)
59- lock.withLock { activeNetworks[network]?.caps = capabilities }
81+
82+ lock.withLock {
83+ activeNetworks[network]?.caps = capabilities
84+ recomputeDefaultNetworkLocked(" onCapabilitiesChanged" )
85+ }
6086 }
6187
6288 override fun onLinkPropertiesChanged (network : Network , linkProperties : LinkProperties ) {
6389 super .onLinkPropertiesChanged(network, linkProperties)
90+
6491 lock.withLock {
6592 activeNetworks[network]?.linkProps = linkProperties
93+ recomputeDefaultNetworkLocked(" onLinkPropertiesChanged" )
6694 maybeUpdateDNSConfig(" onLinkPropertiesChanged" , dns)
6795 }
6896 }
6997
7098 override fun onLost (network : Network ) {
7199 super .onLost(network)
72100
73- TSLog .d(TAG , " onLost: network ${network} " )
101+ TSLog .d(TAG , " onLost: network $network " )
102+
74103 lock.withLock {
75104 activeNetworks.remove(network)
105+ recomputeDefaultNetworkLocked(" onLost" )
76106 maybeUpdateDNSConfig(" onLost" , dns)
77107 }
78108 }
@@ -101,14 +131,14 @@ object NetworkChangeCallback {
101131 activeNetworks.filter { (_, info) ->
102132 info.caps.hasCapability(NetworkCapabilities .NET_CAPABILITY_INTERNET ) &&
103133 info.caps.hasCapability(NetworkCapabilities .NET_CAPABILITY_NOT_VPN ) &&
104- info.linkProps.dnsServers.isNotEmpty() == true
134+ info.linkProps.dnsServers.isNotEmpty()
105135 }
106136
107137 // If we have one; just return it; otherwise, prefer networks that are also
108138 // not metered (i.e. cell modems).
109- val nonMeteredNetwork = pickNonMetered(networks)
110- if (nonMeteredNetwork != null ) {
111- return nonMeteredNetwork
139+ val nonMetered = pickNonMetered(networks)
140+ if (nonMetered != null ) {
141+ return nonMetered
112142 }
113143
114144 // Okay, less good; just return the first network that has the INTERNET and
@@ -121,47 +151,63 @@ object NetworkChangeCallback {
121151 info.caps.hasCapability(NetworkCapabilities .NET_CAPABILITY_NOT_VPN )) {
122152 Log .w(
123153 TAG ,
124- " no networks available that also have DNS servers set ; falling back to first network ${ network} " )
154+ " no networks with DNS; falling back to first network $network " )
125155 return network
126156 }
127157 }
128158
129159 // Otherwise, return nothing; we don't want to return a VPN network since
130160 // it could result in a routing loop, and a non-INTERNET network isn't
131161 // helpful.
132- Log .w(TAG , " no networks available to pick a default network" )
162+ Log .w(TAG , " no networks available to pick default network" )
133163 return null
134164 }
135165
136- // maybeUpdateDNSConfig will maybe update our DNS configuration based on the
137- // current set of active Networks.
166+ // Update cached default network + log interface name.
167+ private fun recomputeDefaultNetworkLocked (why : String ) {
168+ val newNetwork = pickDefaultNetwork()
169+ cachedDefaultNetwork = newNetwork
170+
171+ val info = if (newNetwork != null ) activeNetworks[newNetwork] else null
172+ cachedDefaultNetworkInfo = info
173+ cachedDefaultInterfaceName = info?.linkProps?.interfaceName
174+
175+ TSLog .d(
176+ TAG ,
177+ " $why : cachedDefaultNetwork=$newNetwork iface=${cachedDefaultInterfaceName ? : " none" } " )
178+ }
179+
180+ /* *
181+ * Update DNS config when underlying network changes.
182+ */
138183 private fun maybeUpdateDNSConfig (why : String , dns : DnsConfig ) {
139- val defaultNetwork = pickDefaultNetwork()
184+ val defaultNetwork = cachedDefaultNetwork
140185 if (defaultNetwork == null ) {
141- TSLog .d(TAG , " ${ why} : no default network available; not updating DNS config " )
186+ TSLog .d(TAG , " $why : no default network available; not updating DNS" )
142187 return
143188 }
144- val info = activeNetworks[defaultNetwork]
189+
190+ val info = cachedDefaultNetworkInfo
145191 if (info == null ) {
146- Log .w(
147- TAG ,
148- " ${why} : [unexpected] no info available for default network; not updating DNS config" )
192+ Log .w(TAG , " $why : no info for default network; not updating DNS" )
149193 return
150194 }
151195
152196 val sb = StringBuilder ()
153197 for (ip in info.linkProps.dnsServers) {
154198 sb.append(ip.hostAddress).append(" " )
155199 }
200+
156201 val searchDomains: String? = info.linkProps.domains
157202 if (searchDomains != null ) {
158203 sb.append(" \n " )
159204 sb.append(searchDomains)
160205 }
206+
161207 if (dns.updateDNSFromNetwork(sb.toString())) {
162208 TSLog .d(
163209 TAG ,
164- " ${ why} : updated DNS config for network ${defaultNetwork} ( ${ info.linkProps.interfaceName}) " )
210+ " $why : updated DNS config for iface= ${ info.linkProps.interfaceName}" )
165211 Libtailscale .onDNSConfigChanged(info.linkProps.interfaceName)
166212 }
167213 }
0 commit comments