Reviving NAT-PMP in v2.x on Android 14+

Hi,

We’ve recently fixed the interface address discovery on Android by using the “anet” go library. Now there’s one problem left with the NAT-PMP feature of SyncthingNative. Android 14+ deny native code to read /proc/net/route. This approach is used by SyncthingNative in “lib/pmp.go:34” using the go lib “GitHub - jackpal/gateway: A golang library for discovering the address of a LAN gateway.”:

ip, err = gateway.DiscoverGateway()

I’ve tried to fix it in jackpal/gateway but could not find a solution. Multiple approaches don’t work from Android 14+, e.g. “ip route show”, “ip route get 8.8.8.8”, “cat /proc/net/…” or fake-binding a socket and reading out the info.

Android 14+ only lets java code get the gateway IPv4 address which is used in SynchtingNative’s NAT-PMP feature.

So I’ve added the java code to the wrapper, got the router IP address and feeded it to SynchtingNative by setting the env var “ANDROID_NET_GATEWAY_IPV4”.

This revives the NAT feature:

[Z36WU] INFO: Detected 1 NAT service

Function used based on the v2-main of SyncthingNative was:

func Discover(ctx context.Context, renewal, timeout time.Duration) []nat.Device {
	var ip net.IP
	err := svcutil.CallWithContext(ctx, func() error {
		var err error
		ip, err = gateway.DiscoverGateway()
		if err != nil {
			// Fails on Android 14+ due to permission denied error when reading
			// /proc/net/route. The wrapper may give a hint then because it is
			// able to discover the gateway from java code.
			if v := os.Getenv("ANDROID_NET_GATEWAY_IPV4"); v != "" {
				ip = net.ParseIP(v)
				l.Debugln("Using gateway IP hint from env var ANDROID_NET_GATEWAY_IPV4", ip)
				return nil
			}
		}
		return err
	})
	if err != nil {
		l.Debugln("Failed to discover gateway", err)
		return nil
	}
	if ip == nil || ip.IsUnspecified() {
		return nil
	}
(...)

Would you be fine with that workaround if I open a PR to Syncthing’s go code? It’s again a hacky workaround, but I personally think something lightweight is better than disabling the NAT feature completely for Android 14+.

What’s your opinion on this?

Should I also try to do the more-complicated-looking JNI binding from go code to java code?

More refs/reading on this:

It’s far from the worst thing I’ve seen. I’d hide it in a wrapper netutil.Gateway.

Absolutely not :sweat_smile:

2 Likes

You can just put the function in the existing netutil.go

1 Like

I’m not sure how well this will work, given the value is set once, and phones are usually the things that migrate from network to network.

Perhaps a better option would be to specify a file you write it to and update it in the file periodically and get syncthing to read the file?

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.