ipnumbers - IP-Netze ausgeben

Na ja, bis hierhin war's einfach. Habt ihr schonmal versucht alle Rechner in einem Netzwerk anzupingen? fping ist da euer Freund. Es nimmt beliebig viele IP-Nummern, pingt die angegebenen Adressen an und meldet zurück von welchen Rechnern Antworten kamen und welchen nicht. Das Ganze geht auch recht fix, jedenfalls wenn man es mit einzelnen ping Aufrufen vergleicht.

Die Sache hat aber eine Haken. fping nimmt nur IP-Nummern und versteht keine Netzwerkdefinitionen der Art 192.168.1.3/24. Was also machen wir, wenn wir alle IP-Nummern in, sagen wir mal, einem C-Netz anpingen wollen?

Sehen wir uns erstmal die Notation an. Bei 192.168.1.3/24 ist 192.168.1.3 die IP-Nummer und "/24" steht für die Netzwerkmaske. Genauer für die Anzahl der gesetzten Bits in der Netzwerkmaske, wenn man sich die Angabe binär aufschreibt. Alles klar? Ok, schon verstanden. Machen wir also ein Beispiel.

Das ist unsere Netzwerkmaske ... 255.255.255.0
Das war doch einfach, oder? Jetzt machen wir aus den Hexzahlen Binärzahlen ... 11111111.11111111.11111111.00000000

Und weil in der Netzmaske 255.255.255.0 gerade 24 Einsen gesetzt sind schreibt man hierfür auch /24. Sollte sich übrigens zwischen den Einsen eine Null verstecken (oder umgekehrt) ist das bullshit und keine Netzwerkmaske.

Wo wir schon mal dabei sind machen wir noch ein bißchen weiter. Wir hatten die IP-Nummer 192.168.1.3.

Unsere IP-Nummer ... 192.168.1.3
sieht Hexadezimal so aus: C0.A8.01.03
Das ist unsere Netzwerkmaske (hexadezimal): FF.FF.FF.00
Das ist das Ergebnis der bitweisen UND-Verknüpfung zwischen der IP-Nummer und der Netzwerkmaske (immer noch hexadezimal) ... C0.A8.01.00

192.168.1.0 ist aber auch die sogenannte Netzwerkadresse unseres kleinen IP-Netzes. Aha. Nimmt man das Komplement der Netzwerkmaske und setzt diese Einsen im Wert einer IP-Nummer aus dem Netzwerk so erhält man die Broadcastadresse (ich spare mir dieses Beispiel aber hier). Die Rechner im Netz sind jetzt genau die, die zwischen der Netzwerkadresse und der Broadcastadresse liegen.

Die Beispiele zeigen ganz deutlich auch in welche Richtung die Sache geht: Rechnen mit Bits.

Hier kann gawk ab Version 3.1 weiter helfen. Ab dieser Version gibt es nämlich die Funktionen and(), or(), xor(), lshift(), rshift() und compl(). Die Funktionen arbeiten wie in anderen Programmiersprachen auch. Der Unterschied zu z.B. C ist, das die Funktionen in C als Operatoren implementiert sind.

Damit wird die Sache recht einfach, wenn man die IP-Nummern in Dezimalzahlen umwandelt. Das geht wie folgt:

function ipval(str,   i, n, x, val) {
	if ((n = split(str, x, ".")) != 4)
		return (0);

	val = x[1];
	for (i=2; i<=4; i++)
		val = lshift(val, 8) + x[i];

	return (val);
	}

Dazu braucht man natürlich noch eine passende Funktion, die die interne Zahldarstellung wieder in das verwandelt, was wir eine IP-Nummer nennen:

function ipstr(val,   i, str) {
	str = sprintf ("%d", and(val, 255));
	for (i=2; i<=4; i++) {
		val = rshift(val, 8);
		str = sprintf ("%d", and(val, 255)) "." str;
		}

	return (str);
	}

Das komplette Programm sieht dann so aus:

#!/usr/bin/gawk -f
#

#
# ipnumbers <ipnum>/<ipmask>
#
#	print all IP-number in the given network omitting the network
#	and broadcast address.
#


function ipval(str,   i, n, x, val) {
	if ((n = split(str, x, ".")) != 4)
		return (0);

	val = x[1];
	for (i=2; i<=4; i++)
		val = lshift(val, 8) + x[i];

	return (val);
	}

function ipmask(str,   val) {
	if (index(str, ".") == 0) {
		str = str + 0;
		if (str < 0  ||  str > 32)
			str = 32;

		val = (str == 0)? 0: lshift(compl(0), 32 - str);
		}
	else {
		val = ipval(str);
		if (val == 0)
			val = compl(0);
		}

	return (val);
	}

function ipstr(val,   i, str) {
	str = sprintf ("%d", and(val, 255));
	for (i=2; i<=4; i++) {
		val = rshift(val, 8);
		str = sprintf ("%d", and(val, 255)) "." str;
		}

	return (str);
	}

BEGIN {
	STDERR = "/dev/stderr";
	iparg = ARGV[1];  ARGV[1] = "";
	if (index(iparg, "/") == 0)
		iparg = iparg "/" 32;

	k = index(iparg, "/");
	ip = ipval(substr(iparg, 1, k-1));
	mask = ipmask(substr(iparg, k+1));

	first = and(ip, mask) + 1;
	last = or(ip, compl(mask)) - 1;
	if (compl(0) - mask > 3) {
		first++;
		last--;
		}

	for (adr=first; adr <= last; adr++)
		print ipstr(adr);

	exit (0);
	}

Das das Programm die Netzwerk- und die Broadcastadresse nicht ausgibt liegt daran, daß diese i.d.R. keine normalen Adressen sind. Auf beiden Adressen bekommt man ping Antworten von jeweils einem ganzen Pulk von Rechnern (leider aber nicht von allen, das wäre ja zu einfach). Das soll also so sein. Das hätte auch zur Folge, daß ipnumber für die Netzwerkmasken /31 und /32 keine Ausgabe liefert aber ich habe da einen kleinen Workaround gemacht. ipnumber versteht übrigens auch "normale" Netzwerkmasken. Man kann also sowohl /24 als aus 255.255.255.0 schreiben. Ach ja, wir wollten fping was zu tun geben, ohne die ganzen IP-Nummern selber einzutippen:

# ipnumbers 192.168.1.7/24 | fping

und los geht's.

< dag | at | awk-scripting.de >