Android VPN Service Explained with Packet Bypass Example Program

Android Android provides a user level interface for VPN services with which programmer only need to focus on the interaction with remote server.

Other actions, such as virtual interface creation, address and route configuration are done by OS.

From the APP side, there are two components for a VPN connection, the client and the service.

1. Make a VPN connection from Client

For the client, an intent for the VPN service must be required by the call of VpnService.prepare(). The intent can make sure that there is only one active VPN connection. If there is already a prepared VPN connection (e.g. stop and then start the same VPN connection manually), null will be returned. For which case, we should call the onActivityResult function manually.

Below is the code used to start a VPN service on Button’s onClick callback.

public void onClick(View v) {
  Intent intent = VpnService.prepare(getApplicationContext());
  if (intent != null) {
  	startActivityForResult(intent, 0);
  } else {
  	onActivityResult(0, RESULT_OK, null);

After the intent finished or null returned, VPN service can be started by the calling of startService, with a intent of a VPN service as the parameter.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (resultCode == RESULT_OK) {
  	Intent intent = new Intent(this, MyVpnService.class);

2. Implement a VPN Service

There are two function must be implemented when extending the Service class, the onStartCommand and the onDestroy. Usually a Thread can be used to provide the service in background, which can be start or interrupted/destroyed in above functions.

Android creates a TUN interface for VPN service, and provides API used by APP to interact with the TUN. We should do the following to implement a VPN service:

  1. Use a Builder to obtain an interface (FileDescription) for the TUN. The ip address, dns, and route table can be configured via the Builder. Please make sure the addRoute is properly called, as route table determinants which packet will be routed to the TUN.
  2. Get a input and output stream for the interface. Input stream and output stream are used to read and write packets in a manner of stream.
  3. Make a connection to the VPN server. Programmer can make any kinds of logic to handle the packet, usually use a connection to tunnel packet to the remote VPN server.
  4. Protect the tunnel socket from the VPN service. In order to avoid the deal loop of packet (tunnel packet be routed back to the TUN other than remote server), the connection must be protected (keep untouched) from the VPN service.
  5. Loop with the packet.

The inputstream (in), outputstream (out), tunnel connection (tunnel) and remote server work as below:

Network Activity -> TUN -> in -> tunnel -> Remote Server
Network Activity <- TUN <- out <- tunnel <- Remote Server

Below code shows these steps.

public class MyVpnService extends VpnService {

  private Thread mThread;
  private ParcelFileDescriptor mInterface;
  //a. Configure a builder for the interface.
  Builder builder = new Builder();

  // Services interface
  public int onStartCommand(Intent intent, int flags, int startId) {
  	// Start a new session by creating a new thread.
  	mThread = new Thread(new Runnable() {
  	  public void run() {
  	  	try {
  	  	  //a. Configure the TUN and get the interface.
  	  	  mInterface = builder.setSession("MyVPNService")
  	  	  	.addAddress("", 24)
  	  	  	.addRoute("", 0).establish();
  	  	  //b. Packets to be sent are queued in this input stream.
  	  	  FileInputStream in = new FileInputStream(
  	  	  //b. Packets received need to be written to this output stream.
  	  	  FileOutputStream out = new FileOutputStream(
                  //c. The UDP channel can be used to pass/get ip package to/from server
  	  	  DatagramChannel tunnel =;
  	  	  // Connect to the server, localhost is used for demonstration only.
  	  	  tunnel.connect(new InetSocketAddress("", 8087));
  	  	  //d. Protect this socket, so package send by it will not be feedback to the vpn service.
  	  	  //e. Use a loop to pass packets.
  	  	  while (true) {
  	  	  	//get packet with in
  	  	  	//put packet to tunnel
  	  	  	//get packet form tunnel
  	  	  	//return packet with out
  	  	  	//sleep is a must

  	  	} catch (Exception e) {
  	  	  // Catch any exception
  	  	} finally {
  	  	  try {
  	  	  	if (mInterface != null) {
  	  	  		mInterface = null;
  	  	  } catch (Exception e) {


  	}, "MyVpnRunnable");

  	//start the service
  	return START_STICKY;

  public void onDestroy() {
  	// TODO Auto-generated method stub
  	if (mThread != null) {

3. Permission

Service and Internet permission should be declared.

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

4. A Packet bypass Implementation

Using the service, we can build many kinds of interesting Apps, such as Proxy, Package Filter, Bandwidth Saver. There is an example (ToyVpn) in Android Samples for SDK (file path: sdk\samples\android-17\ToyVpn ), which can also be found in google source code here .

Here we are going to implement a packet bypass function, in which without the remote server, we bypass/filter packets that go through the VPN service.

The TUN is working on OSI layer 3, so what be read and write here is the IP packet.

From APP level, Android/Java do not support raw socket, so we can not directly bypass these IP packet into network interface.

But we can protect socket from VPN service, so its possible to use a protected socket to send/get the packet. TCP/UDP socket is working on OSI layer 4, so we need do a layer translate here:

  1. Get IP packet from TUN. Same as all VPN service does.
  2. Extract layer 4 information. Protocol type (e.g. TCP/UDP) and its payload is a must. As there is a handshake procedure in TCP, before getting actually payload data from it, we need to write back handshake packet first.
  3. Choose corresponding socket to send out the payload. As this step is working on layer 4, so we need to save the socket and try to get return data later. If there is any return data, we need to pass these packet to TUN.
  4. Get packet from socket, and build a layer 3 packet. First, we need to build a valid layer 4 packet. UDP is a bit easier as the 4 byte UDP header only contains source address, source port, destination address, destination port. TCP is more complex as it’s a state connection, the sequence number and acknowledge number should be properly set. Then, use the layer 4 packet as payload, we need to build a valid layer 3 packet.
  5. Write IP packet back to TUN. Same as all VPN service does.

5. Suggestions

Building these IP packet is not a easy thing, the suggestion is to use a packeting filter/capture tool likeWiresharkto check it first.

ByteBuffer is a good choice for packet build and value retrieve, but keep in mind that Java treat short/long as signed values, do convert it to unsigned first.

Take UDP packet first. After the TUN is connected, usually OS will send out some DNS queries, which is on UDP port 53. Thenslookup commandis a good test for it, if something goes wrong, a timeout error will be reported.

Linux Sysadmin Course Linux provides several powerful administrative tools and utilities which will help you to manage your systems effectively. If you don’t know what these tools are and how to use them, you could be spending lot of time trying to perform even the basic administrative tasks. The focus of this course is to help you understand system administration tools, which will help you to become an effective Linux system administrator.
Get the Linux Sysadmin Course Now!

If you enjoyed this article, you might also like..