Showing posts with label Debian. Show all posts
Showing posts with label Debian. Show all posts

Friday, March 6, 2015

Create your own Cloud PBX with Asterisk and FreePBX Part 3

We can now go to our web browser and type the IP of our system to manage our FreePBX. The first thing we'll be required to do is set up the admin account. After that, we can just go to "FreePBX Administration" and begin setting things up.



I guess that's expected when you use a t2.micro instance
We can go right ahead and upgrade, this interface makes the whole process too easy.


 

 


And after it's finished, if we check again:


After it's done, we want to "Apply Config", then more modules will become available, so feel free to update everything, applying the config and checking again until there's nothing more to upgrade.

root@localhost:/usr/src/freepbx# amportal a ma refreshsignatures

After you enable and update the modules in FreePBX, you might see the following error: Symlink from modules failed

To correct this error just delete the list of failed files:

root@localhost:/usr/src/freepbx# cd /etc/asterisk
root@localhost:/etc/asterisk# rm ccss.conf confbridge.conf features.conf sip.conf iax.conf logger.conf extensions.conf sip_notify.conf

Then on the FreePBX webUI go to the ‘Module Admin’ and uninstall and reinstall the ‘Camp On’ module. This should resolve the Symlink issue.

Before we move any further, I'd like to recommend something: Head on over to Digium and buy their g729 codec. It's amazing. I am in no way affiliated with Digium but please consider this. Not just because of the fact that Asterisk is a great product and you should support its development team; g729 is really, really worth every cent of its cost.

These are the steps required to install the g729 codec.

We need to know our Asterisk version and our platform before we install it:

root@localhost:~# asterisk -V
Asterisk 11.16.0
root@localhost:~# uname -a
Linux localhost 3.2.0-77-virtual #112-Ubuntu SMP Tue Feb 10 15:34:22 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

Right, so we're running Asterisk 11.16.0 on a 64-bit system.

root@localhost:~# mkdir g729_codec
root@localhost:~# cd g729_codec/

After we've received the license key in our mailbox, we need to go to http://downloads.digium.com/pub/register and find which version of the file we need to download.

root@localhost:~/g729_codec# wget http://downloads.digium.com/pub/register/linux/register
root@localhost:~/g729_codec# chmod 700 register 
root@localhost:~/g729_codec# ./register

We now have to go to http://downloads.digium.com/pub/telephony/codec_g729/benchg729 and find which version of the file we need to download.

root@localhost:~/g729_codec# wget http://downloads.digium.com/pub/telephony/codec_g729/benchg729/x86-64/benchg729-1.0.8-x86_64
root@localhost:~/g729_codec# chmod 700 benchg729-1.0.8-x86_64
root@localhost:~/g729_codec# ./benchg729-1.0.8-x86_64 
Results:
Average for flavor 'generic' is 238 milliseconds of CPU time.
Average for flavor 'nocona' is 241 milliseconds of CPU time.
Average for flavor 'core2' is 237 milliseconds of CPU time.
Average for flavor 'opteron' is 440 milliseconds of CPU time.
Average for flavor 'opteron-sse3' is 438 milliseconds of CPU time.
Average for flavor 'barcelona' is 237 milliseconds of CPU time.

Recommended flavor for this system is 'core2' with an average of 237 milliseconds of CPU time.

So the recommended flavor for my Linode is 'core2'. Now go to http://my.digium.com/en/docs/G729/g729-download/ and select your platform and the recommended flavor, which in my case is core2.

root@localhost:~/g729_codec# wget http://downloads.digium.com/pub/telephony/codec_g729/asterisk-11.0/x86-64/codec_g729a-11.0_3.1.7-core2_64.tar.gz
root@localhost:~/g729_codec# tar zxvf codec*
root@localhost:~/g729_codec# cd codec*
root@localhost:~/g729_codec/codec_g729a-11.0_3.1.7-core2_64# cp codec*.so /usr/lib/asterisk/modules
root@localhost:~/g729_codec/codec_g729a-11.0_3.1.7-core2_64# amportal restart

That's it!

root@localhost:~/g729_codec# root@localhost:~# asterisk -rvvvv
Asterisk 11.16.0, Copyright (C) 1999 - 2013 Digium, Inc. and others.
Created by Mark Spencer 
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 11.16.0 currently running on localhost (pid = 839)
localhost*CLI> core show translation
         Translation times between formats (in microseconds) for one second of data
          Source Format (Rows) Destination Format (Columns)

            gsm  ulaw  alaw  g726 adpcm  slin lpc10  g729  ilbc g726aal2  g722 slin16 testlaw slin12 slin24 slin32 slin44 slin48 slin96 slin192
      gsm     - 15000 15000 15000 15000  9000 15000 15000 15000    15000 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
     ulaw 15000     -  9150 15000 15000  9000 15000 15000 15000    15000 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
     alaw 15000  9150     - 15000 15000  9000 15000 15000 15000    15000 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
     g726 15000 15000 15000     - 15000  9000 15000 15000 15000    15000 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
    adpcm 15000 15000 15000 15000     -  9000 15000 15000 15000    15000 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
     slin  6000  6000  6000  6000  6000     -  6000  6000  6000     6000  8250   8000    6000   8000   8000   8000   8000   8000   8000    8000
    lpc10 15000 15000 15000 15000 15000  9000     - 15000 15000    15000 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
     g729 15000 15000 15000 15000 15000  9000 15000     - 15000    15000 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
     ilbc 15000 15000 15000 15000 15000  9000 15000 15000     -    15000 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
 g726aal2 15000 15000 15000 15000 15000  9000 15000 15000 15000        - 17250  17000   15000  17000  17000  17000  17000  17000  17000   17000
     g722 15600 15600 15600 15600 15600  9600 15600 15600 15600    15600     -   9000   15600  17500  17000  17000  17000  17000  17000   17000
   slin16 14500 14500 14500 14500 14500  8500 14500 14500 14500    14500  6000      -   14500   8500   8000   8000   8000   8000   8000    8000
  testlaw 15000 15000 15000 15000 15000  9000 15000 15000 15000    15000 17250  17000       -  17000  17000  17000  17000  17000  17000   17000
   slin12 14500 14500 14500 14500 14500  8500 14500 14500 14500    14500 14000   8000   14500      -   8000   8000   8000   8000   8000    8000
   slin24 14500 14500 14500 14500 14500  8500 14500 14500 14500    14500 14500   8500   14500   8500      -   8000   8000   8000   8000    8000
   slin32 14500 14500 14500 14500 14500  8500 14500 14500 14500    14500 14500   8500   14500   8500   8500      -   8000   8000   8000    8000
   slin44 14500 14500 14500 14500 14500  8500 14500 14500 14500    14500 14500   8500   14500   8500   8500   8500      -   8000   8000    8000
   slin48 14500 14500 14500 14500 14500  8500 14500 14500 14500    14500 14500   8500   14500   8500   8500   8500   8500      -   8000    8000
   slin96 14500 14500 14500 14500 14500  8500 14500 14500 14500    14500 14500   8500   14500   8500   8500   8500   8500   8500      -    8000
  slin192 14500 14500 14500 14500 14500  8500 14500 14500 14500    14500 14500   8500   14500   8500   8500   8500   8500   8500   8500       -


Don't forget to reboot after all this is done.

OK, let's begin setting up our FreePBX:

Go to Settings -> Asterisk SIP settings.



What we want to do there is Disallow Anonymous Inbound SIP Calls, enter our IP and our network, define a STUN server (although not really required since this is a public IP), and check the codecs we're planning on using. There's no reason to use codecs our SIP provider doesn't.


Submit and Apply.

Now let's go to Applications -> Extensions. There we select "Generic CHAN SIP device" from the drop-down menu and click on submit.


This one's real easy. We only need to specify the extension number and maybe the Display name. Everything can be left to defaults, except maybe the "secret" under "Device Options", which will be the password your VoIP phone or softphone will need to give to your server, so you might want to change it to something more memorable. Or maybe not.


Submit and Apply Config and repeat the process for as many extensions you want.

Next, we'll create an IVR. We need this so that when someone dials our number, Asterisk will know that it needs to wait for the caller to enter the extension. Ideally we'd have a recording of a person saying "Please enter an extension" or the like, but many companies just leave this without one

So go to Applications -> IVR and select "Add a new IVR". Select a name and a description for it and make sure that "Direct Dial" points to "Extensions". Make a few more choices about what Asterisk should do in the case of an invalid destination or a timeout and finally add all the extensions you added before on the bottom of the page, making sure that "Extensions" is selected under "Destination".


Submit and Apply Config.

Time to create our trunk. Go to Connectivity -> Trunks and select "Add SIP (chan_sip) Trunk".

For a trunk name, I usually select my SIP provider's name. Enter your DID number in the "Outbound CallerID" and select "Force Trunk CID" under CID options.

In Outgoing Settings, Trunk Name just put the same thing you put under General Settings. In the PEER Details box, you should put the settings provided by your VoIP provider. If you weren't provided with any, you could just ask them or google for them.


If your VoIP provider provided you with user (not peer!) connection parameters, this is what you need to enter in USER context and USER Details. Otherwise delete everything from there and leave them blank.


If you left USER context and USER Details blank, then you will need to enter a REGISTER string. That will be in the form of:



username:secret@VoIP_Provider_host/DID

Submit and Apply Config.

Now, on to creating an Inbound route. Go to Connectivity -> Inbound Routes. Here, any Description will do. Just make sure your DID number is in the "DID number" field, and on the bottom of the page under "Set Destination" select IVR and your recently created ivr name (mine was ivr1). You might also want to check Signal RINGING and put 1 in the "Pause Before Answer" field.

Submit and Apply Config.

Finally, let's set up our outbound route. Go to Connectivity -> Outbound Routes. Just put anything in the "Description" field and make sure you put your DID number in the Route CID field. Make sure you enter a dial pattern for this. 

Some examples to get you started can be found here, but you can get away with just entering a dot (literally the character .) in the "match pattern" field. That means that it will route everything. Finally, choose the trunk you created earlier under "Trunk Sequence For Matched Routes".


Submit and Apply Config.

Some VoIP providers require you to have actually activated your DID, or allowed your account to make calls, etc. If you've gone through the process and you're sure there's nothing else to do on that end, you're set. All that remains is for you to get a softphone or a VoIP phone, set it up and start doing calls over IP.




Note that you might need to dial using an international prefix!

Thursday, March 5, 2015

Create your own Cloud PBX with Asterisk and FreePBX Part 1

Cloud PBXes are amazing. Is your internet down? Move your laptop/tablet/smartphone to the nearest publicly available WiFi spot and you can still catch that important call or conference using a softphone without anyone knowing. Couldn't make it in time to the office? Same. Ill and stayed at home? Use a softphone from your own network. Costs? Unbelievably low; any savings from running your own PBX server instead will start to show after years.

Well, let's create our own cloud PBX then.

Honestly, if you want to set up your own cloud PBX, you can't do any better than getting Ward Mundy's AMIs (here's a link to them) and just anwering a few questions his automated script will ask. Easy and secure.

But, what if we want to create our own? Start from the very beginning? Or maybe we don't want all the bells and whistles Ward Mundy provides us? Or perhaps we want to deploy on a different platform than Amazon? Or maybe CentOS just isn't our cup of tea?

Let's start deploying on Linode then.

I'm going to use Ubuntu 12.0.4 LTS for this.

First of all, we'll need configure our server with a static IP. For this example's sake my server will use 1.2.3.4.

root@localhost:~# vi /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
#iface eth0 inet dhcp
iface eth0 inet static
        address 1.2.3.4/24
        gateway 1.2.3.1

Since we're at it, we might as well if our DNS settings are OK:

root@localhost:~# vi /etc/resolv.conf
domain members.linode.com
search members.linode.com
nameserver 109.74.193.20
nameserver 109.74.194.20
nameserver 109.74.192.20
options rotate

Fell free to change your hostname if you wish, something I didn't do for my PBX. Now, let's update our system with the latest packages and install any necessary prerequisites for Asterisk and FreePBX.

root@localhost:~# apt-get update
root@localhost:~# apt-get upgrade
root@localhost:~# apt-get install build-essential wget libssl-dev libncurses5-dev libnewt-dev libxml2-dev libsqlite3-dev libiksemel-utils libiksemel-dev linux-headers-`uname -r` openssh-server apache2 mysql-server mysql-client bison flex php5 php5-curl php5-cli php5-mysql php-pear php-db php5-gd curl sox libmysqlclient15-dev mpg123 sqlite3 pkg-config automake libtool autoconf git subversion uuid uuid-dev

Now, instead of moving on to the actual Asterisk installation, we need to go through an additional, intermediate step here; Asterisk uses the dahdi_dummy kernel module, which means we'll need to use PvGrub and change a few things around as well. Let's install the complete linux kernel for virtual machines:

root@localhost:~# apt-get install linux-virtual

Now we'll be presented with an image that asks us where to install GRUB to. Don't worry about it really, our next step will be to remove this.

From https://www.linode.com/docs/applications/voip/deploy-voip-services-with-asterisk-and-freepbx-on-ubuntu-12-04-precise
All right, let's move on with purging the previous GRUB config and creating one of our own:

root@localhost:~# apt-get purge grub2 grub-pc
root@localhost:~# apt-get install grub
root@localhost:~# update-grub
Searching for GRUB installation directory ... found: /boot/grub
Searching for default file ... Generating /boot/grub/default file and setting the default boot entry to 0
Searching for GRUB installation directory ... found: /boot/grub
Testing for an existing GRUB menu.lst file ... 

Could not find /boot/grub/menu.lst file. Would you like /boot/grub/menu.lst generated for you? (y/N)
y

Now you need to edit the boot configuration file as such: replace # groot=(hd0,0) with # groot=hd(0) and # indomU=detect with # indomU=false

root@localhost:~# vi /boot/grub/menu.lst
....
## default grub root device
## e.g. groot=(hd0,0)
# groot=(hd0)
...
## specify if running in Xen domU or have grub detect automatically
## update-grub will ignore non-xen kernels when running in domU and vice versa
## e.g. indomU=detect
##      indomU=true
##      indomU=false
# indomU=false
....

Let's commit the changes:

root@localhost:~# update-grub

Now go to your Linode dashboard, click on the profile and select the pv-grub-x86_64 (if you're running a 64-bit system, you'll need to select pv-grub-x86-32 if you're running a 32-bit system) kernel.



All right, we're half-way there! Reboot your system and we'll be ready to begin our actual Asterisk installation!

root@localhost:~# reboot

You should have now booted and been greeted with this:

Welcome to Ubuntu 12.04.5 LTS (GNU/Linux 3.2.0-77-virtual x86_64)


Let's start with our iptables rules first. My server's IP is 1.2.3.4, my offices' external IP is 2.3.4.5 and my SIP provider's IP is 3.4.5.6. Here I allow SIP, IAX, IAX2 and MGCP connections from my SIP provider. If you're not interested in IAX, IAX2 and MGCP just skip the lines with ports 5036, 4569 and 2727:

root@localhost:~# iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
root@localhost:~# iptables -A INPUT -i lo -j ACCEPT
root@localhost:~# iptables -A 1.2.3.4 -j ACCEPT
root@localhost:~# iptables -A 2.3.4.5 -j ACCEPT
root@localhost:~# iptables -A INPUT -s 3.4.5.6 -p tcp -m multiport --dports 5060:5070 -j ACCEPT
root@localhost:~# iptables -A INPUT -s 3.4.5.6 -p udp -m multiport --dports 5060:5070 -j ACCEPT
root@localhost:~# iptables -A INPUT -s 3.4.5.6 -p udp -m udp --dport 4569 -j ACCEPT
root@localhost:~# iptables -A INPUT -s 3.4.5.6 -p udp -m udp --dport 5036 -j ACCEPT
root@localhost:~# iptables -A INPUT -s 3.4.5.6 -p udp -m udp --dport 2727 -j ACCEPT
root@localhost:~# iptables -A INPUT -j DROP
root@localhost:~# iptables -A FORWARD -j DROP
root@localhost:~# iptables -A OUTPUT -j ACCEPT
root@localhost:~# iptables-save > /etc/iptables.up.rules
root@localhost:~# vi /etc/init.d/iptables_fw
#!/bin/sh
### BEGIN INIT INFO
# Provides:          iptables_init
# Required-Start:    $local_fs $network
# Required-Stop:
# Default-Start:     2 3 4 5 
# Default-Stop:      0 1 6
# Short-Description: Firewall script
# Description:       Start iptables-based firewall
### END INIT INFO
#
iptables-restore < /etc/iptables.up.rules

root@localhost:~# chmod 755 /etc/init.d/iptables_fw
root@localhost:~# iptables -F
root@localhost:~# service iptables_fw start
root@localhost:~# update-rc.d iptables_fw defaults

Time to get moving with the actual Asterisk installation. Here we install PearDB and Google voice dependencies:

root@localhost:~# pear uninstall db
root@localhost:~# pear install db-1.7.14
root@localhost:~# cd /usr/src
root@localhost:/usr/src# wget https://iksemel.googlecode.com/files/iksemel-1.4.tar.gz
root@localhost:/usr/src# tar xf iksemel-1.4.tar.gz
root@localhost:/usr/src# cd iksemel-*
root@localhost:/usr/src/iksemel-1.4# ./configure
root@localhost:/usr/src/iksemel-1.4# make
root@localhost:/usr/src/iksemel-1.4# make install
root@localhost:/usr/src/iksemel-1.4# reboot

If you get a No releases available for package "pear.php.net/db" error while trying to reinstall PearDB just do:

root@localhost:~# pear install db-1.7.14
No releases available for package "pear.php.net/db"
root@localhost:~# mkdir /pear
root@localhost:~# cd /pear/
root@localhost:/pear# wget http://download.pear.php.net/package/DB-1.7.14.tgz
root@localhost:/pear# pear install DB-1.7.14.tgz

Download and install Asterisk, DAHDI, LIBPRI:

root@localhost:~# cd /usr/src/
root@localhost:/usr/src# wget http://downloads.asterisk.org/pub/telephony/dahdi-linux-complete/dahdi-linux-complete-current.tar.gz
root@localhost:/usr/src# wget http://downloads.asterisk.org/pub/telephony/libpri/libpri-1.4-current.tar.gz
root@localhost:/usr/src# wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-11-current.tar.gz
root@localhost:/usr/src# tar xvfz dahdi-linux-complete-current.tar.gz
root@localhost:~# cd dahdi-linux-complete-*
root@localhost:/usr/src/dahdi-linux-complete-2.10.0.1+2.10.0.1# make all
root@localhost:/usr/src/dahdi-linux-complete-2.10.0.1+2.10.0.1# make install
root@localhost:/usr/src/dahdi-linux-complete-2.10.0.1+2.10.0.1# make config
root@localhost:/usr/src/dahdi-linux-complete-2.10.0.1+2.10.0.1# cd /usr/src
root@localhost:~# tar xvfz libpri-1.4-current.tar.gz
root@localhost:~# cd libpri-*
root@localhost:/usr/src/libpri-1.4.15# make
root@localhost:/usr/src/libpri-1.4.15# make install
root@localhost:/usr/src/libpri-1.4.15# cd /usr/src
root@localhost:~# tar xvfz asterisk-11-current.tar.gz
root@localhost:~# cd asterisk-*
root@localhost:/usr/src/asterisk-11.16.0# ./configure
root@localhost:/usr/src/asterisk-11.16.0# contrib/scripts/get_mp3_source.sh
root@localhost:/usr/src/asterisk-11.16.0# make menuselect

This will bring up a menu. Make sure all the modules, sounds and features you want are included. You at least need to select Resource Modules-> res_xmpp, Channel Drivers -> chan_motif and Compiler Flags -> BUILD_NATIVE



Save and Exit...

root@localhost:/usr/src/asterisk-11.16.0# make
root@localhost:/usr/src/asterisk-11.16.0# make install
root@localhost:/usr/src/asterisk-11.16.0# make config
root@localhost:/usr/src/asterisk-11.16.0# cd /var/lib/asterisk/sounds
root@localhost:/var/lib/asterisk/sounds# wget http://downloads.asterisk.org/pub/telephony/sounds/asterisk-extra-sounds-en-gsm-current.tar.gz
root@localhost:/var/lib/asterisk/sounds# tar xfz asterisk-extra-sounds-en-gsm-current.tar.gz
root@localhost:/var/lib/asterisk/sounds# rm asterisk-extra-sounds-en-gsm-current.tar.gz

That's it! Asterisk is now fully installed! Time to install FreePBX:

root@localhost:/var/lib/asterisk/sounds# cd /usr/src
root@localhost:/usr/src# export VER_FREEPBX=2.11
root@localhost:/usr/src# git clone http://git.freepbx.org/scm/freepbx/framework.git freepbx
root@localhost:/usr/src# cd freepbx/
root@localhost:/usr/src/freepbx# git checkout release/${VER_FREEPBX}
root@localhost:/usr/src/freepbx# adduser asterisk --disabled-password --gecos "Asterisk User"
root@localhost:/usr/src/freepbx# chown asterisk. /var/run/asterisk
root@localhost:/usr/src/freepbx# chown -R asterisk. /etc/asterisk
root@localhost:/usr/src/freepbx# chown -R asterisk. /var/{lib,log,spool}/asterisk
root@localhost:/usr/src/freepbx# chown -R asterisk. /usr/lib/asterisk
root@localhost:/usr/src/freepbx# mkdir /var/www/html
root@localhost:/usr/src/freepbx# chown -R asterisk. /var/www/

Change PHP upload_max_filesize to 120MB and change the default Apache User and Group:

root@localhost:/usr/src/freepbx# sed -i 's/\(^upload_max_filesize = \).*/\120M/' /etc/php5/apache2/php.ini
root@localhost:/usr/src/freepbx# cp /etc/apache2/apache2.conf /etc/apache2/apache2.conf_orig
root@localhost:/usr/src/freepbx# sed -i 's/^\(User\|Group\).*/\1 asterisk/' /etc/apache2/apache2.conf
root@localhost:/usr/src/freepbx# service apache2 restart

Configure MySQL database:

root@localhost:/usr/src/freepbx# mysql_secure_installation
root@localhost:/usr/src/freepbx# export ASTERISK_DB_PW=amp109
root@localhost:/usr/src/freepbx# mysqladmin -u root create asterisk -p
root@localhost:/usr/src/freepbx# mysqladmin -u root create asteriskcdrdb -p
root@localhost:/usr/src/freepbx# mysql -u root asterisk -p < SQL/newinstall.sql 
root@localhost:/usr/src/freepbx# mysql -u root asteriskcdrdb -p < SQL/cdr_mysql_table.sql 
root@localhost:/usr/src/freepbx# mysql -u root -p -e "GRANT ALL PRIVILEGES ON asterisk.* TO asteriskuser@localhost IDENTIFIED BY '${ASTERISK_DB_PW}';"
root@localhost:/usr/src/freepbx# mysql -u root -p -e "GRANT ALL PRIVILEGES ON asteriskcdrdb.* TO asteriskuser@localhost IDENTIFIED BY '${ASTERISK_DB_PW}';"
root@localhost:/usr/src/freepbx# mysql -u root -p -e "flush privileges;"

Time to start it all up:

root@localhost:/usr/src/freepbx# ./start_asterisk start
root@localhost:/usr/src/freepbx# ./install_amp --username=asteriskuser --password=$ASTERISK_DB_PW --webroot /var/www
root@localhost:/usr/src/freepbx# amportal a ma download manager
root@localhost:/usr/src/freepbx# amportal a ma install manager
root@localhost:/usr/src/freepbx# amportal a ma installall
root@localhost:/usr/src/freepbx# amportal a reload

This should give you a few questions to answer, but accepting the default values is more than recommended EXCEPT the "Enter the IP ADDRESS or hostname used to access the AMP web-admin". Obviously you should enter your server's IP address there. In my case that would be 1.2.3.4.

Almost there:

root@localhost:/usr/src/freepbx# ln -s /var/lib/asterisk/moh /var/lib/asterisk/mohmp3
root@localhost:/usr/src/freepbx# amportal start 


After you enable and update the modules in FreePBX, you might see the following error: Symlink from modules failed

To correct this error just delete the list of failed files:

root@localhost:/usr/src/freepbx# cd /etc/asterisk
root@localhost:/etc/asterisk# rm ccss.conf confbridge.conf features.conf sip.conf iax.conf logger.conf extensions.conf sip_notify.conf

References: https://www.linode.com/docs/applications/voip/deploy-voip-services-with-asterisk-and-freepbx-on-ubuntu-12-04-precise
http://wiki.freepbx.org/pages/viewpage.action?pageId=1409028#InstallingFreePBXonUbuntu12.04Server%28PrecisePangolin%29-InstallandConfigureFreePBX

Sunday, November 2, 2014

Zen Load Balancer Performance Tests Part 2

All right. Time to upgrade the entire thing as described in the "Zen Load Balancer 3.0.3 Perfomance and Security Customization"series.

In summary:

Zen Load Balancer 3.0.3 Perfomance and Security Customization Part 5 was skipped, no NIC bonding was necessary;
Zen Load Balancer 3.0.3 Perfomance and Security Customization Part 4 was implemented, with the 1GB+ recommended settings and no high-memory usage settings;
Zen Load Balancer 3.0.3 Perfomance and Security Customization Part 3 was skipped, no point of checking for invalid packets or rate limit. This is just a test server;
Zen Load Balancer 3.0.3 Perfomance and Security Customization Part 2 was implemented;
Zen Load Balancer 3.0.3 Perfomance and Security Customization Part 1 was implemented (no filesystem tuning though), although the system's memory is 2GB, which means the only benefit here is the NX protection which won't be necessary since this is a test server (really, this should actually produce some overhead).

Let's go over my Zen Load Balancer's stats again:

Memory: 2048MB
CPU #1: 3.0GHz

I decided to use G-WAN as the backend, mainly because:

a) I hadn't used it before and it was a good opportunity to do so;
b) They brag a ridiculously high performance throughput -you know you have to at least check if it's partly true, and if it is, all the better as I'll stress Zen even more;
c) They have an enormous amount of haters; from other webserver fanbois who refuse to accept that a different webserver than the one they use might be better, to company plants that ticket non-existant vulnerabilities to try to prove that G-WAN isn't as good as its publishers claim. This alone can make me install software just out of spite.

So G-WAN it is, then!

The webpage that I will serve will be generated by this little C++ servlet here:
root@gwan:/opt/gwan_linux64-bit/0.0.0.0_8080/#0.0.0.0/csp# cat bench.cpp
// ============================================================================
// C++ servlet example to benchmark Zen and G-WAN Web Application Server   (http://gwan.ch/)
// ----------------------------------------------------------------------------
// bench.cpp: Concatenate a bunch of "Hello world!" until it gets to 400KB (encodings included).
//
// This code is based on the hello.cpp example by Thomas Meitz (gwan at jproxx dot com)
// ============================================================================
// imported functions:
//   get_reply(): get a pointer on the 'reply' dynamic buffer from the server
//   xbuf_cat(): like strcat(), but it works in the specified dynamic buffer
// ----------------------------------------------------------------------------
#include "gwan.h" // G-WAN definitions
#include <string>

using namespace std;

class Hello
{
public:
   void whatsup(xbuf_t* reply, string& str)
   {
      xbuf_cat(reply, (char*)str.c_str());
   }
};
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   string h("Hello World! ");
   do {
   h+=h;
   } while (h.length() < 240000);
   Hello hello;
   hello.whatsup(get_reply(argv), h);

   return 200; // return an HTTP code (200:'OK')
}
// ============================================================================
// End of Source Code
// ============================================================================


These were the farm settings:



Tests with Zen Load Balancer, customized as per the recommendations in my blog

The test will be the same as before; we'll send 10, 50 and 100 requests concurrently and 10000 in total, all trying to access the output of our script.

The tests will be run 5 times each. After that, we'll find the median value according to that point in time so as to eliminate any statistical aberrations.


D:\apache\Apache24\bin>abs -c10 -n10000 -f TLS1 https://192.168.0.99:443/?bench.cpp
This is ApacheBench, Version 2.3 <$Revision: 1554214 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.99 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        G-WAN
Server Hostname:        192.168.0.99
Server Port:            443
SSL/TLS Protocol:       TLSv1,RC4-SHA,1024,128

Document Path:          /?bench.cpp
Document Length:        425984 bytes

Concurrency Level:      10
Time taken for tests:   110.865 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      4262600020 bytes
HTML transferred:       4259840000 bytes
Requests per second:    90.20 [#/sec] (mean)
Time per request:       110.865 [ms] (mean)
Time per request:       11.086 [ms] (mean, across all concurrent requests)
Transfer rate:          37547.54 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        4   13   6.9     11     106
Processing:    45   98  24.3     97     756
Waiting:        1   11   8.0     10     103
Total:         57  111  25.0    109     775

Percentage of the requests served within a certain time (ms)
  50%    109
  66%    114
  75%    117
  80%    120
  90%    127
  95%    134
  98%    146
  99%    161
 100%    775 (longest request)

Whoa, got to say, this looks extremely promising. Let's do this four more times, find the median values and create a chart out of it.

This is our Zen's CPU usage for 10 concurrent connections:


This is not bad at all. No excessive CPU usage, and good performance as well for an HTTPS farm; apachebench reported around 90 requests per second.

For 50 concurrent connections:

Insane. Once again, no excessive CPU usage, and good performance for an HTTPS farm; apachebench reported almost 89 requests per second!

For 100 concurrent connections:

And this seals it. With our recommended performance upgrades, Zen is both faster and uses a lot less CPU. Just compare this with our previous results! For the record, apachebench reported around 80 requests per second.

Wednesday, October 8, 2014

Zen Load Balancer Performance Tests Part 1

This is my Zen Load Balancer's stats:

Memory: 2048MB
CPU #1: 3.0GHz

I decided to use G-WAN as the backend, mainly because:

a) I hadn't used it before and it was a good opportunity to do so;
b) They brag a ridiculously high performance throughput -you know you have to at least check if it's partly true, and if it is, all the better as I'll stress Zen even more;
c) They have an enormous amount of haters; from other webserver fanbois who refuse to accept that a different webserver than the one they use might be better, to company plants that ticket non-existant vulnerabilities to try to prove that G-WAN isn't as good as its publishers claim. This alone can make me install software just out of spite.

So G-WAN it is, then!

The webpage that I will serve will be generated by this little C++ servlet here:
root@gwan:/opt/gwan_linux64-bit/0.0.0.0_8080/#0.0.0.0/csp# cat bench.cpp
// ============================================================================
// C++ servlet example to benchmark Zen and G-WAN Web Application Server   (http://gwan.ch/)
// ----------------------------------------------------------------------------
// bench.cpp: Concatenate a bunch of "Hello world!" until it gets to 400KB (encodings included).
//
// This code is based on the hello.cpp example by Thomas Meitz (gwan at jproxx dot com)
// ============================================================================
// imported functions:
//   get_reply(): get a pointer on the 'reply' dynamic buffer from the server
//   xbuf_cat(): like strcat(), but it works in the specified dynamic buffer
// ----------------------------------------------------------------------------
#include "gwan.h" // G-WAN definitions
#include <string>

using namespace std;

class Hello
{
public:
   void whatsup(xbuf_t* reply, string& str)
   {
      xbuf_cat(reply, (char*)str.c_str());
   }
};
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   string h("Hello World! ");
   do {
   h+=h;
   } while (h.length() < 240000);
   Hello hello;
   hello.whatsup(get_reply(argv), h);

   return 200; // return an HTTP code (200:'OK')
}
// ============================================================================
// End of Source Code
// ============================================================================


These were the farm settings:



Tests with stock Zen Load Balancer 3.0.5 community edition

All right, let's begin with stock Zen. No updates and no extra software, except what we'll be using to collect our statistical data (nmon) and any packages needed to install vmware tools.
Also, ipv4 forwarding was set to 1 and I set up NAT to serve my backend, which was in a different network: iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE.

The test will be a little insane, we'll send 10, 50 and 100 requests concurrently and 10000 in total, all trying to access the output of our script.

The tests will be run 5 times each. After that, we'll find the median value according to that point in time so as to eliminate any statistical aberrations.

But first, let's do a few simple HTTP tests to see the difference in performance and resource usage:
D:\apache\Apache24\bin>ab -c10 -n10000 http://192.168.0.99:80/?bench.cpp
This is ApacheBench, Version 2.3 <$Revision: 1554214 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.99 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        G-WAN
Server Hostname:        192.168.0.99
Server Port:            80

Document Path:          /?bench.cpp
Document Length:        425984 bytes

Concurrency Level:      10
Time taken for tests:   112.649 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      4262600000 bytes
HTML transferred:       4259840000 bytes
Requests per second:    88.77 [#/sec] (mean)
Time per request:       112.649 [ms] (mean)
Time per request:       11.265 [ms] (mean, across all concurrent requests)
Transfer rate:          36952.96 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    4  60.4      0    3016
Processing:    16  108 184.0     94    3126
Waiting:        0   23  78.1     16    3047
Total:         16  112 193.4     94    3126

Percentage of the requests served within a certain time (ms)
  50%     94
  66%    109
  75%    125
  80%    125
  90%    156
  95%    172
  98%    203
  99%    266
 100%   3126 (longest request)

That's pretty sweet performance right there! Our webserver offers dynamic content of a 420KB page at almost 89 requests per second, without any caching mechanism to aid. Impressive. I'd go as far as to say that Zen is a bottleneck here.

So let's rinse and repeat 4 more times, calculate the median and create a graph.

This is our Zen's CPU usage for 10 concurrent connections:


For the record, apachebench reported around 84 requests per second on average.

For 50 concurrent connections:



For the record, apachebench reported around 78 requests per second on average.

Let's finish this with 100 concurrent connections:


For the record, apachebench reported around 75 requests per second on average.

As you can see, nothing dramatic, and CPU usage rarely exceeds 45%. Time to see what happens if we use HTTPS instead.

D:\apache\Apache24\bin>abs -c10 -n10000 https://192.168.0.99:443/?bench.cpp
This is ApacheBench, Version 2.3 <$Revision: 1554214 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.99 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        G-WAN
Server Hostname:        192.168.0.99
Server Port:            443
SSL/TLS Protocol:       TLSv1,RC4-SHA,1024,128

Document Path:          /?bench.cpp
Document Length:        425984 bytes

Concurrency Level:      10
Time taken for tests:   129.153 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      4262600018 bytes
HTML transferred:       4259840000 bytes
Requests per second:    77.43 [#/sec] (mean)
Time per request:       129.153 [ms] (mean)
Time per request:       12.915 [ms] (mean, across all concurrent requests)
Transfer rate:          32230.65 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   37 165.0     31    3079
Processing:    16   92 212.3     78    3157
Waiting:        0   17  86.0     16    3049
Total:         16  129 268.0    109    3172

Percentage of the requests served within a certain time (ms)
  50%    109
  66%    109
  75%    109
  80%    125
  90%    125
  95%    141
  98%    141
  99%    375
 100%   3172 (longest request)

Wow, our performance is close to what apachebench reported for 50 concurrent connections on plain HTTP requests!

So let's rinse and repeat 4 more times, calculate the median and create a graph.

This is our Zen's CPU usage for 10 concurrent connections:



The CPU usage cost is staggering; we rarely see it drop below 80%. For the record, apachebench reported almost 85 requests per second on average.

For 50 concurrent connections:


Look at that CPU usage! Most of the time is at 100%! For the record, apachebench reported almost 90 requests per second on average.

For 100 concurrent connections:


Considering the previous results, honestly I expected even worse! For the record, apachebench reported around 74 requests per second on average.

Tests with stock Zen Load Balancer 3.0.5 community edition + PCRE + TCMalloc + HOARD

If PCRE, tcmalloc (from the Google perftools package) and/or Hoard are available Pound will link against them. This will provide a significant performance boost and is highly recommended.

Let's see the perfomance boost for ourselves then.

root@zen-lb:~# apt-get install make cmake g++ libpcrecpp0 libpcre3-dev libpcre3 libpcre++0 libpcre++-dev libtcmalloc-minimal0 libgoogle-perftools0 libgoogle-perftools-dev
root@zen-lb:~# mkdir hoard
root@zen-lb:~# cd hoard/
root@zen-lb:~/hoard# wget --no-check-certificate https://github.com/emeryberger/Hoard/releases/download/3.10/Hoard-3.10-source.tar.gz
root@zen-lb:~/hoard# gunzip Hoard-3.10-source.tar.gz 
root@zen-lb:~/hoard# tar -xf Hoard-3.10-source.tar 
root@zen-lb:~/hoard# cd Hoard/src
root@zen-lb:~/hoard/Hoard/src# make linux-gcc-x86
root@zen-lb:~/hoard/Hoard/src# cp libhoard.so /usr/lib/.

Add this to our /etc/profile so that the hoard library is loaded:
root@zen-lb:~/hoard/Hoard/src# vi /etc/profile
export LD_PRELOAD=/usr/lib/libhoard.so

Test that it's loaded:
root@zen-lb:~# ldd /bin/ls
        linux-gate.so.1 =>  (0xb7767000)
        /usr/lib/libhoard.so (0xb7725000)
        libselinux.so.1 => /lib/libselinux.so.1 (0xb7704000)
        librt.so.1 => /lib/i686/cmov/librt.so.1 (0xb76fa000)
        libacl.so.1 => /lib/libacl.so.1 (0xb76f3000)
        libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb75ac000)
        libdl.so.2 => /lib/i686/cmov/libdl.so.2 (0xb75a8000)
        libpthread.so.0 => /lib/i686/cmov/libpthread.so.0 (0xb758f000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7499000)
        libm.so.6 => /lib/i686/cmov/libm.so.6 (0xb7473000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7455000)
        /lib/ld-linux.so.2 (0xb7768000)
        libattr.so.1 => /lib/libattr.so.1 (0xb7450000)


This is our Zen's CPU usage for 10 concurrent connections:


Well, it might just be me but I am seeing a difference, even though small. For the record, apachebench reported around 78 requests per second on average.

This is our Zen's CPU usage for 50 concurrent connections:


Wow, now that's a big difference. The CPU rarely goes above 80%. That's even better than 10 concurrent connections without our optimization libraries installed! For the record, apachebench reported around 72 requests per second on average. It looks like with our new libraries installed, there's a CPU/requests per second tradeoff.

For 100 concurrent connections:

The stress is noticeably less on the CPU. For the record, apachebench reported almost 75 requests per second on average.

Honestly, I wasn't planning on doing any HTTP tests for the stock+libs Zen as the CPU load isn't too large and therefore there isn't much benefit from it, but just out of curiosity, I did a series of tests for 50 concurrent HTTP connections to see difference in requests per second; it was around 76. That's a 14 requests/second drop. Not sure if I can call that a performance enhancement!

Finally, if you're curious, here are a few benchmarks from our Zen LB to the backend:

root@zen-lb:~# ab -c100 -n10000 http://192.168.0.99:80/?bench.cpp
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.99 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        G-WAN
Server Hostname:        192.168.0.99
Server Port:            80

Document Path:          /?bench.cpp
Document Length:        425984 bytes

Concurrency Level:      100
Time taken for tests:   37.843 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      4262600000 bytes
HTML transferred:       4259840000 bytes
Requests per second:    264.25 [#/sec] (mean)
Time per request:       378.429 [ms] (mean)
Time per request:       3.784 [ms] (mean, across all concurrent requests)
Transfer rate:          109999.38 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    7  12.2      6     221
Processing:    37  335 558.7    217    8559
Waiting:        1   71 232.8     17    1659
Total:         37  342 559.0    225    8574

Percentage of the requests served within a certain time (ms)
  50%    225
  66%    250
  75%    293
  80%    328
  90%    413
  95%   1172
  98%   1786
  99%   3626
 100%   8574 (longest request)

root@zen-lb:~# ab -c50 -n10000 http://192.168.0.99:80/?bench.cpp
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.99 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        G-WAN
Server Hostname:        192.168.0.99
Server Port:            80

Document Path:          /?bench.cpp
Document Length:        425984 bytes

Concurrency Level:      50
Time taken for tests:   36.863 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      4262600000 bytes
HTML transferred:       4259840000 bytes
Requests per second:    271.28 [#/sec] (mean)
Time per request:       184.313 [ms] (mean)
Time per request:       3.686 [ms] (mean, across all concurrent requests)
Transfer rate:          112924.62 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   4.3      0      83
Processing:    27  169 338.1     84   11517
Waiting:        3   66 227.5     19    3014
Total:         27  171 338.3     85   11517

Percentage of the requests served within a certain time (ms)
  50%     85
  66%     97
  75%    111
  80%    129
  90%    305
  95%    363
  98%   1357
  99%   1545
 100%  11517 (longest request)

root@zen-lb:~# ab -c10 -n10000 http://192.168.0.99:80/?bench.cpp
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.99 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        G-WAN
Server Hostname:        192.168.0.99
Server Port:            80

Document Path:          /?bench.cpp
Document Length:        425984 bytes

Concurrency Level:      10
Time taken for tests:   38.634 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      4262600000 bytes
HTML transferred:       4259840000 bytes
Requests per second:    258.84 [#/sec] (mean)
Time per request:       38.634 [ms] (mean)
Time per request:       3.863 [ms] (mean, across all concurrent requests)
Transfer rate:          107746.80 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       2
Processing:    23   38   5.7     38      77
Waiting:        4    9   2.6      9      38
Total:         23   39   5.7     38      78

Percentage of the requests served within a certain time (ms)
  50%     38
  66%     40
  75%     42
  80%     43
  90%     45
  95%     48
  98%     53
  99%     57
 100%     78 (longest request)


Tuesday, September 9, 2014

Zen Load Balancer 3.0.3 Perfomance and Security Customization Part 5

Now, let's move on to NIC bonding. This is useful if one of our NICs goes dead; we obviously want to make sure if that happens, we have another standing by that will take over.

Many admins have a dedicated VLAN for cluster synchronization purposes. Some others just connect two nodes using a crossover cable. That means that if one NIC goes down, all hell breaks loose; if it is the cluster synchronization NIC, then both nodes think that the other node has gone down and they both try to become masters causing havoc to the network; in any other case your frontends and backends seem to be down due to your NIC being dead.

So in that case, we employ NIC bonding. There are actually a few types of network bonding (from here): 
  • balance-rr or 0: Round-robin policy: Transmit packets in sequential order from the first available slave through the last.  This mode provides load balancing and fault tolerance.
  • active-backup or 1: Active-backup policy: Only one slave in the bond is active.  A different slave becomes active if, and only if, the active slave fails. The bond's MAC address is externally visible on only one port (network adapter) to avoid confusing the switch.
    In bonding version 2.6.2 or later, when a failover occurs in active-backup mode, bonding will issue one or more gratuitous ARPs on the newly active slave. One gratutious ARP is issued for the bonding master interface and each VLAN interfaces configured above it, provided that the interface has at least one IP address configured.  Gratuitous ARPs issued for VLAN interfaces are tagged with the appropriate VLAN id. This mode provides fault tolerance.
  • balance-xor or 2: XOR policy: Transmit based on the selected transmit hash policy.  The default policy is a simple [(source MAC address XOR'd with destination MAC address) modulo slave count].  Alternate transmit policies may be selected via the xmit_hash_policy option. This mode provides load balancing and fault tolerance.
  • broadcast or 3: Broadcast policy: transmits everything on all slave interfaces.  This mode provides fault tolerance.
  • 802.3ad or 4: IEEE 802.3ad Dynamic link aggregation.  Creates aggregation groups that share the same speed and duplex settings.  Utilizes all slaves in the active aggregator according to the 802.3ad specification. Slave selection for outgoing traffic is done according to the transmit hash policy, which may be changed from the default simple XOR policy via the xmit_hash_policy option. Note that not all transmit policies may be 802.3ad compliant, particularly in regards to the packet mis-ordering requirements of section 43.2.4 of the 802.3ad standard.  Differing peer implementations will have varying tolerances for noncompliance. Prerequisites:
    1. Ethtool support in the base drivers for retrieving the speed and duplex of each slave.
    2. A switch that supports IEEE 802.3ad Dynamic link aggregation.
    3. Most switches will require some type of configuration to enable 802.3ad mode.
  • balance-tlb or 5: Adaptive transmit load balancing: channel bonding that does not require any special switch support.  The outgoing traffic is distributed according to the current load (computed relative to the speed) on each slave.  Incoming traffic is received by the current slave.  If the receiving slave fails, another slave takes over the MAC address of the failed receiving slave. Prerequisites:
    1. Ethtool support in the base drivers for retrieving the speed and duplex of each slave.
  • balance-alb or 6: Adaptive load balancing: includes balance-tlb plus receive load balancing (rlb) for IPV4 traffic, and does not require any special switch support.  The receive load balancing is achieved by ARP negotiation. The bonding driver intercepts the ARP Replies sent by the local system on their way out and overwrites the source hardware address with the unique hardware address of one of the slaves in the bond such that different peers use different hardware addresses for the server. Receive traffic from connections created by the server is also balanced. When the local system sends an ARP Request the bonding driver copies and saves the peer's IP information from the ARP packet.  When the ARP Reply arrives from the peer, its hardware address is retrieved and the bonding driver initiates an ARP reply to this peer assigning it to one of the slaves in the bond. A problematic outcome of using ARP negotiation for balancing is that each time that an ARP request is broadcast it uses the hardware address of the bond.  Hence, peers learn the hardware address of the bond and the balancing of receive traffic collapses to the current slave.  This is handled by sending updates (ARP Replies) to all the peers with their individually assigned hardware address such that the traffic is redistributed.  Receive traffic is also redistributed when a new slave is added to the bond and when an inactive slave is re-activated.  The receive load is distributed sequentially (round robin) among the group of highest speed slaves in the bond. When a link is reconnected or a new slave joins the bond the receive traffic is redistributed among all active slaves in the bond by initiating ARP Replies with the selected mac address to each of the clients. The updelay parameter (detailed below) must be set to a value equal or greater than the switch's forwarding delay so that the ARP Replies sent to the peers will not be blocked by the switch. Prerequisites:
    1. Ethtool support in the base drivers for retrieving the speed and duplex of each slave.
    2. Base driver support for setting the hardware address of a device while it is open. This is required so that there will always be one slave in the team using the bond hardware address (the curr_active_slave) while having a unique hardware address for each slave in the bond. If the curr_active_slave fails its hardware address is swapped with the new curr_active_slave that was chosen.
In this example we will employ the active-backup method. This is the safest method to use. Most googlers like link aggregation, since an aggregation group will increase the overall bandwidth of the resulting interface.

Let's suppose we want to bond eth8 and eth3 to an interface with the IP 172.16.0.8/22, eth9 and eth4 to an interface with the IP 172.16.4.8/22, and eth0 and eth9 to an interface with the IP 172.16.8.8/23:

root@zen-lb:~# apt-get install ifenslave-2.6
root@zen-lb:~# vi /etc/network/interfaces
auto lo
iface lo inet loopback
auto bond0
iface bond0 inet static
    address 172.16.0.8
    netmask 255.255.252.0
    network 172.16.0.0
    gateway 172.16.0.1
    slaves eth8 eth3
    bond-mode active-backup
    bond-miimon 100
    bond-primary eth8
auto bond1
iface bond1 inet static
    address 172.16.4.8
    netmask 255.255.252.0
    network 172.16.4.0
    slaves eth9 eth4
    bond-mode active-backup
    bond-miimon 100
    bond-primary eth9
auto bond2
iface bond2 inet static
    address 172.16.8.8
    netmask 255.255.254.0
    network 172.16.8.0
    slaves eth0 eth5
    bond-mode active-backup
    bond-miimon 100
    bond-primary eth0

bond-primary is the NIC that will be our primary device.
bond-miimon is how often the link state will be polled.
So, in our case, every 100ms eth8 and eth3 will be polled; if eth8 is up, then this will serve our incoming and outgoing requests, otherwise eth3 will take charge.

root@zen-lb:~# rm /usr/local/zenloadbalancer/config/if_eth*
root@zen-lb:~# vi /usr/local/zenloadbalancer/config/if_bond0_conf
bond0::172.16.0.8:255.255.252.0:up::
root@zen-lb:~# vi /usr/local/zenloadbalancer/config/if_bond1_conf
bond1::172.16.4.8:255.255.252.0:up::
root@zen-lb:~# vi /usr/local/zenloadbalancer/config/if_bond2_conf
bond2::172.16.8.8:255.255.254.0:up::
root@zen-lb:~# vi /usr/local/zenloadbalancer/config/global.conf_conf
.....
#System Default Gateway
$defaultgw="172.16.0.1";
#Interface Default Gateway
$defaultgwif="bond0";
.....
#Also change the ntp server
.....
$ntp="0.europe.pool.ntp.org";
.....

You might also want to change these particular ports on your switch to portfast. That way, you won't have to wait for the forward delay (and as far as these particular ports go, forward delay is useless any way) and the transition will be seemless.

All right, let's see if it all works:

root@zen-lb:~# cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.5.0 (November 4, 2008)

Bonding Mode: fault-tolerance (active-backup)
Primary Slave: None
Currently Active Slave: eth8
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

Slave Interface: eth8
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:19:b9:e4:12:a3

Slave Interface: eth3
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:1f:29:57:cf:fe

root@zen-lb:~# cat /proc/net/bonding/bond1
Ethernet Channel Bonding Driver: v3.5.0 (November 4, 2008)

Bonding Mode: fault-tolerance (active-backup)
Primary Slave: None
Currently Active Slave: eth9
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

Slave Interface: eth9
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:19:b9:e4:12:a5

Slave Interface: eth4
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:1f:29:0d:69:81

root@zen-lb:~# cat /proc/net/bonding/bond2
Ethernet Channel Bonding Driver: v3.5.0 (November 4, 2008)

Bonding Mode: fault-tolerance (active-backup)
Primary Slave: None
Currently Active Slave: eth0
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

Slave Interface: eth0
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:1f:29:57:cf:fd

Slave Interface: eth5
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:1f:29:0d:69:80

And if you try to disconnect, or otherwise bring down any of the primary slave interfaces you'll see that the active backup will come up almost instantly (provided you set those ports to portfast on your switch).