3
votes

I was able to extract my computer's host IP address using the ff:

$IPAddr = Get-NetIPConfiguration -InterfaceAlias "Ethernet" | select IPv4Address 

How do I get it's corresponding subnet address based on its subnet mask (e.g. /24, /20, /16, etc.)?

So, if the host IP address is 192.168.100.45, and the subnet mask is /24 or 255.255.255.0, the code should get the subnet address value of 192.168.100.0.

2
See the excellent answer to this question; it's simple binary math. - Ron Maupin
Yes indeed, that link offers an excellent answer to the question in that particular post. But for this post, in the simplest logic, I was hoping to get guidance on how to turn the value from the variable $IPAddr, which is 192.168.100.45, with its CIDR SM of /24, into its corresponding SN value of 192.168.100.0, and pass it on to another variable to be used for another part of my script. - Jee
Could you parse results from netsh output? Say, netsh interface ip show config 'Ethernet' ? - vonPryz
The idea is to use purely PowerShell commands to avoid invoking processes outside the shell when running the script. But if you could suggest a code that would just strip off the subnet address value from output of the above netsh one-liner, and then directly pass that value to a variable for another command, then by all means that's worth a try. - Jee
Since the goal is to determine the actual subnet address (not just the subnet mask) of a computer, if there could be a simpler way to do it in PS than making calculations or conversions out of values that are outputted by another command, then that would be great. - Jee

2 Answers

6
votes

You can use the IPAddress class. Example:

[IPAddress] (([IPAddress] "192.168.100.45").Address -band ([IPAddress] "255.255.255.0").Address)

This will output an IPAddress object containing the IP address 192.168.100.0.

To convert a bit count to an equivalent bitmask string, you can use a function such as:

function ConvertTo-IPv4MaskString {
  param(
    [Parameter(Mandatory = $true)]
    [ValidateRange(0, 32)]
    [Int] $MaskBits
  )
  $mask = ([Math]::Pow(2, $MaskBits) - 1) * [Math]::Pow(2, (32 - $MaskBits))
  $bytes = [BitConverter]::GetBytes([UInt32] $mask)
  (($bytes.Count - 1)..0 | ForEach-Object { [String] $bytes[$_] }) -join "."
}

Using this function, you could write:

[IPAddress] (([IPAddress] "192.168.100.45").Address -band ([IPAddress] (ConvertTo-IPv4MaskString 24)).Address)

This outputs an IPAddress object containing the IP address 192.168.100.0.

A couple of other functions might be useful:

function Test-IPv4MaskString {
  param(
    [Parameter(Mandatory = $true)]
    [String] $MaskString
  )
  $validBytes = '0|128|192|224|240|248|252|254|255'
  $MaskString -match `
    ('^((({0})\.0\.0\.0)|'      -f $validBytes) +
    ('(255\.({0})\.0\.0)|'      -f $validBytes) +
    ('(255\.255\.({0})\.0)|'    -f $validBytes) +
    ('(255\.255\.255\.({0})))$' -f $validBytes)
}

function ConvertTo-IPv4MaskBits {
  param(
    [parameter(Mandatory = $true)]
    [ValidateScript({Test-IPv4MaskString $_})]
    [String] $MaskString
  )
  $mask = ([IPAddress] $MaskString).Address
  for ( $bitCount = 0; $mask -ne 0; $bitCount++ ) {
    $mask = $mask -band ($mask - 1)
  }
  $bitCount
}

ConvertTo-IPv4MaskBits is the inverse of ConvertTo-IPv4MaskString and uses Test-IPv4MaskString to validate whether the mask is a valid IPv4 mask.

There's a bit more detail available in an article I wrote a while back:

IT Pro Today - Working with IPv4 Addresses in PowerShell

2
votes

The IP-address implementation from Microsoft is quite slow and unfortunatley they also put the octet-values in the wrong order. For all IP-related calculation I decide to better work with my own functions for that. Here my code-snippet:

function ipNr($s) {
    [byte[]]$ip = $s.Split(".")
    [array]::Reverse($ip)
    [bitconverter]::ToUInt32($ip,0)
}

function ipString($n) {
    $ip = [bitconverter]::GetBytes($n)
    [array]::Reverse($ip)
    [string]::Join(".", $ip)
}

function maskNr($b) {
    (1 -shl $b)-1 -shl (32-$b)
}

$ip = "192.168.100.45"
$prefixLength = 24
$subnetId = ipString ((ipNr $ip) -band (maskNr $prefixLength)) 
write-host "$subnetId/$prefixLength"