1 /*
2  *  Copyright (c) 2019-2020, Peter Haag
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *
8  *   * Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *   * Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *   * Neither the name of the author nor the names of its contributors may be
14  *     used to endorse or promote products derived from this software without
15  *     specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  *  POSSIBILITY OF SUCH DAMAGE.
28  *
29  */
30 
31 #include "config.h"
32 
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <time.h>
40 #include <string.h>
41 
42 #ifdef HAVE_STDINT_H
43 #include <stdint.h>
44 #endif
45 
46 #include "util.h"
47 #include "nfdump.h"
48 #include "nffile.h"
49 #include "nfx.h"
50 #include "output_util.h"
51 #include "output_csv.h"
52 
53 #define STRINGSIZE 10240
54 #define IP_STRING_LEN (INET6_ADDRSTRLEN)
55 
56 static char data_string[STRINGSIZE];
57 
58 // record counter
59 static uint32_t recordCount;
60 
csv_prolog(void)61 void csv_prolog(void) {
62 	recordCount = 0;
63 	memset(data_string, 0, STRINGSIZE);
64 
65 	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");
66 
67 } // End of csv_prolog
68 
csv_epilog(void)69 void csv_epilog(void) {
70 
71 } // End of csv_epilog
72 
flow_record_to_csv(void * record,char ** s,int tag)73 void flow_record_to_csv(void *record, char ** s, int tag) {
74 char 		*_s, as[IP_STRING_LEN], ds[IP_STRING_LEN];
75 char		datestr1[64], datestr2[64], datestr3[64];
76 char		s_snet[IP_STRING_LEN], s_dnet[IP_STRING_LEN];
77 ssize_t		slen, _slen;
78 time_t		when;
79 struct tm 	*ts;
80 master_record_t *r = (master_record_t *)record;
81 
82 	as[0] = 0;
83 	ds[0] = 0;
84 	if ( (r->flags & FLAG_IPV6_ADDR ) != 0 ) { // IPv6
85 		uint64_t snet[2];
86 		uint64_t dnet[2];
87 
88 		snet[0] = htonll(r->V6.srcaddr[0]);
89 		snet[1] = htonll(r->V6.srcaddr[1]);
90 		dnet[0] = htonll(r->V6.dstaddr[0]);
91 		dnet[1] = htonll(r->V6.dstaddr[1]);
92 		inet_ntop(AF_INET6, snet, as, sizeof(as));
93 		inet_ntop(AF_INET6, dnet, ds, sizeof(ds));
94 
95 		inet6_ntop_mask(r->V6.srcaddr, r->src_mask, s_snet, sizeof(s_snet));
96 		inet6_ntop_mask(r->V6.dstaddr, r->dst_mask, s_dnet, sizeof(s_dnet));
97 
98 	} else {	// IPv4
99 		uint32_t snet, dnet;
100 		snet = htonl(r->V4.srcaddr);
101 		dnet = htonl(r->V4.dstaddr);
102 		inet_ntop(AF_INET, &snet, as, sizeof(as));
103 		inet_ntop(AF_INET, &dnet, ds, sizeof(ds));
104 
105 		inet_ntop_mask(r->V4.srcaddr, r->src_mask, s_snet, sizeof(s_snet));
106 		inet_ntop_mask(r->V4.dstaddr, r->dst_mask, s_dnet, sizeof(s_dnet));
107 
108 	}
109 	as[IP_STRING_LEN-1] = 0;
110 	ds[IP_STRING_LEN-1] = 0;
111 
112 	when = r->first;
113 	ts = localtime(&when);
114 	strftime(datestr1, 63, "%Y-%m-%d %H:%M:%S", ts);
115 
116 	when = r->last;
117 	ts = localtime(&when);
118 	strftime(datestr2, 63, "%Y-%m-%d %H:%M:%S", ts);
119 
120 	double duration = r->last - r->first;
121 	duration += ((double)r->msec_last - (double)r->msec_first) / 1000.0;
122 
123 	_s = data_string;
124 	slen = STRINGSIZE;
125 	snprintf(_s, slen-1, "%s,%s,%.3f,%s,%s,%u,%u,%s,%s,%u,%u,%llu,%llu,%llu,%llu",
126 		datestr1, datestr2, duration, as,ds,r->srcport, r->dstport, ProtoString(r->prot, 0),
127 		FlagsString(r->tcp_flags), r->fwd_status, r->tos, (unsigned long long)r->dPkts,
128 		(unsigned long long)r->dOctets, (long long unsigned)r->out_pkts, (long long unsigned)r->out_bytes);
129 
130 	_slen = strlen(data_string);
131 	_s += _slen;
132 	slen -= _slen;
133 
134 	// EX_IO_SNMP_2:
135 	// EX_IO_SNMP_4:
136 	snprintf(_s, slen-1, ",%u,%u" , r->input, r->output);
137 		_slen = strlen(data_string);
138 		_s = data_string + _slen;
139 		slen = STRINGSIZE - _slen;
140 
141 	// EX_AS_2:
142 	// EX_AS_4:
143 	snprintf(_s, slen-1, ",%u,%u", r->srcas, r->dstas);
144 		_slen = strlen(data_string);
145 		_s = data_string + _slen;
146 		slen = STRINGSIZE - _slen;
147 
148 	// EX_MULIPLE:
149 	snprintf(_s, slen-1, ",%u,%u,%u,%u" , r->src_mask, r->dst_mask, r->dst_tos, r->dir );
150 		_slen = strlen(data_string);
151 		_s = data_string + _slen;
152 		slen = STRINGSIZE - _slen;
153 
154 	if ( (r->flags & FLAG_IPV6_NH ) != 0 ) { // IPv6
155 		// EX_NEXT_HOP_v6:
156 		as[0] = 0;
157 		r->ip_nexthop.V6[0] = htonll(r->ip_nexthop.V6[0]);
158 		r->ip_nexthop.V6[1] = htonll(r->ip_nexthop.V6[1]);
159 		inet_ntop(AF_INET6, r->ip_nexthop.V6, as, sizeof(as));
160 		as[IP_STRING_LEN-1] = 0;
161 
162 		snprintf(_s, slen-1, ",%s", as);
163 		_slen = strlen(data_string);
164 		_s = data_string + _slen;
165 		slen = STRINGSIZE - _slen;
166 
167 	} else {
168 		// EX_NEXT_HOP_v4:
169 		as[0] = 0;
170 		r->ip_nexthop.V4 = htonl(r->ip_nexthop.V4);
171 		inet_ntop(AF_INET, &r->ip_nexthop.V4, as, sizeof(as));
172 		as[IP_STRING_LEN-1] = 0;
173 
174 		snprintf(_s, slen-1, ",%s", as);
175 		_slen = strlen(data_string);
176 		_s = data_string + _slen;
177 		slen = STRINGSIZE - _slen;
178 	}
179 
180 	if ( (r->flags & FLAG_IPV6_NHB ) != 0 ) { // IPv6
181 		// EX_NEXT_HOP_BGP_v6:
182 		as[0] = 0;
183 		r->bgp_nexthop.V6[0] = htonll(r->bgp_nexthop.V6[0]);
184 		r->bgp_nexthop.V6[1] = htonll(r->bgp_nexthop.V6[1]);
185 		inet_ntop(AF_INET6, r->ip_nexthop.V6, as, sizeof(as));
186 		as[IP_STRING_LEN-1] = 0;
187 
188 		snprintf(_s, slen-1, ",%s", as);
189 		_slen = strlen(data_string);
190 		_s = data_string + _slen;
191 		slen = STRINGSIZE - _slen;
192 
193 	} else {
194 		// 	EX_NEXT_HOP_BGP_v4:
195 		as[0] = 0;
196 		r->bgp_nexthop.V4 = htonl(r->bgp_nexthop.V4);
197 		inet_ntop(AF_INET, &r->bgp_nexthop.V4, as, sizeof(as));
198 		as[IP_STRING_LEN-1] = 0;
199 
200 		snprintf(_s, slen-1, ",%s", as);
201 		_slen = strlen(data_string);
202 		_s = data_string + _slen;
203 		slen = STRINGSIZE - _slen;
204 
205 	}
206 
207 	// EX_VLAN:
208 	snprintf(_s, slen-1, ",%u,%u", r->src_vlan, r->dst_vlan);
209 	_slen = strlen(data_string);
210 	_s = data_string + _slen;
211 	slen = STRINGSIZE - _slen;
212 
213 
214 	/* already in default output:
215 	EX_OUT_PKG_4:
216 	EX_OUT_PKG_8:
217 	EX_OUT_BYTES_4:
218 	EX_OUT_BYTES_8:
219 	*/
220 
221 	// case EX_MAC_1:
222 	{
223 		int i;
224 		uint8_t mac1[6], mac2[6];
225 
226 		for ( i=0; i<6; i++ ) {
227 			mac1[i] = (r->in_src_mac >> ( i*8 )) & 0xFF;
228 		}
229 		for ( i=0; i<6; i++ ) {
230 			mac2[i] = (r->out_dst_mac >> ( i*8 )) & 0xFF;
231 		}
232 
233 		snprintf(_s, slen-1, ",%.2x:%.2x:%.2x:%.2x:%.2x:%.2x,%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
234 			mac1[5], mac1[4], mac1[3], mac1[2], mac1[1], mac1[0],
235 			mac2[5], mac2[4], mac2[3], mac2[2], mac2[1], mac2[0] );
236 		_slen = strlen(data_string);
237 		_s = data_string + _slen;
238 		slen = STRINGSIZE - _slen;
239 	}
240 
241 	// EX_MAC_2:
242 	{
243 		int i;
244 		uint8_t mac1[6], mac2[6];
245 
246 		for ( i=0; i<6; i++ ) {
247 			mac1[i] = (r->in_dst_mac >> ( i*8 )) & 0xFF;
248 		}
249 		for ( i=0; i<6; i++ ) {
250 			mac2[i] = (r->out_src_mac >> ( i*8 )) & 0xFF;
251 		}
252 
253 		snprintf(_s, slen-1, ",%.2x:%.2x:%.2x:%.2x:%.2x:%.2x,%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
254 			mac1[5], mac1[4], mac1[3], mac1[2], mac1[1], mac1[0],
255 			mac2[5], mac2[4], mac2[3], mac2[2], mac2[1], mac2[0] );
256 		_slen = strlen(data_string);
257 		_s = data_string + _slen;
258 		slen = STRINGSIZE - _slen;
259 	}
260 
261 	// EX_MPLS:
262 	{
263 		unsigned int i;
264 		for ( i=0; i<10; i++ ) {
265 			snprintf(_s, slen-1, ",%u-%1u-%1u",
266 				r->mpls_label[i] >> 4 , (r->mpls_label[i] & 0xF ) >> 1, r->mpls_label[i] & 1 );
267 			_slen = strlen(data_string);
268 			_s = data_string + _slen;
269 			slen = STRINGSIZE - _slen;
270 		}
271 	}
272 
273 	{
274 		double f1, f2, f3;
275 		f1 = (double)r->client_nw_delay_usec / 1000.0;
276 		f2 = (double)r->server_nw_delay_usec / 1000.0;
277 		f3 = (double)r->appl_latency_usec / 1000.0;
278 
279 				snprintf(_s, slen-1,
280 ",%9.3f,%9.3f,%9.3f", f1, f2, f3);
281 
282 		_slen = strlen(data_string);
283 		_s = data_string + _slen;
284 		slen = STRINGSIZE - _slen;
285 	}
286 
287 
288 	// EX_ROUTER_IP_v4:
289 	if ( (r->flags & FLAG_IPV6_EXP ) != 0 ) { // IPv6
290 		// EX_NEXT_HOP_v6:
291 		as[0] = 0;
292 		r->ip_router.V6[0] = htonll(r->ip_router.V6[0]);
293 		r->ip_router.V6[1] = htonll(r->ip_router.V6[1]);
294 		inet_ntop(AF_INET6, r->ip_router.V6, as, sizeof(as));
295 		as[IP_STRING_LEN-1] = 0;
296 
297 		snprintf(_s, slen-1, ",%s", as);
298 		_slen = strlen(data_string);
299 		_s = data_string + _slen;
300 		slen = STRINGSIZE - _slen;
301 
302 	} else {
303 		// EX_NEXT_HOP_v4:
304 		as[0] = 0;
305 		r->ip_router.V4 = htonl(r->ip_router.V4);
306 		inet_ntop(AF_INET, &r->ip_router.V4, as, sizeof(as));
307 		as[IP_STRING_LEN-1] = 0;
308 
309 		snprintf(_s, slen-1, ",%s", as);
310 		_slen = strlen(data_string);
311 		_s = data_string + _slen;
312 		slen = STRINGSIZE - _slen;
313 	}
314 
315 	// EX_ROUTER_ID
316 	snprintf(_s, slen-1, ",%u/%u", r->engine_type, r->engine_id);
317 		_slen = strlen(data_string);
318 		_s = data_string + _slen;
319 		slen = STRINGSIZE - _slen;
320 
321 	// Exporter SysID
322 	snprintf(_s, slen-1, ",%u", r->exporter_sysid);
323 		_slen = strlen(data_string);
324 		_s = data_string + _slen;
325 		slen = STRINGSIZE - _slen;
326 
327 	// Date flow received
328 	when = r->received / 1000LL;
329  	ts = localtime(&when);
330  	strftime(datestr3, 63, ",%Y-%m-%d %H:%M:%S", ts);
331 
332  	snprintf(_s, slen-1, "%s.%03llu", datestr3, (long long unsigned)r->received % 1000LL);
333  	        _slen = strlen(data_string);
334  	        _s = data_string + _slen;
335  	        slen = STRINGSIZE - _slen;
336 
337 	// snprintf(_s, slen-1, "\n");
338 	data_string[STRINGSIZE-1] = 0;
339 	*s = data_string;
340 
341 } // End of flow_record_to_csv
342