// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause package libtailscale import ( "errors" "fmt" "log" "net" "net/netip" "reflect" "runtime/debug" "strings" "github.com/tailscale/wireguard-go/tun" "golang.org/x/sys/unix" "inet.af/netaddr" "tailscale.com/net/dns" "tailscale.com/net/netmon" "tailscale.com/util/dnsname" "tailscale.com/wgengine/router" ) // errVPNNotPrepared is used when VPNService.Builder.establish returns // null, either because the VPNService is not yet prepared or because // VPN status was revoked. var errVPNNotPrepared = errors.New("VPN service not prepared or was revoked") // errMultipleUsers is used when we get a "INTERACT_ACROSS_USERS" error, which // happens due to a bug in Android. See: // // https://github.com/tailscale/tailscale/issues/2180 var errMultipleUsers = errors.New("VPN cannot be created on this device due to an Android bug with multiple users") // Report interfaces in the device in net.Interface format. func (a *App) getInterfaces() ([]netmon.Interface, error) { var ifaces []netmon.Interface ifaceString, err := a.appCtx.GetInterfacesAsString() if err != nil { return ifaces, err } for _, iface := range strings.Split(ifaceString, "\n") { // Example of the strings we're processing: // wlan0 30 1500 true true false false true | fe80::2f60:2c82:4163:8389%wlan0/64 10.1.10.131/24 // r_rmnet_data0 21 1500 true false false false false | fe80::9318:6093:d1ad:ba7f%r_rmnet_data0/64 // mnet_data2 12 1500 true false false false false | fe80::3c8c:44dc:46a9:9907%rmnet_data2/64 if strings.TrimSpace(iface) == "" { continue } fields := strings.Split(iface, "|") if len(fields) != 2 { log.Printf("getInterfaces: unable to split %q", iface) continue } var name string var index, mtu int var up, broadcast, loopback, pointToPoint, multicast bool _, err := fmt.Sscanf(fields[0], "%s %d %d %t %t %t %t %t", &name, &index, &mtu, &up, &broadcast, &loopback, &pointToPoint, &multicast) if err != nil { log.Printf("getInterfaces: unable to parse %q: %v", iface, err) continue } newIf := netmon.Interface{ Interface: &net.Interface{ Name: name, Index: index, MTU: mtu, }, AltAddrs: []net.Addr{}, // non-nil to avoid Go using netlink } if up { newIf.Flags |= net.FlagUp } if broadcast { newIf.Flags |= net.FlagBroadcast } if loopback { newIf.Flags |= net.FlagLoopback } if pointToPoint { newIf.Flags |= net.FlagPointToPoint } if multicast { newIf.Flags |= net.FlagMulticast } addrs := strings.Trim(fields[1], " \n") for _, addr := range strings.Split(addrs, " ") { ip, err := netaddr.ParseIPPrefix(addr) if err == nil { newIf.AltAddrs = append(newIf.AltAddrs, ip.IPNet()) } } ifaces = append(ifaces, newIf) } return ifaces, nil } // googleDNSServers are used on ChromeOS, where an empty VpnBuilder DNS setting results // in erasing the platform DNS servers. The developer docs say this is not supposed to happen, // but nonetheless it does. var googleDNSServers = []netip.Addr{ netip.MustParseAddr("8.8.8.8"), netip.MustParseAddr("8.8.4.4"), netip.MustParseAddr("2001:4860:4860::8888"), netip.MustParseAddr("2001:4860:4860::8844"), } func (b *backend) updateTUN(service IPNService, rcfg *router.Config, dcfg *dns.OSConfig) error { if reflect.DeepEqual(rcfg, b.lastCfg) && reflect.DeepEqual(dcfg, b.lastDNSCfg) { b.logger.Logf("updateTUN: no change to Routes or DNS, ignore") return nil } b.logger.Logf("updateTUN: changed") defer b.logger.Logf("updateTUN: finished") // Close previous tunnel(s). // This is necessary for ChromeOS, native Android devices // seem to handle seamless handover between tunnels correctly. // // TODO(eliasnaur): If seamless handover becomes a desirable feature, skip // the closing on ChromeOS. b.logger.Logf("updateTUN: closing old TUNs") b.CloseTUNs() b.logger.Logf("updateTUN: closed old TUNs") if len(rcfg.LocalAddrs) == 0 { return nil } builder := service.NewBuilder() b.logger.Logf("updateTUN: got new builder") if err := builder.SetMTU(defaultMTU); err != nil { return err } b.logger.Logf("updateTUN: set MTU") if dcfg != nil { nameservers := dcfg.Nameservers if b.avoidEmptyDNS && len(nameservers) == 0 { nameservers = googleDNSServers } for _, dns := range nameservers { if err := builder.AddDNSServer(dns.String()); err != nil { return err } } for _, dom := range dcfg.SearchDomains { if err := builder.AddSearchDomain(dom.WithoutTrailingDot()); err != nil { return err } } b.logger.Logf("updateTUN: set nameservers") } for _, route := range rcfg.Routes { // Normalize route address; Builder.addRoute does not accept non-zero masked bits. route = route.Masked() if err := builder.AddRoute(route.Addr().String(), int32(route.Bits())); err != nil { return err } } for _, route := range rcfg.LocalRoutes { addr := route.Addr() if addr.IsLoopback() { continue // Skip the loopback addresses since VpnService throws an exception for those (both IPv4 and IPv6) - see https://android.googlesource.com/platform/frameworks/base/+/c741553/core/java/android/net/VpnService.java#303 } route = route.Masked() if err := builder.ExcludeRoute(route.Addr().String(), int32(route.Bits())); err != nil { return err } } b.logger.Logf("updateTUN: added %d routes", len(rcfg.Routes)) for _, addr := range rcfg.LocalAddrs { if err := builder.AddAddress(addr.Addr().String(), int32(addr.Bits())); err != nil { return err } } b.logger.Logf("updateTUN: added %d local addrs", len(rcfg.LocalAddrs)) parcelFD, err := builder.Establish() if err != nil { if strings.Contains(err.Error(), "INTERACT_ACROSS_USERS") { return errMultipleUsers } return fmt.Errorf("VpnService.Builder.establish: %v", err) } b.logger.Logf("updateTUN: established VPN") if parcelFD == nil { return errVPNNotPrepared } // detachFd. tunFD, err := parcelFD.Detach() if err != nil { return fmt.Errorf("detachFd: %v", err) } b.logger.Logf("updateTUN: detached FD") // Create TUN device. tunDev, _, err := tun.CreateUnmonitoredTUNFromFD(int(tunFD)) if err != nil { unix.Close(int(tunFD)) return err } b.logger.Logf("updateTUN: created TUN device") b.devices.add(tunDev) b.logger.Logf("updateTUN: added TUN device") b.lastCfg = rcfg b.lastDNSCfg = dcfg return nil } // CloseVPN closes any active TUN devices. func (b *backend) CloseTUNs() { b.lastCfg = nil b.devices.Shutdown() } // ifname is the interface name retrieved from LinkProperties on network change. If a network is lost, an empty string is passed in. func (b *backend) NetworkChanged(ifname string) { defer func() { if p := recover(); p != nil { log.Printf("panic in NetworkChanged %s: %s", p, debug.Stack()) panic(p) } }() // Set the interface name and alert the monitor. netmon.UpdateLastKnownDefaultRouteInterface(ifname) if b.sys != nil { if nm, ok := b.sys.NetMon.GetOK(); ok { nm.InjectEvent() } } } func (b *backend) getDNSBaseConfig() (ret dns.OSConfig, _ error) { defer func() { // If we couldn't find any base nameservers, ultimately fall back to // Google's. Normally Tailscale doesn't ever pick a default nameserver // for users but in this case Android's APIs for reading the underlying // DNS config are lacking, and almost all Android phones use Google // services anyway, so it's a reasonable default: it's an ecosystem the // user has selected by having an Android device. if len(ret.Nameservers) == 0 && b.appCtx.IsPlayVersion() { log.Printf("getDNSBaseConfig: none found; falling back to Google public DNS") ret.Nameservers = append(ret.Nameservers, googleDNSServers...) } }() baseConfig := b.getPlatformDNSConfig() lines := strings.Split(baseConfig, "\n") if len(lines) == 0 { return dns.OSConfig{}, nil } config := dns.OSConfig{} addrs := strings.Trim(lines[0], " \n") for _, addr := range strings.Split(addrs, " ") { ip, err := netip.ParseAddr(addr) if err == nil { config.Nameservers = append(config.Nameservers, ip) } } if len(lines) > 1 { for _, s := range strings.Split(strings.Trim(lines[1], " \n"), " ") { domain, err := dnsname.ToFQDN(s) if err != nil { log.Printf("getDNSBaseConfig: unable to parse %q: %v", s, err) continue } config.SearchDomains = append(config.SearchDomains, domain) } } return config, nil } func (b *backend) getPlatformDNSConfig() string { return b.appCtx.GetPlatformDNSConfig() } func (b *backend) setCfg(rcfg *router.Config, dcfg *dns.OSConfig) error { return b.settings(rcfg, dcfg) }