content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  package com.cloud.test;
  
  import java.lang.reflect.Array;
  import java.sql.Connection;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Statement;
  import java.util.List;
  import java.util.UUID;
  import java.util.Vector;
  
  import com.cloud.utils.component.ComponentContext;
  import com.cloud.utils.db.DB;
  import com.cloud.utils.db.TransactionLegacy;
  import com.cloud.utils.net.NetUtils;
  
  public class IPRangeConfig {
  
      public static void main(String[] args) {
          IPRangeConfig config = ComponentContext.inject(IPRangeConfig.class);
          config.run(args);
          System.exit(0);
      }
  
      private String usage() {
          return "Usage: ./change_ip_range.sh [add|delete] [public zone | private pod zone] startIP endIP";
      }
  
      public void run(String[] args) {
          if (args.length < 2) {
              printError(usage());
          }
  
          String op = args[0];
          String type = args[1];
  
          if (type.equals("public")) {
              if (args.length != 4 && args.length != 5) {
                  printError(usage());
              }
              String zone = args[2];
              String startIP = args[3];
              String endIP = null;
              if (args.length == 5) {
                  endIP = args[4];
              }
  
              String result = checkErrors(type, op, null, zone, startIP, endIP);
              if (!result.equals("success")) {
                  printError(result);
              }
  
              long zoneId = PodZoneConfig.getZoneId(zone);
              result = changeRange(op, "public", -1, zoneId, startIP, endIP, null, -1);
              result = result.replaceAll("<br>", "/n");
              System.out.println(result);
          } else if (type.equals("private")) {
              if (args.length != 5 && args.length != 6) {
                  printError(usage());
              }
              String pod = args[2];
              String zone = args[3];
              ;
              String startIP = args[4];
              String endIP = null;
              if (args.length == 6) {
                  endIP = args[5];
              }
  
              String result = checkErrors(type, op, pod, zone, startIP, endIP);
              if (!result.equals("success")) {
                  printError(result);
              }
  
              long podId = PodZoneConfig.getPodId(pod, zone);
              long zoneId = PodZoneConfig.getZoneId(zone);
              result = changeRange(op, "private", podId, zoneId, startIP, endIP, null, -1);
              result = result.replaceAll("<br>", "/n");
              System.out.println(result);
          } else {
              printError(usage());
          }
      }
  
      public List<String> changePublicIPRangeGUI(String op, String zone, String startIP, String endIP, long physicalNetworkId) {
          String result = checkErrors("public", op, null, zone, startIP, endIP);
          if (!result.equals("success")) {
              return DatabaseConfig.genReturnList("false", result);
          }
  
          long zoneId = PodZoneConfig.getZoneId(zone);
          result = changeRange(op, "public", -1, zoneId, startIP, endIP, null, physicalNetworkId);
  
          return DatabaseConfig.genReturnList("true", result);
      }
  
      public List<String> changePrivateIPRangeGUI(String op, String pod, String zone, String startIP, String endIP) {
          String result = checkErrors("private", op, pod, zone, startIP, endIP);
          if (!result.equals("success")) {
              return DatabaseConfig.genReturnList("false", result);
          }
  
          long podId = PodZoneConfig.getPodId(pod, zone);
          long zoneId = PodZoneConfig.getZoneId(zone);
          result = changeRange(op, "private", podId, zoneId, startIP, endIP, null, -1);
  
          return DatabaseConfig.genReturnList("true", result);
      }
  
      private String checkErrors(String type, String op, String pod, String zone, String startIP, String endIP) {
          if (!op.equals("add") && !op.equals("delete")) {
              return usage();
          }
  
          if (type.equals("public")) {
              // Check that the zone is valid
              if (!PodZoneConfig.validZone(zone)) {
                  return "Please specify a valid zone.";
              }
          } else if (type.equals("private")) {
              // Check that the pod and zone are valid
              if (!PodZoneConfig.validZone(zone)) {
                  return "Please specify a valid zone.";
              }
              if (!PodZoneConfig.validPod(pod, zone)) {
                  return "Please specify a valid pod.";
              }
          }
  
          if (!validIP(startIP)) {
              return "Please specify a valid start IP";
          }
  
          if (!validOrBlankIP(endIP)) {
              return "Please specify a valid end IP";
          }
  
          // Check that the IPs that are being added are compatible with either the zone's public netmask, or the pod's CIDR
          if (type.equals("public")) {
              // String publicNetmask = getPublicNetmask(zone);
              // String publicGateway = getPublicGateway(zone);
  
              // if (publicNetmask == null) return "Please ensure that your zone's public net mask is specified";
              // if (!sameSubnet(startIP, endIP, publicNetmask)) return "Please ensure that your start IP and end IP are in the same subnet, as per the zone's netmask.";
              // if (!sameSubnet(startIP, publicGateway, publicNetmask)) return "Please ensure that your start IP is in the same subnet as your zone's gateway, as per the zone's netmask.";
              // if (!sameSubnet(endIP, publicGateway, publicNetmask)) return "Please ensure that your end IP is in the same subnet as your zone's gateway, as per the zone's netmask.";
          } else if (type.equals("private")) {
              String cidrAddress = getCidrAddress(pod, zone);
              long cidrSize = getCidrSize(pod, zone);
  
              if (!sameSubnetCIDR(startIP, endIP, cidrSize)) {
                  return "Please ensure that your start IP and end IP are in the same subnet, as per the pod's CIDR size.";
              }
              if (!sameSubnetCIDR(startIP, cidrAddress, cidrSize)) {
                  return "Please ensure that your start IP is in the same subnet as the pod's CIDR address.";
              }
              if (!sameSubnetCIDR(endIP, cidrAddress, cidrSize)) {
                  return "Please ensure that your end IP is in the same subnet as the pod's CIDR address.";
              }
          }
  
          if (!validIPRange(startIP, endIP)) {
              return "Please specify a valid IP range.";
          }
  
          return "success";
      }
  
      private String genChangeRangeSuccessString(List<String> problemIPs, String op) {
          if (problemIPs == null) {
              return "";
          }
  
          if (problemIPs.size() == 0) {
              if (op.equals("add")) {
                  return "Successfully added all IPs in the specified range.";
              } else if (op.equals("delete")) {
                  return "Successfully deleted all IPs in the specified range.";
              } else {
                  return "";
              }
          } else {
              String successString = "";
              if (op.equals("add")) {
                  successString += "Failed to add the following IPs, because they are already in the database: <br><br>";
              } else if (op.equals("delete")) {
                  successString += "Failed to delete the following IPs, because they are in use: <br><br>";
              }
  
              for (int i = 0; i < problemIPs.size(); i++) {
                  successString += problemIPs.get(i);
                  if (i != (problemIPs.size() - 1)) {
                      successString += ", ";
                  }
              }
  
              successString += "<br><br>";
  
              if (op.equals("add")) {
                  successString += "Successfully added all other IPs in the specified range.";
              } else if (op.equals("delete")) {
                  successString += "Successfully deleted all other IPs in the specified range.";
              }
  
              return successString;
          }
      }
  
      private String changeRange(String op, String type, long podId, long zoneId, String startIP, String endIP, Long networkId, long physicalNetworkId) {
  
          // Go through all the IPs and add or delete them
          List<String> problemIPs = null;
          if (op.equals("add")) {
              problemIPs = saveIPRange(type, podId, zoneId, 1, startIP, endIP, networkId, physicalNetworkId);
          } else if (op.equals("delete")) {
              problemIPs = deleteIPRange(type, podId, zoneId, 1, startIP, endIP);
          }
  
          if (problemIPs == null) {
              return null;
          } else {
              return genChangeRangeSuccessString(problemIPs, op);
          }
      }
  
      private String genSuccessString(Vector<String> problemIPs, String op) {
          if (problemIPs == null) {
              return "";
          }
  
          if (problemIPs.size() == 0) {
              if (op.equals("add")) {
                  return "Successfully added all IPs in the specified range.";
              } else if (op.equals("delete")) {
                  return "Successfully deleted all IPs in the specified range.";
              } else {
                  return "";
              }
          } else {
              String successString = "";
              if (op.equals("add")) {
                  successString += "Failed to add the following IPs, because they are already in the database: <br><br>";
              } else if (op.equals("delete")) {
                  successString += "Failed to delete the following IPs, because they are in use: <br><br>";
              }
  
              for (int i = 0; i < problemIPs.size(); i++) {
                  successString += problemIPs.elementAt(i);
                  if (i != (problemIPs.size() - 1)) {
                      successString += ", ";
                  }
              }
  
              successString += "<br><br>";
  
              if (op.equals("add")) {
                  successString += "Successfully added all other IPs in the specified range.";
              } else if (op.equals("delete")) {
                  successString += "Successfully deleted all other IPs in the specified range.";
              }
  
              return successString;
          }
      }
  
      public static String getCidrAddress(String pod, String zone) {
          long dcId = PodZoneConfig.getZoneId(zone);
          String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\"";
          String errorMsg = "Could not read CIDR address for pod/zone: " + pod + "/" + zone + " from database. Please contact Cloud Support.";
          return DatabaseConfig.getDatabaseValueString(selectSql, "cidr_address", errorMsg);
      }
  
      public static long getCidrSize(String pod, String zone) {
          long dcId = PodZoneConfig.getZoneId(zone);
          String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\"";
          String errorMsg = "Could not read CIDR address for pod/zone: " + pod + "/" + zone + " from database. Please contact Cloud Support.";
          return DatabaseConfig.getDatabaseValueLong(selectSql, "cidr_size", errorMsg);
      }
  
      @DB
      protected Vector<String> deleteIPRange(String type, long podId, long zoneId, long vlanDbId, String startIP, String endIP) {
          long startIPLong = NetUtils.ip2Long(startIP);
          long endIPLong = startIPLong;
          if (endIP != null) {
              endIPLong = NetUtils.ip2Long(endIP);
          }
  
          TransactionLegacy txn = TransactionLegacy.currentTxn();
          Vector<String> problemIPs = null;
          if (type.equals("public")) {
              problemIPs = deletePublicIPRange(txn, startIPLong, endIPLong, vlanDbId);
          } else if (type.equals("private")) {
              problemIPs = deletePrivateIPRange(txn, startIPLong, endIPLong, podId, zoneId);
          }
  
          return problemIPs;
      }
  
      public Vector<String> updatePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long vlanDbId, boolean forSystemvms) {
          String updateSql = "UPDATE `cloud`.`user_ip_address` SET forsystemvms = ? WHERE public_ip_address = ? AND vlan_db_id = ?";
  
          Vector<String> problemIPs = new Vector<String>();
          Connection conn = null;
          try {
              conn = txn.getConnection();
          }
          catch (SQLException e) {
              System.out.println("updatePublicIPRange. Exception: " + e.getMessage());
              return null;
          }
          try (PreparedStatement stmt = conn.prepareStatement(updateSql);) {
              while (startIP <= endIP) {
                  stmt.clearParameters();
                  stmt.setBoolean(1, forSystemvms);
                  stmt.setString(2, NetUtils.long2Ip(startIP));
                  stmt.setLong(3, vlanDbId);
                  stmt.executeUpdate();
                  startIP += 1;
              }
          } catch (Exception ex) {
              System.out.println("updatePublicIPRange. Exception: " + ex.getMessage());
              return null;
          }
  
          return problemIPs;
      }
  
      public Vector<String> deletePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long vlanDbId) {
          String deleteSql = "DELETE FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_db_id = ?";
          String isPublicIPAllocatedSelectSql = "SELECT * FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_db_id = ?";
  
          Vector<String> problemIPs = new Vector<String>();
          Connection conn = null;
          try {
              conn = txn.getConnection();
          }
          catch (SQLException e) {
              System.out.println("deletePublicIPRange. Exception: " + e.getMessage());
              return null;
          }
          try (PreparedStatement stmt = conn.prepareStatement(deleteSql);
              PreparedStatement isAllocatedStmt = conn.prepareStatement(isPublicIPAllocatedSelectSql);) {
              while (startIP <= endIP) {
                  if (!isPublicIPAllocated(NetUtils.long2Ip(startIP), vlanDbId, isAllocatedStmt)) {
                          stmt.clearParameters();
                          stmt.setString(1, NetUtils.long2Ip(startIP));
                          stmt.setLong(2, vlanDbId);
                          stmt.executeUpdate();
                  }
                  else {
                      problemIPs.add(NetUtils.long2Ip(startIP));
                  }
                  startIP += 1;
              }
          } catch (Exception ex) {
             System.out.println("deletePublicIPRange. Exception: " + ex.getMessage());
             return null;
          }
  
          return problemIPs;
      }
  
      private Vector<String> deletePrivateIPRange(TransactionLegacy txn, long startIP, long endIP, long podId, long zoneId) {
          String deleteSql = "DELETE FROM `cloud`.`op_dc_ip_address_alloc` WHERE ip_address = ? AND pod_id = ? AND data_center_id = ?";
          String isPrivateIPAllocatedSelectSql = "SELECT * FROM `cloud`.`op_dc_ip_address_alloc` WHERE ip_address = ? AND data_center_id = ? AND pod_id = ?";
          Vector<String> problemIPs = new Vector<String>();
          try {
              Connection conn = txn.getConnection();
              try (PreparedStatement stmt = conn.prepareStatement(deleteSql);
                   PreparedStatement isAllocatedStmt = conn.prepareStatement(isPrivateIPAllocatedSelectSql);) {
                  while (startIP <= endIP) {
                      if (!isPrivateIPAllocated(NetUtils.long2Ip(startIP), podId, zoneId, isAllocatedStmt)) {
                          stmt.clearParameters();
                          stmt.setString(1, NetUtils.long2Ip(startIP));
                          stmt.setLong(2, podId);
                          stmt.setLong(3, zoneId);
                          stmt.executeUpdate();
                      } else {
                          problemIPs.add(NetUtils.long2Ip(startIP));
                      }
                      startIP += 1;
                  }
              } catch (SQLException e) {
                  System.out.println("deletePrivateIPRange. Exception: " + e.getMessage());
                  printError("deletePrivateIPRange. Exception: " + e.getMessage());
              }
          } catch (SQLException e) {
              System.out.println("deletePrivateIPRange. Exception: " + e.getMessage());
              printError("deletePrivateIPRange. Exception: " + e.getMessage());
          }
          return problemIPs;
      }
  
      private boolean isPublicIPAllocated(String ip, long vlanDbId, PreparedStatement stmt) {
         try {
             stmt.clearParameters();
             stmt.setString(1, ip);
             stmt.setLong(2, vlanDbId);
             try (ResultSet rs = stmt.executeQuery()) {
             if (rs.next()) {
                 return (rs.getString("allocated") != null);
             } else {
                 return false;
             }
             }
         catch (SQLException ex) {
             System.out.println(ex.getMessage());
             return true;
         }
         } catch (SQLException ex) {
             System.out.println(ex.getMessage());
             return true;
         }
      }
  
      private boolean isPrivateIPAllocated(String ip, long podId, long zoneId, PreparedStatement stmt) {
          try {
              stmt.clearParameters();
              stmt.setString(1, ip);
              stmt.setLong(2, zoneId);
              stmt.setLong(3, podId);
              try(ResultSet rs = stmt.executeQuery();) {
                  if (rs.next()) {
                      return (rs.getString("taken") != null);
                  } else {
                      return false;
                  }
              }
              catch (Exception ex) {
                  System.out.println(ex.getMessage());
                  return true;
              }
          } catch (SQLException ex) {
              System.out.println(ex.getMessage());
              return true;
          }
      }
  
      @DB
      public List<String> saveIPRange(String type, long podId, long zoneId, long vlanDbId, String startIP, String endIP, Long sourceNetworkId, long physicalNetworkId) {
          long startIPLong = NetUtils.ip2Long(startIP);
          long endIPLong = startIPLong;
          if (endIP != null) {
              endIPLong = NetUtils.ip2Long(endIP);
          }
  
          TransactionLegacy txn = TransactionLegacy.currentTxn();
          List<String> problemIPs = null;
  
          if (type.equals("public")) {
              problemIPs = savePublicIPRange(txn, startIPLong, endIPLong, zoneId, vlanDbId, sourceNetworkId, physicalNetworkId, false);
          } else if (type.equals("private")) {
              problemIPs = savePrivateIPRange(txn, startIPLong, endIPLong, podId, zoneId);
          }
  
          String[] linkLocalIps = NetUtils.getLinkLocalIPRange("169.254.0.0/16");
          long startLinkLocalIp = NetUtils.ip2Long(linkLocalIps[0]);
          long endLinkLocalIp = NetUtils.ip2Long(linkLocalIps[1]);
  
          saveLinkLocalPrivateIPRange(txn, startLinkLocalIp, endLinkLocalIp, podId, zoneId);
  
          return problemIPs;
      }
  
      public Vector<String> savePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long zoneId, long vlanDbId, Long sourceNetworkId, long physicalNetworkId, boolean forSystemVms) {
          String insertSql =
              "INSERT INTO `cloud`.`user_ip_address` (public_ip_address, data_center_id, vlan_db_id, mac_address, source_network_id, physical_network_id, uuid, forsystemvms) VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?), ?, ?, ?, ?)";
          String updateSql = "UPDATE `cloud`.`data_center` set mac_address = mac_address+1 where id=?";
          Vector<String> problemIPs = new Vector<String>();
  
          Connection conn = null;
          try {
              conn = txn.getConnection();
          } catch (SQLException e) {
              return null;
          }
          while (startIP <= endIP) {
              try (PreparedStatement insert_stmt = conn.prepareStatement(insertSql);
              PreparedStatement update_stmt = conn.prepareStatement(updateSql);
              ){
                  insert_stmt.setString(1, NetUtils.long2Ip(startIP));
                  insert_stmt.setLong(2, zoneId);
                  insert_stmt.setLong(3, vlanDbId);
                  insert_stmt.setLong(4, zoneId);
                  insert_stmt.setLong(5, sourceNetworkId);
                  insert_stmt.setLong(6, physicalNetworkId);
                  insert_stmt.setString(7, UUID.randomUUID().toString());
                  insert_stmt.setBoolean(8, forSystemVms);
                  insert_stmt.executeUpdate();
                  update_stmt.setLong(1, zoneId);
                  update_stmt.executeUpdate();
              } catch (Exception ex) {
                  problemIPs.add(NetUtils.long2Ip(startIP));
              }
              startIP++;
          }
  
          return problemIPs;
      }
  
      public List<String> savePrivateIPRange(TransactionLegacy txn, long startIP, long endIP, long podId, long zoneId) {
          String insertSql =
              "INSERT INTO `cloud`.`op_dc_ip_address_alloc` (ip_address, data_center_id, pod_id, mac_address) VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?))";
          String updateSql = "UPDATE `cloud`.`data_center` set mac_address = mac_address+1 where id=?";
          Vector<String> problemIPs = new Vector<String>();
  
          try {
              Connection conn = txn.getConnection();
              while (startIP <= endIP) {
                  try (PreparedStatement insert_stmt = conn.prepareStatement(insertSql);
                       PreparedStatement update_stmt = conn.prepareStatement(updateSql);
                  )
                  {
                      insert_stmt.setString(1, NetUtils.long2Ip(startIP));
                      insert_stmt.setLong(2, zoneId);
                      insert_stmt.setLong(3, podId);
                      insert_stmt.setLong(4, zoneId);
                      insert_stmt.executeUpdate();
                      update_stmt.setLong(1, zoneId);
                      update_stmt.executeUpdate();
                  } catch (Exception e) {
                      problemIPs.add(NetUtils.long2Ip(startIP));
                  }
                  startIP++;
              }
          } catch (Exception ex) {
              System.out.print(ex.getMessage());
              ex.printStackTrace();
          }
  
          return problemIPs;
      }
  
      private Vector<String> saveLinkLocalPrivateIPRange(TransactionLegacy txn, long startIP, long endIP, long podId, long zoneId) {
          String insertSql = "INSERT INTO `cloud`.`op_dc_link_local_ip_address_alloc` (ip_address, data_center_id, pod_id) VALUES (?, ?, ?)";
          Vector<String> problemIPs = new Vector<String>();
  
          Connection conn = null;
          try {
              conn = txn.getConnection();
          } catch (SQLException e) {
              System.out.println("Exception: " + e.getMessage());
              printError("Unable to start DB connection to save private IPs. Please contact Cloud Support.");
          }
          long start = startIP;
  
          try(PreparedStatement stmt = conn.prepareStatement(insertSql);)
          {
              while (startIP <= endIP) {
                  stmt.setString(1, NetUtils.long2Ip(startIP++));
                  stmt.setLong(2, zoneId);
                  stmt.setLong(3, podId);
                  stmt.addBatch();
              }
              int[] results = stmt.executeBatch();
              for (int i = 0; i < results.length; i += 2) {
                  if (results[i] == Statement.EXECUTE_FAILED) {
                      problemIPs.add(NetUtils.long2Ip(start + (i / 2)));
                  }
              }
          } catch (Exception ex) {
              System.out.println("saveLinkLocalPrivateIPRange. Exception: " + ex.getMessage());
          }
          return problemIPs;
      }
  
      public static String getPublicNetmask(String zone) {
          return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE name = \"" + zone + "\"", "netmask",
              "Unable to start DB connection to read public netmask. Please contact Cloud Support.");
      }
  
      public static String getPublicGateway(String zone) {
          return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE name = \"" + zone + "\"", "gateway",
              "Unable to start DB connection to read public gateway. Please contact Cloud Support.");
      }
  
      public static String getGuestNetworkCidr(Long zoneId) {
          return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE id = \"" + zoneId + "\"", "guest_network_cidr",
              "Unable to start DB connection to read guest cidr network. Please contact Cloud Support.");
      }
  
      public static boolean validCIDR(final String cidr) {
          if (cidr == null || cidr.isEmpty()) {
              return false;
          }
          String[] cidrPair = cidr.split("\\/");
          if (cidrPair.length != 2) {
              return false;
          }
          String cidrAddress = cidrPair[0];
          String cidrSize = cidrPair[1];
          if (!validIP(cidrAddress)) {
              return false;
          }
          int cidrSizeNum = -1;
  
          try {
              cidrSizeNum = Integer.parseInt(cidrSize);
          } catch (Exception e) {
              return false;
          }
  
          if (cidrSizeNum < 1 || cidrSizeNum > 32) {
              return false;
          }
  
          return true;
      }
  
      public static boolean validOrBlankIP(final String ip) {
          if (ip == null || ip.isEmpty()) {
              return true;
          }
          return validIP(ip);
      }
  
      public static boolean validIP(final String ip) {
          final String[] ipAsList = ip.split("\\.");
  
          // The IP address must have four octets
          if (Array.getLength(ipAsList) != 4) {
              return false;
          }
  
          for (int i = 0; i < 4; i++) {
              // Each octet must be an integer
              final String octetString = ipAsList[i];
              int octet;
              try {
                  octet = Integer.parseInt(octetString);
              } catch (final Exception e) {
                  return false;
              }
              // Each octet must be between 0 and 255, inclusive
              if (octet < 0 || octet > 255) {
                  return false;
              }
  
              // Each octetString must have between 1 and 3 characters
              if (octetString.length() < 1 || octetString.length() > 3) {
                  return false;
              }
  
          }
  
          // IP is good, return true
          return true;
      }
  
      public static boolean validIPRange(String startIP, String endIP) {
          if (endIP == null || endIP.isEmpty()) {
              return true;
          }
  
          long startIPLong = NetUtils.ip2Long(startIP);
          long endIPLong = NetUtils.ip2Long(endIP);
          return (startIPLong < endIPLong);
      }
  
      public static boolean sameSubnet(final String ip1, final String ip2, final String netmask) {
          if (ip1 == null || ip1.isEmpty() || ip2 == null || ip2.isEmpty()) {
              return true;
          }
          String subnet1 = NetUtils.getSubNet(ip1, netmask);
          String subnet2 = NetUtils.getSubNet(ip2, netmask);
  
          return (subnet1.equals(subnet2));
      }
  
      public static boolean sameSubnetCIDR(final String ip1, final String ip2, final long cidrSize) {
          if (ip1 == null || ip1.isEmpty() || ip2 == null || ip2.isEmpty()) {
              return true;
          }
          String subnet1 = NetUtils.getCidrSubNet(ip1, cidrSize);
          String subnet2 = NetUtils.getCidrSubNet(ip2, cidrSize);
  
          return (subnet1.equals(subnet2));
      }
  
      private static void printError(String message) {
          DatabaseConfig.printError(message);
      }
  
  }
  