Skip to content

Commit f0dcec6

Browse files
authored
Merge pull request #21 from tailscale/getinterfaces
cmd/tailscale: implement getInterfaces + SDK 30
2 parents d0b4a09 + 02a6ae0 commit f0dcec6

File tree

5 files changed

+147
-2
lines changed

5 files changed

+147
-2
lines changed

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ android {
2424
compileSdkVersion 30
2525
defaultConfig {
2626
minSdkVersion 22
27-
targetSdkVersion 29
27+
targetSdkVersion 30
2828
versionCode 69
2929
versionName "1.15.210-tbabd163aa-g9b52c6b357b"
3030
}

android/src/main/java/com/tailscale/ipn/App.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,18 @@
3939
import java.io.File;
4040
import java.io.FileOutputStream;
4141

42+
import java.lang.StringBuilder;
43+
44+
import java.net.InetAddress;
45+
import java.net.InterfaceAddress;
46+
import java.net.NetworkInterface;
47+
4248
import java.security.GeneralSecurityException;
4349

50+
import java.util.ArrayList;
51+
import java.util.Collections;
52+
import java.util.List;
53+
4454
import androidx.core.app.NotificationCompat;
4555
import androidx.core.app.NotificationManagerCompat;
4656

@@ -312,4 +322,54 @@ private void createNotificationChannel(String id, String name, int importance) {
312322
private static native void onConnectivityChanged(boolean connected);
313323
static native void onShareIntent(int nfiles, int[] types, String[] mimes, String[] items, String[] names, long[] sizes);
314324
static native void onWriteStorageGranted();
325+
326+
// Returns details of the interfaces in the system, encoded as a single string for ease
327+
// of JNI transfer over to the Go environment.
328+
//
329+
// Example:
330+
// rmnet_data0 10 2000 true false false false false | fe80::4059:dc16:7ed3:9c6e%rmnet_data0/64
331+
// dummy0 3 1500 true false false false false | fe80::1450:5cff:fe13:f891%dummy0/64
332+
// wlan0 30 1500 true true false false true | fe80::2f60:2c82:4163:8389%wlan0/64 10.1.10.131/24
333+
// r_rmnet_data0 21 1500 true false false false false | fe80::9318:6093:d1ad:ba7f%r_rmnet_data0/64
334+
// rmnet_data2 12 1500 true false false false false | fe80::3c8c:44dc:46a9:9907%rmnet_data2/64
335+
// r_rmnet_data1 22 1500 true false false false false | fe80::b6cd:5cb0:8ae6:fe92%r_rmnet_data1/64
336+
// rmnet_data1 11 1500 true false false false false | fe80::51f2:ee00:edce:d68b%rmnet_data1/64
337+
// lo 1 65536 true false true false false | ::1/128 127.0.0.1/8
338+
// v4-rmnet_data2 68 1472 true true false true true | 192.0.0.4/32
339+
//
340+
// Where the fields are:
341+
// name ifindex mtu isUp hasBroadcast isLoopback isPointToPoint hasMulticast | ip1/N ip2/N ip3/N;
342+
String getInterfacesAsString() {
343+
List<NetworkInterface> interfaces;
344+
try {
345+
interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
346+
} catch (Exception e) {
347+
return "";
348+
}
349+
350+
StringBuilder sb = new StringBuilder("");
351+
for (NetworkInterface nif : interfaces) {
352+
try {
353+
// Android doesn't have a supportsBroadcast() but the Go net.Interface wants
354+
// one, so we say the interface has broadcast if it has multicast.
355+
sb.append(String.format("%s %d %d %b %b %b %b %b |", nif.getName(),
356+
nif.getIndex(), nif.getMTU(), nif.isUp(), nif.supportsMulticast(),
357+
nif.isLoopback(), nif.isPointToPoint(), nif.supportsMulticast()));
358+
359+
for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
360+
// InterfaceAddress == hostname + "/" + IP
361+
String[] parts = ia.toString().split("/", 0);
362+
if (parts.length > 1) {
363+
sb.append(String.format("%s/%d ", parts[1], ia.getNetworkPrefixLength()));
364+
}
365+
}
366+
} catch (Exception e) {
367+
// TODO(dgentry) should log the exception not silently suppress it.
368+
continue;
369+
}
370+
sb.append("\n");
371+
}
372+
373+
return sb.toString();
374+
}
315375
}

cmd/tailscale/main.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"io"
1313
"log"
1414
"mime"
15+
"net"
1516
"net/http"
1617
"net/url"
1718
"os"
@@ -33,6 +34,7 @@ import (
3334
"tailscale.com/ipn"
3435
"tailscale.com/ipn/ipnlocal"
3536
"tailscale.com/net/dns"
37+
"tailscale.com/net/interfaces"
3638
"tailscale.com/net/netns"
3739
"tailscale.com/paths"
3840
"tailscale.com/tailcfg"
@@ -218,6 +220,7 @@ func main() {
218220
fatalErr(err)
219221
}
220222
a.store = newStateStore(a.jvm, a.appCtx)
223+
interfaces.RegisterInterfaceGetter(a.getInterfaces)
221224
go func() {
222225
if err := a.runBackend(); err != nil {
223226
fatalErr(err)
@@ -1251,6 +1254,86 @@ func (a *App) contextForView(view jni.Object) jni.Object {
12511254
return ctx
12521255
}
12531256

1257+
// Report interfaces in the device in net.Interface format.
1258+
func (a *App) getInterfaces() ([]interfaces.Interface, error) {
1259+
var ifaceString string
1260+
err := jni.Do(a.jvm, func(env *jni.Env) error {
1261+
cls := jni.GetObjectClass(env, a.appCtx)
1262+
m := jni.GetMethodID(env, cls, "getInterfacesAsString", "()Ljava/lang/String;")
1263+
n, err := jni.CallObjectMethod(env, a.appCtx, m)
1264+
ifaceString = jni.GoString(env, jni.String(n))
1265+
return err
1266+
1267+
})
1268+
var ifaces []interfaces.Interface
1269+
if err != nil {
1270+
return ifaces, err
1271+
}
1272+
1273+
for _, iface := range strings.Split(ifaceString, "\n") {
1274+
// Example of the strings we're processing:
1275+
// wlan0 30 1500 true true false false true | fe80::2f60:2c82:4163:8389%wlan0/64 10.1.10.131/24
1276+
// r_rmnet_data0 21 1500 true false false false false | fe80::9318:6093:d1ad:ba7f%r_rmnet_data0/64
1277+
// mnet_data2 12 1500 true false false false false | fe80::3c8c:44dc:46a9:9907%rmnet_data2/64
1278+
1279+
if strings.TrimSpace(iface) == "" {
1280+
continue
1281+
}
1282+
1283+
fields := strings.Split(iface, "|")
1284+
if len(fields) != 2 {
1285+
log.Printf("getInterfaces: unable to split %q", iface)
1286+
continue
1287+
}
1288+
1289+
var name string
1290+
var index, mtu int
1291+
var up, broadcast, loopback, pointToPoint, multicast bool
1292+
_, err := fmt.Sscanf(fields[0], "%s %d %d %t %t %t %t %t",
1293+
&name, &index, &mtu, &up, &broadcast, &loopback, &pointToPoint, &multicast)
1294+
if err != nil {
1295+
log.Printf("getInterfaces: unable to parse %q: %v", iface, err)
1296+
continue
1297+
}
1298+
1299+
newIf := interfaces.Interface{
1300+
Interface: &net.Interface{
1301+
Name: name,
1302+
Index: index,
1303+
MTU: mtu,
1304+
},
1305+
AltAddrs: []net.Addr{}, // non-nil to avoid Go using netlink
1306+
}
1307+
if up {
1308+
newIf.Flags |= net.FlagUp
1309+
}
1310+
if broadcast {
1311+
newIf.Flags |= net.FlagBroadcast
1312+
}
1313+
if loopback {
1314+
newIf.Flags |= net.FlagLoopback
1315+
}
1316+
if pointToPoint {
1317+
newIf.Flags |= net.FlagPointToPoint
1318+
}
1319+
if multicast {
1320+
newIf.Flags |= net.FlagMulticast
1321+
}
1322+
1323+
addrs := strings.Trim(fields[1], " \n")
1324+
for _, addr := range strings.Split(addrs, " ") {
1325+
ip, err := netaddr.ParseIPPrefix(addr)
1326+
if err == nil {
1327+
newIf.AltAddrs = append(newIf.AltAddrs, ip.IPNet())
1328+
}
1329+
}
1330+
1331+
ifaces = append(ifaces, newIf)
1332+
}
1333+
1334+
return ifaces, nil
1335+
}
1336+
12541337
func fatalErr(err error) {
12551338
// TODO: expose in UI.
12561339
log.Printf("fatal error: %v", err)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
1111
golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c
1212
inet.af/netaddr v0.0.0-20210721214506-ce7a8ad02cc1
13-
tailscale.com v1.1.1-0.20211005220235-67e5fabdbdba
13+
tailscale.com v1.1.1-0.20211008004653-1f506d23514a
1414
)
1515

1616
require (

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,3 +1013,5 @@ software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237/go.mod h1:
10131013
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
10141014
tailscale.com v1.1.1-0.20211005220235-67e5fabdbdba h1:szGUzQIThyKAKJDxXuL5lgUmEU4hzWUW+noas/MYD8M=
10151015
tailscale.com v1.1.1-0.20211005220235-67e5fabdbdba/go.mod h1:2ordBMiDa1Gx6r8ci5EtSS3Wl0RanS46+l8T2vwqUtI=
1016+
tailscale.com v1.1.1-0.20211008004653-1f506d23514a h1:htyjSQ+1+YeW+aapSMq8hlCPk+dJlkbWvIuWIVgbWP4=
1017+
tailscale.com v1.1.1-0.20211008004653-1f506d23514a/go.mod h1:+dISSbd6iiY6F6/dRGQRgshjYXMkhMzVvRd9En8RLfQ=

0 commit comments

Comments
 (0)