2023-01-05

Run yourself a local telephone network with Asterisk and NixOS

This is just a short post explaining how the phone network used by the VOC at 22f3 worked.

Note: this post is provided “as is”, with no assurance of correctness of any kind. Be aware that it was written by someone who, three weeks ago, didn’t know a thing about how any of this stuff worked, and that most of it is the result of a single late-night config-file hacking session. It’s meant to be notes for myself should I need such a setup again as much as it’s meant to be an explanation for others.

Why?

Traditionally, there is a lot of DECT on Congress, run by eventphone / the POC and used by all kinds of beings for all sorts of things. But 22f3 was not Congress (nor was it trying to be); there was no visitor-facing infrastructure for calling each other, and if people in the event’s orga had to talk to someone not in the same room, they mostly used old-school walkie-talkies, which also get the job done (but don’t need any physical infrastructure at the location, and so could be used from early stages of buildup right through the event to the end of teardown).

But seen from the VOC, walkie-talkies have a couple disadvantages:

  • even if you turn off all their beeping, walkie-talkies are noisy, which is not a good thing to be if you are trying to be quiet while recording a talk.
  • their sound quality tends to be bad, and especially when people try to talk quietly, understanding each other is difficult
  • this maybe could’ve been mitigated by using earpieces, but we had none
  • they generally cause anxiety, especially if there’s neurodivergent people around (hi!)

Yet since a surprising room plan change the week before the event meant that the lecture halls and backoffice were all pretty far apart, we needed some way of contacting everything that was quicker than running up and down four flights of stairs.

So, phones.

Overview

The standard c3voc room case already contains a snom IP telephone, so we had one for each of the lecture halls where we had a recording setup. I also got two more cheaply (10€) from Ebay Klein­anzeigen.

The VOC’s main use case for these is that if something breaks in a lecture hall and the people there don’t know how to fix it, they can call our backoffice for help.

Less important but still nice to have is the reverse direction: if the backoffice notices that something is off, it can also call the lecture halls. The ring tone on the snoms can be set to a very quiet “ding”-sound — and while we never actually had to call a lecture hall during a talk, it’s still nice to know you could do so if necessary without causing further disturbance.

A telephone server

We used asterisk running on a NixOS machine as an SIP server to let the phones talk to each other.

Asterisk is — in both good and confusing — software that was conceived in the 90ies, so it expects lots of config files in a self-defined format under /etc, and also while using it you can occasionally smell the C. It comes with exhaustive documentation, which is frequently helpful, but in some corners itself notes that it’s partially incomplete, unwritten, or that some passages have probably fallen out of date.

Luckily, NixOS comes with a corresponding module, and the following is enough to start it:

{ config, lib, pkgs, ... }:
{
  services.asterisk = {
    enable = true;
    confFiles = {
      # config files go here
    };
  };
  # we had a sepearte VLAN for this, so *shrug*
  # makes things easier if I don't have to keep track of ports
  networking.firewall.enable = false;
}

If you add the snippet above to your configuration and switch to it asterisk won’t do much yet, but it also won’t fail on startup because NixOS gives it a couple default config files. It will still try to load a lot of unconfigured modules which fail immediately, but these errors are non-fatal and can be safely ignored.

One thing to note: Asterisk can live-reload its own config, and to avoid breaking things NixOS won’t automatically restart asterisk when doing nixos-rebuild switch. This is very sensible, but it does also mean you shouldn’t forget to manually restart asterisk or make it reload its config before wondering why a deploy didn’t do anything.

What’s a phone call?

Asterisk is an amazingly powerful piece of software, and can apparently do everything from interacting with actual, analog telephone hardware to shepherding WebRTC sessions. Unfortunately this generality also means it has no simple, high-level concept of what a “phone call” is.

It does know things called “channels” and “bridges”. I recommend reading the Asterisk Architecture section of the “Getting Started”-Guide before attempting to do anything with it.

PJSIP

Asterisk has (at least) two ways of interacting with SIP: the older chan_sip module, and the newer res_pjsip. The latter is recommended, but although the former is deprecated and will be removed in a future version, often the wiki’s examples still uses the former and only informally note what would change with PJSIP.

So here’s an annotated pjsip.conf example, patched together from the wiki:

; we use UDP for transport
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0

; Note: this defines a macro, to shorten the config further down
[endpoint_internal](!)
type=endpoint
context=from-internal
disallow=all
allow=ulaw

[auth_userpass](!)
type=auth
auth_type=userpass

[aor_dynamic](!)
type=aor
max_contacts=1


; here come the definitions for our phones, using the macros from above

; lecture hall 1
[saal1](endpoint_internal)
auth=saal1
aors=saal1
[saal1](auth_userpass)
; well, maybe set a better password than this
password=saal1
username=saal1
[saal1](aor_dynamic)

; lecture hall 2
[saal2](endpoint_internal)
auth=saal2
aors=saal2
[saal2](auth_userpass)
password=saal2
username=saal2
[saal2](aor_dynamic)

[backoffice](endpoint_internal)
auth=backoffice
aors=backoffice
[backoffice](auth_userpass)
password=backoffice
username=backoffice
[backoffice](aor_dynamic)

Note that a single phone usually consists of (at least) three things:

endpoint:
defines the “SIP account” and references the other two.
auth:
defines the authentication method, which here is just a password stored in plaintext.
aor:

defines how the server ought to reach the phone. Without further config, this is done dynamically—SIP clients register themselves when they start up, and the server remembers their IP address. But we could also just set a static IP here (useful if you can’t get a SIP client to register correctly — e.g. I didn’t get linphone to work except with static addresses), or do any number of other things.

A phone doesn’t have to have an aor associated with it — but without one, you have a phone that can only place calls, not receive any.

Extensions

This is all nice and fine, but so far we’ve not seen any phone numbers! These go into the extensions.conf config file, and define how asterisk should create bridges between the channels that PJSIP provides:

[from-internal]
; dial the lecture rooms & backoffice
; the syntax is NUMBER,SEQUENCE,FUNCTION
; to call someone do Dial(MODULE/account, timeout)
exten => 1001,1,Dial(PJSIP/saal1,20)
exten => 1002,1,Dial(PJSIP/saal2,20)
exten => 1600,1,Dial(PJSIP/backoffice,20)

; Dial 100 for "hello, world"
; this is useful when configuring/debugging clients (snoms)
exten => 100,1,Answer()
same  =>     n,Wait(1)
same  =>     n,Playback(hello-world)
same  =>     n,Hangup()
; note: "n" is a keyword meaning "the last line's value, plus 1"
; "same" is a keyword referring to the last-defined extension

That’s all we need, and now we can connect our phones!

Snom Snom

These snoms (we had one snom 300 and three snom 320) are suprisingly comfortable, no-fuss devices (though if you’ve bought yours used, you might have to figure out how to reset them first — for some reason, their manual doesn’t mention this). They can also run on PoE, which is especially useful if you’ve run out of power adapters.

On startup, they will display their own IP address (or, of unconfigured, ask you if you want them to use DHCP).

After that it’s easiest to use the web interface to configure them. Set the name, password & server of an identity, and enable that identity. If you expect to make calls across a NAT, also go to the NAT tab & set it to send keepalive packets every second or so.

The backoffice snom’s webinterface, configuring identity 1 (the snom 320 is limited to 12 of these, which it numbers sequentially). Note that the SIP account name occurs three times, once for the name which is shown on the snom’s display, and apparently twice SIP itself. The last two of these should probably be identical.

Make sure to disable any other SIP accounts that might be configured (e.g. from previous events) and which you don’t need.

duut-duut-duut …?

You can ask the server which clients it knows by letting it display the current aors of PJSIP. It’s best to enter the asterisk cli for this:

 sudo asterisk -r
Asterisk 19.7.0, Copyright (C) 1999 - 2022, Sangoma Technologies Corporation and others.
Created by Mark Spencer <markster@digium.com>
Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.
This is free software, with components licensed under the GNU General Public
License version 2 and other licenses; you are welcome to redistribute it under
certain conditions. Type 'core show license' for details.
=========================================================================
Connected to Asterisk 19.7.0 currently running on televoc (pid = 704)
televoc*CLI> pjsip show aors

      Aor:  <Aor..............................................>  <MaxContact>
    Contact:  <Aor/ContactUri............................> <Hash....> <Status> <RTT(ms)..>
==========================================================================================

      Aor:  saal1                                                1

      Aor:  saal2                                                1

      Aor:  backoffice                                           1
    Contact:  backoffice/sip:backoffice@10.0.73.117:2048;li e3bba38c1f NonQual         nan


Objects found: 3

televoc*CLI> 

If a client has connected, their “Contact” field will include their IP address; in the above example, only the backoffice phone has registered successfully. Note that this does not necessarily mean the other phones can’t place calls, just that the server doesn’t know how to reach them if anyone else tries to call their extension.

There are many more useful things you can do in the asterisk cli interface; poking around it is definitely worth it. Just as an example, you can start ad-hoc calls, like this,

televoc*CLI> channel originate PJSIP/saal1 extension 100@from-internal

after which the saal1 phone should ring.

The wiki gives a few basic examples, but since most asterisk’s functionality is provided by individual modules, the actually useful examples are a little spread out. Most of the time it’s easier to find interesting things in the cli using tab-completion, and then using the built-in help function to find out what they do.

Conversly, with the above config you can test if a client can reach the server by dialing 100 and checking that it answers with the “hello, world”. If you’ve done it wrong, it’ll probably reject your call immediately, and the snom will display an SIP status code and error message on its display. Some of these may be familiar to you from HTTP, but probably not all — wikipedia has a complete list if you need it.

If you dial and nothing at all happens (not even a “dialing” or “hold” sound signal), then you’ve probably run into network issues. Perhaps there’s a NAT or a firewall in the way somewhere, or the snom is attempting to reach an IP where there’s no server that could answer?

Some recommendations:

  • give all clients, and especially the server, static IP addresses, or make their DHCP leases static. This isn’t strictly necessary, but it makes fixing problems much easier
  • if things don’t work for no apparent reason and were fine earlier, try rebooting the snom in question, or at least make it re-register (there’s a button for that in its web gui)
  • send keepalive packets if there’s a NAT somewhere (once every couple seconds seems to work mostly fine), or the server won’t be able to reach the phone if someone attempts to call it. This will hopefully be enough; if not, there are entire subsections of the wiki dedicated to various NAT-related issues

Thinkpads make great servers

This is a sidenote, but: the asterisk server ran on an old T430 that the VOC had lying around, and which we kept in our backoffice the entire time. I can highly recommend doing this over running it on e.g. a single-board computer, simply because if in doubt, it comes with a keyboard and monitor built in, so you can always quickly jump into asterisk’s cli interface.

Just don’t forget to set it so it won’t go to sleep if someone closes its lid (and perhaps set the “Boot on AC Attach” BIOS option).

Future Work

There’s lots of other stuff asterisk can do, like DECT, or a dial-out & dial-in to a larger phone network, automatically redirecting calls if no one picks up, callgroups, …

Maybe I’ll look at some of those the next time we have a surprise telephone setup at some place where there’s no POC.

Conclusion

Overall, this setup worked pretty well. The stationary nature of the phones was seldom an issue, and the overall lack of DECT was less noticable than I’d feared.

We only made a few calls during the event, but they were a great help in resolving issues that came up, and made the overall work of the VOC much less stressful.

You might’ve also noticed that one of the snoms is still unaccounted for — I wrote at the beginning that we had four, and then only mentioned three of them in the config. Well, it turns out that eventphone also runs an SIP server, so we logged the last snom into theirs so that people on other chaos events could call us.

Of course, that does mean there was technically no need to run our own SIP server in the first place; we could’ve just used the eventphone server.

But consider this: it was fun doing so!