Anmeldebildschirm ('splash screen') nachrüsten

Aus Freifunk Rheinland e.V.
Zur Navigation springen Zur Suche springen

Motivation

Die sog. "alte Firmware" (letzter Release 1.9.2013) brachte standardmäßig einen Anmeldebildschirm ('splash screen') mit. Die sog. "neue Firmware" hatte dann keinen mehr, was zu heftigen Diskussionen innerhalb des Vereins geführt hatte. Die Argumente der Befürworter des Anmeldebildschirms waren für mich nachvollziehbar, die der Gegner wirkten auf mich ein wenig vorgeschoben. Für einen Verein sollte es wichtig sein, alle Mitglieder mitzunehmen. Letztmalig habe ich diese Diskussion auf dem Freifunktag im März 2014 miterlebt, was mich damals zu der Aussage brachte: "Wenn die Entwickler das nicht machen wollen, dann werde ich eine Lösung zum Nachrüsten finden." Nun, der Freifunktag ist jetzt schon etwas länger her und ich habe vom Anmeldebildschirm nichts mehr gehört. Ich weiß nicht, ob irgendwo noch andere Freifunker an der Wiedereinführung des Anmeldebildschirms arbeiten. Wenn ja, dann wäre es schön einmal zu sehen, wie Ihr es gemacht habt.

(uwho)

Update: Ich habe gerade gemerkt, daß ich vergessen habe die Änderungen an der /etc/firewall.ffrl und der /etc/firewall.user zu beschreiben. Ohne die funktioniert diese Anleitung nicht! Ich hole das in Kürze nach.

Und hier meine Lösung:

Basis war die "neue Firmware" für Rheinufer (Release 25.2.2014). Der Routertyp spielt keine Rolle. Anfänglich war es mein Ziel, das alte Anmeldeverfahren bloß wieder gängig zu machen. Leider habe ich es nicht geschafft. Das liegt vor allem daran, daß die "neue Firmware" überwiegend auf ebtables und nicht auf iptables setzt und im Layer 3 einiges anders läuft. Kurz gesagt, ich habe das Anmeldeverfahren jetzt auch mit ebtables realisiert und so läuft's.

Fühlt Euch frei hier Verbesserungen einzubringen!

Jetzt geht es los!

1. Wir legen von der Komandozeile des Routers aus die Datei /etc/mksplash neu an

vi /etc/mksplash

und kopieren als Dateiinhalt die folgenden Zeilen hinein.

ebtables -t broute -F BROUTING
ebtables -t broute -A BROUTING --mark ! 1/1 -p IPV4 --ip-protocol 6 --ip-destination-port 80 -j redirect --redirect-target ACCEPT

Speichern!

Danach wird die Datei noch mit

chmod 777 /etc/mksplash

ausführbar gemacht.

2. Anschließend tragen wir unsere neu erstellte Datei in /etc/rc.local ein, damit sie bei jedem Systemstart ausgeführt wird.
Dazu öffnen wir die Datei mit

vi /etc/rc.local

und schreiben den Dateinamen /etc/mksplash hinein.

# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.

/etc/mksplash
exit 0

Speichern und fertig.

3. Wir öffnen die Datei /etc/crontabs/root mit

vi /etc/crontabs/root

und tragen dort als letzte Zeile 0 3 * * * /etc/mksplash ein. Speichern! Bei mir sieht die Datei danach so aus:

*/3 * * * * /usr/sbin/fastd_watchdog # fastd watchdog
0 * * * *  /usr/sbin/owm-update update_map # owm update
*/1 * * * * /usr/sbin/splash_sync queen # splash sync
0 3 * * * /etc/mksplash 

Anschließend sorgen wir noch dafür, daß cron auch wirklich läuft.

/etc/init.d/cron enable
/etc/init.d/cron restart



4. Damit die Umleitung von Webseiten zuverlässig immer auf die Datei /index.html führt, müssen wir noch eine Konfigurationseinstellung des Routerwebservers (uhttpd) ergänzen. Dort muß die Fehlerseite ("Fehler 404") auf /index.html umgeleitet werden. Die Option dazu ist bei der verwendeten Firmware noch nicht eingetragen und wird neu angelegt.

Wir öffnen die Datei /etc/config/uhttpd und fügen für 'uhttpd service' die Option 'error_page /index.html' ein. Das sieht z.B. dann so aus: (Dateiausschnitt)

. . .

# service instance
config uhttpd service
	option home		/www/service
	option listen_http 	80

	option error_page    /index.html

	# Reject requests from RFC1918 IP addresses
	# directed to the servers public IP(s).

. . .

Speichern!

Ab jetzt sollte jeder Versuch eine unverschlüsselte Webseite aufzurufen auf dem altbekannten Anmeldebildschirm landen. Das Anmelden selbst funktioniert aber noch nicht.

5. Ein Problem ist noch zu lösen: Der Anmeldebildschirm zeigt die Meldung, daß diese Wolke zz. kein Internet hat. Das liegt daran, daß das alte Verfahren zur Überprüfung des Internetstatus nicht mehr funktioniert. Hier ist also noch eine Baustelle! Ich habe vorerst auf diese Überprüfung verzichtet und den Inhalt von /www/service/cgi-bin/online.json wie folgt geändert.

vi /www/service/cgi-bin/online.json

Dateiinhalt gegen diesen hier austauschen:

#!/bin/sh -e

. /www/service/cgi-bin/common.sh

echo -e "Status: 200 OK\r
Content-Type: application/json\r
\r
#$(have_internet && echo true || echo false)"
$(echo true && echo true)"

Speichern!

6. Jetzt fehlt nur noch das eigentliche Verfahren zur Anmeldung. Das habe ich in der Datei /www/service/cgi-bin/splash_click.html untergebracht. Für jeden Client wird dort eine ebtables-Regel, die Datenpakete auf Port 80 von seiner MAC-Adresse zukünftig bis auf Widerruf durchläßt, erzeugt. Weiterhin erfolgt hier, wie gehabt, die Weiterleitung auf die Infoseite. Zz. ist hier die Freifunk Rheinland e.V. Webseite eingestellt.

Wir öffnen die bestehende Datei mit

vi /www/service/cgi-bin/splash_click.html

und tauschen den gesamten Inhalt gegen den Folgenden aus.

#!/bin/sh -e

. $IPKG_INSTROOT/etc/functions.sh
#. /etc/splash.sh
. /www/service/cgi-bin/common.sh

# decode request params
URL="$(sed -n 's/^.*target_url=\([^&]*\).*$/\1/p' | urldecode || true)"
#URL=""
USER_MAC=$(grep ^$REMOTE_HOST </proc/net/arp | awk 'BEGIN { FS = " " } ; { print $4 }')
[ -n "$USER_MAC" ]


ebtables -t broute -I BROUTING -s $USER_MAC -p IPV4 --ip-protocol 6 --ip-destination-port 80 -j mark --set-mark 1 --mark-target CONTINUE                                                         

echo -en "Status: 200 OK\r
Content-Type: text/html\r
\r
"
sed "s/targeturl/$(echo "$URL")/g" iframe.html
exit 0
EOF

Speichern!

7. Da ein Verfahren zum URL-dekodieren nicht mehr wie in der "alten Firmware" funktioniert, habe ich die Dateien /www/service/index.html und /www/service/iframe.html anpassen müssen. Das URL-dekodieren ist jetzt über Javascript in diesen beiden Dateien gelöst. (Randeffekt: Rechenzeit auf dem Router eingespart.)

Wir öffnen die bestehende Datei mit

vi /www/service/index.html

und tauschen den gesamten Inhalt gegen den Folgenden aus.

<html lang="de">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Freifunk Rheinland</title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/ffj.css">
</head>
<body>

<div class="navbar"><div class="navbar-inner"><div class="container-fluid">
<a class="brand" href="/">Freifunk Rheinland </a>
<div class="nav-collapse hidden" id="nav"><ul class="nav">
<li><a href="/settings.html"><i class="icon-cog icon-white"></i> Einstellungen</a></li>
</ul></div>
</div></div></div>

<div class="container-fluid">

  <div class="hero-unit">
    <img class="pull-right" src="/img/logo.png">
    <h1><a id="content" name="content"></a>Willkommen!</h1>

    <p>Du bist jetzt mit dem freien Funknetz
    <a href="http://www.freifunk-rheinland.net">Freifunk Rheinland</a>
    verbunden.</p>

    <p>Wir sind ein experimentelles Gemeinschaftsnetzwerk, aber kein
    Internetanbieter.</p>

    <p><a href="http://www.freifunk-rheinland.net" class="btn btn-success btn-large">Mehr lernen</a></p>
  </div>

  <div class="alert alert-error hidden" id="offline">
    <p><b>Das Internet ist in dieser Wolke vorrübergehend nicht
      verfügbar.</b><span class="hidden" id="local_services"> Die <a href="/services.html"><i class="icon-home"></i>
      lokalen Dienste</a> der Wolke stehen weiter zur
      Verfügung.</span></p>
  </div>

  <div class="row">
    <div class="span4">
      <h2>Lokales Netzwerk</h2>
      <p>Im Freifunknetz kann <emph>jeder</emph> Dienste anbieten:
      alle teilnehmenden Computer können direkt miteinander
      kommunizieren.<span class="hidden" id="settings"> Unter <a href="/settings.html" style="white-space: nowrap"><i class="icon-cog"></i> Einstellungen</a>
      kannst du festlegen, wie du das Netzwerk nutzen möchtest
      und Resourcen mit anderen Freifunkern teilen.</span></p>
    </div>

    <div class="span4">
      <h2>Internet</h2>
      <p>Ein Zugang ins Internet ist möglich, da einige
	Freifunker ihre privaten Internetzugänge zur
	Verfügung stellen. Diese Zugänge müssen sich
	hier alle teilen. Bitte sei Dir dessen bewusst und verhalte
	Dich dementsprechend:
      </p>

      <ul>
	<li><strong>keine Filesharing-Programme</strong></li>
	<li><strong>keine unnötigen Downloads oder Streams</strong></li>
	<li><strong>keine illegalen Aktivitäten</strong></li>
      </ul>

      <p>Mit einem Klick auf <em>Akzeptieren</em> kannst du für 1
	Stunde unser Netz verwenden. Dann wirst du erneut aufgefordet,
	diese Bedingungen zu akzeptieren.</p>

      <form name="akzept" action="/cgi-bin/splash_click.html" method="POST">
	<input type="text" name="username" value="Freifunk" style="display:none">
	<input type="password" name="password" value="Hotspot" style="display:none">
	<input type="hidden" name="target_url" value="targeturl"/>
	<input type="submit" class="btn btn-primary" value="Akzeptieren" />
	<a class="btn" href="/services.html">Ablehnen</a>
      </form>
    </div>

<script language="javascript">
<!--
   document.akzept.target_url.value = window.location.href;
//-->
</script>

    <div class="span4">
      <h2>Mitmachen</h2>

      <p>Wenn Du unsere Idee gut findest und das Netz regelmässig
	benutzt, dann bitten wir Dich um Unterstützung:
      </p>

      <ul>
	<li><a href="http://www.freifunk-rheinland.net/">Werde selbst
	Freifunker</a>. Dazu muss man nur einen handelsüblichen
	WLAN-Router ins Fensterbrett stellen.</a></li>
	<li><a href="http://www.freifunk-rheinland.net/">Spende</a> ein paar Euro, damit wir unser
	Netz weiter betreiben und ausbauen können.</li>
	<li>Wenn Du selbst privat genutzte WLAN-Geräte betreibst
	nutze dafür bitte andere Kanäle
	als wir.</li>
      </ul>
    </div>
  </div>
</div>

<script src="/js/jquery.min.js"></script>
<script src="/js/parse_services.js"></script>
<script src="/js/bootstrap.min.js" defer></script>
<script>

$.getJSON("cgi-bin/online.json", function(haveInternet) {
    if(!haveInternet)
	$('#offline').removeClass('hidden');
});

$.getJSON("cgi-bin/client_net.json", function(clientInfo) {
    if(clientInfo.wired) {
        $('#settings').removeClass('hidden');
        $('#nav').removeClass('hidden');
    }
});

$.when(
    $.getJSON('/cgi-bin/services.json'),
    $.ajax({url: '/js/parse_services.js', dataType: 'script', cache: true})
).done(function(res) {
    services(res[0]).drawLine($('#service-ul'));
    $('p#service-loading').remove();
    if (res[0].length > 0) 
	$('#local_services').removeClass('hidden');
});

</script>

</body>
</html>

und wir öffnen die bestehende Datei mit

vi /www/service/iframe.html

und tauschen den gesamten Inhalt gegen den Folgenden aus.

<!doctype html>
<html class="no-js" lang="de">
	<head>
		<meta charset="utf-8">
		<title>Freifunk Rheinland Redirect</title>
	</head>
	<body style="margin:0px;">
		<div style="width:100%;padding:5px 0;background-color:#FFCB05;text-align:center;border-bottom:3px solid #E0256C">
		<a style="color:#000;font-weight:bold;text-decoration:none" id="targeturl" href="targeturl">Weiter zur gesuchten Seite >>></a></div>

		<iframe id="frame" src="http://www.freifunk-rheinland.net/" width="99%"></iframe>
		<script>
			function pageY(elem) {
				return elem.offsetParent ? (elem.offsetTop + pageY(elem.offsetParent)) : elem.offsetTop;
			}
			function resizeIframe() {
				var height = document.documentElement.clientHeight;
				height -= pageY(document.getElementById('frame')) + 20 ;
				height = (height < 0) ? 0 : height;
				document.getElementById('frame').style.height = height + 'px';
			}
			window.onresize = resizeIframe;
			resizeIframe();
		</script>
		<script language="javascript">
		var link = document.getElementById("targeturl");
		var linkarr = decodeURIComponent( document.getElementById("targeturl").href ).split("//");
		link.setAttribute( "href" , "http://" + linkarr[2] );
		</script>
	</body>
</html>

Speichern und fertig! Nach einem Reboot sollte der Freifunkrouter über eine Anmeldeseite ('splash screen') verfügen.

Ich habe diese Konfiguration bisher ausschließlich mit einer allein betriebenen Queen ohne Drohnen getestet. Wie sich das ganze mit Drohnen verhält, weiß ich noch gar nicht. Ich bin noch in der Bastelphase. Ich freue mich über Jeden, der mitbastelt.

Der Anmeldebildschitm wird anders als früher nicht mehr von der zuständigen Queen sondern von dem Router erzeugt, mit dem man verbunden ist. Es können Drohnen mit und ohne Anmeldebildschirm problemlos im selben Funknetz betrieben werden. Jeder Router kann so auch eine individuelle Anmeldeseite (als Werbeeffekt, etc.) haben.