mrtexaz
mrtexaz

Reputation: 663

Android Vpn service - error on write to vpn interface

I am using VpnService API as shown in ToyVpn Android example;

The vpn device is opened correctly (the system icon is shown).

I get an exception when writing to the vpn interface. I try to write random bytes or a complete captured packet (in the example below) the result is the same, i.e. "Invalid argument".

The tun device descriptor is reported to be valid.

What could be the issue? Are there controls on the data being written to the vpn device?

Up to now I have tested this on two different emulators, with Android 7.1 and Android 4.2 and I get an exception in both cases.

Here is the complete log on Android 7.1:

02-01 11:01:08.099 ... I/MainActivity: onActivityResult
02-01 11:01:08.105 ... I/TestVpnService: opening tun device
02-01 11:01:08.118 ... I/TestVpnService: mInterface {ParcelFileDescriptor: java.io.FileDescriptor@c8cad37}
02-01 11:01:08.118 ... I/TestVpnService: mInterface FD: 45
02-01 11:01:08.118 ... I/TestVpnService: vpn descriptor valid? true
02-01 11:01:08.118 ... I/TestVpnService: testing write to tun device...
02-01 11:01:08.119 ... E/TestVpnService: error: Invalid argument
02-01 11:01:08.119 ... W/System.err: java.io.IOException: Invalid argument
02-01 11:01:08.119 ... W/System.err:     at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
02-01 11:01:08.119 ... W/System.err:     at sun.nio.ch.FileDispatcherImpl.write(FileDispatcherImpl.java:63)
02-01 11:01:08.119 ... W/System.err:     at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
02-01 11:01:08.119 ... W/System.err:     at sun.nio.ch.IOUtil.write(IOUtil.java:65)
02-01 11:01:08.119 ... W/System.err:     at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:214)
02-01 11:01:08.119 ... W/System.err:     at testvpn.vpnwritetest.AndroidVpnService$override.onStartCommand(AndroidVpnService.java:71)
02-01 11:01:08.119 ... W/System.err:     at testvpn.vpnwritetest.AndroidVpnService$override.access$dispatch(AndroidVpnService.java)
02-01 11:01:08.120 ... W/System.err:     at testvpn.vpnwritetest.AndroidVpnService.onStartCommand(AndroidVpnService.java:0)
02-01 11:01:08.120 ... W/System.err:     at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3326)
02-01 11:01:08.120 ... W/System.err:     at android.app.ActivityThread.-wrap21(ActivityThread.java)
02-01 11:01:08.120 ... W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1582)
02-01 11:01:08.120 ... W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
02-01 11:01:08.120 ... W/System.err:     at android.os.Looper.loop(Looper.java:154)
02-01 11:01:08.120 ... W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6119)
02-01 11:01:08.120 ... W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
02-01 11:01:08.120 ... W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
02-01 11:01:08.120 ... W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
02-01 11:01:08.120 ... I/TestVpnService: closing tun device

the manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="testvpn.vpnwritetest">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service
            android:name=".AndroidVpnService"
            android:permission="android.permission.BIND_VPN_SERVICE">
            <intent-filter>
                <action android:name="android.net.VpnService" />
            </intent-filter>
        </service>


        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

the activity:

package testvpn.vpnwritetest;

import android.content.Intent;
import android.net.VpnService;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    Button button_vpn_test;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        button_vpn_test = (Button) findViewById(R.id.button_vpn_test);

        button_vpn_test.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = VpnService.prepare(MainActivity.this);
                if (intent != null) {
                    startActivityForResult(intent, 0);
                } else {
                    onActivityResult(0, RESULT_OK, null);
                }

            }
        });

    }

    @Override
    protected void onActivityResult(int request, int result, Intent data) {

        Log.i("MainActivity", "onActivityResult");

        if (result == RESULT_OK) {


            Intent intent = new Intent(this, AndroidVpnService.class);

            startService(intent);
        }
    }


}

the service:

package testvpn.vpnwritetest;

import android.content.Intent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
import android.util.Log;

import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Created by mrtexaz on 2/1/17.
 */

public class AndroidVpnService extends VpnService {

    private static final String TAG = "TestVpnService";


    private ParcelFileDescriptor mInterface;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {



        Log.i(TAG, "opening tun device");

        Builder builder = new Builder();
        builder = new Builder();
        builder.setMtu(1500);
        builder.addAddress("10.0.0.2", 24);
//        builder.addRoute("0.0.0.0", 0);

        mInterface = builder.establish();

        Log.i(TAG,"mInterface " + mInterface);
        Log.i(TAG,"mInterface FD: " + mInterface.getFd());

        FileDescriptor df = mInterface.getFileDescriptor();

        Log.i(TAG,"vpn descriptor valid? " + df.valid());

        FileChannel vpnOutput = new FileOutputStream(mInterface.getFileDescriptor()).getChannel();


        int [] testpayload = { 0x98, 0xe7, 0xf5, 0xd7, 0xce, 0xb6, 0x10, 0x02, 0xb5, 0x56, 0x59, 0x87, 0x08, 0x00, 0x45, 0x00,
                0x00, 0x54, 0xd0, 0x93, 0x40, 0x00, 0x40, 0x01, 0xe6, 0x5f, 0xc0, 0xa8, 0x01, 0x64, 0xc0, 0xa8,
                0x01, 0x01, 0x08, 0x00, 0x63, 0xc6, 0x7e, 0x2e, 0x00, 0x02, 0x95, 0xaa, 0x91, 0x58, 0x00, 0x00,
                0x00, 0x00, 0x21, 0x33, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
                0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
                0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
                0x36, 0x37};

        ByteBuffer test = ByteBuffer.allocate(testpayload.length);

        for (int i = 0; i < testpayload.length; i++) {

            test.array()[i] = (byte) (testpayload[i] & 0xFF);

        }

        try {

            Log.i(TAG, "testing write to tun device...");


            vpnOutput.write(test);

            Log.i(TAG, "testing write to tun device OK");

        } catch (Exception ex) {
            Log.e(TAG,"error: " + ex.getMessage());
            ex.printStackTrace();
        }


        Log.i(TAG, "closing tun device");

        try {
            mInterface.close();
        } catch (IOException e) {
            e.printStackTrace();
        }




        return START_STICKY;
    }

    @Override
    public void onDestroy() {

    }


}

Upvotes: 1

Views: 1846

Answers (1)

mrtexaz
mrtexaz

Reputation: 663

OK solved!

I need to write a valid tcp/ip packet to the tun device.

In the example, it is sufficient to remove the Ethernet II headers (the first 14 bytes of the sample array).

So using this sample payload, write to vpn device works:

int [] testpayload = {
        //0x98, 0xe7, 0xf5, 0xd7, 0xce, 0xb6, 0x10, 0x02, 0xb5, 0x56, 0x59, 0x87, 0x08, 0x00,
        0x45, 0x00,
        0x00, 0x54, 0xd0, 0x93, 0x40, 0x00, 0x40, 0x01, 0xe6, 0x5f, 0xc0, 0xa8, 0x01, 0x64, 0xc0, 0xa8,
        0x01, 0x01, 0x08, 0x00, 0x63, 0xc6, 0x7e, 0x2e, 0x00, 0x02, 0x95, 0xaa, 0x91, 0x58, 0x00, 0x00,
        0x00, 0x00, 0x21, 0x33, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
        0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
        0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
        0x36, 0x37};

Upvotes: 1

Related Questions