HAMVOIP: Unterschied zwischen den Versionen

Aus DRC Wiki

KKeine Bearbeitungszusammenfassung
Implementierung vom "Caller ID Name" dokumentiert und Updates beim Dialplan
 
Zeile 1: Zeile 1:


== Allgemeine Infos ==
== Allgemeine Infos ==
Im HAMNET existiert ein VOIP-Verbund auf Basis von Asterisk (teils "bare-bone" Asterisk, teils FreePBX).
Im HAMNET existiert ein VOIP-Verbund auf Basis von Asterisk-Servern (teils "bare-bone" Asterisk, teils FreePBX), welche über DUNDi miteinander im Verbund stehen. Somit ist es möglich, Rufnummern auf anderen Servern zu finden und anzuwählen.


Die Telefonieserver heißen in der HAMNETDB meist ''sip.standort'' und haben auch meist die Beschreibung ''asterisk_dundi'' im Kommentarfeld.
Diese VOIP-Server lassen sich anhand der HAMNETDB finden: sie heißen meist ''sip.standort'' und haben meist die Beschreibung ''asterisk_dundi'' im Kommentarfeld.


Da sich diese Server im Verbund befinden, ist es möglich, auch Rufnummern auf anderen Servern anzuwählen.
=== Rufnummern ===
 
Damit für das Funktionieren des Systems keine zentrale "Nummernvergabestelle" notwendig ist, macht man sich die Tatsache zu nutze, dass jeder Funkamateur ein eindeutiges Rufzeichen besitzt. Die VOIP-Rufnummer leitet sich nun durch "Buchstabenwahl" direkt vom Rufzeichen ab:
=== Rufnummernschema ===
Das Rufnummernschema leitet sich durch "Buchstabenwahl" direkt vom Rufzeichen ab:


* Erste Zahl: ''auf welcher Taste befindet sich der Buchstabe?''
* Erste Zahl: ''auf welcher Taste befindet sich der Buchstabe?''
Zeile 27: Zeile 25:
Sofern das verwendete Telefon eine Tastatur mit Buchstaben hat, ist die "Übersetzung" des Rufzeichens recht intuitiv.
Sofern das verwendete Telefon eine Tastatur mit Buchstaben hat, ist die "Übersetzung" des Rufzeichens recht intuitiv.


=== Dundi-Map Projekt ===
=== DUNDi Network Map Projekt ===
Es gibt ein Projekt, welches versucht, den DUNDi-Verbund im HAMNET mit seinen Peerings auf einer Karte darzustellen.
Es gibt ein Projekt, welches versucht, den DUNDi-Verbund im HAMNET mit seinen Peerings auf einer Karte darzustellen.


Hier findet sich die Übersichtsseite im HAMNET:
Hier findet sich die Übersichtsseite (nur im HAMNET erreichbar):


http://db0bt.hamnet.radio:85/
http://db0bt.hamnet.radio:85/


Um mit einem eigenen Asterisk-Server am Projekt teilzunehmen, muss:
Um mit einem eigenen Asterisk-Server am ''DUNDi Network Map'' Projekt teilzunehmen, muss:


* In der HamnetDB im Kommentar des SIP-Servers '''asterisk_dundi''' stehen
* In der HamnetDB im Kommentar des SIP-Servers '''asterisk_dundi''' stehen
* Auf dem SIP-Server muss ein Webserver laufen, der die beiden Files ''/dundi.info'' und ''/dundi_show_peers.info'' bereitstellt. Das erste File ist statisch, letzteres wird durch einen Cronjob stündlich aktualisiert:
* Auf dem SIP-Server ein Webserver laufen, der die beiden Files ''/dundi.info'' und ''/dundi_show_peers.info'' bereitstellt. Das erste File ist statisch, letzteres wird durch einen Cronjob stündlich aktualisiert:


  ~# cat /var/www/html/dundi.info  
  ~# cat /var/www/html/dundi.info  
Zeile 57: Zeile 55:


=== Verteiltes Telefonverzeichnis ===
=== Verteiltes Telefonverzeichnis ===
Für die internationale Vernetzung via HAMNET hat sich das Modul "DUNDi" etabliert. DUNDi steht für ''Distributed Universal Number Discovery'' und ist vereinfacht gesagt eine Implementierung eines verteilten/Peer-to-Peer basierten Telefonbuchs.
Für die internationale Vernetzung via HAMNET wird das Modul "DUNDi" eingesetzt. DUNDi steht für ''Distributed Universal Number Discovery'' und ist vereinfacht gesagt eine Implementierung eines verteilten/Peer-to-Peer basierten Telefonbuchs.


Der Ablauf eines Verbindungsaufbaus läuft vereinfacht dargestellt folgendermaßen ab:
Der Ablauf eines Verbindungsaufbaus läuft vereinfacht dargestellt folgendermaßen ab:
Zeile 230: Zeile 228:
  exten => _Z.,1,Goto(internal,${EXTEN},1)
  exten => _Z.,1,Goto(internal,${EXTEN},1)


= Anbindung von SVXLINK =
==== Update ====
Svxlink kann durch die Erweiterung SipLogic an einen VOIP-Server angebunden werden.
<blockquote>Mit neueren Asterisk-Versionen fehlt beim Verwenden dieser Konfiguration das Klingelzeichen, wenn man eine Nummer außerhalb des eigenen Servers wählt. Mit der folgenden Änderung im Kontext [internal] funktioniert es korrekt:</blockquote>
[internal]
include => dundiextens
exten => _Z.,1,Dial(PJSIP/${EXTEN})
same => n,Goto(lookupdundi,${EXTEN},1)
same => n,Set(DEST=${DUNDILOOKUP(${EXTEN},priv)})
same => n,GotoIf($["${DEST}"=""]?noresult)
same => n,Dial(${DEST},30,r)
same => n(noresult),NoOp(No DUNDI result found)
same => n,Hangup()
 
=== Rufzeichenanzeige ohne zentrales Telefonbuch ===
Relativ schnell kam die Idee auf, dass es angenehm wäre, die Rufzeichen seiner Verbindungspartner auf dem Display angezeigt zu bekommen.
 
Diese Funktionalität ist vom Prinzip her nichts neues und wurde auch bereits im HAMNET implementiert - jedoch größtenteils durch LDAP oder andere zentralisierte Verzeichnissysteme.
 
Diese haben jedoch einige Nachteile:
 
* Viele verschiedene Standards (LDAP, XML-Basierte, etc.), teils Herstellerspezifisch
* Konfigurationsaufwand auf Benutzerseite
* Nicht zwingend von allen Endgeräten unterstützt
* Hängen meistens von einem zentralen Server ab, welcher das Verzeichnis bereitstellt
 
Bei unserem Server IR3UHF am Rittnerhorn wurde hingegen ein etwas anderer Ansatz implementiert, welcher dem ''Talker Alias'' in DMR-Netzen ähnelt:
 
* "In-Band" Rufzeichenübertragung direkt als "''Caller ID Name"'' im SIP Protokoll
* Keine Konfiguration beim Benutzer notwendig
* Benutzer kann den eigenen Anzeigenamen auf Wunsch verändern
* Wenn der Benutzer nichts angibt (z.B. wenn das Endgerät/Softphone dies nicht unterstützt) setzt der Server das Rufzeichen als Anzeigename. Dieses lässt sich direkt von der Rufnummer "Rückwärts-Auflösen"
 
Kurz gesagt erlaubt diese Konfiguration also, dass ohne weiteres Zutun des Benutzers das Rufzeichen des Anrufers auf dem Telefon erscheint - ohne dass dafür ein zentraler Server notwendig ist.
 
Technisch realisiert ist diese Funktionalität über ein Python-Skript, welches über AGI (Asterisk Gateway Interface) eingebunden wird. Der Aufruf erfolgt direkt über den Dialplan (''/etc/asterisk/extensions.conf'') im Abschnitt [internal].
 
Zum Testen wurde außerdem die (nur lokal erreichbare) Nummer '''''4331''' (ID)'' konfiguriert, welche dem Benutzer seinen aktuellen Anzeigenamen buchstabiert.
 
Bei IR3UHF sieht der [internal]-Kontext nun folgendermaßen aus:
[internal]
include => dundiextens
; 4331 (ID): Anzeigename buchstabieren
exten => 4331,1,NoOp(Buchstabiere CallerID Name)
same => n,AGI(name.py)
same => n,Answer()
same => n,wait(1)
same => n,GotoIf($["${CALLERID(name)}" = ""]?noname)
same => n,Playback(vm-from)
same => n,SayPhonetic(${CALLERID(name)})
same => n,wait(1)
same => n,Hangup()
; Wenn kein CallerID Name vorhanden
exten => 4331,n(noname),Playback(from-unknown-caller)
same => n,Hangup()
exten => _Z.,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN})
same => n,AGI(name.py)
same => n,Dial(PJSIP/${EXTEN})
;same => n,Goto(lookupdundi,${EXTEN},1)
same => n,Set(DEST=${DUNDILOOKUP(${EXTEN},priv)})
same => n,GotoIf($["${DEST}"=""]?noresult)
same => n,Dial(${DEST},30,r)
same => n(noresult),NoOp(No DUNDI result found)
same => n,Hangup()
Das Skript ''name.py'' selbst liegt in ''/usr/share/asterisk/agi-bin/'' und sieht folgendermaßen aus:
#!/usr/bin/env python3
##
#
# Dieses Skript wird aus der extensions.conf aufgerufen und
# setzt die CallerID des Anrufers auf das Rufzeichen,
# sofern vom Benutzer keine CallerID gesendet wird.
#
# Zur Kontrolle werden Statusmeldungen in stdout ausgegeben
# Diese sieht man im systemd journal
# Alternativ kann zum debuggen in der Asterisk CLI
# 'agi set debug on' aktiviert werden
#
##
import sys
lookup = {
    "21" : 'A',
    "22": 'B',
    "23": 'C',
    "31" : 'D',
    "32" : 'E',
    "33" : 'F',
    "41" : 'G',
    "42" : 'H',
    "43" : 'I',
    "51" : 'J',
    "52" : 'K',
    "53" : 'L',
    "61" : 'M',
    "62" : 'N',
    "63" : 'O',
    "71" : 'P',
    "72" : 'Q',
    "73" : 'R',
    "74" : 'S',
    "81" : 'T',
    "82" : 'U',
    "83" : 'V',
    "91" : 'W',
    "92" : 'X',
    "93" : 'Y',
    "94" : 'Z',
    "10" : '1',
    "20" : '2',
    "30" : '3',
    "40" : '4',
    "50" : '5',
    "60" : '6',
    "70" : '7',
    "80" : '8',
    "90" : '9',
    "00" : '0'
}
def getCallsign(number: str) -> str:
    # split number in chunks of two digits
    letters = [number[i:i+2] for i in range(0, len(number), 2)]
    callsign = ""
    for l in letters:
        callsign += lookup.get(l,"")
    return callsign
def send_agi_command(cmd):
    sys.stdout.write(f"{cmd}\n")
    sys.stdout.flush()
    # lese die "200" response
    return sys.stdin.readline()
# AGI header einlesen
agi_env = {}
while True:
    line = sys.stdin.readline().strip()
    if line == "":
        break
    key, value = line.split(":", 1)
    agi_env[key.strip()] = value.strip()
# Rufnummer des Anrufers
number = agi_env.get("agi_callerid", "")
# Caller ID Name ds Anrufers
callername = agi_env.get("agi_calleridname","")
# Rufnummer des Angerufenen
called = agi_env.get("agi_dnid","")
# Rufzeichen des Angerufenen
calledname = getCallsign(called)
sys.stderr.write(f"Anruf von {number}, eingehende Callerid {callername}\n")
sys.stderr.write(f"Anruf an {called}, Kontakt {calledname}\n")
sys.stderr.flush()
# Setze das Rufzeichen der gewaehlten Nummer, damit dieses auf dem Telefon
# des Anrufers angezeigt wird
send_agi_command(f'SET VARIABLE CONNECTEDLINE(name,i) "{calledname}"')
if callername in ["","unknown"]:
    # keine Caller ID, setze Rufzeichen
    callername = getCallsign(number)
    sys.stderr.write(f"Setze CallerID {callername}\n");
    sys.stderr.flush()
    # Asterisk-Variable setzen
    send_agi_command(f'SET VARIABLE CALLERID(name) "{callername}"')
Wie man sieht, wird nicht nur der Name des Anrufers vom Server gesetzt, sondern auch der Name des Angerufenen. Dies hat bei einigen VOIP-Telefonen - z.B. einem Snom D715 - den Effekt, dass bei ausgehenden Anrufen das Rufzeichen im Display angezeigt wird, sobald das Klingelzeichen empfangen wird.
 
== Anbindung von SVXLINK ==
Svxlink kann durch die Erweiterung ''SipLogic'' an einen VOIP-Server angebunden werden.


Dazu braucht es:
Dazu braucht es:
Zeile 238: Zeile 407:
* Svxlink von Source kompiliert, mit der Option <code>-DWITH_CONTRIB_SIP_LOGIC=ON</code>
* Svxlink von Source kompiliert, mit der Option <code>-DWITH_CONTRIB_SIP_LOGIC=ON</code>


== PJPROJECT und Svxlink Kompilieren ==
=== PJPROJECT und Svxlink Kompilieren ===
Damit PJPROJECT kompiliert, muss der Configure-Befehl lt. Doku mit der Option <code>-fPIC</code> ausgeführt werden. Auf unseren Relais habe ich mit dem folgenden Configure-Befehl erfolgreich kompiliert:
Damit PJPROJECT kompiliert, muss der Configure-Befehl lt. Doku mit der Option <code>-fPIC</code> ausgeführt werden. Auf unseren Relais habe ich mit dem folgenden Configure-Befehl erfolgreich kompiliert:


Zeile 249: Zeile 418:
Danach kann SVXLINK vorbereitet werden. Am besten mit den selben cmake Flags wie immer, aber zusätzlich mit <code>-DWITH_CONTRIB_SIP_LOGIC=ON</code>
Danach kann SVXLINK vorbereitet werden. Am besten mit den selben cmake Flags wie immer, aber zusätzlich mit <code>-DWITH_CONTRIB_SIP_LOGIC=ON</code>


== SVXLINK Konfiguration ==
=== SVXLINK Konfiguration ===
Sämtliche Konfigurationen der SIP-Logic befinden sich in /etc/svxlink/svxlink.d/SipLogic.conf.
Sämtliche Konfigurationen der SIP-Logic befinden sich in /etc/svxlink/svxlink.d/SipLogic.conf.



Aktuelle Version vom 29. Dezember 2025, 15:22 Uhr

Allgemeine Infos

Im HAMNET existiert ein VOIP-Verbund auf Basis von Asterisk-Servern (teils "bare-bone" Asterisk, teils FreePBX), welche über DUNDi miteinander im Verbund stehen. Somit ist es möglich, Rufnummern auf anderen Servern zu finden und anzuwählen.

Diese VOIP-Server lassen sich anhand der HAMNETDB finden: sie heißen meist sip.standort und haben meist die Beschreibung asterisk_dundi im Kommentarfeld.

Rufnummern

Damit für das Funktionieren des Systems keine zentrale "Nummernvergabestelle" notwendig ist, macht man sich die Tatsache zu nutze, dass jeder Funkamateur ein eindeutiges Rufzeichen besitzt. Die VOIP-Rufnummer leitet sich nun durch "Buchstabenwahl" direkt vom Rufzeichen ab:

  • Erste Zahl: auf welcher Taste befindet sich der Buchstabe?
  • zweite Zahl: der wievielte Buchstabe auf der Taste?
    • für die Ziffer selbst ist die zweite Zahl die 0

Beispiel: IN3FQQ wird zu:

  • I => 4 3 (dritter Buchstabe auf der Taste 4)
  • N => 6 2
  • 3 => 3 0 (die Ziffer selbst auf der Taste 3)
  • F => 3 3
  • Q => 7 2
  • Q => 7 2

Die gesamte Nummer für IN3FQQ lautet also: 43 62 30 33 72 72

Sofern das verwendete Telefon eine Tastatur mit Buchstaben hat, ist die "Übersetzung" des Rufzeichens recht intuitiv.

DUNDi Network Map Projekt

Es gibt ein Projekt, welches versucht, den DUNDi-Verbund im HAMNET mit seinen Peerings auf einer Karte darzustellen.

Hier findet sich die Übersichtsseite (nur im HAMNET erreichbar):

http://db0bt.hamnet.radio:85/

Um mit einem eigenen Asterisk-Server am DUNDi Network Map Projekt teilzunehmen, muss:

  • In der HamnetDB im Kommentar des SIP-Servers asterisk_dundi stehen
  • Auf dem SIP-Server ein Webserver laufen, der die beiden Files /dundi.info und /dundi_show_peers.info bereitstellt. Das erste File ist statisch, letzteres wird durch einen Cronjob stündlich aktualisiert:
~# cat /var/www/html/dundi.info 
#Rufzeichen;IP des Asterisk-Servers;MAC des Asterisk-Servers;Names des Betreibers;Rufzeichen des Betreibers;Email des Betreibers
IR3UHF;44.169.1.29;ee:19:2c:04:d9:fe;Simon;IN3FQQ;tech@drc.bz

~# cat /etc/cron.hourly/dundistatus
#!/bin/sh
asterisk -r -x 'dundi show peers' > /var/www/html/dundi_show_peers.info

Technische Hintergründe

Funktionsweise

Als Basis verwenden die Server jeweils die Software asterisk. Manche verwenden die Distribution FreePBX, welche mit einer Weboberfläche und einer ganzen Reihe an Erweiterungen kommt.

Auch das "HamServerPI"-Image, das in DL recht verbreitet ist, hat unter anderem Asterisk mit eingebaut. Auch dieses verwendet FreePBX, welcher standardmäßig auf Port 81 mit einer Weboberfläche antwortet.

Unser Server sip.ir3uhf auf dem Rittnerhorn hingegen ist eher minimalistisch aufgebaut: es läuft ein asterisk "standalone" auf einem Debian LXC Container.

Verteiltes Telefonverzeichnis

Für die internationale Vernetzung via HAMNET wird das Modul "DUNDi" eingesetzt. DUNDi steht für Distributed Universal Number Discovery und ist vereinfacht gesagt eine Implementierung eines verteilten/Peer-to-Peer basierten Telefonbuchs.

Der Ablauf eines Verbindungsaufbaus läuft vereinfacht dargestellt folgendermaßen ab:

  1. Ist die Telefonnummer am lokalen Server registriert, dann kann diese natürlich direkt erreicht werden.
  2. Falls nicht, werden die DUNDi-Peers befragt, ob irgendjemand diese Nummer kennt.
  3. Falls ein Server die Nummer kennt, wird mitgeschickt, wie man diesen Server erreichen kann.
  4. Der eigene Server baut eine Verbindung zum Zielserver auf
  5. Das Telefon des gewählten Verbindungspartners klingelt.

Zuerst einmal das einfachere Negativbeispiel anhand einer fiktiven Nummer 12345 welche nicht existiert:

In diesem Fall sieht die Antwort folgendermaßen aus:

vdrcsip01*CLI> dundi lookup 12345@priv
DUNDi lookup returned no results.
DUNDi lookup completed in 532 ms

Sollte diese Nummer also auch auf dem lokalen Server nicht bekannt sein, ist in diesem Fall nichts mehr zu machen. Die Verbindung kann nicht aufgebaut werden und das Telefon signalisiert dies dem Benutzer.

Im folgenden Beispiel wird nun nach der Nummer des DL-Rundspruchs 3122007431213153 gesucht.

Diese ist existent und es sollte folglich ein Server im Verbund gefunden werden, der diese Nummer kennt:

vdrcsip01*CLI> dundi lookup 3122007431213153@priv
  1.     0 IAX2/iaxuser:##secret##@44.149.166.36/3122007431213153 (EXISTS)
     from 62:ec:e3:5a:57:7f, expires in 50 s
DUNDi lookup completed in 1668 ms

In diesem Fall konnte der Server ermittelt werden, welcher diese Nummer aktuell aufliegen hat. Konkret handelt es sich im Beispiel um den Server 44.149.166.36 (sip.db0sda) bei DB0SDA in Aachen.

Dabei ist hervorzuheben, dass es nicht zwingend notwendig ist, zu diesem Server ein direktes DUNDi-Peering zu haben.

Jeder Peer kann nämlich von sich aus bei einem seiner eigenen Peers weiterfragen, falls er die Information nicht selbst kennt.

Das DUNDi-Netzwerk muss somit also nicht zwingend ein Full-Mesh sein. Es reicht jemanden zu kennen, der einen kennt, der die Nummer aufliegen hat. Die Rekursionstiefe kann dabei in der Konfigurationsdatei angegeben werden.

Außerdem müssen keine IAX-Trunks oder ähnliches statisch konfiguriert werden.


Wie man aus der Antwort des DUNDi-Lookups erkennen kann, wird beim Auffinden einer Telefonnummer die Information mitgeliefert, wie man diese Nummer erreichen kann. Diese besteht aus den folgenden Teilen:

  • Technologie/Protokoll, in diesem fall IAX2
  • Benutzername und temporäres Passwort für die Verbindung
  • IP Adresse des Zielservers
  • die Anschlussnummer
  • die ID des DUNDi-Peers, von dem die Information empfangen wurde

Im HAMNET VOIP Verbund wird die Verbindung zum Zielserver in den meisten Fällen über das IAX2-Protokoll realisiert.

Die entsprechenden Zugangsdaten für die Verbindung werden in der DUNDi-Antwort mitgeliefert, wobei das Passwort mehrmals täglich rotiert.

In vielen HAMNET VOIP Beispielen wird in der dundi.conf ein secret angegeben. Dieses ist vermutlich ein Relikt aus vergangenen Zeiten.

In der DUNDi-Dokumentation ist diese Konfigurationsvariable nicht mehr dokumentiert und effektiv funktionieren die Peerings im HAMNET auch ohne ein Secret anzugeben

Konfiguration

Im Folgenden wird eine funktionierende Basiskonfiguration geschildert, so wie sie auch bei sip.ir3uhf verwendet wird.

sip.conf / pjsip.conf

[global]
regcontext=dundiextens

[transport-udp]
type=transport
protocol=udp    ;udp,tcp,tls,ws,wss
bind=0.0.0.0

Die regcontext Option legt fuer jeden angemeldeten Benutzer dynamisch einen Eintrag im angegebenen Dialplan-Kontext dundiextens an und entfernt ihn wieder, sobald sich ein Benutzer/Telefon abmeldet.

Die hier eingetragenen Nummern sind jene, die spaeter von DUNDi announced werden.

Die Benutzeraccounts selbst werden in unserem Fall über die Konfigurationsdatei pjsip_wizard.conf anhand der Wizard-Funktion von PJSIP angelegt.

Alternativ kann das auch anhand einer Datenbank gemacht werden, welche man dann ggf. auch auf weitere Standorte replizieren kann, um den Benutzern auf weiteren Servern in der Region einen Login mit denselben Zugangsdaten zu erlauben.

dundi.conf

;
; DUNDi configuration file
;
; For more information about DUNDi, see http://www.dundi.com
;
;
[general]
department=DRC
organization=Dolomites Radio Club
locality=Bozen
country=IT
email=drc@drc.bz
ttl=2
cachetime=50
autokill=yes
; eigene ID, normalerweise die eigene MAC Adresse.
; diese geben wir explizit an, damit nach etwaigem Serverumzug die ID gleichbleibt
entityid=ab:cd:ef:ab:cd:ef  

[mappings]

priv => dundiextens,0,IAX2,iaxuser:${SECRET}@44.169.1.29/${NUMBER},nopartial


;; konfiguration eines DUNDi-Peers
[aa:bb:cc:dd:ee:ff]
; ip adresse oder hostname des Peers
host=sip.standort.hamnet.radio
include=priv
model=symmetric
order=primary
permit=priv
qualify=yes

In der dundi.conf werden nun die eigenen Details (department, organization etc. sind im Grunde nicht notwendig) konfiguriert.

Wichtig sind die Optionen ttl, cachetime sowie autokill und entityid.

Die entityid wird - sofern nicht explizit angegeben - von der eigenen MAC-Adresse abgeleitet. Da diese aber bei allen Peers angegeben werden muss, empfiehlt es sich die ID explizit in der Konfigurationsdatei anzugeben, damit diese im Falle eines Server-Umzugs beibehalten werden kann.

Die option ttl beschreibt, wieviele Hops der DUNDi-Lookup "weitergereicht" werden darf. Der wert 2 bedeutet also, dass alle angeschlossenen DUNDi-Peers auch noch ihre eigenen Peers befragen dürfen. Danach wird nicht mehr weitergefragt.

Die cachetime gibt an, für wieviele Sekunden eine empfangene Antwort zwischengespeichert werden soll. Sofern nicht angegeben ist der Standardwert 3600 Sekunden (1 Stunde), was für den Einsatzzweck im HAMNET natürlich nicht sinnvoll wäre.

In unserem Fall setzen wir die cachetime auf einen wesentlich kürzeren Wert.

autokill definiert, dass Anfragen an nicht antwortende Peers nach standardmäßig 2sec abgebrochen und der Peer als offline markiert wird.

Der wichtigste Teil der Konfiguration befindet sich jedoch im Bereich [mappings].

Hier wird definiert, welche Nummern vom eigenen Server in den DUNDi-Verbund announced werden.

Die Konfigurationszeile ordnet dem DUNDi-Kontext priv den internen Kontext dundiextens zu. Dieser enthält (siehe vorhergehende pjsip.conf Konfiguration) die Telefonnummern aller aktuell am eigenen Server angemeldeten Benutzer/Telefone.

Die darauffolgende Zahl 0 gibt an, dass die Nummern direkt an diesem Server aufliegen.

Abschließend wird der Verbindungsstring angegeben, welcher den anfragenden DUNDi-Peers in der Antwort zurückgeschickt wird.

Die Option nopartial beschreibt, dass der Server nur für exakt übereinstimmende Nummern und nicht für partielle übereinstimmungen antworten soll.

iax.conf

Nach dem Auffinden des Zielservers über ein DUNDi-Lookup kann eine Verbindung zu diesem hergestellt werden.

Dafür wird das IAX2-Protokoll verwendet.

Die Datei iax.conf definiert die Einzelheiten des Protokolls. Besonders wichtig dabei ist das Anlegen des Benutzers iaxuser für die einkommenden Verbindungen aus dem Verbund.

Für die Wahl des Passwortes wird dbsecret=dundi/secret angegeben. Damit wird ein temporäres Passwort referenziert, welches von DUNDi automatisch generiert und mehrfach täglich rotiert wird.

Die eingehenden Verbindungen werden in den Dialplan-Kontext incomingdundi geleitet.

Im Abschnitt [general] sind die erlaubten Codecs definiert, welche für eingehende und ausgehende Verbindungen von/in den Verbund verwendet werden dürfen.

[general]
nochecksums=no
bandwidth=low
disallow=all
allow=g722
;allow=ulaw
allow=alaw
allow=gsm                      
;jitterbuffer=no
jitterbuffer=yes

[iaxuser]
type=friend
dbsecret=dundi/secret
context=incomingdundi 

extensions.conf

[lookupdundi]
switch => DUNDi/priv

[internal]
include => dundiextens

exten => _Z.,1,Dial(PJSIP/${EXTEN})
same => n,Goto(lookupdundi,${EXTEN},1)
same => n,Hangup()

[incomingdundi]
exten => _Z.,1,Goto(internal,${EXTEN},1)

Update

Mit neueren Asterisk-Versionen fehlt beim Verwenden dieser Konfiguration das Klingelzeichen, wenn man eine Nummer außerhalb des eigenen Servers wählt. Mit der folgenden Änderung im Kontext [internal] funktioniert es korrekt:

[internal]
include => dundiextens

exten => _Z.,1,Dial(PJSIP/${EXTEN})
same => n,Goto(lookupdundi,${EXTEN},1)
same => n,Set(DEST=${DUNDILOOKUP(${EXTEN},priv)})
same => n,GotoIf($["${DEST}"=""]?noresult)
same => n,Dial(${DEST},30,r)
same => n(noresult),NoOp(No DUNDI result found)
same => n,Hangup()

Rufzeichenanzeige ohne zentrales Telefonbuch

Relativ schnell kam die Idee auf, dass es angenehm wäre, die Rufzeichen seiner Verbindungspartner auf dem Display angezeigt zu bekommen.

Diese Funktionalität ist vom Prinzip her nichts neues und wurde auch bereits im HAMNET implementiert - jedoch größtenteils durch LDAP oder andere zentralisierte Verzeichnissysteme.

Diese haben jedoch einige Nachteile:

  • Viele verschiedene Standards (LDAP, XML-Basierte, etc.), teils Herstellerspezifisch
  • Konfigurationsaufwand auf Benutzerseite
  • Nicht zwingend von allen Endgeräten unterstützt
  • Hängen meistens von einem zentralen Server ab, welcher das Verzeichnis bereitstellt

Bei unserem Server IR3UHF am Rittnerhorn wurde hingegen ein etwas anderer Ansatz implementiert, welcher dem Talker Alias in DMR-Netzen ähnelt:

  • "In-Band" Rufzeichenübertragung direkt als "Caller ID Name" im SIP Protokoll
  • Keine Konfiguration beim Benutzer notwendig
  • Benutzer kann den eigenen Anzeigenamen auf Wunsch verändern
  • Wenn der Benutzer nichts angibt (z.B. wenn das Endgerät/Softphone dies nicht unterstützt) setzt der Server das Rufzeichen als Anzeigename. Dieses lässt sich direkt von der Rufnummer "Rückwärts-Auflösen"

Kurz gesagt erlaubt diese Konfiguration also, dass ohne weiteres Zutun des Benutzers das Rufzeichen des Anrufers auf dem Telefon erscheint - ohne dass dafür ein zentraler Server notwendig ist.

Technisch realisiert ist diese Funktionalität über ein Python-Skript, welches über AGI (Asterisk Gateway Interface) eingebunden wird. Der Aufruf erfolgt direkt über den Dialplan (/etc/asterisk/extensions.conf) im Abschnitt [internal].

Zum Testen wurde außerdem die (nur lokal erreichbare) Nummer 4331 (ID) konfiguriert, welche dem Benutzer seinen aktuellen Anzeigenamen buchstabiert.

Bei IR3UHF sieht der [internal]-Kontext nun folgendermaßen aus:

[internal]
include => dundiextens

; 4331 (ID): Anzeigename buchstabieren
exten => 4331,1,NoOp(Buchstabiere CallerID Name)
same => n,AGI(name.py)
same => n,Answer()
same => n,wait(1)
same => n,GotoIf($["${CALLERID(name)}" = ""]?noname)
same => n,Playback(vm-from)
same => n,SayPhonetic(${CALLERID(name)})
same => n,wait(1)
same => n,Hangup()
; Wenn kein CallerID Name vorhanden
exten => 4331,n(noname),Playback(from-unknown-caller)
same => n,Hangup()

exten => _Z.,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN})
same => n,AGI(name.py)
same => n,Dial(PJSIP/${EXTEN})
;same => n,Goto(lookupdundi,${EXTEN},1)
same => n,Set(DEST=${DUNDILOOKUP(${EXTEN},priv)})
same => n,GotoIf($["${DEST}"=""]?noresult)
same => n,Dial(${DEST},30,r)
same => n(noresult),NoOp(No DUNDI result found)
same => n,Hangup()

Das Skript name.py selbst liegt in /usr/share/asterisk/agi-bin/ und sieht folgendermaßen aus:

#!/usr/bin/env python3

##
#
# Dieses Skript wird aus der extensions.conf aufgerufen und
# setzt die CallerID des Anrufers auf das Rufzeichen,
# sofern vom Benutzer keine CallerID gesendet wird.
#
# Zur Kontrolle werden Statusmeldungen in stdout ausgegeben
# Diese sieht man im systemd journal
# Alternativ kann zum debuggen in der Asterisk CLI
# 'agi set debug on' aktiviert werden
#
##

import sys

lookup = {
    "21" : 'A',
    "22": 'B',
    "23": 'C',
    "31" : 'D',
    "32" : 'E',
    "33" : 'F',
    "41" : 'G',
    "42" : 'H',
    "43" : 'I',
    "51" : 'J',
    "52" : 'K',
    "53" : 'L',
    "61" : 'M',
    "62" : 'N',
    "63" : 'O',
    "71" : 'P',
    "72" : 'Q',
    "73" : 'R',
    "74" : 'S',
    "81" : 'T',
    "82" : 'U',
    "83" : 'V',
    "91" : 'W',
    "92" : 'X',
    "93" : 'Y',
    "94" : 'Z',
    "10" : '1',
    "20" : '2',
    "30" : '3',
    "40" : '4',
    "50" : '5',
    "60" : '6',
    "70" : '7',
    "80" : '8',
    "90" : '9',
    "00" : '0' 
}
def getCallsign(number: str) -> str:
    # split number in chunks of two digits
    letters = [number[i:i+2] for i in range(0, len(number), 2)]
    callsign = ""
    for l in letters:
        callsign += lookup.get(l,"")
    return callsign

def send_agi_command(cmd):
    sys.stdout.write(f"{cmd}\n")
    sys.stdout.flush()
    # lese die "200" response
    return sys.stdin.readline()

# AGI header einlesen
agi_env = {}
while True:
    line = sys.stdin.readline().strip()
    if line == "":
        break
    key, value = line.split(":", 1)
    agi_env[key.strip()] = value.strip()


# Rufnummer des Anrufers
number = agi_env.get("agi_callerid", "")
# Caller ID Name ds Anrufers
callername = agi_env.get("agi_calleridname","")
# Rufnummer des Angerufenen
called = agi_env.get("agi_dnid","")
# Rufzeichen des Angerufenen
calledname = getCallsign(called)

sys.stderr.write(f"Anruf von {number}, eingehende Callerid {callername}\n")
sys.stderr.write(f"Anruf an {called}, Kontakt {calledname}\n")
sys.stderr.flush()

# Setze das Rufzeichen der gewaehlten Nummer, damit dieses auf dem Telefon
# des Anrufers angezeigt wird
send_agi_command(f'SET VARIABLE CONNECTEDLINE(name,i) "{calledname}"')

if callername in ["","unknown"]:
    # keine Caller ID, setze Rufzeichen 
    callername = getCallsign(number)
    sys.stderr.write(f"Setze CallerID {callername}\n");
    sys.stderr.flush()
    # Asterisk-Variable setzen
    send_agi_command(f'SET VARIABLE CALLERID(name) "{callername}"')

Wie man sieht, wird nicht nur der Name des Anrufers vom Server gesetzt, sondern auch der Name des Angerufenen. Dies hat bei einigen VOIP-Telefonen - z.B. einem Snom D715 - den Effekt, dass bei ausgehenden Anrufen das Rufzeichen im Display angezeigt wird, sobald das Klingelzeichen empfangen wird.

Anbindung von SVXLINK

Svxlink kann durch die Erweiterung SipLogic an einen VOIP-Server angebunden werden.

Dazu braucht es:

  • PJPROJECT
  • Svxlink von Source kompiliert, mit der Option -DWITH_CONTRIB_SIP_LOGIC=ON

PJPROJECT und Svxlink Kompilieren

Damit PJPROJECT kompiliert, muss der Configure-Befehl lt. Doku mit der Option -fPIC ausgeführt werden. Auf unseren Relais habe ich mit dem folgenden Configure-Befehl erfolgreich kompiliert:

./configure --disable-libwebrtc --disable-video CPPFLAGS=-fPIC CXXFLAGS=-fPIC CFLAGS=-fPIC

Danach

make dep
make
make install

Danach kann SVXLINK vorbereitet werden. Am besten mit den selben cmake Flags wie immer, aber zusätzlich mit -DWITH_CONTRIB_SIP_LOGIC=ON

SVXLINK Konfiguration

Sämtliche Konfigurationen der SIP-Logic befinden sich in /etc/svxlink/svxlink.d/SipLogic.conf.

Die SipLogic hat auch eine eigene TCL-Logik, und zwar /usr/share/svxlink/events.d/SipLogic.tcl