Tuesday, June 1, 2010

Google Voice / SIP Solutions

For this project I was to have a Google Voice / Gizmo5 SIP solution.
I put for the the following requirements for myself:
  1. No dedicated server.
  2. Could work with existing HW SIP adapters as well as SW softphones
  3. Could dial out 800 numbers w/o involving GV
  4. Could dial out 411 w/o involving GV
  5. Could receive GV calls
  6. Ability to initiate GV calls
Not having a dedicated server instantly rules out any FreePBX, Trixbox, or PBX in a flash type solutions. So this meant I had to adapt bits and pieces from a lot of solutions that were already out in the open. Surely others want to do solutions similar to mine, so here's how!

I've already got a Gizmo5 account and linked it to my GV account. Directions on how to do this can be found at: http://www.jrin.net/2009_07_26/use-gizmo5-for-free-calls-with-google-voice. If you don't have a Gizmo5 account, there are other solutions out there that will route over a different trunk like ipkall or sipgate, but I imagine they will introduce additional latency to the equation due to the additional hops from PSTN to SIP and back.

Not having a dedicated server means that I likely needed to install Asterisk directly onto a router. Asterisk is a bit of a beast space wise though, and the flash on my old router was a bit tiny. Being that I was overdue for a new one and wanted Wireless N and USB anyhow, I ended up purchasing a Dlink DIR-825 instead.

Flash Firmware
The DIR-825 is supported by OpenWRT's current release, "backfire". Unfortunately I had a little bit of trouble getting it flashed in the first place. The existing backfire images are not properly building the headers to allow it to be flashed. This has been fixed in trunk (images newer than April 26th). To workaround the issue in backfire, the headers can be crafted as follows, using the original firmware image and a backfire image respectively:

dd if=dir825_revB_firmware_202NA.bin of=new.bin
dd if=/dev/zero of=new.bin bs=4194304 count=1 conv=notrunc
dd if=openwrt-ar71xx-dir-825-b1-squashfs-backup-loader.bin of=new.bin bs=2424836 count=1 conv=notrunc







Once the initial firmware is loaded, the device still won't support a USB overlay - so don't install Asterisk yet.

Prepare External Rootfs
You'll have to follow the directions at http://wiki.openwrt.org/doc/howto/rootfsonexternalstorage for information on how to build your own firmware image with support for externalstorage overlays. I found the documentation on how to configure the new /etc/config/fstab horrible, so here's the one I finally came up with:

config global automount
option from_fstab 1
option anon_mount 1

config mount
option target /overlay
option device /dev/sda1
option fstype ext4
option options rw,sync
option enabled 1
option enabled_fsck 0
option is_rootfs 1

Reboot the system and make sure that you actually are using the external rootfs before you proceed to install Asterisk. You'll notice that your flash drive is mounted on /overlay.

root@router:~# df -h
Filesystem Size Used Available Use% Mounted on
/dev/root 1.9M 1.9M 0 100% /rom
tmpfs 30.3M 1.3M 29.0M 4% /tmp
tmpfs 512.0K 0 512.0K 0% /dev
/dev/mtdblock4 3.3M 264.0K 3.0M 8% /tmp/overlay
/dev/sda1 3.7G 95.4M 3.4G 3% /overlay
mini_fo:/overlay 1.9M 1.9M 0 100% /

Now that you have tons of space, configure the general stuff on the router to your liking. 





Install Python

Python is needed for the AGI script that connects to the GV website.


opkg install python python-openssl

Install Asterisk
I purposely didn't mention to install Asterisk yet. The Asterisk 1.6 that comes with backfire doesn't support AGI, which we'll need for the dialer. What you'll want to do is grab the one from trunk instead. The easiest way to do this is to comment out the backfire repository in /etc/opkg.conf and add the trunk one temporarily.

src/gz packages http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages

Now you can go and install Asterisk 1.6.

opkg install asterisk16 asterisk16-app-system asterisk16-codec-a-mu asterisk16-codec-g726 asterisk16-format-g726 asterisk16-format-g729 asterisk16-func-db asterisk16-res-agi asterisk16-sounds

Revert the repositories back to backfire now. You don't want to be pulling anything else from Trunk.

Next we need to configure Asterisk, the dialplans, and the sip settings. First thing you need to do is make sure it is set to use the proper directories.

Configure Asterisk
Modify the first line in /etc/asterisk/asterisk.conf from:

[directories](!) ; remove the (!) to enable this

to

[directories] ; remove the (!) to enable this

Configure SIP Settings
In the [general] section of /etc/asterisk/sip.conf add a registration for the G5 server:

register => 1747000000:secretpassword@proxy01.sipphone.com

Now, add a new section for the account's inbound connections. If you plan to use G5 for outbound as well, change the type to friend.

[proxy01.sipphone.com]
type=user
context=default
disallow=all
allow=gsm
allow=ulaw
allow=ilbc
dtmfmode=rfc2833
host=proxy01.sipphone.com
insecure=port,invite
username=1747000000
secret=secretpassword
canreinvite=no

Next we add a section for our HW device or softphone.
[101]
username=101
secret=101
type=friend
callerid="Mario"
host=dynamic
context=outgoing
outgoinglimit=1
incominglimit=1
canreinvite=no

Configure Dialplan Settings
The dialplan is stored in /etc/asterisk/extensions.conf.
We're going to have two separate starting contexts - for outgoing and incoming calls. For the outgoing context, we route all 411 and 800 numbers to a free 800 terminator like ideasip. For anything else that looks like a regular number we're going to use a google voice AGI script (which we'll get to later).

[outgoing]
exten => _411, 1, Dial(SIP/18004664411@proxy.ideasip.com,60)
exten => _1800NXXXXXX,1,Dial(SIP/${EXTEN}@proxy.ideasip.com,60)
exten => _1888NXXXXXX,1,Dial(SIP/${EXTEN}@proxy.ideasip.com,60)
exten => _1877NXXXXXX,1,Dial(SIP/${EXTEN}@proxy.ideasip.com,60)
exten => _1866NXXXXXX,1,Dial(SIP/${EXTEN}@proxy.ideasip.com,60)
exten => _1NXXNXXXXXX,1,AGI(google-voice-dialout.agi)

For the incoming context we need to check whether or not we are coming from a GV bridge or a regular SIP call. If it's a GV bridge we bridge the calls together, otherwise we forward it to our extension.

[default]
exten => s,1,GotoIf(${DB_EXISTS(gv_dialout/channel)}?bridged)
exten => s,n,Dial(SIP/101,10)
exten => s,n,Hangup
exten => s,n(bridged),Bridge(${DB_DELETE(gv_dialout/channel)}, p)

Google Voice AGI
Now fetch the google voice AGI script from Paul Marks at http://www.pmarks.net/posted_links/google-voice-dialout.agi. Configure your info at the top of it, and throw it in /usr/lib/asterisk/agi-bin.

Start it out and Test
To start asterisk run it's init script

/etc/init.d/asterisk start

I think that should cover everything. You should be able to add your info to your softphone or SIP adapter now.