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");
}
}