Search:

ipgrep - IP-Nummern grep'en

Wenn man den Bogen erstmal 'raus hat ist es einfach mit IP-Nummern zu hantieren. Bislang haben wir noch nichts zum grep'en. Ich denke da an ein Programm das alle Zeilen ausgibt die eine IP-Nummer aus einem gesuchten Netz enthalten.

Im Prinzip läuft es darauf hinaus zu dem Netz nach dem wir suchen die erste und letzte IP-Nummer im Netz zu bestimmen. Damit lässt sich dann einfach entscheiden, ob eine IP-Nummer im gesuchten Netz liegt oder nicht.

Aber das klingt viel zu einfach, machen wir es was schwieriger. Das Feld in dem die IP-Nummer steht soll ein Parameter auf der Kommandozeile sein (so wie bei ipsort).Ausserdem sollte das Programm mehrere Netze als Parameter unterstützen, eine Zeile wird ausgegeben, wenn ihre IP-Nummer in einem der Netze liegt. Und als drittes könnte man eine Invertierung einbauen.

#!/usr/bin/gawk -f
#

#
# ipgrep <field> [-]<ipnum>/<mask> <file> ...
#
#	Grep ip-numbers in awk field <field> from <file>.
#

...
ipval(), ipmask() and ipstr() as in ipnumbers above
...

BEGIN {
	STDERR = "/dev/stderr";

	argi = 1;
	field = ARGV[argi];  ARGV[argi++] = "";

	n = split(ARGV[argi], x, ",");  ARGV[argi++] = "";
	for (i=1; i<=n; i++) {
		iparg = x[i];
		if (substr(iparg, 1, 1) == "-") {
			invert[i] = 1;
			iparg = substr(iparg, 2);
			}

		if (index(iparg, "/") == 0)
			iparg = iparg "/32";

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

		first[i] = and(ip, mask);
		last[i] = or(ip, compl(mask));
		}
	}

/./ {
	if (split($field, x, ".") == 4) {
		printline = 0;
		ipnum = ipval($field);
		for (i=1; i<=n; i++) {
			matches = 0;
			if (first[i] <= ipnum  &&  ipnum <= last[i])
				matches = 1;

			if (invert[i] + matches == 1) {
				printline = 1;
				break;
				}
			}

		if (printline != 0)
			print $0;
		}
	}

Im BEGIN Block verarbeiten wir den zweiten Parameter: eine Komma-getrenne Liste von Netzwerkangaben. Das funktioniert genauso wie in den anderen Skripts, da ist nichts wirklich neues. Zu den Netzwerkangaben first[] und last[] wird ggf. noch der Wert im invert[] Array gesetzt je nachdem, ob das Testergebnis invertiert werden soll oder nicht.

Für jede Zeile die an der angegebenen Position sowas wie eine IP-Nummer enthält wird dann nachgesehen, ob die IP-Nummer in eines der Netze fällt. Sobald das zutrifft (unter Berücksichtigung der Invertierung) wird die Zeile ausgegeben. So einfach kann das sein. Aber ich möchte auch noch darauf hinweisen, daß wenn man eine Liste von Netzen angibt und eins oder mehrere Netz ihren Test invertieren man nicht unbedingt die Ausgabe bekommt die man erwartet. Eventuell ist es keine gute Idee mit gemischten Testarten zu arbeiten.

# ipgrep 1 192.168.1.1/24 /etc/hosts
192.168.1.8	linux-1		linux-1.meine.domain
192.168.1.10	gw		gw.meine.domain

Soweit ist alles klar, das ist ziemlich genaus das, was ich erwartet habe.

# ipgrep 1 192.168.1.1/21,-192.168.1.10 /etc/hosts
127.0.0.1	localhost	localhost.localdomain
192.168.1.8	linux-1		linux-1.meine.domain
192.168.4.1	win-pc		win-pc.meine.domain
192.168.1.10	gw		gw.meine.domain
192.168.12.49	nameserver	nameserver.provider.domain
192.168.12.50	mailserver	mailserver.provider.domain

Das ist das Problem das ich meinte, nicht mischen! So geht's richtig:

# ipgrep 1 192.168.1.1/21 /etc/hosts | ipgrep 1 -192.168.1.10
192.168.1.8	linux-1		linux-1.meine.domain
192.168.4.1	win-pc		win-pc.meine.domain

Eventuell waere es sinnvoller die Invertierung als globale Option, d.h. entweder für alle Netze oder keins zu implementieren. Dann muß aber auch die Auswahllogik bei der Invertierung von "fällt in mindestens eins" in "fällt nicht in alle" umgewandelt werden.

Aber da wir keine Eile haben können wir es auch richtig machen:

#!/usr/bin/gawk -f
#

#
# ipgrep <field> [-]<ipnum>/<mask> <file> ...
#
#	Grep ip-numbers in awk field <field> from <file>.
#

...
ipval(), ipmask() and ipstr() as in ipnumbers above
...

BEGIN {
	STDERR = "/dev/stderr";

	argi = 1;
	invert = 0;
	if (ARGV[argi] == "-i") {
		invert = 1;
		ARGV[argi++] = "";
		}

	field = ARGV[argi];  ARGV[argi++] = "";
	n = split(ARGV[argi], x, ",");  ARGV[argi++] = "";
	for (i=1; i<=n; i++) {
		iparg = x[i];
		if (index(iparg, "/") == 0)
			iparg = iparg "/32";

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

		first[i] = and(ip, mask);
		last[i] = or(ip, compl(mask));
		}
	}

/./ {
	if (split($field, x, ".") == 4) {
		ipnum = ipval($field);
		for (i=1; i<=n; i++) {
			if (first[i] <= ipnum  &&  ipnum <= last[i]) {
				if (invert == 0)
					print $0;

				next;
				}
			}

		if (invert == 1)
			print $0;
		}
	}

Unser Auswahlkommando von oben sieht nun so aus:

# ipgrep 1 192.168.1.1/21 /etc/hosts | ipgrep -i 1 192.168.1.10
192.168.1.8	linux-1		linux-1.meine.domain
192.168.4.1	win-pc		win-pc.meine.domain

Ansonsten gibt's auch keine Verwirrung mehr bezüglich der Testinvertierung, entweder keine Invertierung oder Invertierung aller (wie grep also). Und das funktioniert auch:

# ipgrep -i 1 192.168.1.10,127.0.0.0/8
192.168.1.8	linux-1		linux-1.meine.domain
192.168.4.1	win-pc		win-pc.meine.domain
192.168.12.49	nameserver	nameserver.provider.domain
192.168.12.50	mailserver	mailserver.provider.domain

Ich würde sagen wir haben ein neues Programm für unsere kleine Bibliothek.

< dag | at | awk-scripting.de >