[ N O C T E R N I T Y ] Contributing to the general pollution of the internet

27Mar/130

Playing with Asterisk (4/?) – Voice mail

Even in the context of a home set-up, voice mail is a "must-have" feature. My idea is to have a set of voicemail boxes - one per person, and a global "family" mailbox. Ideally, local phones / softphones should be able to access the corresponding mailboxes (the mailbox of the phone's owner and the family mailbox) without having to type a password, but other mailboxes should still be available with an identifier and passwords on all internal phones.

Basic set-up

Before trying to do that, I created a simple set-up that only worked for one of the test lines, and always required credentials to access. The first thing to configure is the voice mail application itself. I had to re-enable the app_voicemail.so module, and write a configuration file for the application (aptly named voicemail.conf). I also wanted email notifications with the message attached. Finally, I noticed the options of having the system force setting greeting messages and password when initially logging in, and that seemed interesting. Here is the resulting configuration file.

[general]
format=wav49|gsm|wav

serveremail=phones@nocternity.net
fromstring=Téléphonie
attach=yes
emaildateformat=%A, %d %B %Y at %H:%M:%S
sendvoicemail=yes
charset=UTF-8

maxmsg=100
maxsecs=180
minsecs=3 
maxgreet=90
maxsilence=2 
silencethreshold=128

moveheard=yes
review=yes
forcename=yes
forcegreetings=yes

maxlogins=3
minpassword=4 

[zonemessages]
; Empty

[default]
1 => 1,Emmanuel Benoît,tseeker@nocternity.net

I left the zonemessages section empty, because I really don't care about that feature; as for the password, it is set to "1" on my user in order to cause the voice mail application to force me to set it when I connect to it.

The next thing to configure was the dial plan. I had to modify one of the extensions for the internal phones in order to have it fall back on the voice mail when necessary. I initially used the following configuration:

exten => 100,1,Dial(${DIAL_TEST1},10,tT)
	exten => 100,n,Voicemail(1@default)
	exten => 100,n,Hangup()

The new argument to Dial() simply indicates the maximal ringing time before falling back to the voice mail. In this case, "ring for 10 seconds and fall back to the voice mail if there is no response".

I also had to add an extension that would give users (namely, me) access to their voice mail:

exten => 666,1,VoiceMailMain()
	exten => 666,n,Hangup()

Finally, I modified the voice mail fall-back  in order to play either the "busy" greeting or the "unavailable" greeting, depending on the Dial() application's result. This took me a while to figure out due to the somewhat twisted syntax.

exten => 100,1,Dial(${DIAL_TEST1},10,tT)
		exten => 100,n,Voicemail(1@default,${IF($["${DIALSTATUS}" = "BUSY"]?b:u)})
		exten => 100,n,Hangup()

Note about voice mail notifications

I tried setting up voice mail notifications for the SIP clients, by adding the following to their section in the sip.conf file:

mailbox=1@default

However, linphone does not appear to support this (it causes a warning on Asterisk's side), and the other free (not as in beer) softphones I tried had a tendency to blow up in my face.

General set-up

I needed to make quite a few changes in order to implement the idea I mentioned in this post's introduction.

Voice mail fall-backs for all phones

First, having a voice mail fall-back for all phones. In order to do that without the configuration becoming a copy-pasted mess, I had to learn about macros, which are basically dial plan sections which can be invoked from other areas of the dial plan. Here's the macro I used for the voice mail fall-back:

[macro-internal-call]

exten => s,1,Dial(${ARG1},10,tT)
	exten => s,n,Voicemail(${ARG2}@default,${IF($["${DIALSTATUS}" = "BUSY"]?b:u)})
	exten => s,n,Hangup()

This macro takes two arguments: the phone(s) to dial, and the voice mail box to fall back to. It then needs to be used in place of the internal phones' extensions:

exten => 100,1,Macro(internal-call,${DIAL_TEST1},1)
exten => 101,1,Macro(internal-call,${DIAL_TEST2},1)
exten => 102,1,Macro(internal-call,${DIAL_TEST3},2)

(I'd added box #2 and #9 in the meantime.) I also had to "emulate" that family voice mail box. I simply added the following extension:

exten => 999,1,Macro(internal-call,${DIAL_TEST1}&${DIAL_TEST2}&${DIAL_TEST3},9)

... which will cause all phones to ring when dialed, and fall back on mailbox #9.

Password-less access to voice mail

The next step was to allow clients to access their own voice mail, as well as the global one, without password. I started by changing the contexts for SIP clients to something that corresponds to their owners in sip.conf:

; ...

[test1](test-template)
        context=phones-tseeker
        ;...

[test2](test-template)
        context=phones-tseeker
        ;...

[test3](test-template)
        context=phones-ju
        ;...

Of course, I then needed to define these contexts in the dial plan. However, I wanted to avoid having to copy/paste common configuration; this is where the include thing comes in. It allows the extensions listed in another section to be considered. The modified extensions.conf looked somewhat like this:

[phones-tseeker]

        include => tests
        include => phones

[phones-ju]

        include => tests
        include => phones

[tests]

        ; Various 8378-prefixed tests here

[phones]

exten => 100,1,Macro(internal-call,${DIAL_TEST1},1)
; etc...

exten => 666,1,VoiceMailMain()
        exten => 666,n,Hangup()

Finally, I added the lines allowing direct access to the voice mail to each "owner-specific" section:

[phones-tseeker]

        include => tests
        include => phones

exten => 700,1,VoiceMailMain(1@default,s)
        exten => 700,n,Hangup()
exten => 701,1,VoiceMailMain(9@default,s)
        exten => 701,n,Hangup()

[phones-ju]

        include => tests
        include => phones

exten => 700,1,VoiceMailMain(2@default,s)
        exten => 700,n,Hangup()
exten => 701,1,VoiceMailMain(9@default,s)
        exten => 701,n,Hangup()

Some thoughts for later

I think it would be best if all internal numbers were prefixed with something that is not associated with outgoing calls at all. The "star" key seems like a decent prefix. In addition, it would be a good idea to make the various extensions and voice mail box numbers a little more rational and consistent.

26Mar/130

Playing with Asterisk (3/?) – A menu (sort of)

I wanted to try adding something that sort of feels like a menu to the dial plan. I am not sure that I will actually use one in the final setup, but it wasn't that much effort anyway.

A menu (or something with similar functionality) can be implemented using either Background() or WaitExten(). Or both, actually. Since I don't have anything to play as a prompt, I chose to do with the latter only. After all, this is only a test.

First, I replaced the previous test with the following:

[test]

exten => 8378,1,Answer()
        exten => 8378,n,Set(TESTCOUNT=0)
        exten => 8378,n,Goto(numbers,0,1)

This will cause Asterisk to answer calls to 8378 ("test"), set a channel-specific variable called TESTCOUNT to zero, and jump to extension 0 of the numbers section in the dial plan. The idea is to accumulate the digits typed by the user, and then to have Asterisk say that number. So, for each possible digit, the section will contain something like this:

[numbers]

exten => 0,1,Set(TESTCOUNT=$[ ${TESTCOUNT} * 10 ])
        exten => 0,n,WaitExten()
exten => 1,1,Set(TESTCOUNT=$[ ${TESTCOUNT} * 10 + 1 ])
        exten => 1,n,WaitExten()
; ... Same thing for 2 - 9 here ...

Finally, pressing the star key will cause the system to say the number (using SayNumber()) that was typed and to hangup.

    exten => *,1,SayNumber(${TESTCOUNT})
	    exten => *,n,Hangup()
21Mar/130

Playing with Asterisk (2/?)

Last time I managed to get a very basic Asterisk setup to work. Since then, I haven't had the time to really work on it, but I've made a few improvements here and there.

Dial plan improvements

In quite a few documents I read, dial plans always include a Hangup() line for all extensions, whatever they contain. I'm not 100% sure it's actually useful, but I added it anyway:

exten => 100,1,Dial(SIP/test1)
        exten => 100,2,Hangup()

exten => 101,1,Dial(SIP/test2)
        exten => 101,2,Hangup()

One of the things that was a little scary about the dial plan in general was the fact that I would have needed to renumber all lines for an extension if I wanted to insert stuff in one of its parts. That would have been rather annoying. As it turns out, Asterisk supports using "n" as the priority field in the dial plan's extension definitions. Using it causes Asterisk to handle the "line numbering" automatically (with the exception of the first line that still needs to be 1):

exten => 8378,1,Answer()
        exten => 8378,n,Playback(hello-world)
        exten => 8378,n,Hangup

exten => 100,1,Dial(SIP/test1)
        exten => 100,n,Hangup()

exten => 101,1,Dial(SIP/test2)
        exten => 101,n,Hangup()

Finally, I started using global variables to define the dialing targets, which would make it much easier to change if I needed to:

[globals]
        DIAL_TEST1=SIP/test1
        DIAL_TEST2=SIP/test2

[test]

; ...

exten => 100,1,Dial(${DIAL_TEST1})
        exten => 100,n,Hangup()

; ...

Granted, it is "a little" pointless in this context, but it will come in handy later.

Caller IDs for SIP clients

I also added caller IDs for SIP clients. It is extremely simple to do that by simply adding a line that looks like this...

        callerid="TSeeker's mobile" <101>

...to a SIP client's configuration.

Transferring calls

One other thing I added is the ability to transfer calls. First, I started by creating a features.conf file in order to define the DTMF sequences to use. Here's what it contains:

[general]
        ; Empty

[featuremap]
        blindxfer => #1
        atxfer => #2

[applicationmap]
        ; Empty

This is not strictly necessary, as the defaults can be used. Anyway - the next step is to enable the functionality when calls are being made. This is done from the dial plan:

; ...
exten => 100,1,Dial(${DIAL_TEST1},,tT)
; ...

In this specific case, I want to allow both sides to transfer the calls, as these are internal phones anyway (I added a third client on Ju's also-microphone-less PC to run tests).

Miscellaneous changes

Amongst other minor changes, I also re-configured logging using the following configuration in logger.conf:

[general]
        ; Empty

[logfiles]
        console => notice,warning,error,debug
        messages => notice,warning,error

No need for Asterisk-based log file rotation as the Debian package handles that using logrotate.

I also modified modules.conf to remove more currently useless modules (it's just a bunch of noload's for stuff I know I'm not gonna need so I'm not pasting this here).

Finally, I did not add IPv6 support yet, as I need to upgrade to 1.8 for that and I figured IPv4 would be fine for testing purposes; I don't expect many things to change because of IPv6 support anyway (although I may be wrong).

19Mar/130

Playing with Asterisk (1/?)

The long term "plan" here at home is to switch over to VoIP for all phone calls. This includes getting rid of our old POTS phones, using softphones on the PCs, possibly buying hardware VoIP phones or, if I ever get the time to finish playing with the STM32 board, making our own; it also implies subscribing to a pair of SIP lines and using a board to connect our POTS line to the network.

There's a catch, tho: I've never used Asterisk (or any other kind of VoIP software), and it seems to be rather complex. So before we do anything, I'm going to be exploring its configuration a little when I have some time and nothing more important to do.

Since I'm basically a complete newbie when it comes to it, I'll try and post about it as it might be useful to someone in a similar situation.

System

Before I go on, it should be noted that I am working with a Debian Squeeze system, running inside a Xen VM. That VM is on a network that has both IPv4 and IPv6 stacks. The idea is to have everything local going through IPv6, with support for IPv4 through NAT (which means the Linux routers here need to have nf_conntrack_sip and nf_nat_sip loaded; the VM itself only has nf_conntrack_sip). While I've read that NAT is terrible when it comes to SIP (and for everything else), I'm afraid I don't have much choice on the matter.

For now I won't be doing anything that requires specific hardware, as it is a bit pointless to buy a €700 board if you're not even remotely sure you can do what you need to with it.

I have ordered a pair of webcams that include microphones, and they should arrive in a few days. At the moment, however, the PCs here have no way to record sound, so my ability to test with them is limited. I figured I could use my mobile phone for testing, though. I installed Linphone on both my PC and my smartphone.

Installing Asterisk

This is a Debian system so  it's rather easy to install Asterisk and its various dependencies:

apt-get install asterisk

Ok, I actually removed some recommended dependencies I won't be needing (either "for now" or "permanently").

The Debian package comes with a metric ton of sample configuration files under /etc/asterisk; while these are rather handy to have around as they are very well commented, I moved them out of the way as I prefer writing my own configuration from scratch.

First steps

So, the first thing I want to do is set up the server so it is possible to connect to it. That's the definition of "minimal configuration", but one has to start somewhere.

I left both asterisk.conf and modules.conf mostly untouched (with the exception of comments being removed), although I'll probably have to remove some modules when I have a better idea of what I'm doing. Most of the action here takes place in sip.conf:

[general]
        context=default
        allowoverlap=no
        udpbindaddr=0.0.0.0
        tcpenable=no
        srvlookup=yes

[test1]
        type=friend
        host=dynamic
        canreinvite=no
        nat=yes
        context=default
        dtmfmode=rfc2833
        allow=all
        username=test1
        secret=...

The general section is basically the default, followed by a test user which I can use to try and connect to the server. I also included an empty dial plan (while I'm not sure it was necessary, I figured it wouldn't hurt):

[general]
static=yes
writeprotect=no
clearglobalvars=no

[globals]
; No variables

[default]
; Empty context

After some tinkering with the Linphone configuration (I needed to add a proxy account, using sip:test1@server-ip as the identity and sip:server-ip as the proxy address), I was able to get it to connect to the server.

A basic dial plan

The next logical step was to try and have the client dialing, with the server responding with something. Fortunately, Asterisk comes with a lovely hello-world.gsm file.

First I modified my test user so that its default context would be something other than default. I called that new context test. For some reason.  Then I created a simple dial plan that would cause the server to answer, play the "hello world" sound, and hangup, when the client dials "1".

[test]
exten => 1,1,Answer()
exten => 1,2,Playback(hello-world)
exten => 1,3,Hangup

And "1" was dialed, and "Hello world" was heard, and it was good.

Two accounts

Having done that, I decided to try implementing calls between SIP clients. Of course, since only one of the two SIP clients has a microphone, my ability to test is limited.

So, I wanted to add a second SIP client. Since its configuration would end up being mostly the same as the one I already had, I decided to use a template for the clients. Here's the resulting sip.conf:

[test-template](!)
        type=friend
        host=dynamic
        canreinvite=no
        nat=yes
        context=test
        dtmfmode=rfc2833
        allow=all

[test1](test-template)
        username=test1
        secret=...

[test2](test-template)
        username=test2
        secret=...

Then I had to add a few lines to the dial plan in order to allow dialing between one client and the other. It only takes two new lines in the dial plan:

exten => 100,1,Dial(SIP/test1)
exten => 101,1,Dial(SIP/test2)

Because I don't have WiFi here, I had to whitelist the phone carrier's IP range on the firewall to let it connect to the server. The configuration of Linphone uses the server's internal IPv4 address (i.e. its address on the LAN here) as the domain, and the IPv4 address of the DSL connection as the proxy. With this configuration, I was able to call the phone from the PC and vice-versa.

20Oct/110

Criminal incompetence

I have a lot of work to do, and many posts I'd like to write, but that will have to wait. I need to vent some frustration right now.

I am currently on a systems administration mission where I need to inspect and fix (if necessary... lol!) a few virtual servers.

Let's start with the documentation. Oh wait, I can't really start from there. There's nothing. A bunch of IP addresses, a few very weak passwords. A list of a few shell commands, out of the blue. A list of stuff that's been done, or at least I suppose that's what it's supposed to be (the list contains entries such as "Access phpmyadmin", followed by "Change init level"). Nothing to see here, let's move on.

The server's list of virtual hosts includes a SSH gateway, from which you're supposed to connect to the server's other VMs. Well, that sounded fine to me, as it may have meant I wouldn't need to go there and could simply log on through that gateway. Of course, since the "existing" documentation doesn't really list the VM's IP addresses, it's a bit messy. Especially since most ICMP packets seem to be blocked, somewhere, somehow. Oh, and some of the VMs do not listen on port 22. Has it been changed to some other port? Is it being blocked by iptables rules? No fucking clue. In addition, it was possible to log in as root (using a password that wasn't much better than "test") on the gateway.

For now, I've been able to find 3 out of the 11 VMs I'm supposed to take a look at,  including the aforementioned gateway. Each host includes the full Gnome desktop, as well as a random assortment of useless stuff (laptop-detect? Dude, it's a VM!). Of course, since the... person... who did this decided to use Ubuntu Server, it's not even possible to remove dbus, because upstart depends on it. Still, I was able to remove a few gigabytes of crap on the servers I could access.

I expect more bad surprises as I keep on working on that. In the meantime, my opinion is that whoever committed this in the first place should seriously think about giving up on systems administration and looking for a career in, oh, I don't know, floor wiping. Or something like that.

14Oct/112

Installing Debian GNU/Linux on a LaCie NAS

Since I recently set up Azathoth's 6th revision, I had the old LaCie NAS on a shelf, completely useless. I had to do something with it; however, the system which comes pre-installed on that thing is definitely not too customisable beyond the basics, so I decided to try and install Debian GNU/Linux on it.

The NAS, with its cover removed

One important thing about this old NAS - it uses an Intel EM7210 motherboard, with an Intel 80219 CPU. As it happens, that board is more or less supported by Debian.

The NAS's EM-7210 motherboard

However, it is necessary to access the system's RedBoot bootloader, which is only possible using a serial cable whose connector is directly on the motherboard.

IDC10 serial connector on the motherboard

Building the cable

The serial cable that allows access to the system's console needs to be plugged on an IDC10 connector. While it might have been possible to use the connector from an old PC along with a null-modem cable, I no longer possess either of these items, so I built my own cable. This is what you need to connect:

Female DE9 Female IDC10
2 5
3 3
5 9

And if you need a picture for the IDC10 side (I had to look it up, so I might not be the only one in this case):

Pins on the motherboard's IDC10 connector

I hadn't done any soldering for 16 years, so I was a little anxious about that. As it turns out, soldering was the easy part; the wires I used were apparently a little too big for my IDC10 connector, and I broke it. I had to rip connectors from an unused audio front panel and solder them to my wires instead. In addition, since none of my computers here have RS232 interfaces, I had to use an USB<->RS232 adapter.

I should really be ashamed

Minicom settings

The next step was to connect to the NAS's console using Minicom (any serial terminal would do, but I'm used to this one). The settings that need to be used are:

  • transfer rate: 115200 bps
  • data bits: 8
  • parity: none
  • stop bits: 1

It is also important to make sure that both hardware and software flow control are turned off.

 Booting the Debian installer

Obviously, the next thing to do is to try and start the Debian installer.

In order to do that, it is necessary to boot into the NAS's bootloader, which can be achieved by pressing Ctrl+C in the console before the bootloader's script starts executing (you need to be rather quick, as you only have one second to do so). This leads you to a command prompt.

You will also need to download kernel and RAM disk images for the architecture. Both images can be found on Debian's server (you need to download zImage and initrd.gz).

Once you are ready to proceed, you will need to upload the images to the NAS through the serial cable. Start by telling the bootloader that you want to load the RAM disk image:

load -v -r -b 0x1800000 -m ymodem ramdisk.gz

The bootloader will then expect you to send it the RAM disk's data (the initrd.gz file mentioned above) using YMODEM. In Minicom, that can be done by pressing Ctrl+A followed by S, then selecting "ymodem" in the list, and finally selecting the file to send.

(Wait. Wait some more. Wait even more.)

When that transfer has been completed, you will also need to send it the kernel image by typing

load -v -r -b 0x1008000 -m ymodem zImage

and then sending the zImage file.

(Wait. Wait some more. Wait even more.)

Once both files have been uploaded, you are ready to start the installer. The following command will do just that:

exec -c "console=ttyS0,115200 rw root=/dev/ram mem=256M@0xa0000000" -r 0x01800000
Completing the installation

I've skipped the part about installing Debian, because that's pretty much the usual process. However, there's one additional operation to perform once the installer is done running, as the system will not boot without that. You need to edit the boot script.

In order to do that, when the installer reboots, enter the bootloader by pressing Ctrl+C, then enter the following command:

fconfig boot_script_data

The prompt will change to >> to indicate that you are editing the configuration. You need to set the boot script to this:

fis load -b 0x01800000 ramdisk.gz
fis load -b 0x01008000 zImage
exec -c "console=ttyS0,115200 rw root=/dev/ram mem=256M@0xa0000000" -r 0x01800000

Don't forget to exit the boot script editor by finishing with an empty line. RedBoot will ask you to confirm the changes then write the configuration to flash memory.

A few notes
  1. I initially removed the HDDs from the box while trying to boot it in order to avoid unnecessary power cycles. However, if you try to insert them while the Debian installer is running, they will not be detected, and you will end up having to reboot anyway.
  2. Setting up the partitions takes an awful lot of time, and so does RAID array resynchronisation if that's what you want to use.
21Aug/113

Azathoth, revision 6

Azathoth is the name of our file server. We've had "it" since 2000, although it was replaced many times. One of the goals we had after we moved to the house was to get rid of the 3 boxes full of CDs and DVDs; however, that goal was not attainable without replacing the file server (or at the very least its disks), as it was full.

Last week, I ordered the hardware I needed to build a new revision of Azathoth. This included an Antec micro-ATX case, a micro-ATX PSU, a rather weak Celeron CPU (the weakest I could find, actually) along with some RAM and a micro-ATX motherboard, and a backplane for the disks and, well, of course, disks (4x 3TB SATA disks).

I actually made a few mistakes (this tweet being quite relevant):

  • the description of the Antec case said "3x 5.25-inch bays" which was right, of course, but it really was "2x + 1x";
  • I searched for the RAM by frequency, and didn't bother to actually read the description - turns out I ordered DDR2 instead of DDR3...

So I had to find an unused ATX case, and I "borrowed" some RAM from Ju's computer (and ordered replacement RAM). Here are a few pictures taken during the hardware's installation:

The hardware

I then installed and configured the server's software. I installed a lovely Debian Squeeze using an USB key, using the disks as a software RAID10 array with a LVM on top, then configured a NFS server and a Samba server, in addition to the usual stuff I tend to pollute my systems with (Nagios's NRPE and a NTP server, mostly). A few things worth noting:

  • I gave up on NFSv4 for now, as I don't have Kerberos set up (and this situation will probably last for some time, as I need another server for that - I wouldn't want the whole LAN to become unusable if good ol' Tulzscha dies, now, would I?)
  • NFSv3 does not support IPv6, so Azathoth will be one of the last few things that are not using IPv6 on the LAN.
  • I attempted to set up transport-level IPsec between Azathoth and the NFS clients. While I was successful at setting up racoon with X.509 authentication, I gave up on that as well for practical reasons (see below); this was obvious from the start but I seem to have forgotten to think before I did it. Ah well, at least now I know how to set up transport-level IPsec :)
Configuration dd if=/dev/zero of=...
Azathoth rev. 5 11.3 MB/s
Azathoth rev. 6 (no IPsec) 55.8 MB/s
Azathoth rev. 6 (with IPsec) 9.4 MB/s

I'm not completely done with that yet: I intend to convert Azathoth v5 (an old LaCie NAS - which is actually an undercover Intel NAS) into a backup system for a part of Azathoth v6's RAID array (that part has been left unused for now; I'm considering using DRBD to simply "clone" it, along with LVM snapshots). However, I'd rather wait for a few weeks just to make sure that the new Azathoth is stable before I destroy the data from the old one.