#!/usr/bin/perl -w #------------------------------------------------------------------------------- # subnetzcalc.pl (C) 2003 T.Birnthaler OSTC GmbH # Aller Netze und Subnetze für IP + Netzmaske + Subnetzmaske berechnen. #------------------------------------------------------------------------------- # Input: IP = 192.168.65.23 # Maske = 255.255.255.0 oder /24 (beides erlaubt) # SubMaske = 255.255.255.128 oder /25 (beides erlaubt) #------------------------------------------------------------------------------- # Output: Binärdarstellungen von IP und Maske # IP = 11000000.10101000.01000001.00010111 # Maske = 11111111.11111111.11111111.00000000 # Netzwerk in Dot-Notation und binär: # Netzwerk = 192.168.65.0 # Netzwerk = 11000000.10101000.01000001.00000000 # Minimale + max. Host-Adresse (Dot-Notation + Binär): # 192.168.65.1 11000000.10101000.01000001.00000001 # 192.168.65.254 11000000.10101000.01000001.11111110 # Anzahl der Hosts pro Netz: # 254 # Netzwerk-Klasse: # C # Anzahl Subnetze # 2 # Für jedes Subnetz die obigen Netzdaten # ... # Anzahl Hosts in allen Subnetzen zusammen # 252 #--------------------------------------------------------------------- # Möglichkeiten zur Repräsentation der IP-Adressen/Netzwerk-Masken: # 1.) Array mit 32 Elementen "0"/"1". # (evtl. zerlegt in 4 Arrays a 8 Bit) # 2.) Für Netzwerk-Masken: Anzahl der gesetzten Bits: /1 .. /31 # 3.) Binär mit 32 Bit (long!) ********* HIER VERWENDET! # 4.) String mit Dot-Notation # (evtl. zerlegt in 4 Elemente 0..255) # ... # Abhängig davon werden bestimmte Programmteile einfach, dafür aber # andere kompliziert. Entwurfsentscheidung, d.h. nicht unbedingt # eine optimale Lösung erkennbar (Auswahl abhängig vom Kenntnisstand). #--------------------------------------------------------------------- use strict; # Für die Eingaben my ($ip, $netmask, $subnetmask); # Option "-h" => Usage-Meldung if (defined $ARGV[0]) { Usage() if $ARGV[0] eq "-h"; } # Eingabe beim Aufruf über Argumente oder einlesen? if ((($ip, $netmask, $subnetmask) = @ARGV) < 2) { # Eingabe Einlesen (für Austesten nicht so geschickt, da langsam!) print "IP-Adresse (W.X.Y.Z): :"; chomp($ip = ); print "Netzwerk-Maske (K.L.M.N oder /BIT oder BIT): :"; chomp($netmask = ); print "Sub-Netzwerk-Maske (K.L.M.N oder /BIT oder BIT) :"; chomp($subnetmask = ); } else { print "IP-Adresse: $ip\n" if defined($ip); print "Netzwerk-Maske: $netmask\n" if defined($netmask); print "Sub-Netzwerk-Maske: $subnetmask\n" if defined($subnetmask); } # Hash für die Umwandlung Slash- => Dot-Notation füllen # (32 relativ komplizierte Zeilen, d.h. Tippfehler-anfällig) #my %bits_to_dot = { # "1" => "128.0.0.0", # "2" => "192.0.0.0", # "3" => "224.0.0.0", # "4" => "240.0.0.0", # "5" => "248.0.0.0", # "6" => "252.0.0.0", # "7" => "254.0.0.0", # "8" => "255.0.0.0", # "9" => "255.128.0.0", # ... #}; # Besser: Hash für Umwandlung Slash- => Dot-Notation per Schleife berechnen # ACHTUNG: Mit höchstwertigem Bit wird Aufbau begonnen (links nach rechts)! my %bits_to_dot; my $sum = 0; for (1..32) { $sum += 2 ** (32 - $_); $bits_to_dot{$_} = Bin2Dot($sum); } # Hash invertieren für Umwandlung Dot- => Slash-Notation my %dot_to_bits = reverse %bits_to_dot; # Hauptnetz ausgeben my ($net_bin, $netmask_bin) = &PrintMainNet($ip, $netmask); # Subnetz-Maske angegeben? => Subnetze ausgeben if (defined($subnetmask)) { &PrintSubNets($net_bin, $netmask_bin, $subnetmask); } # Hauptnetz-Daten berechnen und ausgeben sub Usage { print "usage: subnetzcalc.pl [IP NETMASK [SUBNETMASK]]"; print " IP = W.X.Y.Z\n"; print " NETMASK = K.L.M.N oder /BIT (/1../31) oder BIT (1..31)\n"; print " SUBNETMASK = K.L.M.N oder /BIT (/1../31) oder BIT (1..31)\n"; exit; } # Hauptnetz-Daten berechnen und ausgeben sub PrintMainNet { my ($ip, $netmask) = @_; $netmask = Mask2Dot($netmask); # Eingaben in (interne) Binär-Form umwandeln und Netz ausrechnen my $ip_bin = &Dot2Bin($ip); my $netmask_bin = &Dot2Bin($netmask); my $net_bin = $ip_bin & $netmask_bin; # Netz = IP UND-verknüpft Maske # Hauptnetz ausgeben print "\nHauptnetz\n"; printf("IP = %-15s %08b.%08b.%08b.%08b\n", $ip, Bin2Arr($ip_bin)); PrintNet($net_bin, $netmask_bin); return ($net_bin, $netmask_bin); } # Subnetz-Daten berechnen und ausgeben sub PrintSubNets { my ($net_bin, $netmask_bin, $subnetmask) = @_; my $subnetmask_bin = &Dot2Bin(Mask2Dot($subnetmask)); # Anzahl der Subnetze und IPs pro Subnetz my $netmask_bits = $dot_to_bits{&Bin2Dot($netmask_bin)}; my $subnetmask_bits = $dot_to_bits{&Bin2Dot($subnetmask_bin)}; my $subnets = 2 ** ($subnetmask_bits - $netmask_bits); my $subnet_ips = 2 ** (32 - $subnetmask_bits); printf("\nAnzahl Subnetze = %d\n", $subnets); # Alle Subnetze ausrechnen und ausgeben my $subnet_hosts = 0; for (1..$subnets) { print "\n$_. Subnetz\n"; &PrintNet($net_bin, $subnetmask_bin); $net_bin += $subnet_ips; $subnet_hosts += $subnet_ips - 2; } # Subnetz-Statistik berechnen und ausgeben my $net_hosts = ~ $netmask_bin - 1; print "\n"; printf("Hosts im Gesamtnetz: %d\n", $net_hosts); printf("Hosts in Subnetzen: %d\n", $subnet_hosts); printf("Verschnitt: %d (%.2f%%)\n\n", $net_hosts - $subnet_hosts, 100 * ($net_hosts - $subnet_hosts) / $net_hosts); } # Netzwerk-Daten berechnen und ausgeben sub PrintNet { my ($net_bin, $netmask_bin) = @_; my $host_min = $net_bin + 1; my $host_max = $net_bin | ~ $netmask_bin - 1; # Maske invertiert addieren my $net_hosts = $host_max - $host_min + 1; # 1. Methode $net_hosts = ~ $netmask_bin - 1; # 2. Methode my $host_bits = 32 - $dot_to_bits{&Bin2Dot($netmask_bin)}; $net_hosts = 2 ** $host_bits - 2; # 3. Methode my $broadcast = $net_bin | ~ $netmask_bin; printf("Maske = %-15s %08b.%08b.%08b.%08b\n", &Bin2Dot($netmask_bin), &Bin2Arr($netmask_bin)); printf("Netzwerk = %-15s %08b.%08b.%08b.%08b\n", &Bin2Dot($net_bin), &Bin2Arr($net_bin)); printf("Broadcast = %-15s %08b.%08b.%08b.%08b\n", &Bin2Dot($broadcast), &Bin2Arr($broadcast)); printf("Min. Host = %-15s %08b.%08b.%08b.%08b\n", &Bin2Dot($host_min), &Bin2Arr($host_min)); printf("Max. Host = %-15s %08b.%08b.%08b.%08b\n", &Bin2Dot($host_max), &Bin2Arr($host_max)); printf("Anz.Hosts = %d\n", $net_hosts); printf("Klasse = %s\n", &NetClass($net_bin)); } # Umwandlungsfunktion: /24 => 255.255.255.0 oder 255.255.255.0 lassen sub Mask2Dot { my ($mask) = @_; # Art der Masken-Angabe erkennen und in Dot-Notation umwandeln if ((my @tmp = split(/\./, $mask)) != 4) { # @tmp nur wg. split-Warnung! $mask =~ s/\/0*//; $mask = $bits_to_dot{$mask}; } return $mask; } # Umwandlungsfunktion: 192.168.65.23 => 32-Bit-Binär-Zahl sub Dot2Bin { my $ip = shift @_; # Am Punkt in 4 Teile zerlegen my @ip = split(/\./, $ip); # (Punkt "\" schützen, da Metazeichen) $ip = $ip[0] << 24 | # Um 24 Bit nach links verschieben $ip[1] << 16 | # "<<" = shift nach links $ip[2] << 8 | # (entspricht * 2 hoch 24) $ip[3]; # "|" = oder (auch "+" wäre okay!) return $ip; } # Umwandlungsfunktion: 32-Bit-Binär-Zahl => 192.168.65.23 sub Bin2Dot { return join(".", Bin2Arr($_[0])); # 4 Byte mit Punkten zu Dot- } # Notation zusammensetzen # Umwandlungsfunktion: 32-Bit-Binär-Zahl => Array sub Bin2Arr { # Die GANZE IP wird verschoben, my $ip = shift @_; # (über Rand hinausschieben geht) my @ip = (($ip >> 24) & 255, # Byte 1 (192) isolieren (& = und) ($ip >> 16) & 255, # Byte 2 (168) isolieren (">>" =) ($ip >> 8) & 255, # Byte 3 (65) isolieren (nach rechts) $ip & 255); # Byte 4 (23) isolieren (schieben) return @ip; } # Aus binärer Netzwerk-Adresse die Netzwerk-Klasse ermitteln # (definiert durch die ersten 1-4 Bits) # Klasse A = 0 # 0 Dezimal # Klasse B = 10 # 128 Dezimal # Klasse C = 110 # 192 Dezimal # Klasse D = 1110 # 224 Dezimal # Klasse E = 1111 # 240 Dezimal sub NetClass { my $net_bin = shift @_; my $netclass; $netclass = $net_bin & 0b1000_0000_0000_0000_0000_0000_0000_0000; if ($netclass == 0) { return "A"; } $netclass = $net_bin & 0b1100_0000_0000_0000_0000_0000_0000_0000; if ($netclass == 0b1000_0000_0000_0000_0000_0000_0000_0000) { return "B"; } $netclass = $net_bin & 0b1110_0000_0000_0000_0000_0000_0000_0000; if ($netclass == 0b1100_0000_0000_0000_0000_0000_0000_0000) { return "C"; } $netclass = $net_bin & 0b1111_0000_0000_0000_0000_0000_0000_0000; if ($netclass == 0b1110_0000_0000_0000_0000_0000_0000_0000) { return "D"; } return "E"; }