| 1220 | } |
| 1221 | |
| 1222 | func (e *userspaceEngine) linkChange(delta *netmon.ChangeDelta) { |
| 1223 | |
| 1224 | up := delta.AnyInterfaceUp() |
| 1225 | if !up { |
| 1226 | e.logf("LinkChange: all links down; pausing: %v", delta.StateDesc()) |
| 1227 | } else if delta.RebindLikelyRequired { |
| 1228 | e.logf("LinkChange: major, rebinding: %v", delta.StateDesc()) |
| 1229 | } else { |
| 1230 | e.logf("[v1] LinkChange: minor") |
| 1231 | } |
| 1232 | |
| 1233 | e.health.SetAnyInterfaceUp(up) |
| 1234 | if !up || delta.RebindLikelyRequired { |
| 1235 | if err := e.dns.FlushCaches(); err != nil { |
| 1236 | e.logf("wgengine: dns flush failed after major link change: %v", err) |
| 1237 | } |
| 1238 | } |
| 1239 | |
| 1240 | // Hacky workaround for Unix DNS issue 2458: on |
| 1241 | // suspend/resume or whenever NetworkManager is started, it |
| 1242 | // nukes all systemd-resolved configs. So reapply our DNS |
| 1243 | // config on major link change. |
| 1244 | // |
| 1245 | // On Darwin (netext), we reapply the DNS config when the interface flaps |
| 1246 | // because the change in interface can potentially change the nameservers |
| 1247 | // for the forwarder. On Darwin netext clients, magicDNS is ~always the default |
| 1248 | // resolver so having no nameserver to forward queries to (or one on a network we |
| 1249 | // are not currently on) breaks DNS resolution system-wide. There are notable |
| 1250 | // timing issues here with Darwin's network stack. It is not guaranteed that |
| 1251 | // the forward resolver will be available immediately after the interface |
| 1252 | // comes up. We leave it to the network extension to also poke magicDNS directly |
| 1253 | // via [dns.Manager.RecompileDNSConfig] when it detects any change in the |
| 1254 | // nameservers. |
| 1255 | // |
| 1256 | // TODO: On Android, Darwin-tailscaled, and openbsd, why do we need this? |
| 1257 | if delta.RebindLikelyRequired && up { |
| 1258 | switch runtime.GOOS { |
| 1259 | case "linux", "android", "ios", "darwin", "openbsd": |
| 1260 | e.wgLock.Lock() |
| 1261 | dnsCfg := e.lastDNSConfig |
| 1262 | e.wgLock.Unlock() |
| 1263 | if dnsCfg.Valid() { |
| 1264 | if err := e.dns.Set(*dnsCfg.AsStruct()); err != nil { |
| 1265 | e.logf("wgengine: error setting DNS config after major link change: %v", err) |
| 1266 | } else if err := e.reconfigureVPNIfNecessary(); err != nil { |
| 1267 | e.logf("wgengine: error reconfiguring VPN after major link change: %v", err) |
| 1268 | } else { |
| 1269 | e.logf("wgengine: set DNS config again after major link change") |
| 1270 | } |
| 1271 | } |
| 1272 | } |
| 1273 | } |
| 1274 | |
| 1275 | e.magicConn.SetNetworkUp(up) |
| 1276 | |
| 1277 | why := "link-change-minor" |
| 1278 | if delta.RebindLikelyRequired { |
| 1279 | why = "link-change-major" |