Subversion Repositories ALCASAR

Rev

Go to most recent revision | Blame | Last modification | View Log

#!/usr/bin/perl
#
#  Copyright (c) 2004, SWITCH - Teleinformatikdienste fuer Lehre und Forschung
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#        this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#        this list of conditions and the following disclaimer in the documentation
#        and/or other materials provided with the distribution.
#   * Neither the name of SWITCH nor the names of its contributors may be
#        used to endorse or promote products derived from this software without
#        specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#  POSSIBILITY OF SUCH DAMAGE.
#
#  $Author: peter $
#
#  $Id: PortTracker.pm 27 2011-12-29 12:53:29Z peter $
#
#  $LastChangedRevision: 27 $

# Demo plugin for NfSen
#
# This plugin demonstrates the use of plugins

package PortTracker;

use strict;
use NfSen;
use NfConf;

#
# The plugin may send any messages to syslog
# Do not initialize syslog, as this is done by 
# the main process nfsen-run
use Sys::Syslog;
  
our $VERSION = 130;

our %cmd_lookup = (
        'get-portgraph' => \&GetPortGraph,
        'get-topN'              => \&GetTopN,
);

my ( $nftrack, $PROFILEDATADIR );

my $PORTSDBDIR = "/var/log/netflow/porttracker";

my $EODATA  = ".\n";

# colours used in graphs
# if more than 12 graphs are drawn ( does this really make sense ? ) 
# the same colours are used again
my @colour = ( 
        '#ff0000', '#ff8000', '#ffff00', '#80ff00', '#00ff00',
        '#00ff80', '#00ffff', '#0080ff', '#0000ff', '#8000ff',
        '#ff00ff', '#ff0080'
);


sub GetTopN {
    my $socket  = shift;
    my $opts    = shift;

        my $interval;
        if ( !exists $$opts{'interval'} ) {
                $interval = 1;
        } else {
                $interval = $$opts{'interval'};
        }
        print $socket ".Get topN ports\n";

        my $statfile = $interval == 24 ? 'portstat24.txt' : 'portstat.txt';
        print $socket ".topN ports $PORTSDBDIR/$statfile\n";
        if ( !open STAT, "$PORTSDBDIR/$statfile" ) {
                print $socket $EODATA;
                print $socket "ERR Open statfile '$PORTSDBDIR/$statfile': $!\n";
                return;
        }

        print $socket ".topN read ports\n";
        while ( <STAT> ) {
                chomp;
                print $socket "_topN=$_\n";
        }
        print $socket $EODATA;
        print $socket "OK Command completed\n",
                
} # End of GetPortGraph

sub GetPortGraph {
    my $socket  = shift;
    my $opts    = shift;

        # get all arguments:
        # Example:
        # proto typw  logscale light tstart     tend       topN              track_list
        # tcp   flows 0        0     1116495000 1116581400 '22 445 135 1433' '80 143'
        if ( !exists $$opts{'arg'} ) {
                print $socket $EODATA;
                print $socket "ERR Missing Arguments.\n";
        }
        my $ARGS = $$opts{'arg'};
        my $proto               = shift @$ARGS; # 'tcp' or 'udp'
        my $type                = shift @$ARGS; # 'flows', 'packets' or 'bytes'
        my $logscale    = shift @$ARGS; # 0 or 1
        my $stacked             = shift @$ARGS; # 0 or 1
        my $light               = shift @$ARGS; # 0 or 1
        my $tstart              = shift @$ARGS; # start time - UNIX format
        my $tend                = shift @$ARGS; # end time - UNIX format
        my $topN                = shift @$ARGS; # TopN port list: string: ' ' separated port list
        my $track_list  = shift @$ARGS; # Static track port list: string: ' ' separated port list
        my $skip_list   = shift @$ARGS; # Static skip port list: string: ' ' separated port list

        if ( !defined $proto || !defined $type || !defined $logscale || !defined $stacked ||
                 !defined $light || !defined $tstart || !defined $tend || !defined $topN || 
                 !defined $track_list || !defined $skip_list ) {
                print $socket $EODATA;
                print $socket "ERR Argument Error.\n";
                return;
        }
        my @skipPorts = split '-', $skip_list;

        my @topN = split '-', $topN;
        my @track_list = split '-', $track_list;

        # remove the common ports in both lists from the dynamic topN list
        my %_tmp;
        @_tmp{@track_list} = @track_list;
        delete @_tmp{@topN};
        @track_list = sort keys %_tmp;

        # %_tmp = ();
        # @_tmp{@topN} = @topN;
        # delete @_tmp{@skipPorts};
        # @topN = keys %_tmp;

        %_tmp = ();
        my @_tmp;
        @_tmp{@skipPorts} = @skipPorts;
        foreach my $port ( @topN ) {
                push @_tmp, $port unless exists $_tmp{$port};
        }
        @topN = @_tmp;

        my $datestr = scalar localtime($tstart) . " - " . scalar localtime($tend);
        my $title   = uc($proto) . " " . ucfirst($type);

        my @DEFS = ();

        # Compile rrd args
        my @rrdargs = ();
        push @rrdargs, "-";     # output graphics to stdout
        
        foreach my $port ( @topN, @track_list ) {
                # assemble filename
                my $fileident = $port >> 10;
                my $rrdfile     = "$PORTSDBDIR/${proto}-${type}-$fileident.rrd";
                # which ident in this rrd file
                my $ident       =  $port & 1023;        # 0x0000001111111111 mask
                push @rrdargs, "DEF:Port${port}=$rrdfile:p${ident}:AVERAGE";
        }

        push @rrdargs, "--start",  "$tstart";
        push @rrdargs, "--end",    "$tend";
        push @rrdargs, "--title",  "$datestr - $title" unless $light;
        push @rrdargs, "--vertical-label", "$title"  unless $light;
        
        # lin or log graph?
        push @rrdargs, "--logarithmic" if $logscale;

        if ( $light ) {
                push @rrdargs, "-w";
                push @rrdargs, "288";
                push @rrdargs, "-h";
                push @rrdargs, "150";
                push @rrdargs, "--no-legend";   # no legend in small pictures
        } else {
                push @rrdargs, "-w";
                push @rrdargs, "576";
                push @rrdargs, "-h";
                push @rrdargs, "300";
        }


        my $i=0;
        my $area_set = 0;
        my $n = scalar @topN;
        push @rrdargs, "COMMENT:Top $n Ports\\n";
        if ( $stacked && scalar @topN ) {
                my $port = shift @topN;
                push @rrdargs, "AREA:Port${port}$colour[$i]:Port ${port}";
                $i++;
                $area_set = 1;
                foreach my $port ( @topN ) {
                        push @rrdargs, "STACK:Port${port}$colour[$i]:Port ${port}";
                        $i++;
                }

        } else {
                foreach my $port ( @topN ) {
                        push @rrdargs, "LINE1:Port${port}$colour[$i]:Port ${port}";
                        $i++;
                }
        }
        
        if ( scalar @track_list) {
                push @rrdargs, "COMMENT:\\n";
                push @rrdargs, "COMMENT:\\n";
                push @rrdargs, "COMMENT:Tracked Ports\\n";
        }
        if ( $stacked && scalar @track_list) {
                if ( !$area_set ) {
                        my $port = shift @track_list;
                        push @rrdargs, "AREA:Port${port}$colour[$i]:Port ${port}";
                        $i++;
                }
                foreach my $port ( @track_list ) {
                        push @rrdargs, "STACK:Port${port}$colour[$i]:Port ${port}";
                        $i++;
                }
                
        } else {
                foreach my $port ( @track_list ) {
                        push @rrdargs, "LINE2:Port${port}$colour[$i]:Port ${port}";
                        $i++;
                }
        }
        if ( scalar @skipPorts) {
                push @rrdargs, "COMMENT:\\n";
                push @rrdargs, "COMMENT:\\n";
                my $portlist = join ',', @skipPorts;
                push @rrdargs, "COMMENT:Skipped Ports $portlist\\n";
        }
        my ($averages,$xsize,$ysize) = RRDs::graph( @rrdargs );
                
        if (my $ERROR = RRDs::error) {
                print "ERROR: $ERROR\n";
        } 

} # End of GenPortGraph


sub nftrack_execute {
        my $command = shift;

        syslog('debug', $command);

        my $ret = system($command);
        if ( $ret == - 1 ) {
                syslog('err', "Failed to execute nftrack: $!\n");
        } elsif ($ret & 127) {
                syslog('err', "nftrack died with signal %d, %s coredump\n", ($ret & 127),  ($ret & 128) ? 'with' : 'without');
        } else {
                syslog('debug', "nftrack exited with value %d\n", $ret >> 8);
        }

} # End of nftrack_execute

#
# Periodic function
#   input:  hash reference including the items:
#           'profile'       profile name
#           'profilegroup'  profile group
#           'timeslot'      time of slot to process: Format yyyymmddHHMM e.g. 200503031200
sub run {
    my $argref       = shift;

    my $profile      = $$argref{'profile'};
    my $profilegroup = $$argref{'profilegroup'};
    my $timeslot     = $$argref{'timeslot'};

        syslog('debug', "PortTracker run: Profile: $profile, Time: $timeslot");

        my %profileinfo  = NfProfile::ReadProfile($profile);
        my $netflow_sources = "$PROFILEDATADIR/$profile/$profileinfo{'sourcelist'}";

        # 
        # process all sources of this profile at once
        my $command = "$nftrack -L $NfConf::syslog_facility -M $netflow_sources -r nfcapd.$timeslot -d $PORTSDBDIR -A -t $timeslot -s -p -w $PORTSDBDIR/portstat.txt";
        nftrack_execute($command);

        $command = "$nftrack -d $PORTSDBDIR -S -p -w $PORTSDBDIR/portstat24.txt";
        nftrack_execute($command);

        #
        # Process the output and notify the duty team

        syslog('debug', "PortTracker run: Done.");

} # End of run

sub Init {
        syslog("info", "PortTracker: Init");

        # Init some vars
        $nftrack  = "$NfConf::PREFIX/nftrack";
        $PROFILEDATADIR = "$NfConf::PROFILEDATADIR";

        return 1;
}

sub Cleanup {
        syslog("info", "PortTracker Cleanup");
        # not used here
}

1;