Firewall de Android con VpnService

Estoy tratando de implementar un firewall simple para Android con VpnService para el proyecto BS. Elijo VpnService porque funcionará en dispositivos no rooteados. Registrará las conexiones y le permitirá filtrar la conexión. (Basado en IP)

Hay una aplicación que hace esto para que sea posible.

Google Play App Store

Investigué un poco y descubrí que VpnService crea una interfaz Tun. Nada mas. (Sin implementación de VPN solo un túnel) Le permite dar una dirección a esta interfaz y agregar rutas. Devuelve un descriptor de archivo. Puede leer paquetes salientes y escribir paquetes entrantes.

Creé una clase derivada de VpnService y comencé el servicio. Puedo configurar tun0 con la clase VpnService.Builder. Cuando miro mobiwol's conexión mobiwol's con adb shell netcfg crea una interfaz tun0 con la dirección 10.2.3.4/32. Enruta todos los paquetes a esta red privada y los envía a internet. Estoy intentando lo mismo. Creado una interfaz con 10.0.0.2/32 dirección. Se agregó una ruta con la función addRoute. 0.0.0.0/0 para que pueda capturar todos los paquetes de toda la red hasta donde yo entiendo. (Soy bastante nuevo en este tema y sigo aprendiendo. Encontré piezas en Internet, así que no estoy muy seguro. Corrígeme si me equivoco).

Creé 2 hilos en servicio. Uno lee desde el descriptor de archivo y lo escribe en 127.0.0.1 con un socket protegido. (No estoy seguro de si debería leer / escribir en 127.0.0.1. Tal vez este es el problema).

Analicé los paquetes que leí del descriptor de archivo. Por ejemplo:

 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 

No encontré ningún otro encabezado IP en el rest de los datos. Creo que debería haber una encapsulación entre la red 10.0.0.2 a la red local (192.168.2.1) e Internet. No estoy seguro.

Mi verdadero problema es que me quedé atrapado en el hilo de los paquetes entrantes. No puedo leer nada Ninguna respuesta. Como puede ver en la captura de pantalla, no hay datos entrantes:

captura de pantalla

Intento leer desde la misma conexión que uso para escribir en 127.0.0.1 con un socket protegido.

Android Tun Interface (tun0) conexión a Internet

Todos los paquetes 10.0.0.2 127.0.0.1? 192.168.2.1 Internet?

No pude encontrar nada útil sobre VpnService. (El ejemplo de ToyVPN es inútil) Leo documentos sobre Tun / Tap de Linux, pero se trata de hacer túneles entre el host y el control remoto. Quiero el host y el control remoto en el mismo dispositivo. No como túneles.

¿Cómo puedo hacer esto?

Editar: Código solicitado. Está en una etapa muy temprana. Como mencioné antes, es una clase derivada de VpnService. 2 hilos (lectura y escritura) creados en hilo de servicio.

 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 > 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()); } } 

Hace unos meses , se hizo una pregunta similar , y aunque las respuestas no son muy reveladoras, los comentarios en la respuesta aceptada dan una idea de lo que puede estar yendo mal.

Debe tener en cuenta qué capa del modelo OSI reside su lógica:

  • Las transmisiones entrantes y salientes de VpnService se encuentran en la capa de red; está recibiendo (y debería estar transmitiendo) paquetes de IP sin formato, como describe en su pregunta.

    En la secuencia de bytes de muestra, puede ver que la secuencia de bytes entrantes es un datagtwig IPv4, ya que los primeros cuatro bits son 0100 (4). Consulte esta especificación de estructura de paquetes para obtener detalles sobre IPv4.

  • Al reenviar las solicitudes, se encuentra en la capa de la aplicación; Debería estar transmitiendo los contenidos de la carga útil UDP o TCP (es decir, solo sus datos, no los encabezados) usando respectivamente un DatagramSocket o un Socket.

    Tenga en cuenta que esto omite la capa de transporte ya que esas implementaciones se encargan de construir el encabezado UDP (en el caso de DatagramSocket) y el encabezado TCP y las opciones (en el caso de Socket).

Su aplicación esencialmente necesitará poder interpretar y construir encabezados y opciones IPv4 e IPv6, y como carga útil IP, los encabezados UDP y las cabeceras y opciones de TCP.

Tal vez es mejor buscar proyectos de código abierto como OpenVpn . Funciona en el nivel API 14+ (Ice Cream Sandwhich) sin acceso de root.