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 // for asprintf prototype
34 #define _GNU_SOURCE
35 
36 #include <stdio.h>
37 #include <stddef.h>
38 #include <stdlib.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <time.h>
44 #include <string.h>
45 
46 #ifdef HAVE_STDINT_H
47 #include <stdint.h>
48 #endif
49 
50 #include "util.h"
51 #include "nfdump.h"
52 #include "nffile.h"
53 #include "nfx.h"
54 #include "output_util.h"
55 #include "output_json.h"
56 
57 #define STRINGSIZE 10240
58 #define IP_STRING_LEN (INET6_ADDRSTRLEN)
59 
60 static char data_string[STRINGSIZE];
61 
62 // record counter
63 static uint32_t recordCount;
64 
String_Flags(master_record_t * r,char * string)65 static void String_Flags(master_record_t *r, char *string) {
66 
67 	string[0] = r->tcp_flags & 128 ? 'C' : '.';	// Congestion window reduced -  CWR
68 	string[1] = r->tcp_flags &  64 ? 'E' : '.';	// ECN-Echo
69 	string[2] = r->tcp_flags &  32 ? 'U' : '.';	// Urgent
70 	string[3] = r->tcp_flags &  16 ? 'A' : '.';	// Ack
71 	string[4] = r->tcp_flags &   8 ? 'P' : '.';	// Push
72 	string[5] = r->tcp_flags &   4 ? 'R' : '.';	// Reset
73 	string[6] = r->tcp_flags &   2 ? 'S' : '.';	// Syn
74 	string[7] = r->tcp_flags &   1 ? 'F' : '.';	// Fin
75 	string[8] = '\0';
76 
77 } // End of String_Flags
78 
json_prolog(void)79 void json_prolog(void) {
80 	recordCount = 0;
81 	memset(data_string, 0, STRINGSIZE);
82 	printf("[\n");
83 } // End of json_prolog
84 
json_epilog(void)85 void json_epilog(void) {
86 	printf("]\n");
87 } // End of json_epilog
88 
89 
flow_record_to_json(void * record,char ** s,int tag)90 void flow_record_to_json(void *record, char ** s, int tag) {
91 char 		*_s, as[IP_STRING_LEN], ds[IP_STRING_LEN], *datestr1, *datestr2, datebuff[64], flags_str[16];
92 int			i, id;
93 ssize_t		slen, _slen;
94 time_t		when;
95 struct tm 	*ts;
96 master_record_t *r = (master_record_t *)record;
97 extension_map_t	*extension_map = r->map_ref;
98 
99 	when = r->first;
100 	ts = localtime(&when);
101 	strftime(datebuff, 63, "%Y-%m-%dT%H:%M:%S", ts);
102 	asprintf(&datestr1, "%s.%u", datebuff, r->msec_first);
103 
104 	when = r->last;
105 	ts = localtime(&when);
106 	strftime(datebuff, 63, "%Y-%m-%dT%H:%M:%S", ts);
107 	asprintf(&datestr2, "%s.%u", datebuff, r->msec_last);
108 
109 	String_Flags(record, flags_str);
110 
111 	if ( recordCount ) {
112 		strncpy(data_string, ",\n", STRINGSIZE-1);
113 	}
114 	recordCount++;
115 
116 	_slen = strlen(data_string);
117 	_s = data_string + _slen;
118 	slen = STRINGSIZE - _slen;
119 	snprintf(_s, slen-1, "{\n"
120 "	\"type\" : \"%s\",\n"
121 "	\"sampled\" : %u,\n"
122 "	\"export_sysid\" : %u,\n"
123 "	\"t_first\" : \"%s\",\n"
124 "	\"t_last\" : \"%s\",\n"
125 "	\"proto\" : %u,\n"
126 , TestFlag(r->flags, FLAG_EVENT) ? "EVENT" : "FLOW",
127 	TestFlag(r->flags, FLAG_SAMPLED) ? 1 : 0,
128 	r->exporter_sysid, datestr1, datestr2, r->prot);
129 
130 	free(datestr1);
131 	free(datestr2);
132 	_slen = strlen(data_string);
133 	_s = data_string + _slen;
134 	slen = STRINGSIZE - _slen;
135 
136 	as[0] = 0;
137 	ds[0] = 0;
138 	if ( TestFlag(r->flags,FLAG_IPV6_ADDR ) != 0 ) { // IPv6
139 		uint64_t _src[2];
140 		uint64_t _dst[2];
141 
142 		_src[0] = htonll(r->V6.srcaddr[0]);
143 		_src[1] = htonll(r->V6.srcaddr[1]);
144 		_dst[0] = htonll(r->V6.dstaddr[0]);
145 		_dst[1] = htonll(r->V6.dstaddr[1]);
146 		inet_ntop(AF_INET6, _src, as, sizeof(as));
147 		inet_ntop(AF_INET6, _dst, ds, sizeof(ds));
148 		as[IP_STRING_LEN-1] = 0;
149 		ds[IP_STRING_LEN-1] = 0;
150 
151 		snprintf(_s, slen-1,
152 "	\"src6_addr\" : \"%s\",\n"
153 "	\"dst6_addr\" : \"%s\",\n"
154 , as, ds );
155 	} else {	// IPv4
156 		uint32_t _src, _dst;
157 		_src = htonl(r->V4.srcaddr);
158 		_dst = htonl(r->V4.dstaddr);
159 		inet_ntop(AF_INET, &_src, as, sizeof(as));
160 		inet_ntop(AF_INET, &_dst, ds, sizeof(ds));
161 		as[IP_STRING_LEN-1] = 0;
162 		ds[IP_STRING_LEN-1] = 0;
163 
164 		snprintf(_s, slen-1,
165 "	\"src4_addr\" : \"%s\",\n"
166 "	\"dst4_addr\" : \"%s\",\n"
167 , as, ds );
168 	}
169 	_slen = strlen(data_string);
170 	_s = data_string + _slen;
171 	slen = STRINGSIZE - _slen;
172 
173 
174 	if ( r->prot == IPPROTO_ICMP || r->prot == IPPROTO_ICMPV6 ) { // ICMP
175 		snprintf(_s, slen-1,
176 "	\"icmp_type\" : %u,\n"
177 "	\"icmp_code\" : %u,\n"
178 , r->icmp_type, r->icmp_code);
179 	} else {
180 		snprintf(_s, slen-1,
181 "	\"src_port\" : %u,\n"
182 "	\"dst_port\" : %u,\n"
183 , r->srcport, r->dstport);
184 	}
185 
186 	_slen = strlen(data_string);
187 	_s = data_string + _slen;
188 	slen = STRINGSIZE - _slen;
189 
190 	snprintf(_s, slen-1,
191 "	\"fwd_status\" : %u,\n"
192 "	\"tcp_flags\" : \"%s\",\n"
193 "	\"src_tos\" : %u,\n"
194 "	\"in_packets\" : %llu,\n"
195 "	\"in_bytes\" : %llu,\n"
196 , r->fwd_status, flags_str, r->tos,
197 	(unsigned long long)r->dPkts, (unsigned long long)r->dOctets);
198 
199 	_slen = strlen(data_string);
200 	_s = data_string + _slen;
201 	slen = STRINGSIZE - _slen;
202 
203 	i = 0;
204 	while ( (id = extension_map->ex_id[i]) != 0 ) {
205 		if ( slen <= 20 ) {
206 		// XXX
207 			data_string[STRINGSIZE-1] = 0;
208 			*s = data_string;
209 		}
210 		switch(id) {
211 			case EX_IO_SNMP_2:
212 			case EX_IO_SNMP_4:
213 				snprintf(_s, slen-1,
214 "	\"input_snmp\" : %u,\n"
215 "	\"output_snmp\" : %u,\n"
216 , r->input, r->output);
217 				break;
218 			case EX_AS_2:
219 			case EX_AS_4:
220 				snprintf(_s, slen-1,
221 "	\"src_as\" : %u,\n"
222 "	\"dst_as\" : %u,\n"
223 , r->srcas, r->dstas);
224 				break;
225 			case EX_BGPADJ:
226 				snprintf(_s, slen-1,
227 "	\"next_as\" : %u,\n"
228 "	\"prev_as\" : %u,\n"
229 , r->bgpNextAdjacentAS, r->bgpPrevAdjacentAS);
230 				break;
231 			case EX_MULIPLE:
232 				snprintf(_s, slen-1,
233 "	\"src_mask\" : %u,\n"
234 "	\"dst_mask\" : %u,\n"
235 "	\"dst_tos\" : %u,\n"
236 "	\"direction\" : %u,\n"
237 , r->src_mask, r->dst_mask, r->dst_tos, r->dir );
238 				break;
239 			case EX_NEXT_HOP_v4: {
240 				uint32_t _ip;
241 				as[0] = 0;
242 				_ip = htonl(r->ip_nexthop.V4);
243 				inet_ntop(AF_INET, &_ip, as, sizeof(as));
244 				as[IP_STRING_LEN-1] = 0;
245 
246 				snprintf(_s, slen-1,
247 "	\"ip4_next_hop\" : \"%s\",\n"
248 , as);
249 			} break;
250 			case EX_NEXT_HOP_v6: {
251 				uint64_t _ip[2];
252 				as[0] = 0;
253 				_ip[0] = htonll(r->ip_nexthop.V6[0]);
254 				_ip[1] = htonll(r->ip_nexthop.V6[1]);
255 				inet_ntop(AF_INET6, _ip, as, sizeof(as));
256 				as[IP_STRING_LEN-1] = 0;
257 
258 				snprintf(_s, slen-1,
259 "	\"ip6_next_hop\" : \"%s\",\n"
260 , as);
261 			} break;
262 			case EX_NEXT_HOP_BGP_v4: {
263 				uint32_t _ip;
264 				as[0] = 0;
265 				_ip = htonl(r->bgp_nexthop.V4);
266 				inet_ntop(AF_INET, &_ip, as, sizeof(as));
267 				as[IP_STRING_LEN-1] = 0;
268 
269 				snprintf(_s, slen-1,
270 "	\"bgp4_next_hop\" : \"%s\",\n"
271 , as);
272 			} break;
273 			case EX_NEXT_HOP_BGP_v6: {
274 				uint64_t _ip[2];
275 				as[0] = 0;
276 				_ip[0] = htonll(r->bgp_nexthop.V6[0]);
277 				_ip[1] = htonll(r->bgp_nexthop.V6[1]);
278 				inet_ntop(AF_INET6, _ip, as, sizeof(as));
279 				as[IP_STRING_LEN-1] = 0;
280 
281 				snprintf(_s, slen-1,
282 "	\"bgp6_next_hop\" : \"%s\",\n"
283 , as);
284 			} break;
285 			case EX_VLAN:
286 				snprintf(_s, slen-1,
287 "	\"src_vlan\" : %u,\n"
288 "	\"dst_vlan\" : %u,\n"
289 , r->src_vlan, r->dst_vlan);
290 			break;
291 			case EX_OUT_PKG_4:
292 			case EX_OUT_PKG_8:
293 				snprintf(_s, slen-1,
294 "	\"out_packets\" : %llu,\n"
295 , (long long unsigned)r->out_pkts);
296 			break;
297 			case EX_OUT_BYTES_4:
298 			case EX_OUT_BYTES_8:
299 				snprintf(_s, slen-1,
300 "	\"out_bytes\" : %llu,\n"
301 , (long long unsigned)r->out_bytes);
302 			break;
303 			case EX_AGGR_FLOWS_4:
304 			case EX_AGGR_FLOWS_8:
305 				snprintf(_s, slen-1,
306 "	\"aggr_flows\" : %llu,\n"
307 , (long long unsigned)r->aggr_flows);
308 			break;
309 			case EX_MAC_1: {
310 				int i;
311 				uint8_t mac1[6], mac2[6];
312 
313 				for ( i=0; i<6; i++ ) {
314 					mac1[i] = (r->in_src_mac >> ( i*8 )) & 0xFF;
315 				}
316 				for ( i=0; i<6; i++ ) {
317 					mac2[i] = (r->out_dst_mac >> ( i*8 )) & 0xFF;
318 				}
319 				snprintf(_s, slen-1,
320 "	\"in_src_mac\" : \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
321 "	\"out_dst_mac\" : \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
322 , mac1[5], mac1[4], mac1[3], mac1[2], mac1[1], mac1[0],
323   mac2[5], mac2[4], mac2[3], mac2[2], mac2[1], mac2[0] );
324 			} break;
325 			case EX_MAC_2: {
326 				int i;
327 				uint8_t mac1[6], mac2[6];
328 
329 				for ( i=0; i<6; i++ ) {
330 					mac1[i] = (r->in_dst_mac >> ( i*8 )) & 0xFF;
331 				}
332 				for ( i=0; i<6; i++ ) {
333 					mac2[i] = (r->out_src_mac >> ( i*8 )) & 0xFF;
334 				}
335 				snprintf(_s, slen-1,
336 "	\"in_dst_mac\" : \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
337 "	\"out_src_mac\" : \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
338 , mac1[5], mac1[4], mac1[3], mac1[2], mac1[1], mac1[0],
339   mac2[5], mac2[4], mac2[3], mac2[2], mac2[1], mac2[0] );
340 			} break;
341 			case EX_MPLS: {
342 				unsigned int i;
343 				for ( i=0; i<10; i++ ) {
344 					snprintf(_s, slen-1,
345 "	\"mpls_%u\" : \"%u-%u-%u\",\n", i+1
346 , r->mpls_label[i] >> 4 , (r->mpls_label[i] & 0xF ) >> 1, r->mpls_label[i] & 1 );
347 					_slen = strlen(data_string);
348 					_s = data_string + _slen;
349 					slen = STRINGSIZE - _slen;
350 				}
351 			} break;
352 			case EX_ROUTER_IP_v4: {
353 				uint32_t _ip;
354 				as[0] = 0;
355 				_ip = htonl(r->ip_router.V4);
356 				inet_ntop(AF_INET, &_ip, as, sizeof(as));
357 				as[IP_STRING_LEN-1] = 0;
358 				snprintf(_s, slen-1,
359 "	\"ip4_router\" : \"%s\",\n"
360 , as);
361 			} break;
362 			case EX_ROUTER_IP_v6: {
363 				uint64_t _ip[2];
364 				as[0] = 0;
365 				_ip[0] = htonll(r->ip_router.V6[0]);
366 				_ip[1] = htonll(r->ip_router.V6[1]);
367 				inet_ntop(AF_INET6, _ip, as, sizeof(as));
368 				as[IP_STRING_LEN-1] = 0;
369 				snprintf(_s, slen-1,
370 "	\"ip6_router\" : \"%s\",\n"
371 , as);
372 			} break;
373 			case EX_LATENCY: {
374 				double f1, f2, f3;
375 				f1 = (double)r->client_nw_delay_usec / 1000.0;
376 				f2 = (double)r->server_nw_delay_usec / 1000.0;
377 				f3 = (double)r->appl_latency_usec / 1000.0;
378 
379 				snprintf(_s, slen-1,
380 "	\"cli_latency\" : %f,\n"
381 "	\"srv_latency\" : %f,\n"
382 "	\"app_latency\" : %f,\n"
383 , f1, f2, f3);
384 			} break;
385 			case EX_ROUTER_ID:
386 				snprintf(_s, slen-1,
387 "	\"engine_type\" : %u,\n"
388 "	\"engine_id\" : %u,\n"
389 , r->engine_type, r->engine_id);
390 				break;
391 			case EX_RECEIVED: {
392 				char *datestr, datebuff[64];
393 				when = r->received / 1000LL;
394 				ts = localtime(&when);
395 				strftime(datebuff, 63, "%Y-%m-%dT%H:%M:%S", ts);
396 				asprintf(&datestr, "%s.%llu", datebuff, (long long unsigned)r->received % 1000L);
397 
398 				snprintf(_s, slen-1,
399 "	\"t_received\" : \"%s\",\n"
400 , datestr);
401 				free(datestr);
402 				} break;
403 #ifdef NSEL
404 			case EX_NSEL_COMMON: {
405 				char *datestr, datebuff[64];
406 				when = r->event_time / 1000LL;
407 				ts = localtime(&when);
408 				strftime(datebuff, 63, "%Y-%m-%dT%H:%M:%S", ts);
409 				asprintf(&datestr, "%s.%llu", datebuff, r->event_time % 1000LL);
410 				snprintf(_s, slen-1,
411 "	\"connect_id\" : \"%u\",\n"
412 "	\"event_id\" : \"%u\",\n"
413 "	\"event\" : \"%s\",\n"
414 "	\"xevent_id\" : \"%u\",\n"
415 "	\"sgt_id\" : \"%u\",\n"
416 "	\"t_event\" : \"%s\",\n"
417 , r->conn_id, r->event, r->event_flag == FW_EVENT ? FwEventString(r->event) : EventString(r->event)
418 , r->fw_xevent, r->sec_group_tag, datestr);
419 				free(datestr);
420 				} break;
421 			case EX_NEL_COMMON: {
422 				snprintf(_s, slen-1,
423 "	\"nat_event_id\" : \"%u\",\n"
424 "	\"nat_event\" : \"%s\",\n"
425 "	\"ingress_vrf\" : \"%u\",\n"
426 "	\"egress_vrf\" : \"%u\",\n"
427 , r->event, r->event_flag == FW_EVENT ? FwEventString(r->event) : EventString(r->event)
428 , r->ingress_vrfid, r->egress_vrfid);
429 				} break;
430 			case EX_NSEL_XLATE_PORTS: {
431 				snprintf(_s, slen-1,
432 "	\"src_xlt_port\" : \"%u\",\n"
433 "	\"dst_xlt_port\" : \"%u\",\n"
434 , r->xlate_src_port, r->xlate_dst_port );
435 				} break;
436 			case EX_PORT_BLOCK_ALLOC: {
437 				snprintf(_s, slen-1,
438 "	\"pblock_start\" : \"%u\",\n"
439 "	\"pblock_end\" : \"%u\",\n"
440 "	\"pblock_step\" : \"%u\",\n"
441 "	\"pblock_size\" : \"%u\",\n"
442 , r->block_start, r->block_end, r->block_step, r->block_size );
443 				} break;
444 			case EX_NSEL_XLATE_IP_v4: {
445 				uint32_t _src, _dst;
446 				as[0] = 0;
447 				ds[0] = 0;
448 				_src = htonl(r->xlate_src_ip.V4);
449 				_dst = htonl(r->xlate_dst_ip.V4);
450 				inet_ntop(AF_INET, &_src, as, sizeof(as));
451 				inet_ntop(AF_INET, &_dst, ds, sizeof(ds));
452 				as[IP_STRING_LEN-1] = 0;
453 				ds[IP_STRING_LEN-1] = 0;
454 
455 				snprintf(_s, slen-1,
456 "	\"src4_xlt_ip\" : \"%s\",\n"
457 "	\"dst4_xlt_ip\" : \"%s\",\n"
458 , as, ds);
459 			} break;
460 			case EX_NSEL_XLATE_IP_v6: {
461 				uint64_t _src[2], _dst[2];
462 				as[0] = 0;
463 				ds[0] = 0;
464 				_src[0] = htonll(r->xlate_src_ip.V6[0]);
465 				_src[1] = htonll(r->xlate_src_ip.V6[1]);
466 				_dst[0] = htonll(r->xlate_dst_ip.V6[0]);
467 				_dst[1] = htonll(r->xlate_dst_ip.V6[1]);
468 				inet_ntop(AF_INET6, _src, as, sizeof(as));
469 				inet_ntop(AF_INET6, _dst, ds, sizeof(ds));
470 				as[IP_STRING_LEN-1] = 0;
471 				ds[IP_STRING_LEN-1] = 0;
472 
473 				snprintf(_s, slen-1,
474 "	\"src6_xlt_ip\" : \"%s\",\n"
475 "	\"dst6_xlt_ip\" : \"%s\",\n"
476 , as, ds);
477 			} break;
478 			case EX_NSEL_ACL:
479 				snprintf(_s, slen-1,
480 "	\"ingress_acl\" : \"0x%x/0x%x/0x%x\",\n"
481 "	\"egress_acl\" : \"0x%x/0x%x/0x%x\",\n"
482 , r->ingress_acl_id[0], r->ingress_acl_id[1], r->ingress_acl_id[2],
483   r->egress_acl_id[0], r->egress_acl_id[1], r->egress_acl_id[2]);
484 				break;
485 			case EX_NSEL_USER:
486 			case EX_NSEL_USER_MAX:
487 				snprintf(_s, slen-1,
488 "	\"user_name\" : \"%s\",\n"
489 , r->username[0] ? r->username : "<empty>");
490 				break;
491 #endif
492 		}
493 		_slen = strlen(data_string);
494 		_s = data_string + _slen;
495 		slen = STRINGSIZE - _slen;
496 		i++;
497 	}
498 
499 
500 	// add label and close json object
501 	snprintf(_s, slen-1,
502 "	\"label\" : \"%s\"\n"
503 "}", r->label ? r->label : "<none>");
504 
505 	data_string[STRINGSIZE-1] = 0;
506 	*s = data_string;
507 
508 
509 } // End of flow_record_to_json
510