Class VpnTunnel

java.lang.Object
com.codename1.vpn.tunnel.VpnTunnel

public abstract class VpnTunnel extends Object

The packet loop, written once for both platforms.

public class MyTunnel extends VpnTunnel {
    protected void onStart(TunnelConfiguration cfg) { }
    protected void onPacket(PacketBuffer p) { forward(p); }
    protected void onStop(TunnelStopReason r) { }
}
Where this runs

On Android, in the app's own process, inside the port's VpnService. On iOS, in a Network Extension: a separate process, with a virtual machine of its own, which the build generates for a project that sets ios.vpn.tunnel.

Write the tunnel as though it ran somewhere else, because on one of the two platforms it does. Everything it needs travels in TunnelConfiguration.getData(); a static the app set happens to be there on Android and is not a thing to rely on -- on iOS it is not there at all, and reaching for the app's own classes drags them into the extension's translation, where the ones backed by UIKit fail its link.

What is not there, on iOS

The extension carries the translated program and the virtual machine and no networking stack. Socket, ConnectionRequest and anything else that reaches the implementation find nothing there, and ParparVM's java.net is URI and URL -- there are no sockets in it either. So an iOS tunnel inspects, rewrites, drops and forward(PacketBuffer)s packets; it cannot open a connection to a remote server. On Android it can, because it runs in the app's own process. A tunnel that needs to relay is not the same class on both platforms, and the difference is worth an interface rather than a surprise.

Constructing it

On Android the app constructs the tunnel and hands it to Tunnels.start(VpnTunnel, TunnelSetup). On iOS the EXTENSION constructs it, because Tunnels.start(VpnTunnel, TunnelSetup) ran in another process that has since gone away -- so an iOS tunnel needs an accessible no-argument constructor, and the class named by ios.vpn.tunnel.class is the one it calls. A tunnel with only a parameterised constructor fails the build rather than the device.

What is expensive

A host process for a tunnel runs under a memory budget far below an app's, and is killed rather than warned when it exceeds it. The buffers handed to onPacket(PacketBuffer) are reused for exactly that reason, so copying every packet -- or holding one past the call -- gives back the headroom the pooling was for. See PacketBuffer.

  • Constructor Details

    • VpnTunnel

      public VpnTunnel()
  • Method Details

    • onStart

      protected abstract void onStart(TunnelConfiguration configuration)
      The tunnel is up and the platform has configured the link.
    • onPacket

      protected abstract void onPacket(PacketBuffer packet)

      One packet arrived from the device, bound for the far end.

      The buffer is REUSED once this returns; see PacketBuffer.

    • onStop

      protected abstract void onStop(TunnelStopReason reason)
      The tunnel is going down, for the given reason.
    • forward

      protected final void forward(PacketBuffer packet)

      Sends a packet back to the device.

      Safe to call with the buffer onPacket was given, which is the ordinary pass-through case and copies nothing.

    • forward

      protected final void forward(byte[] packet, int offset, int length)
      Sends bytes the tunnel produced itself.