New-IPSubnet.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
<#PSScriptInfo
.VERSION 1.0.3 .GUID 3bb10ee7-38c1-41b9-88ea-16899164fc19 .AUTHOR Anthony J. Raymond .COMPANYNAME .COPYRIGHT (c) 2022 Anthony J. Raymond .TAGS IP subnet network ipv4 ipv6 .LICENSEURI https://github.com/CodeAJGit/posh/blob/main/LICENSE .PROJECTURI https://github.com/CodeAJGit/posh .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES 20220302-AJR: v1.0.0 - Initial Release 20220302-AJR: v1.0.1 - Fix Clerical Errors and Added Tags 20220305-AJR: v1.0.2 - Updated Metadata 20220305-AJR: v1.0.3 - Replace void cast with $null .PRIVATEDATA #> <# .DESCRIPTION Creates an [IPSubnet] object for use to manipulate IPv4 & IPv6 subnets. .EXAMPLE .\New-IPSubnet.ps1 192.168.1.0/30 .EXAMPLE .\New-IPSubnet.ps1 -Subnet 2001:db8::1 -Prefix 32 .PARAMETER Subnet Specifies an IPv4 or IPv6 address. .PARAMETER Prefix <Optional> Specifies the prefix (or network portion) of the address. #> [CmdletBinding()] [OutputType([object])] ## PARAMETERS ############################################################# param ( [Parameter( Position = 0, Mandatory )] [string] $Subnet, [Parameter()] [ValidateRange(0, 128)] [int] $Prefix = ($Subnet -split '\\|\/')[-1] ) ## FUNCTIONS AND SCRIPT VARIABLES ######################################### class IPSubnet : System.Object { # class properties #################################################### [System.Net.IPAddress] $Network [System.Net.Sockets.AddressFamily] $AddressFamily [ValidateRange(0, 128)] [int] $Prefix [System.Net.IPAddress] $SubnetMask [System.Net.IPAddress] $LastAddress [bigint] hidden $PrefixInt [bigint] hidden $StartInt [bigint] hidden $EndInt # class methods ####################################################### [string] ToString() { return ("{0}/{1}" -f $this.Network, $this.Prefix) } [bool] Contains([System.Net.IPAddress] $InputIPAddress) { # compute and compare network address $InputAddress = $this.ToAddress($InputIPAddress, $false) -band $this.PrefixInt $NetworkAddress = $this.ToAddress($this.Network, $false) return ($NetworkAddress -eq $InputAddress) } [IPSubnet[]] Subnet([int] $InputPrefix) { $Power = switch ($this.AddressFamily) { "InterNetwork" { [bigint]::Pow(2, (32 - $InputPrefix)) } "InterNetworkV6" { [bigint]::Pow(2, (128 - $InputPrefix)) } } $Subnets = for ($AddressInt = $this.StartInt; $AddressInt -le $this.EndInt; $AddressInt += $Power) { [IPSubnet]::new(($this.FromAddress($AddressInt, $true)), $InputPrefix) } return $Subnets } [bigint] hidden ToAddress([System.Net.IPAddress] $InputIPAddress, [bool] $Reverse) { # here we go address -> bytes -> reverse -> bigint [byte[]] $Bytes = $InputIPAddress.GetAddressBytes() if ($Reverse) { [array]::Reverse($Bytes) } # append zero byte for unsigned return [bigint]::new($Bytes + 0) } [System.Net.IPAddress] hidden FromAddress([bigint] $InputInt, [bool] $Reverse) { # here we go backward bigint -> bytes -> reverse -> address [byte[]] $Bytes = $InputInt.ToByteArray() # we're going to pad the array for the size that the IPAddress constructor expects switch ($this.AddressFamily) { "InterNetwork" { [array]::Resize([ref] $Bytes, 4) } "InterNetworkV6" { [array]::Resize([ref] $Bytes, 16) } } if ($Reverse) { [array]::Reverse($Bytes) } return ([System.Net.IPAddress] $Bytes) } [bigint] hidden GetPrefixInt([int] $InputInt) { # turn prefix into binary string so we can work with it $Binary = switch ($this.AddressFamily) { "InterNetwork" { ('1' * $InputInt).PadRight(32, '0') } "InterNetworkV6" { ('1' * $InputInt).PadRight(128, '0') } } # the last element of this match is null, so skip it $Octet = $Binary -split '(?<=\G[01]{8})' | Select-Object -SkipLast 1 $Bytes = $Octet | ForEach-Object { [System.Convert]::ToUInt32($_, 2) } # append zero byte for unsigned return [bigint]::new($Bytes + 0) } [void] hidden __init__([System.Net.IPAddress] $InputIPAddress, [int] $InputPrefix) { $this.Prefix = $InputPrefix $this.AddressFamily = $InputIPAddress.AddressFamily # quick prefix validation switch ($true) { {$this.AddressFamily -eq "InterNetwork" -and $this.Prefix -in 0..32} { break } {$this.AddressFamily -eq "InterNetworkV6" -and $this.Prefix -in 0..128} { break } default {throw [System.InvalidCastException] "An invalid prefix for the given address family was specified."} } $this.PrefixInt = $this.GetPrefixInt($this.Prefix) $this.SubnetMask = $this.FromAddress($this.PrefixInt, $false) # using the prefix we can find the network address $StartAddress = $this.ToAddress($InputIPAddress, $false) -band $this.PrefixInt $this.Network = $this.FromAddress($StartAddress, $false) # using the prefix, we can find the last address $EndAddress = switch ($this.AddressFamily) { "InterNetwork" { $StartAddress -bor (-bnot $this.PrefixInt -band ([bigint]::Pow(2, 32) -1)) } "InterNetworkV6" { $StartAddress -bor (-bnot $this.PrefixInt -band ([bigint]::Pow(2, 128) -1)) } } $this.LastAddress = $this.FromAddress($EndAddress, $false) $this.StartInt = $this.ToAddress($this.Network, $true) $this.EndInt = $this.ToAddress($this.LastAddress, $true) Update-TypeData -TypeName IPSubnet -MemberType AliasProperty -MemberName Broadcast -Value LastAddress -Force Update-TypeData -TypeName IPSubnet -MemberType AliasProperty -MemberName Mask -Value SubnetMask -Force Update-TypeData -TypeName IPSubnet -DefaultDisplayPropertySet Network, AddressFamily, Prefix, LastAddress -Force } # class constructors ################################################## IPSubnet([System.Net.IPAddress] $InputIPAddress, [int] $InputPrefix) { $this.__init__($InputIPAddress, $InputPrefix) } IPSubnet([string] $InputString) { try { $SplitString = $InputString -split '\\|\/' [System.Net.IPAddress] $InputIPAddress = $SplitString[0] [int] $InputPrefix = $SplitString[-1] } catch { throw [System.InvalidCastException] "An invalid IP address or prefix format was specified." } $this.__init__($InputIPAddress, $InputPrefix) } } ## EXECUTION ############################################################## [IPSubnet]::new(($Subnet -split '\\|\/')[0], $Prefix) ## CLEAN UP ############################################################### $null = [System.GC]::GetTotalMemory($true) |