【问题标题】:Writing to VpnService output stream provides no response写入 VpnService 输出流不提供响应
【发布时间】:2013-12-14 16:15:51
【问题描述】:

我的应用程序实现了 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)和序列号。在应用程序中:

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

  • 源和目标 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 数据包示例:

4500003c7de04000400605f10a0203044faa5a3bb9240050858bc52b00000000a00239089a570000020405b40402080a00bfb8cb0000000001030306

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

450000bb30394000800613194faa5a3b0a0203040050b92400a00000858bc52b501820001fab0000485454502f312e3120323030204f4b0a446174653a205475652c203139204e6f7620323031332031323a32333a303320474d540a436f6e74656e742d547970653a20746578742f68746d6c0a436f6e74656e742d4c656e6774683a2031320a457870697265733a205475652c203139204e6f7620323031332031323a32333a303320474d540a0a48656c6c6f20776f726c6421 P>

但是,简单的测试会超时;我创建了一个新套接字并将其连接到上面的 IP,但上面提供的响应从未到达。

可能出了什么问题?有什么方法可以解决我的回复未到达的原因吗?

【问题讨论】:

    标签: android tcp vpn


    【解决方案1】:

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

    450000bb30394000800613194faa5a3b0a0203040050b92400a00000858bc52b50182000的 1fab B> 0000485454502f312e3120323030204f4b0a446174653a205475652c203139204e6f7620323031332031323a32333a303320474d540a436f6e74656e742d547970653a20746578742f68746d6c0a436f6e74656e742d4c656e6774683a2031320a457870697265733a205475652c203139204e6f7620323031332031323a32333a303320474d540a0a48656c6c6f20776f726c6421 P>

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

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


    (来源:tcpipguide.com

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

    【讨论】:

    • 我将字节流导入到 Wireshark 中,看起来你是对的,因为它也说校验和不正确。我对 TCP 结构的理解是校验和是可选的,但我不知道我是怎么想到的。我会看看,看看纠正它是否能解决问题。
    • 确实,我只计算 TCP 段上的校验和,并省略了伪标头。它现在正在工作!
    • 嗨@PaulLammertsma,我正在尝试理解处理传入数据包并将它们再次写回背后的概念。是否有任何文件可以让我掌握您工作背后的强调原则。熟悉网络和 OSI 层。有什么指点吗?
    • @sirackh 我不知道,但这种方法对于 VPN 来说很典型:VPN 服务器中继请求,因此一方面您在 VPN 服务器和客户端之间有请求和响应,并且另一方面,您有 VPN 服务器向远程服务器发出的中继请求和响应。也许你会更幸运地找到描述通用架构的文档,而不是特定于 Android 的文档。
    猜你喜欢
    • 2018-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-15
    相关资源
    最近更新 更多