Linux - AHA
Kleine und große Linux AHAs
Kleine und große Linux AHAs
03. Mai
Folgendes Szenario: Der Asterisk Prozess schmiert aus irgendeinem Grund ab. Der Dienst wird wieder hochgefahren und die Telefone sind nicht mehr registriert. Was nun? Klar, nach einer gewissen Zeit erneuern die Geräte ihre Registrierung und sind wieder erreichbar. Aber bis dahin? Und wie lange dauert das?
Ok, wir sprechen hier über Snom Telefone. Diese können per Remote bedient werden und das machen wir uns zu Nutze. Wir simulieren einfach den Klick auf “Re-Registrieren” der Weboberfläche. Dazu nutzen wir wget.
Aber
Normalerweise nutze ich um die Telefone anzusprechen die IP aus der Datenbank. Leider ist in dem Fall in der Datenbank auch kein IP Eintrag – Telefon ja nicht registriert
Also was anderes. Mir kam die Idee das ganze über den ARP Cache zu machen. OMG! Warum das denn?
Naja eigentlich ganz einfach. Anhand der MAC kann man Snom Geräte bestimmen. Damit ich dann wirklich nur Snom Geräte mit dem Befehl anspreche muss ich lediglich alle MAC Adressen nehmen die mit 00:04:13 beginnen. Damit hab ich alle IPs die ich ansprechen muss.
#!/bin/bash
#
# Scan ARP Cache for Snom Devices and send a re-register to them
#
SNOM_USER="webloginUser"
SNOM_PASSWORD="webloginpw"
for IP in `grep "00:04:13" /proc/net/arp | cut -f1 -d " "`</p>
do
wget --no-proxy --post-data="REREGISTER:1=Re-Registrieren" http://${SNOM_USER}:${SNOM_PASSWORD}@${IP}/line_login.html >/dev/null 2>&1;
print "Did ${IP}";
}
Nachdem das Script gelaufen ist sollten sich wieder alle Telefone am Asterisk Server angemeldet haben.
30. Apr
Um unseren Asterisk möglichst flexibel zu halten wurde Asterisk mit MySQL Schnittstelle installiert. Das bringt uns in die Lage recht einfach Konfigurationsänderungen vorzunehmen – nämlich in der Datenbank und damit ohne Neustart des Dienstes. Nun leben wir in Deutschland und in Deutschland gibt es Umlaute. Umlaute sind immer so eine Sache in Datenbanken, Programmen, Scripten etc..
Auch unser Asterisk kann per Default nichts mit Umlauten anfangen – wenigstens nicht aus der Datenbank so wie wir sie aufgesetzt haben. Server als auch Client (MySQL & Asterisk) müssen auf einen passenden Zeichensatz gebracht werden um ohne Verluste miteinander sprechen zu können – es empfiehlt sich UTF8.
Woran sieht man ob es überhaupt Probleme gibt? Naja, man lege eine Nebenstelle an. Die CallerID (Name) lautet “Max Müller”. Was kommt beim Angerufenen an? Ich schätze Sonderzeichen stehen im Display anstatt “Max Müller”. Das ganze ebenfalls bei der Queue – wenn man diese aus der Datenbank bedient. Schaut man sich dann die Mitglieder der Queue an und hat ein Mitglied mit Sonderzeichen im Namen, so ist das weniger hübsch
Hat man nun einen Asterisk mit MySQL Anbindung in Betrieb, so kann man wie folgt vorgehen:
Anpassen der MySQL Konfiguration (/etc/mysql/my.cnf oder /etc/my.cnf):
im Bereich [Client] folgendes hinzufügen:
default-character-set=utf8
im Bereich [mysqld] dieses:
default-character-set=utf8 default-collation=utf8_general_ci character-set-server=utf8 collation-server=utf8_general_ci
mysqldump
mysqldump -uroot -pasterisk -c -e --default-character-set=utf8 --single-transaction --skip-set-charset --add-drop-database -B asterisk_db > /tmp/asterisk_db.sql cp /tmp/asterisk_db.sql /tmp/asterisk_db_utf8.sq
Anpassen des Dumps
sed -i 's/DEFAULT CHARACTER SET latin1/DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci/' /tmp/asterisk_db_utf8.sql sed -i 's/DEFAULT CHARSET=latin1/DEFAULT CHARSET=utf8/' /tmp/asterisk_db_utf8.sql sed -i 's/CHARACTER SET latin1/CHARACTER SET utf8/' /tmp/asterisk_db_utf8.sql
Durch diese sed Befehle werden alle latin1 Character Sets durch UTF8 ersetzt. Dadurch wird beim Import für MySQL die Datenbank und alle Tabellen/Spalten auf UTF8 gesetzt.
Als Script sieht das ganze dann so aus:
#!/bin/bash # Dump the DB mysqldump -uroot -pasterisk -c -e --default-character-set=utf8 --single-transaction --skip-set-charset --add-drop-database -B asterisk_db > /tmp/asterisk_db.sql /etc/init.d/mysql restart # make copy cp /tmp/asterisk_db.sql /tmp/asterisk_db_utf8.sql # update table and column definitions sed -i 's/DEFAULT CHARACTER SET latin1/DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci/' /tmp/asterisk_db_utf8.sql sed -i 's/DEFAULT CHARSET=latin1/DEFAULT CHARSET=utf8/' /tmp/asterisk_db_utf8.sql sed -i 's/CHARACTER SET latin1/CHARACTER SET utf8/' /tmp/asterisk_db_utf8.sql mysql -uroot -pasterisk < /tmp/asterisk_db_utf8.sql sleep 1 /etc/init.d/asterisk restart
Da das Script quasi alles macht und die Datenbank im Normalfall nicht so wahnsinnig groß ist, kann man das “Zwischendurch” machen. Je nach Umgebung sollte natürlich eine Zeit gewählt werden, in der wenig bis gar nichts los ist. Das Script braucht nur wenige Sekunden. Bisschen Mut gehört dazu
Wichtig zu wissen ist, dass sich die Telefone neu registrieren müssen bevor sie wieder Gespräche annehmen können.
23. Apr
Der nächste Hardware “Baustein” ist ein E1 (Primär Multiplex) Gateway. Wie in Season 3 bereits beschrieben werden die Geräte von Patton eingesetzt.
Die Konfiguration für die 4960 ist vom handling her analog zur 4118. Von daher ist die Beschreibung der 4118 ein guter Anfang. Die 4960 ist relativ fett was die Funktionen angeht. Benötigt man lediglich ein Gateway für die Verbindung zwischen Asterisk und E1/PMX Anschluss, so genügt auch die 4940 die um einiges günstiger ist. Bei uns hätte die auch genügt
Von der Konfiguration her wird das aber keinen allzu großen Unterschied machen. Es werden wohl – verglichen zu meinem Beispiel – einfach ein paar Teile wegfallen.
Ich versuche das ganze durch Kommentare möglichst verständlich zu machen
#----------------------------------------------------------------# # # # SN4960/1E30V # # R5.8 2011-07-01 H323 RBS SIP # # 2012-04-22T09:29:44 # # SN/00A0BA070FC4 # # Generated configuration file # # # #----------------------------------------------------------------# # Die ersten Teile sind analog zur 4118 cli version 3.20 administrator admin password NR7Hh4jseumPf7xjTSemHQ== encrypted clock local default-offset +02:00 dns-relay webserver port 80 language en snmp community nagios ro snmp community configsave rw snmp host 10.10.210.36 security-name monitoring snmp host 10.10.210.36 security-name configsave sntp-client sntp-client server primary 10.10.210.201 port 123 version 4 system hostname pat01 system location Schrank1 system contact netzwerkmanagement@meinefirma.de system ic voice 0 system # clock source ist extrem wichtig. Gerade fur den Faxempfang clock-source 1 e1t1 0 0 profile r2 default profile napt NAPT_WAN profile ppp default profile tone-set default profile voip default # preferierte Codecs codec 1 g711alaw64k rx-length 30 tx-length 30 codec 2 g711ulaw64k rx-length 30 tx-length 30 codec 3 g729 rx-length 30 tx-length 30 rtp traffic-class local-default dejitter-mode static dejitter-max-delay 120 # Fax per T.38 fax transmission 1 relay t38-udp # Modemkonfiguration, bspw. fuer Datentransfer etc. modem transmission 1 bypass g711alaw64k rx-length 10 tx-length 10 modem dejitter-max-delay 60 no modem detection on-remote-fax-request profile pstn default profile sip default no autonomous-transitioning profile dhcp-server DHCPS_LAN # die 4960 hat einen internen DHCP Server # diesen nutzen wir allerdings nicht # alternativ kann man auch eine Patton 4940 # nehmen, die hat weniger funktionen - wenn # es rein um ein E1 Gateway geht. network 192.168.1.0 255.255.255.0 include 1 192.168.1.10 192.168.1.99 lease 2 hours default-router 1 192.168.1.1 profile aaa default method 1 local method 2 none context ip router interface WAN ipaddress dhcp use profile napt NAPT_WAN tcp adjust-mss rx mtu tcp adjust-mss tx mtu interface LAN # die IP des LAN Interfaces ipaddress 10.31.2.13 255.255.255.0 tcp adjust-mss rx mtu tcp adjust-mss tx mtu context ip router # Default gateway route 0.0.0.0 0.0.0.0 10.31.2.201 0 context cs switch # analog zum 4118 die Routing Tables # RT_ISDN_IN routing-table called-e164 RT_ISDN_IN # Default Destination auf RT_LOCAL. Damit die Nummer # intern zurueckgerufen werden kann, wird 00 vor die # von der Telekom empfangene Nummer gesetzt. # eine 0 fuers Amt und eine, da die Nummer ohne fuehrende # 0 uebertragen wird. route default dest-table RT_LOCAL MAP_ADD_PREFIX_00 # route 0(.%) dest-interface IF_SIP_VOIPSERVER MAP_ADD_PREFIX_00 # die 501 ist die Nummer des Anschlusses und wird per # MAP_REMOVE_PREFIX_401 entfernt, damit nur die Nummer # der Nebenstelle als Ziel uebrig bleibt route 501(.%) dest-table RT_ISDN_IN MAP_REMOVE_PREFIX_501 # das ist ein Spezialfall. Frueher wurde die 30 als Zentrale # angerufen. Nun muss die 300 angerufen werden. # Mit dieser Regel wird die 30 in 300 umgeschrieben route 30$ dest-table RT_LOCAL CF_MAP30TO300 # Table RT_LOCAL routing-table called-e164 RT_LOCAL # Default zum Voipserver route default dest-interface IF_SIP_VOIPSERVER # 3 Stellen zum Voipserver route ... dest-interface IF_SIP_VOIPSERVER # Faxanschluesse zu der jeweiligen Patton route 338 dest-interface IF_SIP_PAT03 route 341 dest-interface IF_SIP_PAT03 route 344 dest-interface IF_SIP_PAT03 route 345 dest-interface IF_SIP_PAT03 route 346 dest-interface IF_SIP_PAT03 route 350 dest-interface IF_SIP_PAT03 route 310 dest-interface IF_SIP_PAT02 route 318 dest-interface IF_SIP_PAT02 route 323 dest-interface IF_SIP_PAT02 route 9366 dest-interface IF_SIP_PAT02 route 332 dest-interface IF_SIP_PAT02 route 333 dest-interface IF_SIP_PAT02 route 334 dest-interface IF_SIP_PAT02 route 335 dest-interface IF_SIP_PAT02 # Table RT_SIP_IN, Anrufe von dem Voipserver initiiert routing-table called-e164 RT_SIP_IN # Default nach RT_LOCAL route default dest-table RT_LOCAL # Zielnummer beginnt mit 0? Dann nach extern, aber zuerst noch # die Sendernummer anpassen (CF_CLIP_NO_SCREENING_501) route 0(.%) dest-interface IF_ISDN_0_0 CF_CLIP_NO_SCREENING_501 mapping-table calling-type-of-number to calling-type-of-number MAP_TON_TO_NATIONAL map default to national # Mapping um zwei Nullen voran zu stellen mapping-table calling-e164 to calling-e164 MAP_ADD_PREFIX_00 map (.%) to 00\1 # Mapping um eine Null am Anfang zu entfernen (die Amtsnull) mapping-table called-e164 to called-e164 MAP_REMOVE_PREFIX_0 map 0(.%) to \1 # Mapping um die Anschlussnummer zu entfernen (Nebenstelle bleibt uebrig) mapping-table called-e164 to called-e164 MAP_REMOVE_PREFIX_501 map 501(.%) to \1 # Mapping um die Anschlussnummer voran zu stellen mapping-table calling-e164 to calling-e164 MAP_ADD_PREFIX_1234501 # Alle Nebenstellen die mit einer 3 beginnen bekommen diese Anschlussnummer # vorangesetzt map (3..)$ to 1234501\1 # Mapping um aus der 30 die 300 zu machen (s.o.) mapping-table called-e164 to called-e164 MAP_30_TO_300 map 30 to 300 # Complex Functions (mehrere Mappings zusammengefasst) # Diese werden oben aufgerufen innerhalb der Tables complex-function CF_CLIP_NO_SCREENING_501 execute 1 MAP_ADD_PREFIX_1234501 execute 2 MAP_TON_TO_NATIONAL execute 3 MAP_REMOVE_PREFIX_0 complex-function CF_MAP30TO300 execute 1 MAP_30_TO_300 execute 2 MAP_ADD_PREFIX_00 # Das Interface IF_ISDN_0_0 interface isdn IF_ISDN_0_0 # Destination Table route call dest-table RT_ISDN_IN # Definition der Patton Gateways (meist Fax) # auf diese wird oben in der RT_LOCAL geroutet interface sip IF_SIP_PAT02 bind context sip-gateway GW_SIP route call dest-table RT_SIP_IN remote 10.31.2.14 interface sip IF_SIP_PAT03 bind context sip-gateway GW_SIP route call dest-table RT_SIP_IN remote 10.31.2.15 # Der Voipserver interface sip IF_SIP_VOIPSERVER bind context sip-gateway GW_SIP route call dest-table RT_SIP_IN remote 10.31.2.10 context cs switch no shutdown context sip-gateway GW_SIP interface GW_IF bind interface LAN context router port 5060 context sip-gateway GW_SIP no shutdown # ethernet 0 0 ist das LAN Interface port ethernet 0 0 medium auto encapsulation ip bind interface LAN router no shutdown # das zweite Interface nutzen wir nicht port ethernet 0 1 medium auto encapsulation ip shutdown # der Port e1t1 ist der PMX Port port e1t1 0 0 port-type e1 clock slave framing crc4 encapsulation q921 q921 uni-side auto encapsulation q931 q931 protocol dss1 uni-side user bchan-number-order ascending encapsulation cc-isdn # das Interface wird auf IF_ISDN_0_0 gebunden bind interface IF_ISDN_0_0 switch port e1t1 0 0 no shutdown
Dieses Konfiguarationsbeispiel sollte für viele Installationen ein guter Anfangspunkt sein. Wenn ihr sie anpasst und einspielt, dann könnt ihr über die Weboberfläche die Routing Tables ganz einfach administrieren (Nummern <-> Pattons (für Faxnummern)). Das sollte dann nach der Erstkonfiguration auch das sein, was man so im laufenden Betrieb machen muss. Bei Faxnummern oder generell bei Routing auf andere Patton Geräte daran denken, dass auch auf dem “ZielPatton” die Nebenstelle konfiguriert werden muss.
Übrigens: Um einen Eintrag zu ändern (gleiche Nummer, anderes Interface) legt man einfach einen neuen Eintrag mit der gleichen Nummer an
23. Feb
Viele kennen es aus Hotels, Bahnhöfen, Cafes oder öffentlichen Plätzen. Es gibt ein offenes WLAN zum Internet und wenn man sich damit verbindet landet man zuerst einmal auf einer speziellen Seite. Auf dieser muss man sich dann irgendwie registrieren oder halt bezahlen. Aber wie funktioniert sowas und macht das vielleicht auch Sinn für ein Unternehmen intern?
Also hier geht es nicht um Provider oder Firmen die das ganze öffentlich anbieten. Es gibt beispielsweise die Anforderung in Unternehmen die Produktionsstraßen besitzen, Großraumhallen, Fertigungshallen oder ähnliches. Oft gibt es dort viele Geräte verschiedener Hersteller die gewartet werden (nur ein Beispiel). Es kommen also externe Dienstleister um Updates einzuspielen oder Support zu leisten. Diese müssen mittlerweile, um vernünftig arbeiten zu können, Kontakt mit Ihrer Firma haben. Das kann über 3G geschehen, ist allerdings schneller über WLAN. Für solche Dienstleister macht ein WLAN Zugang also Sinn. Das auch für einen “normalen” Besprechungs- oder Schulungsraum schon Sinn machen.
Nun hat man ein Problem. Das WLAN offen zu lassen ist quasi grob fahrlässig. Das WLAN schützen macht es umständlich – je nach Anzahl der verschiedenen Benutzer. Dann haben wir da noch den Aspekt des Loggings. Prinzipiell muss man die Verbindungsdaten mitloggen, da man je nach dem in die Beweispflicht genommen werden kann. Um zu loggen muss ich auch wissen wer zu welchem Gerät gehört, da mir ansonsten die Information nichts bringt. Das alles kann ich mit der Anmeldung quasi erledigen. Ich weiß wer zu welchem Gerät gehört und kann auch mitloggen. Bevor der Nutzer sich über das WLAN mit dem Internet verbindet, muss er einer entsprechenden Nutzungserklärung zustimmen (wegen logging etc.).
Diese Portale sind gar nicht so kompliziert wie man denkt. Um so etwas unter Linux zu realisieren kann man sich fertiger Software bedienen oder auch einfach das ganze selbst – relativ schnell – programmieren. Ein Projekt mit einer “CaptivePortal” Funktion ist beispielsweise pfsense. Die meisten CaptivePortal Funktionen befinden sich im Bundle mit Firewalls und bieten weit mehr als eine einfach Authentifizierung für einen Netzzugang.
Um so etwas mit Linux Mitteln darstellen zu können habe ich folgende Komponenten genutzt:
Das ganze funktioniert nach folgendem Ablauf:
Zur Registrierung benötigt man einen Schlüssel (5 Stellen, Alphanumerisch). Diesen kann man sich an eine interne Mailaddresse schicken lassen. Also können nur interne Accounts quasi den Zugriff genehmigen. Wenn man einen Schlüssel besitzt, dann kann man sich über das Portal registrieren (Name, Firma und Schlüssel werden abgefragt). Mit der Registrierung – richtiger Schlüssel vorausgesetzt – wird der Zugang für einen Tag freigegeben.
Der Client bekommt eine IP Adresse vom DHCP Server. Alle Zugriffe ins (bspw.) Internet werden erst einmal geblockt, außer Zugriffen auf Port 80 – diese werden umgeleitet auf den Webserver des Gatewyas – das CaptivePortal erscheint. Hat man noch keinen Schlüssel zum registrieren, so muss dieser angefordert werden. Man trägt eine eMail Adresse ein und sendet die Anfrage. Im Hintergrund wird per Perl Script geprüft ob die Empfängeradresse ok ist (bspw. interne Domain oder in einer Liste) und ein Schlüssel generiert. Dieser Schlüssel inkl. Mailadresse wird in der Datenbank hinterlegt und per Mail an den Empfänger geschickt.
Mit dem Schlüssel registriert sich der Client am Portal (inkl. Name, Firma). Mit dem Klicken des Buttons “registrieren” wird der Schlüssel geprüft und deaktiviert. MAC Adresse, Name und Firma des Anforderers werden in die Datenbank geschrieben. Danach wird ein iptables Befehl abgesetzt, der dem Client mit der betreffenden MAC Adresse den Zugriff zum Netz gewährt. Ebenfalls wird in der Datenbank ein Zeitstempel gespeichert, dadurch ist es möglich über ein iptables-update den Zugriff generell nur für eine gewisse Zeit freizugeben und dann automatisch wieder zu sperren. In der Datenbank gibt es ein Flag um zu definieren, ob dieser Eintrag überhaupt abläuft oder nicht (expires yes/no). Damit kann man also auch das ganze für interne Mitarbeiter statisch freigeben, damit diese sich nicht immer wieder neu registrieren müssen.
(Die Scripte liegen bei mir unter /opt/captivePortal – der Pfad ist in manchen Scripten zu finden)
iptables ist ja quasi auf jedem Linux System vorhanden und muss auch nicht groß konfiguriert werden.
Script 1 (Initiales iptables Script / iptables.sh):
#!/bin/bash # Initial Iptables Script to enable captivePortal # # Ronny Becker, 02.2012 IPTABLES=/sbin/iptables # the interface to authenticate PORTAL_INT=eth2 # the interface where traffic goes through OUTPUT_INT=eth0 # PortalIP (for captivePortal website) PORTAL_IP=192.168.199.201 # clear all rules iptables -F iptables -X iptables -t nat -F iptables -t nat -X iptables -t mangle -F iptables -t mangle -X iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT # Create captivePortal chain $IPTABLES -N captivePortal -t mangle # All traffic goes to this chain $IPTABLES -t mangle -A PREROUTING -j captivePortal ###### captivePortal CHAIN ########## # get allowed MACs out of the database (maybe persistent or in time) /opt/captivePortal/iptablesFromDB.pl # DNS is allowed for all $IPTABLES -t mangle -A captivePortal -i $PORTAL_INT -p udp --dport 53 -j RETURN # Mark packets that are not allowed till here $IPTABLES -t mangle -A captivePortal -i $PORTAL_INT -j MARK --set-mark 99 # redirect port 80 to captivePortal $IPTABLES -t nat -A PREROUTING -m mark --mark 99 -i $PORTAL_INT -p tcp --dport 80 -j DNAT --to-destination $PORTAL_IP # drop all marked with 99 $IPTABLES -t filter -A FORWARD -m mark --mark 99 -j DROP # masquerading (if needed) iptables -t nat -A POSTROUTING -o $OUTPUT_INT -s 192.168.199.0/24 -j MASQUERADE
Das iptables.sh Script muss bei jedem Systemstart ausgeführt werden. Des Weiteren muss ip_forward (/etc/sysctl.conf) gesetzt sein, damit der Server routen kann.
Script 2 (Perl Script zum Auslesen der aktuellen Berechtigungen aus der Datenbank / iptablesFromDB.pl):
#!/usr/bin/perl
# captivePortal
# Script to get registered MACs out of DB
use DBI;
# get Config from central conf file
BEGIN { require "/opt/captivePortal/captivePortal.conf" };
# database connection
$connectionInfo="DBI:mysql:database=$db;$host:$port";
$dbi = DBI->connect($connectionInfo,$userid,$passwd);
# select datasets
$sql="select mac from registered where expires='no' or ((UNIX_TIMESTAMP()-timestamp) < $tte)";
$sth = $dbi->prepare($sql);
$sth->execute() or die("Cannot get MACs");
$sth->bind_columns(undef, \$db_mac);
while ( $sth->fetch() ) {
# and run iptables
`/sbin/iptables -t mangle -A captivePortal -m mac --mac-source "$db_mac" -j RETURN`;
}
$sth->finish();
$dbi->disconnect();
Config (Konfig für die Perl Scripte (s. Script 2 / captivePortal.conf):
# captivePortal - central config # this conf file is used in perl scripts use vars qw( $tte $db $host $port $userid $passwd $iptables_if ); # Time To Expire for dynamic subscriptions $tte="86400"; # Database Information $db="captivePortal"; $host="localhost"; $port="3306"; $userid="portal"; $passwd="captive"; # portal interface $iptables_if="eth2";
Script 3 (Updatescript für iptables um abgelaufene Registrierungen zu löschen / iptables_update.sh):
#!/bin/bash # captivePortal # Update iptables rules to delete expired registrations IPTABLES=/sbin/iptables # the interface to authenticate PORTAL_INT=eth2 # clear the chain $IPTABLES -t mangle -F captivePortal # run the rules out of the db /opt/captivePortal/iptablesFromDB.pl # add default rules (see iptables.sh) $IPTABLES -t mangle -A captivePortal -i $PORTAL_INT -p udp --dport 53 -j RETURN $IPTABLES -t mangle -A captivePortal -i $PORTAL_INT -j MARK --set-mark 99
Das Update Script sollte per cron alle Xmin / Xstunden ausgeführt werden, damit abgelaufene Registrierungen den Zugriff verlieren. Wenn man den cronjob nicht gerade jede Minute laufen lässt, ist damit natürlich nicht gewährleistet dass eine Registrierung genau so lange gültig ist, wie es in der Konfiguration steht. In meinem Fall läuft das Script jede Stunde – das ist für meine Zwecke ausreichend.
Die Datenbank besteht aus zwei Tabellen -> captivePortal.sql
Wir benötigen lediglich ein CGI Script. Auf dieses muss per Default umgeleitet werden sobald der Server eine Anfrage bekommt. Das kann man auf verschiedenste Weisen machen – also nicht schimpfen
Redirect
Damit das ganze mit iPhone und Android funktioniert, habe ich folgende Apache config am Start:
<VirtualHost 192.168.199.201:80>
ServerAdmin webmaster@localhost
Servername captivePortal
DocumentRoot /var/www
Redirect /index.html http://192.168.199.201/cgi-bin/captivePortal.pl
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI Indexes -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^/$ http://192.168.199.201/cgi-bin/captivePortal.pl [L,R]
</IfModule>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Dem entsprechend liegt das CGI (captivePortal.pl) unter /usr/lib/cgi-bin.
Das CGI
#!/usr/bin/perl
# captivePortal
#
# This is the cgi for the simple captivePortal
#
# Do not forget to change
# line 51: valid domain to send regkey to
# line 58: sender adress
#
#
#
# Ronny Becker, 02.2012
use CGI qw(:all);
use DBI;
# get Config from central conf file
BEGIN { require "/opt/captivePortal/captivePortal.conf" };
# connect to DB
$connectionInfo="DBI:mysql:database=$db;$host:$port";
$dbi = DBI->connect($connectionInfo,$userid,$passwd);
# params
$thisscript=$ENV{'SCRIPT_NAME'};
# cgi params
$para_req_sendto=param('req_sendto');
$para_req_submit=param('req_submit');
$para_reg_company=param('reg_company');
$para_reg_name=param('reg_name');
$para_reg_key=param('reg_key');
$para_reg_submit=param('reg_submit');
# print some headers
print "Content-type: text/html\n\n";
print '
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>WLAN Gastzugang</title>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
</head>
<body style="font-family: courier,courier-new">
<h2>WLAN Gastzugang</h2><br>';
# request for key
if ( $para_req_submit ) {
# address valid?
if ( $para_req_sendto ) {
if ( $para_req_sendto =~ /.*\@mydomain\.de/i || $para_req_sendto =~ /.*\@mydomain2\.de/ ) {
# generate a key
my @chars=('a'..'z','0'..'9');
foreach (1..5) {
$secret.=$chars[rand @chars];
}
# send key to recipient
$result=`export EMAIL="gastzugang\@mydomain.de"; echo "Der Registrierungsschluessel fuer Ihren Gast lautet: $secret" | mail -s "WLAN Gastzugang" $para_req_sendto`;
print "$result";
# Insert Key into DB
$sql="insert into regkeys(regkey,sendto,datetime) values('$secret','$para_req_sendto',NOW())";
$sth = $dbi->prepare($sql);
$sth->execute() or die("Fehler beim schreiben in die Datenbank");
$sth->finish();
print "<div style='background-color: green; color: white'><p><b>Registrierungsschluessel wurde zugeschickt</b></p></div>";
}
}
}
# request for registration
if ( $para_reg_submit ) {
# name given?
if ( length($para_reg_name) <= 5 || length($para_reg_key) != 5 ) {
print "<div style='background-color: red; color: white'><p><b>Sie muessen einen Namen und den Registrierungsschluessel angeben.</b></p></div>";
} else {
# Check DB for regkey
$sql="select sendto from regkeys where regkey='$para_reg_key' and isActive='1'";
$sth = $dbi->prepare($sql);
$sth->execute() or die("DB: Cannot run select");
$sth->bind_columns(undef, \$db_sendto);
$sth->fetch();
$sth->finish();
if ( $db_sendto ) {
# disable key
$sql="update regkeys set isActive='0' where regkey='$para_reg_key'";
$sth = $dbi->prepare($sql);
$sth->execute() or die("DB: Cannot update");
$sth->finish();
# get MAC
open (ARP,"</proc/net/arp");
while (<ARP>) {
$line = $_;
$line =~ s/ */;/g;
($arp_ip,undef,undef,$arp_mac,undef,undef) = split(";",$line);
if ( $arp_ip eq $ENV{'REMOTE_ADDR'} ) {
$mac=$arp_mac;
}
}
close ARP;
# write data into db
$sql="insert into registered(name,guestof,mac,timestamp,expires) values('$para_reg_name, $para_reg_company','$db_sendto','$mac',UNIX_TIMESTAMP(),'yes')";
$sth = $dbi->prepare($sql);
$sth->execute() or die("DB: Cannot insert");
$sth->finish();
$output=`sudo /sbin/iptables -I captivePortal 1 -t mangle -m mac --mac-source $mac -j RETURN`;
print "<div style='background-color: green; color: white'><p><b>erfolgreich registriert</b><br>$output</p></div>";
} else {
print "<div style='background-color: red; color: white'><p><b>Registrierungsschluessel nicht gefunden</b></p></div>";
}
}
}
# disconnect from DB
$dbi->disconnect();
# print form etc.
print '
<form method=get action='.$thisscript.'>
<b>Registrierungsschluessel anfragen</b><br>
senden an: <input type=text name=req_sendto size=40> <input type=submit name=req_submit value=zusenden></form>
<hr><br>
<form method=get action='.$thisscript.'>
<b>Registrieren</b><br>
<b><u>Achtung! Registrieren Sie sich mit dem Geraet, mit dem Sie Zugriff benoetigen</b></u><br>
<table>
<tr><td>Name:</td><td><input type=text name=reg_name size=30></td></tr>
<tr><td>Firma:</td><td><input type=text name=reg_company size=30></td></tr>
<tr><td>Registrierungsschluessel:</td><td><input type=text name=reg_key size=10></td></tr>
<tr><td colspan=2 align=center><font color=red><b><u>Nutzungsbedingungen:</u><br>
Mit klicken auf den Button "registrieren" stimmen Sie zu,dass die ihre Verbindungsdaten (Zeitpunkt und Dauer einer Verbindung, Addressierungsdaten) mitprotokolliert werden.
Dies dient lediglich zur Fehleranalyse und zur Sicherheitsueberpruefung.
Es werden keine Daten an dritte weitergegeben oder weiterverarbeitet.<br><br><br>
Sie haben (per Default) ab dem Zeitpunkt der registrierung Zugriff fuer 24 Stunden.
<input type=submit name=reg_submit value=registrieren></td></tr>
</table>
</form>
</body>
</html>';
Das Webinterface ist recht einfach gehalten damit es auch mit mobilen Endgeräten keine Probleme gibt. Das ganze ist natürlich nach belieben anzupassen oder zu ändern
Ich hoffe ich hab nix vergessen – falls doch einfach fragen.
03. Jan
Ein neuer HP Server DL380 G7. Die aktuelle Baureihe – tolles Teil. Mit einem kleinen – wie ich finde total unnötigen Schönheitsfehler.
Hört sich komisch an – ist aber so. Was wollten wir tun? Wir wollten auf diese Maschinen einen VMware Server installieren. Der neue VMware ESXi ist quasi eine freie Version des ESX Servers und wird direkt auf einem Server installiert, ohne dass ein Betriebssystem vorhanden sein muss (früher lief unter dem ESX ein RedHat, was heute darunter läuft weiß ich leider nicht).
Das ganze funktioniert auch mit der Hardware einwandfrei. Alles gut. Dann wollte ich die Maschine monitoren. Wie? Es gibt keine Agents für VMware. Also wollte ich das “Integrated Lights Out” (ILO) Board nutzen. Über diese Karte – mit separatem Netzwerkanschluss – kann man die Maschine remote administrieren (Konsole), bzw. auch direkt über die Hardware neu starten, Systeminfos einsehen (…). Unter anderem zeigt ILO auch das IML (Integrated Management Log) an. In diesem Log werden Systemmeldungen gespeichert wie z.B. Festplattenprobleme, ausgefallene Lüfter oder ähnliches.
Das ILO Interface kann über IPMI abgefragt werden (muss aktiviert werden). Damit lässt sich dann das Log auslesen, bzw. mit Hilfe des ipmievd das ganze auf Fehler beobachten. Der Befehl dazu lautet:
server01:~# ipmitool -I lanplus -U ilouser -H 10.10.200.98 -P <password> sel list 1 | 12/01/2011 | 15:10:13 | Power Supply #0x04 | Failure detected | Asserted 2 | 12/19/2011 | 07:16:21 | Power Supply #0x04 | Failure detected | Asserted
Das sah sehr gut aus. Nun sollte das mit einem Festplattenausfall getestet werden. Also eine Platte raus. Log abrufen …. nix. Keine Meldung. Hmmmm…. Bei den älteren Servern ging das.
Also alle Firmwarestände geprüft und am Ende mit HP in Kontakt getreten.
Von HP haben wir dann erfahren, dass HP eine eigene Version (ich denke gepatcht mit Treibern/Modulen) des ESXi für ihre Hardware anbietet. Das ganze ist auch kostenfrei – nach Registrierung. Man benötigt diese Version, damit das ILO mitbekommt wenn eine Festplatte – oder möglicherweise auch andere Hardware – ausfällt, bzw. ein Problem hat. Verstehen muss ich das nicht, oder? Es muss auf dem System ein Agent/Modul/Treiber oder wie auch immer laufen, damit die eigene Hardware, in dem Fall eine Management Karte, den Status der Hardware mitbekommt.
Naja, wie dem auch sei. Nun funktioniert das ganze. Gut finde ich nicht, dass es eine solche Abhängigkeit gibt.
27. Dez
Die Konfiguration eines Patton 4118 wurde im letzten Artikel beschrieben. Damit nichts verloren geht, sollte man regelmäßig die Konfiguration sichern. Das ganze zu Fuß zu machen macht keinen Sinn also automatisch. Aber wie?
Meine erste Idee war die Sicherung über Telnet. So hat man auch früher die Konfiguation von Cisco Switches gesichert. Ein Script verbindet sich per Telnet auf das Gerät, listet die Konfiguration und sichert sie in einer Datei. Das würde mit den Pattons auch funktionieren. Allerdings gibt es eine bessere Möglichkeit.
Es gibt die Möglichkeit die Konfiguration per TFTP zu sichern. Nicht als Cronjob auf dem Gerät selbst, sondern mit einem SNMP Aufruf (siehe SNMP Community zum schreiben im letzten Artikel). Dazu gibt es mehrere OIDs mit denen man
setzen kann. Des Weiteren gibt es eine OID die die Sicherung ausführt und eine um den Erfolg zu prüfen.
… sieht das ganze dann so aus:
#!/bin/bash
#
# Do Patton devices upload their current config via tftp
# -> for Backup
#
# .1.3.6.1.4.1.1768.100.3.1.1.0 = INTEGER: noOp(0)
# -- configsave $CLIENT SMARTNODE-MIB::uploadExecute.0
# .1.3.6.1.4.1.1768.100.3.1.2.0 = STRING: "10.21.3.10"
# -- SMARTNODE-MIB::uploadTftpServerAddress.0
# .1.3.6.1.4.1.1768.100.3.1.4.0 = STRING: "moe-pat05_20110929.cfg"
# -- SMARTNODE-MIB::uploadTftpServerPath.0
# .1.3.6.1.4.1.1768.100.3.1.5.0 = INTEGER: success(1)
# -- SMARTNODE-MIB::uploadStatus.0
UPLOAD_SERVER="10.21.3.10"
UPLOAD_FILENAME="_`date +%Y%m%d`.cfg"
UPLOAD_CLIENTS="patton01 patton02 patton03 patton04"
for CLIENT in $UPLOAD_CLIENTS
do
snmpset -v 1 -c configsave $CLIENT .1.3.6.1.4.1.1768.100.3.1.2.0 s $UPLOAD_SERVER >/dev/null
snmpset -v 1 -c configsave $CLIENT .1.3.6.1.4.1.1768.100.3.1.4.0 s "${CLIENT}${UPLOAD_FILENAME}" >/dev/null
snmpset -v 1 -c configsave $CLIENT .1.3.6.1.4.1.1768.100.3.1.1.0 i 1 >/dev/null
sleep 3
STATUS=`snmpget -v 1 -c nagios $CLIENT .1.3.6.1.4.1.1768.100.3.1.5.0`
if [[ $STATUS =~ failed ]]
then
echo "$CLIENT: tftp Backup via snmp failed"
fi
done
Damit liegen die Konfigurationen mit Namen patton01_20111217.cfg im TFTP Verzeichnis des – in meinem Fall – VoIP Servers. Das ganze hübsch in einen Cronjob verpackt und fertig ist die Sicherung.
21. Dez
Der nächste Hardware “Baustein” ist ein Analog Gateway. Wie in Season 3 bereits beschrieben werden die Geräte von Patton eingesetzt.
KonfigurationDie Patton Geräte sind von der Konfiguration her den Geräten von Cisco sehr ähnlich. Es gibt eine serielle Konsole, Zugriff per Telnet oder auch per Webfrontend.
Zuerst einmal wäre eine IP Adresse hübsch. Also verbinden wir uns mit dem seriellen Kabel mit dem Gateway und nehmen das Terminalprogramm unseres Vertrauens (GtkTerm, minicom, …). Die Anmeldung geschieht mit Benutzernamen “admin” und Passwort “.”. Damit ist man angemeldet. Nun in den erweiterten Modus -> enable & configure.
IP Adresse und Default Gateway vergeben mit:
context ip router interface LAN ipaddress 10.21.3.122 255.255.255.0 context ip router route 0.0.0.0 0.0.0.0 10.21.3.201 0 port ethernet 0 0 medium auto encapsulation ip bind interface LAN router no shutdown
Damit sollte das Gateway über die LAN Schnittstelle 0/0 über die IP 10.21.3.122 erreichbar sein.
Da das Gerät nun über das Netzwerk erreichbar ist, hat man nun die Qual der Wahl. Hat man die wirklich?? Ja hat man. Es geht prinzipiell alles über CLI als auch über das Webfrontend. Die Grundkonfiguration (oder Template) kann man – sobald ein Beispiel vorhanden ist – in einem Editor anpassen und dann einfach per Copy&Paste im Terminalfenster an das Gateway weitergeben. Das geht um einiges einfacher als sich alles immer über das Webfrontend zusammen zu klicken.
Für andere Dinge (dazu kommen wir noch) ist das Webfrontend allerdings besser geeignet, bzw. einfacher.
Die Überschrift ist komisch – passt aber. Es ist eine eigene Logik die Patton innerhalb der Geräte anwendet. Es gibt verschiedene Contexte die auf verschiedene Interfaces gebunden werden (können) – das ganze läuft über Contexte und Bindings. Da die Konfiguration jedoch – so denke ich – bei fast allen recht gleich ist, sollte ein erklärtes Template den meisten eine gute Ausgangsbasis darstellen. Das gibt es gleich.
Die Logik zum Routen der Calls sieht so aus, dass man alles über 3 “Tabellen” abhandelt (kann man bestimmt auch anders machen). Dadurch ist man flexibel für viele Anforderungen und hat trotzdem einen relativ kompakten überblick über die aktuelle Konfiguration (Nebenstellen <-> Ports). Ein Anruf von “Außen” (über LAN an das Gateway) kommt zuerst einmal in das Routing Table “RT_SIP_IN”. Dort kann beispielsweise die Nummer bearbeitet werden (nur ein Beispiel), bevor es dann weitergeht in die “RT_LOCAL”. Innerhalb der RT_LOCAL wird dann entschieden an welchen Port der Anruf weitergeleitet wird. Dazu wird einfach die Nummer der Nebenstelle direkt auf ein Interface gebunden. Damit ist der Anruf zugestellt.
Ein Anruf von einem analogen Port aus funktioniert vergleichbar. Der Anruf landet zuerst in der Routing Table “RT_FXS_IN”. Dort kann je nach dem die ein oder andere Funktion ausgeführt werden, bevor es dann weitergeht in die RT_LOCAL. Dort wird entschieden wohin der Anruf geht. Würde der Anruf in unserem Beispiel an die 10 gehen, so würde der Anruf innerhalb der RT_LOCAL direkt an das betreffende Interface weitergereicht. Der Anruf würde also innerhalb des Patton Gateways abgehandelt. Trifft jedoch die angerufene Nummer nicht auf eine der lokalen Nebenstellen, so wird der Anruf an den default Eintrag weitergeleitet. Also an das SIP Interface, welches auf ein zentrales Gateway oder auf die Asterisk zeigen sollte. In unserem Beispiel (siehe Season 3) gehen alle Anrufe der analogen Gateways über das zentrale Patton Gateway (E1 Gateway zum PSTN). Dadurch bleiben Analoge Anrufe komplett in der Hand der Gateways (also reine Hardware) ohne dass eine Modulation von Asterisk benötigt wird. Das ist gerade für Faxe oder Modems ein wichtiger Vorteil.
Ich versuche das ganze durch Kommentare möglichst verständlich zu machen
#----------------------------------------------------------------#
# #
# SN4118/JS/EUI #
# R5.8 2011-07-01 H323 SIP FXS FXO #
# 2011-10-31T10:12:08 #
# SN/00A0BA052727 #
# Generated configuration file #
# #
#----------------------------------------------------------------#
cli version 3.20
clock local default-offset +02:00
webserver port 80 language en
# die SNMP Community zum auslesen von Statusinfos
snmp community nagios ro
# die SNMP Community zu schreiben von Werten
# diese ist sehr interessant und wichtig zum sichern der Konfiguration!!
snmp community configsave rw
# nun folgend die Hosts die SNMP abfragen duerfen und welche Rechte sie haben
snmp host 10.10.210.36 security-name monitoring
snmp host 10.10.210.36 security-name configsave
snmp host 10.21.3.10 security-name configsave
# NTP, die richtige Zeit ist wichtig
sntp-client
sntp-client server primary 10.10.210.201 port 123 version 4
# Hostname nicht nur fuer SNMP
system hostname patton03
system location "Schrank 1"
system contact netzwerkmanagement@einefirma.de
system
ic voice 0
low-bitrate-codec g729
profile ppp default
profile tone-set default
profile voip default
codec 1 g711alaw64k rx-length 20 tx-length 20
codec 2 g711ulaw64k rx-length 20 tx-length 20
rtp traffic-class local-default
# folgend nun diverse Einstellungen fuer die Faxuebertragung
# die sich bewaehrt haben
fax transmission 1 relay t38-udp
fax redundancy low-speed 2 high-speed 3
fax dejitter-max-delay 60
fax max-bit-rate 9600
fax detection fax-frames
# gleiches fuer Modems
modem transmission 1 bypass g711alaw64k rx-length 10 tx-length 10
modem dejitter-max-delay 60
no modem detection on-remote-fax-request
profile pstn default
profile ringing-cadence default
play 1 1000
pause 2 4000
profile sip default
no autonomous-transitioning
profile aaa default
method 1 local
method 2 none
context ip router
interface LAN
# IP Adresse des LAN Interfaces
ipaddress 10.21.3.122 255.255.255.0
context ip router
# Routen werden hier gesetzt, folgend die Default Route
route 0.0.0.0 0.0.0.0 10.21.3.201 0
context cs switch
digit-collection timeout 3
# im folgenden werden die Routing Tables definiert
# RT_SIP_IN
routing-table called-e164 RT_SIP_IN
route default dest-table RT_LOCAL
# RT_LOCAL (die zentrale Tabelle)
# inklusive einiger Portzuordnungen
routing-table called-e164 RT_LOCAL
route default dest-interface IF_SIP_patton01
route 334 dest-interface IF_FXS0
route 275 dest-interface IF_FXS1
route 285 dest-interface IF_FXS2
route 311 dest-interface IF_FXS4
route 312 dest-interface IF_FXS5
route 322 dest-interface IF_FXS7
route 270 dest-interface IF_FXS3
# der folgende Service ist eine "Hunt-Group"
# dabei wird auf mehrere verteilt (round-robin like)
route 310 dest-service DFUE_310
# RT_FXS_IN (Verbindung von analog Ports ausgehend)
routing-table called-e164 RT_FXS_IN
route default dest-table RT_LOCAL
route .T dest-table RT_LOCAL
# das Interface zur zentralen Patton
interface sip IF_SIP_patton01
bind context sip-gateway GW_SIP
# die Routing Table auf das Interface binden
route call dest-table RT_SIP_IN
# die IP des Gateways
remote 10.21.3.120
# nun kommen die einzelnen Interfaces
interface fxs IF_FXS0
# binden an die RT_FXS_IN
route call dest-table RT_FXS_IN
caller-id-presentation pre-ring
# die Nummer (fuer CLI)
subscriber-number 334
# und so weiter, fuer jedes Interface
interface fxs IF_FXS1
route call dest-table RT_FXS_IN
subscriber-number 275
interface fxs IF_FXS2
route call dest-table RT_FXS_IN
subscriber-number 285
interface fxs IF_FXS3
route call dest-table RT_FXS_IN
subscriber-number 270
interface fxs IF_FXS4
route call dest-table RT_FXS_IN
subscriber-number 311
interface fxs IF_FXS5
route call dest-table RT_FXS_IN
subscriber-number 312
interface fxs IF_FXS6
route call dest-table RT_FXS_IN
subscriber-number 313
interface fxs IF_FXS7
route call dest-table RT_FXS_IN
subscriber-number 322
# Das ist die Hunt-Group
# es wird nach quasi rr verteilt auf
# zwei Ports (siehe weiter unten)
service hunt-group DFUE_310
cyclic
drop-cause normal-unspecified
drop-cause no-circuit-channel-available
drop-cause network-out-of-order
drop-cause temporary-failure
drop-cause switching-equipment-congestion
drop-cause access-info-discarded
drop-cause circuit-channel-not-available
drop-cause resources-unavailable
drop-cause user-busy
# diese beiden Ports spielen mit
route call 1 dest-interface IF_FXS4
route call 2 dest-interface IF_FXS5
context cs switch
no shutdown
context sip-gateway GW_SIP
# hier wird das lokale interface erstellt, welches
# SIP Pakete entgegen nimmt
interface GW_IF
bind interface LAN context router port 5060
context sip-gateway GW_SIP
no shutdown
port ethernet 0 0
medium auto
encapsulation ip
bind interface LAN router
no shutdown
# nun wird jeder Port konfiguriert
port fxs 0 0
encapsulation cc-fxs
# gebunden wird dieses Interface an den Context IF_FXS0 (s. oben)
bind interface IF_FXS0 switch
# aktivieren
no shutdown
# und so weiter fuer jeden Port
port fxs 0 1
encapsulation cc-fxs
bind interface IF_FXS1 switch
no shutdown
port fxs 0 2
encapsulation cc-fxs
bind interface IF_FXS2 switch
no shutdown
port fxs 0 3
encapsulation cc-fxs
bind interface IF_FXS3 switch
no shutdown
port fxs 0 4
encapsulation cc-fxs
bind interface IF_FXS4 switch
no shutdown
port fxs 0 5
encapsulation cc-fxs
bind interface IF_FXS5 switch
no shutdown
port fxs 0 6
encapsulation cc-fxs
bind interface IF_FXS6 switch
no shutdown
port fxs 0 7
encapsulation cc-fxs
bind interface IF_FXS7 switch
no shutdown
Dieses Konfiguarationsbeispiel sollte für viele Installationen ein guter Anfangspunkt sein. Wenn ihr sie anpasst und einspielt, dann könnt ihr über die Weboberfläche die Routing Tables ganz einfach administrieren (Nummern <-> Ports). Das sollte dann nach der Erstkonfiguration auch das sein, was man so im laufenden Betrieb machen muss.
Übrigens: Um einen Eintrag zu ändern (gleiche Nummer, anderes Interface) legt man einfach einen neuen Eintrag mit der gleichen Nummer an
Die Konfiguationsänderungen müssen – Cisco like – auf der CLI per
copy running-config startup-config
gesichert werden, bzw. auf dem Webinterface über “save”. Ansonsten sind die Änderungen bei einem Neustart verloren.
Was ich Euch hier gezeigt habe ist eine ganz simple Variante der Konfiguration. Man kann mit den Patton Gateways noch sehr viel mehr an Funktionen und allerhand Schweinereien treiben. Ein paar Funktionen werde ich noch zeigen, wenn es an das zentrale Gateway geht.