/*
* Copyright (c) 2019-2020, Peter Haag
* 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 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.
*
*/
#include "config.h"
#include <stdio.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include "util.h"
#include "nfdump.h"
#include "nffile.h"
#include "nfx.h"
#include "output_util.h"
#include "output_csv.h"
#define STRINGSIZE 10240
#define IP_STRING_LEN (INET6_ADDRSTRLEN)
static char data_string[STRINGSIZE];
// record counter
static uint32_t recordCount;
void csv_prolog(void) {
recordCount = 0;
memset(data_string, 0, STRINGSIZE);
printf("ts,te,td,sa,da,sp,dp,pr,flg,fwd,stos,ipkt,ibyt,opkt,obyt,in,out,sas,das,smk,dmk,dtos,dir,nh,nhb,svln,dvln,ismc,odmc,idmc,osmc,mpls1,mpls2,mpls3,mpls4,mpls5,mpls6,mpls7,mpls8,mpls9,mpls10,cl,sl,al,ra,eng,exid,tr\n");
} // End of csv_prolog
void csv_epilog(void) {
} // End of csv_epilog
void flow_record_to_csv(void *record, char ** s, int tag) {
char *_s, as[IP_STRING_LEN], ds[IP_STRING_LEN];
char datestr1[64], datestr2[64], datestr3[64];
char s_snet[IP_STRING_LEN], s_dnet[IP_STRING_LEN];
ssize_t slen, _slen;
time_t when;
struct tm *ts;
master_record_t *r = (master_record_t *)record;
as[0] = 0;
ds[0] = 0;
if ( (r->flags & FLAG_IPV6_ADDR ) != 0 ) { // IPv6
uint64_t snet[2];
uint64_t dnet[2];
snet[0] = htonll(r->V6.srcaddr[0]);
snet[1] = htonll(r->V6.srcaddr[1]);
dnet[0] = htonll(r->V6.dstaddr[0]);
dnet[1] = htonll(r->V6.dstaddr[1]);
inet_ntop(AF_INET6, snet, as, sizeof(as));
inet_ntop(AF_INET6, dnet, ds, sizeof(ds));
inet6_ntop_mask(r->V6.srcaddr, r->src_mask, s_snet, sizeof(s_snet));
inet6_ntop_mask(r->V6.dstaddr, r->dst_mask, s_dnet, sizeof(s_dnet));
} else { // IPv4
uint32_t snet, dnet;
snet = htonl(r->V4.srcaddr);
dnet = htonl(r->V4.dstaddr);
inet_ntop(AF_INET, &snet, as, sizeof(as));
inet_ntop(AF_INET, &dnet, ds, sizeof(ds));
inet_ntop_mask(r->V4.srcaddr, r->src_mask, s_snet, sizeof(s_snet));
inet_ntop_mask(r->V4.dstaddr, r->dst_mask, s_dnet, sizeof(s_dnet));
}
as[IP_STRING_LEN-1] = 0;
ds[IP_STRING_LEN-1] = 0;
when = r->first;
ts = localtime(&when);
strftime(datestr1, 63, "%Y-%m-%d %H:%M:%S", ts);
when = r->last;
ts = localtime(&when);
strftime(datestr2, 63, "%Y-%m-%d %H:%M:%S", ts);
double duration = r->last - r->first;
duration += ((double)r->msec_last - (double)r->msec_first) / 1000.0;
_s = data_string;
slen = STRINGSIZE;
snprintf(_s, slen-1, "%s,%s,%.3f,%s,%s,%u,%u,%s,%s,%u,%u,%llu,%llu,%llu,%llu",
datestr1, datestr2, duration, as,ds,r->srcport, r->dstport, ProtoString(r->prot, 0),
FlagsString(r->tcp_flags), r->fwd_status, r->tos, (unsigned long long)r->dPkts,
(unsigned long long)r->dOctets, (long long unsigned)r->out_pkts, (long long unsigned)r->out_bytes);
_slen = strlen(data_string);
_s += _slen;
slen -= _slen;
// EX_IO_SNMP_2:
// EX_IO_SNMP_4:
snprintf(_s, slen-1, ",%u,%u" , r->input, r->output);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
// EX_AS_2:
// EX_AS_4:
snprintf(_s, slen-1, ",%u,%u", r->srcas, r->dstas);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
// EX_MULIPLE:
snprintf(_s, slen-1, ",%u,%u,%u,%u" , r->src_mask, r->dst_mask, r->dst_tos, r->dir );
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
if ( (r->flags & FLAG_IPV6_NH ) != 0 ) { // IPv6
// EX_NEXT_HOP_v6:
as[0] = 0;
r->ip_nexthop.V6[0] = htonll(r->ip_nexthop.V6[0]);
r->ip_nexthop.V6[1] = htonll(r->ip_nexthop.V6[1]);
inet_ntop(AF_INET6, r->ip_nexthop.V6, as, sizeof(as));
as[IP_STRING_LEN-1] = 0;
snprintf(_s, slen-1, ",%s", as);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
} else {
// EX_NEXT_HOP_v4:
as[0] = 0;
r->ip_nexthop.V4 = htonl(r->ip_nexthop.V4);
inet_ntop(AF_INET, &r->ip_nexthop.V4, as, sizeof(as));
as[IP_STRING_LEN-1] = 0;
snprintf(_s, slen-1, ",%s", as);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
}
if ( (r->flags & FLAG_IPV6_NHB ) != 0 ) { // IPv6
// EX_NEXT_HOP_BGP_v6:
as[0] = 0;
r->bgp_nexthop.V6[0] = htonll(r->bgp_nexthop.V6[0]);
r->bgp_nexthop.V6[1] = htonll(r->bgp_nexthop.V6[1]);
inet_ntop(AF_INET6, r->ip_nexthop.V6, as, sizeof(as));
as[IP_STRING_LEN-1] = 0;
snprintf(_s, slen-1, ",%s", as);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
} else {
// EX_NEXT_HOP_BGP_v4:
as[0] = 0;
r->bgp_nexthop.V4 = htonl(r->bgp_nexthop.V4);
inet_ntop(AF_INET, &r->bgp_nexthop.V4, as, sizeof(as));
as[IP_STRING_LEN-1] = 0;
snprintf(_s, slen-1, ",%s", as);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
}
// EX_VLAN:
snprintf(_s, slen-1, ",%u,%u", r->src_vlan, r->dst_vlan);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
/* already in default output:
EX_OUT_PKG_4:
EX_OUT_PKG_8:
EX_OUT_BYTES_4:
EX_OUT_BYTES_8:
*/
// case EX_MAC_1:
{
int i;
uint8_t mac1[6], mac2[6];
for ( i=0; i<6; i++ ) {
mac1[i] = (r->in_src_mac >> ( i*8 )) & 0xFF;
}
for ( i=0; i<6; i++ ) {
mac2[i] = (r->out_dst_mac >> ( i*8 )) & 0xFF;
}
snprintf(_s, slen-1, ",%.2x:%.2x:%.2x:%.2x:%.2x:%.2x,%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
mac1[5], mac1[4], mac1[3], mac1[2], mac1[1], mac1[0],
mac2[5], mac2[4], mac2[3], mac2[2], mac2[1], mac2[0] );
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
}
// EX_MAC_2:
{
int i;
uint8_t mac1[6], mac2[6];
for ( i=0; i<6; i++ ) {
mac1[i] = (r->in_dst_mac >> ( i*8 )) & 0xFF;
}
for ( i=0; i<6; i++ ) {
mac2[i] = (r->out_src_mac >> ( i*8 )) & 0xFF;
}
snprintf(_s, slen-1, ",%.2x:%.2x:%.2x:%.2x:%.2x:%.2x,%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
mac1[5], mac1[4], mac1[3], mac1[2], mac1[1], mac1[0],
mac2[5], mac2[4], mac2[3], mac2[2], mac2[1], mac2[0] );
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
}
// EX_MPLS:
{
unsigned int i;
for ( i=0; i<10; i++ ) {
snprintf(_s, slen-1, ",%u-%1u-%1u",
r->mpls_label[i] >> 4 , (r->mpls_label[i] & 0xF ) >> 1, r->mpls_label[i] & 1 );
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
}
}
{
double f1, f2, f3;
f1 = (double)r->client_nw_delay_usec / 1000.0;
f2 = (double)r->server_nw_delay_usec / 1000.0;
f3 = (double)r->appl_latency_usec / 1000.0;
snprintf(_s, slen-1,
",%9.3f,%9.3f,%9.3f", f1, f2, f3);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
}
// EX_ROUTER_IP_v4:
if ( (r->flags & FLAG_IPV6_EXP ) != 0 ) { // IPv6
// EX_NEXT_HOP_v6:
as[0] = 0;
r->ip_router.V6[0] = htonll(r->ip_router.V6[0]);
r->ip_router.V6[1] = htonll(r->ip_router.V6[1]);
inet_ntop(AF_INET6, r->ip_router.V6, as, sizeof(as));
as[IP_STRING_LEN-1] = 0;
snprintf(_s, slen-1, ",%s", as);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
} else {
// EX_NEXT_HOP_v4:
as[0] = 0;
r->ip_router.V4 = htonl(r->ip_router.V4);
inet_ntop(AF_INET, &r->ip_router.V4, as, sizeof(as));
as[IP_STRING_LEN-1] = 0;
snprintf(_s, slen-1, ",%s", as);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
}
// EX_ROUTER_ID
snprintf(_s, slen-1, ",%u/%u", r->engine_type, r->engine_id);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
// Exporter SysID
snprintf(_s, slen-1, ",%u", r->exporter_sysid);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
// Date flow received
when = r->received / 1000LL;
ts = localtime(&when);
strftime(datestr3, 63, ",%Y-%m-%d %H:%M:%S", ts);
snprintf(_s, slen-1, "%s.%03llu", datestr3, (long long unsigned)r->received % 1000LL);
_slen = strlen(data_string);
_s = data_string + _slen;
slen = STRINGSIZE - _slen;
// snprintf(_s, slen-1, "\n");
data_string[STRINGSIZE-1] = 0;
*s = data_string;
} // End of flow_record_to_csv