写入VpnService输出流不提供任何响应

时间:2013-11-28 15:02:36

标签: android tcp vpn

我的应用程序实现VpnService以拦截网络流量并提供定制的响应。目标是处理特定地址的流量,并丢弃其他请求。

目前,我成功解析了传入的请求以及构建和发送响应。但问题是,这些答复并未作为对原始请求的实际答复而到达;使用套接字连接进行测试只会超时。

为了做出这种区分,我现在正在解析来自VpnService输入流的原始IP数据包,如下所示:

VpnService.Builder b = new VpnService.Builder();
b.addAddress("10.2.3.4", 28);
b.addRoute("0.0.0.0", 0);
b.setMtu(1500);
...
ParcelFileDescriptor vpnInterface = b.establish();
final FileInputStream in = new FileInputStream(
        vpnInterface.getFileDescriptor());
final FileOutputStream out = new FileOutputStream(
        vpnInterface.getFileDescriptor());

// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(32767);

// We keep forwarding packets till something goes wrong.
try {
    while (vpnInterface != null && vpnInterface.getFileDescriptor() != null
            && vpnInterface.getFileDescriptor().valid()) {
        packet.clear();
        SystemClock.sleep(10);
        // Read the outgoing packet from the input stream.
        final byte[] data = packet.array();
        int length = in.read(data);
        if (length > 0) {
            packet.limit(length);
        /*
         1. Parse the TCP/UDP header
         2. Create an own socket with the same src/dest port/ip
         3. Use protect() on this socket so it not routed over tun0
         4. Send the packet body (excluding the header)
         5. Obtain the response
         6. Add the TCP header to the response and forward it
         */
        final IpDatagram ip = IpDatagram.create(packet);
        ...
    }
}

IpDatagram是一个类,create()将字节数组解析为IP包的表示,包含IP头,选项和主体。我继续根据协议类型解析正文的字节数组。在这种情况下,我只对带有TCP负载的IPv4感兴趣 - 在这里我也创建了TCP头,选项和正文的表示。

获取IpDatagram的实例后,我可以确定源IP和目标IP(来自IP头)和端口(来自TCP头)。我也承认请求TCP的标志(例如SYN,ACK和PSH)和序列号。在应用程序中:

Parsing incoming TCP/IP packet

随后我构建了一个新的IpDatagram作为响应,其中:

  • 源IP地址和目标IP与传入请求相反;
  • 源和目标端口与传入请求相反;
  • TCP确认号码设置为传入请求的序列号;
  • 提供虚拟HTTP / 1.1有效负载作为TCP的正文。

我将生成的IpDatagram转换为字节数组并将其写入VpnServer的输出流:

TcpDatagram tcp = new TcpDatagram(tcpHeader, tcpOptions, tcpBody);
IpDatagram ip = new Ip4Datagram(ipHeader, ipOptions, tcp);
out.write(ip.toBytes());

我的应用程序显示应该是的传出数据报,但是,所有连接仍然超时。

TCP/IP response

以下是十六进制的传入TCP / IP数据包示例:

  

4500003c7de04000400605f10a0203044faa5a3bb9240050858bc52b00000000a00239089a570000020405b40402080a00bfb8cb0000000001030306

以十六进制形式生成的传出TCP / IP数据包:

  

450000bb30394000800613194faa5a3b0a0203040050b92400a00000858bc52b501820001fab0000485454502f312e3120323030204f4b0a446174653a205475652c203139204e6f7620323031332031323a32333a303320474d540a436f6e74656e742d547970653a20746578742f68746d6c0a436f6e74656e742d4c656e6774683a2031320a457870697265733a205475652c203139204e6f7620323031332031323a32333a303320474d540a0a48656c6c6f20776f726c6421

然而,简单的测试只是超时;我创建一个新套接字并将其连接到上面的IP,但上面提供的响应永远不会到达。

可能出现什么问题?有没有办法解决为什么我的回复没有到来?

1 个答案:

答案 0 :(得分:7)

此TCP / IP响应不包含有效的TCP标头校验和:

  

450000bb30394000800613194faa5a3b0a0203040050b92400a00000858bc52b50182000的 1fab 0000485454502f312e3120323030204f4b0a446174653a205475652c203139204e6f7620323031332031323a32333a303320474d540a436f6e74656e742d547970653a20746578742f68746d6c0a436f6e74656e742d4c656e6774683a2031320a457870697265733a205475652c203139204e6f7620323031332031323a32333a303320474d540a0a48656c6c6f20776f726c6421

更一般地说,请求和响应机制非常挑剔。这当然是由于网络的本质所在,并且由于内核负责确保响应良好以及应该向哪个端口发送响应,因此任何不计算的内容都将被丢弃为坏包。当您在网络层上运行时,从VpnService的输出流响应时也是如此。

要返回上面的特定情况:IP数据包是正确的(包括校验和),但TCP数据包不是。您需要计算TCP报头校验和,而不仅仅是TCP数据包,而是以伪标头为前缀,如下所示:

TCP pseudo-header http://www.tcpipguide.com/free/diagrams/tcppseudoheader.png

然后应该通过以下字节计算:

TCP header checksum