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
sno
[ Parent | Reply to this comment ]
[ Send Message | View Weblogs ]
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 ]
[ Parent | Reply to this comment ]
[ 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.
[ Parent | Reply to this comment ]
[ Parent | Reply to this comment ]
i dont want my domain name to show up there. thanks
[ Parent | Reply to this comment ]
[ Parent | Reply to this comment ]
[ Parent | Reply to this comment ]
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 ]