#! /usr/bin/perl # # Purpose: # FlowGrapher_Main.cgi permits a Web user to analyze Net Flow data stored in # flow tools format and create a graph and partial listing of flows. # # 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. A graph is then created using the GD graphics # package. # # 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 # detail_lines Limit of detailed flow lines to print out for graphs # sample_time Amount of time for each graphing 'bucket' # graph_multiplier Multiplier of standard width for presented graph # resolve_addresses Whether or not to resolve IP addresses # # Modification history: # Author Date Vers. Description # ----------------------------------------------------------------------- # J. Loiacono 01/01/2006 2.0 Original released version # J. Loiacono 01/16/2006 2.1 Fixed compute of concatenation date, # end-of-year problem # J. Loiacono 01/26/2006 2.2 New flow_select to determine inclusion, # adjusted processing for flow_select # J. Loiacono 04/15/2006 2.3 Minimized use of timelocal for epoch to # speed things up ten-fold # J. Loiacono 05/31/2006 2.3.1 Fixed last bucket problem; caused spikes # (thanks Mark Foster) # J. Loiacono 07/04/2006 3.0 Changed name for reorganization # Resolve address option (thanks Mark Foster) # Single script for GDBM/NDBM (thanks Ed Ravin) # J. Loiacono 12/25/2006 3.1 Changes for MIN/MAX, more than 30 days # J. Loiacono 02/22/2007 3.2 [No Change to this module] # J. Loiacono 04/01/2007 3.2 Fixes to bucket alignment (removed rounding) # (thanks Dario La Guardia) # J. Loiacono 05/01/2007 3.2 Fixed FlowGrapher first, last buckets # #$Author$ #$Date$ #$Header$ # ########################################################################### # # BEGIN EXECUTABLE STATEMENTS # use FlowViewer_Configuration; use FlowViewer_Utilities; use GD; use GD::Graph::linespoints; use GD::Graph::mixed; use GD::Graph::bars; if ($debug_grapher eq "Y") { open (DEBUG,">$work_directory/DEBUG_GRAPHER"); } # Tie in the 'names' file 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;' ) { if ($debug_grapher eq "Y") { 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;' ) { if ($debug_grapher eq "Y") { print DEBUG "Using NDBM\n"; } }; # Set up graphing colors GD::Graph::colour::read_rgb("FlowGrapher_Colors") or die "cannot read colors"; # 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 FlowGrapher graph $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'}; $detail_lines = $FORM{'detail_lines'}; $sample_time = $FORM{'sample_time'}; $graph_multiplier = $FORM{'graph_multiplier'}; $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 the web page output print "Content-type:text/html\n\n"; print ""; print ""; print ""; print "FlowGrapher $version Graph Results"; print "
\n"; # Check dates and times for input errors ($mn,$da,$yr) = split(/\//,$start_date); if ( $mn=~/\D/ || $da=~/\D/ || $yr=~/\D/ ) { &print_error("Bad date format: $start_date"); }; if ( $mn<1 || $mn>12 || $da<1 || $da>31 || $yr<1990 || $yr>2100 ) { &print_error("Bad date format: $start_date"); } $start_mn = $mn; if (length($start_mn) < 2) { $start_mn = "0" . $start_mn; } $start_da = $da; if (length($start_da) < 2) { $start_da = "0" . $start_da; } $start_md = $start_mn . $start_da; $start_ymd = $yr . $start_mn . $start_da; ($hr,$mi,$sc) = split(/:/,$start_time); if ( $hr=~/\D/ || $mi=~/\D/ || $sc=~/\D/ ) { &print_error("Bad time format: $start_time"); }; if (length($hr)>2 || $hr>23 || length($mi)>2 || $mi>59 || length($sc)>2 || $sc>59 ) { &print_error("Bad time format: $start_time"); } $start_secs = 3600*$hr + 60*$mi + $sc; ($mn,$da,$yr) = split(/\//,$end_date); if ( $mn=~/\D/ || $da=~/\D/ || $yr=~/\D/ ) { &print_error("Bad date format: $end_date"); }; if ( $mn<1 || $mn>12 || $da<1 || $da>31 || $yr<1990 || $yr>2100 ) { &print_error("Bad date format: $end_date"); } $end_mn = $mn; if (length($end_mn) < 2) { $end_mn = "0" . $end_mn; } $end_da = $da; if (length($end_da) < 2) { $end_da = "0" . $end_da; } $end_md = $end_mn . $end_da; ($hr,$mi,$sc) = split(/:/,$end_time); if ( $hr=~/\D/ || $mi=~/\D/ || $sc=~/\D/ ) { &print_error("Bad time format: $end_time"); }; if (length($hr)>2 || $hr>23 || length($mi)>2 || $mi>59 || length($sc)>2 || $sc>59 ) { &print_error("Bad time format: $end_time"); } $end_secs = 3600*$hr + 60*$mi + $sc; # 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; $cat_start = epoch_to_date($cat_start_epoch,"LOCAL"); $cat_end = epoch_to_date($cat_end_epoch,"LOCAL"); ($cat_start_date,$cat_start_time) = split(/ /,$cat_start); ($start_month,$start_day,$start_yr) = split(/\//,$cat_start_date); ($cat_end_date,$cat_end_time) = split(/ /,$cat_end); ($end_month,$end_day,$end_yr) = split(/\//,$cat_end_date); $end_ymd = $end_yr . $end_month . $end_day; $midnight = "00:00:00"; $cat_end_midnight = date_to_epoch($cat_end_date,$midnight,"LOCAL") + 86400; $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_midnight) { 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"; } if (-e $cat_directory) { $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/FlowGrapher_filter_$suffix"; open (FILTER,">$filter_file"); 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/FlowGrapher_filter_$suffix -FFlow_Filter"; # Set up the flow-print command $print_report = 5; $flowprint_command = "$flow_bin_directory/flow-print -f$print_report >$work_directory/FlowGrapher_output_$suffix"; # Execute the piped flow-tools command $flow_run = "$flowcat_command | $flownfilter_command | $flowprint_command"; if ($debug_grapher eq "Y") { print DEBUG "$flow_run\n"; } system ($flow_run); # Collect data into buckets to form the plot for the graph $total_buckets = int($report_length / $sample_time) - 1; open (FLOWS,"<$work_directory/FlowGrapher_output_$suffix"); while () { $first_char = substr($_,0,1); if (!($first_char =~ /[0-9]/)) { next; } ($s_time,$e_time,$sif,$sip,$sp,$dif,$dip,$dp,$p,$fl,$pkt,$oct) = split(/\s+/,$_); ($smd,$s_tm,$s_ms) = split(/\./,$s_time); ($emd,$e_tm,$e_ms) = split(/\./,$e_time); ($shr,$smn,$ssc) = split(/:/,$s_tm); $ssc_ms = $ssc + (0.001 * $s_ms); $ssc = int($ssc_ms); ($ehr,$emn,$esc) = split(/:/,$e_tm); $esc_ms = $esc + (0.001 * $e_ms); $esc = int($esc_ms); $flow_delta_md = $emd - $smd; $ss_delta_md = $smd - $start_md; $es_delta_md = $emd - $start_md; $se_delta_md = $smd - $end_md; if (se_delta_md > 0) { next; } if (($flow_delta_md == 0) && (($ss_delta_md == 0) || ($ss_delta_md == 1)) && (($es_delta_md == 0) || ($es_delta_md == 1))) { $s_secs = 3600*$shr + 60*$smn + $ssc; $e_secs = 3600*$ehr + 60*$emn + $esc; $s_secs_ms = 3600*$shr + 60*$smn + $ssc_ms; $e_secs_ms = 3600*$ehr + 60*$emn + $esc_ms; if (($es_delta_md == 0) && ($e_secs < $start_secs)) { next; } if (($se_delta_md == 0) && ($s_secs > $end_secs)) { next; } $flow_length_ms = $e_secs_ms - $s_secs_ms; if ($flow_length_ms <= 0) { $flow_length_ms = 0.001; } $flow_length = $flow_length_ms; $flow_bits = $oct * 8; if ($ss_delta_md == 0) { $start_delta = $s_secs - $start_secs; } elsif ($ss_delta_md == 1) { $start_delta = 86400 - $start_secs + $s_secs; } if ($es_delta_md == 0) { $end_delta = $e_secs - $start_secs; } elsif ($es_delta_md == 1) { $end_delta = 86400 - $start_secs + $e_secs; } $s_epoch = $start_epoch + $start_delta; } else { # This method is required for infrequent situations where Epoch (much slower) is best used for date/times $s_mnt = substr($smd,0,2); $s_day = substr($smd,2,2); $s_date = $s_mnt ."/". $s_day ."/". $start_yr; # End of year special processing (particular flow-tools output used lacks year value) if ($end_yr ne $start_yr) { if (($s_mnt ge "07") && ($s_mnt le "12")) { $s_date = $s_mnt ."/". $s_day ."/". $start_yr; } if (($s_mnt ge "01") && ($s_mnt le "06")) { $s_date = $s_mnt ."/". $s_day ."/". $end_yr; } } $sms_epoch = date_to_epoch($s_date,$s_tm,"LOCAL") + $s_ms/1000; $s_epoch = int($sms_epoch); $e_mnt = substr($emd,0,2); $e_day = substr($emd,2,2); $e_date = $e_mnt ."/". $e_day ."/". $start_yr; # End of year special processing (particular flow-tools output used lacks year value) if ($end_yr ne $start_yr) { if (($e_mnt ge "07") && ($s_mnt le "12")) { $e_date = $e_mnt ."/". $e_day ."/". $start_yr; } if (($e_mnt ge "01") && ($e_mnt le "06")) { $e_date = $e_mnt ."/". $e_day ."/". $end_yr; } } $ems_epoch = date_to_epoch($e_date,$e_tm,"LOCAL") + $e_ms/1000; $e_epoch = int($ems_epoch); $flow_length_ms = $ems_epoch - $sms_epoch; if ($flow_length_ms <= 0) { $flow_length_ms = 0.001; } $flow_length = $flow_length_ms; $flow_bits = $oct * 8; if ($e_epoch < $start_epoch) { next; } if ($s_epoch > $end_epoch) { next; } $start_delta = int($s_epoch - $start_epoch); $end_delta = int($e_epoch - $start_epoch); } # Determine first, last, and total number of buckets spanned by flow if ($start_delta <= 0) { $first_bucket = 0; } else { $first_bucket = int($start_delta / $sample_time); } if ($end_delta > $report_length) { $last_bucket = int($report_length / $sample_time) - 1; } else { $last_bucket = int($end_delta / $sample_time); } if ($first_bucket > $last_bucket) { $first_bucket = $last_bucket; } $num_buckets = $last_bucket - $first_bucket + 1; # Determine bucket amount and accumulate into buckets, start with bits/second $per_second_amount = int($flow_bits / $flow_length); if ($num_buckets == 1) { if (($first_bucket == 0) && ($start_delta < 0)) { $buckets[$first_bucket] += int(($end_delta/$flow_length) * $flow_bits); } elsif (($first_bucket eq $total_buckets) && ($end_delta > $report_length)) { $buckets[$first_bucket] += int((($report_length - $start_delta)/$flow_length) * $flow_bits); } else { $buckets[$first_bucket] += $flow_bits; } } else { for ($i=$first_bucket;$i<=$last_bucket;$i++) { # Pro-rate for first bucket if ($i == $first_bucket) { $bucket_offset = ($ssc_ms % $sample_time) + (0.001 * $s_ms); if ($bucket_offset == 0) { $bucket_amount = $sample_time * $per_second_amount; } else { $bucket_amount = int( ($sample_time - $bucket_offset) * $per_second_amount); } } # Pro-rate for last bucket elsif ($i == $last_bucket) { $bucket_offset = ($esc_ms % $sample_time) + (0.001 * $e_ms); if ($bucket_offset == 0) { $bucket_amount = $sample_time * $per_second_amount; } else { $bucket_amount = int( $bucket_offset * $per_second_amount ); } } # All other buckets ... else { $bucket_amount = $sample_time * $per_second_amount; } # Accumulate into the buckets $buckets[$i] += $bucket_amount; } } # Collect data for detail lines if turned on (detail lines > 0) if ($detail_lines > 0) { if ($num_bigs < $detail_lines) { $num_bigs++; if ($num_bigs == 1) { $smallest_flow = $flow_bits; } $len_flow_bits = length($flow_bits); $detail_line_key = $flow_bits; for ($i=$len_flow_bits;$i<22;$i++) { $detail_line_key = "0" . $detail_line_key; } $big_flow = $detail_line_key ."&". $s_epoch .";". $s_tm .";". $e_tm .";". $sip .";". $sp .";". $dip .";". $dp ."&". $flow_bits ."&". $flow_length_ms; push(@biggest_flows,$big_flow); if ($flow_bits < $smallest_flow) { $smallest_flow = $flow_bits; } } elsif ($flow_bits > $smallest_flow) { $len_flow_bits = length($flow_bits); $detail_line_key = $flow_bits; for ($i=$len_flow_bits;$i<22;$i++) { $detail_line_key = "0" . $detail_line_key; } $big_flow = $detail_line_key ."&". $s_epoch .";". $s_tm .";". $e_tm .";". $sip .";". $sp .";". $dip .";". $dp ."&". $flow_bits ."&". $flow_length_ms; shift(@biggest_flows); push(@biggest_flows,$big_flow); @biggest_flows = sort(@biggest_flows); $smallest_flow = substr($biggest_flows[0],0,22); } } } # Determine the statistics and graphing times $stats_pct = 100.0; $total_graph_times = $report_length / $sample_time - 1; $skip_graph_times = $total_graph_times / 8; for ($i=0;$i<=$total_graph_times;$i++) { $buckets[$i] = $buckets[$i] / $sample_time; if ($i == 1) { $stats_min = $buckets[$i]; } $stats_total += $buckets[$i]; $stats_avg = int ($stats_total / ($i+1)); if ($buckets[$i] > $stats_max) { $stats_max = int($buckets[$i]); } if ($buckets[$i] < $stats_min) { $stats_min = int($buckets[$i]); } $graph_labels[$i] = ""; if (($i % $skip_graph_times) == 0) { $bucket_time = $start_epoch + (($i) * $sample_time); $bucket_time_d = epoch_to_date($bucket_time,"LOCAL"); ($bucket_date,$bucket_hms) = split(/ /,$bucket_time_d); $graph_labels[$i] = $bucket_hms; if (($i != 0) && (!$found_first)) { $x_tick_offset = $i; $x_label_skip = $i; $found_first = 1; } } } push (@graph_labels," "); # Create the plot ... @plot = ([@graph_labels],[@buckets]); # Create the graph ... $horizontal = "Time: LOCAL"; $vertical = "Bits/Second"; $legend = "Flow data from $device_name"; $description = "Flow data from $device_name"; $x_ticks = 1; $long_ticks = 1; $graph_width *= $graph_multiplier; $graph = GD::Graph::mixed->new($graph_width,$graph_height); $graph->set( boxclr => "$boxclr", bgclr => "$bgclr", transparent => "$transparent", borderclrs => "$borderclrs", fgclr => "$fgclr", labelclr => "$labelclr", axislabelclr => "$axislabelclr", legendclr => "$legendclr", valuesclr => "$valuesclr", textclr => "$textclr", dclrs => ['pale orange','pale brown','dark red','pale blue','pale yellow'], types => ['area','lines'], line_width => "2", t_margin => "$t_margin", b_margin => "$b_margin", l_margin => "$l_margin", r_margin => "$r_margin", x_ticks => "$x_ticks", long_ticks => "$long_ticks", x_label_skip => "$x_label_skip", x_tick_offset => "$x_tick_offset", y_label => "$vertical", x_label => "$horizontal", x_label_position => "0.5", y_number_format => \&y_format, skip_undef => "$skip_undef", title => "$description", x_label_font => "gdTinyFont", title_font => "gdTinyFont", legend_font => "gdTinyFont", legend_placement => "BL", legend_marker_width => "24", legend_marker_height => "4" ) or warn $graph->error; $graph->set_legend($legend); $graph->set_x_axis_font($x_axis_font); # Create the image and write it to the correct directory $image = $graph->plot(\@plot) or die $graph->error; @sorted_buckets = sort by_number (@buckets); $index_95 = int (0.95 * $total_graph_times); $stats_pct = int ($sorted_buckets[$index_95]); $stats_max = format_number($stats_max); $stats_pct = format_number($stats_pct); $stats_avg = format_number($stats_avg); $stats_min = format_number($stats_min); $stats_max_out = "Maximum : $stats_max"; $stats_pct_out = "95th Pct.: $stats_pct"; $stats_avg_out = "Average : $stats_avg"; $stats_min_out = "Minimum : $stats_min"; $font_color = $image->colorAllocate(0,0,0); $image->string(gdSmallFont,$horz_max,$vert_max,$stats_max_out,$font_color); $image->string(gdSmallFont,$horz_pct,$vert_pct,$stats_pct_out,$font_color); $image->string(gdSmallFont,$horz_avg,$vert_avg,$stats_avg_out,$font_color); $image->string(gdSmallFont,$horz_min,$vert_min,$stats_min_out,$font_color); $report_title = "Flow Graph"; $save_file =~ s/\//_/g; $save_filename = $prefix ."_". $save_file . "_graph.html"; $png_filename = $prefix ."_". $save_file . "_graph.png"; $flowgraph_link = "$graphs_short/$png_filename"; open(PNG,">$graphs_directory/$png_filename"); binmode PNG; print PNG $image->png; # Begin a temporary copy of the report page in case user wishes to save $save_fullname = "$work_directory/$save_filename"; open (HTML,">$save_fullname"); chmod $html_file_perms, $save_fullname; print HTML ""; print HTML ""; print HTML "FlowGrapher Graph Results"; print HTML ""; print HTML "
\n"; print HTML ""; print ""; print ""; print ""; print ""; print ""; # Output report input filtering criteria print ""; print ""; print ""; print ""; print ""; print "
"; print "

"; # Output button for saved graph print "

"; print "

"; print "

"; print "
\n";
printf "      Report: %-28s                    Sample Time: %-4s\n", $report_title, $sample_time;
printf "  Start Time: %-28s                       End Time: %-28s\n", $start_flows, $end_flows;
 
print_formatted_parameters(%FORM,$html_file);

printf "Detail Lines: %-28s               Graph Multiplier: %-28s\n", $detail_lines, $graph_multiplier;
printf "  Include if: %-28s\n",   $flow_select_manner;
print "
"; print "
\n"; print HTML ""; print HTML ""; print HTML ""; print HTML ""; print HTML ""; print HTML ""; print HTML "
"; print HTML "
"; print HTML "
";
printf HTML "      Report: %-28s                    Sample Time: %-4s\n", $report_title, $sample_time;
printf HTML "  Start Time: %-28s                       End Time: %-28s\n", $start_flows, $end_flows;
 
$html_file = "$work_directory/$save_filename";
print_formatted_parameters(%FORM,$html_file);

printf HTML "Detail Lines: %-28s               Graph Multiplier: %-28s\n", $detail_lines, $graph_multiplier;
printf HTML "  Include if: %-28s\n",   $flow_select_manner;
print  HTML "\n";
print HTML "
"; print "
\n"; print HTML "
\n"; # Print out details if they have been requested if ($detail_lines > 0) { print "
";
	print HTML "
";

	if ($resolve_addresses eq "Y") {
		printf "%-9s %-9s %6s %-25s  %-6s   %-25s  %-6s  %14s  %7s\n\n", "Start", "End", "Len", "Source Host", "Port", "Destination Host", "Port", "Total Bytes", "Mbps";
		printf HTML "%-9s %-9s %6s %-25s  %-6s   %-25s  %-6s  %14s  %7s\n\n", "Start", "End", "Len", "Source Host", "Port", "Destination Host", "Port", "Total Bytes", "Mbps"; }
	else {
		printf "%-9s %-9s %6s %-20s  %-6s   %-20s  %-6s  %14s  %7s\n\n", "Start", "End", "Len", "Source Host", "Port", "Destination Host", "Port", "Total Bytes", "Mbps";
		printf HTML "%-9s %-9s %6s %-20s  %-6s   %-20s  %-6s  %14s  %7s\n\n", "Start", "End", "Len", "Source Host", "Port", "Destination Host", "Port", "Total Bytes", "Mbps"; 
	}

	foreach $big_flow (@biggest_flows) {
		$big_flow = substr($big_flow,23,120);
	}
	@biggest_flows = sort(@biggest_flows);

	for ($m=0;$m<=$detail_lines;$m++) {
		if ($biggest_flows[$m] eq "") { next; }
		($big_flow_key,$big_flow_bits,$big_flow_length) = split(/&/,$biggest_flows[$m]);
		if ($big_flow_length < 1) { $big_flow_length = 1; }
		$big_flow_rate = ($big_flow_bits / $big_flow_length) / 1000000;
		$big_flow_bytes = int ($big_flow_bits / 8);
		$formatted_bytes = format_number($big_flow_bytes);

		($s_epoch,$s_tm,$e_tm,$sip,$sp,$dip,$dp) = split(/;/,$big_flow_key);

		if ($resolve_addresses eq "Y") {
			$sip = dig($sip);
			$dip = dig($dip);
		}

		if ($resolve_addresses eq "Y") {
			printf "%-9s %-9s %6.1f %-25.25s  %-6s   %-25.25s  %-6s  %14s  %7.3f\n", $s_tm, $e_tm, $big_flow_length, $sip, $sp, $dip, $dp, $formatted_bytes, $big_flow_rate;
			printf HTML" %-9s %-9s %6.1f %-25.25s  %-6s   %-25.25s  %-6s  %14s  %7.3f\n", $s_tm, $e_tm, $big_flow_length, $sip, $sp, $dip, $dp, $formatted_bytes, $big_flow_rate; }
		else {
			printf "%-9s %-9s %6.1f %-20.20s  %-6s   %-20.20s  %-6s  %14s  %7.3f\n", $s_tm, $e_tm, $big_flow_length, $sip, $sp, $dip, $dp, $formatted_bytes, $big_flow_rate;
			printf HTML "%-9s %-9s %6.1f %-20.20s  %-6s   %-20.20s  %-6s  %14s  %7.3f\n", $s_tm, $e_tm, $big_flow_length, $sip, $sp, $dip, $dp, $formatted_bytes, $big_flow_rate;
		}

	}
}

print "\n";  
print "\n";  
print "\n";  
print "\n";  
print "\n";  
print "\n";  
print HTML "\n";  
print HTML "\n";  
print HTML "\n";  
print HTML "\n";  
print HTML "\n";  
print HTML "\n";

print "";
print HTML "";

# Remove temporary files

$rm_command = "/bin/rm $work_directory/FlowGrapher_filter_$suffix"; 
if ($debug_files ne "Y") { system($rm_command); }
$rm_command = "/bin/rm $work_directory/FlowGrapher_output_$suffix"; 
if ($debug_files ne "Y") { system($rm_command); }
 
sub by_number { $a <=> $b; }

sub print_error {

	my ($error_text) = @_;

	print "";
	print "";
	print "";
	print "FlowGrapher Error";
	print "
\n"; print "

$error_text


"; print "
\n"; print ""; print ""; exit; } sub dig { my ($host_address) = @_; $host_name = $host_address; if (defined($host_names{$host_address})) { $host_name = $host_names{$host_address}; } else { open(DIG,"$dig $host_address 2>&1|"); $answer_record = 0; while () { chop; if (/ANSWER SECTION/) { $answer_record = 1; next; } if ($answer_record) { ($in_addr,$rec_timeout,$rec_direction,$rec_type,$host_name) = split(/\s+/,$_); $last_period = rindex($host_name,"\."); $host_name = substr($host_name,0,$last_period); $length_name = length($host_name); if ($length_name > 30) { $left_start = $length_name - 30; $host_name = substr($host_name,$left_start,30); } if (/CNAME/) { $host_name = $host_address; } last; } } if (length($host_name) < 1) { $host_name = $host_address; } $host_names{$host_address} = $host_name; } return $host_name; }