How to isolate a second router from the rest of your local network

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

I was recently asked how to solve a particular problem and I came up with what I think is an interesting solution, especially given my overall rather limited knowledge of networking.  The issue was this: In the home in question, they have cable broadband and a router that feeds jacks throughout the house.  For security reasons, the homeowner never installed any kind of wireless networking (even though his primary router supports it, he keeps it turned off).  Also his primary router is down in the basement.

Recently he got his wife a Motorola XOOM table computer and wouldn’t you know, it requires Wi-Fi access to connect to the Internet.  In order to extend the range, and so that he or his wife could easily turn off the Wi-Fi when the XOOM isn’t in use, he bought a second Wi-Fi router and put it upstairs.  Note that this router is connected BEHIND the original router in the basement.  In other words, the sequence of connection is as follows:

Cable Modem —> Basement (Primary) Router —> Upstairs (Wi-Fi) Router —> Tablet Computer

Now, as I said, he is very security conscious.  So the question he asked me is, if someone managed to break into his Wi-Fi, is there a way to set it up so that they could ONLY get to the Internet, and not to any other system on his local network.  I said I didn’t know, but to first try accessing other machines on his network (the ones that had web interfaces, anyway) from the XOOM.  Turned out that he could do so without any problem.  Because the Wi-Fi router used a different network segment from the original (addresses in the 192.168.2.x range, whereas the original router handed out address in the 192.168.0.x range), as far as anything connected to the Wi-Fi router was concerned, anything on the primary router might as well have been on the Internet (please forgive the non-technical explanation, I’m probably missing several technical details here, but that’s the gist of the problem).

I didn’t think it would be a good idea to try to make the Wi-Fi router use the same address space for both WAN and LAN, and while I could assign it a static IP address on the WAN side, it had to be able to reach the router/gateway at 192.168.0.1.  So here is what we did.

On the PRIMARY router, we took a look at the LAN settings and found that its DHCP server was assigning addresses starting at 192.168.0.2.  We changed that to start at 192.168.0.5 (probably could have used 192.168.0.4 in retrospect).

This way, we could change the WAN address of the Wi-Fi router to use a STATIC IP address of 192.168.0.2, and (this is the important part) a NETMASK of 255.255.255.252.

This means that as far as the Wi-Fi router is concerned, there are only four valid IP addresses in the 192.168.0.x range:

192.168.0.0 (not used)
192.168.0.1 (primary router/gateway)
192.168.0.2 (Wi-Fi router)
192.168.0.3 (Reserved for “broadcast” as far as Wi-Fi router is concerned)

One thing to remember is that after changing the DHCP assignment on the PRIMARY router is that computers already using IP address 192.168.0.2 and 192.168.0.3 will not automatically vacate those addresses until their DHCP lease comes up for renewal.  So if you change the second router’s WAN address to 192.168.0.2, it may not actually be able to connect until the computer or device currently on 192.168.0.2 “loses its lease”.  Rebooting the primary router may help, but in some cases you may have to track down the computer with the conflicting address and shut it off, or if you know how, renew its IP address assignment (this can usually be done from within the network settings panel).  Eventually, though, it should work, and at that point you should find that devices connected to the secondary router cannot connect to any addresses in the 192.168.0.x range outside the three mentioned above, which means they won’t be able to “see” anything else on your network that’s been assigned a DHCP address.

This tip falls into the category of “it worked in this particular situation, but I don’t guarantee it will work for you”.  So if you try this, be sure to test to make sure that the other machines on your primary network are actually unreachable from the secondary router.

Now let the comments begin, telling me how there’s a better way to do this, or why it won’t work, or something to that effect…

A Perl script to rewrite the "static" IP address in the FreePBX Asterisk SIP Settings when it is changed by your ISP

 

Important
This is a heavily edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

This was originally posted in August, 2011. Unless you are deeply in love with Perl, I suggest you also take a look at the newer article, A Bash script to rewrite the "static" IP address in the FreePBX Asterisk SIP Settings when it is changed by your ISP. Although it is still considered experimental, it is simpler than the script shown here, does not require the installation of additional modules, and the text has been updated somewhat to reflect the changes in FreePBX since this article was written.

This post is going to be a bit long because I first need to explain the “why” behind this script, then how to obtain the prerequisite Perl modules, then the script itself and how to test it after installation.

If you are using a recent version of Asterisk and FreePBX you may be using the Asterisk SIP Settings module (under the “Tools” tab) to automatically set various SIP parameters.  This module is a great help to those who don’t know what they are doing, but there is a trap for the unwary (and in this case it’s NOT the fault of FreePBX – it’s a longstanding bug in Asterisk that’s the problem).

At the top of the Asterisk SIP Settings configuration page, in the NAT Settings section, there are two options that can be set.  The first is NAT and there are four possible choices:

  • yes = Always ignore info and assume NAT
  • no = Use NAT mode only according to RFC3581
  • never = Never attempt NAT mode or RFC3581
  • route = Assume NAT, don’t send rport

In theory, if you have a fixed IP address AND your Asterisk server is not behind an external router that does NAT translation, you should use “no” (and most of the rest of this article will not be relevant to you).  This article is intended more for home and SOHO users that both have their Asterisk server behind a hardware router of some kind, and that get their broadband service from a company that occasionally changes their IP address without warning.  For such users, the preferred setting is “yes”.  I’m not enough of a networking guru to tell you under what circumstances one of the other settings might be appropriate (if you understand this stuff, feel free to leave a comment and enlighten us).

FreePBX: Asterisk SIP Settings page, NAT Settings (Public IP Option)

It’s the next set of settings that can get us into trouble.  This is the IP Configuration and there are three possible choices:

  • Public IP
  • Static IP
  • Dynamic IP

If your IP address never changes AND you aren’t behind a hardware firewall then you can usually just set this to “Public IP” and let it go at that.  You will not be asked to fill in any other values.  But most users that are not in that situation will pick one of the other two choices, and this is where the problem arises.  Conventional wisdom has it that if your ISP ever changes your IP address without advance warning (which is the case for most cable broadband and DSL users), you should use the Dynamic IP setting.  In this case there is an auto-configure button that will fill out the fields for you, although you may need to fill in the Dynamic Host field yourself.  This is the “External FQDN as seen on the WAN side of the router and updated dynamically, e.g. mydomain.dyndns.com” (as explained if you mouse over the words “Dynamic Host”).  You can use a DynDNS address (or an address from a similar service) or an address you have purchased.  But the problem is that for some users, THIS METHOD SIMPLY DOES NOT WORK.

FreePBX: Asterisk SIP Settings page, NAT Settings (Dynamic IP Option)

If you try to use Dynamic IP and it won’t work for you, what happens is you will get all sorts of weird errors.  You may get one way audio, some calls may disconnect for no apparent reason after about five seconds, and you will see other weird errors in your CLI.  If you change this setting to “Static IP” and click the auto-configure button and then submit the changes, the problems magically go away – UNTIL your ISP changes your IP address, at which point you suddenly have no connectivity to the outside world.  If you ask for help, everybody and their brother will tell you to use the Dynamic IP setting, and the minute you try that you’ll get all the weird errors again.

FreePBX: Asterisk SIP Settings page, NAT Settings (Static IP Option)

So if that’s your situation, you need this Perl script.  Coupled with a cron job, it goes out and checks your IP address every five minutes and if it notices it has changed, it changes it in the MySQL database (same as if you entered it into the External IP text box on the Asterisk SIP Settings configuration page) and then reloads Asterisk.  Therefore, you can use the Static IP method and it hopefully it will work reliably.  If and when your IP address changes, you should only be down for about five to ten minutes at most (hopefully your broadband provider usually does such changes in the middle of the night!).

Prerequisites:

You still have to use a Dynamic DNS service to keep track of your IP address if you want external extensions to be able to find your server on the Internet.  It’s not required for this script to work, though, so I won’t say any more about that except to note that if you use a recent vintage hardware router, it probably has DDNS support built in.

You may have to install some Perl modules on your system.  This script uses two or three: WWW::Mechanize (only if you use the first variation of the script shown below), Data::Validate::IP, and DBD::mysql.  There are typically two ways to install any missing Perl modules on your system.  One is to do this from the Linux command prompt:

perl -MCPAN -e shell

This will put you into a Perl CPAN shell and if it’s the very first time you’ve ever run this, it may ask you to do some configuration first.  Go ahead and do it.  If you don’t know how to answer a particular question, accepting the default is usually a pretty safe bet (if you disagree with me on this, then you know enough to know how to answer the questions, so you don’t need my help). However there are a couple questions related to buffers where you have the option to not create one, and I usually don’t because I don’t spend much time in the Perl shell.  Just read the questions and either use the default answer, or another suggested answer that fits your preferences.  When it comes time to pick servers (from which you will download modules), just pick two or three that are close to you.

After you’ve done the configuration, just install each module (if you already have it, it may say “nothing to do” and stop).  Alternately, if you configured Perl to ask before downloading dependencies, you may need to answer “yes” a few times to allow dependencies to be downloaded and installed. To install the required modules from within the CPAN shell, just do these, one at a time:

install WWW::Mechanize (only if you use the first variation of the script)
install Data::Validate::IP
install DBD::mysql   (you might already have this).

To quit the CPAN shell, just type quit and press Enter.

Alternately, in some distributions you can get certain Perl modules from the distribution’s repository.  For example, in Centos you may be able to use:

yum install perl-WWW-Mechanize.noarch (only if you use the first variation of the script)
yum install perl-Data-Validate-IP.noarch
yum install perl-DBD-MySQL.noarch

Or in any Debian-based Linux, including Ubuntu Server, try these:

sudo apt install libwww-mechanize-perl (only if you use the first variation of the script)
sudo apt install libdata-validate-ip-perl
sudo apt install libdbd-mysql-perl

It’s likely you already have the Mysql module. Depending on your distro you may have to leave off the .noarch, or find a specific version in an appropriate repository.  Installing from the CPAN shell make take a bit more time for the initial configuration, and some people find the CPAN shell difficult to use, but you will always get the correct version of the module.

Note that if you use Webmin, there is a third way – you can install modules from the Other | Perl Modules | Install Module page.  BUT, that may not work correctly until you have configured CPAN as mentioned above.  Don’t let that stop you from trying it, though!

The Script:

There are now two versions of this script. One uses WWW::Mechanize to get your IP address from a web site that returns only your IP address, while the second uses a dig command to get your IP address. We recommend the second one, since public sites that return only your IP address have a nasty habit of disappearing. Note that as always, these WILL overflow the lines in WordPress, so you will want to cut and paste your preferred script into a text editor.  Also note that WordPress MAY change apostrophes and quotes into “prettified” versions, and if it does that will totally mess up Perl.  I’m going to put this in a preformatted text block so hopefully WordPress won’t change anything (it doesn’t appear that it has), but you never know.  One final note, don’t confuse backticks (`) with apostrophes (‘) – backticks are used to run a command that would normally be run from a Linux command prompt.

These scripts were written for use with FreePBX 2.11; there are a couple of lines that need to be changed for FreePBX 12 and above which will be posted below the main scripts:

#!/usr/bin/perl

# This program gets the current IP address (as assigned by the ISP) from
# a web page and modifies the FreePBX Asterisk SIP settings if the
# external IP address has changed. Invoke it as cron job that runs every 5 minutes.

use strict;
use warnings;
use WWW::Mechanize;
use Data::Validate::IP qw(is_public_ipv4);

# GET CURRENT IP ADDRESS
my $mech = WWW::Mechanize->new( autocheck => 1 );

# NOTE THE http QUERY IN THE NEXT LINE - PLEASE PASTE THIS INTO YOUR WEB
# BROWSER AND MAKE SURE IT RETURNS YOUR IP ADDRESS AND NOTHING ELSE.
$mech->get('http://some_web_site_that_returns_your_IP_address');
$mech->success or die 'Cannot connect to web site';
my ($ip) = ($mech->content() =~ /(d+.d+.d+.d+)/);

# VALIDATE RESULT RECEIVED

if (is_public_ipv4($ip)) {

	# SET UP TO CONNECT TO MySQL DATABASE
	use DBI();

	# CONNECT TO DATABASE
	my $connect = DBI->connect("DBI:mysql:database=asterisk;host=localhost", "user", "pw", {'RaiseError' => 1});

	# GET IP ADDRESS FROM DATABASE
	my ($externip) = $connect->selectrow_array("SELECT data FROM sipsettings WHERE keyword like ?", undef, "externip_val");

	# COMPARE IP ADDRESSES

	if ($externip ne $ip) {

		# WAIT 5 SECONDS AND RECHECK IP TO AVOID FALSE POSITIVES

		sleep 5;
		$mech->get('http://some_web_site_that_returns_your_IP_address');
		$mech->success or die 'Cannot connect to web site';
		my ($ip) = ($mech->content() =~ /(d+.d+.d+.d+)/);
		if ($externip ne $ip) {

			# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
			$connect->do("UPDATE sipsettings SET data = ? WHERE keyword = ?", undef, "$ip", "externip_val");

			# WRITE CONFIG FILES AND RELOAD ASTERISK
			`/var/lib/asterisk/bin/module_admin reload`;

			# OPTIONAL SEND EMAIL TO SYSTEM ADMINISTRATOR(S)

			# my $mailstring = 'echo "This is an automated message - please do not reply. Either we had a power or Internet outage (in which case there is a slight chance you may receive this message even if our IP address is unchanged), or our Internet Service Provider has changed the IP address of our phone server to ' . $ip . '" | mail -s "ISP may have changed our IP address" someaddress@gmail.com,anotheraddress@somewhere.com';
			# system($mailstring);
		};
	};
};

Variation (recommended) – note, do NOT confuse backticks and apostrophes, since both are used in this script and they are NOT interchangeable!

#!/usr/bin/perl

# This program gets the current IP address (as assigned by the ISP) from
# OpenDNS and modifies the FreePBX Asterisk SIP settings if the external IP
# address has changed. Invoke it as cron job that runs every 5 minutes.

use strict;
use warnings;
use Data::Validate::IP qw(is_public_ipv4);

my $dig = 'dig +short myip.opendns.com @resolver1.opendns.com';
my $ip=`$dig`;
chomp $ip;
if ($ip=~/((\d){1,3}\.){3}(\d){1,3}/) {
	if (is_public_ipv4($ip)) {

		# SET UP TO CONNECT TO MySQL DATABASE
		use DBI();

		# CONNECT TO DATABASE
		my $connect = DBI->connect("DBI:mysql:database=asterisk;host=localhost", "user", "pw", {'RaiseError' => 1});

		# GET IP ADDRESS FROM DATABASE
		my ($externip) = $connect->selectrow_array("SELECT data FROM sipsettings WHERE keyword like ?", undef, "externip_val");

		# COMPARE IP ADDRESSES

		if ($externip ne $ip) {

			# WAIT 5 SECONDS AND RECHECK IP TO AVOID FALSE POSITIVES

			sleep 5;
			$ip=`$dig`;
			chomp $ip;
			if ($ip=~/((\d){1,3}\.){3}(\d){1,3}/) {
				if (is_public_ipv4($ip)) {
					if ($externip ne $ip) {

						# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
						$connect->do("UPDATE sipsettings SET data = ? WHERE keyword = ?", undef, "$ip", "externip_val");

						# WRITE CONFIG FILES AND RELOAD ASTERISK
						`/var/lib/asterisk/bin/module_admin reload`;

						# OPTIONAL SEND EMAIL TO SYSTEM ADMINISTRATOR(S)

						# my $mailstring = 'echo "This is an automated message - please do not reply. Either we had a power or Internet outage (in which case there is a slight chance you may receive this message even if our IP address is unchanged), or our Internet Service Provider has changed the IP address of our phone server to ' . $ip . '" | mail -s "ISP may have changed our IP address" someaddress@gmail.com,anotheraddress@somewhere.com';
						# system($mailstring);

					};
				};
			};
		};
	};
};

NOTES on the above scripts, including THINGS YOU MUST CHANGE:

In the first script, change both occurrences of http://some_web_site_that_returns_your_IP_address to a web address that returns only your IP address and nothing else. Enter the link into a web browser to make sure you get the expected result — it should show your external IP address and nothing else. These services tend to come and go, and you’ll need to find one that returns your IP address, and ONLY your IP address, with no extraneous HTML formatting or text. If you don’t know of such a source, then try the second variation.

These rest of this applies to both scripts:

Note the two bolded variables user and pw. These must be changed to the correct values for YOUR system. You will usually find these in one of two places. You can look in /etc/amportal.conf and look for the variables AMPDBUSER and AMPDBPASS — these will usually be near the bottom of the file in newer installs, in a “— CATEGORY: Bootstrapped or Legacy Settings —” section, but they can be anywhere in the file.

Another place they may be found is in the file /etc/freepbx.conf — in that file, look for lines similar to:

$amp_conf[‘AMPDBUSER’] = ‘freepbxuser’;
$amp_conf[‘AMPDBPASS’] = ‘password’;

Those will give you the values to insert into the user and pw variables in the script. YOU MUST INSERT THE CORRECT VALUES OR THE SCRIPT WILL NOT WORK! By the way, if you have both of the above-mentioned files, make sure that the AMPDBUSER and AMPDBPASS variables are set to the same respective values in both files, otherwise your CDR Reports page may not work.

Finally, if you want an e-mail notification when your IP address has changed, uncomment the two lines under “# OPTIONAL SEND EMAIL TO SYSTEM ADMINISTRATOR(S)” and modify the first line appropriately (make sure you use one or more valid e-mail addresses!). BE CAREFUL NOT TO DELETE THE TRAILING APOSTROPHE (just before the semicolon). Yeah, I did that once. 🙁

IF YOUR ARE RUNNING FREEPBX 12 (NOT FreePBX 14, see below), it appears they have changed the location where the IP address is stored in the database. In that case, two sections of the script need to be changed:

Change this:

		# GET IP ADDRESS FROM DATABASE
		my ($externip) = $connect->selectrow_array("SELECT data FROM sipsettings WHERE keyword like ?", undef, "externip_val");

To this:

		# GET IP ADDRESS FROM DATABASE
		my ($externip) = $connect->selectrow_array("SELECT val FROM kvstore WHERE `key` = ?", undef, "externip");

Change this:

						# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
						$connect->do("UPDATE sipsettings SET data = ? WHERE keyword = ?", undef, "$ip", "externip_val");

To this:

						# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
						$connect->do("UPDATE kvstore SET val = ? WHERE `key` = ?", undef, "$ip", "externip");

IF YOU ARE RUNNING FREEPBX 14, it appears they have changed the location again where the IP address is stored in the database. In that case, two sections of the script need to be changed:

Change this:

		# GET IP ADDRESS FROM DATABASE
		my ($externip) = $connect->selectrow_array("SELECT data FROM sipsettings WHERE keyword like ?", undef, "externip_val");

To this:

		# GET IP ADDRESS FROM DATABASE
		my ($externip) = $connect->selectrow_array("SELECT val FROM kvstore_Sipsettings WHERE `key` = ?", undef, "externip");

Change this:

						# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
						$connect->do("UPDATE sipsettings SET data = ? WHERE keyword = ?", undef, "$ip", "externip_val");

To this:

						# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
						$connect->do("UPDATE kvstore_Sipsettings SET val = ? WHERE `key` = ?", undef, "$ip", "externip");

(Thanks to “Tony” for posing the FreePBX 14 changes in the comment section.)

Regardless of which version you are running (12 or 14), BE CAREFUL, in both of the above lines the word keyword (without quotes) is changed to `key` (with backtick quotes). If you leave out the backticks, or change them to something else such as apostrophes, IT WILL NOT WORK.

Save your script to either the /root directory or the /var/lib/asterisk/agi-bin directory, or to another location of your choosing. I named it checkip.pl, solely because that was the name of a previous script I had run and I had already created a cron job for it. You must make the script executable, for example:

chmod u+rx /var/lib/asterisk/agi-bin/checkip.pl

Of course you will specify the correct filename and directory. Now it’s time to test the script. From the Linux command prompt, navigate to the directory where you stored the script:

cd /var/lib/asterisk/agi-bin

Now run the script from the command prompt:

./checkip.pl

Hopefully you won’t see any error messages. Remember it’s going out to do a query to get your external IP address, so don’t get concerned if it takes a second or two. If you had an incorrect address stored in your FreePBX Asterisk SIP Settings configuration, it will take longer because it will reload the FreePBX configuration. The script has a couple of different checks to make sure it only stores a real IP address (and not something invalid like an error message) in the database, so if it appears to not be working, make sure the underlying call to the web server or the dig command (depending on which variation you use) is returning a valid IP address.

Usually if you do see errors they will fall into one of two categories. The first is a missing Perl module, which you will need to obtain as described above. The second is a syntax error, which you should not get if you cut and pasted the script, and made the changes noted above. If you get a permissions error, you probably forgot to make the script executable!

Setting up a cron job:

Once it runs without errors, you will want to create a cron job so it runs automatically every five minutes. Do NOT run it more often than that, or the lookup service may ban your IP address, and you don’t want that to happen (whatismyip.com would do that, which is another reason not to use them), and besides, it’s not polite to hog the resources of someone else’s server! And if you are running it on multiple servers at the same IP address, then adjust the polling speed so that the total polling from all servers doesn’t exceed once every five minutes. An occasional additional test is probably not an issue, but if you try to poll every minute you just might get banned!

The usual way to add a cron job is to run this command:

crontab -e

(If you’re not currently running as root use sudo crontab -e instead)

This will open a text editor showing your current cron jobs. Just add a new line to the bottom of the file with your new cron job. To run the script every five minutes, you could use something like this:

*/5 * * * * /var/lib/asterisk/agi-bin/checkip.pl

Or to be more specific as to when the script runs (this will run it exactly on the hour, at five minutes after the hour, at ten minutes after the hour, and so on):

0,5,10,15,20,25,30,35,40,45,50,55 * * * * /var/lib/asterisk/agi-bin/checkip.pl

Just save the changed file when you are finished. The alternate method is to use Webmin’s System | Scheduled Cron Jobs module to set up your cron job.

Final testing:

The easiest way to test to make sure this is all working is to wait until a time that there are no active calls on the system, then go to the Asterisk SIP Settings configuration page and change the External IP address to something invalid (just change the last digit of the current address and Submit Changes, then do the usual orange bar reload). On the next five minute interval, the script should detect that the external IP address doesn’t match the one stored in the database, and it will write the correct value to the database and reload Asterisk. If you watch the Asterisk CLI during this time, you should actually see the reload take place. After that, if you go back to the Asterisk SIP Settings configuration page, the correct IP address should be there. To be extra safe, you should also view the contents of the file /etc/asterisk/sip_general_additional.conf and make sure that the externip= line shows the correct IP address.

Now you don’t have to worry about frantic calls from users at inopportune times because your ISP changed your IP address and none of the phones are working, and you also won’t have any of the problems associated with the Dynamic IP method!

I want to thank Moshe Brevda for giving me the information I needed to do the MySQL database write, after a particularly frustrating middle of the night session (not helped by bumping into a truly arrogant bastard on an IRC channel), and also for one correction to this article (see my comment in the comments section below). If any “Perl purists” are reading this and you want to offer a constructive comment without giving me any attitude, I’m fine with that. But if you are like some of your I-know-it-all-and-your-coding-sucks brethren in the IRC channel, don’t even waste your time posting a comment, because I won’t approve it. No, you really DON’T need to use any other Perl database modules to do this simple task, and no, I DON’T want to learn your philosophy of writing Perl code (there are some really sucky mom’s-basement-dwellers inhabiting the IRC channel — some of those folks really need to get professional help, and that is all I will say about that). EDIT: Credit to the article Quickly Get an External IP Address from the Command Line (OS X Daily) for revealing the method of using dig with OpenDNS to get your IP address.

NOTE: As usual, there are no warranties — we’re experimenters here, and sometimes we don’t catch all the bugs, especially on the first go around! However, I would assume that anyone who is running a “professional” installation would pay their ISP for a true static IP address (one that never changes), and therefore wouldn’t need one of these scripts in the first place.

ASRock Vision 3D or other Home Theater PC and "Sparklies"

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

Just a quick note that may save someone a lot of frustration.  This problem was observed with an ASRock Vision 3D system but could affect other HTPC’s as well.

If you are getting single-color (probably red) “sparklies” (pinpoints of light that don’t belong) in certain scenes, or when viewing certain static images, it may NOT be a software problem.  It seems that on certain systems the HDMI output may be running a little “hot” (not referring to temperature, but rather output levels) and may be overdriving the HDMI input of a connected TV.  To the best of my knowledge, there is absolutely nothing you can do to fix this in software – it’s a hardware problem, perhaps a hardware defect.

But what you can try is attenuating the HDMI signal just a bit.  If you have a HDMI switch (preferably an unamplified one), try making the connection through that instead of directly to the TV.  In at least one case, that solved the problem.  Or, if you have a very long but unamplified HDMI cable handy, you could try that (remember, any additional amplification of the signal will probably only make the problem worse!).

And if you found this article by searching on “ASRock Vision 3D”, I will just say that in my opinion, it’s not worth the price they are currently getting for it.  It’s kind of a hassle to get it working under Ubuntu Linux, and although it costs about two to three times as much as, say, an Acer Asprie Revo, you don’t get two to three times the performance (in my admittedly subjective evaluation), and you might get the HDMI output issue mentioned here.  Whether it works any better under Windows I wouldn’t know – even at the price they charge they don’t supply a copy of Windows, so we opted to use Ubuntu, which worked fine back when we set up the Acer Aspire Revo’s (if we were doing it today, we’d probably choose Linux Mint instead).  In my personal opinion, you might be a lot happier with something else unless you are a real Linux geek and don’t mind tinkering until you can get everything working right, or perhaps if you plan to splurge for a copy of Windows — again, can’t say if that would work any better.

How to show the source DID in FreePBX call detail reports

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

This article was originally published in July, 2011. This functionality may or may not be already present in newer versions of FreePBX.

NOTE:  If you have installed a separate FreePBX CDR report tool module and it does not already include this functionality, you may need to do this before you follow these instructions: Copy this article (starting with the paragraph just before “[custom-from-trunk-accountcode]”) and paste it into a text editor, then use the editor’s search-and-replace to search for the string “accountcode” and change every occurrence of it to “did” (without the quotes in both cases), and follow those instructions.  That way you’ll be saving your DID information to the did field created in the CDR database by the module. I cannot tell you if you need to do this or not, because the last version of FreePBX I used was 2.9, and I have no plans to upgrade.

Another thing I am posting just so I can find it later…

Many people have wished for a way to see the source DID in FreePBX call detail reports.  It turns out there is a pretty easy way to do it IF you are not using Account Codes in your system, and have no plans to do so.  Many FreePBX users never use account codes so the following method will work for them.

Asterisk stores its CDR data in /var/log/asterisk/cdr-csv/Master.csv.  If you know the format in which it stores its data, you realize that the first field, accountcode, isn’t used on most systems.  Since Asterisk currently doesn’t store the source DID for a call, we can repurpose the accountcode field to store our source DID information.

So, there are two things that need to be done.  The first is to get the DID into the accountcode and to do that, we simply add a new context to /etc/asterisk/extensions_custom.conf as follows:

[custom-from-trunk-accountcode]
exten => _X!,1,Set(CDR(accountcode)=${EXTEN})
exten => _X!,n,Goto(from-trunk,${EXTEN},1)
exten => h,1,Macro(hangupcall,)

NOTE: The above assumes that all your DID’s are entirely numeric.  It will fail (and calls may not reach your inbound routes) if you do not first strip off any non-numeric characters, or change the context to accept them.  For example, if a provider sends a + at the start of the DID (NOT the Caller ID, which is more common), then try changing both occurrences of _X! to _[+X]!

Then for each of our trunks, we go into FreePBX and change the context= line from context=from-trunk to:

context=custom-from-trunk-accountcode

If by some chance you are already sending calls from one or more of your trunks to a custom context in extensions_custom.conf rather than from-trunk, you can simply add one of the following lines to that custom context. If you add it as the first line, use:

exten => _X!,1,Set(CDR(accountcode)=${EXTEN})

And make sure that any subsequent line is not also numbered “1”.  If it’s NOT the first line, use:

exten => _X!,n,Set(CDR(accountcode)=${EXTEN})

That should get your DID into the accountcode field of /var/log/asterisk/cdr-csv/Master.csv

To actually display the field, I refer you to this article on the PSU VoIP blog:

Modify FreePBX call reports to show destination channel

Read that article and the comments underneath, because it explains the principle of displaying additional fields in the FreePBX CDR.  This is how I set up my /etc/asterisk/call-log-table.php file:

calldate", "10%", "center", "SORT", "19");
        $FG_TABLE_COL[]=array ("Source Channel", "channel", "14%", "center", "", "32");
        $FG_TABLE_COL[]=array ("Source DID", "accountcode", "10%", "center", "SORT", "20");
        $FG_TABLE_COL[]=array ("Source", "src", "10%", "center", "SORT", "30");
        $FG_TABLE_COL[]=array ("CLID", "clid", "20%", "center", "", "80",'','','','',
          '','filter_html');
        $FG_TABLE_COL[]=array ("Dest.", "dst", "10%", "center", "SORT", "30");
        $FG_TABLE_COL[]=array ("Dest. Channel", "dstchannel", "14%", "center", "", "32");
        $FG_TABLE_COL[]=array ("Disposition", "disposition", "6%", "center", "", "30");
        if ((!isset($resulttype)) || ($resulttype=="min"))
          $minute_function= "display_minute";
        $FG_TABLE_COL[]=array ("Duration", "duration", "6%", "center", "SORT", "30",
          "", "", "", "", "", "$minute_function");

        $FG_TABLE_DEFAULT_ORDER = "calldate";
        $FG_TABLE_DEFAULT_SENS = "DESC";

        // This Variable stores the argument for the SQL query
        $FG_COL_QUERY='calldate, channel, accountcode, src, clid, dst, dstchannel, disposition,
          duration';
        $FG_COL_QUERY_GRAPH='calldate, duration';

        // The variable LIMITE_DISPLAY define the limit of record to display by page
        $FG_LIMITE_DISPLAY=25;

        // Number of column in the html table
        $FG_NB_TABLE_COL=count($FG_TABLE_COL);

        // The variable $FG_EDITION define if you want process to the edition of the
        // database record
        $FG_EDITION=true;

        //This variable will store the total number of columns
        $FG_TOTAL_TABLE_COL = $FG_NB_TABLE_COL;
        if ((isset($FG_DELETION) && $FG_DELETION) || $FG_EDITION)
          $FG_TOTAL_TABLE_COL++;

        //This variable define the Title of the HTML table
        $FG_HTML_TABLE_TITLE=" - Call Logs - ";

        //This variable define the width of the HTML table
        $FG_HTML_TABLE_WIDTH="100%";
?>

As always you should copy and paste the code, so you get complete lines including anything that WordPress truncates on the display.

The above shows the Source DID column just to the right of the Source Channel column.  I also made Source DID a sortable column, so you can show your usage by DID by clicking on the “Source DID” column heading.

At the start I said that you can only use this if you’re not using Account Codes on your system.  Actually, that may not be strictly true.  A DID would normally only be saved for an incoming call, while an Account Code would normally only be saved for an outgoing call.  So, it may be possible to make that field do double duty, but since I don’t use Account Codes and don’t know anyone who does, I’ll leave that as an exercise for the reader.

Using a dynamic DNS (DDNS) to solve the problem of keeping a firewall open to remote users at changeable IP addresses

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

(Updated July 1, 2011 to include rudimentary test for string returned that doesn’t contain an actual IP address)

One problem faced by Asterisk users (and probably also users of other software PBX’s) is that you want to secure your system by not opening ports up to the entire Internet, but if you have remote users (users not on the same local network as your Asterisk server) you need to make an exception for them to allow them to penetrate your firewall.  If all your external users have fixed IP addresses, it’s not a problem — you simply add a specific rule in your firewall to permit access from each user’s IP address.  However, if their ISP changes their IP address frequently, or if they are using a softphone on a laptop computer, then you can’t just assume they will constantly be at same IP.  And if one of those users happens to be your boss or your mother, they are not going to be happy if they can’t use the phone until they make contact with you, and you enter their new IP address in the firewall.  And they’re probably not going to be real happy if they have to go to a web site or take some other action before they can make and receive calls.

This solution will work for many users in this situation, provided that you are using the iptables firewall. Again, the goal is to keep all your ports closed to outsiders, except for your authorized users. But if you can get each user to set up a Dynamic DNS account and then set their router to do the Dynamic DNS updates (as described here for DD-WRT users), OR failing that if you can get them to install a software Dynamic DNS client on their computer (which is a poorer choice because the computer has to be on for updates to occur), then you can run a script on your Asterisk box every five minutes to check to see if their IP address has changed, and if so, update iptables. I have one script that is called as a cron job every five minutes, and looks like this:

#!/bin/bash
/root/firewall-dynhosts.sh someaddress.afraid.org
/root/firewall-dynhosts.sh someotheraddress.afraid.org
/root/firewall-dynhosts.sh someaddress.no-ip.com

In other words it has one line for each Dynamic DNS host I want to check. For each host it calls a script named firewall-dynhosts.sh which in turn contains this:

#!/bin/bash
# filename: firewall-dynhosts.sh
#
# A script to update iptable records for dynamic dns hosts.
# Written by: Dave Horner (http://dave.thehorners.com)
# Released into public domain.
#
# Run this script in your cron table to update ips.
#
# You might want to put all your dynamic hosts in a sep. chain.
# That way you can easily see what dynamic hosts are trusted.
#
# create the chain in iptables.
# /sbin/iptables -N dynamichosts
# insert the chain into the input chain @ the head of the list.
# /sbin/iptables -I INPUT 1 -j dynamichosts
# flush all the rules in the chain
# /sbin/iptables -F dynamichosts

HOST=$1
HOSTFILE=”/root/dynhosts/host-$HOST”
CHAIN=”dynamichosts” # change this to whatever chain you want.
IPTABLES=”/sbin/iptables”

# check to make sure we have enough args passed.
if [ “${#@}” -ne “1” ]; then
echo “$0 hostname”
echo “You must supply a hostname to update in iptables.”
exit
fi

# lookup host name from dns tables
IP=`/usr/bin/dig +short $HOST | /usr/bin/tail -n 1`
if [ “${#IP}” = “0” ]; then
echo “Couldn’t lookup hostname for $HOST, failed.”
exit
fi

if [ ! `expr “$IP” : ‘([1-9])’` ]; then
echo “Did not return valid IP address, failed.”
exit
fi

OLDIP=””
if [ -a $HOSTFILE ]; then
OLDIP=`cat $HOSTFILE`
# echo “CAT returned: $?”
fi

# has address changed?
if [ “$OLDIP” == “$IP” ]; then
echo “Old and new IP addresses match.”
exit
fi

# save off new ip.
echo $IP>$HOSTFILE

echo “Updating $HOST in iptables.”
if [ “${#OLDIP}” != “0” ]; then
echo “Removing old rule ($OLDIP)”
`$IPTABLES -D $CHAIN -s $OLDIP/32 -j ACCEPT`
fi
echo “Inserting new rule ($IP)”
`$IPTABLES -A $CHAIN -s $IP/32 -j ACCEPT`

echo “Changing rule in /etc/sysconfig/iptables”
sed -i “0,/-A\sdynamichosts\s-s\s$OLDIP\s-j\sACCEPT/s//-A dynamichosts -s $IP -j ACCEPT/” /etc/sysconfig/iptables
# sed -i “s/-A\sdynamichosts\s-s\s$OLDIP\s-j\sACCEPT/-A dynamichosts -s $IP -j ACCEPT/g” /etc/sysconfig/iptables

echo “Sending e-mail notification”
`echo “This is an automated message – please do not reply. The address of dynamic host $HOST has been changed from $OLDIP to $IP. You may need to change the dynamichosts chain in Webmin’s Linux Firewall configuration.” | mail -s “IP address of dynamic host changed on machine name recipient@someaddress.com,anotherrecipient@someaddress.net`

As always, copy and paste the above script, so you can see where the line breaks are really supposed to be (the last line in particular is quite long, and will likely be broken up into four or five lines on the screen). Also, beware of WordPress or other software changing the single or double quotation marks to “prettified” versions — only the plain text normal quotation marks will work.

Note that prior to the first run of the script you will need to run the three commented-out commands shown near the top of the script, right after “create the chain in iptables”, to create the chain. For your convenience here they all are in one place, without the interleaved comment lines:

/sbin/iptables -N dynamichosts
/sbin/iptables -I INPUT 1 -j dynamichosts
/sbin/iptables -F dynamichosts

The lines in blue in firewall-dynhosts.sh are custom additions by me. Just in case something goes wrong, I suggest you make a backup copy of /etc/sysconfig/iptables in a safe place before running this script.  My first addition checks the first character of the string returned in $IP to make sure it is actually a number.  This was a quick and dirty addition to keep it from trying to use a string like ;; connection timed out; no servers could be reached as a valid IP address (yes, it really did that).  I’m sure that the test there could be improved upon (for example, to do a full check for a valid IP address rather than just checking the first digit) but as I say this was a quick and dirty fix.  If you have any suggestions on how to improve it, please leave a comment.  I did find this article, Validating an IP Address in a Bash Script, but it seemed like a bit of overkill considering that in this case what I’m really trying to do is simply weed out error messages.

The second set of additions change the address in the dynamichosts chain of /etc/sysconfig/iptables. Please note that this file may be at a different location in some versions of Linux (such as /etc/iptables.up.rules), if so you will need to change this accordingly. This is particularly important if you run both Webmin and fail2ban. If fail2ban is running it will add some lines to the in-memory version of iptables, so you don’t want to do a simple commit to save the in-memory version back to the iptables file. But at the same time, if you use Webmin’s “Linux Firewall” module to maintain iptables, you want any changes in IP addresses to show up the next time you call up Webmin’s Linux Firewall page. So this simply does a search and replace in /etc/sysconfig/iptables on the rule containing the old IP address, and replaces it with the new one. There are two lines in that section that contain the sed command, the first one will replace only the first instance of the old IP address if it’s in iptables more than once, while the second (which is commented out) would replace all instances of the old IP address. Uncomment whichever you prefer and leave the other commented out, but bear in mind that if two or more of your remote extensions might ever be at the same IP address at the same time, you want the first version (the one that is uncommented above) so that when one of those extensions moves to a different IP address it doesn’t change the IP address for all of the extensions.

Note there’s still a possibility of missing a change if you are actually working in Webmin when a change occurs (since you’ll already have loaded a copy of iptables, and if you then make changes and save it out it could overwrite any change made by the script). But, the last two lines of the script send you an e-mail to alert you to that possibility. If you don’t use Webmin and don’t need or want an e-mail notification for some other reason, you can omit those last two lines, otherwise change the parts in red text to sane values for your situation. While editing, pay attention to the backtick at the end of the line (it’s easy to accidentally delete it when editing an e-mail address — don’t do that!).

When you’re all finished, make sure both scripts are executable and the permissions are correct, then create a cron job to call the first script every five minutes.

The only slight drawback to this method is that when an IP address changes it can take up to ten minutes to update (five for the Dynamic DNS to pick it up, and five more for the cron job to fire that gets it from the Dynamic DNS). Fortunately, most ISP’s tend to change IP address assignments in the middle of the night. Note that using the wrong DNS servers can cause the updates to take significantly longer; I set my computers to use Google’s DNS (8.8.8.8 and 8.8.4.4) and that works fairly well. Note that if ALL your Dynamic DNS addresses are from freedns.afraid.org then you may want to change one line in the above script, from

IP=`/usr/bin/dig +short $HOST | /usr/bin/tail -n 1`

to

IP=`/usr/bin/dig +short @ns1.afraid.org $HOST | /usr/bin/tail -n 1`

This change will specify that the afraid.org DNS server is to be used for these lookups (and ONLY for these lookups, not for every DNS request your system makes – don’t want to overload the servers of this free service!). This may be particularly important if the DNS server you normally use is a caching server that doesn’t always do real-time lookups for each DNS request (for example, if you have installed the BIND DNS Server on your system). If some of the Dynamic DNS addresses come from other services then you could use a similar modification that checks a public DNS service that does not cache entries for long periods of time; as I write this Google’s DNS servers seem to update in near real time.

One thing some may not like is that this script basically hands the “keys to the kingdom” to your authorized users, by giving them access to all ports, or at least all ports not explicitly denied by rules higher in priority. It would be easy enough to change the rule that is written to iptables, or even add additional ones, in the above script, so that you could specify access to individual ports. The other problem is it works great for those external users at fixed locations that don’t move around a lot. It might not work quite as well as well for softphone users on laptops due to the delay between the time they turn on the laptop and the time your Asterisk server picks up the new address.

This has actually worked the best for me of anything I’ve tried so far because once you get the external user’s router set up to do the Dynamic DNS updates, they don’t have to think about doing anything else prior to making a call.

EDIT (December, 2015): If it is not possible or appropriate to update the dynamic DNS automatically from the users’ router, there may be another option. If any of your users have Obihai devices (or possibly another brand of VoIP device that includes an accessible “Auto Provisioning” feature that is not currently being utilized), you may want to know that they do not need to run a separate client to update their dynu.com or freedns.afraid.org dynamic IP address, because an Obihai device (and possibly some other brands of VoIP devices) can do that automatically. This is NOT a recommendation for Obihai devices, but if you or one of your users happens to already have one, here is the information as originally found in this thread on the Obihai forum, posted by user giqcass, who wrote:

Rough Draft for hackish DNS updates:

This hack will let your OBi update Dynamic DNS. It isn’t perfect but it works very well. It’s as simple as calling a url to update the DNS at afraid.org. I believe it would be a simple task to add this feature to the OBi firmware directly. So please add this OBiHai. Pretty please. Until then here you go.

Set up a Dynamic DNS host at http://freedns.afraid.org/
Go to the Dynamic DNS tab.
Copy the “direct” update url link.
Open your Obi admin page.
Click the System management page.
Click Auto Provisioning.
Under “ITSP Provisioning” Change the following.
Method = Periodically
Interval = This setting must be greater then 400 so not to over use resources. I use 3667.
ConfigURL = Paste the update link you got from afraid.org (use http://… not https://…)

Press Submit at the bottom of the page. Restart you OBi.

If you use choose to use dynu.com instead of freedns.afraid.org (which you might because dynu.com doesn’t force you to visit their web site periodically to keep your domain), the procedure is the same (after the first line), except that for the ConfigURL you would use:

http://api.dynu.com/nic/update?hostname=YOUR_DYNU_DYNAMIC_DNS&username=YOUR_DYNU_USERNAME&password=MD5_HASH_OF_PASSWORD

Replace YOUR_DYNU_DYNAMIC_DNS with your dynamic DNS domain name, YOUR_DYNU_USERNAME with the username you use to log into your dynu.com account, and MD5_HASH_OF_PASSWORD with the MD5 hash of your dynu.com password OR your IP Update Password if you have set one (which is recommended). To get the MD5 hash of the password you can enter it on this page. To set or update your IP Update Password, use this page.

The advantage of this is that if one of your users travels and takes their VoIP device with them, it would be able to change the dynamic DNS each time they plug in at a new location (not immediately, but after several minutes at most), so that if you use the technique outlined in this article your server will recognize their current address and permit access. Remember that it’s okay to use more than one Dynamic DNS service simultaneously, in case you or your user are already using a different one that doesn’t provide a simple update URL like dynu.com and freedns.afraid.org do. Other brands of VoIP adapters that have a similar “Auto Provisioning” feature may be able to do this as well, but we don’t have specific information for any of them. If you do, please feel free to add that information in a comment.

Note that we are not recommending any particular free dynamic DNS service. If you want to know what your options are, there is an article on the Best Free Dynamic DNS Services that will show you some options. You want one that is reliable and that will not disappear in a few months, but since we don’t have a crystal ball, we can’t tell you which ones might fit that criteria.

Do you use Webmin to configure iptables and also run fail2ban? Don’t forget to do this!

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

For many Linux users this will be a “Thank you, Captain Obvious” type of post, but it’s one of those things that some Webmin users might not realize.  If you use Webmin’s “Linux Firewall” configuration page to configure the iptables firewall in Linux, and you click “Apply Configuration”, it will remove fail2ban‘s rules from your active iptables configuration.  So, you must go to a Linux command prompt and enter service fail2ban restart — UNLESS you make a small change in the Webmin “Linux Firewall” configuration.

From the Webmin “Linux Firewall” main page, click Module Config, then on the configuration page, in the “Configurable options” section, look for the line “Command to run after applying configuration.” Click the button next to the text box on that line, and in the text box enter service fail2ban restart and then click the Save button at the bottom of the page. That’s all you need — now every time you make a firewall change and click “Apply Configuration”, it will automatically restart fail2ban for you.

Fixing Midnight Commander’s unreadable dropdown menus

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

If you’ve installed Midnight Commander and haven’t changed the default colors, when you try to access a dropdown menu you may see this:

Midnight Commander — Original Colors

REALLY hard to read that menu, isn’t it? Wouldn’t you rather see this?

Midnight Commander — Changed Colors

To fix the unreadable menus, just make sure Midnight Commander is not open, then use any text editor (such as nano) to open ~/.mc/ini:

nano ~/.mc/ini

Assuming that there is no existing [Colors] section in the file, just add this at the bottom of the file (if the second line exceeds the blog column width, just use copy and paste to get it all):

[Colors] base_color=default,default:menu=black,cyan:menuhot=brightmagenta,cyan:menusel=white,blue:menuhotsel=brightmagenta,blue

If there is an existing [Colors] section, you can try tweaking it using the parameters shown above. If you have a very recent version of Midnight Commander (which you probably will have if you are running Ubuntu), then instead of menu= you’ll need to use menunormal=, as shown here:

[Colors] base_color=default,default:menunormal=black,cyan:menuhot=brightmagenta,cyan:menusel=white,blue:menuhotsel=brightmagenta,blue

Note that for some reason the base_color parameter must appear, or the other items are ignored. Save the change, exit the editor, and open Midnight Commander. If you then close Midnight Commander, you may find that the position of the [Colors] section has moved within the ini file — apparently Midnight Commander rewrites the file when you close it — but if you don’t like the changes you can remove the [Colors] section to reverse the change.

I figured out how to do this after reading this blog post:
Ajnasz Blog – Midnight Commander colors and themes
Another source of information is:
Zagura’s blog – Midnight Commander Color Themes

A Perl script to send Caller ID popups from Asterisk to computers running Notify OSD (such as Ubuntu Linux) or any command-line invoked notification system

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

This is basically an update to my article, A Perl script to send Caller ID popups from Asterisk to computers running Growl under OS X on a Mac or Growl for Windows, and you should still use that article if you are sending notifications to a computer on your local network that runs Growl or Growl for Windows as the notification system.

I wanted to find a way to send Caller ID popups to a Ubuntu Linux box, and in the process I discovered a different method of sending such notifications.  There are pros and cons to using the new method, so let me explain those first:

Pros:

  • Can send notifications to any computer that supports command line generated notifications (so it could also be used with Growl, if you can use growlnotify from a command prompt to generate a notification).
  • Can send notifications to any computer that you can SSH into, provided you have it set up to use public/private key authentication rather than password authentication.

Cons:

  • Notifications typically display a couple of seconds later than under the previous method.  I suspect this is due to the SSH authentication taking a second or two.
  • It’s a little bit more complicated to set this up, though not horribly so.
  • Because this uses SSH and requires that Asterisk be granted permission to establish an SSH connection as the super user (by using sudo), there may be unforeseen security risks.

Read that last point again, and please understand that as with all projects on this site, I offer this for experimental purposes only.  I explicitly do not warrant this method as being 100% secure, nor will I tell you that it could not be exploited to do bad things on your system.  I don’t think it can (and feel free to leave a comment if you think I’m wrong), but I just don’t know that for sure.  So, if you decide to use anything in this article, you agree to assume all risks. If you’re the type that likes to sue other people when something goes wrong, then you do not have permission to use this code.  We’re all experimenters here, so no guarantees!

As with the previous method, you must have the Perl language installed on your Asterisk server, and you must have the Asterisk::AGI module installed (I’m going to assume you know how to install a Perl module from the CPAN repository – if you have Webmin installed, it can be done from within Webmin). Chances are you already have Asterisk::AGI installed, unless you built your Asterisk server “from scratch” and never installed it.

There’s one additional thing you must do on the Asterisk server before this will run, and that’s allow Asterisk to run the ssh command as root. So, add this to your /etc/sudoers file (probably at the very end, but in any case it should be obvious where to add this because it will be in a section where Asterisk is granted similar privileges with regard to other programs):

asterisk ALL = NOPASSWD: /usr/bin/ssh

Next you want to copy and paste the following Perl script to the filename /var/lib/asterisk/agi-bin/notifysend.agi on your Asterisk server (to create a non-existent file, you can use the touch command, and after that you can edit it in Midnight Commander or by using the text editor of your choice). If this code looks somewhat familiar, it’s because it’s adapted from some code that originally appeared in a FreePBX How-To, which I have modified.

#!/usr/bin/perl
use strict;
use warnings;
use Asterisk::AGI;
my $agi = new Asterisk::AGI;
my %input = $agi->ReadParse();

# Next two lines fork the process so Asterisk can get on with handling the call
open STDOUT, '>/dev/null';
fork and exit;

my $num = $input{'callerid'};
my $name = $input{'calleridname'};
my $ext = $input{'extension'};
my $user = $ARGV[0];
my $ip = $ARGV[1];

if ( $ip =~ /^([0-9a-f]{2}(:|$)){6}$/i ) {
    $ip = $agi->database_get('growlsend',uc($ip));
}

# OMIT this section if you don't want IP address
# checking (e.g. you want to use foo.bar.com)
unless ( $ip =~ /^(d+).(d+).(d+).(d+)$/ ) {
    exit;
}

if ( $ARGV[2] ne "" ) {
 $ext = $ARGV[2];
}

my @months = (
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
);
my @weekdays = (
    "Sunday", "Monday", "Tuesday", "Wednesday",
    "Thursday", "Friday", "Saturday"
);
my (
    $sec,  $min,  $hour, $mday, $mon,
    $year, $wday, $yday, $isdst
) = localtime(time);
my $ampm = "AM";
if ( $hour > 12 ) {
    $ampm = "PM";
    $hour = ( $hour - 12 );
}
elsif ( $hour eq 12 ) { $ampm = "PM"; }
elsif ( $hour eq 0 )  { $hour = "12"; }
if ( $min < 10 ) { $min = "0" . $min; }
$year += 1900;
my $fulldate =
"$hour:$min $ampm on $weekdays[$wday], $months[$mon] $mday, $year";

# Next two lines normalize NANP numbers, probably not wanted outside of U.S.A./Canada/other NANP places
$num =~ s/^([2-9])(d{2})([2-9])(d{2})(d{4})$/$1$2-$3$4-$5/;
$num =~ s/^(1)([2-9])(d{2})([2-9])(d{2})(d{4})$/$1-$2$3-$4$5-$6/;

my $cmd = qq(./remotenotify.sh "$name" "$num calling $ext at $fulldate");
$cmd = "sudo ssh $user@$ip '$cmd'";
exec "$cmd";

Also, if you want to be able to specify computers that you wish to send notifications to using MAC addresses rather than IP addresses (in case computers on your network get their addresses via DHCP, and therefore the IP address of the target computer can change from time to time), then you must in addition install the following Perl script (if you have not already done so when using the previous method). Note that if you have a mix of computers on your network and you are using both the new and old methods, you only need to do this once — it works with both methods (hence the reference to “growlsend” in the database and “gshelper” as the name of this script). Call it /var/lib/asterisk/agi-bin/gshelper.agi and note that there is a line within it that you may need to change to reflect the scope of your local network:

#!/usr/bin/perl
use strict;
use warnings;
my ($prev, @mac, @ip);
# Change the 192.168.0.0/24 in the following line to reflect the scope of your local network, if necessary
my @nmap = `nmap -sP 192.168.0.0/24|grep -B 1 MAC`;
foreach (@nmap) {
    if (index($_, "MAC Address:") >= 0) {
        @mac = split(" ");
        @ip = split(" ",$prev);
        `/usr/sbin/asterisk -rx "database put growlsend $mac[2] $ip[1]"`;
    }
    $prev=$_;
}

Make sure to modify the permissions on both scripts to make them the same as other scripts in that directory (owner and group should be asterisk, and the file should be executable), and if you use the gshelper script, make sure to set up a cron job to run it every so often (I would suggest once per hour, but it’s up to you).

Now go to this page and search for the paragraph starting with, “After you have created that file, check the ownership and permissions” (it’s right under a code block, just a bit more than halfway down the page) and if you are using FreePBX follow the instructions from there on out (if you are not using FreePBX then just read that section of the page so you understand how this works, and in any case ignore the top half of the page, it’s talking about a different notification system entirely). However, note that the syntax used in extensions_custom.conf differs from what is shown there, depending on whether you are specifying an IP address or a MAC address to identify the target computer.

First, if you are specifying the IP address of the target computer, then instead of using this syntax:

exten => ****525,1,AGI(growlsend.agi,192.168.0.123,GrowlPassWord,525)

You will need to use this:

exten => ****525,1,AGI(notifysend.agi,username,192.168.0.123,525)

Note that username is the account name you use when doing an ssh login into the destination system, and it should also be the desktop user on the system (not root!). Let’s say that the system is currently at IP address 192.168.0.123. In order for this to work, you need to be able to ssh into your Ubuntu box from your Asterisk server, using the following command from the Asterisk server’s command line:

ssh username@192.168.0.123

If it asks for a password, then you need to follow the instructions at Stop entering passwords: How to set up ssh public/private key authentication for connections to a remote server, and get it set up so that it will not ask for a password (if you don’t like my article, maybe this one will make it clearer).

It’s probably easiest to configure each computer that is to receive notifications to use a static IP address. But note that if you use the above code and have the gshelper.agi program running as a cron job, then after the first time it has run while the computer to receive the notifications is online you should be able to use a computer’s MAC address instead of the IP address. This only works if you’ve used the modified script on this page, not the one shown in the FreePBX How-To. As an example, instead of

exten => ****525,1,AGI(growlsend.agi,192.168.0.123,GrowlPassWord,525)

as shown in the example there, you could use

exten => ****525,1,AGI(notifysend.agi,username,01:23:45:AB:CD:EF,525)

(the above is all one line) where 01:23:45:AB:CD:EF is the MAC address of the computer you want to send the notification to. Once again, just in case you missed it the first time I said it, this won’t work until the gshelper.agi script has been run at least once while the computer to receive the notifications was online. If for some reason it still doesn’t appear to work, run the nmap command (from gshelper.agi) including everything between the two backticks (`) directly from a Linux command prompt and see if it’s finding the computer (depending on the size of your network, it might be several seconds before you see any output, which is why I don’t try to run this in real time while a call is coming in).

If you are NOT running FreePBX, but instead writing your Asterisk dial plans by hand, then you will have to insert a line similar to one of the above examples into your dial plan, except that you don’t need the four asterisks (****) in front of the extension number, and if it’s not the first line in the context, you’ll probably want to use n rather than 1 for the line designator (and, you won’t be putting the line into extensions_custom.conf because you probably don’t have such a file; instead you’ll just put it right in the appropriate section of your dial plan). In other words, something like this (using extension 525 as an example):

exten => 525,n,AGI(notifysend.agi,username,192.168.0.123,525)

This line should go before the line that actually connects the call through to extension 525. I do not write Asterisk dial plans by hand, so that’s about all the help I can give you. And if you don’t write your dial plans by hand, but you aren’t using FreePBX, then I’m afraid you’ll have to ask for help in whatever forum you use for advice on the particular software that you do use to generate dial plans, because I can’t tell you how to insert the above line (or something like it) into your dial plan.

Now is where it gets just a bit more complicated than in the original method. If you have followed the above instructions, you’ll be able to send the notifications to the remote system using SSH, but there will be nothing there to receive them. So we have to create a small script on the receiving system to do something with the received notifications. That script will vary depending on the receiving system, but it must be named remotenotify.sh and it must be placed in the destination user’s home directory, and don’t forget to make it executable! Here’s one that will work in most Ubuntu installations that have Notify OSD installed:

export DISPLAY=:0
notify-send --urgency="critical" --icon="phone" "$1" "$2"

Those two lines are all you need. On a different type of system (or if you have multiple displays) you may need to or wish to do something different. For example, as I mentioned above, if the destination system is running Growl then your remotenotify.sh script will need to call growlnotify, but beyond that I wouldn’t know what to use there (EDIT: But if the target system is a Mac that is running OS X, a pretty good guess would probably be that you’d only need one line, something like this:

growlnotify -s -p 1 -a Telephone -m "$2" $1

In this case it should make the notification sticky until dismissed by the user, give it a priority of 1 — the default is 0 — and use the application icon from the “Telephone” application if you have it installed. Instead of -a to specify an application’s icon you could use -I followed by a path to an .icns file that contains an icon you want to use.  Type growlnotify –help to see all the growlnotify options.  Oh, and before you can make an SSH connection to a Mac you have to go into System Preferences | Sharing and turn on Remote Login).

The beauty of this approach is that you can make the remotenotify.sh script as simple or as complicated as you need — you could even make it forward a notification to other devices if you wish, but figuring out how to do that is up to you (if you come up with something good, please leave a comment and tell us about it!).

If you’re running Ubuntu on the target system, here’s a few articles you may wish to use to help you get your notifications to look the way you want them to appear:

Tweak The NotifyOSD Notifications In Ubuntu 10.10 Maverick Meerkat [Patched NotifyOSD PPA Updated]
Get Notifications With A Close Button In Ubuntu
Configurable NotifyOSD Bubbles For Ubuntu 11.04: Move, Close On Click, Change Colors And More

If you want to be able to review missed notifications, you may be able to use this (as a side note, why don’t they have something like this for Growl?):

Never Miss A NotifyOSD Notification With “Recent Notifications” GNOME Applet

The idea behind the shell script that runs on the target system was found in a comment on the following article, which may be of special interest to MythTV users:

Send OSD notification messages to all systems on a network

There are links to other original sources throughout the article, so feel free to follow those if you want more in-depth commentary.

Why your brand new router may cause your VoIP to stop working

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.
I quote directly from Voip-Info.org:

Many of today’s commercial routers implement SIP ALG (Application-level gateway), coming with this feature enabled by default. While ALG could help in solving NAT related problems, the fact is that many routers’ ALG implementations are wrong and break SIP.

The article goes on to explain why the implementation is broken, and how to disable it in several brands of routers.  Certain VoIP adapter manufacturers also recommend disabling this feature if you are having problems with SIP registration, not being able to receive a call or one-way audio.  But note that this issue can affect any type of SIP-based communications, regardless of hardware or software used.

EDIT (May, 2018): For information on another issue that may cause problems when you switch routers, see this DSLReports thread: SIP registration times.

How to export Outbound Route Dial Patterns and Trunk Dialed Number Manipulation Rules to a CSV file in FreePBX

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

If you use a recent version of FreePBX, you are familiar with the new and tedious method of entering Outbound Route Dial Patterns and Trunk Dialed Number Manipulation Rules.  Fortunately, version 2.9 and above offer a way to import a list of patterns from a CSV file (there’s a way to patch FreePBX 2.8 to get this functionality as well — see Ticket #4691).

What they don’t give you is a way to export a list of patterns once you have them installed.  So if you want to clone a route and you’ve lost your original CSV file (or never had one to begin with because FreePBX converted your existing routes and trunks when you upgraded from version 2.7), what do you do?  Fear not, because it actually is possible, if not exactly the most straightforward process.

EDIT: Now there is an easy way around all this — see this thread on the FreePBX Swiss Army Knife Module.  If you use that module, you don’t need to read the rest of this article (although, you might be interested in the part about editing CSV files). Unfortunately, it is reported that the module does not work with FreePBX 2.10 or above, and the author has said he will not fix it to work with newer versions. It is possible that the ability to export outbound route and trunk data to a CSV file may be present in the newest version(s) of FreePBX.

EDIT: Steps 1 and 2 involve using a Database editor module to export the data to a CSV file.  Unfortunately, one commenter says that this module no longer works with FreePBX 2.9.  There are other ways to accomplish the same thing — see the edit at the end of this article to use Webmin or phpMyAdmin instead of the Database editor module.  If you export the data using one of those other programs, then skip to step 3 below.

Step 1: Go to the FreePBX bug tracker and look for Ticket #4793 — Database editor module (like phpMyAdmin for FreePBX).  On that page you’ll find a download link for dbeditor-1.0.tgz which (at the time of this writing) is the only version of this software available. Download and install it as you would any third-party module (download it to your computer, then in FreePBX’s GUI navigate to Module Admin and then click the “Upload Module” link, then upload the module and follow the directions to complete the install).

Step 2: Once you have the Database Editor installed, it will appear under the FreePBX “Tools” tab, in the “System Administration” section. Click on the “Database Editor” link, and you should see a list of database tables used by FreePBX. The two you are interested in are called outbound_route_patterns and trunk_dialpatterns. Near each pattern name you will see two links for “Export” and “Drop” — do not click either of those (especially be careful not to click drop!), because the “Export” here will export the table in MySQL format, which is not what you want. Instead, click on the name of the table (that is, click on either outbound_route_patterns or trunk_dialpatterns) and a new page will open. Near the top of the page you will see a place where it says, “Export to CSV: pipe – tab – comma – semicolon” — click on comma and it should bring up a file save dialog that will let you save the file to your system.

Step 3. Load the downloaded file into any text editor that can deal with Linux/Unix-style line endings and not change them (so, don’t use Notepad!), or better yet, use a CSV editor if you have one (an excellent free one is CSVed, which runs under Windows but will also install and run under CodeWeavers’ CrossOver on a Mac, which tends to make me think it would probably also run under WINE on a Linux or Mac OS X computer). I do NOT recommend opening the file in a spreadsheet application such as Excel, because if you have any patterns that start with one or more leading zeroes, those might be removed, and it’s also possible that any non-numeric characters may be misinterpreted or removed.

The first number in each line is associated with a particular outbound route or trunk, so, you want to cut out the lines not applicable to the route or trunk you want to keep. Don’t erase the top (header) line. If you have many routes or trunks, it may be a bit tricky to figure out which is which, since the numbers don’t tell you the name of the route or trunk they are associated with.

Deleting columns using CSVed

After you do that, you also have to get rid if the first column in each line. So let’s say you are using route 3, and each line starts with 3,. What you want to do is a search and replace on <newline>3, (or expressed as a regular expression: n3,) and replace it with a newline only (n as a regular expression). In a CSV editor you may be able to just delete the first column. For a trunk, the principle is the same except that you will need to remove the first AND last columns, leaving only the middle three.

While you’re at it, it’s also possible to use search and replace in other ways. For example, if you are duplicating list of outbound route patterns but need to change the extension field pattern in all lines, you could do that using search and replace, if you understand what you’re doing.

Step 4. Once you have edited out all the lines except the ones pertaining to the route or trunk you want, you need to change the header line at the top. It’s important to get this right. For an outbound route you want to change it from this:

route_id,match_pattern_prefix,match_pattern_pass,match_cid,prepend_digits

(Note that the route_id may be missing after the previous edit) to this:

prefix,match pattern,callerid,prepend

Visually inspect the lines following the header to make sure you have four fields separated by exactly three commas.

For a trunk, you will need to change the first header line from this:

trunkid,match_pattern_prefix,match_pattern_pass,prepend_digits,seq

(Note that the trunkid and seq may be missing after the previous edit) to this:

prefix,match pattern,prepend

Visually inspect the lines following the header to make sure you have three fields separated by exactly two commas. Also, and this applies to trunks only, if it is important that trunk dial patterns be in a particular order then you will want to check to make sure they are in the correct order in the CSV file, since the “seq” column is not preserved. In many situations this is not an issue but in certain special cases the order of trunk dial patterns can make a difference in how they are processed.

Step 5: Save the modified file to a file with the .csv extension (if using a CSV editor make sure you are saving in comma-delimited format). Again, try to make sure your editor doesn’t change the line endings – I don’t know for certain that it would make a difference, but it might.

Step 6: Now you can create a new route or trunk, and in the “Dial patterns wizards” or “Dial Rules Wizards” dropdown select “Upload from CSV” and select your file to upload. Note that if you are using a beta version of FreePBX 2.9, it may complain if you try to submit an outbound route with no patterns, even if you are uploading a CSV file. In that case, just put a single “X” in the “match pattern” field. After you submit changes, be sure to scroll through the patterns to make sure they appear to be correct. In particular, make sure that all values are in the correct fields.

There are probably other ways to accomplish this, and maybe eventually the FreePBX developers will add an export function on the route and trunk pages (obviously, it would probably not be a good idea for me to request it, and besides, it appears that someone already has).

EDIT:  Here is a way to export the data using Webmin or phpMyAdmin (replacing steps 1 and 2 above).  Use Webmin if you have it, because it produces cleaner output:

If using Webmin, from the main Webmin page, go to “Servers”, then “MySQL Database Server.”  Under “MySQL Databases”, click on “asterisk”, and it should take you to the “Edit Database” page (be very careful from here on out because if you do the wrong thing you could really mess up your system).  In the “Edit table” dropdown, select either outbound_route_patterns or trunk_dialpatterns, depending on which you want to work with.  That should take you to an “Edit table” page, but at the bottom of that page you should see a button labeled “Export as CSV.” Click on that button, and it will take you to a “CSV export options” page.  You want to select the following:

  • CSV with quotes
  • Yes to “Include column names in CSV?”
  • For export destination, use whichever is more convenient for you (note that if you “Save to file” it will be placed in a directory on your server, so you might find it easier to display it in a browser window and then save it from there).
  • Export all rows
  • Leave all columns selected in “Columns to include in CSV” (you’ll discard the first column in step 3, but you’ll still need it to allow you to determine which rows to keep for each route or trunk)

Then click the “Export Now” button.  If you exported to a browser window, use Ctrl-A to select all the lines, and Ctrl-C to copy them (⌘A and ⌘C on a Mac), then proceed with Step 3 above (except use Ctrl-V or ⌘V to paste the lines into the text editor). Or, if you prefer to use a CSV editor, then in your browser simply go to File | Save Page As… and save the entire page as a plain text file with a .csv extension to your local machine, and then proceed with Step 3 above.

If using phpMyAdmin, from the main page select “asterisk” in the left-hand column, then (still in the left-hand column) either outbound_route_patterns or trunk_dialpatterns, depending on which you want to work with. Then click the “Export” tab in the main window, and then under “View dump (schema) of table”, in the “Export” section select CSV.  The view should change to show an “Options” section, and there you want to change “Fields terminated by:” from a semicolon to a comma, and then check the “Put fields names in the first row” checkbox.   Then click the “Go” button and it should display the data in a format that can be copied and pasted into a text editor. Sorry, that’s the best I can advise you with regard to phpMyAdmin, since I seldom use that program (and I may have an older version, so things might have changed slightly).

Recent Posts

Recent Comments

Archives

Categories

Meta

GiottoPress by Enrique Chavez