Recent pushes for "green" technology focus mostly on talk, with little action for the typical home- or small-office environment. Many users leave their systems online continuously through laziness or ignorance, resulting in a significant source of power consumption, as well as an additional vector for malware propagation. The tools and code presented here allow you to find those inactive systems and securely start the shutdown process. With a Linux® box monitoring your network connections using Argus and some custom Perl code, any system that supports Perl can be set to be remotely shut down when a centralized set of inactivity rules are met.
Although the control code for securely communicating and shutting down systems is cross-platform, you'll need hardware capable of running Linux for the monitoring computer, which we'll call the control node. Such a node could be a Linksys NSLU2 or similar low-wattage device running Linux. The control node needs a method for monitoring network connections throughout your entire network. Minimal processing power, RAM, and storage are required, unless you intend to monitor hundreds of machines on a high-bandwidth pipe. Virtually any PC manufactured after 2000 with an appropriate network adapter and the capability to run Linux should provide enough capacity to monitor a small office or home network. One network adapter for the control node will suffice in low-traffic settings, but two is better on busy networks. See the "Hardware placement" section for suggestions on where to place your selected system.
Snort, Tcpdump, or a number of network monitoring software packages will work in theory for measuring usage. This article uses a combination of Argus and racluster (an Argus client) due to its simplicity and light weight for tracking connection status. In addition to the heavy lifting done by Argus, you'll need Perl and the associated modules for network communications, random string generation, and encryption. These modules are POE, String::MkPasswd, and Crypt::Blowfish, respectively. See Resources for details on finding and installing these modules.
Selecting an appropriate location for a full-network monitoring connection point is a complex topic with many variables dependent on specific network attributes. A certain amount of familiarity with network topologies is assumed, as well as the ability to identify the upstream connection point all network traffic flows through.
For example, consider a typical home network with four computers connected to a wireless access point (WAP) or router with an outbound connection through a cable or DSL modem. (I realize using the term "modem" here is inaccurate, but since the industry latched onto the misnomer, I'll use it, too — albeit reluctantly.) Placing the control node network connection between the WAP and the modem allows monitoring of all connections to the Internet from any machine on network. Alternatively, many home-office WAPs are also routers with wired Ethernet ports, so connecting to the WAP's wired port directly may suffice.
Keep in mind that the correct placement of the control node (with the appropriate network interface) and the associated configuration of the network adapter or adapters on the control node may present a formidable challenge for the casual reader. See Resources for tutorials and research materials to help you get started in network monitoring.
Argus setup on the control node
Providing simple measurements of network connection attributes with minimal resource consumption is ideal for this article. Instead of writing Snort rules or analyzing Tcpdump output, use Argus and some command-line switches to provide a minimal list of network connections. On the control node, install Argus and run the following command.
Listing 1. Argus command on control node
argus -w /tmp/argusOutput -i eth0 |
Transaction records recorded by Argus are appended to the /tmp/argusOutput file, and
the eth0 interface is assigned to sniff packets in promiscuous mode. Now that Argus is
set up to monitor the connections for each host on the network, use one of the many
Argus clients to print out information about those connections. Use the command racluster -L0 -M -u -r /tmp/argusOutput tcp to show the current
connections on the network, including information like total bytes transferred and
connection start times. Note that this command may produce a great deal of
information, so consider isolating the most active connections with the command racluster -L0 -M -u -r /tmp/argusOutput tcp -w - | rasort -m bytes |
head.
rasort combined with head will produce a list of the top 10 connections by the number of bytes transferred.
Note that the Argus and process_racluster.pl program (listed below) need to be run as a user with appropriate privileges for accessing the network adapter in promiscuous mode. In most cases, this is the root user.
Selecting machines and bandwidth usage
Each machine to be monitored for inactivity needs to have an identifier, a number of bytes indicating inactivity, and a command to run when inactivity is detected. Listing 2 shows an example activityRules file.
Listing 2. Example activityRules file
192.168.1.30_#_10_#_xmessage "Elena's laptop inactive" 192.168.1.35_#_400_#_xmessage "Alexander's desktop inactive" |
Note that these numbers are examples only and need to be tuned for the specific usage of machines on the network. The first line will pop up a message on the control nodes' display (if X Window System is active), letting the user know that the laptop is inactive and needs to be powered down.
process_racluster.pl for inactivity monitoring
Reading the activityRules file, processing racluster output and executing commands is performed by the
process_racluster.pl program. Listing 3 lists the first part of process_racluster.pl.
Listing 3. process_racluster.pl part 1
#!/usr/bin/perl -w
# process_racluster.pl - run commands if inactivity conditions are met
use strict;
my $sleepTime = 600; # seconds between network activity reads
my $argusFile = "/tmp/argusOutput";
my $raclusterFile = "/tmp/raclusterOutput";
my %rules = (); # store commands, thresholds for each ip
open( INFILE, "activityRules" ) or die "can't open rules file";
while(my $line = <INFILE> )
{
my( $ip, $threshold, $command ) = split "_#_", $line;
$rules{ $ip }{ threshold } = $threshold;
$rules{ $ip }{ command } = $command;
}#while in file
close( INFILE );
|
After variable declaration and reading the activityRules file, the next step is the main logic loop.
Listing 4. process_racluster.pl part 2
while( 1 )
{
# check if Argus is running
my $cmd = qq{ps -aef | grep argus | wc -l };
die "argus no longer running" unless ( `$cmd` ne "1\n" );
# transform the argus data into human readable form
$cmd = qq{racluster -L0 -M -u -r $argusFile tcp > $raclusterFile};
$cmd = `$cmd`;
my %hosts = ();
open( INFILE, "$raclusterFile" ) or die "can't open argus output file";
while(my $line = <INFILE> )
{
# grab destination addr, port, total packets, bytes, connection status
$line = substr($line,68);
my( $ipAndPort, undef, $bytes ) = split " ", $line;
my @parts = split '\.', $ipAndPort;
# skip if there is no port associated (probably not tcp)
next unless( $#parts == 4 );
# get just the ip address
my $ip = substr($ipAndPort, 0, rindex($ipAndPort,".") );
$hosts{$ip} += $bytes;
}#while linein
close(INFILE);
|
At each pass of the main logic, a simple process check is run to make sure the Argus
network connection monitor is active. If so, the racluster command described above is run, and the IP address and
associated byte count for all connections to that address is recorded in the %hosts hash. Listing 5 shows the remainder of the main logic loop.
Listing 5. process_racluster.pl part 3
for my $key( keys %rules )
{
my $foundKey = 0;
$foundKey = 1 if( ! exists($hosts{$key}) );
if( exists($hosts{$key}) && ($hosts{$key} < $rules{$key}{threshold}) )
{
$foundKey = 1;
}#if key is not found
next unless ( $foundKey == 1 );
# run the command, remove the entry from processing
system( $rules{$key}{command} );
delete $rules{$key};
}#for each key in the rules
# delete the processed files, wait for next pass
$cmd = qq{ rm $argusFile $raclusterFile };
$cmd = `$cmd`;
sleep($sleepTime);
}#while 1 loop
|
Each defined IP in the rule dataset is checked for existence in the %hosts hash. If the IP is not found, or the byte threshold is less
than that specified in the rules file, the inactive command is run. After an inactive
state has been found, the entry in the rule dataset is removed to prevent further
processing of an already-inactive host. /tmp/argusOutput is deleted to reset the recorded connection status, and the temporary racluster output file is deleted to save space. After sleeping for 10 minutes, the process repeats.
To begin the inactivity processing, run the command perl
process_racluster.pl with the same privileges as the ID that ran Argus (usually
root). As the "laptop" and "desktop" systems move to inactive states, you'll see a
pop-up message on the control node showing you which person to give the Kermit guilt
trip ("It's not easy being green").
Providing a method for remotely shutting down systems is a great way to open doors for control and abuse. To prevent some of these simpler attacks, the following client and server implement a simple challenge-response system. This approach helps ensure that systems will only respond to the shutdown command if the correct encryption key is used.
Listen for shutdown requests: shutdownListener.pl
Each system that will respond to remote shutdown requests needs to run the shutdownListener.pl program.
Listing 6. shutdownListener.pl
#!/usr/bin/perl -w
# shutdownListener.pl - manage challenge/response, shutdown if authenticated
use strict;
use POE qw(Component::Server::TCP Filter::Reference);
use Crypt::Blowfish;
use String::MkPasswd qw(mkpasswd);
my $key = "<enter your key of choice here>";
my $random = substr(mkpasswd,0,8);
my $cipher = new Crypt::Blowfish $key;
my $cipherText = "";
# heavily influenced by the POE cookbook:
POE::Component::Server::TCP->new
( Alias => "shutdownListener",
Port => 11211,
ClientFilter => "POE::Filter::Reference",
ClientInput => sub {
my ( $sender, $heap, $input ) = @_[ SESSION, HEAP, ARG0 ];
if( $cipherText eq "" )
{
# send the challenge text back to client
$heap->{client}->put( \$random );
$cipherText = $cipher->encrypt( $random );
}elsif( $cipherText eq $$input )
{
print "challenge response match, shutting down\n";
# uncomment this line to actually shutdown the system
# system(`shutdown -h now`); # linux
# system(`shutdown -s -t 01`); # windows
exit(0);
}else
{
print "challenge response does not match, exiting server";
exit(1);
}#if cipher text not generated
},
);
$poe_kernel->run();
exit 0;
|
The POE module provides one of the easiest ways to create bidirectional TCP communications using Perl. When a shutdown request is received, the current random string is sent back to the client. If the client's encrypted return string and the locally encrypted string match, the shutdown command is run. Note that you should replace the key with a unique value that matches the key defined in clientShutdown.pl below. In addition to shutting down the system, the shutdownListener.pl will terminate its execution if the challenge response is not met to deter repeated connection attempts.
Save the shutdownListener.pl code on each machine that should enable remote shutdowns. shutdownListener.pl should be run as a user with appropriate privileges to shutdown the machine and access the port selected.
Send shutdown requests: clientShutdown.pl
Each machine to be shut down listens to the control node for shutdown connection requests. These requests come from the clientShutdown.pl program.
Listing 7. clientShutdown.pl
#!/usr/bin/perl -w
# clientShutdown.pl - compute challenge request, send response
use strict;
use POE qw( Component::Client::TCP Filter::Reference);
use Crypt::Blowfish;
die "specify hostname " unless @ARGV == 1;
my $host = $ARGV[0];
my $key = "<enter your key of choice here>";
my $cipher = new Crypt::Blowfish $key;
my $port = 11211;
my $sendStr = "getChallenge";
# heavily influenced by the POE cookbook:
POE::Component::Client::TCP->new
( RemoteAddress => $host,
RemotePort => $port,
Filter => "POE::Filter::Reference",
Connected => sub {
$_[HEAP]->{server}->put( \$sendStr );
},
ConnectError => sub {
die "could not connect to $host:$port ...\n";
},
ServerInput => sub {
# get the server response
my ( $input ) = @_[ ARG0 ];
# encrypt the response, send back
my $cipherText = $cipher->encrypt( $$input );
$_[HEAP]->{server}->put( \$cipherText );
},
);
$poe_kernel->run();
exit 0;
|
Similar to shutdownListener.pl, the clientShutdown.pl program connects to the machine to be shut down, retrieves the random text, encrypts it with the shared key, and returns the encrypted text. Again, the encryption key needs to match between the client and server. Modify the activityRules file by inserting the line shown in Listing 8.
Listing 8. Remote shutdown in activityRules
192.168.1.33_#_5555554_#_perl clientShutdown.pl 192.168.1.33 |
Now when process_racluster.pl is restarted and the total network usage for the 192.168.1.33 machine drops below approximately 5.5 MB over 10 minutes, the shutdown command will be sent.
To recap the usage instructions: Modify the activityRules file to run the appropriate commands for each machine. Start shutdownListener.pl on any machine that should be remotely shut down. Start Argus and the process_racluster.pl program on the control node.
With the tools and code in this article, you can set up a network monitor to track the total usage of specific machines on your network. Use the programs presented herein to enforce an automatic shutdown when systems become inactive on the network. Consider modifying Argus to search for specific connection types or hosts. Shut down systems when only 15-minute e-mail checks are active, or when a streaming audio connection is left playing in mute for hours. Monitor the protocol used or the connection start times to further enhance the detection of inactivity that triggers a shutdown.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | os-automatic-shutdown-inactiveShutdown_0.1.zip | 3KB | HTTP |
Information about download methods
Learn
-
For more information about network monitoring, check out the Tcpdump man page and the Snort users manual.
-
The Linksys NSLU2 and WRT54G are special-purpose, low-energy devices upon which users
have installed NSLU2-Linux and BatBox wrt54g, respectively.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Stay current with developerWorks' Technical events and webcasts.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
-
Learn how to install Perl
modules and read more about Perl.
Get products and technologies
-
Argus monitors your network connections. Argus Downloads offers binary and
source code. The Argus
"server" and the Argus client source code include
raclusterandrasort. -
The challenge/response shutdown server uses the POE, String::MkPasswd, and Crypt::BlowFish Perl modules.
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
-
Download IBM product evaluation versions, and get your
hands on application development tools and middleware products from DB2®,
Lotus®, Rational®, Tivoli®, and WebSphere®.
- Download Perl, or read more about Perl at Perl.org.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.

Nathan Harrington is a programmer working with Linux at IBM. Check out his recently published articles.
Comments (Undergoing maintenance)





