Mittwoch, 27. Juni 2018

OpenWrt Router abfragen

Dieser Beitrag im Forum gab den Ansatz für eine Abfrage des OpenWrt Routers nach angemeldeten Wlan Clients. Für mich als Halbwissender war es mal wieder gar nicht einfach den kurzen Artikel umzusetzen. Deswegen hier etwas ausführlicher.

Vorbereitung des Routers

Für das cgi Script braucht man die Komponente bash auf dem Router, das ist schnell installiert.
System / Software im Feld "Download and install package:" einfach bash eintippen und Ok drücken.
Im Pfad /www/cgi-bin/ muss man jetzt eine Datei wifi-client mit folgendem Inhalt anlegen (z.B. mit WinsSCP).
Ich habe gegenüber dem Original Artikel noch die Umwandlung von Gross in Kleinbuchstaben eingebaut.
Die Zeile im Code unterhalb dem Hinweis  # Pfade anpassen je nach AP-Config!! muss man unbedingt anpassen!
Je nach dem welche Wlan Hardware verbaut ist:
  • radio0 -> phy0 
  • radio1 -> phy1
Und welche Wlans definiert sind (Beispiel):
  • wlan1 -> netdev:wlan1
  • wlan1-2 ->  netdev:wlan1-2
Die Benennung war hier nicht ganz konsequent ableitbar, also unbedingt im Pfad /sys/kernel/debug/ieee80211 nachschauen! Oder mit dem Befehl iwinfo die Liste der Wlan Namen ausgeben.
#!/bin/bash

# Decodes an URL-string
# an URL encoding has "+" instead of spaces
# and no special characters but "%HEX"
function urlDec() {
  local value=${*//+/%20}                   # replace +-spaces by %20 (hex)
  for part in ${value//%/ \\x}; do          # split at % prepend \x for printf
    printf "%b%s" "${part:0:4}" "${part:4}" # output decoded char
  done
}

# For all given query strings
# parse them an set shell variables
function setQueryVars() {
  local vars=${*//\*/%2A}                      # escape * as %2A
  for var in ${vars//&/ }; do                  # split at &
    local value=$(urlDec "${var#*=}")          # decode value after =
    value=${value//\\/\\\\}                    # change \ to \\ for later
    value=${value,,}                           # upper in lower case 
    eval "CGI_${var%=*}=\"${value//\"/\\\"}\"" # evaluate assignment
  done
}

# Execute the evaluation
# set all variables for both, POST and GET data
# $QUERY_STRING ist part of the environment and contain the part after ?
setQueryVars $QUERY_STRING $(</dev/stdin)

# Set formatting the Response to plain Text
echo Content-type: text/plain
echo
# for the Query ?dev=xxxxxx&test=yyyyyy
# Query dev= -> $CGI_dev contain value after dev=
# Query test= -> $CGI_test contain value after test= 
# Test the string contain only numbers,lower letters and :
case $CGI_dev in
        *[!0-9a-f:]*|"") echo "NOK MAC"; exit 0;;
esac

# Pfade anpassen je nach AP-Config!!
if [ -d "/sys/kernel/debug/ieee80211/phy1/netdev:wlan1/stations/$CGI_dev" ] || [ -d "/sys/kernel/debug/ieee80211/phy1/netdev:wlan1-1/stations/$CGI_dev" ] || [ -d "/sys/kernel/debug/ieee80211/phy1/netdev:wlan1-2/stations/$CGI_dev" ] || [ -d "/sys/kernel/debug/ieee80211/phy0/netdev:wlan0/stations/$CGI_dev" ]; then
       echo "connected"
else
       echo "no device"
fi
Diese Datei braucht jetzt nur noch das Ausführen (x) Recht für Alle, kann man auch gleich mit WinSCP machen.

Ohne Weiteres kann man sofort die Funktion testen:
http://wrt1900/cgi-bin/wifi-client?dev=11:22:33:aa:bb:cc
Bei bekannter MAC Adresse erscheint ein connected als Quittung, ansonsten eine entsprechende Ausgabe.

Funktion in 99_myUtils.pm

Um diese Abfrage in einem PRESENCE Device zu verwenden, braucht man noch eine kleine Perlfunktion.
sub wlan_det($) {
use LWP::UserAgent;
my ($client)= @_;
        my $ua = new LWP::UserAgent;
        my $response = $ua->get('http://wrt1900/cgi-bin/wifi-client?dev='.$client);

        unless ($response->is_success) {
                              return 99;
        }

        my $content = $response->decoded_content();

        if ($content =~ m/connected/) {
                              return 1;
        } else {
                              return 0;
        }
}
Ich musste gegenüber dem Originalbeitrag noch die Zeile mit "use LWP:.." ergänzen. Es ist eigentlich besser diese einmalig am Anfang der 99_myUtils.pm nach der Zeile
"# Enter you functions below _this_ line."
und vor allen eigenen Subs zu platzieren.
Die Definition in FHEM ist wie gewohnt:
defmod iPhone PRESENCE function {wlan_det("11:22:33:aa:bb:cc")}

Nacharbeit

Das war jetzt quick&dirty -  man kann noch
  • die Übergabe oder Definition der Router IP besser machen.
  • die Definition der abzufragenden Pfade im Router verbessern.
  • Prüfen was genau passiert wenn der Router nicht erreichbar ist (FHEM blockiert?)
  • Die Abfrage funktioniert so nur für Wifi, LAN Clients ermitteln? 

3 Kommentare:

  1. Hallo, danke für die Zusammenstellung.
    Frage: Ist tatsächlich 'defmode' gemeint ?

    AntwortenLöschen
  2. Das Programm
    http://wrt1900/cgi-bin/wifi-client?dev=11:22:33:aa:bb:cc
    ausgeführt in der Browserzeiler funktioniert super.
    Wird das Programm auf dem Router aber über die Perl-Funktion in der 99_myUtils aufgerufen liefert die Funktion wlan_det() immer 99.

    AntwortenLöschen
  3. Möglich Ursache für Ergebnis 99, ist das ein self signed Zertifikaten verwendet.
    Einfach

    my $ua = new LWP::UserAgent;

    durch ersetzten

    my $ua = LWP::UserAgent->new(
    ssl_opts => {
    SSL_verify_mode => SSL_VERIFY_NONE(), # ausschalten
    verify_hostname => 0, # und auch auslassen!
    }
    );

    AntwortenLöschen