462 lines
14 KiB
C
Executable File
462 lines
14 KiB
C
Executable File
/*
|
|
* Copyright (c) 2017, Peter Haag
|
|
* Copyright (c) 2016, Peter Haag
|
|
* Copyright (c) 2014, Peter Haag
|
|
* Copyright (c) 2012, Peter Haag
|
|
*
|
|
* 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 the author 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.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef HAVE_STDINT_H
|
|
#include <stdint.h>
|
|
#endif
|
|
|
|
#ifndef DEVEL
|
|
# define dbg_printf(...) /* printf(__VA_ARGS__) */
|
|
#else
|
|
# define dbg_printf(...) printf(__VA_ARGS__)
|
|
#endif
|
|
|
|
#include "util.h"
|
|
#include "nffile.h"
|
|
#include "nfx.h"
|
|
#include "nfnet.h"
|
|
#include "bookkeeper.h"
|
|
#include "collector.h"
|
|
#include "exporter.h"
|
|
#include "nf_common.h"
|
|
#include "netflow_v1.h"
|
|
#include "netflow_v5_v7.h"
|
|
#include "netflow_v9.h"
|
|
#include "ipfix.h"
|
|
|
|
/* global */
|
|
generic_exporter_t **exporter_list;
|
|
|
|
/* local variables */
|
|
#define MAX_EXPORTERS 65535
|
|
static generic_exporter_t *exporter_root;
|
|
|
|
#include "nffile_inline.c"
|
|
|
|
/* local prototypes */
|
|
|
|
/* functions */
|
|
int InitExporterList(void) {
|
|
|
|
exporter_list = calloc(MAX_EXPORTERS, sizeof(generic_exporter_t *));
|
|
if ( !exporter_list ) {
|
|
LogError("malloc() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
|
|
return 0;
|
|
}
|
|
exporter_root = NULL;
|
|
return 1;
|
|
|
|
} // End of InitExporterList
|
|
|
|
int AddExporterInfo(exporter_info_record_t *exporter_record) {
|
|
uint32_t id = exporter_record->sysid;
|
|
int i;
|
|
char *p1, *p2;
|
|
|
|
// sanity check
|
|
if ( id >= MAX_EXPORTERS ) {
|
|
LogError("Exporter id: %u out of range. Skip exporter", id);
|
|
return 0;
|
|
}
|
|
if ( exporter_list[id] != NULL ) {
|
|
// slot already taken - check if exporters are identical
|
|
exporter_record->sysid = exporter_list[id]->info.sysid;
|
|
if ( memcmp((void *)exporter_record, (void *)&(exporter_list[id]->info), sizeof(exporter_info_record_t)) == 0 ) {
|
|
dbg_printf("Found identical exporter record at SysID: %i, Slot: %u\n", exporter_record->sysid, id);
|
|
// we are done
|
|
return 2;
|
|
} else {
|
|
// exporters not identical - move current slot
|
|
int i;
|
|
// search first emty slot at the top of the list
|
|
for ( i = id+1; i < MAX_EXPORTERS && exporter_list[i] != NULL; i++ ) {;}
|
|
if ( i >= MAX_EXPORTERS ) {
|
|
// all slots taken
|
|
LogError("Too many exporters (>256)\n");
|
|
return 0;
|
|
}
|
|
dbg_printf("Move existing exporter from slot %u, to %i\n", id, i);
|
|
// else - move slot
|
|
exporter_list[i] = exporter_list[id];
|
|
exporter_list[id] = NULL;
|
|
exporter_record->sysid = i;
|
|
}
|
|
}
|
|
|
|
// slot[id] is now available
|
|
exporter_list[id] = (generic_exporter_t *)calloc(1, sizeof(generic_exporter_t));
|
|
if ( !exporter_list[id] ) {
|
|
LogError("malloc() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
|
|
return 0;
|
|
}
|
|
|
|
// SPARC gcc fails here, if we use directly a pointer to the struct.
|
|
// SPARC barfs and core dumps otherwise
|
|
// memcpy((void *)&(exporter_list[id]->info), (void *)exporter_record, sizeof(exporter_info_record_t));
|
|
p1 = (char *)&(exporter_list[id]->info);
|
|
p2 = (char *)exporter_record;
|
|
for ( i=0; i<sizeof(exporter_info_record_t); i++ )
|
|
*p1++ = *p2++;
|
|
|
|
dbg_printf("Insert exporter record in Slot: %i, Sysid: %u\n", id, exporter_record->sysid);
|
|
|
|
#ifdef DEVEL
|
|
{
|
|
#define IP_STRING_LEN 40
|
|
char ipstr[IP_STRING_LEN];
|
|
if ( exporter_record->sa_family == AF_INET ) {
|
|
uint32_t _ip = htonl(exporter_record->ip.v4);
|
|
inet_ntop(AF_INET, &_ip, ipstr, sizeof(ipstr));
|
|
printf("SysID: %u, IP: %16s, version: %u, ID: %2u, Slot: %u\n", exporter_record->sysid,
|
|
ipstr, exporter_record->version, exporter_record->id, id);
|
|
} else if ( exporter_record->sa_family == AF_INET6 ) {
|
|
uint64_t _ip[2];
|
|
_ip[0] = htonll(exporter_record->ip.v6[0]);
|
|
_ip[1] = htonll(exporter_record->ip.v6[1]);
|
|
inet_ntop(AF_INET6, &_ip, ipstr, sizeof(ipstr));
|
|
printf("SysID: %u, IP: %40s, version: %u, ID: %2u, Slot: %u\n", exporter_record->sysid,
|
|
ipstr, exporter_record->version, exporter_record->id, id);
|
|
} else {
|
|
strncpy(ipstr, "<unknown>", IP_STRING_LEN);
|
|
printf("**** Exporter IP version unknown ****\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
|
|
if ( !exporter_root ) {
|
|
exporter_root = exporter_list[id];
|
|
}
|
|
|
|
return 1;
|
|
} // End of AddExporterInfo
|
|
|
|
int AddSamplerInfo(sampler_info_record_t *sampler_record) {
|
|
uint32_t id = sampler_record->exporter_sysid;
|
|
generic_sampler_t **sampler;
|
|
|
|
if ( !exporter_list[id] ) {
|
|
LogError("Exporter SysID: %u not found! - Skip sampler record", id);
|
|
return 0;
|
|
}
|
|
sampler = &exporter_list[id]->sampler;
|
|
while ( *sampler ) {
|
|
if ( memcmp((void *)&(*sampler)->info, (void *)sampler_record, sizeof(sampler_info_record_t)) == 0 ) {
|
|
// Found identical sampler already registered
|
|
dbg_printf("Identical sampler already registered: Exporter SysID: %u, Sampler: id: %i, mode: %u, interval: %u\n",
|
|
sampler_record->exporter_sysid, sampler_record->id, sampler_record->mode, sampler_record->interval);
|
|
return 2;
|
|
}
|
|
sampler = &((*sampler)->next);
|
|
}
|
|
|
|
*sampler = (generic_sampler_t *)malloc(sizeof(generic_sampler_t));
|
|
if ( !*sampler ) {
|
|
LogError("malloc() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
|
|
return 0;
|
|
}
|
|
(*sampler)->next = NULL;
|
|
sampler_record->exporter_sysid = exporter_list[id]->info.sysid;
|
|
|
|
memcpy((void *)&(*sampler)->info, (void *)sampler_record, sizeof(sampler_info_record_t));
|
|
dbg_printf("Insert sampler record for exporter at slot %i:\n", id);
|
|
|
|
#ifdef DEVEL
|
|
{
|
|
if ( sampler_record->id < 0 ) {
|
|
printf("Exporter SysID: %u, Generic Sampler: mode: %u, interval: %u\n",
|
|
sampler_record->exporter_sysid, sampler_record->mode, sampler_record->interval);
|
|
} else {
|
|
printf("Exporter SysID: %u, Sampler: id: %i, mode: %u, interval: %u\n",
|
|
sampler_record->exporter_sysid, sampler_record->id, sampler_record->mode, sampler_record->interval);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
} // End of AddSamplerInfo
|
|
|
|
int AddExporterStat(exporter_stats_record_t *stat_record) {
|
|
int i, use_copy;
|
|
exporter_stats_record_t *rec;
|
|
|
|
// 64bit counters can be potentially unaligned
|
|
if ( ((ptrdiff_t)stat_record & 0x7) != 0 ) {
|
|
rec = malloc(stat_record->header.size);
|
|
if ( !rec ) {
|
|
LogError("malloc() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
|
|
exit(255);
|
|
}
|
|
memcpy(rec, stat_record, stat_record->header.size);
|
|
use_copy = 1;
|
|
} else {
|
|
rec = stat_record;
|
|
use_copy = 0;
|
|
}
|
|
|
|
for (i=0; i<rec->stat_count; i++ ) {
|
|
uint32_t id = rec->stat[i].sysid;
|
|
if ( !exporter_list[id] ) {
|
|
LogError("Exporter SysID: %u not found! - Skip stat record record.\n");
|
|
continue;
|
|
}
|
|
exporter_list[id]->sequence_failure += rec->stat[i].sequence_failure;
|
|
exporter_list[id]->packets += rec->stat[i].packets;
|
|
exporter_list[id]->flows += rec->stat[i].flows;
|
|
dbg_printf("Update exporter stat for SysID: %i: Sequence failures: %u, packets: %llu, flows: %llu\n",
|
|
id, exporter_list[id]->sequence_failure, exporter_list[id]->packets, exporter_list[id]->flows);
|
|
}
|
|
|
|
if ( use_copy )
|
|
free(rec);
|
|
|
|
return 1;
|
|
|
|
} // End of AddExporterStat
|
|
|
|
void ExportExporterList( nffile_t *nffile ) {
|
|
int i;
|
|
|
|
// sysid 0 unused -> no exporter available
|
|
i = 1;
|
|
while ( i < MAX_EXPORTERS && exporter_list[i] != NULL ) {
|
|
exporter_info_record_t *exporter;
|
|
generic_sampler_t *sampler;
|
|
|
|
exporter = &exporter_list[i]->info;
|
|
AppendToBuffer(nffile, (void *)exporter, exporter->header.size);
|
|
|
|
sampler = exporter_list[i]->sampler;
|
|
while ( sampler ) {
|
|
AppendToBuffer(nffile, (void *)&(sampler->info), sampler->info.header.size);
|
|
sampler = sampler->next;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
|
|
} // End of ExportExporterList
|
|
|
|
void PrintExporters(char *filename) {
|
|
int i, done, found = 0;
|
|
nffile_t *nffile;
|
|
record_header_t *record;
|
|
uint32_t skipped_blocks;
|
|
uint64_t total_bytes;
|
|
|
|
printf("Exporters:\n");
|
|
|
|
nffile = OpenFile(filename, NULL);
|
|
if ( !nffile ) {
|
|
return;
|
|
}
|
|
|
|
total_bytes = 0;
|
|
skipped_blocks = 0;
|
|
done = 0;
|
|
while ( !done ) {
|
|
int i, ret;
|
|
|
|
// get next data block from file
|
|
ret = ReadBlock(nffile);
|
|
|
|
switch (ret) {
|
|
case NF_CORRUPT:
|
|
case NF_ERROR:
|
|
if ( ret == NF_CORRUPT )
|
|
LogError("Corrupt data file '%s': '%s'\n",filename);
|
|
else
|
|
LogError("Read error in file '%s': %s\n",filename, strerror(errno) );
|
|
done = 1;
|
|
continue;
|
|
break;
|
|
// fall through - get next file in chain
|
|
case NF_EOF:
|
|
done = 1;
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
// successfully read block
|
|
total_bytes += ret;
|
|
}
|
|
|
|
if ( nffile->block_header->id != DATA_BLOCK_TYPE_2 ) {
|
|
skipped_blocks++;
|
|
continue;
|
|
}
|
|
|
|
// block type = 2
|
|
|
|
record = (record_header_t *)nffile->buff_ptr;
|
|
|
|
for ( i=0; i < nffile->block_header->NumRecords; i++ ) {
|
|
switch ( record->type ) {
|
|
// ExporterRecordType and SamplerRecordype tc versions only
|
|
case ExporterRecordType: {
|
|
#define IP_STRING_LEN 40
|
|
char ipstr[IP_STRING_LEN];
|
|
exporter_record_t *exporter_record = (exporter_record_t *)record ;
|
|
found = 1;
|
|
printf("\n");
|
|
if ( exporter_record->sa_family == AF_INET ) {
|
|
uint32_t _ip = htonl(exporter_record->ip.v4);
|
|
inet_ntop(AF_INET, &_ip, ipstr, sizeof(ipstr));
|
|
printf("SysID: %u, IP: %16s, version: %u, ID: %2u, Sequence Failures: %u\n", exporter_record->sysid,
|
|
ipstr, exporter_record->version, exporter_record->exporter_id, exporter_record->sequence_failure);
|
|
} else if ( exporter_record->sa_family == AF_INET6 ) {
|
|
uint64_t _ip[2];
|
|
_ip[0] = htonll(exporter_record->ip.v6[0]);
|
|
_ip[1] = htonll(exporter_record->ip.v6[1]);
|
|
inet_ntop(AF_INET6, &_ip, ipstr, sizeof(ipstr));
|
|
printf("SysID: %u, IP: %40s, version: %u, ID: %2u, Sequence Failures: %u\n", exporter_record->sysid,
|
|
ipstr, exporter_record->version, exporter_record->exporter_id, exporter_record->sequence_failure);
|
|
} else {
|
|
strncpy(ipstr, "<unknown>", IP_STRING_LEN);
|
|
printf("**** Exporter IP version unknown ****\n");
|
|
}
|
|
} break;
|
|
case SamplerRecordype: {
|
|
sampler_record_t *sampler_record = (sampler_record_t *)record;;
|
|
if ( sampler_record->id < 0 ) {
|
|
printf(" Generic Sampler: mode: %u, interval: %u\n",
|
|
sampler_record->mode, sampler_record->interval);
|
|
} else {
|
|
printf(" Sampler: id: %i, mode: %u, interval: %u\n",
|
|
sampler_record->id, sampler_record->mode, sampler_record->interval);
|
|
}
|
|
} break;
|
|
case ExporterInfoRecordType:
|
|
found = 1;
|
|
if ( !AddExporterInfo((exporter_info_record_t *)record) ) {
|
|
LogError("Failed to add Exporter Record\n");
|
|
}
|
|
break;
|
|
case ExporterStatRecordType:
|
|
AddExporterStat((exporter_stats_record_t *)record);
|
|
break;
|
|
case SamplerInfoRecordype:
|
|
if ( !AddSamplerInfo((sampler_info_record_t *)record) ) {
|
|
LogError("Failed to add Sampler Record\n");
|
|
}
|
|
break;
|
|
}
|
|
// Advance pointer by number of bytes for netflow record
|
|
record = (record_header_t *)((pointer_addr_t)record + record->size);
|
|
}
|
|
}
|
|
|
|
CloseFile(nffile);
|
|
DisposeFile(nffile);
|
|
if ( !found ) {
|
|
printf("No Exporter records found\n");
|
|
}
|
|
|
|
i = 1;
|
|
while ( i < MAX_EXPORTERS && exporter_list[i] != NULL ) {
|
|
#define IP_STRING_LEN 40
|
|
char ipstr[IP_STRING_LEN];
|
|
|
|
exporter_info_record_t *exporter;
|
|
generic_sampler_t *sampler;
|
|
|
|
printf("\n");
|
|
exporter = &exporter_list[i]->info;
|
|
if ( exporter->sa_family == AF_INET ) {
|
|
uint32_t _ip = htonl(exporter->ip.v4);
|
|
inet_ntop(AF_INET, &_ip, ipstr, sizeof(ipstr));
|
|
if ( exporter_list[i]->flows )
|
|
printf("SysID: %u, IP: %16s, version: %u, ID: %2u, Sequence failures: %u, packets: %llu, flows: %llu\n",
|
|
exporter->sysid, ipstr, exporter->version, exporter->id,
|
|
exporter_list[i]->sequence_failure,
|
|
(long long unsigned)exporter_list[i]->packets,
|
|
(long long unsigned)exporter_list[i]->flows);
|
|
else
|
|
printf("SysID: %u, IP: %16s, version: %u, ID: %2u\n",
|
|
exporter->sysid, ipstr, exporter->version, exporter->id);
|
|
|
|
} else if ( exporter->sa_family == AF_INET6 ) {
|
|
uint64_t _ip[2];
|
|
_ip[0] = htonll(exporter->ip.v6[0]);
|
|
_ip[1] = htonll(exporter->ip.v6[1]);
|
|
inet_ntop(AF_INET6, &_ip, ipstr, sizeof(ipstr));
|
|
if ( exporter_list[i]->flows )
|
|
printf("SysID: %u, IP: %40s, version: %u, ID: %2u, Sequence failures: %u, packets: %llu, flows: %llu\n ",
|
|
exporter->sysid, ipstr, exporter->version, exporter->id,
|
|
exporter_list[i]->sequence_failure,
|
|
(long long unsigned)exporter_list[i]->packets,
|
|
(long long unsigned)exporter_list[i]->flows);
|
|
else
|
|
printf("SysID: %u, IP: %40s, version: %u, ID: %2u\n ",
|
|
exporter->sysid, ipstr, exporter->version, exporter->id);
|
|
} else {
|
|
strncpy(ipstr, "<unknown>", IP_STRING_LEN);
|
|
printf("**** Exporter IP version unknown ****\n");
|
|
}
|
|
|
|
sampler = exporter_list[i]->sampler;
|
|
while ( sampler ) {
|
|
if ( sampler->info.id < 0 ) {
|
|
printf(" Sampler for Exporter SysID: %u, Generic Sampler: mode: %u, interval: %u\n",
|
|
sampler->info.exporter_sysid, sampler->info.mode, sampler->info.interval);
|
|
} else {
|
|
printf(" Sampler for Exporter SysID: %u, Sampler: id: %i, mode: %u, interval: %u\n",
|
|
sampler->info.exporter_sysid, sampler->info.id, sampler->info.mode, sampler->info.interval);
|
|
}
|
|
sampler = sampler->next;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
|
|
} // End of PrintExporters
|
|
|