@Override
  public RemoteAccessVpn createRemoteAccessVpn(
      long publicIpId, String ipRange, boolean openFirewall, long networkId)
      throws NetworkRuleConflictException {
    UserContext ctx = UserContext.current();
    Account caller = ctx.getCaller();

    // make sure ip address exists
    PublicIpAddress ipAddr = _networkMgr.getPublicIpAddress(publicIpId);
    if (ipAddr == null) {
      throw new InvalidParameterValueException(
          "Unable to create remote access vpn, invalid public IP address id" + publicIpId);
    }

    _accountMgr.checkAccess(caller, null, true, ipAddr);

    if (!ipAddr.readyToUse()) {
      throw new InvalidParameterValueException(
          "The Ip address is not ready to be used yet: " + ipAddr.getAddress());
    }

    IPAddressVO ipAddress = _ipAddressDao.findById(publicIpId);
    _networkMgr.checkIpForService(ipAddress, Service.Vpn, null);

    RemoteAccessVpnVO vpnVO = _remoteAccessVpnDao.findByPublicIpAddress(publicIpId);

    if (vpnVO != null) {
      // if vpn is in Added state, return it to the api
      if (vpnVO.getState() == RemoteAccessVpn.State.Added) {
        return vpnVO;
      }
      throw new InvalidParameterValueException(
          "A Remote Access VPN already exists for this public Ip address");
    }

    // TODO: assumes one virtual network / domr per account per zone
    vpnVO = _remoteAccessVpnDao.findByAccountAndNetwork(ipAddr.getAccountId(), networkId);
    if (vpnVO != null) {
      // if vpn is in Added state, return it to the api
      if (vpnVO.getState() == RemoteAccessVpn.State.Added) {
        return vpnVO;
      }
      throw new InvalidParameterValueException(
          "A Remote Access VPN already exists for this account");
    }

    // Verify that vpn service is enabled for the network
    Network network = _networkMgr.getNetwork(networkId);
    if (!_networkMgr.areServicesSupportedInNetwork(network.getId(), Service.Vpn)) {
      throw new InvalidParameterValueException(
          "Vpn service is not supported in network id=" + ipAddr.getAssociatedWithNetworkId());
    }

    if (ipRange == null) {
      ipRange = _clientIpRange;
    }
    String[] range = ipRange.split("-");
    if (range.length != 2) {
      throw new InvalidParameterValueException("Invalid ip range");
    }
    if (!NetUtils.isValidIp(range[0]) || !NetUtils.isValidIp(range[1])) {
      throw new InvalidParameterValueException("Invalid ip in range specification " + ipRange);
    }
    if (!NetUtils.validIpRange(range[0], range[1])) {
      throw new InvalidParameterValueException("Invalid ip range " + ipRange);
    }

    Pair<String, Integer> cidr = NetUtils.getCidr(network.getCidr());

    // FIXME: This check won't work for the case where the guest ip range
    // changes depending on the vlan allocated.
    String[] guestIpRange = NetUtils.getIpRangeFromCidr(cidr.first(), cidr.second());
    if (NetUtils.ipRangesOverlap(range[0], range[1], guestIpRange[0], guestIpRange[1])) {
      throw new InvalidParameterValueException(
          "Invalid ip range: "
              + ipRange
              + " overlaps with guest ip range "
              + guestIpRange[0]
              + "-"
              + guestIpRange[1]);
    }
    // TODO: check sufficient range
    // TODO: check overlap with private and public ip ranges in datacenter

    long startIp = NetUtils.ip2Long(range[0]);
    String newIpRange = NetUtils.long2Ip(++startIp) + "-" + range[1];
    String sharedSecret = PasswordGenerator.generatePresharedKey(_pskLength);
    _rulesMgr.reservePorts(
        ipAddr,
        NetUtils.UDP_PROTO,
        Purpose.Vpn,
        openFirewall,
        caller,
        NetUtils.VPN_PORT,
        NetUtils.VPN_L2TP_PORT,
        NetUtils.VPN_NATT_PORT);
    vpnVO =
        new RemoteAccessVpnVO(
            ipAddr.getAccountId(),
            ipAddr.getDomainId(),
            ipAddr.getAssociatedWithNetworkId(),
            publicIpId,
            range[0],
            newIpRange,
            sharedSecret);
    return _remoteAccessVpnDao.persist(vpnVO);
  }