Seiten

Sonntag, 20. Juli 2014

Kabelanschluss: Reconnect mit FritzBox hinter Kabelmodem

Da ich nun stolzer Besitzer eines Kabelanschlusses bin und auch wie vorher bei DSL einen Reconnect durchführen wollte, stellte ich fest das das nicht ganz so trivial wie vorher ist. Nach ein bisschen Recherche hatte ich zumindest die grobe Funktionsweise raus:
  1. Mac-Adresse des Routers ändern
  2. Kabelmodem neustarten
Klingt eigentlich ganz einfach, dachte ich... Mein Kabelmodem ist ein Scientific Atlanta (Cisco) EPC2203, welches vor einer FritzBox 7570 steckt. Nach mehreren erfolglosen Google-Anfragen konnte ich keine passende Lösung für mein Problem finden, also beschloss ich mir selbst etwas zu schreiben.
Meine Voraussetzungen sind eigentlich ganz einfach:
  • Skript zur Einbindung in unterschiedlichen Programmen, also kein Kompilat
  • Skript muss größtenteils mit Standard-Linux-Befehlen funktionieren
  • Keine modifizierte oder erweiterte Firmware der FritzBox
So weit so gut, der Befehl für das Neustarten des Modems war schnell im Netz gefunden:

curl http://192.168.100.1/goform/gscan -d SADownStartingFrequency=687000000

Funktionierte auf Anhieb, jedoch hat man da noch keine neu IP, es fehlt ja noch das Ändern der Mac-Adresse. Meine FritzBox bietet über die Oberfläche eine Möglichkeit die MAC zu ändern, welche komischerweise nicht funktioniert. Zumindest bleibt die MAC nach dem Übernehmen nicht gespeichert. Egal, da die Box ja auch einen Zugang über telnet anbietet mit dem man dies auch bewerkstelligen kann. Die dafür verantwortliche Datei nennt sich Ar7.cfg und muss bearbeitet werden. Diese Datei befindet sich unter /var/flash auf der Box und es müssen zwei Sachen bearbeitet werden:

  • enable_mac_override muss auf yes gesetzt werden, damit die Adresse überschrieben werden kann
  • macdsl_override muss auf die neue MAC gesetzt werden

telnet fritz.box

Da die ar7.cfg nicht direkt bearbeitet werden kann muss diese erst in das tmp-Verzeichnis der Box kopiert werden und wird dann später wieder zurückkopiert. Nach zahllosen Versuchen mit den begrenzten Befehlssatz der Box um meine Anforderungen zu erfüllen, kam ich dann letztendlich zu dieser Lösung:

cat /var/flash/ar7.cfg > /var/tmp/ar7.cfg
sed -i '/enable_mac_override/cenable_mac_override = yes;' /var/tmp/ar7.cfg
sed -i "s/\(macdsl_override = .*:\)\(.*\);$/\1$(cat /dev/urandom | tr -cd 'A-F  0-9' | head -c 2);/" /var/tmp/ar7.cfg
cat /var/tmp/ar7.cfg > /var/flash/ar7.cfg
exec /etc/init.d/rc.net reload

Zur zeilenweisen Erklärung :
Zeile 1:
Das vorher angesprochene Kopieren der Datei in den temporären Ordner
Zeile 2:
Es wird die Zeile mit enable_mac_override gesucht und - unabhängig von deren Inhalt wird diese mit enable_mac_override = yes; überschrieben.
Zeile 3 (die hat mit Abstand am längsten gedauert ;) ):
Es wird nach macdsl_override gesucht und der letzte Teil der MAC-Adresse ausgelesen. Dieser wird mit einer hexadezimalen Zufallszahl aus /dev/urandom ersetzt. Danch wird die ganze Zeile in die ar7.cfg zurückgeschrieben.
Zeile 4:
Die ar7.cfg wird wieder an ihren Ursprungsort kopiert
Zeile 5:
Damit die Einstellungen übernommen werden, wird nun noch die Netzwerkkonfiguration der FritzBox neu eingelesen.

Nachdem das nun funktioniert hat bleibt noch die Herausforderung das ganze per Skript auszuführen. Leider kommt man hier mit bash nicht wirklich weit und muss auf expect ausweichen. In den meisten Distributionen ist dies schon vorinstalliert. Hiermit ist es möglich interaktive Terminalsitzungen - wie bspw. telnet -  auszulagern, Befehle abzusetzen und bestimmte Ausgaben zu überprüfen.
Das resultierende Skript sieht dann so aus:

#!/usr/bin/expect

spawn telnet fritz.box
expect "password: "
send "MySecretPasswordReplaceWithYourOwn\n"
expect "#"
send "cat /var/flash/ar7.cfg > /var/tmp/ar7.cfg\n"
expect "#"
send "cat /var/tmp/ar7.cfg | grep overr\n"
expect "#"
send "sed -i '/enable_mac_override/c\enable_mac_override = yes;' /var/tmp/ar7.cfg\n"
expect "#"
send "sed -i \"s/\\(macdsl_override = .*:\\)\\(.*\\);$/\\1\$(cat /dev/urandom | tr -cd 'A-F0-9' | head -c 2);/\" /var/tmp/ar7.cfg\n"
expect "#"
send "cat /var/tmp/ar7.cfg > /var/flash/ar7.cfg\n"
expect "#"
send "cat /var/flash/ar7.cfg | grep overr\n"
expect "#"
send "exec /etc/init.d/rc.net reload\n"
expect "#"
send "exit"
exit

Am Anfang wird die telnet-Verbindung herausgelöst (gespawned) und gewartet bis die Box eine Zeile mit dem Inhalt password: zurückgibt. Danach wird mein Passwort hingeschickt, da meine Box abgesichert ist. Es ist immer empfehlenswert die Oberfläche des Routers abzusichern, das gleiche Passwort wird auch bei Telnet benutzt. Wichtig ist nur das jede Send-Zeile mit \n beendet werden muss, dies bestätigt den Befehl und darf nicht gelöscht werden wenn ihr euer Passwort ersetzt.
Im Anschluss folgen die Befehle, die ich vorher schon einmal beschrieben habe. Diese sehen leicht verändert aus, da man im Skript bestimmte Zeichen maskieren muss. Die zwei zusätzlichen Zeilen, die grep beinhalten verändern nichts an der Funktion und geben lediglich die Vorher- und Nachherwerte der beiden geänderten Parameter aus. Diese Ausgabe kann bspw. für ein Logfile verwendet werden.

Das Schlimmste ist geschafft, nun muss nur noch das Skript zum Ändern der MAC und das zum Neustarten des Modems zusammengführt werden. Ich habe das expect-Skript als fritz.sh im selben Ordner abgelegt und rufe dieses über ein anderes Skript namens reconnect.sh auf:

#!/bin/bash

DIR=$(cd $(dirname "$0"); pwd)
LOG=$DIR/reconnect.log
MACSCRIPT=$DIR/fritz.sh
PASSWORD=MySecretPasswordReplaceWithYourOwn

getIP() {
 curl http://ipecho.net/plain; echo
}


changeMAC() {
 $MACSCRIPT
}

restartModem() {
  curl http://192.168.100.1/goform/gscan -d SADownStartingFrequency=687000000
}

waitUntilOnline() {
while true
 do
  ping -c 1 ipecho.net >> $LOG
  if [[ $? == 0 ]]; then
   echo "Connection established. Waiting five more seconds." >> $LOG
   sleep 5
   break;
  else
   echo "Waiting..." >> $LOG
   sleep 5
  fi
 done
}


echo Old IP = $(getIP) > $LOG
echo Restarting Modem++++++++++++++++ >> $LOG
restartModem >> $LOG
echo Changing FritzBox Mac ++++++++++++++++ >> $LOG
changeMAC >> $LOG
waitUntilOnline
echo New IP = $(getIP) >> $LOG

Im Grunde ist das ganze ganz einfach. Zuerst wird die aktuelle IP ausgelesen und in eine Logdatei geschrieben. Anschließend wird das Modem neugestartet und die MAC der FritzBox geändert. waitUntilOnline prüft alle 5 Sekunden, ob die Internetverbindung schon wieder da ist um dann zum Schluss die neue IP ins Logfile zu schreiben.

Das war schon Alles ;) Das Skript kann man bestimmt noch etwas aufhübschen und etwas fehlertoleranter gestalten, es tut aber was es soll. Der Reconnect dauert zwischen 3 und 5 Minuten, da das Modem relativ träge ist bis es neu gestartet ist.

Falls ihr das Skript nachvollziehen wollt, muss ich vorher noch eine Warnung loswerden:
Die Skripte können im schlimmsten Fall die Box lahmlegen und es sollte vorher recherchiert werden, ob dies bei eurer FB genauso funktioniert. Ich kann und werde keine Garantie für kaputte Geräte übernehmen.

Ich konnte das Skript mit mehreren FritzBox-Modellen ausprobieren und es hat immer gut funktioniert. Im Zweifelsfall solltet ihr aber die einzelnen Teile der Telnet-Session einzeln ausprobieren, deshalb habe ich diese separat geschrieben. Ich konnte das Skript auch - mit leichten Anpassungen - unter Windows mit Hilfe von Cygwin laufen lassen. Folgendes musste ich dafür tun:

  • expect und curl nachinstallieren (waren in der Standardinstallation nicht vorhanden)
  • Der Ordner der Skripte durfte kein Leerzeichen beinhalten
  • Alle Logausgaben entfernt (> $LOG), habe nicht näher recherchiert warum das nicht funktioniert
  • Im expect-Skript die Zeile spawn telnet fritz.box durch spawn /usr/bin/telnet fritz.box ersetzt, da sonst das Win-Standard-Telnet benutzt wird.

Punkt 2 und 3 ist bestimmt auch anders lösbar, vielleicht will ja ein Windows-Nutzer seine Lösung posten.

Was ihr natürlich noch ändern müsst ist der Mechanismus zum Neustarten des Modems per URL, falls ihr ein anderes Modem habt. Dies sind aber im Netz relativ leicht zu finden.

Viel Spaß mit den beiden Skripten, ich freue mich auf eure Kommentare.

Keine Kommentare:

Kommentar veröffentlichen