Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Nagios plugin to detect changed IP address of WRT54G router.

by blue_cowdawg (Monsignor)
on Feb 24, 2006 at 22:58 UTC ( [id://532692]=CUFP: print w/replies, xml ) Need Help??

Problem Statement

Like a lot of geeks out there I run my own mail server on my Linux box at home. To connect to the Internet I use a Linksys WRT54G router and I'm connected to the Greater Comcat High Speed Internet service via a cable modem.

So far so good.

Add to my levels of complexity the site that hosts my personal web page for me has graciously set me up with a CNAME and an MX record pointing to my home box for my personal domian of berghold.net

swell... except for one thing...

Every once in a great while and sometimes on rare occasion as frequently as thrice in a week the DHCP server at Comcast gets cranky on me and re-assigns my IP addres.

OH NO! What's a geek to do?

Before I had the Linksys router attached to the cable modem, I had the Linux box connected directly to the cable modem I had a Perl script that took the output of

/sbin/ifconfig eth0
and munged it so I knew what the IP address was right now and I checked that against what I expected it to be and if it changed I would get paged and I knew to send the ever patient with me staff at my hosting provider and have them update my CNAME and MX record.

Cool..

But now with the Linksys in the way things are a bit dicier. I can't exactly run ifconfig eth?? much less a Perl script from cron on it. I could I suppose if I loaded OpenWRT on my router, but having not totally read up on the subject I'm not even sure about that. Probably not.

Also being the geek that I am I have Nagios running on one of my boxen that keeps track of the rest of my boxen. And it keeps track of my hosted web site and makes sure it is running and a few other odds and ends that I have it doing. I am even working on a sensor that will hook into Nagios and warn me if my freezer stops running. But I digress.

The WRT54G router comes with an administrative web interface that also includes a page showing the status of the router as well as the IP address that its outbound interface has been assigned by the ISP.

If there is a web page... I can load it into memory and parse it!

Looking at the output of this interface I realized that the pattern I was looking for was a table cell containing a call to a javascript function called Capture with the parameter share.ipaddr and that the very next cell after that would (stuck in the middle of some markup code) contain the IP address I was after.

Now that I can find that, I can compare that to what I expect my IP address to be and from there trigger Nagios if need be.

What is Nagios Looking for?

The Nagios standard calls for the following exit codes from a well behaved plugin:
return codestate
0OK
1WARNGING
2CRITICAL
With that in mind I also want to return some text as when the notification goes out this is the text that is sent along with the notification. For this I decided to keep it simple stupid and have one of two states be returned by my plugin.
return codestateMessage
0OKOK: My IP Address is: {IP}
2CRITICAL CRITICAL: My IP Address has changed to: {NEWIP}

The Code

Without further ado, here is the code:

#!/usr/bin/perl -w ###################################################################### +## use strict; use warnings; use LWP::UserAgent; use Data::Dumper; use HTML::TableContentParser; use Getopt::Long; my $host=""; my $realm=""; my $password=""; my $addr = ""; my $port=80; my $result=GetOptions( "host=s" => \$host, "realm=s" => \$realm, "password=s" => \$password, "ipaddress=s" => \$addr, "port=i" => \$port ); my $ua=LWP::UserAgent -> new(); barf_and_complain() unless ( $host and $realm and $password and $addr ); my $host_settings=sprintf("%s:%d",$host,$port); my $url = sprintf("http://%s:%d/Status_Router.asp",$host,$port); $ua -> credentials ($host_settings,$realm,"",$password); my $response = $ua->get($url); my $content = $response->content(); my $parser=HTML::TableContentParser->new(); my $tables = $parser->parse($content); my $capture_ip = 0; my $ip_addr=""; foreach my $table(@$tables){ foreach my $row(@{$table->{rows}}){ foreach my $cell(@{$row->{cells}}){ next unless $cell->{data}; if ( ! $capture_ip ) { $capture_ip++ if $cell->{data} =~ m/\<script\>Capture\(share.ipaddr +\)/; next; } if ( $capture_ip) { $cell->{data} =~ m@\<B\>(\d+\.\d+\.\d+\.\d+)\<\/B\>@; $ip_addr=$1; $capture_ip=0; } } } } if ( $ip_addr eq $addr ){ printf "OK: My IP Address is: %s\n",$ip_addr; exit 0; } else { printf "CRITICAL: My IP ADDESS HAS CHANGED to %s\n",$ip_addr; exit 2; } sub barf_and_complain { printf "%s\n",qq( You must specify all options for this plugin to work: --host hostname or ip of router --realm security realm of router --ipaddress expected ip address --password login password for router the --port option is just that, an option and default +s to 80 ); exit(1); }

Integrating this with Nagios

For completeness sake, here is the command definition for the check. There is more than one way of implementing this check, but here is how I did it since I only have one router. If I had more (why would I at home?) then I'd leave the command definition more flexible.

define command { command_name check_my_router_ip command_line $USER1$/check_routerip.pl --host 111.222.234.2 +34 --realm myroutername --ipaddress 234.321.123.321 --password 's3cr3 +t!!' }
No.. don't think for one second any of those IP addresses or that password are real. Put your own in there!

To further integrate this you have to associated it with a host definition as a service. Here is what my service definition looks like:

define service { use generic-service host_name my_router_inside_ip service_description Assigned DHCP Address is_volatile 0 check_period 24x7 max_check_attempts 10 normal_check_interval 20 retry_check_interval 5 contact_groups boss notification_period 24x7 notification_interval 960 check_command check_my_router_ip }

That's pretty much it... There are ways I could improve on this, but I'll leave that a) as a project for the reader's intellect and b) as a subject for a future follow up on this. Enjoy!

Replies are listed 'Best First'.
Re: Nagios plugin to detect changed IP address of WRT54G router. - TIMTOWTDI
by tmiklas (Hermit) on Feb 27, 2006 at 14:26 UTC
    Hmmm - very nice code and perl usage at all BIG ++

    ... but to get the same result you don't have to run such 'combinations'... maybe i should not write this as this is Perlmonks but using Perl should make our life easier... and sometimes not-using Perl does the same :-/

    Fast and tested solution... register yourself with dyndns or no-ip system or anything like this (it's free)... and set your DNS properly:
    hostname.my.domain. IN CNAME dynamic.update.domain. hostname.my.domain. IN MX 1 hostname.my.domain.
    then point the MX to the same host (as shown above)... all of the systems provide free tools for updating it's entries... and to do it Perl-ish way, use Net::DNS::DynDNS :-)

    The task now is to get the information about IP change no later than a few seconds after it has changed... without constantly querying the router or blindly updating the dns database without checking if it was necesery. Eventually your host will be inaccessible for some time (but anyway much shorter than waiting for the ISP staff to change the DNS entries and reload configuration).

    I hope i won't get downvoted (too much) for describing non-Perl solution, hehe...

    Greetz, Tom.
          Fast and tested solution... register yourself with dyndns or no-ip system or anything like this (it's free)... and set your DNS properly

      Your suggestion has merit. It does however miss the point of why I do what I do. My MX is for the domain that I have registered to myself. I don't want "dyndns" in the middle somwhere. I already pay folks for the privelege of using their machine to host my website, so why not also let them host my CNAME and MX?

      The good news is the DHCP assigned address changes infrequently enough that this approach that I'm using isn't a major PITA. If it ever became such I'd re-review my options and possibly implement something along the lines of what you suggest. When I first started hosting my own mailserver and farming out my MX DNS entry DynDNS was not an ooption.

      In fact, the incidence of my IP address changing happens so infrequently that I've even considered adding to this script (and it's predecessor which wasn't as nice as this one) logic to automatically create the trouble ticket with my hosting provider for them to update the DNS entry.

      I'm just waiting for the day when T1 lines become "affordable" and I can host my whole domain myself. Yeah... right... dream on...


      Peter L. Berghold -- Unix Professional
      Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
        Cheap but equally sad solution I used once in a similar situation was to have a cron hit a page on my external website every X minutes/hours - then update my external records as needed via the ping-page.



        "I have never written bad code. There are merely unanticipated features."

        I also use dyndns, and find it to work wonderfully for my purposes (with my own domains as well).

        With Tom's suggestion you will not need to receive at email@yourdomain.dyndns.org, you will still be able to accept mail at email@yourdomain.com.

        The changes at your dns host would look something like this:

        OLD: www.yourdomain.com. IN A 111.222.111 (static ip of webserver) emailserver.yourdomain.com. IN CNAME homecomputer.yourdomain.com. homecomputer.yourdomain.com. IN A 222.111.222 (dynamic ip) yourdomain.com. IN MX 10 emailserver.yourdomain.com. NEW: www.yourdomain.com. IN A 111.222.111 (static ip of webserver) emailserver.yourdomain.com. IN CNAME uniquename.dyndns.org. yourdomain.com. IN MX 10 emailserver.yourdomain.com.

        In the NEW: example, your dns host no longer needs to update any ip addresses. DynDNS would have a record that looks something like this:

        uniquename.dyndns.org. IN A 222.111.222 (dynamic ip)
        You would run a DynDNS client (Perl script?) that automatically updates that ip address as it changes.

        With dyndns.org keeping track of your routable ip address, your dns host only needs to know that they can reach that server at uniquename.dyndns.org.

        This will allow you to sit back and relax, knowing that even if your ip address changes everyday, email@yourdomain.com will still be delivered to your email server without you having to call anyone.

        FWIW, the last router (Netgear) I bought for my house has a DynDNS client installed on it, so I don't need to run the client on one of my computers.

        Of course i agree 100% with your oppinion... i had the same situation here. I hope you did not get me wrong - the easiest way to express my point of view is TIMTOWTDI - nothing more. Leaving your A and MX records to guys at DynDNS has some more concerns (like security... for paranoid people) and lots more... Anyway it does not change my oppinion that I like your code shown above - it has already helped me today with another problem :-)

        Cheers, Tom.
        The #1 reason to have your DNS and Other Services hosted by separate entities.... If your Service Provider flakes out with webhosting ( say, a DDOS attack... ) *YOU* can redirect *YOUR* traffic away from them without any intervention on *THEIR* part... Control Am Good.
Re: Nagios plugin to detect changed IP address of WRT54G router.
by ambrus (Abbot) on Feb 28, 2006 at 20:42 UTC
    No.. don't think for one second any of those IP addresses or that password are real. Put your own in there!

    That applies especially to the ip address 234.321.123.321. :)

Re: Nagios plugin to detect changed IP address of WRT54G router.
by sserafim (Initiate) on Dec 07, 2009 at 06:25 UTC

    Two solutions for you:

    1 - Get a STATIC IP. It's cheap.

    2 - DYNDNS will resolve this issue. But you need to know how to use it. You can even have your router update the DYNDNS IP automatically.

    Also, make sure you get a service like POSTINI, which can filter and SPOOL your emails in case the server "seems" down because the IP change. This way you don't lose any email. And people sending emails to your domain don't get bounced emails.

    Sergio Serafim (sergio.b.serafim@gmail.com)
Re: Nagios plugin to detect changed IP address of WRT54G router.
by Anonymous Monk on Oct 26, 2007 at 02:07 UTC
    I'm REALLY surprised nobody mention something WAY easier. Your website is hosted on an external host. Why not create a http://whatsmyip.com style page on your personal website? Or use http://whatsmyip.com (specifically www.whatismyip.com/automation/n09230945.asp which gives you just your IP!). Sure it reduces the amount of perl coding to almost zero, so it's not really fun, but no need to deal with logging into the Linksys and scraping the IP there and all that trouble.
          Why not create a http://whatsmyip.com style page on your personal website?

      What if perchance the link is down? Then I could end up with a false positive. I'll stick with my method thnak you very much.


      Peter L. Berghold -- Unix Professional
      Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://532692]
Approved by atcroft
Front-paged by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (6)
As of 2024-04-23 15:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found