package org.strongswan.android.logic;
import java.io.File;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import org.strongswan.android.ui.MainActivity;
import org.strongswan.android.utils.SettingsWriter;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.os.ParcelFileDescriptor;
import android.security.KeyChain;
import android.security.KeyChainException;
+import android.system.OsConstants;
import android.util.Log;
public class CharonVpnService extends VpnService implements Runnable
startConnection(mCurrentProfile);
mIsDisconnecting = false;
- BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName());
+ BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName(), mCurrentProfile.getSplitTunneling());
if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD)))
{
Log.i(TAG, "charon started");
public class BuilderAdapter
{
private final String mName;
+ private final Integer mSplitTunneling;
private VpnService.Builder mBuilder;
private BuilderCache mCache;
private BuilderCache mEstablishedCache;
- public BuilderAdapter(String name)
+ public BuilderAdapter(String name, Integer splitTunneling)
{
mName = name;
+ mSplitTunneling = splitTunneling;
mBuilder = createBuilder(name);
- mCache = new BuilderCache();
+ mCache = new BuilderCache(mSplitTunneling);
}
private VpnService.Builder createBuilder(String name)
{
try
{
- mBuilder.addAddress(address, prefixLength);
mCache.addAddress(address, prefixLength);
}
catch (IllegalArgumentException ex)
try
{
mBuilder.addDnsServer(address);
+ mCache.recordAddressFamily(address);
}
catch (IllegalArgumentException ex)
{
{
try
{
- mBuilder.addRoute(address, prefixLength);
mCache.addRoute(address, prefixLength);
}
catch (IllegalArgumentException ex)
{
try
{
- mBuilder.setMtu(mtu);
mCache.setMtu(mtu);
}
catch (IllegalArgumentException ex)
ParcelFileDescriptor fd;
try
{
+ mCache.applyData(mBuilder);
fd = mBuilder.establish();
}
catch (Exception ex)
* builder anymore, but we might need another when reestablishing */
mBuilder = createBuilder(mName);
mEstablishedCache = mCache;
- mCache = new BuilderCache();
+ mCache = new BuilderCache(mSplitTunneling);
return fd.detachFd();
}
public class BuilderCache
{
private final List<PrefixedAddress> mAddresses = new ArrayList<PrefixedAddress>();
- private final List<PrefixedAddress> mRoutes = new ArrayList<PrefixedAddress>();
+ private final List<PrefixedAddress> mRoutesIPv4 = new ArrayList<PrefixedAddress>();
+ private final List<PrefixedAddress> mRoutesIPv6 = new ArrayList<PrefixedAddress>();
+ private final int mSplitTunneling;
private int mMtu;
+ private boolean mIPv4Seen, mIPv6Seen;
+
+ public BuilderCache(Integer splitTunneling)
+ {
+ mSplitTunneling = splitTunneling != null ? splitTunneling : 0;
+ }
public void addAddress(String address, int prefixLength)
{
mAddresses.add(new PrefixedAddress(address, prefixLength));
+ recordAddressFamily(address);
}
public void addRoute(String address, int prefixLength)
{
- mRoutes.add(new PrefixedAddress(address, prefixLength));
+ try
+ {
+ if (isIPv6(address))
+ {
+ mRoutesIPv6.add(new PrefixedAddress(address, prefixLength));
+ }
+ else
+ {
+ mRoutesIPv4.add(new PrefixedAddress(address, prefixLength));
+ }
+ }
+ catch (UnknownHostException ex)
+ {
+ ex.printStackTrace();
+ }
}
public void setMtu(int mtu)
mMtu = mtu;
}
+ public void recordAddressFamily(String address)
+ {
+ try
+ {
+ if (isIPv6(address))
+ {
+ mIPv6Seen = true;
+ }
+ else
+ {
+ mIPv4Seen = true;
+ }
+ }
+ catch (UnknownHostException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void applyData(VpnService.Builder builder)
{
for (PrefixedAddress address : mAddresses)
{
builder.addAddress(address.mAddress, address.mPrefix);
}
- for (PrefixedAddress route : mRoutes)
+ /* add routes depending on whether split tunneling is allowed or not,
+ * that is, whether we have to handle and block non-VPN traffic */
+ if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) == 0)
+ {
+ if (mIPv4Seen)
+ { /* split tunneling is used depending on the routes */
+ for (PrefixedAddress route : mRoutesIPv4)
+ {
+ builder.addRoute(route.mAddress, route.mPrefix);
+ }
+ }
+ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ { /* allow traffic that would otherwise be blocked to bypass the VPN */
+ builder.allowFamily(OsConstants.AF_INET);
+ }
+ }
+ else if (mIPv4Seen)
+ { /* only needed if we've seen any addresses. otherwise, traffic
+ * is blocked by default (we also install no routes in that case) */
+ builder.addRoute("0.0.0.0", 0);
+ }
+ /* same thing for IPv6 */
+ if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) == 0)
+ {
+ if (mIPv6Seen)
+ {
+ for (PrefixedAddress route : mRoutesIPv6)
+ {
+ builder.addRoute(route.mAddress, route.mPrefix);
+ }
+ }
+ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ {
+ builder.allowFamily(OsConstants.AF_INET6);
+ }
+ }
+ else if (mIPv6Seen)
{
- builder.addRoute(route.mAddress, route.mPrefix);
+ builder.addRoute("::", 0);
}
builder.setMtu(mMtu);
}
+ private boolean isIPv6(String address) throws UnknownHostException
+ {
+ InetAddress addr = InetAddress.getByName(address);
+ if (addr instanceof Inet4Address)
+ {
+ return false;
+ }
+ else if (addr instanceof Inet6Address)
+ {
+ return true;
+ }
+ return false;
+ }
+
private class PrefixedAddress
{
public String mAddress;