I haven’t had land-line phone service in more than a decade, but that doesn’t mean that I haven’t missed it. If I’m on a two hour conference call I don’t want to be burning up the minutes and battery on my cell phone (or be tethered to the wall where the charger is plugged in), or hunched in front of the computer using Google Voice. I miss having a decent speakerphone, too.
So when I read that Asterisk 1.8 supported Google Voice, I was excited. It only costs about $8 a month to run a small tower PC. That’s already several times cheaper than a land-line phone, moreso if you consider that long distance is included. Plus you can do some neat things with an in-home PBX.
Unfortunately, support for the Cisco 7940G handset has been broken as of Asterisk 1.8.4. There’s a bug — documented in ASTERISK-17830 and ASTERISK-17535 –wherein the handsets can’t complete SIP registration. Basically the SIP registration includes an IP address and port in a Via header, and this pair needs to be correct in order for the registration to succeed… and the IP address is wrong. The handsets can speak to the PBX but two-way communications are problematic, which makes most of the fun stuff impossible. What follows is what I went through to get this all working.
I figured I’d get Asterisk up and running first using soft phones (software that runs on your computer and interacts with the PBX just as a SIP handset would), then worry about getting the Cisco handsets working.
Building Asterisk
On a fairly bland CentOS install, I had to install a few things just to configure the build. I’ve gotten used to having a stocked development system, so I felt pretty dumb about some of these.
configure: error: C++ preprocessor "/lib/cpp" fails sanity check
The Asterisk Common Compiling Issues recommends installing gcc-c++ … duh. I’ll spare you the details, but configure also complained about libxml2.
box# yum install gcc-c++ libxml2-devel
Try again, and… nice.
configure: Menuselect build configuration successfully completed
.$$$$$$$$$$$$$$$=..
.$7$7.. .7$$7:.
.$$:. ,$7.7
.$7. 7$$$$ .$$77
..$$. $$$$$ .$$$7
..7$ .?. $$$$$ .?. 7$$$.
$.$. .$$$7. $$$$7 .7$$$. .$$$.
.777. .$$$$$$77$$$77$$$$$7. $$$,
$$$~ .7$$$$$$$$$$$$$7. .$$$.
.$$7 .7$$$$$$$7: ?$$$.
$$$ ?7$$$$$$$$$$I .$$$7
$$$ .7$$$$$$$$$$$$$$$$ :$$$.
$$$ $$$$$$7$$$$$$$$$$$$ .$$$.
$$$ $$$ 7$$$7 .$$$ .$$$.
$$$$ $$$$7 .$$$.
7$$$7 7$$$$ 7$$$
$$$$$ $$$
$$$$7. $$ (TM)
$$$$$$$. .7$$$$$$ $$
$$$$$$$$$$$$7$$$$$$$$$.$$$$$$
$$$$$$$$$$$$$$$$.
configure: Package configured for:
configure: OS type : linux-gnu
configure: Host CPU : i686
configure: build-cpu:vendor:os: i686 : pc : linux-gnu :
configure: host-cpu:vendor:os: i686 : pc : linux-gnu :
Now I did some pre-reading and knew that Google Voice would require a few other dependencies, notably iksemel (an XML parsing for XMPP) and libgnutls (for securing communications back to Google).
box# wget http://iksemel.googlecode.com/files/iksemel-1.4.tar.gz
box# tar -zxvf iksemel-1.4.tar.gz
box# cd iksemel-1.4
box# ./configure --with-gnutls
.
.
checking for libgnutls - version >= 0.1.0... no
*** The libgnutls-config script installed by LIBGNUTLS could not be found
*** If LIBGNUTLS was installed in PREFIX, make sure PREFIX/bin is in
*** your path, or set the LIBGNUTLS_CONFIG environment variable to the
*** full path to libgnutls-config.
I tried this workaround, which didn’t work. I rolled back and installed gnutls-devel package and then everything built fine, plain vanilla. Some of these fixes are dated, clearly.
box# yum install gnutls-devel
box# cd iksemel-1.4
box# ./configure
box# make
box# make check
box# make install
Back to Asterisk, dependencies satisfied we begin to build.
Building Documentation For: channels pbx apps codecs formats cdr cel bridges funcs tests main res addons
+--------- Asterisk Build Complete ---------+
+ Asterisk has successfully been built, and +
+ can be installed by running: +
+ +
+ make install +
+-------------------------------------------+
real 20m33.595s
user 19m2.208s
sys 1m15.730s
Oh, single 750Mhz processor on legacy hardware… ouch.
Configuring Google Voice
I started out with the Calling using Google guide, which I found didn’t work. Not exactly, but not entirely the fault of the configuration shown there.
After starting Asterisk up using the dialplan defined in the Asterisk documentation, outbound calls were failing.
== Using SIP RTP CoS mark 5
-- Executing [14105551234@users:1] Dial("SIP/gv-00000000", "gtalk/asterisk/+14105551234@voice.google.com") in new stack
[Aug 20 13:24:03] WARNING[23196]: channel.c:5503 ast_request: No channel type registered for 'gtalk'
[Aug 20 13:24:03] WARNING[23196]: app_dial.c:2197 dial_exec_full: Unable to create channel of type 'gtalk' (cause 66 - Channel not implemented)
== Everyone is busy/congested at this time (1:0/0/1)
-- Auto fallthrough, channel 'SIP/gv-00000000' status is 'CHANUNAVAIL'
It turns out that the res_gtalk.so resource libraries weren’t being loaded. Since this was a from-source build and not a pretty pre-build, Asterisk had no concept of where there crypto libraries were. Will fix this later (as well as running as root), but for now we’ll just start the PBX like so…
box# export LD_LIBRARY_PATH=/usr/local/lib ; asterisk -vvvvc
Outbound calls were still failing…
== Using SIP RTP CoS mark 5
-- Executing [14105551234@users:1] Dial("SIP/gv-00000000", "gtalk/asterisk/+14105551234@voice.google.com") in new stack
-- Called gtalk/asterisk/+14105551234@voice.google.com
-- Gtalk/+14105551234@voice.google.com-f411 is ringing
== Spawn extension (users, 14105551234, 1) exited non-zero on 'SIP/gv-00000000'
I knew that we were talking to Google, since there’s an endless stream of presence message flying by on the console; the more buddies you have the worse it is. Enabling debug mode for Jabber (jabber set debug on) outputs every XMPP stanza to the Asterisk console, even more verbose. And therein was a clue.
JABBER: google_voice INCOMING:
<iq type="error" to="ACCOUNTNAME@gmail.com/1234567890" from="14105551234@voice.google.com" id="aaaaa">
<ses:session type="initiate" initiator="ACCOUNTNAME@gmail.com/1234567890" id="abcdefghijklmop" xmlns:ses="http://www.google.com/session">
<pho:description xml:lang="en" xmlns:pho="http://www.google.com/session/phone">
<pho:payload-type id="0" name="PCMU" clockrate="8000" bitrate="64000"/>
<pho:payload-type id="100" name="EG711U" clockrate="8000" bitrate="64000"/>
<pho:payload-type id="101" name="telephone-event" clockrate="8000"/></pho:description>
</ses:session>
<error code="302" type="modify">
<sta:redirect xmlns:sta="urn:ietf:params:xml:ns:xmpp-stanzas">xmpp:14105551234@voice.google.com/srvres-abc123abc123</sta:redirect>
<ses:redirect xmlns:ses="http://www.google.com/session">xmpp:14105551234@voice.google.com/srvres-abc123abc123</ses:redirect>
</error>
</iq>
So Google is redirecting us to a new resource, but res_gtalk.so isn’t picking it up. As it turns out, a bug report had been filed a few hours prior along with a simple patch.
Index: channels/chan_gtalk.c
===================================================================
--- channels/chan_gtalk.c (revision 332695)
+++ channels/chan_gtalk.c (working copy)
@@ -496,7 +496,7 @@
break;
}
if (!strcasecmp(name, "error") &&
- (redirect = iks_find_cdata(traversenodes, "redirect")) &&
+ (redirect = iks_find_cdata(traversenodes, "sta:redirect")) &&
(redirect = strstr(redirect, "xmpp:"))) {
redirect += 5;
ast_debug(1, "redirect %s\n", redirect);
It seems Google altered the name of their redirect stanza to be “sta:redirect” instead of “redirect”. After a quick recompile, outbound calls were working but inbound were failing.
Update: Naturally, it appears that Google has flipped back to the “redirect” version. No idea if they’ll stick with it or not, so perhaps I’ll apply this patch so that I can accept both.
== Starting Gtalk/+17607058888-d417 at google-in,ACCOUNTNAME@gmail.com,1 failed so falling back to exten 's'
-- Executing [s@google-in:1] Answer("Gtalk/+17607058888-d417", "") in new stack
-- Executing [s@google-in:2] Wait("Gtalk/+17607058888-d417", "2") in new stack
-- Executing [s@google-in:3] SendDTMF("Gtalk/+17607058888-d417", "1") in new stack
-- Executing [s@google-in:4] Dial("Gtalk/+17607058888-d417", "SIP/101,20,D(:1)") in new stack
== Using SIP RTP CoS mark 5
[Aug 20 20:36:41] WARNING[27059]: acl.c:708 ast_ouraddrfor: Cannot connect
[Aug 20 20:36:41] WARNING[27059]: chan_sip.c:3349 __sip_xmit: sip_xmit of 0x9b3bb20 (len 892) to 0.0.0.101:5060 returned -1: Invalid argument
-- Called SIP/101
[Aug 20 20:36:41] WARNING[27034]: chan_sip.c:3349 __sip_xmit: sip_xmit of 0x9b3bb20 (len 892) to 0.0.0.101:5060 returned -1: Invalid argument
[Aug 20 20:36:42] WARNING[27034]: chan_sip.c:3349 __sip_xmit: sip_xmit of 0x9b3bb20 (len 892) to 0.0.0.101:5060 returned -1: Invalid argument
[Aug 20 20:36:44] WARNING[27034]: chan_sip.c:3349 __sip_xmit: sip_xmit of 0x9b3bb20 (len 892) to 0.0.0.101:5060 returned -1: Invalid argument
== Spawn extension (google-in, s, 4) exited non-zero on 'Gtalk/+17607058888-d417'
[Aug 20 20:36:48] WARNING[27034]: chan_sip.c:3349 __sip_xmit: sip_xmit of 0x9b3bb20 (len 892) to 0.0.0.101:5060 returned -1: Invalid argument
[Aug 20 20:36:56] WARNING[27034]: chan_sip.c:3349 __sip_xmit: sip_xmit of 0x9b3bb20 (len 892) to 0.0.0.101:5060 returned -1: Invalid argument
[Aug 20 20:37:12] WARNING[27034]: chan_sip.c:3349 __sip_xmit: sip_xmit of 0x9b3bb20 (len 892) to 0.0.0.101:5060 returned -1: Invalid argument
[Aug 20 20:37:13] WARNING[27034]: chan_sip.c:3620 retrans_pkt: Retransmission timeout reached on transmission 7b0e2ef22c3e8660699c921c665c1c93@127.0.0.1:5060 for seqno 102 (Critical Request) -- See https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions
Packet timed out after 32000ms with no response
I could see the XMPP stanzas arriving, and it turned out to be a dialplan problem. Rather, I had specified a SIP address in my dialplan that didn’t exist. As I’m re-discovering after 9 years away from Asterisk, most of the problems are with your dialplan.
So now that inbound and outbound works via my Google Voice number (which itself is pretty fucking cool), back to the SIP registration problem.
Getting Cisco 7940G Working as a SIP Device
Supposedly, Asterisk 1.8.6.0-rc1 contains the following patch, to insert the proper IP in the Via header. But that didn’t work right away. I tried a bunch of other patches, none of which did the trick. So I decided to upgrade the firmware on the phones.
Before:
Cisco Systems, Inc. Copyright 2000-2004
Cisco IP phone MAC: 0000:0000:0000
Loadid: SW: P0S3-06-3-00 ARM: PAS3ARM1 Boot: PC030301 DSP: PS03AT38
After:
Cisco Systems, Inc. Copyright 2000-2005
Cisco IP phone MAC: 0000:0000:0000
Loadid: SW: P0S3-8-12-00 ARM: PAS3ARM1 Boot: PC030301 DSP: 4.0(5.0)[A0]
And there’s a lot of bullshit written out there about what files the phone will try to retrieve via TFTP, most of which appears to be incorrect. After the flash to v8 here’s what the 7940G retrieved, where the string of zeros represents the hardware address of the handset.
Aug 21 00:29:59 box in.tftpd[28275]: RRQ from 192.168.1.6 filename CTLSEP000000000000.tlv
Aug 21 00:29:59 box in.tftpd[28276]: RRQ from 192.168.1.6 filename SEP000000000000.cnf.xml
Aug 21 00:30:00 box in.tftpd[28277]: RRQ from 192.168.1.6 filename P0S3-8-12-00.loads
Aug 21 00:30:21 box in.tftpd[28278]: RRQ from 192.168.1.6 filename SIPDefault.cnf
Aug 21 00:30:21 box in.tftpd[28279]: RRQ from 192.168.1.6 filename ./SIP000000000000.cnf
After the upgrade to v8, SIP registration succeeded. Believe me, that’s a lot easier that the rigamarole that I went through…
Asterisk Configuration Files
All of the relevant configuration files follow, half from the Asterisk documentation and half from Mario’s Adventures in Geekery. I especially like how numbers are dialed automatically after a pause, similar to a land-line phone… compared to many VOIP phones where you have to explicitly tell them to dial. Options in all CAPS have been sanitized and should be tailored to your specific install; note that while the configuration lists @gmail.com as the account name, you can use your Google Apps account without any problem.
sip.conf
[office]
type=friend
host=dynamic
secret=PASSWORD
callerid="Office <104>"
context=outbound
[alex]
type=friend
host=dynamic
secret=PASSWORD
callerid="Alex <102>"
context=outbound
[soft1]
type=friend
host=dynamic
secret=PASSWORD
callerid="Soft Phone 1 <101>"
context=outbound
jabber.conf
[general]
autoregister=yes
[google_apps_alex]
type=client
serverhost=talk.google.com
username=ACCOUNTNAME@gmail.com/RESOURCE_NAME
secret=PASSWORD
port=5222
status=xaway
statusmessage="Danger, Will Robinson..."
usetls=yes
usesasl=yes
timeout=100
gtalk.conf
[general]
context=google-in
bindaddr=0.0.0.0
allowguests=yes
externip=EXTERNAL_IP
allowguest=yes
[gtalk-alex]
username=ACCOUNTNAME@gmail.com
disallow=all
allow=ulaw
context=google-in
connection=google_apps_alex
[guest]
disallow=all
allow=ulaw
extensions.conf
[general]
static=yes
writeprotect=no
clearglobalvars=no
[globals]
CONSOLE=Console/dsp ; Console interface for demo
IAXINFO=guest ; IAXtel username/password
TRUNK=DAHDI/G2 ; Trunk interface
TRUNKMSD=1 ; MSD digits to strip (usually 1 or 0)
[ani]
exten => _X.,40000(ani),NoOp(ANI: ${EXTEN})
exten => _X.,n,Wait(0.25)
exten => _X.,n,Answer()
exten => _X.,n,Playback(vm-from)
exten => _X.,n,SayDigits(${CALLERID(ani)})
exten => _X.,n,Wait(1.25)
exten => _X.,n,SayDigits(${CALLERID(ani)}) ; playback again in case of missed digit
exten => _X.,n,Return()
[default]
exten => s,1,Set(CALLERID(name)=${DB(cidname/${CALLERID(num)})})
exten => s,n,Dial(SIP/soft1,10)
exten => s,n, Hangup
exten => 101, 1, Dial(SIP/soft1, 10)
exten => 102, 1, Dial(SIP/alex, 10)
exten => 104, 1, Dial(SIP/office, 10)
include => ani
exten => 778,1,Gosub(ani)
exten => 778,n,Hangup()
[google-in]
exten => ACCOUNTNAME@gmail.com, 1, GotoIf(${DB_EXISTS(gv_dialout/channel)}?bridged)
exten => ACCOUNTNAME@gmail.com, n, NoOp(Callerid ${CALLERID(name)})
exten => ACCOUNTNAME@gmail.com, n, Set(CALLERID(num)=${SHIFT(CALLERID(name),@)})
exten => ACCOUNTNAME@gmail.com, n, Set(CALLERID(name)=${DB(cidname/${CALLERID(num)})})
exten => ACCOUNTNAME@gmail.com, n, Dial(SIP/alex&SIP/office, 180, D(:1))
exten => ACCOUNTNAME@gmail.com, n(bridged),Bridge(${DB_DELETE(gv_dialout/channel)}, p)
[outbound]
include => seven-digit
include => local-devices
include => tollfree
include => talk-gmail-outbound
include => talk-numeric-outbound
include => dial-uri
[local-devices]
exten => _2, 1, Dial(SIP/alex,10)
exten => 102, 1, Dial(SIP/alex,10)
exten => _4, 1, Dial(SIP/office,10)
exten => 104, 1, Dial(SIP/office,10)
[tollfree]
exten => _1800NXXXXXX,1,Dial(Gtalk/gtalk_alex/${EXTEN}@voice.google.com)
exten => _1888NXXXXXX,1,Dial(Gtalk/gtalk_alex/${EXTEN}@voice.google.com)
exten => _1877NXXXXXX,1,Dial(Gtalk/gtalk_alex/${EXTEN}@voice.google.com)
exten => _1866NXXXXXX,1,Dial(Gtalk/gtalk_alex/${EXTEN}@voice.google.com)
[seven-digit]
exten => _NXXXXXX,1,Set(CALLERID(dnid)=1410${CALLERID(dnid)})
exten => _NXXXXXX,n,Goto(1410${EXTEN},1)
exten => _NXXNXXXXXX,1,Set(CALLERID(dnid)=1${CALLERID(dnid)})
exten => _NXXNXXXXXX,n,Goto(1${EXTEN},1)
[talk-gmail-outbound]
exten => _[a-z].@gmail.com,1,Dial(Gtalk/gtalk_alex/${EXTEN}@gmail.com)
exten => _[A-Z].@gmail.com,1,Dial(Gtalk/gtalk_alex/${EXTEN}@gmail.com)
[talk-numeric-outbound]
exten => _1NXXNXXXXXX,1,Dial(Gtalk/gtalk_alex/${EXTEN}@voice.google.com)
exten => _+1NXXNXXXXXX,1,Dial(Gtalk/gtalk_alex/${EXTEN}@voice.google.com)
[gv-agi-outbound]
exten => _1NXXNXXXXXX,1,AGI(google-voice-dialout.agi)
exten => _+1NXXNXXXXXX,1,AGI(google-voice-dialout.agi)
[dial-uri]
exten => _[a-z].,1,Dial(SIP/${EXTEN}@${SIPDOMAIN},120,tr)
exten => _[A-Z].,1,Dial(SIP/${EXTEN}@${SIPDOMAIN},120,tr)
exten => _X.,1,Dial(SIP/${EXTEN}@${SIPDOMAIN},120,tr)
This dialplan technically allows you to call Google Talk users by dialing user@gmail.com, although I haven’t had time to play with it.
What Remains
The end result is that outbound calls are routed through Google Voice with correct ANI (verified with MCI’s 1-800-444-4444 service). Inbound calls to my Google Voice number are routed to two handsets, with proper caller ID being displayed.
In the future, we’ll be adding another Google Voice trunk for my roommate. Outbound calls from a third handset will be routed through their account, since Asterisk has no troubles connecting to numerous Jabber XMPP streams simultaneously. Inbound calls from that account’s telephone number will be routed solely to the roommate’s handset.
There are some other fun things that I discovered, to be documented soon.
Tags: 7940g, asterisk, cisco, google, google voice, sip, voip