VpnServiceによるAndroidファイアウォール

私はBSプロジェクトのためのVpnServiceとアンドロイドのための簡単なファイアウォールを実装しようとしています。私はVpnServiceを選択します。なぜなら、それはルートされていないデバイスで動作するからです。接続をログに記録し、接続をフィルタリングします。 (IPベース)

これを行うアプリケーションがあるので、可能です。

Google Playアプリストア

私はいくつかの調査を行い、VpnServiceがTunインタフェースを作成することを発見しました。これ以上何もない。 (VPN実装はトンネルだけではありません)このインターフェースにアドレスを与えてルートを追加することができます。ファイル記述子を返します。送信パッケージを読み込んで、受信パッケージを書き込むことができます。

私はVpnService派生クラスを作成し、私はサービスを開始しました。 VpnService.Builderクラスで tun0 を設定できます。 mobiwol の接続を adb shell netcfg と見ると、10.2.3.4/32アドレスの tun0 インターフェースが作成されます。すべてのパッケージをこのプライベートネットワークにルーティングし、インターネットに送信します。私は同じことをやっている。 10.0.0.2/32アドレスのインターフェイスを作成しました。 addRoute関数でルートを追加しました。 0.0.0.0/0だから私はすべてのネットワークからすべてのパッケージを私が理解できる限りキャプチャすることができます。私は間違っていると私を訂正します。

私は2つのスレッドを作成しました。 1つはファイルディスクリプタから読み込み、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

残りのデータで他のIPヘッダーを見つけられませんでした。私は10.0.0.2ネットワークとローカルネットワーク(192.168.2.1)とインターネットの間にカプセル化があるべきだと思います。よく分かりません。

私の本当の問題は、入ってくるパッケージのスレッドに執着していることです。私は何も読むことができません。応答なし。あなたがスクリーンショットで見ることができるように、入ってくるデータはありません:

スクリーンショット

私は保護されたソケットで127.0.0.1に書き込むために使用しているのと同じ接続から読み込もうとしています。

Android <-> Tun Interface (tun0) <-> Internet connection

All packages <-> 10.0.0.2 <-> 127.0.0.1? <-> 192.168.2.1 <-> Internet?

私は何もVpnServiceに役立つ見つけることができませんでした。 (ToyVPNの例はまったく役に立たない)私はLinux Tun/Tapについての文書を読むが、それはホストとリモートの間のトンネリングに関するものである。私はホストと同じデバイス上のリモートが欲しい。トンネリングに似ていません。

これどうやってするの?

編集:コードが要求されました。それは非常に初期段階です。前述のとおり、VpnService派生クラスです。サービススレッドで作成された2つのスレッド(読み書き)。

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

    }

}
27
nl ru de
@fatihdurmusあなたはそれを解決しましたか?解決策を投稿してください
追加された 著者 Taranfx,
こんにちはFatihdrumus、私はまた、ファイアウォールのvith vpnserviceを作成したいと思います。私はまだあなたの問題を解決したことを知らないのですか?
追加された 著者 Huỳnh Ngọc Bang,
Stackoverflowへようこそ。素敵な徹底的な質問。これまでにコーディングの面で試したことをお見せください。
追加された 著者 LuigiEdlCarno,
解決したら解決策を教えてください。
追加された 著者 ospider,
あなたは一時的なアプリケーションを無効にするか、またはインターネットや無アクセスから恒久的に拒否したいですか?
追加された 著者 Farshid,
AndroidはTAPインターフェースを持っていません。私はwlan0をタップで橋渡ししたいが、それは不可能である。
追加された 著者 fatihdurmus,
私はコードを投稿した。私は別のサーバーアドレス、ルート、tunインターフェイスのアドレスを試しました。私はシングルスレッドで読み書きを試みました。多くは変わらない。私はネットワーク知識がもっと必要だと思う。 (インターネットチューン/タップ間のルーティング方法)
追加された 著者 fatihdurmus,

2 答え

A similar question was asked a few months ago, and while the answers there aren't very insightful, the comments in the accepted answer give some insight into what may be going wrong.

あなたのロジックが存在する OSIモデルのどのレイヤーに注意する必要があります:

  • Incoming and outgoing streams of the VpnService are in the network layer; you are receiving (and should in turn be transmitting) raw IP packets, as you describe in your question.

    In your sample byte stream, you can see that the incoming byte stream is an IPv4 datagram as the first four bits are 0100 (4). Consult this packet structure specification for details on IPv4.

  • When forwarding the requests, you are in the application layer; you should be transmitting the contents of the UDP or TCP payload (i.e. only their data, not the headers themselves) using respectively a DatagramSocket or a Socket.

    Bear in mind that this skips the transport layer as those implementations take care of constructing the UDP header (in case of DatagramSocket) and the TCP header and options (in case of Socket).

アプリケーションでは、基本的に、IPv4とIPv6のヘッダーとオプション、およびIPペイロード、UDPヘッダー、TCPヘッダーとオプションを解釈して構築できる必要があります。

15
追加された
@FarshidあなたのVPNインターフェイスがリクエストを転送していない場合(そして自分のパケットをループからインターフェイスに戻すことを除いて)、他のアプリケーションからのすべてのリクエストは無視されます。これらのアプリケーションは、要求をタイムアウトさせるだけです。
追加された 著者 Paul Lammertsma,
@おかげで申し訳ありません、私はあなたの質問を理解していない。
追加された 著者 Paul Lammertsma,
@SimonLanghoff他を見る: stackoverflow.com/a/1037999/154306
追加された 著者 Paul Lammertsma,
@SimonLanghoffと一緒に作業して以来、しばらくしてきましたが、識別のための仕様は断片を再組み立てするためだけであると私は信じています。私が思い出す限り、応答の識別は独立しています。応答がリクエストに関連付けられる方法は、正しくリコールするとポート番号を使用する方法ですが、間違っている可能性があります。
追加された 著者 Paul Lammertsma,
@Suman本文には、本文のサイズに関する情報が含まれているため、ヘッダーを単に貼り付けることはできません。正しいデータで再構築する必要があります。
追加された 著者 Paul Lammertsma,
ああ、情報Paulありがとう:)
追加された 著者 Suman,
@Paul - 受信したパケットのヘッダを取り除く必要があるか(vpnservice.builder.establishのときに取得したトンネルfdから受け取ったAndroid VpnServiceのパケットのデータを抽出する)、新しいヘッダを追加する変更されたfromアドレス(Toアドレスは受信したパケットのものになります)で送信され、次に送信されます。返信を取得した後、マッパを維持して古いヘッダを元に戻してから元に戻し、トンネルソケットに書き戻す必要があります(VpnService.Builder.establish()から受信しました)。
追加された 著者 Suman,
リクエストを転送して返信すると、どのクライアントに返信する必要があるのですか?転送したパケットの16ビットの識別子フラグを使用できますか?これは受信したパケットのIDと同じですか?
追加された 著者 Simon Langhoff,
@Paulだから基本的に、私はlayer4のルータとして作成したvpnserviceを扱うことができますか?
追加された 著者 ospider,
アンドロイドアプリケーションでvpnServiceを起動すると、インターネットへのアクセスからアプリを無効にしたいのですが、このアプリを無効にすると、他のアプリは接続または不要でvpnを使用していますか?
追加された 著者 Farshid,
vpnserviceを起動した後、vpnまたはnoを使用してインターネットに接続しているアプリですか?
追加された 著者 Farshid,
私はこの文章のような私のアプリが欲しい: "インターネットアクセスがインターネットにアクセスしたいと思っている場合、私のアプリで一時的または永続的にこのアクセスを無効にし、インターネットからのデータダウンロードが許可または拒否する"このアプリケーションを実装する? "とこの質問のように、アンドロイドのインタラクティブなファイアウォールアプリを作成する
追加された 著者 Farshid,
プロジェクトをLinuxファイアウォールに変更しました。 (netfilterフックとconntrackに基づいて)しかし、私はこれが正しい答えだと言うことができます。私は現時点でこれを試してはいけませんが、私は趣味のプロジェクトとして学期の休暇でそれをやろうとします。助けてくれてありがとう。
追加された 著者 fatihdurmus,

多分、 OpenVpn のようなオープンソースプロジェクトを探す方が良いでしょう。ルートアクセスなしでAPIレベル14+(Ice Cream Sandwhich)で動作します。

0
追加された
あなたが知っているように、我々はイランにこのプログラムでフィルタリングをアンドロイドで渡している。それはパケットの送信についてだけではありません。
追加された 著者 Ali,
私はしたが、彼らのコードは、VPNクライアント - サーバーパケットの送信についてです。私はインターネットへのルーティングがサーバー側にあると思います。
追加された 著者 fatihdurmus,