id_name)) or filters filtered
*
* @param mixed $filter Array with filter conditions to retrieve filters or
* false.
*
* @return array List of all filters.
*/
function netflow_get_filters($filter=false)
{
if ($filter === false) {
$filters = db_get_all_rows_in_table('tnetflow_filter', 'id_name');
} else {
$filters = db_get_all_rows_filter('tnetflow_filter', $filter);
}
$return = [];
if ($filters === false) {
return $return;
}
foreach ($filters as $filter) {
$return[$filter['id_name']] = $filter['id_name'];
}
return $return;
}
/**
* Selects all netflow reports (array (id_name => id_name)) or filters filtered
*
* @param mixed $filter Array with filter conditions to retrieve filters or
* false.
*
* @return array List of all filters.
*/
function netflow_get_reports($filter=false)
{
if ($filter === false) {
$filters = db_get_all_rows_in_table('tnetflow_report', 'id_name');
} else {
$filters = db_get_all_rows_filter('tnetflow_report', $filter);
}
$return = [];
if ($filters === false) {
return $return;
}
foreach ($filters as $filter) {
$return[$filter['id_name']] = $filter['id_name'];
}
return $return;
}
/**
* Check if a filter owns to a certain group.
*
* @param integer $id_sg Id group to check.
*
* @return boolean True if user manages that group.
*/
function netflow_check_filter_group($id_sg)
{
global $config;
$id_group = db_get_value('id_group', 'tnetflow_filter', 'id_sg', $id_sg);
$own_info = get_user_info($config['id_user']);
// Get group list that user has access.
$groups_user = users_get_groups($config['id_user'], 'IW', $own_info['is_admin'], true);
$groups_id = [];
$has_permission = false;
foreach ($groups_user as $key => $groups) {
if ($groups['id_grupo'] == $id_group) {
return true;
}
}
return false;
}
/**
* Get a filter.
*
* @param integer $id_sg Filter id to be fetched.
* @param mixed $filter Extra filter.
* @param mixed $fields Fields to be fetched.
*
* @return array A netflow filter matching id and filter.
*/
function netflow_filter_get_filter($id_sg, $filter=false, $fields=false)
{
if (! is_array($filter)) {
$filter = [];
}
$filter['id_sg'] = (int) $id_sg;
return db_get_row_filter('tnetflow_filter', $filter, $fields);
}
/**
* Compare two flows according to the 'data' column.
*
* @param array $a First flow.
* @param array $b Second flow.
*
* @return Result of the comparison.
*/
function compare_flows($a, $b)
{
return $a['data'] < $b['data'];
}
/**
* Sort netflow data according to the 'data' column.
*
* @param array $netflow_data Netflow data array.
*
* @return void (Array passed by reference)
*/
function sort_netflow_data(&$netflow_data)
{
usort($netflow_data, 'compare_flows');
}
/**
* Show a table with netflow statistics.
*
* @param array $data Statistic data.
* @param string $start_date Start date.
* @param string $end_date End date.
* @param string $aggregate Aggregate field.
*
* @return string HTML statistics table.
*/
function netflow_stat_table($data, $start_date, $end_date, $aggregate)
{
global $nfdump_date_format;
$start_date = date($nfdump_date_format, $start_date);
$end_date = date($nfdump_date_format, $end_date);
$values = [];
$table = new stdClass();
$table->width = '100%';
$table->cellspacing = 0;
$table->class = 'databox';
$table->data = [];
$j = 0;
$x = 0;
$table->head = [];
$table->head[0] = ''.netflow_format_aggregate($aggregate).'';
$table->head[1] = ''.__('Value').'';
$table->style[0] = 'padding: 6px;';
$table->style[1] = 'padding: 6px;';
while (isset($data[$j])) {
$agg = $data[$j]['agg'];
if (!isset($values[$agg])) {
$values[$agg] = $data[$j]['data'];
} else {
$values[$agg] += $data[$j]['data'];
}
$table->data[$x][0] = $agg;
$table->data[$x][1] = network_format_bytes($data[$j]['data']);
$j++;
$x++;
}
return html_print_table($table, true);
}
/**
* Show a table with netflow data.
*
* @param array $data Netflow data.
* @param string $start_date Start date.
* @param string $end_date End date.
* @param string $aggregate Aggregate field.
*
* @return string HTML data table.
*/
function netflow_data_table($data, $start_date, $end_date, $aggregate)
{
global $nfdump_date_format;
$period = ($end_date - $start_date);
$start_date = date($nfdump_date_format, $start_date);
$end_date = date($nfdump_date_format, $end_date);
// Set the format.
if ($period <= SECONDS_6HOURS) {
$time_format = 'H:i:s';
} else if ($period < SECONDS_1DAY) {
$time_format = 'H:i';
} else if ($period < SECONDS_15DAYS) {
$time_format = 'M d H:i';
} else if ($period < SECONDS_1MONTH) {
$time_format = 'M d H\h';
} else {
$time_format = 'M d H\h';
}
$values = [];
$table = new stdClass();
$table->size = ['100%'];
$table->class = 'databox';
$table->cellspacing = 0;
$table->data = [];
$table->head = [];
$table->head[0] = ''.__('Timestamp').'';
$table->style[0] = 'padding: 4px';
$j = 0;
$source_index = [];
$source_count = 0;
if (isset($data['sources'])) {
foreach ($data['sources'] as $source => $null) {
$table->style[($j + 1)] = 'padding: 4px';
$table->align[($j + 1)] = 'right';
$table->headstyle[($j + 1)] = 'text-align: right;';
$table->head[($j + 1)] = $source;
$source_index[$j] = $source;
$source_count++;
$j++;
}
} else {
$table->style[1] = 'padding: 4px;';
}
// No aggregates.
if ($source_count == 0) {
$table->head[1] = __('Data');
$table->align[1] = 'right';
$i = 0;
foreach ($data as $timestamp => $value) {
$table->data[$i][0] = date($time_format, $timestamp);
$table->data[$i][1] = network_format_bytes($value['data']);
$i++;
}
} else {
$i = 0;
foreach ($data['data'] as $timestamp => $values) {
$table->data[$i][0] = date($time_format, $timestamp);
for ($j = 0; $j < $source_count; $j++) {
$table->data[$i][($j + 1)] = network_format_bytes(
$values[$source_index[$j]]
);
}
$i++;
}
}
return html_print_table($table, true);
}
/**
* Show a table with a traffic summary.
*
* @param array $data Summary data.
*
* @return string HTML summary table.
*/
function netflow_summary_table($data)
{
global $nfdump_date_format;
$values = [];
$table = new stdClass();
$table->cellspacing = 0;
$table->class = 'databox';
$table->styleTable = 'width: 100%';
$table->data = [];
$table->style[0] = 'font-weight: bold; padding: 6px';
$table->style[1] = 'padding: 6px';
$row = [];
$row[] = __('Total flows');
$row[] = format_for_graph($data['totalflows'], 2);
$table->data[] = $row;
$row = [];
$row[] = __('Total bytes');
$row[] = network_format_bytes($data['totalbytes']);
$table->data[] = $row;
$row = [];
$row[] = __('Total packets');
$row[] = format_for_graph($data['totalpackets'], 2);
$table->data[] = $row;
$row = [];
$row[] = __('Average bits per second');
$row[] = network_format_bytes($data['avgbps']);
$table->data[] = $row;
$row = [];
$row[] = __('Average packets per second');
$row[] = format_for_graph($data['avgpps'], 2);
$table->data[] = $row;
$row = [];
$row[] = __('Average bytes per packet');
$row[] = format_for_graph($data['avgbpp'], 2);
$table->data[] = $row;
$html = html_print_table($table, true);
return $html;
}
/**
* Returns 1 if the given address is a network address.
*
* @param string $address Host or network address.
*
* @return 1 if the address is a network address, 0 otherwise.
*/
function netflow_is_net($address)
{
if (strpos($address, '/') !== false) {
return 1;
}
return 0;
}
/**
* Returns netflow data for the given period in an array.
*
* @param string $start_date Period start date.
* @param string $end_date Period end date.
* @param mixed $interval_length Resolution points or hourly or daily.
* @param string $filter Netflow filter.
* @param string $aggregate Aggregate field.
* @param integer $max Maximum number of aggregates.
* @param boolean $absolute True to give the absolute data and false
* to get troughput.
* @param string $connection_name Node name when data is get in meta.
* @param boolean $address_resolution True to resolve ips to hostnames.
*
* @return array An array with netflow stats.
*/
function netflow_get_data(
$start_date,
$end_date,
$interval_length,
$filter,
$aggregate,
$max,
$absolute,
$connection_name='',
$address_resolution=false
) {
global $nfdump_date_format;
global $config;
// Requesting remote data.
if (defined('METACONSOLE') && $connection_name != '') {
$data = metaconsole_call_remote_api(
$connection_name,
'netflow_get_data',
"$start_date|$end_date|$interval_length|".base64_encode(json_encode($filter))."|$aggregate|$max|1".(int) $address_resolution
);
return json_decode($data, true);
}
if ($start_date > $end_date) {
return [];
}
// Calculate the number of intervals.
$multiplier_time = ($end_date - $start_date);
switch ($interval_length) {
case NETFLOW_RES_LOWD:
case NETFLOW_RES_MEDD:
case NETFLOW_RES_HID:
case NETFLOW_RES_ULTRAD:
$multiplier_time = ceil(($end_date - $start_date) / $interval_length);
break;
case NETFLOW_RES_HOURLY:
$multiplier_time = SECONDS_1HOUR;
break;
case NETFLOW_RES_DAILY:
$multiplier_time = SECONDS_1DAY;
break;
default:
$multiplier_time = ($end_date - $start_date);
break;
}
// Recalculate to not pass of netflow_max_resolution.
if ($config['netflow_max_resolution'] > 0
&& (($end_date - $start_date) / $multiplier_time) > 50
) {
$multiplier_time = ceil(
(($end_date - $start_date) / $config['netflow_max_resolution'])
);
}
// Put all points into an array.
$intervals = [($start_date - $multiplier_time)];
while ((end($intervals) < $end_date) === true) {
$intervals[] = (end($intervals) + $multiplier_time);
}
if (end($intervals) != $end_date) {
$intervals[] = $end_date;
}
// Calculate the top values.
$values = netflow_get_top_data(
$start_date,
$end_date,
$filter,
$aggregate,
$max
);
// Update the filter to get properly next data.
netflow_update_second_level_filter(
$filter,
$aggregate,
array_keys($values['sources'])
);
// Resolve addresses if required.
$get_hostnames = false;
if ($address_resolution === true) {
global $hostnames;
netflow_address_resolution($values, $get_hostnames, $aggregate);
}
foreach ($intervals as $k => $time) {
$interval_start = $time;
if (!isset($intervals[($k + 1)])) {
continue;
}
$interval_end = $intervals[($k + 1)];
// Set default values.
foreach ($values['sources'] as $source => $discard) {
$values['data'][$interval_end][$source] = 0;
}
$data = netflow_get_stats(
$interval_start,
$interval_end,
$filter,
$aggregate,
$max,
$absolute,
$connection_name
);
foreach ($data as $line) {
// Address resolution start.
if ($get_hostnames) {
if (!isset($hostnames[$line['agg']])) {
$hostname = false;
// Trying to get something like an IP from the description.
if (preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $line['agg'], $matches)
|| preg_match(
"/(((?=(?>.*?(::))(?!.+\3)))\3?|([\dA-F]{1,4}(\3|:?)|\2))(?4){5}((?4){2}|(25[0-5]|
(2[0-4]|1\d|[1-9])?\d)(\.(?7)){3})/i",
$line['agg'],
$matches
)
) {
if ($matches[0]) {
$hostname = gethostbyaddr($line['agg']);
}
}
if ($hostname !== false) {
$hostnames[$line['agg']] = $hostname;
$line['agg'] = $hostname;
}
} else {
$line['agg'] = $hostnames[$line['agg']];
}
}
// Address resolution end.
if (! isset($values['sources'][$line['agg']])) {
continue;
}
$values['data'][$interval_end][$line['agg']] = $line['data'];
}
}
if (empty($values['data'])) {
return [];
}
return $values;
}
/**
* Returns netflow stats for the given period in an array.
*
* @param string $start_date Period start date.
* @param string $end_date Period end date.
* @param string $filter Netflow filter.
* @param string $aggregate Aggregate field.
* @param integer $max Maximum number of aggregates.
* @param boolean $absolute True to give the absolute data and false
* to get troughput.
* @param string $connection_name Node name when data is get in meta.
* @param boolean $address_resolution True to resolve ips to hostnames.
*
* @return array With netflow stats.
*/
function netflow_get_stats(
$start_date,
$end_date,
$filter,
$aggregate,
$max,
$absolute=true,
$connection_name='',
$address_resolution=false
) {
global $config, $nfdump_date_format;
// Requesting remote data.
if (defined('METACONSOLE') && $connection_name != '') {
$data = metaconsole_call_remote_api($connection_name, 'netflow_get_stats', "$start_date|$end_date|".base64_encode(json_encode($filter))."|$aggregate|$max|$absolute|".(int) $address_resolution);
return json_decode($data, true);
}
// Get the command to call nfdump.
$command = netflow_get_command($filter);
// Execute nfdump.
$command .= " -o csv -q -n $max -s $aggregate/bytes -t ".date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date);
exec($command, $string);
if (! is_array($string)) {
return [];
}
// Remove the first line.
$string[0] = '';
$i = 0;
$values = [];
$interval_length = ($end_date - $start_date);
foreach ($string as $line) {
if ($line == '') {
continue;
}
$val = explode(',', $line);
$values[$i]['date'] = $val[0];
$values[$i]['time'] = $val[1];
// Create field to sort array.
$datetime = $val[0];
$end_date = strtotime($datetime);
$values[$i]['datetime'] = $end_date;
// Address resolution start.
if ($address_resolution && ($aggregate == 'srcip' || $aggregate == 'dstip')) {
global $hostnames;
if (!isset($hostnames[$val[4]])) {
$hostname = gethostbyaddr($val[4]);
if ($hostname !== false) {
$hostnames[$val[4]] = $hostname;
$val[4] = $hostname;
}
} else {
$val[4] = $hostnames[$val[4]];
}
}
// Address resolution end.
$values[$i]['agg'] = $val[4];
if (! isset($val[9])) {
return [];
}
$values[$i]['data'] = $val[9];
if (!$absolute) {
$values[$i]['data'] = ($values[$i]['data'] / $interval_length);
}
$i++;
}
sort_netflow_data($values);
return $values;
}
/**
* Returns a traffic summary for the given period in an array.
*
* @param string $start_date Period start date.
* @param string $end_date Period end date.
* @param string $filter Netflow filter.
* @param string $connection_name Node name when data is get in meta.
*
* @return array With netflow summary data.
*/
function netflow_get_summary($start_date, $end_date, $filter, $connection_name='')
{
global $nfdump_date_format;
global $config;
// Requesting remote data.
if (defined('METACONSOLE') && $connection_name != '') {
$data = metaconsole_call_remote_api($connection_name, 'netflow_get_summary', "$start_date|$end_date|".base64_encode(json_encode($filter)));
return json_decode($data, true);
}
// Get the command to call nfdump.
$command = netflow_get_command($filter);
// Execute nfdump.
$command .= ' -o csv -n 1 -s srcip/bytes -t '.date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date);
exec($command, $string);
if (! is_array($string) || ! isset($string[5])) {
return [];
}
// Read the summary.
$summary = explode(',', $string[5]);
if (! isset($summary[5])) {
return [];
}
$values['totalflows'] = $summary[0];
$values['totalbytes'] = $summary[1];
$values['totalpackets'] = $summary[2];
$values['avgbps'] = $summary[3];
$values['avgpps'] = $summary[4];
$values['avgbpp'] = $summary[5];
return $values;
}
/**
* Returns a relationships data for the given period in an array.
*
* @param string $start_date Period start date.
* @param string $end_date Period end date.
* @param string $filter Netflow filter.
* @param integer $max Maximum number of elements.
* @param string $aggregate One of srcip, srcport, dstip, dstport.
*
* @return array With raw relationship data.
*/
function netflow_get_relationships_raw_data(
$start_date,
$end_date,
$filter,
$max,
$aggregate
) {
global $nfdump_date_format;
global $config;
$max_data = netflow_get_top_data(
$start_date,
$end_date,
$filter,
$aggregate,
$max
);
// Update src and dst filter (both).
$sources_array = array_keys($max_data['sources']);
$is_ip = netflow_aggregate_is_ip($aggregate);
netflow_update_second_level_filter(
$filter,
($is_ip === true) ? 'dstip' : 'dstport',
$sources_array
);
netflow_update_second_level_filter(
$filter,
($is_ip === true) ? 'srcip' : 'srcport',
$sources_array
);
// Get the command to call nfdump.
$command = sprintf(
'%s -q -o csv -n %s -s %s/bytes -t %s-%s',
netflow_get_command($filter),
NETFLOW_MAX_DATA_CIRCULAR_MESH,
'record',
date($nfdump_date_format, $start_date),
date($nfdump_date_format, $end_date)
);
// Get the command to call nfdump.
$command = netflow_get_command($filter);
// Execute nfdump.
$command .= ' -q -o csv -n 10000 -s record/bytes -t '.date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date);
exec($command, $result);
if (! is_array($result)) {
return [
'lines' => [],
'sources' => [],
];
}
return [
'lines' => $result,
'sources' => $sources_array,
];
}
/**
* Parse the raw relationships data to be painted by circular mesh chart.
*
* @param array $result Lines gotten from nfdump call.
* @param array $sources_array Array with sources involved in the chart.
* @param boolean $is_ip Is ip or port.
*
* @return array With data to be parsed on circular mesh chart.
*/
function netflow_parse_relationships_for_circular_mesh(
$result,
$sources_array,
$is_ip
) {
if (empty($result)) {
return [];
}
// Initialize some data structures.
$data = [
'elements' => [],
'matrix' => [],
];
$initial_data = [];
// This array has the ips or port like keys and the array position as value.
$inverse_sources_array = array_flip($sources_array);
foreach ($sources_array as $sdata) {
$data['elements'][$inverse_sources_array[$sdata]] = $sdata;
$initial_data[$inverse_sources_array[$sdata]] = 0;
}
foreach ($sources_array as $sdata) {
$data['matrix'][$inverse_sources_array[$sdata]] = $initial_data;
}
// Port are situated in a different places from addreses.
$src_key = ($is_ip === true) ? 3 : 5;
$dst_key = ($is_ip === true) ? 4 : 6;
// Store a footprint of initial data to be compared at the end.
$freeze_data = md5(serialize($data));
foreach ($result as $line) {
if (empty($line) === true) {
continue;
}
// Parse the line.
$items = explode(',', $line);
// Get the required data.
$src_item = $inverse_sources_array[$items[$src_key]];
$dst_item = $inverse_sources_array[$items[$dst_key]];
$value = $items[12];
// Check if valid data.
if (!isset($value)
|| !isset($data['matrix'][$dst_item][$src_item])
|| !isset($data['matrix'][$src_item][$dst_item])
) {
continue;
}
// Update the value.
$data['matrix'][$src_item][$dst_item] += (int) $value;
}
// Comparte footprints.
if ($freeze_data === md5(serialize($data))) {
// Taht means that all relationships are 0.
return [];
}
return $data;
}
/**
* Returns the command needed to run nfdump for the given filter.
*
* @param array $filter Netflow filter.
*
* @return string Command to run.
*/
function netflow_get_command($filter)
{
global $config;
// Build command.
$command = io_safe_output($config['netflow_nfdump']).' -N';
// Netflow data path.
if (isset($config['netflow_path']) && $config['netflow_path'] != '') {
$command .= ' -R. -M '.$config['netflow_path'];
}
// Filter options.
$command .= netflow_get_filter_arguments($filter);
return $command;
}
/**
* Returns the nfdump command line arguments that match the given filter.
*
* @param array $filter Netflow filter.
*
* @return string Command line argument string.
*/
function netflow_get_filter_arguments($filter)
{
// Advanced filter.
$filter_args = '';
if ($filter['advanced_filter'] != '') {
$filter_args = preg_replace('/["\r\n]/', '', io_safe_output($filter['advanced_filter']));
return ' "('.$filter_args.')"';
}
if ($filter['router_ip'] != '') {
$filter_args .= ' "(router ip '.$filter['router_ip'].')';
}
// Normal filter.
if ($filter['ip_dst'] != '') {
$filter_args .= ' "(';
$val_ipdst = explode(',', io_safe_output($filter['ip_dst']));
for ($i = 0; $i < count($val_ipdst); $i++) {
if ($i > 0) {
$filter_args .= ' or ';
}
if (netflow_is_net($val_ipdst[$i]) == 0) {
$filter_args .= 'dst ip '.$val_ipdst[$i];
} else {
$filter_args .= 'dst net '.$val_ipdst[$i];
}
}
$filter_args .= ')';
}
if ($filter['ip_src'] != '') {
if ($filter_args == '') {
$filter_args .= ' "(';
} else {
$filter_args .= ' and (';
}
$val_ipsrc = explode(',', io_safe_output($filter['ip_src']));
for ($i = 0; $i < count($val_ipsrc); $i++) {
if ($i > 0) {
$filter_args .= ' or ';
}
if (netflow_is_net($val_ipsrc[$i]) == 0) {
$filter_args .= 'src ip '.$val_ipsrc[$i];
} else {
$filter_args .= 'src net '.$val_ipsrc[$i];
}
}
$filter_args .= ')';
}
if ($filter['dst_port'] != '') {
if ($filter_args == '') {
$filter_args .= ' "(';
} else {
$filter_args .= ' and (';
}
$val_dstport = explode(',', io_safe_output($filter['dst_port']));
for ($i = 0; $i < count($val_dstport); $i++) {
if ($i > 0) {
$filter_args .= ' or ';
}
$filter_args .= 'dst port '.$val_dstport[$i];
}
$filter_args .= ')';
}
if ($filter['src_port'] != '') {
if ($filter_args == '') {
$filter_args .= ' "(';
} else {
$filter_args .= ' and (';
}
$val_srcport = explode(',', io_safe_output($filter['src_port']));
for ($i = 0; $i < count($val_srcport); $i++) {
if ($i > 0) {
$filter_args .= ' or ';
}
$filter_args .= 'src port '.$val_srcport[$i];
}
$filter_args .= ')';
}
if (isset($filter['proto']) && $filter['proto'] != '') {
if ($filter_args == '') {
$filter_args .= ' "(';
} else {
$filter_args .= ' and (';
}
$val_proto = explode(',', io_safe_output($filter['proto']));
for ($i = 0; $i < count($val_proto); $i++) {
if ($i > 0) {
$filter_args .= ' or ';
}
$filter_args .= 'proto '.$val_proto[$i];
}
$filter_args .= ')';
}
if ($filter_args != '') {
$filter_args .= '"';
}
return $filter_args;
}
/**
* Get the types of netflow charts.
*
* @return array of types.
*/
function netflow_get_chart_types()
{
return [
'netflow_area' => __('Area graph'),
'netflow_summary' => __('Summary'),
'netflow_data' => __('Data table'),
'netflow_mesh' => __('Circular mesh'),
'netflow_host_treemap' => __('Host detailed traffic'),
];
}
/**
* Draw a netflow report item.
*
* @param string $start_date Period start date.
* @param string $end_date Period end date.
* @param mixed $interval_length Resolution points or hourly or daily.
* @param string $type Chart type.
* @param array $filter Netflow filter.
* @param integer $max_aggregates Maximum number of aggregates.
* @param string $connection_name Node name when data is get in meta.
* @param string $output Output format. Only HTML, PDF and XML
* are supported.
* @param boolean $address_resolution True to resolve ips to hostnames.
*
* @return string The netflow report in the appropriate format.
*/
function netflow_draw_item(
$start_date,
$end_date,
$interval_length,
$type,
$filter,
$max_aggregates,
$connection_name='',
$output='HTML',
$address_resolution=false
) {
$aggregate = $filter['aggregate'];
$interval = ($end_date - $start_date);
if (defined('METACONSOLE')) {
$width = 950;
} else {
$width = 850;
}
$height = 320;
// Process item.
switch ($type) {
case 'netflow_area':
$data = netflow_get_data(
$start_date,
$end_date,
$interval_length,
$filter,
$aggregate,
$max_aggregates,
false,
$connection_name,
$address_resolution
);
if (empty($data)) {
break;
}
if ($output == 'HTML' || $output == 'PDF') {
$html .= graph_netflow_aggregate_area(
$data,
$interval,
$width,
$height,
($output === 'HTML') ? 1 : 2,
($output === 'HTML'),
$end_date
);
return $html;
} else if ($output == 'XML') {
$xml .= '
'; $html .= netflow_summary_table($data_summary); $html .= ' | '; $html .= ''; $html .= graph_netflow_aggregate_pie( $data_pie, netflow_format_aggregate($aggregate), ($output === 'HTML') ? 1 : 2, ($output === 'HTML') ); $html .= ' | '; $html .= '