Android防火墙与VpnService

我试图为BS项目实施一个简单的防火墙与VpnService的Android。 我selectVpnService,因为它将在非根设备上工作。 它会logging连接并让你过滤连接。 (基于IP)

有一个应用程序这样做是可能的。

Google Playapp store

我做了一些研究,发现VpnService创build了一个Tun接口。 而已。 (没有VPN实现只是一个隧道)它可以让你给这个接口的地址,并添加路由。 它返回一个文件描述符。 您可以读取传出包并写入传入包。

我创build了一个VpnService派生类,我开始服务。 我可以使用VpnService.Builder类来configurationtun0 。 当我看到mobiwol'sadb shell netcfg mobiwol's连接时,它会创build一个带有10.2.3.4/32地址的tun0接口。 它将所有包路由到这个专用networking并发送到互联网。 我正在尝试。 创build一个与10.0.0.2/32地址的接口。 添加了一个addRoute函数的路线。 0.0.0.0/0,所以我可以从所有networking捕获所有包,据我所知。 (我对这个主题很陌生,还在学习,我通过互联网find了一些片断,所以我不太确定,如果我错了,请纠正我。

我在服务中创build了2个线程。 一个从文件描述符读取,并使用受保护的套接字将其写入127.0.0.1。 (我不知道我是否应该读/写127.0.0.1。也许这是问题。)

我分析了从文件描述符中读取的数据包。 例如:

 01000101 byte:69 //ipv4 20byte header 00000000 byte:0 //TOS 00000000 byte:0 //Total Length 00111100 byte:60 //Total Length 11111100 byte:-4 //ID 11011011 byte:-37 //ID 01000000 byte:64 //fragment 00000000 byte:0 //" 01000000 byte:64 //TTL 00000110 byte:6 //Protocol 6 -> TCP 01011110 byte:94 //Header checksum 11001111 byte:-49 //Header checksum 00001010 byte:10 //10.0.0.2 00000000 byte:0 00000000 byte:0 00000010 byte:2 10101101 byte:-83 //173.194.39.78 //google 00111110 byte:-62 00100111 byte:39 ******** byte:78 10110100 byte:-76 // IP option 01100101 byte:101 00000001 byte:1 10111011 byte:-69 //20byte IP haeder 01101101 byte:109 . . //40byte data (i couldnt parse TCP header, I think its not needed when I route this in IP layer) . . . . 00000110 byte:6 

我没有find其他数据的其他IP头。 我认为应该有一个10.0.0.2networking到本地networking(192.168.2.1)和互联网之间的封装。 我不确定。

我真正的问题是我卡在传入的包线程。 我什么都看不懂 没有反应。 正如你可以在截图中看到没有传入的数据:

截图

我试图从我正在用写保护套接字写入127.0.0.1相同的连接读取。

Android < – > Tun接口(tun0)< – > Internet连接

所有软件包< – > 10.0.0.2 < – > 127.0.0.1? < – > 192.168.2.1 < – >互联网?

我找不到有关VpnService的任何帮助。 (ToyVPN的例子是没用的)我读了关于Linux Tun / Tap的文档,但是关于主机和远程之间的隧道。 我想要在同一台设备上的主机和远程。 不像隧道。

我怎样才能做到这一点?

编辑:代码请求。 这是在很早的阶段。 正如我之前提到的,它是一个VpnService派生类。 2个线程(读取和写入)在服务线程中创build。

 package com.git.firewall; public class GITVpnService extends VpnService implements Handler.Callback, Runnable { private static final String TAG = "GITVpnService"; private String mServerAddress = "127.0.0.1"; private int mServerPort = 55555; private PendingIntent mConfigureIntent; private Handler mHandler; private Thread mThread; private ParcelFileDescriptor mInterface; @Override public int onStartCommand(Intent intent, int flags, int startId) { // The handler is only used to show messages. if (mHandler == null) { mHandler = new Handler(this); } // Stop the previous session by interrupting the thread. if (mThread != null) { mThread.interrupt(); } // Start a new session by creating a new thread. mThread = new Thread(this, "VpnThread"); mThread.start(); return START_STICKY; } @Override public void onDestroy() { if (mThread != null) { mThread.interrupt(); } } @Override public boolean handleMessage(Message message) { if (message != null) { Toast.makeText(this, (String)message.obj, Toast.LENGTH_SHORT).show(); } return true; } @Override public synchronized void run() { try { Log.i(TAG, "Starting"); InetSocketAddress server = new InetSocketAddress( mServerAddress, mServerPort); run(server); } catch (Exception e) { Log.e(TAG, "Got " + e.toString()); try { mInterface.close(); } catch (Exception e2) { // ignore } Message msgObj = mHandler.obtainMessage(); msgObj.obj = "Disconnected"; mHandler.sendMessage(msgObj); } finally { } } DatagramChannel mTunnel = null; private boolean run(InetSocketAddress server) throws Exception { boolean connected = false; android.os.Debug.waitForDebugger(); // Create a DatagramChannel as the VPN tunnel. mTunnel = DatagramChannel.open(); // Protect the tunnel before connecting to avoid loopback. if (!protect(mTunnel.socket())) { throw new IllegalStateException("Cannot protect the tunnel"); } // Connect to the server. mTunnel.connect(server); // For simplicity, we use the same thread for both reading and // writing. Here we put the tunnel into non-blocking mode. mTunnel.configureBlocking(false); // Authenticate and configure the virtual network interface. handshake(); // Now we are connected. Set the flag and show the message. connected = true; Message msgObj = mHandler.obtainMessage(); msgObj.obj = "Connected"; mHandler.sendMessage(msgObj); new Thread () { public void run () { // Packets to be sent are queued in this input stream. FileInputStream in = new FileInputStream(mInterface.getFileDescriptor()); // Allocate the buffer for a single packet. ByteBuffer packet = ByteBuffer.allocate(32767); int length; try { while (true) { while ((length = in.read(packet.array())) > 0) { // Write the outgoing packet to the tunnel. packet.limit(length); debugPacket(packet); // Packet size, Protocol, source, destination mTunnel.write(packet); packet.clear(); } } } catch (IOException e) { e.printStackTrace(); } } }.start(); new Thread () { public void run () { DatagramChannel tunnel = mTunnel; // Allocate the buffer for a single packet. ByteBuffer packet = ByteBuffer.allocate(8096); // Packets received need to be written to this output stream. FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor()); while (true) { try { // Read the incoming packet from the tunnel. int length; while ((length = tunnel.read(packet)) > 0) { // Write the incoming packet to the output stream. out.write(packet.array(), 0, length); packet.clear(); } } catch (IOException ioe) { ioe.printStackTrace(); } } } }.start(); return connected; } private void handshake() throws Exception { if (mInterface == null) { Builder builder = new Builder(); builder.setMtu(1500); builder.addAddress("10.0.0.2",32); builder.addRoute("0.0.0.0", 0); //builder.addRoute("192.168.2.0",24); //builder.addDnsServer("8.8.8.8"); // Close the old interface since the parameters have been changed. try { mInterface.close(); } catch (Exception e) { // ignore } // Create a new interface using the builder and save the parameters. mInterface = builder.setSession("GIT VPN") .setConfigureIntent(mConfigureIntent) .establish(); } } private void debugPacket(ByteBuffer packet) { /* for(int i = 0; i < length; ++i) { byte buffer = packet.get(); Log.d(TAG, "byte:"+buffer); }*/ int buffer = packet.get(); int version; int headerlength; version = buffer >> 4; headerlength = buffer & 0x0F; headerlength *= 4; Log.d(TAG, "IP Version:"+version); Log.d(TAG, "Header Length:"+headerlength); String status = ""; status += "Header Length:"+headerlength; buffer = packet.get(); //DSCP + EN buffer = packet.getChar(); //Total Length Log.d(TAG, "Total Length:"+buffer); buffer = packet.getChar(); //Identification buffer = packet.getChar(); //Flags + Fragment Offset buffer = packet.get(); //Time to Live buffer = packet.get(); //Protocol Log.d(TAG, "Protocol:"+buffer); status += " Protocol:"+buffer; buffer = packet.getChar(); //Header checksum String sourceIP = ""; buffer = packet.get(); //Source IP 1st Octet sourceIP += buffer; sourceIP += "."; buffer = packet.get(); //Source IP 2nd Octet sourceIP += buffer; sourceIP += "."; buffer = packet.get(); //Source IP 3rd Octet sourceIP += buffer; sourceIP += "."; buffer = packet.get(); //Source IP 4th Octet sourceIP += buffer; Log.d(TAG, "Source IP:"+sourceIP); status += " Source IP:"+sourceIP; String destIP = ""; buffer = packet.get(); //Destination IP 1st Octet destIP += buffer; destIP += "."; buffer = packet.get(); //Destination IP 2nd Octet destIP += buffer; destIP += "."; buffer = packet.get(); //Destination IP 3rd Octet destIP += buffer; destIP += "."; buffer = packet.get(); //Destination IP 4th Octet destIP += buffer; Log.d(TAG, "Destination IP:"+destIP); status += " Destination IP:"+destIP; /* msgObj = mHandler.obtainMessage(); msgObj.obj = status; mHandler.sendMessage(msgObj); */ //Log.d(TAG, "version:"+packet.getInt()); //Log.d(TAG, "version:"+packet.getInt()); //Log.d(TAG, "version:"+packet.getInt()); } } 

Solutions Collecting From Web of "Android防火墙与VpnService"

几个月前 , 有人提出了类似的问题 ,虽然答案不是非常有见地,但是接受答案中的意见给出了一些可能出错的信息。

您应该记住您的逻辑所在的OSI模型中的哪一层:

  • VpnService的入站和出站stream在networking层; 您正在接收(并且应该反过来传输)原始IP数据包,就像您在问题中所描述的那样。

    在您的示例字节stream中,您可以看到,传入的字节stream是IPv4数据报,前四位是0100 (4)。 有关IPv4的详细信息,请参阅此包结构规范 。

  • 转发请求时,您位于应用程序层; 你应该分别使用一个DatagramSocket或者一个Socket来传输UDP或者TCP有效载荷的内容 (也就是只有它们的数据,而不是头部本身)。

    请记住,这将跳过传输层,因为这些实现负责构buildUDP头(在DatagramSocket的情况下)和TCP头和选项(在Socket的情况下)。

您的应用程序本质上需要能够解释和构造IPv4和IPv6标头和选项,以及IP负载,UDP标头和TCP标头和选项。

也许最好找OpenVpn这样的开源项目。 它工作在API级别14+(冰淇淋Sandwhich)没有根访问。