Automating interactions with your netgear router

Posted by spezia on Thu 8 Feb 2007 at 11:54

Here is how I wrote some code using Perl to automate controlling my router. I have a NETGEAR DG834 ADSL router and I wanted to control it via ifup/ifdown so, with the help of sudo, I can allow my home users to connect/disconnect to Internet from a debian box.

Using ifup/ifdown is better than controlling router via its web interface because I don't want to tell all my users the router admin password. I chose Perl because there is good library to build software that simulate a browser.

The Perl script uses the WWW::Mechanize module to perform the following router operations:

  • get status
  • start connection
  • stop connection

The script must be invoked with an action name as argument, which may be one of 'status', to show connection status, 'connect' or 'disconnect'..

When the script ends it print the current status of the connection.

Note: The script read router address and password from the config file /usr/local/etc/adsl_remote_control.conf or from the file pointed by environment variable $ADSL_REMOTE_CONTROL_CONFIG_FILE.

The next step is integration with ifupdown, I configured a fake interface in the file /etc/network/interfaces:

...
iface router inet manual
 up adsl_remote_control.pl connect
 down adsl_remote_control.pl disconnect
...

Finally I added a rule to the sudoers file /etc/sudoers:

...
User_Alias SYSTEM_USERS = pippo, pluto, paperino, qui, quo, qua
Host_Alias LOCALHOST    = localhost, solaris
...
SYSTEM_USERS LOCALHOST = NOPASSWD: /sbin/ifup router, /sbin/ifdown router
...

Now any user in sudo SYSTEM_USERS group can run "ifup router" and connect to the Internet; note that you can write scripts to put in /etc/network/if-* and they will be called by ifup or ifdown when you bring up or tear down the connection: for example, every users that start a connection on my box automatically starts fetchmail to download new mail.

Perl script:

#!/usr/bin/perl

use Config::Simple;
use MIME::Base64;
use WWW::Mechanize;

# Default location of config file
use constant DEFAULT_CONFIG_FILE => '/usr/local/etc/adsl_remote_control.conf';
# Use this environment variable to use another config file
use constant ENVIRON_CONFIG_FILE => 'ADSL_REMOTE_CONTROL_CONFIG_FILE';
# Configuration file entries
use constant ROUTER_PARAM => 'router';
use constant PASSWORD_PARAM => 'password';

# The admin page address
use constant ADMIN_PAGE_TEMPLATE => 'http://%s/setup.cgi?next_file=st_poe.htm';
# The login name of admin user on router(fixed)
use constant ADMIN_USERNAME => 'admin';

# Command line arguments
use constant CONNECT_ACTION => 'connect';
use constant DISCONNECT_ACTION => 'disconnect';
use constant STATUS_ACTION => 'status';

# Exit errors codes
use constant NO_ERRORS => 0;
use constant PARSE_COMMAND_LINE_ERROR => 1;
use constant PARSE_CONFIG_FILE_ERROR => 2;
use constant NETWORK_ERROR => 3;

# Application entry point
my $action = parse_command_line();
my %config = parse_config_file();
do_action($action, $config{+ROUTER_PARAM}, $config{+PASSWORD_PARAM});

exit NO_ERRORS;

# Parse the command line and return the action to do; exit on errors
sub parse_command_line {

  my $action;

  if(@ARGV != 1) {
    usage();
    exit PARSE_COMMAND_LINE_ERROR;
  }
  foreach(CONNECT_ACTION, DISCONNECT_ACTION, STATUS_ACTION) {
    if($ARGV[0] eq $_) {
      $action = $ARGV[0];
      break;
    }
  }
  if(!$action) {
    usage();
    exit PARSE_COMMAND_LINE_ERROR;
  }

  return $action;

}

# Print an help message
sub usage {

  print("Usage: adsl_remote_control.pl connect|disconnect|status\n");

}

# Parse configuration file and return an hash of readed values; exit on errors
sub parse_config_file {

  my $config_file_path;
  if($ENV{+ENVIRON_CONFIG_FILE}) {
    $config_file_path = $ENV{+ENVIRON_CONFIG_FILE};
  }
  else {
    $config_file_path = DEFAULT_CONFIG_FILE;
  }
  my %config;
  Config::Simple->import_from($config_file_path, \%config);
  if(!%config) {
    print("Error while reading config file $config_file_path\n");
    exit PARSE_CONFIG_FILE_ERROR;
  }
  if(!$config{+ROUTER_PARAM} or !$config{+PASSWORD_PARAM} or %config != 2) {
    print("Error while parsing configuration file $config_file_path\n");
    exit PARSE_CONFIG_FILE_ERROR;
  }

  return %config;

}

sub do_action {

  my($action, $router, $password) = (@_);
  my $status;
  my $root_page = build_root_page_url($router);
  my $admin_page = build_admin_page_url($router);
  my $base64_id = MIME::Base64::encode( ADMIN_USERNAME . ':' . $password);
  my $browser = WWW::Mechanize->new();
  $browser->quiet(true);
  $browser->add_header(Authorization => 'Basic ' . $base64_id);
  # Get the initial page, or the script fail at boot
  $browser->get($root_page);
  $browser->get($admin_page);
  if($action ne STATUS_ACTION and $browser->success()) {
    $browser->set_fields('todo' => $action);
    $browser->submit();
  }
  if(!$browser->success()) {
    print_network_problem($router);
    exit NETWORK_ERROR;
  }
  if($browser->content =~ /Connected<\/td>/) {
    $status = 'Connected';
  }
  elsif($browser->content =~ /(Disconnect|Failed)<\/td>/) {
    $status = 'Disconnected';
  }
  else {
    print_network_problem($router);
    exit NETWORK_ERROR;
  }
  print "$status\n";

}

# Buil the address of root page
sub build_root_page_url {

  my $router = shift;
  my $root_page = "http://$router";

  return $root_page;

}

# Build the address of admin page
sub build_admin_page_url {

  my $router = shift;
  my $admin_page = sprintf(ADMIN_PAGE_TEMPLATE, $router);

  return $admin_page;

}

# Print a network error message
sub print_network_problem {

  my $router = shift;
  print "Error during talking with $router\n";

}

Config file

router          router.mynet
password        xxxxxxxxxxxx
Share/Save/Bookmark


Posted by Anonymous (62.254.xx.xx) on Thu 8 Feb 2007 at 14:13
thanks for the article, i doubt my aging netgear router will be supported but something to try :)

sno

[ Parent | Reply to this comment ]

Posted by PJ_at_Belzabar_Software (125.23.xx.xx) on Fri 9 Feb 2007 at 07:12
[ Send Message | View Weblogs ]
If you telnet to most adsl routers you end up with a menu or a busybox interface.

On these I can use "expect", which is a nice lightweight tool for the things I want to do.

Eg, to do a reboot (menu item 12, then 1 to confirm) on the cheap router I have:

#!/usr/bin/expect -f

set remote_server "192.168.1.1"
set my_user_id "admin"
set my_password "mylamepassword"
set my_command1 "12"
set my_command2 "1"

spawn telnet $remote_server
expect "Login:"

send "$my_user_id\r"
expect "Password:"

send "$my_password\r"
expect "Exit"

send "$my_command1\r"
expect "Confirm"

send "$my_command2\r"
expect "wait"

sleep 1
close

PJ

[ Parent | Reply to this comment ]

Posted by spezia (131.175.xx.xx) on Fri 9 Feb 2007 at 10:02
[ Send Message ]
Tanks for the tips on expect, but DG834G don't has a telnet server enabled by default. To enable telnet you must run the router in DEBUG mode. I don't know how to make the router in DEBUG mode.

[ Parent | Reply to this comment ]

Posted by Steve (80.68.xx.xx) on Fri 9 Feb 2007 at 13:29
[ Send Message | View Steve's Scratchpad | View Weblogs ]

Try clicking on this link:

Then you should be able to telnet away .. I found a small page describing the installed software which might be useful too.

Steve

[ Parent | Reply to this comment ]

Posted by Anonymous (62.254.xx.xx) on Fri 9 Feb 2007 at 16:02
excellent, this 7+ year old rp11x model may have some use yet :D it does indeed have telnet access.

[ Parent | Reply to this comment ]

Posted by deadcat (67.166.xx.xx) on Sun 25 Feb 2007 at 06:22
[ Send Message | View Weblogs ]
is it possible to make the script clear the access log on the router?
i dont want my domain name to show up there. thanks

[ Parent | Reply to this comment ]

Posted by spezia (82.52.xx.xx) on Sun 25 Feb 2007 at 13:24
[ Send Message ]
Clear router log isn't implemented in the perl script. You can edit the script adding a new action "clear log". Note that now I'm developing a new modular version of router-control. If you want to extends router-control i can send you the new sources (java code).

[ Parent | Reply to this comment ]

Posted by deadcat (67.166.xx.xx) on Sun 25 Feb 2007 at 17:39
[ Send Message | View Weblogs ]
dont worry about it. its no big deal. thanks

[ Parent | Reply to this comment ]

Posted by Anonymous (85.18.xx.xx) on Tue 17 Jun 2008 at 16:37
Dear Spezia,
excellent idea. I have been looking for something like that for long.
Bad luck I am totally ignorant on programming ....
If I am allowed a suggestion, why don't you produce a small utility (for windows as well?) doing what your code is meant to do and publish it?
I guess you would collect much gratitude
Ciao
G

[ Parent | Reply to this comment ]


User Login

Username:

Password:

[ Advanced Login ]

Register Account

Quick Site Search