适用于Android的防火墙,带有VpnService.响应已传递,但抛出了SocketTimeoutException

Mak*_*iev 25 sockets networking android firewall udp

我正在使用VpnService为Android实现一个简单的防火墙.我的应用程序类似于ToyVpnService,但它不会将原始IP数据包发送到远程VPN服务器,而远程VPN服务器会将它们转发到目的地.

我的实现在这里:https://bitbucket.org/MaksimDmitriev/norootfirewall/src/006f7c33cd1cd4055f372ed3a88664fe2a4be3dd/src/com/norootfw/NoRootFwService.java?at=unix

我可以在本地完成所有这些转发程序吗?这就是我想要实现的.

我初始化一个TUN设备及其文件描述符:

mInterface = new Builder().setSession(getString(R.string.app_name))
                .addAddress("10.0.2.1", 24)
                .addRoute("0.0.0.0", 1)
                .addRoute("128.0.0.0", 1)
                .establish();

in = new FileInputStream(mInterface.getFileDescriptor());
out = new FileOutputStream(mInterface.getFileDescriptor());
Run Code Online (Sandbox Code Playgroud)

我将0.0.0.0/1和128.0.0.0/1分配给TUN设备,使其比0.0.0.0/0的默认路由更优先.我使用了0.0.0.0/0并遇到了同样的异常,如下所示.

这是一个示例UDP请求.

1).我从TUN设备读取了一个IP数据包.

05-06 00:46:52.749: D/UDPChecksum(31077): Sent == [69, 0, 0, 36, 0, 0, 64, 0, 64, 17, 108, 91, 10, 0, 2, 1, -64, -88, 1, -59, -53, 1, -50, -87, 0, 16, 89, -114, 85, 68, 80, 95, 68, 65, 84, 65]
Run Code Online (Sandbox Code Playgroud)

请在此处考虑IPv4数据包结构. 例如,第一个数字69(二进制的0100 0101)表示IP协议的版本是4(4个高位).4个低位代表32位字的Internet标头长度(IHL).

2).然后创建一个protected DatagramSocket并将数据(没有其IP和UDP头)发送到我从捕获的IP数据包中读取的目标地址.

3).我从远程计算机收到响应,并希望将其发送回初始化请求的应用程序.

4).我交换IP数据包中的源和目标IP地址和端口号,计算IPv4报头校验和和UDP校验和(构建了IPv4伪报头).

05-06 00:46:52.889: D/UDPChecksum(31077): mIpv4PseudoHeader == [-64, -88, 1, -59, 10, 0, 2, 1, 0, 17, 0, 14]
Run Code Online (Sandbox Code Playgroud)

5).然后,我将计算出的校验和设置为IP数据包的相应索引,并将IP数据包写入outTUN设备的输出流.

05-06 00:46:52.889: D/UDPChecksum(31077): To TUN == [69, 0, 0, 34, 0, 0, 64, 0, 64, 17, 108, 93, -64, -88, 1, -59, 10, 0, 2, 1, -50, -87, -53, 1, 0, 14, -105, -72, 85, 68, 80, 95, 79, 75]
Run Code Online (Sandbox Code Playgroud)

响应到达我的应用程序.所述DatagramSocket已呼叫阻止其接收()方法之后填补我提供缓冲.

        byte[] responseBuffer = new byte[RESPONSE_SIZE];
        try {
            mDatagramSocket.send(mDatagramPacket);
            final DatagramPacket response = new DatagramPacket(responseBuffer, responseBuffer.length);
            mDatagramSocket.receive(response);
        } catch (IOException e) {
            Log.e("NoRootFwService", "error: " + Arrays.toString(responseBuffer)); // I can see the correct response here.
            logException(e);
        }
Run Code Online (Sandbox Code Playgroud)

但是当超过超时时,它的套接字会抛出异常.

05-05 23:46:58.389: E/CLIENT(20553): java.net.SocketTimeoutException
05-05 23:46:58.389: E/CLIENT(20553):    at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:551)
05-05 23:46:58.389: E/CLIENT(20553):    at libcore.io.IoBridge.recvfrom(IoBridge.java:509)
05-05 23:46:58.389: E/CLIENT(20553):    at java.net.PlainDatagramSocketImpl.doRecv(PlainDatagramSocketImpl.java:161)
05-05 23:46:58.389: E/CLIENT(20553):    at java.net.PlainDatagramSocketImpl.receive(PlainDatagramSocketImpl.java:169)
05-05 23:46:58.389: E/CLIENT(20553):    at java.net.DatagramSocket.receive(DatagramSocket.java:250)
05-05 23:46:58.389: E/CLIENT(20553):    at socket.client.MainActivity$UdpThread.run(MainActivity.java:195)
05-05 23:46:58.389: E/CLIENT(20553): Caused by: libcore.io.ErrnoException: recvfrom failed: EAGAIN (Try again)
05-05 23:46:58.389: E/CLIENT(20553):    at libcore.io.Posix.recvfromBytes(Native Method)
05-05 23:46:58.389: E/CLIENT(20553):    at libcore.io.Posix.recvfrom(Posix.java:141)
05-05 23:46:58.389: E/CLIENT(20553):    at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
05-05 23:46:58.389: E/CLIENT(20553):    at libcore.io.IoBridge.recvfrom(IoBridge.java:506)
05-05 23:46:58.389: E/CLIENT(20553):    ... 4 more
Run Code Online (Sandbox Code Playgroud)

没有防火墙,一切正常.我读了这些讨论,但没有帮助:

Mak*_*iev 0

问题是我使用了错误的 IPv4 伪标头来计算校验和。它既不包含数据包 UDP 标头,也不包含传输的数据。由于我包含了 UDP 标头和数据,因此我没有看到原始问题中的异常。

在此输入图像描述