Bitweise Operatoren mit PowerShell und warum du sie kennen solltest

2024-01-17

Für was sind Bitweise Operatoren gut? Ich zeige es dir hier indem ich einen einfachen Sub-Net-Kalkulator in PowerShell erstelle. Aber mit Biweisen Operatoren kann mann noch wesentlich mehr machen

What are bitwise-operators

Mit Bitweisen Operatoren ist es möglich einzelne Bits in Bitfeldern zu manipulieren. Sie sind nützlich, wenn du mit Statusfalgs arbeitest. Du kannst damit:

  1. prüfen ob ein bestimmtes Flag gesetzt ist oder nicht.
  2. den Status eines bestimmten Flags ändern
  3. ein bestimmtes Flag setzten
  4. alle flägs negieren / umschalten
  5. bits in eine höhere oder niedrigere Position verschieben

Bitweise Operatoren arbeiten auf einem Verhältnismäßig Hardwarenahem level
Aber man kann sie auch verwenden um einen Sub-Net-Kalkulator zu erstellen oder zu prüfen ob eine Zahl gerade oder ungerade ist, den Wert zweier Variablen vertauschen...

Operator name Opearator Funktionalität
Binäres UND -band dieser Operator führt ein Bitweises UND zwischen zwei Werten durch. Jedes Bit, dass auf beiden seiten auf 1 gestzt ist bleibt auf 1 alle anderen werden 0
6 -band 2 = 2 / 0110 -band 0010 = 0010
Binäres ODER -bor Dieser Operator führt ein Bitweises ODER zwischen zwei Werten durch. Im Ergebis, sind alle Bits, die entweder auf einer der Seiten oder auf beiden Seiten 1 sind auf 1 4 -bor 2 = 6 / 0100 -bor 0010 = 0110
Binäes exklusives ODER -bxor Dieser Operator führt ein bitweises exklusives ODER zwischen zwei Werten durch. Im ergebis sin alle Bits die entweder auf der einen oder der anderen Seite 1 sind auf 1 Bits die auf beiden Seiten 1 oder 0 sind sind im Ergebnis 0
13 -bxor 7 = 10 / 1101 -bxor 0111 = 1010
shift right (schieben nach rechts) -shr Dieser Operator schiebt alle bits eines Wertes um die angegeben Anzahl Bits nach rechts.
8 -shr 2 = 2 / 1000 -shr 2 = 0010
shift left (schieben nach links) -shl Dieser Operator schiebt alle bits eines Wertes um die angegebene Anzahl Bits nach liks
1 -shl 3 = 8 / 0001 -shl 3 = 1000
Binäres NOT -bnot Dieser Operator führt ein Bitweises NOT an einem Wert durch. Jedes bit das auf 1 steht wird 0 und jedes Bit das 0 ist wird 1.
Wenn du 4 bit für den Wert zur Verfügung hast, ist der maximale Wert 24-1 = 7
-bnot 10 = 5 / -bnot 1010 = 0101
Nun musst du dir anschauen wie im Binärsystem, Nummern dargestellt werden.

Nimm einmal an, dass du 6 Bits (Stellen) hast, um Zahlen zu speichern oder darzustellen, dann ist der höchste Wert,den du speichern kannst: 26-1 = 63d.

Dies liegt daran, dass jede Zahl eine Potenz von 2 ist. Die Position des Bits minus 1 ist also die entsprechende Potenz von 2.

Die Bits werden von rechts nach links gezählt.

Bit-Position 6 5 4 3 2 1 Wert
Wert des Bit 32 16 8 4 2 1
Berechnung des Werts 2⁵ 2⁴ 2⁰
1 1 1 1 1 1 63d

Binary AND

Beim binären UND werden im Ergebnis nur die Bits auf 1 gesetzt die in beiden Zahlen auf 1 stehen.

32 16 8 4 2 1 Dezimaler Wert
0 0 1 1 1 0 14
-BAND
0 1 0 0 1 1 19
=
0 0 0 0 1 0 2

Das Resultat ist 2d da das einzige Bit das bei beiden Zahlen auf 1 steht, das zweite Bit ist, also 22-1 oder einfacher 21

Mit dem binären UND und lässt sich also einfach feststellen ob eine Zahl gerade oder ungerade ist. Denn bei einer ungeraden Zahl ist immer das Bit 1 gesetzt. Meistens wird für diese überprüfung der Modulo Operator % verwendet, das ist aber nur notwendig, wenn ich den damit ermittelten Rest verwenden möchte. Will ich nur wissen, ob die Zahl gerade oder ungerade ist geht das ebenso mit einem binären UND.

Zum Beispiel:

6 : 2 = 3 R 0 Somit ist 6 eine gerade Zahl.

8 4 2 1
0 1 1 0
6 -band 1 = 0

5 : 2 = 2 R 1 Somit ist 5 eine ungerade Zahl.
8 4 2 1
0 1 0 1

5 -band 1 = 1

In PowerShell sieht das mit dem Modulo Operator so aus:

$number = 3
if(($number % 2) -ne 0) {
    write-output "Die Zahl $number ist ungerade"
} else {
    write-output "Die Zahl $number ist gerade"
}

Und mit dem binären UND sieht es so aus. Dabei wird geprüft, od das erste Bit 20 gesetzt ist oder nicht. Ist es gesetzt so ist es eine ungerade Zahl. Wenn nicht ist die Zahl gerade

$number=3
if($number -band 1){
    write-output "Die Zahl $number ist ungerade"
} else {
    write-output "Die Zahl $number ist gerade"
}

Binäres ODER

32 16 8 4 2 1 Dezimaler Wert
0 0 1 1 1 0 14
-BOR
0 1 0 0 1 1 19
=
0 1 1 1 1 1 31

Wie du sehen kannst, führt das binäre ODER die beiden Zahlen zusammen, es ist aber nicht das Gleiche wie eine Addition. In eineigen Sonderfällen verhält sich das Binäre ODER aber identisch zu einer Addition.

14 -bor 19

Meistens wird das binäre ODER verwendet, um Flags zusammenzufügen. Sagen wir zum Beispiel, du willst eine Datei zum lesen und schreiben öffnen. Aber anderen Prozessen erlauben, die Datei trotzdem zu lesen, aber nicht zu schreiben. Dann musst du die entsprechnden Flags zusammenfügen:

Das Flag für lesen und schreiben ist 0x00000002h = 000010b = 2d

Das Flag um anderen Prozessen das schreiben zu verbieten ist 0x00000020h = 100000b = 32d
Um diese beiden Flags zusammzufügen verwendest du ein binäres ODER

0x00000002 -bor 0x00000020

Das resultat ist: 0x00000022h = 100010b = 34d
Hier würed eine Addition den selben Wert ergeben, allerdings ist es dringend zu empfhelen bei Flags mit einem binären ODER zu arbeiten. Denn sollte das Flag 0x00000020 bereits gesetzt sein, und ich versuche es mittels Addition erneut hinzuzufügen, so bekomme ich ein ganz anderes Ergebnis:
0x00000022 + 0x00000020 = 0x00000040 während
0x00000022 -bor 0x00000020 = 0x00000022

Somit muss das arbeiten mit Statusflags durch Addition und Subtraktion als unsicher angesehen werden während die Verwendung von binären Operatoren insbesonder beim binären ODER als relativ sicher angesehen werden kann.

Binary XOR

32 16 8 4 2 1 Dezimaler Wert
0 0 1 1 1 0 14
-BXOR
0 1 0 0 1 1 19
=
0 1 1 1 0 1 29

In PowerShell, this looks as follows:

14 -bxor 19

Eine spassige verwendung des binären exklusive ODER (XOR) ist es den Inhalt zweier Variablen zu tauschen ohne eine Temporäre Zwischenvariable zu verwenden.
Ok zugegeben, in PowerShell braucht man die sowieso nicht. Daher trift das eher auf C/C++, C#, etc. zu.
In PowerShell tauscht du den Wert zweier Variablen normalerweise so:

$a = 14
$b = 19
$a,$b = $b,$a

Aber es geht auch so, sofern der Wert ein Integer oder ein anderer Wert ist, der mit binären Operatoren bearbeitet werden kann:

$a = 14
$b = 19
$a = $a -bxor $b
$b = $a -bxor $b
$a = $a -bxor $b

Das binäre XOR wird außerdem bei RAID Systemen oder bei Verschlüsselungsalgorythmen eingesetzt.

(Binäres) shcieben nach links (shift left)

32 16 8 4 2 1 Dezimaler Wert
0 0 1 1 1 0 14
-SHL 2
=
1 1 1 0 0 0 56

Wie du siehst werden von rechts einfach nullen nachgeschoben. In PowerShell sieht das so aus:

14 -shl 2

(Binäres) schieben nah rechts (shift right)

32 16 8 4 2 1 Dezimaler Wert
1 1 1 0 0 0 56
-SHR 2
=
0 0 1 1 1 0 14

Wie du siehst, werden hiebei von links nullen nachgeschoben. In PowerShell sieht das so aus:

56 -shr 2

Zu berücksichtigen ist, dass beide Shift-Operatoren "verlustbehaftet" sind.
Das bedeutet, dass wenn ein Wert so weit nach Links oder Rechts geschoben wird, dass auf 1 gesetzte Bits über die obere oder untere Grenze hinausgeschoben werden, diese unwiederrufbar "verloren" sind.
Darüber hinaus gilt, zumindest in PowerShell, dass wenn die Anzahl der Bits um die geshiftet werden soll größer oder gleich groß wie die Anzahl der in dem Datentyp vorhandendn Bits ist kein shift erfolgt. Wenn ein shift left an einem Datentyp der negative zahlen darstellen kann durchgeführt wird und dabei das höchste Bit von 0 auf 1 geht wird die Zahl negativ. Bei einer negativen Zahl werden bei den shift Operationen von rechts und links einsen nachgeschoben und keine nullen. Das ligt daran, dass eine negative Zahl in der Komplementärdarstellung abgebildet wird.

32 16 8 4 2 1 Dezimaler Wert
0 0 0 0 0 1 1
1 1 1 1 1 1 -1

Binäres NICHT (NOT)

32 16 8 4 2 1 Dezimaler Wert
-BNOT
0 0 1 1 1 0 14
=
1 1 0 0 0 1 49
32 16 8 4 2 1 Dezimaler Wert
-BNOT
1 1 0 0 0 1 49
=
0 0 1 1 1 0 14

Leider ist es nicht so einfach, das binäre NOT in PowerShell zu demonstrieren, es gibt nämlich keinen Datentyp der nur 6 Bit hat.
Und den Voragang mit 32 oder mehr Bits zu demonstrieren wird ziemlich unübersichtlich.

Daher versuche ich hier einmal zu erklären wie es funktioniert.

Du musst die niedrigsten 6 bit einer vorzeichenlosen 32-Bit (unsigned integer / uint32) Zahl auf 1 setzen. Das wird deine "Maske".
Berechnet wird das wie folgt:
26 = 64d = 1000000b
Du siehst, dass das 7te Bit auf 1 gesetzt wurde was die Zahl 64 abbildet. Da du aber die 6 ersten bits auf 1 brauchst, musst du 1 von 64 subtrahieren.
26-1 = 63d = 111111b

In PowerShell, hast du verschieden möglichkeiten das gewünschte zu erreichen. Am einfachsten ist es mit einer statischen zuweisung:

[uint32]$mask = 63

Ein wenig flexibler ist es die .NET Bibliothek mit der System.Math Klasse zu verwenden und den oben beschrieben mathematische Weg zu gehen:

$mask=[uint32]([math]::pow(2,6)-1)

Eine andere Möglichkeit ist es, die Eigenschaft MaxValue der .NET Klasse System.uint32 zu verwenden.

[uint32]::MaxValue
4294967295

dann musst du aber die Bits 7 bis 32 auf 0 setzen.
Das geht am einfachsten, indem du einen shift nach rechts um (32 - 6) 26 Bit machst.

4294967295 -shr 26
63

Eine andere Möglichkeit ist es, das ganze ebenfalls über MaxValue zu lösen jedoch anstatt einem shift nach rechts einen nach links zu machen und das Ergebnis mit einem binären NOT umzukehren

4294967295 -shl 6  # 11111111 11111111 11111111 11111111 -> 11111111 11111111 11111111 11000000
4294967232  
-bnot [uint32]4294967232 # 11111111 11111111 11111111 11000000 -> 00000000 00000000 00000000 00111111 
63

Nun hast du die "Maske" und kannst sie verwenden um den richtigen Wert auszugeben wie wenn du nur 6 Bit für die Zahl zur verfügung hättest.

(-bnot 14) -band $mask
49
(-bnot 49) -band $mask
14

Das binäre UND wird hier verwendet, um die "unnötigen" 26 Bit (Bit 7 - 32) die nach dem Binären NOT ebenfalls auf 1 stehen, "wegzuwerfen".

Das binäre NOT gegen die Zahl 14 führt bei einer vorzeichenbehafteten 32 Bit Ganzzahl zu dem ergebnis -15 oder bei einer vorzeichenlosen 32 Bit Ganzzal zu 4294967281 durch das binäre AND gegen das Ergebnis mit 63 bleiben nur die ersten 6 Bits übrig und das Ergebnis ist 49.

Der einfache IPv4 Sub-Net-Rechner

Nun hast du genug Informatione, um einen einfachen Sub-Net-Rechner in PowerShell zu schreiben und dafür binäre Operatoren zu verwenmden.
Du kannst mit wirklich glauben, es ist viel einfacher als es klingt.

Jedenfalls habe ich schon Sub-Net-Rechner gesehen die so etwas mit mathematischen Berechnungen und String operationen gelößt haben, die viel komplizierter sind.

Was ist eine IPv4 Adresse?

Für einen Computer ist eine IPv4 Adresse nichts anderes als eine vorzeichenlose 32 Bit Ganzzahl
Da die IPv4 Adressen entwickelt wurden als es noch keine 32 Bit Integer gab und zur einfacheren Lesbarkeit wurden IPv4 adressen in 4 vorzeichenlose Byte zu je 8 Bit aufgeteilt. Jedes Byte kann einen wert zwischen 0 und 255 speichern.

Heute existiert dieses Problem nicht mehr und du kanst vorzeichenlose 32 Bit Ganzzahlen verwenden um mit IPv4 Adressen zu arbeiten.

Auslesen der IP-Adresse des ersten verbundenen Netzwerkadapters

Um es einfach zu machen, hol dir einfach die erste IP Adresse des esten Netzwerkadapters, der verbunden ist.

$FirstIP= ((Get-NetAdapter | Where-Object {$_.InterfaceType -in @(71,6) -and $_.Status -eq 'Up'})[0] | Get-NetIPAddress -AddressFamily IPv4)[0]

In meinem Fall, ist folgendes in $FirstIP gespeichert:

IPAddress : 172.26.1.105
InterfaceIndex : 10
InterfaceAlias : Wi-Fi
AddressFamily : IPv4
Type : Unicast
PrefixLength : 24
PrefixOrigin : Dhcp
SuffixOrigin : Dhcp
AddressState : Preferred
ValidLifetime : 29.21:16:38
PreferredLifetime : 29.21:16:38
SkipAsSource : False
Store : ActiveStore

Dabei gibt es nur 2 Einträge, die in diesem Fall von Interesse sind: IPAddress, PrefixLength

[ipaddress]$IpAddress = $FirstIP.IPAddress
[byte]$CIDR = $FirstIP.PrefixLength

In $IpAddress sind folgende Informationen gespeichert:

Address : 1761680044
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IsIPv6Teredo : False
IsIPv4MappedToIPv6 : False
IPAddressToString : 172.26.1.105
Der einzige Wert, der dich hier interessiert ist: __Address__

Daher speichere ihn in einer variablen des Typs vorzeichenlose 32 Bit Ganzzal (uint32).

[uint32]$IPAddressAsInt = $IpAddress.Address

Erinnere dich nun, dass eine IPv4 Adresse 4 Bytes hat, welche alle zusammen in einer vorzeichenlosen 32-Bit Ganzzahl gespeichert sind. Na gut, du kannst die Adress Bytes bekommen indem du die Methode GetAddressBytes() verwendest.

$IpAddress.GetAddressBytes()

Aber das bringt dir in diesem Fall keine Vorteile. Daher machst du hier mit der 32-Bit Zahl weiter.

Im nächsten Schritt prüfen wir einmal, ob der Wert von $IPAddressAsInt das ist, was wir erwarten. Wir tun das mit einem binären UND, und einer Maske mit dem Wert 255

$IPAddressAsInt -band 255
172

Gut ist, dass 172 ein Teil unserer IP-Adresse ist. Schlecht ist, dass er auf der "falschen" Seite der Zahl steht. Daher müssen wir nun einmal klären, warum das so ist.

Da IP-Adressen auf verschiedenen Systemen verwendet werden die sich miteinander verständigen können sollen, ist es erforderlich, dass IP-Adressen in einem standardisiertem Format gespeichert werden.
Daher werden IP-Adressen auf allen Systemen in der so genannten Netzwerk-Byte-Ordnung (network byte order) gespeichert. Diese speicherfolge wird auch Big-Endian genannt.
Das erste was wir nun machen, ist die Ganzzahl in das Little-Endian format bringen. Dazu vertauschen wir einfach das vierte mit dem ersten Byte und das dritte mit dem zweiten Byte.

Die Vorgehensweise dabei ist folgende:

Als erstes, machen wir ein binäres UND zwischen $IPAddressAsInt und 255. Anschließend schieben wir das Ergebis dieser Operation um 24 Bit nach lnks (shift left).
Dann schieben wir die 255 um 8 Bit nach links (shift left) und verwenden das Ergebnis um ein binäres UND gegen $IPAddressAsInt durchzuführen. Das ergebnis aus dieser Operation schieben wir anschlißend ebenfalls um 8 Bit nach links.

Sicher kannst du dir schon denken, was jetzt kommt. Richtig! Wir schieben die 255 um 16 Bit nach links (shift left) und verwenden das Ergebnis für ein binäres UND gegen $IPAddressAsInt. Das ergebnis dieser Operation verschieben wir aber um 8 Bit nach rechts (shift right).

Als letztes schieben wir die 255 um 24 Bit nach links (shift left) und verwenden das Ergebnis für ein binäres UND gegen $IPAddressAsInt. Das Ergebnis der Operation schieben wir dann um 24 BIT nach rechts (shift right).
Alle 4 schritte werden mit einem binären ODER verknüpft un das Endergebnis wird wieder in $IPAddressAsInt gespeichert.

In PowerShell sieht das Ganze dann so aus:

$IPAddressAsInt = ($IPAddressAsInt -band 255) -shl 24 -bor ($IPAddressAsInt -band (255 -shl 8)) -shl 8 -bor
($IPAddressAsInt -band (255 -shl 16)) -shr 8 -bor ($IPAddressAsInt -band (255 -shl 24)) -shr 24

Den Erfolg, kannst du ganz einfach prüfen, indem du eine explizite Umwandlung (cast) von $IPAddressAsInt in den Datentyp [ipaddress] durchführst. Das Ergebnis ist die IP-Adresse in der "falschen" Reihenfolge. "wrong" order.

[ipaddress]$IPAddressAsInt
Address : 2887385449
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IsIPv6Teredo : False
IsIPv4MappedToIPv6 : False
IPAddressToString : 105.1.26.172

Umwndeln der PrefixLength / CIDR in eine Sub-Net-Maske als vorzeichenlose Ganzzahl

Um die Sub-Net-Maske aus der Anzahl von gesetzten Netzwerk-Bits zu bekommen, haben wir zwei einfache Möglichkeiten:

  1. Berechnung indem wir 2 mit der Anzahl netzwerkbits Potenzieren und 1 subtrahieren. 2[Anzahl Netzwer Bits] - 1 gefolgt von einem schieben lach links (shift left) um (32 - [Anzahl der NetzwerkBits]) in unserem Fall: 224 - 1 und das Ergebnis um 32 - 24 = 8 nach links schieben.
  2. Oder berechnen der Host Bits nach der Formal: 2(32-[Anzahl der Netzwerk Bits]) - 1 und einem binären NICHT (ninäres NOT) gegen das Ergebnis. In unserem Fall 2(32-24) - 1 und ein binäres NICHT gegen das Ergebnis.
# Method 1
$SubnetMask = [uint32]([math]::pow(2,$CIDR) - 1) -shl (32 - $CIDR)

# Method 2
$SubnetMask = -bnot ([uint32][math]::pow(2,(32 - $CIDR)) - 1)

Um uns das Leben einfacher zu machen, erzeugen wir an der Stelle auch gleich die Host-Maske, auch bekannt als Wildcard Maske oder Invertierte Sub-Net-Maske.
Ich gehe einmal davon aus, dass sichinzwischen jeder denken kann wie das geht...

$HostMask = [uint32][math]::pow(2,(32 - $CIDR)) - 1

Mit diesen Informationen können wir an die eigentliche Arbeit gehen.

Feststellen, der Netzwerkadresse, ersten und letzten IP-Adresse sowie feststellen der Broadcast-Adresse

Die Netzwerkadresse aus jeder beliibigen IP-Adresse zu bekommen ist ganz einfach.
Wir machen einfach ein binäres UND mit der IP-Adresse und der Netzwerkmaske:

$NetworkAddress = $IPAddressAsInt -band $SubnetMask

Für die Broadcast-Adresse machen wir ain binäres ODER zwischen der Netzwerkmaske und der Host-Maske

$BroadcastAddress = $NetworkAddress -bor $HostMask

Das ermitteln der ersten und letzten IP-Adresse ist nun ebenfalls nicht schwer. Für die erste verwendbare IP-Adresse addieren wir einfach 1 zur Netzwerkadresse, für die letzte verwendbare IP-Adresse subtrahieren wir 1 von der Broadcast-Adresse.

$FirstIpAddress = $NetworkAddress + 1
$LastIpAddress = $BroadcastAddress - 1

Ermitteln der Maximal verfügbaren IP-Adressen

Um die Anzahl der verwendbaren IP-Adressen zu ermitteln, subtrhieren wir einfach 1 von der Host-Maske.

Um festzustellen, ob eine IP-Adresse im selben Sub-Net ist prüfst du einfach, ob der Ganzzahlwert der IP-Adresse größer oder gleich der ersten IP-Adresse ist und ob er kleiner oder gleich der letzten IP-Adressse ist.

Notwendige Funktionen

Nun ist es an der Zeit, alles was wir bisher gemacht haben, sinnvoll in Funktionen zu packen.

Ändern der Byte-Reihenfolge
function switchByteOrder{
param(
[Parameter(Mandatory=$true)]
[uint32]$inputInteger
)
return ($inputInteger -band 255) -shl 24 -bor ($inputInteger -band (255 -shl 8)) -shl 8 -bor ($inputInteger -band (255
-shl 16)) -shr 8 -bor ($inputInteger -band (255 -shl 24)) -shr 24
}
IP-Adresse in Ganzzahl umwandeln
function Get-IPAddressAsInteger{
param(
[ipaddress]$IPAddress
)
return (switchByteOrder -inputInteger $IPAddress.Address)
}
Subnetmaske aus der Anzahl gesetzter Netzwerk-Bits ermitteln
function Get-SubnetMaskFromNtworkBits{
param(
[Parameter(Mandatory=$true)]
[ValidateRange(0,32)]
[byte]$CIDR
)
return -bnot ([uint32][math]::pow(2,(32 - $CIDR)) - 1)
}
Host-Maske aus der Anzahl der gesetzten Netzwer-Bits ermitteln
function Get-HostMaskFromNtworkBits{
param(
[Parameter(Mandatory=$true)]
[ValidateRange(0,32)]
[byte]$CIDR
)
return ([uint32][math]::pow(2,(32 - $CIDR)) - 1)
}
Ermitteln der Netzwerkinformationen, aus einer IP-Adresse und der Anzahl von gesetzten Netzwerk-Bits
function Get-NetInfoFromIPasIntAndCIDR {
param(
[Parameter(Mandatory = $true)]
[uint32]$IpAddressAsInteger,
[Parameter(Mandatory = $true)]
[ValidateRange(0, 32)]
[byte]$CIDR
)
[uint32]$NetAddr = $IpAddressAsInteger -band (Get-SubnetMaskFromNtworkBits -CIDR $CIDR)
[uint32]$BCastAddr = $NetAddr -bor (Get-HostMaskFromNtworkBits -CIDR $CIDR)
[uint32]$SubnetMask = (Get-SubnetMaskFromNtworkBits -CIDR $CIDR)
[uint32]$HostMask = (Get-HostMaskFromNtworkBits -CIDR $CIDR)

return (New-Object psobject -Property ([ordered]@{
NetworkAddress = $NetAddr;
BroadcastAddress = $BCastAddr;
FirstIpAddress = $NetAddr + 1;
LastIpAddress = $BCastAddr - 1;
MaxHosts = $HostMask - 1;
SubnetMask = $SubnetMask;
HostMask = $HostMask
}))
}
Ganzzahl in IPAdresse umwandeln
function Convert-IntIPAddressAsIPAddress{
param(
[Parameter(Mandatory=$true)]
[uint32]$IPAsInteger
)
return [ipaddress](switchByteOrder -inputInteger $IPAsInteger)
}
Anzahl der Netzwerk-Bits aus einer Netzwerk-Maske als String / IP-Adresse ermitteln
function Get-NetMaskBitsFromDottedMask {
param(
[ipaddress]$mask
)
$count=0
[uint32]$UintIP = (switchByteOrder -inputInteger $mask.Address)
for($i=0;$i -lt 32;$i++){
if($UintIP -band 1){
$count++
} else {
if($count -ne 0){
throw "Invalid SubnetMask"
}
}
$UintIP=$UintIP -shr 1
}
return $count
}

Andere hilfreiche Funktionen

Konvertieren von einer Ganzzahl IP-Adresse in einen String
function Convert-IntIPAddressToString{
param(
[Parameter(Mandatory=$true)]
[uint32]$IPAsInteger
)
return ,$($($IPAsInteger -shr 24 -band 255).ToString()+"."+$($IPAsInteger -shr 16 -band
255).ToString()+"."+$($IPAsInteger -shr 8 -band 255).ToString()+"."+$($IPAsInteger -band 255).ToString())
}
Convertieren einer vorzeichenlosen Ganzzahl in die binäre Darstellung mit Trennug zwischen jedem einzelnen Bit
function Convert-UInt32ToBinaryStr {
param(
[uint32]$Integer
)
$(for($i=31;$i -ge 0 ;$i--){
if( $Integer -band (1 -shl $i) ){
1
} else {
0
}
}) -join '|'

}

Du kannst das zwar auch über [convert]::ToString($integer,2) machen, das hat aber 2 Nachteile:

  1. die führenden Nullen werden nicht mit ausgegeben
  2. es gibt keine Trennung zwischen den einzelnen bits

Das spielt zwar meistens keine Rolle, aber der Vergleich von zwei binär dargestellten zahlen ist einfacher wenn alle Stellen dargestellt werden.

2025-01-22
Ich habe vor ein paar Tagen eine Methode gesehen, mit der das auch bei der Verwendung von Convert geht:

[convert]::ToString($Integer,2).PadLeft(32,'0') -replace '(\G.)(?!$)','$1|'

Jetzt ist es zeit alles zusammenzufügen

[CmdletBinding(DefaultParameterSetName = 'SubnetMaskAsCIDR')]
param(
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Alias('Address', 'NetworkAddress')]
[ipaddress]$IPAddress,
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "SubnetMaskAsCIDR")]
[ValidateRange(0, 32)]
[Alias('PrefixLength')]
[byte]$CIDR,
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "SubnetMaskAsIP")]
[Alias('SubnetMask')]
[ipaddress]$Mask,
[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName)]
[ipaddress]$IPAddressToCheck

)
#region functions
function switchByteOrder {
param(
[Parameter(Mandatory = $true)]
[uint32]$inputInteger
)
return ($inputInteger -band 255) -shl 24 -bor ($inputInteger -band (255 -shl 8)) -shl 8 -bor ($inputInteger -band (255
-shl 16)) -shr 8 -bor ($inputInteger -band (255 -shl 24)) -shr 24
}

function Get-IPAddressAsInteger {
param(
[ipaddress]$IPAddress
)
return (switchByteOrder -inputInteger $IPAddress.Address)
}

function Get-SubnetMaskFromNtworkBits {
param(
[Parameter(Mandatory = $true)]
[ValidateRange(0, 32)]
[byte]$CIDR
)
return -bnot ([uint32][math]::pow(2, (32 - $CIDR)) - 1)
}

function Get-HostMaskFromNtworkBits {
param(
[Parameter(Mandatory = $true)]
[ValidateRange(0, 32)]
[byte]$CIDR
)
return ([uint32][math]::pow(2, (32 - $CIDR)) - 1)
}
function Get-NetInfoFromIPasIntAndCIDR {
param(
[Parameter(Mandatory = $true)]
[uint32]$IpAddressAsInteger,
[Parameter(Mandatory = $true)]
[ValidateRange(0, 32)]
[byte]$CIDR
)
[uint32]$NetAddr = $IpAddressAsInteger -band (Get-SubnetMaskFromNtworkBits -CIDR $CIDR)
[uint32]$BCastAddr = $NetAddr -bor (Get-HostMaskFromNtworkBits -CIDR $CIDR)
[uint32]$SubnetMask = (Get-SubnetMaskFromNtworkBits -CIDR $CIDR)
[uint32]$HostMask = (Get-HostMaskFromNtworkBits -CIDR $CIDR)

return (New-Object psobject -Property ([ordered]@{
NetworkAddress = $NetAddr;
BroadcastAddress = $BCastAddr;
FirstIpAddress = $NetAddr + 1;
LastIpAddress = $BCastAddr - 1;
MaxHosts = $HostMask -1 ;
SubnetMask = $SubnetMask;
HostMask = $HostMask
}))
}

function Get-IntIPAddressAsIPAddress {
param(
[Parameter(Mandatory = $true)]
[uint32]$IPAsInteger
)
return [ipaddress](switchByteOrder -inputInteger $IPAsInteger)
}

function Get-NetMaskBitsFromDottedMask {
param(
[ipaddress]$mask
)
$count = 0
[uint32]$UintIP = (switchByteOrder -inputInteger $mask.Address)
for ($i = 0; $i -lt 32; $i++) {
if ($UintIP -band 1) {
$count++
}
else {
if ($count -ne 0) {
throw "Invalid SubnetMask"
}
}
$UintIP = $UintIP -shr 1
}
return $count
}

#endregion functions

if ($PSCmdlet.ParameterSetName -eq 'SubnetMaskAsIP') {
$CIDR = Get-NetMaskBitsFromDottedMask -mask $Mask
}

$IpAddressAsInteger = Get-IPAddressAsInteger -IPAddress $IPAddress

$NetWorkInfo = Get-NetInfoFromIPasIntAndCIDR -CIDR $CIDR -IpAddressAsInteger $IpAddressAsInteger

$NetworkInfoToReturn = New-Object psobject -Property $([ordered]@{
NetworkAddress = (Get-IntIPAddressAsIPAddress -IPAsInteger $NetWorkInfo.NetworkAddress);
BroadcastAddress = (Get-IntIPAddressAsIPAddress -IPAsInteger $NetWorkInfo.BroadcastAddress);
FirstIpAddress = (Get-IntIPAddressAsIPAddress -IPAsInteger $NetWorkInfo.FirstIpAddress);
LastIpAddress = (Get-IntIPAddressAsIPAddress -IPAsInteger $NetWorkInfo.LastIpAddress);
MaxUsableHosts = $NetWorkInfo.MaxHosts
SubnetMask = (Get-IntIPAddressAsIPAddress -IPAsInteger $NetWorkInfo.SubnetMask);
WildcardMask = (Get-IntIPAddressAsIPAddress -IPAsInteger $NetWorkInfo.HostMask);
CIDR = $CIDR
IpIsInHostRange = $null;
IPAdressToCheck = $null
})

if ($PSBoundParameters.ContainsKey("IPAddressToCheck")) {
$IpAddressToCheckAsInteger = Get-IPAddressAsInteger -IPAddress $IPAddressToCheck
$NetworkInfoToReturn.IPAdressToCheck = $IPAddressToCheck
if ($IpAddressToCheckAsInteger -ge $($NetWorkInfo.FirstIpAddress) -and $IpAddressToCheckAsInteger -le
$($NetWorkInfo.LastIpAddress)) {
$IpIsInHostRange = $true
}
else {
$IpIsInHostRange = $false
}
$NetworkInfoToReturn.IpIsInHostRange = $IpIsInHostRange
}

return $NetworkInfoToReturn

Hier noch eineige Beispiele

.\SubnetCalcps1.ps1 -IPAddress 10.12.28.123 -CIDR 24

NetworkAddress : 10.12.28.0
BroadcastAddress : 10.12.28.255
FirstIpAddress : 10.12.28.1
LastIpAddress : 10.12.28.254
MaxUsableHosts : 254
SubnetMask : 255.255.255.0
WildcardMask : 0.0.0.255
CIDR : 24
IpIsInHostRange :
IPAdressToCheck :

.\SubnetCalcps1.ps1 -IPAddress 10.12.28.123 -CIDR 29

NetworkAddress : 10.12.28.120
BroadcastAddress : 10.12.28.127
FirstIpAddress : 10.12.28.121
LastIpAddress : 10.12.28.126
MaxUsableHosts : 6
SubnetMask : 255.255.255.248
WildcardMask : 0.0.0.7
CIDR : 29
IpIsInHostRange :
IPAdressToCheck :

.\SubnetCalcps1.ps1 -IPAddress 10.12.28.123 -CIDR 27 -IPAddressToCheck 10.12.28.100

NetworkAddress : 10.12.28.96
BroadcastAddress : 10.12.28.127
FirstIpAddress : 10.12.28.97
LastIpAddress : 10.12.28.126
MaxUsableHosts : 30
SubnetMask : 255.255.255.224
WildcardMask : 0.0.0.31
CIDR : 27
IpIsInHostRange : True
IPAdressToCheck : 10.12.28.100

.\SubnetCalcps1.ps1 -IPAddress 10.12.28.123 -CIDR 28 -IPAddressToCheck 10.12.28.100

NetworkAddress : 10.12.28.112
BroadcastAddress : 10.12.28.127
FirstIpAddress : 10.12.28.113
LastIpAddress : 10.12.28.126
MaxUsableHosts : 14
SubnetMask : 255.255.255.240
WildcardMask : 0.0.0.15
CIDR : 28
IpIsInHostRange : False
IPAdressToCheck : 10.12.28.100

.\SubnetCalcps1.ps1 -IPAddress 10.12.28.123 -CIDR 24 -IPAddressToCheck 10.12.28.100

NetworkAddress : 10.12.28.0
BroadcastAddress : 10.12.28.255
FirstIpAddress : 10.12.28.1
LastIpAddress : 10.12.28.254
MaxUsableHosts : 254
SubnetMask : 255.255.255.0
WildcardMask : 0.0.0.255
CIDR : 24
IpIsInHostRange : True
IPAdressToCheck : 10.12.28.100


Ich denke du weißt jetzt, was man mit binären Operatoren machen kann. Die Möglichkeiten sind wirklich vielfältig.

Wie immer gilt natürlich, alles hier wir ohne Now I think you have an idea of what you can do with binary operators. Wie immer gilt natürlich, alles hier wird ohne Anspruch auf Vollständigkeit und ohne Anspruch auf Gewährleistung und Garantie zur Verfügung gestellt.

In diesem Script habe ich jetzt weder Prüfroutinen noch Fehlerbehandlungen implementiert, aber dich bin mir sicher, dass du das auch alleine schaffst.