DHCP NAT

March 8th, 2010 jud No comments

I have debated about whether or not to post this project. I wanted this to have a sqllite backend and run on the Cisco AXP but never had the time to go back and hack more on it. I think it could have won the AXP hacking contest, but I didn’t even know about the contest at the time.

I also want to say that this project is not for the inexperienced Linux admin. This project involves a solid understanding of DHCP, DNS and iptables. I have rolled a .tar file of all of the scripts and directories needed to make this happen, but I’ve said it before and I will say it again, you got it off the web, your mileage may vary. Here is the file.

History
The basic idea here is this, we have ~375 printers that were all translated using static NAT, but most of our infrastructure runs off DHCP. So when someone moves an office they unplug their VOIP phone and computer and take it to their new office, but the printer involves manual intervention, not good.

There is one thing to say about a lazy network administrator, it usually means problems like this get fixed one way or the other. I wanted a way to be able to move printers within the “enterprise” and have their NATed address follow them. This is my solution.

NAT

A picture is worth a thousand words so here is a simple diagram.

Diagram of WAN.

The 192.168.24.0/24 subnet is shared between the Circus and our Electronic Medical Records vendor, EMR for short. However, the Circus is partially switched and partially routed on campus so we have to NAT IP addresses on the shared subnet into IP addresses in the enterprise. So this diagram shows a translate server with eth0 at 192.168.24.2 and eth1 as 192.168.100.58, eth0:1 is a NAT’ed IP address for a printer somewhere in the enterprise.

Let me explain this again, this time with commands:
WAN address that is shared with another company, in our case a printer:
wan printer — 192.168.24.100 — IP address EMR uses to print.
translate eth0:1 — 192.168.24.100 — IP address EMR uses to print, actually a virtual interface on translate server.
lan printer — 192.168.10.100 — IP address of printer on campus.
translate eth0 — 192.168.24.2 — WAN leg of translate server.
translate eth1 — 192.169.100.2 — LAN leg of translate server.

The wanprinter ip address 192.168.24.100 is a virtual ip address on a linux
server, translate. It would be brought up from the command line like this:

# /sbin/ifconfig eth0:1 192.168.24.100 netmask 255.255.252.0 up

It is then natted from 192.168.24.100 to it’s local ip address:

# /sbin/iptables -t nat -D PREROUTING --destination 192.168.24.100 -j DNAT --to 192.168.10.100

The other rule that is added only once is to make it look like all traffic
is coming from the translate server:

# /sbin/iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to 192.168.100.2

rsyslog setup

You need to download rsyslog. This is because you need reliable remote logging and the normal syslog server (sysklogd) does not handle it. I am using librelp version 0.1.1 and rsyslog version 3.20.2. The problem that relp/rsyslog fixes is that standard syslog does not reliably log remotely. So what ends up happening is that dhcp log messages will be dropped, eventually it will get you because an address update message will be dropped.

The DHCP servers in our organization run Red Hat Enterprise Linux and the translate servers run Ubuntu LTS. Please check the README or INSTALL document from rsyslog for installation on your platform. The dhcp servers will be remote logging dhcp transactions to the translate servers so that a logwatch plugin can monitor address changes.

Just download these and read the doc/install.html.

wget http://download.rsyslog.com/librelp/librelp-0.1.1.tar.gz
./configure
make
make install
wget http://www.rsyslog.com/Downloads-req-getit-lid-141.phtml
./configure --enable-relp --enable-mail --enable-zlib --with-gnu-ld
make
make install

To compile on RedHat I had to use –with-gnu-ld after editing etc/ld.so.conf:

# cat /etc/ld.so.conf.d/librelp.conf
/usr/local/lib

I also had to add an environmental variable:

# export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig

In order to get RHEL standard scripts to work you have to add a symlink to
/usr/local/sbin/rsyslogd from /usr/sbin/rsyslogd:

# ln -s /usr/local/sbin/rsyslogd /usr/sbin/rsyslogd

Make sure to edit the sysklogd init script or copy in a new one. Also make sure you get rsyslogd hacked into init. The easiest way is to run this command after putting my rewritten RH init script on the box. The rewritten script is in the tar file from above as daemon/rh-etc-init.d-rsyslogd.

# chkconfig syslogd off
cp rh-etc-init.d-rsyslogd /etc/init.d/rsyslogd
# chkconfig rsyslogd on
# chkconfig --list | grep sys

If you didn’t use my init script, edit /etc/cron.daily/sysklog script to make sure rsyslog is notified about rotation.

Also edit the logrotate configuration at /etc/logrotate.conf:

# cat /etc/logrotate.d/syslog
/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler /var/log/boot.log /var/log/cron {
    sharedscripts
    postrotate
        /bin/kill -HUP `cat /var/run/rsyslogd.pid 2> /dev/null` 2> /dev/null || true
    endscript
}

Please see my dhcp-rsyslog.conf and translate.rsyslog.conf for the rsyslog
configurations for each server. If it’s not self explanatory copy the file daemon/dhcp-rsyslog.conf to the dhcp server /etc/rsyslog.conf. You will have to change the IP address of the translate server from 192.168.100.2 to whatever you are using.

DHCP Servers

Make these changes in NS1 and NS2, or if you have a setup similar to me,
just copy /etc/dhcpd/dhcpd.ad.master.conf from NS1 to NS2 and restart both DHCP servers.

In /etc/dhcpd/dhcpd.master.conf add these lines:

# Where to log stuff, this sets up remote logging for
# syslogscand and DHCP-Nat.
log-facility local3;

Restart dhcpd.

Translate Server

Our translate server is a Dell 1U 2600 or something like that. It presently
has ~375 virtual interfaces translating printers from the WAN subnet to our
internal addresses.

eth0 == External or WAN address
inet addr:172.22.25.193 Bcast:172.22.27.255 Mask:255.255.252.0

eth1 == Internal or LAN address
inet addr:172.22.100.52 Bcast:172.22.100.255 Mask:255.255.255.0

The internal IP address is only needed for the postrouting rule.

Install syslogscand on this server through CPAN and then copy DHCP.pm to the correct location. If all else fails just head DHCP.pm and see where it tells you to copy it. Then copy the file daemon/etc-syslogscand.conf to /etc/syslogscand.conf.

Finally move rc.local.firewall to a system directory and add it to rc.local
to fire off upon server start.

syslogscand Plugin Configuration

I wrote a syslogscand plugin, DHCP.pm, that watches the output of dhcp and any
time a mac address that it watches is given an IP address it queries through
dns lookups whether or not the mac address has gotten a new IP address. If it
has gotten a new address, the daemon updates the /etc/network/dhcp-nat file
as well as the /etc/network/interfaces file. It then updates iptables with the
new NAT translation.

Below is a dhcp-nat file. It is comma delimited with the first column being
the mac address of the printer, the second column is the internal LAN address,
the third column is the external WAN address and the forth column is the
hostname.

# cat /etc/network/dhcp-nat
00:0e:7f:3c:88:bc,172.22.81.110,172.22.25.252,printest252
00:1f:f3:5a:b2:6a,172.22.81.116,172.22.25.249,trek
00:19:b9:45:04:30,172.22.81.254,172.22.25.251,litespeed
00:11:0a:f6:3f:93,172.22.81.254,172.22.25.250,printest250

I have also added information to the /etc/network/interfaces file,
notice in the following stanza that I have added comments for the
mac and translate(d) address.

# head -6 /etc/network/interfaces
#mac 00:1f:f3:5a:b2:6a
#translate 172.22.81.116
auto eth0:8
iface eth0:8 inet static
address 172.22.25.249
netmask 255.255.252.0

Web Interface
There is also a web interface into this so that PC technicians can interact
with the system easily. In order to make that magic happen I added the
following line to /etc/sudoers:

www-data translate1 = NOPASSWD: /usr/bin/syslogscand, /sbin/iptables, /sbin/ifconfig

It allows the user www-data that apache runs under to restart syslogscand as
well as manipulate iptables and interfaces.

Copy all of the files in the cgi-bin directory to your local cgi-bin directory.

Copy all of the files in the html directory to the web server root.

You may have to tweak directory locations in the scripts.

Categories: Code, Linux Tags:

Testlab

March 5th, 2010 jud 2 comments

The other day I ran across a post by Cybergurl that showed her home lab. It got me thinking that every serious Cisco candidate has some sort of lab. Some people use GNS3, some have a home rack and some have a rack provided by their employer. I have a combination of all three and figured I would share some of it.

The rack I use is provided by my employer but I have added quite a bit of my own kit. It is kept in the data center and I have the ability to log in from home over VPN or SSH.

The testlab at work is a short rack with this setup:
Work owned:
6 x 3640
2 x 3560
2 x 3550
1 x 3500yl
1 x Dell 1750 Linux server

My personal:
4 x 1841
1 x 2611-XM
1 x 3620
1 x 2511-RJ

Testlab Rack

The astute reader will notice that in this picture there is also an ASA. I removed my 3620 and added the ASA so that we could configure and test it before deployment. It is also nice to have that many routers because we had the ASA set up in a BGP environment while at the same time I had the rack configured for the CCNP TSHOOT exam.

What does your testlab look like?

Categories: Routing Tags:

TSHOOT Book

March 4th, 2010 jud 2 comments

This is as close to a rant as I will put on this blog. Don’t get me wrong, I can rant, but there is rarely a reason to rant in public.

I got my TSHOOT book in the mail a couple of days ago. The reading has been slow going in part because I only bought the hard cover. The last version of CCNP books included a pdf of the book on the CD-Rom. Nice. First class even. In this day and age it was refreshing to see a company not try to nickel and dime customers.

Not any more. Now you have to purchase an ebook. I would pay for a pdf of the book like what used to be provided for free. However, that is not how CiscoPress has formatted their new ebook offerings. Check out the this nice press release. Now the customer not only has to purchase the pdf that used to be provided for free, they also have to deal with Adobe DRM on their CiscoPress ebooks and I have decided I won’t pay for that.

I understand that companies need to be able to monetize products, and I agree. I also understand that many of the pdfs ended up on file sharing sites. The way SafariBooksOnline handles pdfs is a great deterrent. Just put my name and customer number all over the document as a watermark. No one would be foolish enough to share content that was so easily traced. Even if I were to edit a watermarked pdf I would be afraid I had missed some metadata.

In the end I just burned a bunch of tokens on SafariBooksOnline and downloaded the whole book, one chapter at a time. I am extremely disappointed in CiscoPress. I would love to tell you how I will never purchase another book from them but they have a captive audience. I will, however, keep a look out for Cisco books from CiscoPress competitors. If you have some favorite Cisco books that are not from CiscoPress please tell me as I am now extremely interested.

Categories: Musings Tags:

Converting AIX print queues to Linux

February 25th, 2010 jud No comments

I spent the last week working on a project to convert all of the forms printing at the Circus from an AIX server to a Linux server. Because the version we are stuck on is not officially supported by the vendor I had to do some reverse engineering to figure out how things work, this article describes some of the scripts I used to understand what the software was doing and how to make it work the way we needed.

The funny thing about any printer project I get into, usage seems to explode. Yesterday we had a water cooler meeting about how to print from a completely different EMR system. I’m not sure we will do it because we need to know patient location but the printing part is easy. Who knew a paperless organization prints so much.

We have about 500 printers on the network and less than 314 forms, my guess would peg us closer to 150 forms, but when I go through the forms directory I get 314.

# ls -1 | cut -d _ -f 1,2 | sort -u | wc
    314     314    3293

Regardless, the number of forms is not the issue. I changed everything I needed programmatically as you will see below. All of the scripts in the post can be downloaded here.

First I needed to get a connection from one of our electronic medical records applications to the new Linux forms print server. I wrote a short script that shows me the arguments passed to the printer as well as the standard input. This allowed me to troubleshoot what was being passed from one server to another and also gave me insight into how the whole process worked.

#!/usr/bin/perl -w
use strict;

# 2010-02-17  Jud Bishop
# Quick hack to see what I am being sent from the port.
# Released under the GNU GPLv2

my $file = "/tmp/print-test.txt";

sleep 5;
unlink $file;

open (NEWFILE, ">$file" ) or die("Error: can't open $file\n$!");

        print NEWFILE "Args:\n";
        for (my $i = 0; $i <= $#ARGV; $i++) {
                print NEWFILE "argv[$i] == $ARGV[$i]\n";
        }

        print NEWFILE "Data:\n";
        while (<STDIN>) {
                print NEWFILE $_;
        }

close NEWFILE or die("Error: can't close $file\n$!");

Once I got the two servers communicating properly it was time to troubleshoot some configuration files that were copied from the AIX server to Linux. I ended up writing the next short script to change some application configuration files that were originally made to work on AIX. This changes the print command from the AIX qprt command to Linux lpd.

# 2010-02-17  Jud Bishop
# This script changes all of the printer configuration channels
# from AIX specific qprt to Linux specific lp commands.
# Released uner the GNU GPLv2

for I in `ls -1`
do
    sed 's/lp -d/lp -d/g' $I > tmp
    if [ $? -eq 0 ]
    then
        mv -f tmp $I
    else
        echo $I "did not complete"
        exit
    fi
done

Now it was time to write a short script to convert the /etc/qconfig printer configuration file from AIX and add the printers on the Linux server. So I wanted to test adding and deleting a printer from cups on the command line. I thought the following command would work.

# lpadmin -p misp1 -E -v socket://misp1.circus.org -m laserjet
# lpadmin: Unable to copy PPD file!

If all else fails read the man page, or in this case the manual on the web. It’s pretty cryptic but they tell you that the -m model has to be from the model directory. Where is the model directory?

# find / -name model
...
/usr/share/cups/model
# ls -1 /usr/share/cups/model/
deskjet2.ppd.gz
epson9.ppd.gz  
laserjet.ppd.gz

So we want the model from /usr/share/cups/model. The following command works:

# lpadmin -p misp1 -E -v socket://misp1.circus.org -m laserjet.ppd.gz

Now it’s time to convert from qconfig to cups. I used the following script to read in qconfig and create the printers. I had to add the sleep because the script got ahead of the lpadmin command. Once I added the sleep it worked and ta-da, 500 printers were created. I realize that printers are added and deleted from the AIX server daily so I had to make sure it would add and delete printers so that we will get them all on go-live day. It’s a rudimentary implementation but it works.

Notes on this script, it was easier than I expected, hence I a have hash instead of just working from the original array. It’s one of those things that I’m not going to go back and clean up for a simple script, sorry.

#!/usr/bin/perl

# 2010-02-18  Jud Bishop
# This script reads in the qconfig file of an AIX server and
# converts it to a Linux based cups file.  
# I am expecting this to be a quick hack...
# Released under the GNU GPLv2

# /etc/qconfig is the AIX printer configuration file.
#labp4:
#        device = @hpjd007
#        up = TRUE
#        host = hpjd007.circus.org
#        s_statfilter = /usr/lib/lpd/bsdshort
#        l_statfilter = /usr/lib/lpd/bsdlong
#        rq = PORT1
#@hpjd007:
#        backend = /usr/lib/lpd/rembak -T 30
open (FILE,"<etc-qconfig") or die "Error: can't open file $! \n";
        @aix = <FILE>;
close FILE or die "Error: can't close file $! \n";

my %table;
my $j = 0;
my ($que, $host, $port, $trash);
for (my $i = 0; $i <= $#aix; $i++) {
        if ( $j == 0 ){
                ($que, $trash) = split (/:/, $aix[$i]);
        } elsif ( $j == 3) {
                chomp($aix[$i]);
                $aix[$i] =~ s/ //g;
                ($trash, $host) = split (/=/, $aix[$i]);
                if ( $host !~ /circus.org/ )
                {
                        $host = sprintf ("%s.circus.org", $host);
                }
        } elsif ( $j == 6) {
                chomp($aix[$i]);
                ($trash, $port) = split (/=/, $aix[$i]);
        }  
        if ( $j == 6 ) {
                $table{$que} = {'host'=>$host, 'port'=>$port};
        }
        $j++;
        if ( $j == 9 ) {
                $j = 0;
        }
}

# The command to add a printer.
# system  lpadmin -p printer -E -v socket://host.circus.org -m laserjet.ppd.gz
# The command to remove a printer, got to be able to back it out.
# lpadmin -x printer
foreach $key (keys(%table)) {
        print "$key $table{$key}->{host} $table{$key}->{port}\n";

        my $socket = sprintf ("socket://%s", $table{$key}->{host});

        # Swap these two commands if you need to delete.        
        #my @args = ("lpadmin", "-x", "$key");
        my @args = ("lpadmin", "-p", "$key", "-E", "-v", "$socket", "-m", "laserjet.ppd.gz" );

        system(@args) == 0
        or die "system @args failed: $?";
        sleep 1;
}
Categories: Code, Linux Tags:

Symantec Linux Backup Client

February 19th, 2010 jud No comments

It’s frustrating when a vendor provides an install script that does not work. Especially when they take the time to print a bunch of fancy output across the screen about how it is working. For those out there this is how you install the Symantec backup client on Linux. This is using CentOS 5.4.

Even though the script says it completed successfully, the install failed because it needed some older libraries.

# yum provides libstdc++-libc6.2-2.so.3
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * addons: centos-distro.cavecreek.net
 * base: www.gtlib.gatech.edu
 * extras: mirrors.igsobe.com
 * updates: mirrors.adams.net
compat-libstdc++-296-2.96-138.i386 : Compatibility 2.96-RH standard C++ libraries
Repo        : base
Matched from:
Other       : libstdc++-libc6.2-2.so.3

compat-libstdc++-296-2.96-138.i386 : Compatibility 2.96-RH standard C++ libraries
Repo        : installed
Matched from:
Other       : Provides-match: libstdc++-libc6.2-2.so.3

# yum install compat-libstdc++-296-2.96-138.i386
# rpm -ivh VRTSralus-10.00.5629-0.i386.rpm
# rpm -ivh VRTSvxmsa-4.2.1-211.i386.rpm

# chkconfig VRTSralus.init on
service VRTSralus.init does not support chkconfig

Looks like we need to make it chkconfig compatible because it is a whole lot easier than creating a bunch of symlinks by hand. Add this to the top of /etc/init.d/VRTSralus.init

# vim /etc/init.d/VRTSralus.init
#
# VRTSralus.init      Start Symantec Backup
#
# chkconfig: 2345 08 95
# description:  Starts and stop backup.
#

And make sure it works.

# chkconfig VRTSralus.init on
# /etc/init.d/VRTSralus.init stop
# /etc/init.d/VRTSralus.init start
Categories: Linux Tags:

TSHOOT Exam Topology

February 19th, 2010 jud 4 comments

One of the Cisco guys has posted the topology for the new TSHOOT exam over at the Cisco Learning Network. Looks like I’ll be recabling the lab this weekend.

Categories: CCNP TSHOOT, Routing Tags:

Parsing Barracuda Log Files

February 17th, 2010 jud No comments

We have a Barracuda web filter here at the Circus. In general we are pleased with its performance, however, as our internet usage has climbed the days of history has declined. The inverse relationship is due to a “ring buffer” of 250,000 entries in the history log. There are times when we have a few hours of history and that doesn’t sit well when a manager wants to see the browsing history for a user or a pc. So we turned on syslog logging to a remote logging server and evaluated the log parsing packages that mentioned they parsed Barracuda logs. Let me give you a hint, there are not many.

My first hack was just to see what was going on and give a rudimentary understanding, it can be downloaded here.

!#~/bin/bash
grep $1 /var/log/barracuda.log | grep http_scan | awk '{print $28}' | sort | uniq -c | sort -n

Which gives output like this:

     63 autotrader.com
     72 google.com
     78 edmunds.com
     81 ad.doubleclick.net
     82 charter.com
    121 dealer.com
    138 alagasco.com
    194 synacor.com
    334 charter.net

The basis of that script gave me more understanding to write the following script which can be downloaded here.

#!/usr/bin/perl -w

use Getopt::Long;
use Number::Format;
use Tie::IxHash;


###############################################
# 2010-02-05 Jud Bishop
# This script parses /var/log/barracuda.log.
# Released under the GNU GPLv2.
###############################################
# Options that can be passed in:
# -u username
# -s source ip address
# -d destination url
# -c category
# -days number of days
# -pc the pcname
###############################################
# The format of a log file message.
# Used this document:
# http://www.barracudanetworks.ca/download/barracuda-web-filter-syslog.pdf
# And this one liner:
# tail -50 /var/log/barracuda.log | awk '{print $28}' && tail -1 /var/log/barracuda.log
#
# 0.  month
# 1.  day
# 2.  time
# 3.  barracuda_ip
# 4.  http_scan[process_id]
# 5.  md5sum
# 6.  number1
# 7.  source_ip
# 8.  destination_ip
# 9. content_type
# 10. source_ip
# 11. destination_address/URL
# 12. data_size
# 13. BYF
# 14. action (ALLOWED, BLOCKED, DETECTED)
# 15. reason (CLEAN, VIRUS, SPYWARE)
# 16. format_ver == 2 (Version of the policy engine output.)
# 17. match_flag (Whether an existing policy matched the traffic: 1=Yes, 0=No.)
# 18. tq_flag (Time qualified flag; 1=Yes 0=No.)
# 19. action_type (The documentation for this flag is incorrect.)
# 20. source_type (0=Any source, 1=group, 2=ipv4addr, 3=login, 4=auth_user, 5=min_score)
# 21. src_detail Detail related to matched source or "(-)" if not.
# 22. dest_detail If there is a destination match, what type (0=any, 1=particular category, 2=any category
#                 3=domain, 4=mimetype, 5=spyware, 6=uri_path_regex, 7=uri_regex, 8=application)
# 23. dest_detail Matched category or "(-)" if not matched.
# 24. spyware (If it is spyware, 0=allow, not spyware, 1=block, 2=infection.)
# 25. spyware_id (Name of the spyware if matched, if not "-".)
# 26. infection_weight Weight of the infection, mostly 0.
# 27. matched_part Part of the rule theat matched.
# 28. matched_category Comma delimited category name that matched traffic.
# 29. user_name Username, ([ANON], [ldap0:jud], [username:jud])

###############################################
# Variables you can change.
###############################################
my $debug = 0;
my $log_file = "/var/log/barracuda.log";
#my $log_file = "/var/log/barracuda.test";

###############################################
# Variables you should NOT change.
###############################################
my $arg_username = 0;
my $arg_pc = 0;
my $arg_source_ip = 0;
my $arg_dest_url = 0;
my $arg_category = 0;
my $arg_days = 0;
my $arg_help = 0;
my %table; # holds all the data for each session.
tie %table, "Tie::IxHash";
my %categories; # holds all of the categories this person went to.
my $data_sum = 0; # holds the total of all data for a user.
my $session_sum = 0; # hold the number of sessions for a user.
# The names of these variable are so that I don't have to keep looking
# above to figure out what name is what item in the array.
my $user = 29;
my $md5sum = 5;
my $month = 0;
my $day = 1;
my $time = 2;
my $source_ip = 7;
my $destination_ip = 8;
my $url = 11;
my $data_size = 12;
my $action = 14;
my $part = 27;
my $category = 28;

# Reads the options passed in.
sub get_options {
        GetOptions(
                'help|?|h!' => \$arg_help,
                'u=s' => \$arg_username,
                's=s' => \$arg_source_ip,
                'd=s' => \$arg_dest_url,
                'c=s' => \$arg_category,
                'days=i' => \$arg_days,
                'pc=s' => \$arg_pc);

        if ($debug)
        {
                print "username == $arg_username\n";
                print "source_ip == $arg_source_ip\n";
                print "pc == $arg_pc\n";
                print "dest_url == $arg_dest_url\n";
                print "category == $arg_category\n";
                print "days == $arg_days\n";
                print "help == $arg_help\n";
        }

        if ($arg_help)
        {
                print "usage: user-report.pl -days number [-u usernname] [-s source-ip] [-d destination-url] ";
                print "[-c category] [-pc pc_name] [--help|-?]\n";
                exit;
        }
}

# Parses the logs.
# Days equals log file days, makes it easy.
sub parse_logs {

        my ($search_field, $search_equals, $days) = @_;

        if ($debug){
                print "--------------------\n";
                print "parse_logs\n";
                print "search_field == $search_field\n";
                print "search_equals == $search_equals\n";
                print "days == $days\n";
        }

        # Loop through the log files based on number of days:
        # 0 == today
        # 1 == barracuda.log.1 one day past...
        # This is not formatted correctly because I added it as a retrofit.
        for (my $i = 0; $i <= $days; $i++)
        {
        if ($i == 0)
        {
                if ($debug) {print "open $log_file\n"};
                open (FILE, $log_file) or die "Error: can't open file\n $! \n";
        } else {
                if ($debug) {print "open $log_file.$days\n"};
                my $file = sprintf ("%s.%s", $log_file, $days);
                open (FILE, $file) or die "Error: can't open file\n $! \n";
        }
        while (<FILE>)
        {
                chomp;
                # Makes split work like awk, don't believe the man page.
                my (@log) = split /\s+/;
                # The next check is to catch the following type messages.
                # Feb  9 06:51:09 last message repeated 8 times
                if (defined ($log[$search_field]) and ($log[$search_field] eq $search_equals))
                {
                        if ($debug){
                                print "$log[$search_field] $search_equals\n";
                                for (my $i = 0; $i <= $#log; $i++)
                                {
                                        print "log[$i] == $log[$i]\n ";
                                }
                        }
                        # Each session gets a different md5sum, which is why it is the key in the table.
                        if( not exists $table{$log[$md5sum]}){
                                $table{$log[$md5sum]} = {'user'=>$log[$user], 'month'=>$log[$month], 'day'=>$log[$day], 'time'=>$log[$time], 'source_ip'=>$log[$source_ip], 'destination_ip'=>$log[$destination_ip], 'url'=>$log[$url], 'data_size'=>$log[$data_size], 'action'=>$log[$action], 'category'=>$log[$category], 'total_data'=>$log[$data_size], 'session_count'=>1 };

                                $data_sum += $log[$data_size];
                                $session_sum += 1;
                                if ( not exists $categories{$log[$category]} )
                                {
                                        $categories{$log[$category]} = 1;
                                } else {
                                        $categories{$log[$category]} += 1;
                                }

                                if($debug){
                                        print "does not exist\n";
                                        print "log user = $log[$user] \n";
                                        print "table user = $table{$log[$md5sum]}->{user} \n";
                                        print "sessions = $session_sum\n";
                                        print "bandwidth = $data_sum\n";
                                }
                        } else {
                                $table{$log[$md5sum]}->{total_data} += $log[$data_size];
                                $data_sum += $log[$data_size];
                                if($debug){
                                        print "exists \n";
                                        print "bandwidth = $data_sum\n";
                                }
                           }
                    }      
        }
        close FILE or die "Error: can't close file\n $! \n";
        }
}

sub print_report {

        if ($debug) {print "print_report\n";}
        if ($arg_username)
        {
                print "Useage report for: $arg_username\n";
        } elsif ($arg_pc) {
                print "Useage report for: $arg_pc\n";
        } elsif ($arg_source_ip) {
                print "Useage report for: $arg_source_ip\n";
        }
        print "Number of sessions: $session_sum\n";

        my $x = new Number::Format;
        $formatted = $x->format_bytes($data_sum);
        print "Total bandwidth consumed: $formatted\n\n";

        foreach my $category (sort (keys(%categories)))
        {
                printf "%s\n", uc($category);

                foreach $key (keys(%table))
                {
                        if ( $table{$key}->{category} eq $category)
                        {
                                my $url = substr($table{$key}->{url}, 0, 35);
                                print "$table{$key}->{month} $table{$key}->{day} $table{$key}->{time} $url $table{$key}->{action}\n";
                        }
                }
                print "\n";
        }
}

###############################################
# main
###############################################
my $search_field;
my $search_equals;

get_options();

if ($arg_username or $arg_pc) {
        $search_field = $user;
        if ($arg_username eq "ANON"){
                $search_equals = $arg_username;
        } else {
                $search_equals = sprintf("[ldap0:%s]", $arg_username);
        }
} elsif ($arg_source_ip) {
        $search_field = $source_ip;
        $search_equals = $arg_source_ip;
} elsif ($arg_dest_url) {
        $search_field = $url;
        $search_equals = $arg_dest_url;
} elsif ($arg_category) {
        $search_field = $category;
        $search_equals = $arg_category;
}
if ($debug) {print "search_field == $search_field\n"}

parse_logs($search_field, $search_equals, $arg_days);

print_report();

This script produces output like the following:

Usage report for: circus-user
Number of sessions: 401
Total bandwidth consumed: 19.39M

ADVERTISEMENT-POP-UPS,GAME-MEDIA,CUSTOM-2
Feb 17 15:44:17 http://games.mochiads.com/c/p/the-r ALLOWED

AUCTIONS
Feb 17 17:08:21 http://rover.ebay.com/ar/1/56033/1? ALLOWED

AUCTIONS,MOTOR-VEHICLES,CUSTOM-1
Feb 17 17:13:44 http://edmunds.autotrader.com/js/jq ALLOWED
Feb 17 17:13:45 http://edmunds.autotrader.com/inc/g ALLOWED
Feb 17 17:13:46 http://edmunds.autotrader.com/inc/j ALLOWED
Feb 17 17:13:46 http://edmunds.autotrader.com/inc/j ALLOWED
Feb 17 17:13:47 http://edmunds.autotrader.com/dwr/i ALLOWED
Feb 17 17:13:50 http://edmunds.autotrader.com/no_ca ALLOWED

BUSINESS
Feb 17 15:09:08 http://www.statcounter.com/counter/ ALLOWED
Feb 17 15:10:15 http://www.alagasco.com/fw/_css/fle ALLOWED
Feb 17 15:10:16 http://www.alagasco.com/scripts/jFa ALLOWED
Feb 17 15:10:17 http://www.alagasco.com/fw/_js/flex ALLOWED
Categories: Code, Linux Tags:

Cold Ride

December 29th, 2009 jud 3 comments

I have a friend in Chicago and another in Boulder who talk about how nice it must be to ride all winter in the south. It kills me. Check the low for each night, because that is the temperature I ride in every day. We ride at 6:00am every Tuesday-Thursday unless it’s raining too hard, it’s too cold or a combination of the two. But what is too cold?

Like a friend said, once you ride in 16 degree weather there aren’t
many of excuses left. I’m not sure many of you know what 16 degrees feels like on a bike ride, a brain freeze when you go down hill. We usually ride mountain bikes when it’s that cold, because the wind chill is too great to ride the road.

This morning was just another training ride, except it had warmed up to 26F.
It was so cold I could feel the wind on my back this morning and figured I had just not tucked in my base layer well enough. It was only when I got home and the back of my jacket was frozen solid that I understood, my bottle had been leaking down my back and the water had frozen my jersey. When it’s in the 20s your bottle freezes shut, but if you put it in your back pocket it usually won’t. Not only had my bottle frozen shut and started to freeze solid, but the water that had leaked out had frozen too.

So to all my friends who still live up north, it’s not all roses in the south either.

Categories: Cycling, Musings Tags:

Riding Rickshaw

December 22nd, 2009 jud No comments

I live in Auburn Alabama and as you might have heard, it is a college town with a major college football team.  In the fall, socializing revolves around football.  I believe part of the reason is because there are no professional level sports in the state of Alabama.  Sure we have class 5A baseball or whatever they call the minor leagues, but in reality college football is king in the state of Alabama.  On game days this year I have been riding for TigerShaw.

In my line of work I rarely interact with the general public and don’t interact with end users very often either.  It’s nice at times because the world I work in is binary, black and white, on or off, there is no gray area.  Something works or it doesn’t.  The goal of my work is to abstract out the human element and when you don’t have that human interaction you feel rusty in social settings.

I sold suits when I was in high school and college undergrad.  When I say sold suits, I don’t mean $300 suits at Sears either, I sold suits at Bachrach back before they had a house brand. We sold Armani, Boss, Joseph Aboud and some high end Hart-Marx.  My biggest sales were in the $10,000 range and this was in the 1990’s.  I made good money for a kid.  What I learned during that job was how to work the public, how to be professional in your interactions, and how to sell.  Think about it, if an executive is going to buy a $2,000 to $3,000 suit from a kid, the kid had better be good.

The same holds true riding rickshaw.  I make good money riding rickshaw but I am professional in my dealings with clients and I work the public.  You have to be professional, work hard and sell yourself the same way to make $20 for dragging someone a mile and a half or $120 for selling a $2,000 suit.

Tailwinds.

Categories: Cycling, Musings Tags:

WLAN Management

December 15th, 2009 jud No comments

The five elements of a Cisco wireless network read like an add right of some glossy, it’s here because there might be a question on the test:
Client devices — laptops, phones and
Mobility platform — Composed of lightweight access points (LWAP), wireless lan controller (WLC) and the wireless control system (WCS)
Network unification — WLCs allow for integration into the wired network
Network management — WCS for control and monitoring
Unified advanced services — Catch-all for marketing

There are two types of Cisco wireless implementations, Autonomous and Lightweight. As a networker who has worked on both, LWAP is the way to go. Management is much easier, mobility groups are your friend and because LWAPP is tunnels wireless interactions across the network to the WCS/WLC it makes it much easier to deploy in a split layer environment, L2 and L3 around campus depending upon location and need.

Autonomous APs — Each AP has its own configuration and operates independently. Configuration can be controlled through CiscoWorks WLSE.

LWAPs — Configuration, monitoring and security is centrally controlled through the WLC/WCS. This is the more scalable solution.

Wireless Lan Solution Engine (WLSE)

    Provides basic management of autonomous APs:

  • Configuration
  • Fault and policy monitoring
  • Reporting
  • Firmware upgrades
  • Radio management

Wireless Control System (WCS)

    Provides advanced management of LWAPs:

  • Location services
  • “Self healing” — If you have a WCS you know what this means.
  • Failover between WLCs
  • Monitoring
  • Configuration
  • Firmware upgrades
Categories: CCNP ONT Tags: