I’ve been extended and upgrading my library of network functions for some time now, I thought I’d post the functions that work directly with my original collection.

These functions are also included in my NetShell module, I need to post an update to that very soon as well.

ConvertTo-HexIP

Sometimes being able to convert to a hexadecimal format is useful, this little function does exactly that. The body is very simple; it uses the built in formatters.

Function ConvertTo-HexIP {
  <#
    .Synopsis
      Converts a dotted decimal IP address into a hexadecimal string.
    .Description
      ConvertTo-HexIP takes a dotted decimal IP and returns a single hexadecimal string value.
    .Parameter IPAddress
      An IP Address to convert.
  #>

  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
    [Net.IPAddress]$IPAddress
  )

  Process {
    "$($IPAddress.GetAddressBytes() | ForEach-Object { '{0:x2}' -f $_ })" -Replace '\s'
  }
}

ConvertFrom-HexIP

And back again, using System.Convert.ToUInt32 with a base-16 value to get to UInt32, then a call to ConvertTo-DottedDecimalIP (from the original set).

Function ConvertFrom-HexIP {
  <#
    .Synopsis
      Converts a hexadecimal IP address into a dotted decimal string.
    .Description
      ConvertFrom-HexIP takes a hexadecimal string and returns a dotted decimal IP address.
    .Parameter IPAddress
      An IP Address to convert.
  #>

  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
    [ValidatePattern('^[0-9a-f]{8}$')]
    [String]$IPAddress
  )

  Process {
    ConvertTo-DottedDecimalIP ([Convert]::ToUInt32($IPAddress, 16))
  }
}

Get-NetworkSummary

Get-NetworkSummary gets a bit of an improvement.

Function Get-NetworkSummary {
  <#
    .Synopsis
      Generates a summary of a network range
    .Description
      Get-NetworkSummary uses most of the IP conversion CmdLets to provide a summary of a network 
      range from any IP address in the range and a subnet mask.
    .Parameter IPAddress
      Any IP address within the network range.
    .Parameter Network
      A network description in the format 1.2.3.4/24
    .Parameter SubnetMask
      The subnet mask for the network.
  #>

  [CmdLetBinding(DefaultParameterSetName = "IPAndMask")]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ParameterSetName = "IPAndMask", ValueFromPipeline = $True)]
    [Net.IPAddress]$IPAddress,
    
    [Parameter(Mandatory = $True, Position = 1, ParameterSetName = "IPAndMask")]
    [Alias("Mask")]
    [Net.IPAddress]$SubnetMask,
    
    [Parameter(Mandatory = $True, ParameterSetName = "CIDRNotation", ValueFromPipeline = $True)]
    [ValidatePattern('^(\d{1,3}\.){3}\d{1,3}[\\/]\d{1,2}$')]
    [String]$Network
  )

  Process {
    If ($PsCmdLet.ParameterSetName -eq 'CIDRNotation') {
      $Temp = $Network.Split("\/")
      $IPAddress = $Temp[0]
      $SubnetMask = ConvertTo-Mask $Temp[1]
    }

    $DecimalIP = ConvertTo-DecimalIP $IPAddress
    $DecimalMask = ConvertTo-DecimalIP $SubnetMask
    $DecimalNetwork =  $DecimalIP -BAnd $DecimalMask
    $DecimalBroadcast = $DecimalIP -BOr ((-BNot $DecimalMask) -BAnd [UInt32]::MaxValue)
  
    $NetworkSummary = New-Object PSObject -Property @{
      "NetworkAddress"   = (ConvertTo-DottedDecimalIP $DecimalNetwork);
      "NetworkDecimal"   = $DecimalNetwork
      "BroadcastAddress" = (ConvertTo-DottedDecimalIP $DecimalBroadcast);
      "BroadcastDecimal" = $DecimalBroadcast
      "Mask"             = $SubnetMask;
      "MaskLength"       = (ConvertTo-MaskLength $SubnetMask);
      "MaskHexadecimal"  = (ConvertTo-HexIP $SubnetMask);
      "HostRange"        = "";
      "NumberOfHosts"    = ($DecimalBroadcast - $DecimalNetwork - 1);
      "Class"            = "";
      "IsPrivate"        = $False}

    If ($NetworkSummary.MaskLength -lt 31) {
      $NetworkSummary.HostRange = [String]::Format("{0} - {1}",
        (ConvertTo-DottedDecimalIP ($DecimalNetwork + 1)),
        (ConvertTo-DottedDecimalIP ($DecimalBroadcast - 1)))
    }
  
    Switch -RegEx ($(ConvertTo-BinaryIP $IPAddress)) {
      "^1111"              { $NetworkSummary.Class = "E" }
      "^1110"              { $NetworkSummary.Class = "D" }
      "^11000000.10101000" { $NetworkSummary.Class = "C"; $NetworkSummary.IsPrivate = $True }
      "^110"               { $NetworkSummary.Class = "C" }
      "^10101100.0001"     { $NetworkSummary.Class = "B"; $NetworkSummary.IsPrivate = $True }
      "^10"                { $NetworkSummary.Class = "B" }
      "^00001010"          { $NetworkSummary.Class = "A"; $NetworkSummary.IsPrivate = $True }
      "^0"                 { $NetworkSummary.Class = "A" }
    }   
  
    Return $NetworkSummary
  }
}

Get-NetworkRange

And so does Get-NetworkRange.

Function Get-NetworkRange {
  <#
    .Synopsis
      Generates IP addresses within the specified network.
    .Description
      Get-NetworkRange finds the network and broadcast address as decimal values then starts a 
      counter between the two, returning Net.IPAddress for each.
    .Parameter IPAddress
      Any IP address within the network range.
    .Parameter Network
      A network description in the format 1.2.3.4/24
    .Parameter SubnetMask
      The subnet mask for the network.
  #>

  [CmdLetBinding(DefaultParameterSetName = "IPAndMask")]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ParameterSetName = "IPAndMask", ValueFromPipeline = $True)]
    [Net.IPAddress]$IPAddress, 
    
    [Parameter(Mandatory = $True, Position = 1, ParameterSetName = "IPAndMask")]
    [Alias("Mask")]
    [Net.IPAddress]$SubnetMask,
    
    [Parameter(Mandatory = $True, ParameterSetName = "CIDRNotation", ValueFromPipeline = $True)]
    [ValidatePattern('^(\d{1,3}\.){3}\d{1,3}[\\/]\d{1,2}$')]
    [String]$Network
  )

  Process {
    If ($PsCmdLet.ParameterSetName -eq 'CIDRNotation') {
      $Temp = $Network.Split("\/")
      $IPAddress = $Temp[0]
      $SubnetMask = ConvertTo-Mask $Temp[1]
    }

    $DecimalIP = ConvertTo-DecimalIP $IPAddress
    $DecimalMask = ConvertTo-DecimalIP $SubnetMask
  
    $DecimalNetwork = $DecimalIP -BAnd $DecimalMask
    $DecimalBroadcast = $DecimalIP -BOr ((-BNot $DecimalMask) -BAnd [UInt32]::MaxValue)

    For ($i = $($DecimalNetwork + 1); $i -lt $DecimalBroadcast; $i++) {
      ConvertTo-DottedDecimalIP $i
    }
  }
}

Get-Subnets

I ran into a need to calculate a list of subnets of a specific length within a super-net, this function does just that.

Function Get-Subnets {
  <#
    .Synopsis
      Generates a list of subnets for a given network range
    .Description
      Generates a list of subnets for a given network range using either the address class or a 
      user-specified value.
    .Parameter NetworkAddress
      Any address in the supernet range.
    .Parameter SubnetMask
      The desired mask, determines the size of the resulting subnet. Must be a valid subnet mask.
    .Parameter SupernetLength
      By default Get-Subnets uses the address class to determine the size of the supernet. Where the 
      supernet describes the range of addresses being split.
  #>

  [CmdLetBinding(DefaultParameterSetName = "PS1")]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ParameterSetName = "IPAndMask", ValueFromPipeline = $True)]
    [Net.IPAddress]$IPAddress, 
    
    [Parameter(Mandatory = $True, Position = 1, ParameterSetName = "IPAndMask")]
    [Alias("Mask")]
    [Net.IPAddress]$SubnetMask,
    
    [Parameter(Mandatory = $True, ParameterSetName = "CIDRNotation", ValueFromPipeline = $True)]
    [ValidatePattern('^(\d{1,3}\.){3}\d{1,3}[\\/]\d{1,2}$')]
    [String]$Network,

    [ValidateRange(1, 32)]
    [UInt32]$SupernetLength
  )

  Process {
    If ($PsCmdLet.ParameterSetName -eq 'CIDRNotation') {
      $Temp = $Network.Split("\/")
      $IPAddress = $Temp[0]
      $SubnetMask = ConvertTo-Mask $Temp[1]
    } Else {
      $SubnetLength = ConvertTo-MaskLength $SubnetMask
    }

    If (!$SupernetLength) {
      $SupernetLength = Switch -RegEx ($(ConvertTo-BinaryIP $IPAddress)) {
        "^110"  { 24 }
        "^10"   { 16 }
        "^0"    { 8 }
        default { 24 }
      }
    }
    If ($SupernetLength -gt $SubnetLength) {
      Write-Error "Subnet is larger than supernet. Aborting"
    }
    $SupernetMask = ConvertTo-Mask $SupernetLength

    $NumberOfNets = [Math]::Pow(2, ($SubnetLength - $SupernetLength))
    $NumberOfAddresses = [Math]::Pow(2, (32 - $SubnetLength))

    $SupernetAddress = Get-NetworkAddress $IPAddress $SupernetMask
    $DecimalAddress = ConvertTo-DecimalIP $SupernetAddress

    For ($i = 0; $i -lt $NumberOfNets; $i++) {
      $NetworkAddress = ConvertTo-DottedDecimalIP $DecimalAddress 

      "" | Select-Object @{n='NetworkAddress';e={ $NetworkAddress }},
        @{n='BroadcastAddress';e={ Get-BroadcastAddress $NetworkAddress $SubnetMask }},
        @{n='SubnetMask';e={ $SubnetMask }},
        @{n='HostAddresses';e={ 
          $NumberOfHosts = $NumberOfAddresses - 2
          If ($NumberOfHosts -lt 0) { 0 } Else { $NumberOfHosts } }}

      $DecimalAddress += $NumberOfAddresses
    }
  }
}

1 Comment

  1. I really like your sub-net math functions – the original collection and the additions. Much appreciated.

    In order to use them on one of the projects that I am working on a clear licence statement would be very helpful. You did mention on the VbScript version post in the comments that all code is intended to be public domain. In contrast to that your footer on every page clearly states “All rights reserved.”

    I also downloaded your NetShell module, and the copyright attribute doesn’t help to clarify either.

    Just trying to stay our of trouble ;-)

    Thanks again!

    Andy

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>