#! /usr/bin/perl # # Purpose: # FlowViewer_Main.cgi permits a Web user to analyze Net Flow data stored # in flow tools format and create an HTML report. # # Description: # The script responds to an HTML form from the user in order to collect # parameters that will control the analysis (e.g., router, time-period, ip # addresses etc.) Upon receipt of the form input the script creates a flow tools # filter file which controls the selection of the data via the invocation of # additional flow tools utilities. An HTML report is then generated. # # Input arguments (received from the form): # Name Description # ----------------------------------------------------------------------- # device_name An identifying name of the device (e.g. router1) # flow_select Identifies which flows to include wrt time period # start_date Start date of analysis period # start_time Start time of analysis period # end_date End date of analysis period # end_time End time of analysis period # source_addresses Constrain flows examined to these source IP addresses # source_ports Constrain flows examined to these source ports # source_ifs Constrain flows examined to these input interfaces # source_ases Constrain flows examined to these source ASes # dest_addresses Constrain flows examined to these dest. IP addresses # dest_ports Constrain flows examined to these dest. ports # dest_ifs Constrain flows examined to these output interfaces # dest_ases Constrain flows examined to these dest. ASes # tos_fields Constrain flows examined by specified TOS field values # tcp_flags Constrain flows examined by specified TCP flag values # protocols Constrain flows examined to these protocols # print_report Select from these various report options # stat_report Select from these various statistics options # cutoff_lines Number of report lines to print out # cutoff_octets Minimum number of octets for inclusion in report # sort_field Which report column to sort lines upon # resolve_addresses Whether or not to resolve IP addresses # # Notes: # 1. It is a good idea to retain the host_names GDBM file (names), even if it # gets large, since it is up to 1000 times faster than using 'dig'. # # Modification history: # Author Date Vers. Description # ----------------------------------------------------------------------- # J. Loiacono 07/04/2005 1.0 Original version. # J. Loiacono 01/01/2006 2.0 FlowGrapher, new functions, speed # J. Loiacono 01/16/2006 2.1 Fixed compute of concatenation date # J. Loiacono 01/26/2006 2.2 New flow_select option for inclusion # J. Loiacono 07/04/2006 3.0 Renamed for re-organization # Single script for GDBM/NDBM (thanks Ed Ravin) # J. Loiacono 12/25/2006 3.1 [No Change to this module] # J. Loiacono 02/14/2007 3.2 [No Change to this module] # #$Author$ #$Date$ #$Header$ # ########################################################################### # # BEGIN EXECUTABLE STATEMENTS # use FlowViewer_Configuration; use FlowViewer_Utilities; if ($debug_viewer eq "Y") { open (DEBUG,">$work_directory/DEBUG_VIEWER"); } if ($debug_viewer eq "Y") { print DEBUG "In FlowViewer_Main.cgi\n"; } # Tie in the appropriate 'names' files which saves IP address resolved names if (eval 'local $SIG{"__DIE__"}= sub { }; use GDBM_File; tie %host_names, "GDBM_File", "$names_directory/names", GDBM_WRCREAT, 0666;' ) { print DEBUG "Using GDBM\n"; }; if (eval 'local $SIG{"__DIE__"}= sub { }; use NDBM_File; use Fcntl; tie %host_names, "GDBM_File", "$names_directory/names", GDBM_WRCREAT, 0666;' ) { print DEBUG "Using NDBM\n"; }; # Tie in the appropriate 'as_names' file which saves resolved AS names if (eval 'local $SIG{"__DIE__"}= sub { }; use GDBM_File; tie %as_names, "GDBM_File", "$names_directory/as_names", GDBM_WRCREAT, 0666;' ) { print DEBUG "Using GDBM\n"; }; if (eval 'local $SIG{"__DIE__"}= sub { }; use NDBM_File; use Fcntl; tie %as_names, "GDBM_File", "$names_directory/as_names", GDBM_WRCREAT, 0666;' ) { print DEBUG "Using NDBM\n"; }; # Retrieve the form inputs read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; $FORM{$name} = $value; } # Clean up input $FORM{source_address} =~ s/\s+//g; $FORM{source_address} =~ s/,/, /g; $FORM{source_port} =~ s/\s+//g; $FORM{source_port} =~ s/,/, /g; $FORM{source_if} =~ s/\s+//g; $FORM{source_if} =~ s/,/, /g; $FORM{source_as} =~ s/\s+//g; $FORM{source_as} =~ s/,/, /g; $FORM{dest_address} =~ s/\s+//g; $FORM{dest_address} =~ s/,/, /g; $FORM{dest_port} =~ s/\s+//g; $FORM{dest_port} =~ s/,/, /g; $FORM{dest_if} =~ s/\s+//g; $FORM{dest_if} =~ s/,/, /g; $FORM{dest_as} =~ s/\s+//g; $FORM{dest_as} =~ s/,/, /g; $FORM{protocols} =~ s/\s+//g; $FORM{protocols} =~ s/,/, /g; $FORM{tos_fields} =~ s/\s+//g; $FORM{tos_fields} =~ s/,/, /g; $FORM{tcp_flags} =~ s/\s+//g; $FORM{tcp_flags} =~ s/,/, /g; # Parameters for generating a FlowViewer report $device_name = $FORM{'device_name'}; $flow_select = $FORM{'flow_select'}; $start_date = $FORM{'start_date'}; $start_time = $FORM{'start_time'}; $end_date = $FORM{'end_date'}; $end_time = $FORM{'end_time'}; $source_addresses = $FORM{'source_address'}; $source_ports = $FORM{'source_port'}; $source_ifs = $FORM{'source_if'}; $source_ases = $FORM{'source_as'}; $dest_addresses = $FORM{'dest_address'}; $dest_ports = $FORM{'dest_port'}; $dest_ifs = $FORM{'dest_if'}; $dest_ases = $FORM{'dest_as'}; $protocols = $FORM{'protocols'}; $tcp_flags = $FORM{'tcp_flags'}; $tos_fields = $FORM{'tos_fields'}; $print_report = $FORM{'print_report'}; $stat_report = $FORM{'stat_report'}; $cutoff_lines = $FORM{'cutoff_lines'}; $cutoff_octets = $FORM{'cutoff_octets'}; $sort_field = $FORM{'sort_field'}; $resolve_addresses = $FORM{'resolve_addresses'}; # Build call string to provide filter parameters to either FlowGrapher or FlowTracker $call_string = "device_name=$device_name&"; $call_string .= "start_date=$start_date&"; $call_string .= "start_time=$start_time&"; $call_string .= "end_date=$end_date&"; $call_string .= "end_time=$end_time&"; $call_string .= "source_addresses=$source_addresses&"; $call_string .= "source_ports=$source_ports&"; $call_string .= "source_ifs=$source_ifs&"; $call_string .= "source_ases=$source_ases&"; $call_string .= "dest_addresses=$dest_addresses&"; $call_string .= "dest_ports=$dest_ports&"; $call_string .= "dest_ifs=$dest_ifs&"; $call_string .= "dest_ases=$dest_ases&"; $call_string .= "protocols=$protocols&"; $call_string .= "tcp_flags=$tcp_flags&"; $call_string .= "tos_fields=$tos_fields&"; $call_string =~ s/\s+//g; $save_file = "$device_name"; # Start web page output in case of errors print "Content-type:text/html\n\n"; print "
"; print "Use the \"back\" key to save inputs
";
exit;
}
if (($stat_report ne "0") && ($print_report ne "0")) {
print "
Two reports selected. Reset one of them.
Use the \"back\" key to save inputs
";
exit;
}
# Retrieve current time to use as a file suffix to permit more than one user to generate reports
($sec,$min,$hr,$date,$mnth,$yr,$day,$yr_date,$DST) = localtime(time);
$mnth++;
$yr += 1900;
if ((0 < $mnth) && ($mnth < 10)) { $mnth = "0" . $mnth; }
if ((0 < $date) && ($date < 10)) { $date = "0" . $date; }
if ((0 <= $hr) && ($hr < 10)) { $hr = "0" . $hr; }
if ((0 <= $min) && ($min < 10)) { $min = "0" . $min; }
if ((0 <= $sec) && ($sec < 10)) { $sec = "0" . $sec; }
$prefix = $yr . $mnth . $date ."_". $hr . $min . $sec;
$suffix = $hr . $min . $sec;
# Determine the flow files concatenation start and end time
$start_epoch = date_to_epoch($start_date,$start_time,"LOCAL");
$end_epoch = date_to_epoch($end_date,$end_time,"LOCAL");
$report_length = $end_epoch - $start_epoch;
if ($report_length <= 0) { &print_error("End time ($end_date $end_time) earlier than Start time ($start_date $start_time)"); }
$start_flows = &flow_date_time($start_epoch,"LOCAL");
$end_flows = &flow_date_time($end_epoch,"LOCAL");
$cat_start_epoch = $start_epoch - $flow_file_length - 1;
$cat_end_epoch = $end_epoch + $flow_capture_interval + 1;
$cat_start = epoch_to_date($cat_start_epoch,"LOCAL");
$cat_end = epoch_to_date($cat_end_epoch,"LOCAL");
$concatenate_parameters = "-t \"$cat_start\" -T \"$cat_end\" ";
if (($start_ymd ne $end_ymd) && ($end_epoch > $start_epoch)) {
for ($i=0;$i<$maximum_days;$i++) {
if (($cat_start_epoch + $i*86400) > $cat_end_epoch + 86400) { last; }
($sec,$min,$hr,$cat_date,$cat_mnth,$cat_yr,$day,$yr_date,$DST) = localtime($cat_start_epoch + $i*86400);
$cat_mnth++;
$cat_yr += 1900;
if ((0 < $cat_mnth) && ($cat_mnth < 10)) { $cat_mnth = "0" . $cat_mnth; }
if ((0 < $cat_date) && ($cat_date < 10)) { $cat_date = "0" . $cat_date; }
$cat_directory = "$flow_data_directory/$device_name";
if ($N == -3) { $cat_directory .= "/$cat_yr/$cat_yr\-$cat_mnth/$cat_yr\-$cat_mnth\-$cat_date"; }
if ($N == -2) { $cat_directory .= "/$cat_yr\-$cat_mnth/$cat_yr\-$cat_mnth\-$cat_date"; }
if ($N == -1) { $cat_directory .= "/$cat_yr\-$cat_mnth\-$cat_date"; }
if ($N == 1) { $cat_directory .= "/$cat_yr"; }
if ($N == 2) { $cat_directory .= "/$cat_yr/$cat_yr\-$cat_mnth"; }
if ($N == 3) { $cat_directory .= "/$cat_yr/$cat_yr\-$cat_mnth/$cat_yr\-$cat_mnth\-$cat_date"; }
$concatenate_parameters .= "$cat_directory ";
if ($N == 0) { last; }
}
}
elsif ($start_ymd eq $end_ymd) {
($sec,$min,$hr,$cat_date,$cat_mnth,$cat_yr,$day,$yr_date,$DST) = localtime($cat_end_epoch);
$cat_mnth++;
$cat_yr += 1900;
if ((0 < $cat_mnth) && ($cat_mnth < 10)) { $cat_mnth = "0" . $cat_mnth; }
if ((0 < $cat_date) && ($cat_date < 10)) { $cat_date = "0" . $cat_date; }
$cat_directory = "$flow_data_directory/$device_name";
if ($N == -3) { $cat_directory .= "/$cat_yr/$cat_yr\-$cat_mnth/$cat_yr\-$cat_mnth\-$cat_date"; }
if ($N == -2) { $cat_directory .= "/$cat_yr\-$cat_mnth/$cat_yr\-$cat_mnth\-$cat_date"; }
if ($N == -1) { $cat_directory .= "/$cat_yr\-$cat_mnth\-$cat_date"; }
if ($N == 1) { $cat_directory .= "/$cat_yr"; }
if ($N == 2) { $cat_directory .= "/$cat_yr/$cat_yr\-$cat_mnth"; }
if ($N == 3) { $cat_directory .= "/$cat_yr/$cat_yr\-$cat_mnth/$cat_yr\-$cat_mnth\-$cat_date"; }
$concatenate_parameters .= "$cat_directory ";
}
else {
&print_error("Start day ($start_date) is past End day ($end_date)");
}
# Create the filter to match the input specifications
$filter_file = "$work_directory/FlowViewer_filter_$suffix";
create_filter_file(%FORM,$filter_file);
# Set up the command to concatenate the files
$flowcat_command = "$flow_bin_directory/flow-cat" . " $concatenate_parameters";
# Set up the command to filter the concatenated file
$flownfilter_command = "$flow_bin_directory/flow-nfilter -f $work_directory/FlowViewer_filter_$suffix -FFlow_Filter";
# Set up the flow-stat command if requested
if ($stat_report ne "0") {
$sort_field--;
if ($stat_report eq "99") {
$flowstat_command = "$flow_bin_directory/flow-stat -S$sort_field >$work_directory/FlowViewer_output_$suffix"; }
else {
$flowstat_command = "$flow_bin_directory/flow-stat -f$stat_report -S$sort_field >$work_directory/FlowViewer_output_$suffix";
}
$flow_run = "$flowcat_command | $flownfilter_command | $flowstat_command";
}
# Set up the flow-print command if requested
if ($print_report ne "0") {
$flowprint_command = "$flow_bin_directory/flow-print -f$print_report >$work_directory/FlowViewer_output_$suffix";
$flow_run = "$flowcat_command | $flownfilter_command | $flowprint_command";
}
# Execute the piped flow-tools command
if ($debug_viewer eq "Y") { print DEBUG "\n$flow_run\n\n"; }
system ($flow_run);
$sort_field++;
if ($print_report ne "0") { $sort_field = "n/a"; }
if ($stat_report == 99) { $report_title = "Summary"; }
if ($stat_report == 5) { $report_title = "UDP/TCP Destination Port"; }
if ($stat_report == 6) { $report_title = "UDP/TCP Source Port"; }
if ($stat_report == 7) { $report_title = "UDP/TCP Port"; }
if ($stat_report == 8) { $report_title = "Destination IP"; }
if ($stat_report == 9) { $report_title = "Source IP"; }
if ($stat_report == 10) { $report_title = "Source/Destination IP"; }
if ($stat_report == 11) { $report_title = "Source or Destination IP"; }
if ($stat_report == 12) { $report_title = "IP Protocol"; }
if ($stat_report == 17) { $report_title = "Input Interface"; }
if ($stat_report == 18) { $report_title = "Output Interface"; }
if ($stat_report == 23) { $report_title = "Input/Output Interface"; }
if ($stat_report == 19) { $report_title = "Source AS"; }
if ($stat_report == 20) { $report_title = "Destination AS"; }
if ($stat_report == 21) { $report_title = "Source/Destination AS"; }
if ($stat_report == 22) { $report_title = "IP ToS"; }
if ($stat_report == 24) { $report_title = "Source Prefix"; }
if ($stat_report == 25) { $report_title = "Destination Prefix"; }
if ($stat_report == 26) { $report_title = "Source/Destination Prefix"; }
$resolve_columns = 0;
if (($resolve_addresses eq "Y") && ($stat_report == 8)) { $resolve_columns = 1; }
if (($resolve_addresses eq "Y") && ($stat_report == 9)) { $resolve_columns = 1; }
if (($resolve_addresses eq "Y") && ($stat_report == 10)) { $resolve_columns = 2; }
if (($resolve_addresses eq "Y") && ($stat_report == 11)) { $resolve_columns = 1; }
if (($resolve_addresses eq "Y") && ($stat_report == 19)) { $resolve_columns = 3; }
if ($print_report == 1) { $report_title = "Flow Times"; }
if ($print_report == 4) { $report_title = "AS Numbers"; }
if ($print_report == 5) { $report_title = "132 Columns"; }
if ($print_report == 9) { $report_title = "1 Line with Tags"; }
if ($print_report == 10) { $report_title = "AS Aggregation"; }
if ($print_report == 11) { $report_title = "Protocol Port Aggregation"; }
if ($print_report == 12) { $report_title = "Source Prefix Aggregation"; }
if ($print_report == 13) { $report_title = "Destination Prefix Aggregation"; }
if ($print_report == 14) { $report_title = "Prefix Aggregation"; }
if ($print_report == 24) { $report_title = "Full (Catalyst)"; }
# Output the FlowViewer report
print "