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);
  }
  private String getTunConfigString() {
    // The format of the string is not important, only that
    // two identical configurations produce the same result
    String cfg = "TUNCFG UNQIUE STRING ips:";

    if (mLocalIP != null) cfg += mLocalIP.toString();
    if (mLocalIPv6 != null) cfg += mLocalIPv6;

    cfg +=
        "routes: "
            + TextUtils.join("|", mRoutes.getNetworks(true))
            + TextUtils.join("|", mRoutesv6.getNetworks(true));
    cfg +=
        "excl. routes:"
            + TextUtils.join("|", mRoutes.getNetworks(false))
            + TextUtils.join("|", mRoutesv6.getNetworks(false));
    cfg += "dns: " + TextUtils.join("|", mDnslist);
    cfg += "domain: " + mDomain;
    cfg += "mtu: " + mMtu;
    return cfg;
  }
  public void setLocalIP(String local, String netmask, int mtu, String mode) {
    mLocalIP = new CIDRIP(local, netmask);
    mMtu = mtu;
    mRemoteGW = null;

    long netMaskAsInt = CIDRIP.getInt(netmask);

    if (mLocalIP.len == 32 && !netmask.equals("255.255.255.255")) {
      // get the netmask as IP

      int masklen;
      long mask;
      if ("net30".equals(mode)) {
        masklen = 30;
        mask = 0xfffffffc;
      } else {
        masklen = 31;
        mask = 0xfffffffe;
      }

      // Netmask is Ip address +/-1, assume net30/p2p with small net
      if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask)) {
        mLocalIP.len = masklen;
      } else {
        mLocalIP.len = 32;
        if (!"p2p".equals(mode)) VpnStatus.logWarning(R.string.ip_not_cidr, local, netmask, mode);
      }
    }
    if (("p2p".equals(mode) && mLocalIP.len < 32) || ("net30".equals(mode) && mLocalIP.len < 30)) {
      VpnStatus.logWarning(R.string.ip_looks_like_subnet, local, netmask, mode);
    }

    /* Workaround for Lollipop, it  does not route traffic to the VPNs own network mask */
    if (mLocalIP.len <= 31 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      CIDRIP interfaceRoute = new CIDRIP(mLocalIP.mIp, mLocalIP.len);
      interfaceRoute.normalise();
      addRoute(interfaceRoute);
    }

    // Configurations are sometimes really broken...
    mRemoteGW = netmask;
  }