@ -12,6 +12,7 @@ import (
"io"
"log"
"mime"
"net"
"net/http"
"net/url"
"os"
@ -33,6 +34,7 @@ import (
"tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/net/dns"
"tailscale.com/net/interfaces"
"tailscale.com/net/netns"
"tailscale.com/paths"
"tailscale.com/tailcfg"
@ -218,6 +220,7 @@ func main() {
fatalErr ( err )
}
a . store = newStateStore ( a . jvm , a . appCtx )
interfaces . RegisterInterfaceGetter ( a . getInterfaces )
go func ( ) {
if err := a . runBackend ( ) ; err != nil {
fatalErr ( err )
@ -1251,6 +1254,86 @@ func (a *App) contextForView(view jni.Object) jni.Object {
return ctx
}
// Report interfaces in the device in net.Interface format.
func ( a * App ) getInterfaces ( ) ( [ ] interfaces . Interface , error ) {
var ifaceString string
err := jni . Do ( a . jvm , func ( env * jni . Env ) error {
cls := jni . GetObjectClass ( env , a . appCtx )
m := jni . GetMethodID ( env , cls , "getInterfacesAsString" , "()Ljava/lang/String;" )
n , err := jni . CallObjectMethod ( env , a . appCtx , m )
ifaceString = jni . GoString ( env , jni . String ( n ) )
return err
} )
var ifaces [ ] interfaces . Interface
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 := interfaces . 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
}
func fatalErr ( err error ) {
// TODO: expose in UI.
log . Printf ( "fatal error: %v" , err )