|
@@ -5,7 +5,6 @@ package netmon
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
"bytes"
|
|
"bytes"
|
|
|
- "errors"
|
|
|
|
|
"log"
|
|
"log"
|
|
|
"net/netip"
|
|
"net/netip"
|
|
|
"os/exec"
|
|
"os/exec"
|
|
@@ -15,7 +14,7 @@ import (
|
|
|
"golang.org/x/sys/unix"
|
|
"golang.org/x/sys/unix"
|
|
|
"tailscale.com/net/netaddr"
|
|
"tailscale.com/net/netaddr"
|
|
|
"tailscale.com/syncs"
|
|
"tailscale.com/syncs"
|
|
|
- "tailscale.com/util/lineread"
|
|
|
|
|
|
|
+ "tailscale.com/util/lineiter"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
var (
|
|
@@ -34,11 +33,6 @@ func init() {
|
|
|
|
|
|
|
|
var procNetRouteErr atomic.Bool
|
|
var procNetRouteErr atomic.Bool
|
|
|
|
|
|
|
|
-// errStopReading is a sentinel error value used internally by
|
|
|
|
|
-// lineread.File callers to stop reading. It doesn't escape to
|
|
|
|
|
-// callers/users.
|
|
|
|
|
-var errStopReading = errors.New("stop reading")
|
|
|
|
|
-
|
|
|
|
|
/*
|
|
/*
|
|
|
Parse 10.0.0.1 out of:
|
|
Parse 10.0.0.1 out of:
|
|
|
|
|
|
|
@@ -54,44 +48,42 @@ func likelyHomeRouterIPAndroid() (ret netip.Addr, myIP netip.Addr, ok bool) {
|
|
|
}
|
|
}
|
|
|
lineNum := 0
|
|
lineNum := 0
|
|
|
var f []mem.RO
|
|
var f []mem.RO
|
|
|
- err := lineread.File(procNetRoutePath, func(line []byte) error {
|
|
|
|
|
|
|
+ for lr := range lineiter.File(procNetRoutePath) {
|
|
|
|
|
+ line, err := lr.Value()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ procNetRouteErr.Store(true)
|
|
|
|
|
+ return likelyHomeRouterIP()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
lineNum++
|
|
lineNum++
|
|
|
if lineNum == 1 {
|
|
if lineNum == 1 {
|
|
|
// Skip header line.
|
|
// Skip header line.
|
|
|
- return nil
|
|
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
if lineNum > maxProcNetRouteRead {
|
|
if lineNum > maxProcNetRouteRead {
|
|
|
- return errStopReading
|
|
|
|
|
|
|
+ break
|
|
|
}
|
|
}
|
|
|
f = mem.AppendFields(f[:0], mem.B(line))
|
|
f = mem.AppendFields(f[:0], mem.B(line))
|
|
|
if len(f) < 4 {
|
|
if len(f) < 4 {
|
|
|
- return nil
|
|
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
gwHex, flagsHex := f[2], f[3]
|
|
gwHex, flagsHex := f[2], f[3]
|
|
|
flags, err := mem.ParseUint(flagsHex, 16, 16)
|
|
flags, err := mem.ParseUint(flagsHex, 16, 16)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return nil // ignore error, skip line and keep going
|
|
|
|
|
|
|
+ continue // ignore error, skip line and keep going
|
|
|
}
|
|
}
|
|
|
if flags&(unix.RTF_UP|unix.RTF_GATEWAY) != unix.RTF_UP|unix.RTF_GATEWAY {
|
|
if flags&(unix.RTF_UP|unix.RTF_GATEWAY) != unix.RTF_UP|unix.RTF_GATEWAY {
|
|
|
- return nil
|
|
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
ipu32, err := mem.ParseUint(gwHex, 16, 32)
|
|
ipu32, err := mem.ParseUint(gwHex, 16, 32)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return nil // ignore error, skip line and keep going
|
|
|
|
|
|
|
+ continue // ignore error, skip line and keep going
|
|
|
}
|
|
}
|
|
|
ip := netaddr.IPv4(byte(ipu32), byte(ipu32>>8), byte(ipu32>>16), byte(ipu32>>24))
|
|
ip := netaddr.IPv4(byte(ipu32), byte(ipu32>>8), byte(ipu32>>16), byte(ipu32>>24))
|
|
|
if ip.IsPrivate() {
|
|
if ip.IsPrivate() {
|
|
|
ret = ip
|
|
ret = ip
|
|
|
- return errStopReading
|
|
|
|
|
|
|
+ break
|
|
|
}
|
|
}
|
|
|
- return nil
|
|
|
|
|
- })
|
|
|
|
|
- if errors.Is(err, errStopReading) {
|
|
|
|
|
- err = nil
|
|
|
|
|
- }
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- procNetRouteErr.Store(true)
|
|
|
|
|
- return likelyHomeRouterIP()
|
|
|
|
|
}
|
|
}
|
|
|
if ret.IsValid() {
|
|
if ret.IsValid() {
|
|
|
// Try to get the local IP of the interface associated with
|
|
// Try to get the local IP of the interface associated with
|
|
@@ -144,23 +136,26 @@ func likelyHomeRouterIPHelper() (ret netip.Addr, _ netip.Addr, ok bool) {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
// Search for line like "default via 10.0.2.2 dev radio0 table 1016 proto static mtu 1500 "
|
|
// Search for line like "default via 10.0.2.2 dev radio0 table 1016 proto static mtu 1500 "
|
|
|
- lineread.Reader(out, func(line []byte) error {
|
|
|
|
|
|
|
+ for lr := range lineiter.Reader(out) {
|
|
|
|
|
+ line, err := lr.Value()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
const pfx = "default via "
|
|
const pfx = "default via "
|
|
|
if !mem.HasPrefix(mem.B(line), mem.S(pfx)) {
|
|
if !mem.HasPrefix(mem.B(line), mem.S(pfx)) {
|
|
|
- return nil
|
|
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
line = line[len(pfx):]
|
|
line = line[len(pfx):]
|
|
|
sp := bytes.IndexByte(line, ' ')
|
|
sp := bytes.IndexByte(line, ' ')
|
|
|
if sp == -1 {
|
|
if sp == -1 {
|
|
|
- return nil
|
|
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
ipb := line[:sp]
|
|
ipb := line[:sp]
|
|
|
if ip, err := netip.ParseAddr(string(ipb)); err == nil && ip.Is4() {
|
|
if ip, err := netip.ParseAddr(string(ipb)); err == nil && ip.Is4() {
|
|
|
ret = ip
|
|
ret = ip
|
|
|
log.Printf("interfaces: found Android default route %v", ip)
|
|
log.Printf("interfaces: found Android default route %v", ip)
|
|
|
}
|
|
}
|
|
|
- return nil
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ }
|
|
|
cmd.Process.Kill()
|
|
cmd.Process.Kill()
|
|
|
cmd.Wait()
|
|
cmd.Wait()
|
|
|
return ret, netip.Addr{}, ret.IsValid()
|
|
return ret, netip.Addr{}, ret.IsValid()
|