Skip to content

Commit

Permalink
Merge branch 'feature/tap'
Browse files Browse the repository at this point in the history
  • Loading branch information
KostyaEsmukov committed Jan 6, 2017
2 parents 8ad3dfd + 7a97147 commit c31a68b
Show file tree
Hide file tree
Showing 14 changed files with 835 additions and 69 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
.DS_Store
/build
*.iml
*.apk
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import java.util.ArrayList;
import java.util.List;

import ru.esmukov.kpfu.lightningrodandroidvpnpoc.packetfilter.PacketFilter;
import ru.esmukov.kpfu.lightningrodandroidvpnpoc.packetfilter.l3.L3PacketFilter;
import ru.esmukov.kpfu.lightningrodandroidvpnpoc.packetfilter.l2.L2ToL3PacketFilter;

/**
* Created by kostya on 09/11/2016.
*/
Expand Down Expand Up @@ -33,6 +37,11 @@ public class SocatServerConnectionInfo {
// o,pi
private boolean mPacketInfo = false;

// Tap -- L2 tunnel.
// Tun (default) -- L3 tunnel -- the same level as the VpnService.
// o,tap || o,tun
private boolean mIsTap = false;

/**
* @param configuration Example: "c,192.168.1.1,12312,tcp a,10.123.123.2,24 r,10.123.123.0,24"
*/
Expand Down Expand Up @@ -79,7 +88,15 @@ public SocatServerConnectionInfo(String configuration) {
this.mVpnServiceName = fields[1];
break;
case 'o':
this.mPacketInfo = "pi".equals(fields[1]);
if ("pi".equalsIgnoreCase(fields[1]))
mPacketInfo = true;
if ("no_pi".equalsIgnoreCase(fields[1]))
mPacketInfo = false;

if ("tap".equalsIgnoreCase(fields[1]))
mIsTap = true;
if ("tun".equalsIgnoreCase(fields[1]))
mIsTap = false;
break;
}
} catch (Exception e) {
Expand Down Expand Up @@ -118,18 +135,23 @@ public String getVpnServiceName() {
return mVpnServiceName;
}

public boolean isPacketInfo() {
return mPacketInfo;
}

public RemoteConnectionInfo getRemoteConnectionInfo() {
return mRemoteConnectionInfo;
}

public PacketFilter createNewPacketFilter() {
if (mIsTap) {
return new L2ToL3PacketFilter(mPacketInfo);
} else {
return new L3PacketFilter(mPacketInfo);
}
}

public interface RemoteConnectionInfo {
SocatProtocol getSocatProtocol();

boolean isConnectSocket();

InetSocketAddress getInetSocketAddress();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.io.FileOutputStream;
import java.nio.ByteBuffer;

import ru.esmukov.kpfu.lightningrodandroidvpnpoc.packetfilter.PacketFilter;
import ru.esmukov.kpfu.lightningrodandroidvpnpoc.serverconnection.ServerConnection;
import ru.esmukov.kpfu.lightningrodandroidvpnpoc.serverconnection.ServerConnectionFactory;

Expand Down Expand Up @@ -140,6 +141,9 @@ private boolean createVpnTunnel() throws Exception {
FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor());
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(32767);

PacketFilter packetFilter = mSocatServerConnectionInfo.createNewPacketFilter();

// We keep forwarding packets till something goes wrong.
while (!Thread.interrupted()) {
// Packet info usage (search for `struct tun_pi`):
Expand All @@ -151,84 +155,38 @@ private boolean createVpnTunnel() throws Exception {

// Tun -> User
// Read the outgoing packet from the input stream.
packet.clear();
int length = in.read(packet.array());
if (length > 0) {
if (mSocatServerConnectionInfo.isPacketInfo()) {

// todo looks like we don't need to ever set the TUN_PKT_STRIP flag, right?
// see:
// static ssize_t tun_put_user(struct tun_struct *tun,
// struct tun_file *tfile,
// struct sk_buff *skb,
// struct iov_iter *iter)
if (length > 0) {
packet.limit(length);

// http://lxr.free-electrons.com/source/include/uapi/linux/if_ether.h
short flags = 0;
short proto = 0x0800; // ETH_P_IP. 0800 - ip4, 86DD - ip6
if (packetFilter.fromLocalToRemote(packet)) {
tunnel.write(packet);
}

packet.limit(length + 4);
for (int i = length - 1; i >= 0; i--) {
packet.put(i + 4, packet.get(i));
}
packet.putShort(0, flags);
packet.putShort(2, proto);
// packet.clear();
// There might be more outgoing packets.
idle = false;
}

length += 4;
}
// Write the outgoing packet to the tunnel.
packet.limit(length);
while (packetFilter.nextCustomPacketToRemote(packet)) {
tunnel.write(packet);
packet.clear();
// There might be more outgoing packets.
idle = false;
}

// User -> Tun
// Read the incoming packet from the tunnel.
packet.clear();
length = tunnel.read(packet);
boolean skip = false;
if (length > 0) {
if (mSocatServerConnectionInfo.isPacketInfo()) {
if (length < 4) {
Log.i(TAG, "PI: dropped packet with invalid length (<4)");
}

/*
struct tun_pi {
__u16 flags;
__be16 proto;
};
*/
// todo check byte order
int flags = ((packet.get(0) & 0xFF) << 8) | (packet.get(1) & 0xFF);
int proto = ((packet.get(2) & 0xFF) << 8) | (packet.get(3) & 0xFF);

if (flags != 0) {
Log.w(TAG, "PI: received non-zero flags: " + flags);
}

if (proto != 0x0800) {
// skin non-IP packets
Log.w(TAG, "PI: received non-IP proto: " + proto);
skip = true;
}

// todo check PI protocol as per
// static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
// void *msg_control, struct iov_iter *from,
// int noblock)
packet.limit(length);

for (int i = 0; i < length; i++) {
packet.put(i, packet.get(i + 4));
}
packet.limit(length - 4);
length -= 4;
if (packetFilter.fromRemoteToLocal(packet)) {
out.write(packet.array(), 0, packet.limit());
}
if (!skip) {
// Write the incoming packet to the output stream.
out.write(packet.array(), 0, length);
}
packet.clear();

// packet.clear();
// There might be more incoming packets.
idle = false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ru.esmukov.kpfu.lightningrodandroidvpnpoc.packetfilter;

import java.nio.ByteBuffer;

/**
* Created by kostya on 16/12/2016.
*/

public class ByteBufferUtils {

public static void moveLeft(ByteBuffer buffer, int shiftBytes) {
int length = buffer.limit();

for (int i = shiftBytes; i < length; i++) {
buffer.put(i - shiftBytes, buffer.get(i));
}
buffer.position(0);
buffer.limit(length - shiftBytes);
}

public static void moveRight(ByteBuffer buffer, int shiftBytes) {
int length = buffer.limit();

buffer.limit(length + shiftBytes);
for (int i = length - 1; i >= 0; i--) {
buffer.put(i + shiftBytes, buffer.get(i));
}
buffer.position(0);
}

public static void copy(ByteBuffer from, ByteBuffer to) {
to.limit(from.limit() + 1); // java.nio.BufferOverflowException
to.put(from.array(), 0, from.limit());
to.limit(from.limit());
to.position(0);
}

public static void put6bytes(ByteBuffer byteBuffer, long l) {
// first byte
byteBuffer.put((byte) ((l >>> (8 * 5)) & 0xFF));
// second byte
byteBuffer.put((byte) ((l >>> (8 * 4)) & 0xFF));
// rest 4 bytes
byteBuffer.putInt((int) (l & 0xFFFFFFFFL));
}

public static long get6bytes(ByteBuffer byteBuffer) {
long l = 0;
l |= (long) (byteBuffer.get() & 0xFF) << (8 * 5);
l |= (long) (byteBuffer.get() & 0xFF) << (8 * 4);
l |= byteBuffer.getInt() & 0xFFFFFFFFL;
return l;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ru.esmukov.kpfu.lightningrodandroidvpnpoc.packetfilter;

import java.nio.ByteBuffer;

/**
* Created by kostya on 16/12/2016.
*/

/**
* Filter (and change, if needed) a packet between remote side and a local VpnService TUN.
*/
public interface PacketFilter {
/**
* Outgoing L3 IP4 packet
*
* @param ip4Packet Local IP4 packet. This method should convert this buffer
* to a format expected by the remote side
* @return Should the packet be sent? Drop it otherwise.
*/
boolean fromLocalToRemote(ByteBuffer ip4Packet);

/**
* Incoming packet
*
* @param remotePacket This method should convert this raw packet received
* from the remote side to a local IP4 packet.
* @return Should the packet be accepted? Drop it otherwise.
*/
boolean fromRemoteToLocal(ByteBuffer remotePacket);

/**
* Initiate custom outgoing packets
*
* @param remotePacket
* @return Is a packet to be sent put to the `remotePacket` buffer
*/
boolean nextCustomPacketToRemote(ByteBuffer remotePacket);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package ru.esmukov.kpfu.lightningrodandroidvpnpoc.packetfilter;

import android.util.Log;

import java.nio.ByteBuffer;

/**
* Created by kostya on 06/01/2017.
*/

public class PacketInfo {
private static final String TAG = "PacketInfo";

public static void prepend(ByteBuffer packet, int etherType) {
// todo looks like we don't need to ever set the TUN_PKT_STRIP flag, right?
// see:
// static ssize_t tun_put_user(struct tun_struct *tun,
// struct tun_file *tfile,
// struct sk_buff *skb,
// struct iov_iter *iter)

// http://lxr.free-electrons.com/source/include/uapi/linux/if_ether.h
short flags = 0;

ByteBufferUtils.moveRight(packet, 4);

packet.putShort(0, flags);

// Ethertype as per http://lxr.free-electrons.com/source/include/uapi/linux/if_ether.h
packet.putShort(2, (short) etherType);
}

public static PacketInfoHeader strip(ByteBuffer remotePacket) throws Exception {
int length = remotePacket.limit();

if (length < 4) {
throw new Exception("packet with invalid length (<4)");
}

/*
struct tun_pi {
__u16 flags;
__be16 proto;
};
*/

int flags = ((remotePacket.get(0) & 0xFF) << 8) | (remotePacket.get(1) & 0xFF);
int proto = ((remotePacket.get(2) & 0xFF) << 8) | (remotePacket.get(3) & 0xFF);

if (flags != 0) {
Log.w(TAG, "received non-zero flags: " + flags);
}

// todo ?? check PI protocol as per
// static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
// void *msg_control, struct iov_iter *from,
// int noblock)

ByteBufferUtils.moveLeft(remotePacket, 4);

return new PacketInfoHeader(proto);
}

public static class PacketInfoHeader {
private int mProto;

private PacketInfoHeader(int mProto) {
this.mProto = mProto;
}

public int getProto() {
return mProto;
}
}

}
Loading

0 comments on commit c31a68b

Please sign in to comment.