Skip to content

Instantly share code, notes, and snippets.

@iyre
Created January 28, 2023 04:01
Show Gist options
  • Save iyre/aa9e3d09306a88477c19aee448cf5a6e to your computer and use it in GitHub Desktop.
Save iyre/aa9e3d09306a88477c19aee448cf5a6e to your computer and use it in GitHub Desktop.
powershell ip math
function IpToInt ([ipaddress]$IPAddress) {
$IpBytes = $IPAddress.GetAddressBytes()
if ([BitConverter]::IsLittleEndian) {
[Array]::Reverse($IpBytes)
}
return [BitConverter]::ToUInt32($IpBytes, 0)
}
function IntToIp ([uint32]$IPInteger) {
$IpBytes = [BitConverter]::GetBytes($IPInteger)
if ([BitConverter]::IsLittleEndian) {
[Array]::Reverse($IpBytes)
}
return [ipaddress]$IpBytes
}
function CidrMaskToInt([int]$NetMask) {
return (([math]::pow(2, 32)) - 1) - (([math]::pow(2, 32 - $NetMask)) - 1)
}
# ip addresses are big endian
# c# converts these to little endian integers
# this results in the byte order being reversed, making some basic "ip math" either impossible or convoluted
# there are 32 bits in an ipv4 address, which means 2^32 possible addresses
# 2^32 = 4294967296, equivalent to FFFFFFFF in hex
# so, any decimal number from 0 to 4294967295 represents a valid IP address
# 192.168.0.1 # dotted decimal
# 0xc0a80001 # in hex
# 11000000101010000000000000000001 # binary
# this is the 32-bit unsigned integer with the "correct" byte order
# 3232235521
# the c# ipaddress class has an "address" property that flips the byte order of the ip address to LITTLE ENDIAN (least significant first)
# ([ipaddress]'192.168.0.1').address
# 16820416
# 0x0100a8c0
# 00000001000000001010100011000000
# this integer does not align with the 2^32 range of decimal numbers
([ipaddress]'192.168.0.1').address
# 16820416
iptoint '192.168.0.1'
# 3232235521
inttoip 3232235521
# 192.168.0.1
# much simpler to offset by a number of networks this way
# given network mask (m), number of networks (n) to offset by
# (2 ^ (32 - m )) * n ) = address offset
# (2 ^ (32 - 24)) * 12) = 3072
(inttoip ((iptoint '192.168.0.1') + 3072)).ipaddresstostring
# 192.168.12.1
# c# alternative is not that bad, just always required to build an actual ip address and convert between the reversed int
([ipaddress](([ipaddress]'192.168.0.1').address + ([ipaddress]'0.0.12.0').address)).ipaddresstostring
# calculating the gateway for a given ip is overly complicated, since we need to convert everything to the weird reversed order and back
([ipaddress]((([ipaddress]'192.168.18.220').address -band ([ipaddress]'255.255.255.0').address) + ([ipaddress]'0.0.0.1').address)).ipaddresstostring
# i realize this looks way more complicated, but it makes sense logically and keeps the byte order consistent
(inttoip (((iptoint '192.168.18.220') -band (([math]::pow(2, 32)) - 1) - (([math]::pow(2, 32-24)) - 1)) + 1)).ipaddresstostring
(inttoip (((iptoint '192.168.18.220') -band (cidrmasktoint 24)) + 1)).ipaddresstostring
# just the network id
(inttoip ((iptoint '192.168.18.220') -band (cidrmasktoint 24))).ipaddresstostring
(inttoip ((iptoint '192.168.18.220') -band (iptoint '255.255.255.0'))).ipaddresstostring
# this doesnt produce the desired result with the c# class - due to the little endian byte order
([ipaddress](([ipaddress]'10.0.240.0').address + ([ipaddress]'0.0.16.0').address)).ipaddresstostring
# this produces 10.0.0.1
# logically, we expect 10.1.0.0
# since the bytes are reversed, the "carry" is moved in the wrong direction
# any operation that needs to span a byte isn't gonna work
# no such issue when we keep the matching byte order. correct octet is incremented
(inttoip ((iptoint '10.0.240.0') + (iptoint '0.0.16.0'))).ipaddresstostring
# 10.1.0.0
# it's also much simpler (and more sensical) to perform ip math with the correctly ordered integers)
# offset by 8 addresses
(inttoip ((iptoint '192.168.0.1') + 8)).ipaddresstostring
# 192.168.0.9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment