1

This question is related to this article

I'm trying to write a tool in C# to handle subnetting of IPv4 addresses. I know there are other libraries, the most popular being IpNetwork2. Currently that library doesn't load in my existing project, and I need to move forward. I wound up converting a PowerShell module, SubnetTools, to fit my needs. The problem is that it relies on the IpAddress.Address property which is deprecated.

For simplicity I'll use the shortest snip from the original article

        public int GetAddress(string ipAddress)
        {
            string hex = string.Concat(ipAddress.Split('.').Reverse().Select(x => int.Parse(x).ToString("X").PadLeft(2, '0'))); // 0100007F 
            return Convert.ToInt32(hex, 16); //16777343
        }

The output is as expected for the normal ip address but when reversed it gives the wrong value. See the output below:

[IpTools.IpObject]::new('172.18.16.0')

ForwardIpAddress ForwardDecimalAddress ReverseIpAddress ReverseDecimalAddress
---------------- --------------------- ---------------- ---------------------
172.18.16.0                    1053356 0.16.18.172                -1408102400

Here is the output from IpAddress:

[ipaddress]'172.18.16.0'

AddressFamily      : InterNetwork
ScopeId            :
IsIPv6Multicast    : False
IsIPv6LinkLocal    : False
IsIPv6SiteLocal    : False
IsIPv6Teredo       : False
IsIPv6UniqueLocal  : False
IsIPv4MappedToIPv6 : False
Address            : 1053356
IPAddressToString  : 172.18.16.0

[ipaddress]'0.16.18.172'

AddressFamily      : InterNetwork
ScopeId            :
IsIPv6Multicast    : False
IsIPv6LinkLocal    : False
IsIPv6SiteLocal    : False
IsIPv6Teredo       : False
IsIPv6UniqueLocal  : False
IsIPv4MappedToIPv6 : False
Address            : 2886864896
IPAddressToString  : 0.16.18.172

The address numbers on the reversed Ip are very different and the way that code I have works means I get the incorrect values. So I guess the question is, How do I get the address of 2886864896 from an ip address of 0.16.18.172. Or how is it done in System.Net.IPAddress?

I'm also very open to suggestions on better ways to deal with Subnetting without using nuget packages, thus far, the two I've tried have both failed.

Related to the first comment, my existing object uses long for the address properties.

public string ForwardIpAddress { get; set; } = string.Empty;
public long ForwardDecimalAddress { get; set; } = 0;
public string ReverseIpAddress { get; set; } = string.Empty;
public long ReverseDecimalAddress { get; set; } = 0;

I have used several answers from the original question but here is one sample that returns a long and I still get the same result.

        public long GetAddress(string ipAddress)
        {
            byte[] bytes = IPAddress.Parse(ipAddress).GetAddressBytes();
            if (!BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }
            long r = BitConverter.ToInt64(bytes, 0);
            return r;
        }

Per the comments I've updated the snip above to use BitConverter.ToInt64(). This results in an error when it runs on the reverse.

[IpTools.IpObject]::new('172.18.16.0')
MethodInvocationException: Exception calling ".ctor" with "1" argument(s): "Destination array is not long enough to copy all the items in the collection. Check array index and length. (Parameter 'value')"

The GetAddress() runs for both forward and reverse.

ForwardIpAddress = ip.ToString();
ForwardDecimalAddress = new Address().GetAddress(ForwardIpAddress);
ReverseIpAddress = reveresed.ToString();
ReverseDecimalAddress = new Address().GetAddress(ReverseIpAddress);
Jeff Patton
  • 551
  • 4
  • 15
  • 1
    The difference is the data type used to store the value - `IpObject` uses a signed int32, whereas `IPAddress.Address` is a `long`/`int64`. The underlying octets are identical: `[bitconverter]::ToInt32([bitconverter]::GetBytes(2886864896L), 0)`. To get the `long` value from `int`: `[bitconverter]::ToInt64([bitconverter]::GetBytes(-1408102400), 0)` – Mathias R. Jessen Jul 20 '23 at 14:03
  • In my code my decaddresses are longs, no amount of editing fixes this so one example so it's less messy: public long ForwardDecimalAddress { get; set; } – Jeff Patton Jul 20 '23 at 14:04
  • It won't help if you first calculate it as an `int` value (causing the wrap-around) and then store it in a `long` after the fact. Please show us the `IpObject` class definition. – Mathias R. Jessen Jul 20 '23 at 14:07
  • `IpNetwork2` works fine in powershell, im currently using it... it even works in pwsh 5.1 – Santiago Squarzon Jul 20 '23 at 14:09
  • Please show the full class definition, not just a list of public properties – Mathias R. Jessen Jul 20 '23 at 14:10
  • This is not used in PowerShell, this is part of a backend API. I've also updated the original question @MathiasR.Jessen let me know if you need more. – Jeff Patton Jul 20 '23 at 14:11
  • The API is the same :) `BitConverter.ToInt64(BitConverter.GetBytes(-1408102400), 0)` – Mathias R. Jessen Jul 20 '23 at 14:12
  • @MathiasR.Jessen I've updated the question – Jeff Patton Jul 20 '23 at 14:24
  • A "reversed IP address" isn't a thing. The address 172.18.16.0 may be stored in memory in two possible orders, but when printed as a dotted decimal the order is always most-significant network octet first, least-significant host octet last. Always. And there is no good reason to ever display or accept a undotted decimal address... doing so has led to vulnerabilities. – Ben Voigt Jul 20 '23 at 14:43
  • @BenVoigt As I originally stated this is being used to handle subnetting, as an internal process, not actually doing any subnetting, I need the data returned. I'm unclear what vulnerabilities there would be here. Again, pulled the working code from a powerShell modules listed in the question, feel free to check it out. – Jeff Patton Jul 20 '23 at 16:36

2 Answers2

1

An IPv4 address is only 32 bits wide, so you need to provide some padding for the octect values passed to BitConverter.ToInt64:

public long GetAddress(string ipAddress)
{
    // create 64-bit buffer
    byte[] bytes = new byte[8];

    // copy ip address bytes to buffer
    IPAddress.Parse(ipAddress).GetAddressBytes().CopyTo(bytes, 0);
    if (!BitConverter.IsLittleEndian)
    {
        Array.Reverse(bytes);
    }
    
    return BitConverter.ToInt64(bytes, 0);
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
1

Try following :

            string ip = "0.16.18.172";
            string[] splitArray = ip.Split(new char[] { '.' });
            byte[] bytes = splitArray.Select(x => byte.Parse(x)).ToArray();
            uint number = BitConverter.ToUInt32(bytes);
jdweng
  • 33,250
  • 2
  • 15
  • 20