Aus Linux-Magazin 01/2008

Hochverfügbarer Internetzugang für zu Hause mit Perl

© Mordechai/Photocase.com

Fällt der Internetprovider aus, sitzt der Nutzer zu Hause auf dem Trockenen. Es sei denn, er hilft sich wie ein Profi und schaltet auf die Konkurrenz um. Ein Perl-Skript konfiguriert dann den Rechner neu.

In Kalifornien gibt es derzeit zwei große Konkurrenten für Internetanschlüsse in Privathaushalten: die Telefongesellschaft AT&T mit ihrem DSL und die Kabelfirma Comcast, die durch das Fernsehkabel ebenfalls Breitband-Internet anbietet. Ein DSL- oder Kabelmodem zapft die Daten aus der Leitung, ein vorgeschalteter Router verbindet sämtliche Rechner des Haushalts im Handumdrehen mit dem Internet (Abbildung 1).

Abbildung 1: Ein Blick auf das Haushaltsnetzwerk der Perlmeister-Studios.

Abbildung 1: Ein Blick auf das Haushaltsnetzwerk der Perlmeister-Studios.

Billig oder schnell

Beide Anbieter haben Vor- und Nachteile. So leidet die Kabelverbindung, wenn zu viele Kinder in der Nachbarschaft “World of Warcraft” spielen, weil die Bandbreite mit der Zahl der Nutzer abnimmt. Der legendäre Werbespot “Web Hog!” [2] des DSL-Anbieters Southern Bell illustriert humorvoll, wie sich friedfertige Nachbarn einer Kleinstadt über Nacht in Berserker verwandeln, weil sie sich angeblich gegenseitig das Internet lahmlegen. Das DSL der Telefongesellschaft ist zumindest in der langsamen Billigversion preisgünstiger und jeder bekommt die gleiche Bandbreite. Allerdings hängen Verfügbarkeit und tatsächlicher Durchsatz von der Entfernung zum nächsten Knotenpunkt ab.

Keines der beiden Verfahren ist jedoch absolut zuverlässig, immer wieder gibt\’s Probleme: ein Stromausfall oder ein Bauarbeiter, der ein Kabel anbohrt, – schon fällt das Internet aus. Ärgerlich für den, der es gerade dringend braucht. Da aber sowohl DSL als auch Kabel-Internet im Sonderangebot monatlich nur etwa 20 Dollar kosten, habe ich mir beide bestellt. Wenn ein Anbieter ausfällt, schalte ich einfach auf den anderen um.

Schalter-Skript

Das Skript »isp-switch« (Listing 1) nimmt als Parameter entweder »cable« oder »dsl« auf der Kommandozeile entgegen und führt die zum Umschalten nötigen Schritte aus. In dem Skript bestimmt Zeile 21 die möglichen Werte dieser Parameter. Bei unbekannten Providernamen bricht es mit »pod2usage()« ab und gibt eine Fehlermeldung sowie die verkürzte Bedienungsanleitung aus.

Die weiche Referenz »$switch_to->()« in Zeile 31 ruft dann die weiter unten definierten Funktionen »cable()« oder »dsl()« auf. Damit der Perl-Interpreter im Strict-Modus bei diesem schmutzigen Trick nicht ausflippt, muss das Skript die Strenge mit »no strict \’refs\’« etwas abmildern. Das umschließende »eval«-Konstrukt fängt etwaige Fehler ab und lässt es weiterrattern, auch wenn nicht alle Schritte erfolgreich ablaufen.

Das Skript nutzt Log::Log4perl für die Status- und Fehlermeldungen. Das in Zeile 48 verwendete Makro »ALWAYS« kam allerdings erst mit Version 1.13 hinzu, deshalb fordert Zeile 9 mindestens diese Version an.

Patch mich!

Mein Linux-Rechner hat eine statische 192.er-IP im lokalen Netzwerk, nutzt also kein DHCP. Unter Fedora konfiguriert er in der Datei »/etc/sysconfig/network« sein Default-Gateway, also den Router, der Anfragen ins Internet weiterleitet. Abbildung 2 zeigt die Datei im Originalzustand. Das Gateway ist ein Gerät der Firma Buffalo vom Typ G54, das seit Jahren den internen Netzwerkverkehr der Perlmeister-Studios zuverlässig mit dem DSL-Modem und damit dem Internet verbindet.

Abbildung 2: Ursprüngliche Version von »/etc/sysconfig/network« vor dem Einspielen des Patch.

Abbildung 2: Ursprüngliche Version von »/etc/sysconfig/network« vor dem Einspielen des Patch.

Kommt wegen eines DSL-Ausfalls jedoch die Kabelverbindung zum Einsatz, benötigt sie einen anderen Router. Wie lassen sich sie Konfigurationsdateien verändern und zugleich ihr Originalzustand speichern, der jederzeit wiederherstellbar sein soll?

Modul für Patches

Das CPAN-Modul Config::Patch fügt Patches in System-Konfigurationsdateien ein und merkt sich den Originaltext als Base-64-kodierte Kommentare. Möchte der User das Patch wieder entfernen, liest das Modul die Base-64-Kodierung, extrahiert den Originaltext und restauriert ihn wieder.

Abbildung 3 zeigt die Datei »network«, nach dem Einspielen des Patch. Die ab Zeile 60 definierte Funktion »gateway _patch« erhält als Parameter die IP-Adresse des Gateway. Der Konstruktor des Config::Patch-Objekts bekommt den Namen der zu patchenden Datei und einen Schlüssel. Diese Zeichenkette speichert typischerweise den Projekt- oder Applikationsnamen und hilft dem Modul, Patches ein und derselben Datei durch verschiedene Schlüssel zu unterscheiden und getrennt zu bearbeiten. Bereits gepatchte Bereiche markiert das Modul als verbotene Zonen und verhindert so überlappende Patches.

Abbildung 3: Mit Hilfe des Moduls Config::Patch modifizierte Konfigurationsdatei.

Abbildung 3: Mit Hilfe des Moduls Config::Patch modifizierte Konfigurationsdatei.

Config::Patch kann Patches wahlweise am Anfang einer Datei, zwischen zwei Zeilen oder am Ende einfügen. Die Methode »replace« ersetzt gar eine mit Hilfe eines regulären Ausdrucks spezifizierte Zeile oder einen Zeilenbereich mit der angegebenen Ersatzzeile. Der Aufruf

$patcher->replace(
 qr(^GATEWAY=.*)m,
 "GATEWAY=$ip");

sucht nach einer Zeile, die mit »GATEWAY=« beginnt. Da der Regex über mehrere Zeilen greift, ist der Modifizierer »/m« relevant, damit das Metazeichen »^« auch tatsächlich sämtliche Zeilenanfänge erkennt und nicht nur den der ersten Zeile. Die auf diese Weise gefundene Gateway-Zeile kodiert Config::Patch zu einem Merker, kommentiert diesen anschließend aus und ersetzt die Zeile durch einen Gateway-Eintrag mit der neuen IP-Adresse.

Um die Datei wieder in den Ursprungszustand zurückzuversetzen, genügt der Aufruf »$patcher->remove()«, denn der Patcher benötigt nur den Dateinamen und den Key, die bereits im Konstruktor des Objekts vorliegen. Die Methode »patched()« stellt fest, ob es sich um eine bereits mit dem angegebenen Schlüssel gepatchte Datei handelt, und liefert im positiven Fall einen wahren Wert zurück.

Strom auf Kommando

Der Kabel-Router ist im DSL-Betrieb abgeschaltet. Mit Hilfe der in [3] vorgestellten X10-Schnittstelle schaltet das CPAN-Modul X10::Home ihn über das Stromnetz ein. Das besorgt der Eintrag in »/etc/x10.conf«, der die Ansteuerung des Kabel-Routers über den Namen »cable_router« erlaubt. Beide Router sind baugleich und arbeiten auch als DHCP-Server. Hängt sich ein Computer ins Hausnetz, erhält er so eine dynamische IP zugewiesen und weiß, an welchen DNS-Server er sich wenden muss, um Hostnamen aufzulösen. Zwei DHCP-Server, die zeitgleich den gleichen Service anbieten, lösen allerdings Chaos aus, schließlich sollen die Rechner im Kabelbetrieb nicht mehr den alten, sondern den neuen DHCP-Server mit dessen Kabel-Gateway nutzen.

DHCP-Doppel verhindern

Um das Problem zu lösen, kontaktiert das CPAN-Modul Buffalo::G54 die Konfigurationsoberfläche des Routers und fummelt mittels Screenscraping über das Modul WWW::Mechanize so lange daran herum, bis der DHCP-Server des DSL-Routers abgestellt ist. Allerdings ist dazu das Admin-Passwort des Routers erforderlich, welches das Skript »isp-switch« mit der Funktion »password_read()« aus dem CPAN-Modul Sysadm::Install beim User abfragt.

Im umgekehrten Fall, wenn es durch das Kommando »dsl« wieder zurück ans DSL geht, aktiviert das Skript den DHCP-Server des DSL-Routers wieder und dreht dem Kabel-Router per X10 den Strom ab. Da es eine Zeit dauert, bis der neue Router hochgefahren ist, wartet »isp-switch« mit »sleep()« 20 Sekunden, bevor es weitere Aktionen einleitet.

Rechner mit dynamischen Adressen holen sich nach einem Reboot oder Restart des Netzwerks eine neue IP-Adresse vom neuen DHCP-Server. Die Linux-Box mit der statischen IP-Adresse muss ebenfalls ihr Netzwerk-Initialisierungsskript neu starten, damit sie Requests an das neue und nicht mehr an das alte Gateway schickt.

Dazu lässt »isp-switch« das Linux-Startup-Skript »/etc/rc.d/init.d/network« mit dem Parameter »restart« laufen. Dafür sind Root-Rechte erforderlich, aber ein Eintrag mit »NOPASSWD« in »/etc/sudoers« für das Skript erlaubt es auch dem nicht privilegierten User »mschilli«, das Netzwerk neu zu initialisieren. Die Funktion »tap« aus dem fast unerschöpflichen Fundus des CPAN-Moduls Sysadm::Install führt das Kommando aus und schluckt dessen Ausgabe.

Mit diesen drei Maßnahmen schaltet »isp-switch« zwischen den beiden Internetanbietern hin und her. Der Internetanschluss ist auf diese Weise zwar auf einmal doppelt so teuer, dafür aber auch doppelt so zuverlässig. Als Alternative könnte man einen dritten Router einsetzen, der als Gateway arbeitet und Requests wahlweise an den Kabel- oder den DSL-Router weiterleitet. Er könnte auf einem Linux-PC oder einem unter FreeWRT laufenden Linksys-Router des Typs WRT54GL arbeiten.

Wer möchte, schreibt sich selber noch ein kleines Nagios-Plugin, das periodisch die Internetverbindung prüft und bei Problemen automatisch umschaltet – ohne dass der User überhaupt etwas mitbekommt. (jcb)

Listing 1:
»isp-switch«

001 #!/usr/bin/perl -w
002 use strict;
003 use Getopt::Std;
004 use Pod::Usage;
005 use Sysadm::Install qw(:all);
006 use Config::Patch;
007 use Buffalo::G54;
008 use X10::Home;
009 use Log::Log4perl 1.13 qw(:easy);
010 
011 my @isps = qw(cable dsl);
012 my($switch_to) = @ARGV;
013 
014 Log::Log4perl->easy_init($INFO);
015 
016 if(! defined $switch_to) {
017     pod2usage(
018           "Specify what ISP to switch to");
019 }
020 
021 if(! grep { $_ eq $switch_to }  @isps) {
022     pod2usage("Unknown isp, use ",
023               join(", ", @isps));
024 }
025 
026 my $x10 = X10::Home->new();
027 
028 my $P = password_read("Router password: ");
029 
030 no strict 'refs';
031 eval { $switch_to->(); };
032 network_restart();
033 
034 ###########################################
035 sub dsl {
036 ###########################################
037   gateway_patch("192.168.10.1");
038   $x10->send("bridge", "off");
039   dhcp("on");
040 }
041 
042 ###########################################
043 sub cable {
044 ###########################################
045   gateway_patch("192.168.10.98");
046   $x10->send("bridge", "on");
047   dhcp("off");
048   ALWAYS "Waiting for bridge to start up";
049   sleep 20;
050 }
051 
052 ###########################################
053 sub network_restart {
054 ###########################################
055   tap "sudo", "/etc/rc.d/init.d/network",
056       "restart";
057 }
058 
059 ###########################################
060 sub gateway_patch {
061 ###########################################
062     my($ip) = @_;
063 
064     my $patcher = Config::Patch->new(
065         file => "/etc/sysconfig/network",
066         key  => "isp-switch",
067     );
068 
069     if($patcher->patched()) {
070         # patched already? Remove old patch
071         $patcher->remove();
072     }
073 
074     $patcher->replace(qr(^GATEWAY=.*)m,
075                       "GATEWAY=$ip");
076 }
077 
078 ###########################################
079 sub dhcp {
080 ###########################################
081     my($onoff) = @_;
082 
083     DEBUG "Setting dhcp to $onoff";
084 
085     my $b = Buffalo::G54->new();
086     DEBUG "Connecting";
087     $b->connect(password => $P);
088 
089     if(defined $onoff) {
090         INFO "Setting DHCP to $onoff";
091         $b->dhcp($onoff);
092     }
093 
094     INFO "DHCP is now ", $b->dhcp() ?
095                          "on" : "off";
096 }
097 
098 __END__
099 
100 =head1 NAME
101 
102     isp-switch - Cable or DSL?
103 
104 =head1 SYNOPSIS
105 
106     isp-switch [dsl|cable]
107 
108 =head1 DESCRIPTION
109 
110 isp-switch switches between Comcast cable
111 and Pacbell DSL.
112 
113 =head1 AUTHOR
114 
115 2007, Mike Schilli <cpan@perlmeister.com>

Der Autor


Michael Schilli arbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat “Goto Perl 5” (deutsch) und “Perl Power” (englisch) für Addison-Wesley geschrieben und ist unter [mschilli@perlmeister.com] zu erreichen.

Infos

[1] Listings zum Artikel: [ftp://www.linux-magazin.de/pub/listings/magazin/2007/11/Perl]

[2] Web Hog: [http://www.youtube.com/watch?v=ubc7zFSyEbg]

[3] M. Schilli, “Heimschaltwarte”: Linux-Magazin 04/07, [https://www.linux-magazin.de/heft_abo/ausgaben/2007/04/heimschaltwarte]

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben