Fritz!Box Monitoring

Eine ganze Zeit lang hat meine Lieblings-Monitoring-Lösung PRTG einen Sensor für meine Fritz!Box enthalten. Leider wurde der irgendwann mal abgekündigt:

Die Entscheidung kann ich natürlich nachvollziehen. eine Fritz!Box ist in der Regel ja nicht gewerblich im Einsatz und die Entwicklung, Pflege und sicher auch die ein oder anderen Support-Anfragen von Privatanwendern, die mit der 100 Sensor-Free-Version arbeiten, kosten natürlich erst einmal Zeit und Geld. Aber durch die offenen APIS von PRTG können Sie natürlich ihren eigenen Sensor schreiben oder einen vorgefertigten Sensor nutzen. Davon gibt es einige, die auch oft heruntergeladen werden.


Quelle http://prtgtoolsfamily.com/downloads/sensorsxml

Diese Sensoren nutzen allerdinge nicht die UPnP-API sondern melden sich an der Webseite an und lesen die Daten aus den HTML-Sensoren aus. Der hier beschriebene Weg als auch mein erweiterter Sensor für die DSL-Qualität (PRTG Fritz!Box-DSL nutzen. Dieses nutzt UPNP.

Mit dem Update auf FritzOS 7 hat wohl AVM am TR64/UPNP Zugang etwas geändert. auf jeden Fall funktioniert mein PRTG Sensor PRTG Fritz!Box nicht mehr. Am Ende war es wohl nur ein kleiner String, der sich geändert hat:

# Alt
#$soapWebRequest.Headers.Add("SOAPAction",'"urn:dslforum-org:service:WANCommonInterfaceConfig:1#GetTotalBytesReceived"')
# Ne$soapWebRequest.Headers.Add("SOAPAction",'"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1#GetAdd-onInfos"')

Hier protokolliere ich dennoch meine Schritte zur Wiedererlangung des Zugangs.

AVM TR-064 – First Steps
https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AVM_TR-064_first_steps.pdf

SSDP Suche

Natürlich wissen sie in der Regel die IP-Adresse ihrer Fritz-Box und können diese später direkt angeben. Aber die "Fritz!Box" verrät sich natürlich auch über einen SSDP Multicast

In dem Protokoll ist auch eine URL enthalten:

Abruf /l2tpv3.xml oder /fboxdesc.xml

Bei der Fritz!Box ist das hier http://192.168.178.1:49000/l2tpv3.xml, die Sie einfach im Browser eingeben können. Zurück kommt eine XML-Struktur mit weiteren Informationen über das Gerät:

Wenn Sie statt 49000 den Port 49443 per https ansprechen, bekommen Sie die gleichen Daten verschlüsselt geliefert. Meine Fritz!Box nutzt dazu ein "SelfSigned"-Zertifikat, was natürlich nicht als vertrauenswürdige eingestuft ist. Den Abruf können Sie auch per PowerShell durchführen:

PS C:\> $l2tpv3= Invoke-RestMethod http://192.168.178.1:49000/l2tpv3.xml
PS C:\> $l2tpv3.root.device

deviceType       : urn:schemas-upnp-org:device:l2tpv3:1
friendlyName     : FRITZ!Box 7490
manufacturer     : AVM Berlin
manufacturerURL  : http://www.avm.de
modelDescription : FRITZ!Box 7490
modelName        : FRITZ!Box 7490
modelNumber      : avm
modelURL         : http://www.avm.de
UDN              : uuid:95802409-bccb-40e7-8e6c-CCCE1E343D04
iconList         : iconList
serviceList      : serviceList
presentationURL  : http://fritz.box

PS C:\> $a.root.device.serviceList.service

serviceType : urn:schemas-any-com:service:l2tpv3:1
serviceId : urn:any-com:serviceId:l2tpv31
controlURL : /upnp/control/l2tpv3
eventSubURL : /upnp/control/l2tpv3
SCPDURL : /l2tpv3SCPD.xml

Unter der URL http://192.168.178.1:49000/upnp/control/l2tpv3 als auch http://192.168.178.1:49000/upnp/control/l2tpv3/l2tpv3SCPD.xml kommt aber nur ein "404 not found" zurück.

Beim Mitschneiden in meinem LAN ist mir noch eine andere URL aufgefallen. Unter http://192.168.178.1:49000/fboxdesc.xml und http://192.168.178.1:49000/avmnexusdesc.xml liefert die Fritz!Box auch Informationen.

TR64-Status

Die Fritz!Box spricht zusätzlich aber auch TR64. AVM ist hier bezüglich der Dokumentationen vorbildlich. Die URLs sind sehr einfach zu finden

Aus der "First Started"-Dokumentationen finden Sie die beiden folgenden URLs, die einfach per HTTP-GET mit einem Browser abgerufen werden können. Die komplette Ausgabe des Browsers erspare ich ihnen, da die XML-Struktur schon deutlich länger ist. Daher ist ein Blick in die Doku interessant, um den richtigen Einstiegspunkt zu finden. In https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AVM_TR-064_first_steps.pdf findet sich dazu:

Hier kann ich zumindest wieder die Datenmenge in Bytes und Paketen ermitteln.

Basistest any.xml und tr64desc.xml

Ehe Ich nun mit POST und SOAP die vermeintliche Box abfragen, bietet sich oftmals ein kleiner Check an, ob der WebServer wirklich XML liefert. Dazu eignet sich die "any.xml".

PS C:\> $b=Invoke-WebRequest http://192.168.178.1:49000/any.xml
PS C:\> $b.Content
<?xml version="1.0"?>
   <scpd xmlns="urn:schemas-upnp-org:service-1-0">
      <specVersion>
         <major>1</major>
         <minor>0</minor>
      </specVersion>
      <serviceStateTable>
         <stateVariable sendEvents="no">
            <name>Dummy</name>
            <dataType>string</dataType>
         </stateVariable>
      </serviceStateTable>
   <actionList />
</scpd>

Wenn hier also eine korrekte XML-Rückgabe kommt, dann scheint nichts die weiteren Schritte zu stören. Ein zweiter Test kann einfach die Abfrage der Dienste sein

$tr64desc=Invoke-WebRequest "http://fritz.box:49000/tr64desc.xml"
([xml]$tr64desc.Content).root.device.servicelist.service

Die URL "http://fritz.box:49000/tr64desc.xml" können Sie auch einfach in einem Browser starten

UPnP und SOAP

Die Daten selbst bekommen wir leider nicht mit einem einfachen HTTP-GET, sondern müssen einen SOAP-Request erstellen und per HTTP-POST senden. Zwar liefert die Abfrage der Datei "l2tpv3.xml" schöne URLs in der XML-Rückgabe, aber die sind nicht einfach per "GET" abzurufen.

<service>
  <serviceId>urn:upnp-org:serviceId:wanpppc:pppoa</serviceId>
  <controlURL>/upnp/control/wanpppcpppoa</controlURL>
  <eventSubURL>/upnp/event/wanpppcpppoa</eventSubURL>
  <SCPDURL>/WANPPPConnection.xml</SCPDURL>
</service>

Man muss diese innerhalb eines SOAP-Request als URL hinterlegen. das ganze als "Trivialversion könnte dann so aussehen:

param (
	[String] $URL = "http://192.168.178.1:49000/igdupnp/control/WANCommonIFC1"
)

write-host "SOAP Request vorbereiten an Ziel: $URL"
$soapWebRequest = [System.Net.WebRequest]::Create($URL) 
$soapWebRequest.Headers.Add("SOAPAction",'"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1#GetAdd-onInfos"')
$soapWebRequest.ContentType = 'text/xml;charset="utf-8"'
$soapWebRequest.Accept      = "text/xml" 
$soapWebRequest.Method      = "POST" 

write-host "SOAP Request XML in den RequestStream schreiben"
$requestStream = $soapWebRequest.GetRequestStream() 
[string]$SOAPRequest='<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
  <u:GetAdd-onInfos xmlns:u=urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 />
</s:Body>
</s:Envelope>';

$SOAPRequestbytearray = ([system.Text.Encoding]::ASCII).GetBytes($SOAPRequest)
$requestStream.write($SOAPRequestbytearray,0,$SOAPRequestbytearray.count)
$requestStream.Close() 

write-host "Sende Request"
$resp = $soapWebRequest.GetResponse() 
write-host "Verarbeite Ergebnisse"
$responseStream = $resp.GetResponseStream() 
$soapReader = [System.IO.StreamReader]($responseStream) 
$ReturnXml = [Xml] $soapReader.ReadToEnd() 
$responseStream.Close() 

write-host "Ergebnisse"
$ReturnXml.Envelope.Body.GetAdd-onInfosResponse
write-host "Ende"

Auf dem Kabel sieht das dann wie folgt aus:

Die Daten können dann natürlich weiter verarbeitet werden.

Download

Das oben schon mit veröffentlichte Skript können Sie hier einfach per Download auf ihren PC übertragen und ausführen:

Bei meinen letzten Tests konnte das Skript keine Daten mehr abrufen. Anscheinend hat AVM die API leicht verändert.

fritzbox_monitoring-20181221.ps1.txt

Es verbindet sich mit der IP-Adresse 192.168.178.1, welche bei den allermeisten Heimnetzwerken auf die Fritz!Box verweisen wird.

UPnP Status freischalten

Alle Checks und Prüfungen über diesen "anonymen" Weg erfordern natürlich die Freischaltung der UPnP Funktion unter Heimnetz - Netzwerk - Netzwerkeinstellungen.

Ansonsten antwortet der Server mit einem 500er Server Error.

HTTP/1.1 500 Internal Server Error
DATE: Fri, 21 Dec 2018 17:15:36 GMT
SERVER: FRITZ!Box 7490 UPnP/1.0 AVM FRITZ!Box 7490 113.07.01
CONNECTION: keep-alive
CONTENT-LENGTH: 433
CONTENT-TYPE: text/xml; charset="utf-8"

<?xml version="1.0"?>
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <s:Body>
      <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring>UPnPError</faultstring>
      <detail>
        <UPnPError xmlns="urn:schemas-upnp-org:control-1-0">
          <errorCode>401</errorCode>
          <errorDescription>Invalid Action</errorDescription>
        </UPnPError>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

Wenn Sie diese Option nicht aktivieren, weil Sie die Status-Informationen eben nicht anonym im LAN bereitstellen wollen, dann bleibt nur die Abfrage über eine Authentifizierung und der WebUI.

SOAP mit SSL

Der folgende Code-Schnipsel wird von mir aktuell nicht mehr eingesetzt. Ich wollte ihn ihnen dennoch nicht vorenthalten, da ich hier über den "System.Net.WebClient" eine SOAP-Abfrage mit Authentifizierung und abgeschalteter Zertifikatprüfung ausführen.

$password="geheim"

$w=New-Object System.Net.WebClient
$w.Encoding=[System.Text.Encoding]::UTF8
$w.Headers.Set("Content-Type", 'text/xml; charset="utf-8"')
$w.Headers.Set("SOAPACTION", 'urn:dslforum-org:service:WANDSLInterfaceConfig:1#GetInfo')

$query='<?xml version="1.0"?>
        <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
        s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <s:Body> ' +
        '<u:GetInfo xmlns:u="urn:dslforum-org:service:WANDSLInterfaceConfig:1">
        </u:GetInfo>' +
        '</s:Body>
        </s:Envelope>'

$w.Credentials=New-Object System.Net.NetworkCredential("dslf-config", $password)
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$r = [xml]$w.UploadString("https://fritz.box:49443/upnp/control/wandslifconfig1",$query)
$r.Envelope.Body.GetInfoResponse 

u                        : urn:dslforum-org:service:WANDSLInterfaceConfig:1
NewEnable                : 1
NewStatus                : up
NewDataPath              : Fast
NewUpstreamCurrRate      : 1035
NewDownstreamCurrRate    : 11896
NewUpstreamMaxRate       : 1036
NewDownstreamMaxRate     : 11896
NewUpstreamNoiseMargin   : 60
NewDownstreamNoiseMargin : 50
NewUpstreamAttenuation   : 130
NewDownstreamAttenuation : 310
NewATURVendor            : 41564d00
NewATURCountry           : 0400
NewUpstreamPower         : 628
NewDownstreamPower       : 597

Sollten keine Daten kommen, dann könnte das Kennwort falsch oder TR-064 in der Fritz!Box deaktiviert worden sein.

Wenn man das Kennwort mit sendet, dann sollten Sie unbedingt auch HTTPS nutzen, damit das Kennwort nicht im Klartext übertragen wird. Damit HTTPS aber funktioniert, muss die Fritz!Box ein Zertifikat anbieten. Meine Box nutzt dazu anscheinend ein "selbstsigniertes" Zertifikat, welches den MyFritz-Namen enthält.

Dieses Zertifikat wird auch für Zugriffe von Extern über MyFritz" genutzt aber eben auch intern bei UPnP über Port 49443.

Monitoring per WebUI

Nicht alle Werte sind per SOA zu erreichen. Sie können natürlich sich einfach auf der Webseite anmelden und die Werte von dort auslesen. Das war schon zu BTC-Zeiten (um 1990) ein gängiges Verfahren für Home-Banking. Dieser Weg wird z.B. von den PRTG-Sensoren auf http://prtgtoolsfamily.com/downloads/sensorsxml genutzt. Allerdings ist die Anmeldung dabei etwas Tricky.

Die Fritz!Box beschränkt die Anzahl der parallelen Sessions und vergibt dazu eine SessionID, die sie mit jedem weiteren Request auch immer wieder verwenden müssen. Eine SessionID verliert nach 60 Min die Gültigkeit.

Entsprechend brauchen wir ein paar mehr Anfragen. Ich habe für das Verständnis nicht erforderliche Zeilen entfernt.

Anforderung einer SessionID

GET /login_sid.lua HTTP/1.1
Host: 192.168.178.1

Antwort mit der ID

HTTP/1.1 200 OK
Content-Type: text/xml
Keep-Alive: timeout=60, max=300

<?xml version="1.0" encoding="utf-8"?>
  <SessionInfo>
    <SID>0000000000000000</SID>
    <Challenge>ee576409</Challenge>
    <BlockTime>0</BlockTime>
    <Rights></Rights>
</SessionInfo>

Anmeldung mit User Response

Beachten Sie, dass der "Challenge" der vorherigen Antwort hier im Response mit addiert werden muss.

GET /login_sid.lua?username=admin&response=ee576409-md5hash-des-kennwort HTTP/1.1
Host: 192.168.178.1

Antwort mit Session SID

In der Antwort ist dann die gültige SID.

HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: text/xml

<?xml version="1.0" encoding="utf-8"?>
<SessionInfo>
  <SID>26936d59c445341a</SID>
  <Challenge>009cbc15</Challenge>
  <BlockTime>0</BlockTime>
  <Rights>
    <Name>Dial</Name><Access>2</Access>
    <Name>App</Name><Access>2</Access>
    <Name>HomeAuto</Name><Access>2</Access>
    <Name>BoxAdmin</Name><Access>2</Access>
    <Name>Phone</Name><Access>2</Access>
    <Name>NAS</Name><Access>2</Access>
  </Rights>
</SessionInfo>

Abruf von inetstat_counter.lua

Nun wird einfach die "Webseite" geladen.

GET /internet/inetstat_counter.lua?sid=26936d59c445341a HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)
Host: 192.168.178.1

Die HTML-Seite muss dann natürlich geparsed werden. Solange AVM mit einem Firmware-update die Anzeige nicht verändert, funktioniert das.

HTTP/1.1 200 OK

<script>...</script>
<style type="text/css">... </style>

...<table id="tStat"><tr class="first_row">
<th class="first_col"></th>
<th>Online-Zeit</th>
<th style="text-align:center;" colspan="3">Datenvolumen (MB)</th>
<th>Verbindungen</th>
</tr>
<tr class="first_row">
<th class="first_col"></th>
<th>(hh:mm)</th>
<th>gesamt</th>
<th>gesendet</th>
<th>empfangen</th>
<th></th>
</tr>
<tr><td datalabel="" class="first_col">Heute</td>
   <td datalabel="Online-Zeit (hh:mm)" class="time">11:09</td>
   <td datalabel="Datenvolumen gesamt(MB)" class="vol">6795</td>
   <td datalabel="Datenvolumen gesendet(MB)" class="vol">2504</td>
   <td datalabel="Datenvolumen empfangen(MB)" class="vol">4291</td>
   <td datalabel="Verbindungen" class="conn">2</td></tr>
<tr>
   <td datalabel="" class="first_col">Gestern</td>
   <td datalabel="Online-Zeit (hh:mm)" class="time">24:00</td>
   <td datalabel="Datenvolumen gesamt(MB)" class="vol">17708</td>
   <td datalabel="Datenvolumen gesendet(MB)" class="vol">5675</td>
   <td datalabel="Datenvolumen empfangen(MB)" class="vol">12033</td>
   <td datalabel="Verbindungen" class="conn">1</td></tr>
<tr>
   <td datalabel="" class="first_col">Aktuelle Woche</td>
   <td datalabel="Online-Zeit (hh:mm)" class="time">107:08</td>
   <td datalabel="Datenvolumen gesamt(MB)" class="vol">86060</td>
   <td datalabel="Datenvolumen gesendet(MB)" class="vol">25798</td>
   <td datalabel="Datenvolumen empfangen(MB)" class="vol">60262</td>
   <td datalabel="Verbindungen" class="conn">6</td></tr>
<tr>
   <td datalabel="" class="first_col">Aktueller Monat</td>
   <td datalabel="Online-Zeit (hh:mm)" class="time">489:03</td>
   <td datalabel="Datenvolumen gesamt(MB)" class="vol">525929</td>
   <td datalabel="Datenvolumen gesendet(MB)" class="vol">226967</td>
   <td datalabel="Datenvolumen empfangen(MB)" class="vol">298963</td>
   <td datalabel="Verbindungen" class="conn">24</td></tr>
<tr>
   <td datalabel="" class="first_col">Vormonat</td>
   <td datalabel="Online-Zeit (hh:mm)" class="time">719:56</td>
   <td datalabel="Datenvolumen gesamt(MB)" class="vol">728810</td>
   <td datalabel="Datenvolumen gesendet(MB)" class="vol">246282</td>
   <td datalabel="Datenvolumen empfangen(MB)" class="vol">482528</td>
   <td datalabel="Verbindungen" class="conn">30</td></tr></table>
</div>
<div>

...

Irgendwo in der HTML-Seite gibt es dann die "Datatable" mit den entsprechenden Werten, die ausgelesen und z.B. an PRTG übergeben werden können.

Abfrage per Telnet

Nicht weiter verfolgt habe ich einen Weg, den z.B. FHEM geht. Anscheinend lesen Sie direkt auf der Fritz!Box die Werte aus oder verbinden sich per Telnet. Man braucht auf jeden Fall "Root"-Rechte dazu.

Plausibilität der Werte

Die Fritz!Box sammelt sehr viele Werte. Allerdings müssen diese auch immer interpretiert werden. Speziell wenn Sie z.B. die Bytes auf dem DSL zählen und auch die Bytes auf dem Ethernet über einen Switch ermitteln. Die Werte passen nie genau zueinander und das hat verschiedene Gründe:

  • Bytes/sek  und Bit/Sek
    Einige Counter liefern "bit/Sek" und natürlich rechnet man 8bit=1Byte um. Aber schon die Festplattenhersteller interpretieren das "k", "M"m "G" und "T" anders uns schreiben, dass 1 TB natürlich 1000GB = 1.000.000MB sind. So wird aus einer 1TB Platte dann keine 1048576MB Disk sondern hat laut Windows dann eben nur 0,95TB.
  • Ethernet-Frame vs. DSL-PPPoE
    Die meisten Anwender sehen erst mal nur die "Nutzlast". Die Umverpackung in form von TCP-Frame, IP-Frame, Ethernet-Frame beim LAN kann sich aber von einer DSL-Übertragung per PPPoE unterscheiden. So kommen weitere Unterschiede dazu
  • Ethernet Mindestgröße
    Auf einer seriellen 1:1 Leitung gibt es keine Kollisionen. Ethernet hingegen basiert auf CSMA/CD und da muss ein Paket eine Mindestgröße haben, damit auf eine Bus auch jeder Client eine Kollision erkennen kann. Auch wenn das in einem voll geswitchten Netzwerk eigentlich nicht mehr passiert, werden auch heute noch Füllbits angehängt.
    Siehe auch http://www.netzmafia.de/skripten/netze/netz4.html. Das sind auf dem LAN also wieder weitere Bytes, die es auf dem DSL wohl so nicht gibt
  • Monitoring gegen die Fritz!Box und NAS
    Vergessen Sie auch nicht, dass ihre Zugriffe auf die Fritz!Box direkt, also z.B. per Webseite aber auch Monitoring-Abfragen und die Nutzung der Fritz!Box-NAS-Funktion und SIP-Telefonie im LAN natürlich Pakete auf dem Ethernet erzeugen, die auf dem DSL nicht sichtbar sind

Hier sind also einige Abweichungen erklärbar. Wenn ihr DSL-Anschluss nach Volumen abgerechnet wird, dann sind wohl die Zähler der DSL-Leitung am ehesten dafür geeignet. Maßgeblich sind aber auch hier leider immer die Daten des Anbieters.

Weitere Link