Wednesday, June 10, 2020

CSharp - Fastest way of getting IPv4 Subnet Mask expansion aka CIDR IP, and speed comparisons














Here's a couple of implementations of getting IPv4 subnet mask expansion or more specifically a CIDR IP subnet mask expansion.

CIDR notation is a compact representation of an IP address and its associated routing prefix. The notation is constructed from an IP address, a slash ('/') character, and a decimal number. The trailing number is the count of leading 1 bits in the routing mask, traditionally called the network mask. The IP address in the notation is always represented according to the standards for IPv4 or IPv6.

192.168.100.14/24 represents the IPv4 address 192.168.100.14 and its associated routing prefix 192.168.100.0, or equivalently, its subnet mask 255.255.255.0, which has 24 leading 1-bits.

https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing 




Code


using System;
using System.Diagnostics;
//https://docs.microsoft.com/en-us/archive/blogs/knom/ip-address-calculations-with-c-subnetmasks-networks
public static class SubnetMask
{
 public static readonly System.Net.IPAddress ClassA = System.Net.IPAddress.Parse("255.0.0.0");
 public static readonly System.Net.IPAddress ClassB = System.Net.IPAddress.Parse("255.255.0.0");
 public static readonly System.Net.IPAddress ClassC = System.Net.IPAddress.Parse("255.255.255.0");

 public static System.Net.IPAddress CreateByHostBitLength(int hostpartLength)
 {
  int hostPartLength = hostpartLength;
  int netPartLength = 32 - hostPartLength;

  if (netPartLength < 2)
   throw new ArgumentException("Number of hosts is to large for IPv4");

  Byte[] binaryMask = new byte[4];

  for (int i = 0; i < 4; i++)
  {
   if (i * 8 + 8 <= netPartLength)
    binaryMask[i] = (byte)255;
   else if (i * 8 > netPartLength)
    binaryMask[i] = (byte)0;
   else
   {
    int oneLength = netPartLength - i * 8;
    string binaryDigit =
     String.Empty.PadLeft(oneLength, '1').PadRight(8, '0');
    binaryMask[i] = Convert.ToByte(binaryDigit, 2);
   }
  }
  return new System.Net.IPAddress(binaryMask);
 }

 public static System.Net.IPAddress CreateByNetBitLength(int netpartLength)
 {
  int hostPartLength = 32 - netpartLength;
  return CreateByHostBitLength(hostPartLength);
 }

 public static System.Net.IPAddress CreateByHostNumber(int numberOfHosts)
 {
  int maxNumber = numberOfHosts + 1;

  string b = Convert.ToString(maxNumber, 2);

  return CreateByHostBitLength(b.Length);
 }
}
public static class Extensions
{
    /// <summary>
    /// Fastest way to to find 1st number in a string and convert it into an absolute integer
    /// </summary>
    /// <param name="intStr"></param>
    /// <returns></returns>
    //  https://metadataconsulting.blogspot.com/2020/06/CSharp-Fastest-way-to-find-a-number-or-integer-in-a-string.html
    public static int GetFirstAbsIntFast(this string intStr)
    {

        int sum = int.MinValue; //any error val -9999
        char[] n = intStr.ToCharArray(); //fastest way to index a string
        //int idxFirstDigit = -1;

        for (int i = 0; i < n.Length; i++)
        {
            if (n[i] >= 48 && n[i] <= 57)  //'0'=48 and '9'=57 get lead number only
            {
                //if (idxFirstDigit == -1) idxFirstDigit = i;
                int z = sum * 10 + (n[i] - 48);
                if (z < 0) //we get into negative, if over int.MaxValue
                    return sum; //we have negative number, it's an error or throw error
                sum = z;
            }
            else //if (idxFirstDigit > -1)
                break;
            //return int.MinValue; //or throw error
        }

        //if (intStr.IndexOf('-') == idxFirstDigit - 1) //chek for neg sign
        //    sum *= -1;

        return sum;

    }
 
 public static string GetIPv4NetworkMaskByPrefix(this string subnet)
    {
        //CIDR IP format accepted  xxx.xxx.xxx.xxx/1-32
        int idxForwardSlash = subnet.IndexOf('/');
        if (string.IsNullOrEmpty(subnet) && idxForwardSlash > -1)
            return string.Empty;

        string CIDRIP = subnet.Substring(idxForwardSlash+1);

        int numCIDRIP = 0;
        numCIDRIP = CIDRIP.GetFirstAbsIntFast();
        if (numCIDRIP < 1 || numCIDRIP > 32) return string.Empty; // /1-32 range accepted for ip4

        int calSubNet = 0;
        calSubNet = numCIDRIP;

        switch (calSubNet)
        {
            case 1: return "128.0.0.0"; //2,147,483,646
            case 2: return "192.0.0.0"; //1,073,741,822
            case 3: return "224.0.0.0"; //536,870,910
            case 4: return "240.0.0.0"; //268,435,454
            case 5: return "248.0.0.0"; //134,217,726
            case 6: return "252.0.0.0"; //67,108,862
            case 7: return "254.0.0.0"; //33,554,430
            case 8: return "255.0.0.0";
            case 9: return "255.128.0.0";
            case 10: return "255.192.0.0";
            case 11: return "255.224.0.0";
            case 12: return "255.240.0.0";
            case 13: return "255.248.0.0";
            case 14: return "255.252.0.0";
            case 15: return "255.254.0.0";
            case 16: return "255.255.0.0";
            case 17: return "255.255.128.0";
            case 18: return "255.255.192.0";
            case 19: return "255.255.224.0";
            case 20: return "255.255.240.0";
            case 21: return "255.255.248.0";
            case 22: return "255.255.252.0";
            case 23: return "255.255.254.0";
            case 24: return "255.255.255.0";  //254
            case 25: return "255.255.255.128";
            case 26: return "255.255.255.192";
            case 27: return "255.255.255.224";
            case 28: return "255.255.255.240";
            case 29: return "255.255.255.248";
            case 30: return "255.255.255.252";
            case 31: return "255.255.255.254"; 
            case 32: return "255.255.255.255";
            default: return "";
        }
    }

    //https://www.ipaddressguide.com/netmask
    //A CIDR IP address looks like a normal IP address except that it ends with a slash followed by a number, called the IP network prefix.
    //10.0.0.0/24 -> Netmask = 255.255.255.0
    public static string GetIPv4SubnetMask(this string subnet)
    {
        //CIDR IP format accepts -> {xxx.xxx.xxx.xxx}/[1-32]
        int idxForwardSlash = subnet.IndexOf('/');
        if (string.IsNullOrEmpty(subnet) && idxForwardSlash > -1)
            return string.Empty;

        string CIDRIP = subnet.Substring(idxForwardSlash+1);

        int numCIDRIP = 0;
        numCIDRIP = CIDRIP.GetFirstAbsIntFast();
        if (numCIDRIP < 1 || numCIDRIP > 32) return string.Empty; // /1-32 range accepted for ip4

        int calSubNet = 0;
        calSubNet = 32 - numCIDRIP;

        // https://weblogs.asp.net/razan/finding-subnet-mask-from-ip4-address-using-c
        long mask = (0xffffffffL << calSubNet & 0xffffffffL);
        mask = System.Net.IPAddress.HostToNetworkOrder((int)mask);
        return new System.Net.IPAddress((UInt32)mask).ToString();
    }

        public static string getSubnetAddressFromIPv4NetMask(this string netMask)
        {
            //CIDR IP format accepted  xxx.xxx.xxx.xxx/1-32
            int idxForwardSlash = netMask.IndexOf('/');
            if (string.IsNullOrEmpty(netMask) && idxForwardSlash > -1)
                return string.Empty;

            string CIDRIP = netMask.Substring(idxForwardSlash + 1);

            int numCIDRIP = 0;
            numCIDRIP = CIDRIP.GetFirstAbsIntFast();
            if (numCIDRIP < 1 || numCIDRIP > 32) return string.Empty; // /1-32 range accepted for ip4

            //long bit = 1;
            //bit <<= pow; //now this bitwise shift works
            int calSubNet = 0;
            calSubNet = 32 - numCIDRIP;

            string subNetMask = string.Empty;

            double result = 0;

            if (calSubNet >= 0 && calSubNet <= 8)
            {
                for (int ipower = 0; ipower < calSubNet; ipower++)
                {
                    long bit = 1;
                    bit <<= ipower; //now this bitwise shift works
                    result += bit; 
                    //result += Math.Pow(2, ipower);
                }
                double finalSubnet = 255 - result;
                subNetMask = "255.255.255." + Convert.ToString(finalSubnet);
            }
            else if (calSubNet > 8 && calSubNet <= 16)
            {
                int secOctet = 16 - calSubNet;

                secOctet = 8 - secOctet;

                for (int ipower = 0; ipower < secOctet; ipower++)
                {
                    long bit = 1;
                    bit <<= ipower; //now this bitwise shift works
                    result += bit;
                    //result += Math.Pow(2, ipower);
                }
                double finalSubnet = 255 - result;
                subNetMask = "255.255." + Convert.ToString(finalSubnet) + ".0";
            }
            else if (calSubNet > 16 && calSubNet <= 24)
            {
                int thirdOctet = 24 - calSubNet;

                thirdOctet = 8 - thirdOctet;

                for (int ipower = 0; ipower < thirdOctet; ipower++)
                {
                    long bit = 1;
                    bit <<= ipower; //now this bitwise shift works
                    result += bit;
                    //result += Math.Pow(2, ipower);
                }
                double finalSubnet = 255 - result;
                subNetMask = "255." + Convert.ToString(finalSubnet) + ".0.0";
            }
            else if (calSubNet > 24 && calSubNet <= 32)
            {
                int fourthOctet = 32 - calSubNet;

                fourthOctet = 8 - fourthOctet;

                for (int ipower = 0; ipower < fourthOctet; ipower++)
                {
                    long bit = 1;
                    bit <<= ipower; //now this bitwise shift works
                    result += bit;
                    //result += Math.Pow(2, ipower);
                }
                double finalSubnet = 255 - result;
                subNetMask = Convert.ToString(finalSubnet) + ".0.0.0";
            }

            return subNetMask;
        }

 public static System.Net.IPAddress CreateBySubNetBitLength(this string netMask)
 {
  //CIDR IP format accepted  xxx.xxx.xxx.xxx/1-32
  int idxForwardSlash = netMask.IndexOf('/');
  if (string.IsNullOrEmpty(netMask) && idxForwardSlash > -1)
   return System.Net.IPAddress.None;

  string CIDRIP = netMask.Substring(idxForwardSlash + 1);

  int numCIDRIP = 0;
  numCIDRIP = CIDRIP.GetFirstAbsIntFast();
  if (numCIDRIP < 1 || numCIDRIP > 32) return System.Net.IPAddress.None; // /1-32 range accepted for ip4

  int calSubNet = 32 - numCIDRIP;

  return SubnetMask.CreateByNetBitLength(calSubNet);

 }


}
    
public class Program
{
 
 public static void Main()
 { 
      //https://www.calculator.net/ip-subnet-calculator.html dup this functionality partially
       
      string subnetprefix = "/25"; 
   Stopwatch sw = new Stopwatch(); 
            sw.Start(); 
            Console.WriteLine("Using getSubnetAddressFromIPNetMask");
            Console.WriteLine(subnetprefix.getSubnetAddressFromIPv4NetMask());
            sw.Stop();
            Console.WriteLine(sw.ElapsedTicks.ToString("#,0") + " ticks");
            sw.Reset();
            sw.Start(); 
            Console.WriteLine("Using compact GetSubnetMask");
            Console.WriteLine(subnetprefix.GetIPv4SubnetMask());
            sw.Stop();
            Console.WriteLine(sw.ElapsedTicks.ToString("#,0") + " ticks");
            sw.Reset();
            sw.Start(); 
            Console.WriteLine("Using compact getNetworkMaskByPrefix");
            Console.WriteLine(subnetprefix.GetIPv4NetworkMaskByPrefix());
            sw.Stop();
            Console.WriteLine(sw.ElapsedTicks.ToString("#,0") + " ticks");
            //https://docs.microsoft.com/en-us/archive/blogs/knom/ip-address-calculations-with-c-subnetmasks-networks
      Console.WriteLine("Using Microsoft's recommended imp - CreateBySubNetBitLength");
            Console.WriteLine(subnetprefix.GetIPv4NetworkMaskByPrefix());
            sw.Stop();
            Console.WriteLine(sw.ElapsedTicks.ToString("#,0") + " ticks");
  
  
 }
}

No comments:

Post a Comment