我的应用程序实现了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
然而,简单的测试只是超时;我创建一个新套接字并将其连接到上面的IP,但上面提供的响应永远不会到来.
怎么可能出错?有没有办法解决为什么我的回复没有到达?
解决方法:
此TCP / IP响应不包含有效的TCP标头校验和:
450000bb30394000800613194faa5a3b0a0203040050b92400a00000858bc52b501820001fab0000485454502f312e3120323030204f4b0a446174653a205475652c203139204e6f7620323031332031323a32333a303320474d540a436f6e74656e742d547970653a20746578742f68746d6c0a436f6e74656e742d4c656e6774683a2031320a457870697265733a205475652c203139204e6f7620323031332031323a32333a303320474d540a0a48656c6c6f20776f726c6421
更一般地说,请求和响应机制非常挑剔.这当然是由于网络的本质所在,并且由于内核负责确保响应良好以及应该向哪个端口发送响应,因此任何不计算的内容都将被丢弃为坏包.当您在网络层上运行时,从VpnService的输出流响应时也是如此.
要返回上面的特定情况:IP数据包是正确的(包括校验和),但TCP数据包不是.您需要计算TCP报头校验和,不仅仅是TCP数据包,而是以伪标头为前缀,如下所示:
TCP pseudo-header http://www.tcpipguide.com/free/diagrams/tcppseudoheader.png
然后应该通过以下字节计算: