public void addRoute(String dest, String mask, String gateway, String device) {
    CIDRIP route = new CIDRIP(dest, mask);
    boolean include = isAndroidTunDevice(device);

    NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32), false);

    if (mLocalIP == null) {
      VpnStatus.logError(
          "Local IP address unset but adding route?! This is broken! Please contact author with log");
      return;
    }
    NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP, true);
    if (localNet.containsNet(gatewayIP)) include = true;

    if (gateway != null && (gateway.equals("255.255.255.255") || gateway.equals(mRemoteGW)))
      include = true;

    if (route.len == 32 && !mask.equals("255.255.255.255")) {
      VpnStatus.logWarning(R.string.route_not_cidr, dest, mask);
    }

    if (route.normalise())
      VpnStatus.logWarning(R.string.route_not_netip, dest, route.len, route.mIp);

    mRoutes.addIP(route, include);
  }
  public ParcelFileDescriptor openTun() {

    // Debug.startMethodTracing(getExternalFilesDir(null).toString() + "/opentun.trace", 40* 1024 *
    // 1024);

    Builder builder = new Builder();

    VpnStatus.logInfo(R.string.last_openvpn_tun_config);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mProfile.mAllowLocalLAN) {
      allowAllAFFamilies(builder);
    }

    if (mLocalIP == null && mLocalIPv6 == null) {
      VpnStatus.logError(getString(R.string.opentun_no_ipaddr));
      return null;
    }

    if (mLocalIP != null) {
      addLocalNetworksToRoutes();
      try {
        builder.addAddress(mLocalIP.mIp, mLocalIP.len);
      } catch (IllegalArgumentException iae) {
        VpnStatus.logError(R.string.dns_add_error, mLocalIP, iae.getLocalizedMessage());
        return null;
      }
    }

    if (mLocalIPv6 != null) {
      String[] ipv6parts = mLocalIPv6.split("/");
      try {
        builder.addAddress(ipv6parts[0], Integer.parseInt(ipv6parts[1]));
      } catch (IllegalArgumentException iae) {
        VpnStatus.logError(R.string.ip_add_error, mLocalIPv6, iae.getLocalizedMessage());
        return null;
      }
    }

    for (String dns : mDnslist) {
      try {
        builder.addDnsServer(dns);
      } catch (IllegalArgumentException iae) {
        VpnStatus.logError(R.string.dns_add_error, dns, iae.getLocalizedMessage());
      }
    }

    String release = Build.VERSION.RELEASE;
    if ((Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT
            && !release.startsWith("4.4.3")
            && !release.startsWith("4.4.4")
            && !release.startsWith("4.4.5")
            && !release.startsWith("4.4.6"))
        && mMtu < 1280) {
      VpnStatus.logInfo(
          String.format(
              Locale.US,
              "Forcing MTU to 1280 instead of %d to workaround Android Bug #70916",
              mMtu));
      builder.setMtu(1280);
    } else {
      builder.setMtu(mMtu);
    }

    Collection<ipAddress> positiveIPv4Routes = mRoutes.getPositiveIPList();
    Collection<ipAddress> positiveIPv6Routes = mRoutesv6.getPositiveIPList();

    ipAddress multicastRange = new ipAddress(new CIDRIP("224.0.0.0", 3), true);

    for (NetworkSpace.ipAddress route : positiveIPv4Routes) {
      try {

        if (multicastRange.containsNet(route))
          VpnStatus.logDebug(R.string.ignore_multicast_route, route.toString());
        else builder.addRoute(route.getIPv4Address(), route.networkMask);
      } catch (IllegalArgumentException ia) {
        VpnStatus.logError(
            getString(R.string.route_rejected) + route + " " + ia.getLocalizedMessage());
      }
    }

    for (NetworkSpace.ipAddress route6 : positiveIPv6Routes) {
      try {
        builder.addRoute(route6.getIPv6Address(), route6.networkMask);
      } catch (IllegalArgumentException ia) {
        VpnStatus.logError(
            getString(R.string.route_rejected) + route6 + " " + ia.getLocalizedMessage());
      }
    }

    if ("samsung".equals(Build.BRAND)
        && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
        && mDnslist.size() >= 1) {
      // Check if the first DNS Server is in the VPN range
      try {
        ipAddress dnsServer = new ipAddress(new CIDRIP(mDnslist.get(0), 32), true);
        boolean dnsIncluded = false;
        for (ipAddress net : positiveIPv4Routes) {
          if (net.containsNet(dnsServer)) {
            dnsIncluded = true;
          }
        }
        if (!dnsIncluded) {
          String samsungwarning =
              String.format(
                  "Warning Samsung Android 5.0+ devices ignore DNS servers outside the VPN range. To enable DNS add a custom route to your DNS Server (%s) or change to a DNS inside your VPN range",
                  mDnslist.get(0));
          VpnStatus.logWarning(samsungwarning);
        }
      } catch (Exception e) {
        VpnStatus.logError("Error parsing DNS Server IP: " + mDnslist.get(0));
      }
    }

    if (mDomain != null) builder.addSearchDomain(mDomain);

    VpnStatus.logInfo(R.string.local_ip_info, mLocalIP.mIp, mLocalIP.len, mLocalIPv6, mMtu);
    VpnStatus.logInfo(R.string.dns_server_info, TextUtils.join(", ", mDnslist), mDomain);
    VpnStatus.logInfo(
        R.string.routes_info_incl,
        TextUtils.join(", ", mRoutes.getNetworks(true)),
        TextUtils.join(", ", mRoutesv6.getNetworks(true)));
    VpnStatus.logInfo(
        R.string.routes_info_excl,
        TextUtils.join(", ", mRoutes.getNetworks(false)),
        TextUtils.join(", ", mRoutesv6.getNetworks(false)));
    VpnStatus.logDebug(
        R.string.routes_debug,
        TextUtils.join(", ", positiveIPv4Routes),
        TextUtils.join(", ", positiveIPv6Routes));
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      setAllowedVpnPackages(builder);
    }

    String session = mProfile.mName;
    if (mLocalIP != null && mLocalIPv6 != null)
      session = getString(R.string.session_ipv6string, session, mLocalIP, mLocalIPv6);
    else if (mLocalIP != null) session = getString(R.string.session_ipv4string, session, mLocalIP);

    builder.setSession(session);

    // No DNS Server, log a warning
    if (mDnslist.size() == 0) VpnStatus.logInfo(R.string.warn_no_dns);

    mLastTunCfg = getTunConfigString();

    // Reset information
    mDnslist.clear();
    mRoutes.clear();
    mRoutesv6.clear();
    mLocalIP = null;
    mLocalIPv6 = null;
    mDomain = null;

    builder.setConfigureIntent(getLogPendingIntent());

    try {
      // Debug.stopMethodTracing();
      ParcelFileDescriptor tun = builder.establish();
      if (tun == null)
        throw new NullPointerException(
            "Android establish() method returned null (Really broken network configuration?)");
      return tun;
    } catch (Exception e) {
      VpnStatus.logError(R.string.tun_open_error);
      VpnStatus.logError(getString(R.string.error) + e.getLocalizedMessage());
      if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        VpnStatus.logError(R.string.tun_error_helpful);
      }
      return null;
    }
  }