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