1 /*
2 ** Copyright (C) 2004-2020 by Carnegie Mellon University.
3 **
4 ** @OPENSOURCE_LICENSE_START@
5 ** See license information in ../../LICENSE.txt
6 ** @OPENSOURCE_LICENSE_END@
7 */
8
9 /*
10 * rwbagcat reads a binary bag, converts it to text, and outputs it
11 * to stdout. It can also print various statistics and summary
12 * information about the bag. It attempts to read the bag(s) from
13 * stdin or from any arguments.
14 *
15 */
16
17 #include <silk/silk.h>
18
19 RCSIDENT("$SiLK: rwbagcat.c ef14e54179be 2020-04-14 21:57:45Z mthomas $");
20
21 #include <silk/skbag.h>
22 #include <silk/skcountry.h>
23 #include <silk/skheap.h>
24 #include <silk/skipaddr.h>
25 #include <silk/skipset.h>
26 #include <silk/skprefixmap.h>
27 #include <silk/sknetstruct.h>
28 #include <silk/sksite.h>
29 #include <silk/skstringmap.h>
30 #include <silk/skstream.h>
31 #include <silk/utils.h>
32
33
34 /* LOCAL DEFINES AND TYPEDEFS */
35
36 /* where to write --help output */
37 #define USAGE_FH stdout
38
39 /* return 1 if 'm_arg' refers to the standard input */
40 #define IS_STDIN(m_arg) \
41 (0 == strcmp((m_arg), "-") || 0 == strcmp((m_arg), "stdin"))
42
43 /* width of count fields in columnar output */
44 #define COUNT_WIDTH 20
45
46 /* the minimum counter allowed by the --mincounter switch */
47 #define BAGCAT_MIN_COUNTER UINT64_C(1)
48
49 /* mask for the key_format to determine which of these values it has */
50 #define KEY_FORMAT_MASK UINT32_C(0xF0000000)
51
52 /* for the --key-format, value to indicate an IP address */
53 #define KEY_FORMAT_IP UINT32_C(0x80000000)
54
55 /* for the --key-format, value to indicate a timestamp */
56 #define KEY_FORMAT_TIME UINT32_C(0x40000000)
57
58 /* for --sort-counter, the initial size of the heap */
59 #define BAGCAT_HEAP_INITIAL_SIZE (1u << 20)
60
61
62 /* return a non-zero value if a record's 'key' and 'counter' values
63 * are within the global limits and if the key is in the global
64 * 'mask_set' if specified */
65 #define CHECK_LIMITS_IPADDR(k, c) \
66 (((c)->val.u64 >= limits.mincounter) \
67 && ((c)->val.u64 <= limits.maxcounter) \
68 && ((0 == limits.key_is_min) \
69 || (skipaddrCompare(&limits.minkey_ip, &(k)->val.addr) <= 0)) \
70 && ((0 == limits.key_is_max) \
71 || (skipaddrCompare(&limits.maxkey_ip, &(k)->val.addr) >= 0)) \
72 && ((NULL == limits.mask_set) \
73 || skIPSetCheckAddress(limits.mask_set, &(k)->val.addr)))
74
75 #define CHECK_LIMITS_UINT32(k, c) \
76 (((c)->val.u64 >= limits.mincounter) \
77 && ((c)->val.u64 <= limits.maxcounter) \
78 && ((0 == limits.key_is_min) \
79 || ((k)->val.u32 >= limits.minkey_u32)) \
80 && ((0 == limits.key_is_max) \
81 || ((k)->val.u32 <= limits.maxkey_u32)))
82
83 /* Allow paging of the output */
84 #define INVOKE_PAGER() \
85 if (pager_invoked) { /* no-op */ } else { \
86 pager_invoked = 1; \
87 skStreamPageOutput(output, pager); \
88 }
89
90 typedef enum bin_scheme_en {
91 BINSCHEME_NONE=0,
92 BINSCHEME_LINEAR=1,
93 BINSCHEME_BINARY=2,
94 BINSCHEME_DECIMAL=3
95 } bin_scheme_t;
96
97
98 typedef enum bagcat_fmt_en {
99 BAGCAT_FMT_ATTRIBUTES,
100 BAGCAT_FMT_COUNTRY,
101 BAGCAT_FMT_IPADDR,
102 BAGCAT_FMT_PMAP,
103 BAGCAT_FMT_SENSOR,
104 BAGCAT_FMT_TCPFLAGS,
105 BAGCAT_FMT_TIME
106 } bagcat_fmt_t;
107
108 struct bagcat_key_st {
109 skBagKeyType_t key_type;
110 bagcat_fmt_t formatter;
111 uint32_t formatter_flags;
112 int width;
113 size_t buflen;
114 };
115 typedef struct bagcat_key_st bagcat_key_t;
116
117 /* printing state */
118 struct state_st {
119 const bagcat_key_t *bc_key;
120 char end_of_line[2];
121 int width[2];
122 size_t buflen;
123 char *buf;
124 };
125 typedef struct state_st state_t;
126
127 /* bagcat_heapnode_t is how key/counter pairs are stored in the heap
128 * that is used to implement the --sort-counter switch */
129 struct bagcat_heapnode_st {
130 uint64_t counter;
131 skBagTypedKey_t key;
132 };
133 typedef struct bagcat_heapnode_st bagcat_heapnode_t;
134
135
136
137 /* LOCAL VARIABLES */
138
139 /* global I/O state */
140 sk_options_ctx_t *optctx = NULL;
141 static skstream_t *output = NULL;
142 static skstream_t *stats_stream = NULL;
143 static int print_statistics = 0;
144 static int print_network = 0;
145 static int sort_counters = 0;
146 static bin_scheme_t bin_scheme = BINSCHEME_NONE;
147 static const char *net_structure = NULL;
148
149 /* delimiter between output columns for hosts/counts */
150 static char output_delimiter = '|';
151
152 /* whether key/counter output is in columns (0) or scrunched together (1) */
153 static int no_columns = 0;
154
155 /* whether to suppress the final delimiter; default no (i.e. end with '|') */
156 static int no_final_delimiter = 0;
157
158 /* how to format the keys. Value is set by the --key-format switch.
159 * Possible values include an skipaddr_flags_t value from silk_types.h
160 * or an sktimestamp_flags_t value from utils.h. The default format
161 * is determined by the type of Bag, or it is SKIPADDR_CANONICAL for
162 * CUSTOM keys (including SiLK-2 Bag files) or SKIPADDR_DECIMAL
163 * otherwise. */
164 static uint32_t key_format = 0;
165
166 /* the caller's key format argument; for error messages */
167 static const char *key_format_arg = NULL;
168
169 /* prefix map to use for keys in bags whose key is a pmap dictionary */
170 static skPrefixMap_t *prefix_map = NULL;
171
172 /* print out keys whose counter is zero---requires a mask_set or that
173 * both --minkey and --maxkey are specified */
174 static int print_zero_counts = 0;
175
176 /* the limits for determining which entries get printed. */
177 static struct limits_st {
178 /* the {min,max}counter entered */
179 uint64_t mincounter;
180 uint64_t maxcounter;
181
182 /* only print keys that appear in this set */
183 skipset_t *mask_set;
184
185 /* the {min,max}key entered */
186 skipaddr_t minkey_ip;
187 skipaddr_t maxkey_ip;
188
189 /* the {min,max}key as a uint32_t */
190 uint32_t minkey_u32;
191 uint32_t maxkey_u32;
192
193 /* true when any limit switch or mask-set was specified */
194 unsigned active :1;
195
196 /* true when minkey or maxkey was given */
197 unsigned key_is_min :1;
198 unsigned key_is_max :1;
199
200 } limits;
201
202 /* name of program to run to page output */
203 static char *pager = NULL;
204
205 /* whether the pager has been invoked. this is set to true when the
206 * --output-path switch is specified to bypass the pager */
207 static int pager_invoked = 0;
208
209 /* values provided to min-key and max-key switches; for errros */
210 static char *min_key = NULL;
211 static char *max_key = NULL;
212
213 /* possible key formats */
214 static const sk_stringmap_entry_t key_format_names[] = {
215 {"canonical", KEY_FORMAT_IP | SKIPADDR_CANONICAL,
216 "canonical IP format (192.0.2.1, 2001:db8::1, ::ffff:127.0.0.1)", NULL},
217 {"decimal", KEY_FORMAT_IP | SKIPADDR_DECIMAL,
218 "integer number in decimal format", NULL},
219 {"hexadecimal", KEY_FORMAT_IP | SKIPADDR_HEXADECIMAL,
220 "integer number in hexadecimal format", NULL},
221 {"no-mixed", KEY_FORMAT_IP | SKIPADDR_NO_MIXED,
222 "canonical IP format but no mixed IPv4/IPv6 for IPv6 IPs", NULL},
223 {"zero-padded", KEY_FORMAT_IP | SKIPADDR_ZEROPAD,
224 "pad IP result to its maximum width with zeros", NULL},
225 {"map-v4", KEY_FORMAT_IP | SKIPADDR_MAP_V4,
226 "map IPv4 to ::ffff:0:0/96 netblock prior to formatting", NULL},
227 {"unmap-v6", KEY_FORMAT_IP | SKIPADDR_UNMAP_V6,
228 "convert IPv6 in ::ffff:0:0/96 to IPv4 prior to formatting", NULL},
229 {"force-ipv6", KEY_FORMAT_IP | SKIPADDR_FORCE_IPV6,
230 "alias equivalent to \"map-v4,no-mixed\"", NULL},
231 {"timestamp", KEY_FORMAT_TIME | 0,
232 "time in yyyy/mm/ddThh:mm:ss format", NULL},
233 {"iso-time", KEY_FORMAT_TIME | SKTIMESTAMP_ISO,
234 "time in yyyy-mm-dd hh:mm:ss format", NULL},
235 {"m/d/y", KEY_FORMAT_TIME | SKTIMESTAMP_MMDDYYYY,
236 "time in mm/dd/yyyy hh:mm:ss format", NULL},
237 {"utc", KEY_FORMAT_TIME | SKTIMESTAMP_UTC,
238 "print as time using UTC", NULL},
239 {"localtime", KEY_FORMAT_TIME | SKTIMESTAMP_LOCAL,
240 "print as time and use TZ environment variable or local timezone", NULL},
241 {"epoch", KEY_FORMAT_TIME | SKTIMESTAMP_EPOCH,
242 "seconds since UNIX epoch (equivalent to decimal)", NULL},
243 SK_STRINGMAP_SENTINEL
244 };
245
246 /* the stringmap that contains those formats */
247 static sk_stringmap_t *key_format_map = NULL;
248
249 /* whether stdin has been used */
250 static int stdin_used = 0;
251
252
253 /* OPTIONS SETUP */
254
255 typedef enum {
256 OPT_NETWORK_STRUCTURE,
257 OPT_BIN_IPS,
258 OPT_SORT_COUNTERS,
259 OPT_PRINT_STATISTICS,
260 OPT_MASK_SET,
261 OPT_MINKEY,
262 OPT_MAXKEY,
263 OPT_MINCOUNTER,
264 OPT_MAXCOUNTER,
265 OPT_ZERO_COUNTS,
266 OPT_PMAP_FILE,
267 OPT_KEY_FORMAT,
268 OPT_INTEGER_KEYS,
269 OPT_ZERO_PAD_IPS,
270 OPT_NO_COLUMNS,
271 OPT_COLUMN_SEPARATOR,
272 OPT_NO_FINAL_DELIMITER,
273 OPT_DELIMITED,
274 OPT_OUTPUT_PATH,
275 OPT_PAGER
276 } appOptionsEnum;
277
278
279 static struct option appOptions[] = {
280 {"network-structure", OPTIONAL_ARG, 0, OPT_NETWORK_STRUCTURE},
281 {"bin-ips", OPTIONAL_ARG, 0, OPT_BIN_IPS},
282 {"sort-counters" , OPTIONAL_ARG, 0, OPT_SORT_COUNTERS},
283 {"print-statistics", OPTIONAL_ARG, 0, OPT_PRINT_STATISTICS},
284 {"mask-set", REQUIRED_ARG, 0, OPT_MASK_SET},
285 {"minkey", REQUIRED_ARG, 0, OPT_MINKEY},
286 {"maxkey", REQUIRED_ARG, 0, OPT_MAXKEY},
287 {"mincounter", REQUIRED_ARG, 0, OPT_MINCOUNTER},
288 {"maxcounter", REQUIRED_ARG, 0, OPT_MAXCOUNTER},
289 {"zero-counts", NO_ARG, 0, OPT_ZERO_COUNTS},
290 {"pmap-file", REQUIRED_ARG, 0, OPT_PMAP_FILE},
291 {"key-format", REQUIRED_ARG, 0, OPT_KEY_FORMAT},
292 {"integer-keys", NO_ARG, 0, OPT_INTEGER_KEYS},
293 {"zero-pad-ips", NO_ARG, 0, OPT_ZERO_PAD_IPS},
294 {"no-columns", NO_ARG, 0, OPT_NO_COLUMNS},
295 {"column-separator", REQUIRED_ARG, 0, OPT_COLUMN_SEPARATOR},
296 {"no-final-delimiter", NO_ARG, 0, OPT_NO_FINAL_DELIMITER},
297 {"delimited", OPTIONAL_ARG, 0, OPT_DELIMITED},
298 {"output-path", REQUIRED_ARG, 0, OPT_OUTPUT_PATH},
299 {"pager", REQUIRED_ARG, 0, OPT_PAGER},
300 {0,0,0,0 } /* sentinel entry */
301 };
302
303
304 static const char *appHelp[] = {
305 NULL,
306 ("Invert the bag and count by distinct volume values. May not\n"
307 "\tbe combined with --network-structure or --sort-counters. Choices:\n"
308 "\tlinear - volume => count(KEYS) [default when no argument]\n"
309 "\tbinary - log2(volume) => count(KEYS)\n"
310 "\tdecimal - variation on log10(volume) => count(KEYS)"),
311 ("Sort the output by counters instead of by keys. May\n"
312 "\tnot be combined with --network-structure or --bin-ips. Choices:\n"
313 "\tdecreasing - print highest counter first [default when no argument]\n"
314 "\tincreasing - print lowest counter first"),
315 ("Print statistics about the bag. Def. no. Write\n"
316 "\toutput to the standard output unless an argument is given.\n"
317 "\tUse 'stderr' to send the output to the standard error"),
318 ("Output records that appear in this IPset. Def. Records\n"
319 "\twith non-zero counters"),
320 NULL,
321 NULL,
322 NULL,
323 NULL,
324 ("Print keys with a counter of zero. Def. No\n"
325 "\t(requires --mask-set or both --minkey and --maxkey)"),
326 ("Use this prefix map as the mapping file when Bag's key\n"
327 "\twas generated by a pmap. May be specified as MAPNAME:PATH, but the\n"
328 "\tmapname is currently ignored."),
329 NULL,
330 "DEPRECATED. Equivalent to --key-format=decimal",
331 "DEPRECATED. Equivalent to --key-format=zero-padded",
332 "Disable fixed-width columnar output. Def. Columnar",
333 "Use specified character between columns. Def. '|'",
334 "Suppress column delimiter at end of line. Def. No",
335 "Shortcut for --no-columns --no-final-del --column-sep=CHAR",
336 "Write the output to this stream or file. Def. stdout",
337 "Invoke this program to page output. Def. $SILK_PAGER or $PAGER",
338 (char *) NULL
339 };
340
341
342 /* LOCAL FUNCTION PROTOTYPES */
343
344 static int appOptionsHandler(clientData cData, int opt_index, char *opt_arg);
345 static int setOutput(const char* filename, skstream_t **stream_out);
346 static int parsePmapFileOption(const char *opt_arg);
347 static int keyFormatParse(const char *format);
348 static void keyFormatUsage(FILE *fh);
349
350
351 /* FUNCTION DEFINITIONS */
352
353 /*
354 * appUsageLong();
355 *
356 * Print complete usage information to USAGE_FH. Pass this
357 * function to skOptionsSetUsageCallback(); skOptionsParse() will
358 * call this funciton and then exit the program when the --help
359 * option is given.
360 */
361 static void
appUsageLong(void)362 appUsageLong(
363 void)
364 {
365 #define USAGE_MSG \
366 ("[SWITCHES] [BAG_FILES]\n" \
367 "\tPrint binary Bag files as text. When multiple files are given,\n" \
368 "\tthe bags are processed sequentially---specifically, their entries\n" \
369 "\tare not merged.\n")
370
371 /* network-structure help string is longer than allowed by C90 */
372 #define NETWORK_STRUCT_HELP1 \
373 ("Print the sum of counters for each specified CIDR\n" \
374 "\tblock in the comma-separed list of CIDR block sizes (0--32) and/or\n" \
375 "\tletters (T=0,A=8,B=16,C=24,X=27,H=32). If argument contains 'S' or\n" \
376 "\t'/', for each CIDR block print host counts and number of occupied\n")
377 #define NETWORK_STRUCT_HELP2 \
378 ("\tsmaller CIDR blocks. Additional CIDR blocks to summarize can be\n" \
379 "\tspecified by listing them after the '/'. Def. v4:TS/8,16,24,27.\n" \
380 "\tA leading 'v6:' treats Bag's keys as IPv6, allows range 0--128,\n" \
381 "\tdisallows A,B,C,X, sets H to 128, and sets default to TS/48,64.\n" \
382 "\tMay not be combined with --bin-ips or --sort-counters")
383
384 FILE *fh = USAGE_FH;
385 unsigned int i;
386 #if SK_ENABLE_IPV6
387 const char *v4_or_v6 = "v6";
388 #else
389 const char *v4_or_v6 = "v4";
390 #endif
391
392 fprintf(fh, "%s %s", skAppName(), USAGE_MSG);
393 fprintf(fh, "\nSWITCHES:\n");
394 skOptionsDefaultUsage(fh);
395 for (i = 0; appOptions[i].name; ++i) {
396 fprintf(fh, "--%s %s. ", appOptions[i].name,
397 SK_OPTION_HAS_ARG(appOptions[i]));
398 switch ((appOptionsEnum)appOptions[i].val) {
399 case OPT_NETWORK_STRUCTURE:
400 fprintf(fh, "%s%s\n", NETWORK_STRUCT_HELP1, NETWORK_STRUCT_HELP2);
401 break;
402 case OPT_MINKEY:
403 fprintf(fh,
404 ("Output records whose key is at least VALUE,"
405 " an IP%s address\n\tor an integer between"
406 " %" PRIu64 " and %" PRIu64 ", inclusive."
407 " Def. Records with\n\tnon-zero counters\n"),
408 v4_or_v6,
409 (uint64_t)SKBAG_KEY_MIN, (uint64_t)SKBAG_KEY_MAX);
410 break;
411 case OPT_MAXKEY:
412 fprintf(fh,
413 ("Output records whose key is not more than VALUE,"
414 " an IP%s\n\taddress or an integer."
415 " Def. Records with non-zero counters\n"),
416 v4_or_v6);
417 break;
418 case OPT_MINCOUNTER:
419 fprintf(fh,
420 ("Output records whose counter is at least VALUE,"
421 " an integer\n\tbetween %" PRIu64 " and %" PRIu64
422 ", inclusive. Def. %" PRIu64 "\n"),
423 BAGCAT_MIN_COUNTER, SKBAG_COUNTER_MAX, BAGCAT_MIN_COUNTER);
424 break;
425 case OPT_MAXCOUNTER:
426 fprintf(fh,
427 ("Output records whose counter is not more than VALUE,"
428 " an\n\tinteger. Def. %" PRIu64 "\n"),
429 SKBAG_COUNTER_MAX);
430 break;
431 case OPT_KEY_FORMAT:
432 keyFormatUsage(fh);
433 break;
434 default:
435 fprintf(fh, "%s\n", appHelp[i]);
436 break;
437 }
438 }
439
440 skOptionsCtxOptionsUsage(optctx, fh);
441 sksiteOptionsUsage(fh);
442 }
443
444
445 /*
446 * appTeardown()
447 *
448 * Teardown all modules, close all files, and tidy up all
449 * application state.
450 *
451 * This function is idempotent.
452 */
453 static void
appTeardown(void)454 appTeardown(
455 void)
456 {
457 static int teardownFlag = 0;
458
459 if (teardownFlag) {
460 return;
461 }
462 teardownFlag = 1;
463
464 if (stats_stream != output) {
465 skStreamDestroy(&stats_stream);
466 }
467 skStreamDestroy(&output);
468
469 skPrefixMapDelete(prefix_map);
470 skStringMapDestroy(key_format_map);
471
472 skOptionsCtxDestroy(&optctx);
473 skAppUnregister();
474 }
475
476
477 /*
478 * appSetup(argc, argv);
479 *
480 * Perform all the setup for this application include setting up
481 * required modules, parsing options, etc. This function should be
482 * passed the same arguments that were passed into main().
483 *
484 * Returns to the caller if all setup succeeds. If anything fails,
485 * this function will cause the application to exit with a FAILURE
486 * exit status.
487 */
488 static void
appSetup(int argc,char ** argv)489 appSetup(
490 int argc,
491 char **argv)
492 {
493 SILK_FEATURES_DEFINE_STRUCT(features);
494 unsigned int optctx_flags;
495 int rv;
496
497 /* verify same number of options and help strings */
498 assert((sizeof(appHelp)/sizeof(char *)) ==
499 (sizeof(appOptions)/sizeof(struct option)));
500
501 /* register the application */
502 skAppRegister(argv[0]);
503 skAppVerifyFeatures(&features, NULL);
504 skOptionsSetUsageCallback(&appUsageLong);
505
506 /* initialize globals */
507 memset(&limits, 0, sizeof(limits));
508 limits.mincounter = SKBAG_COUNTER_MIN;
509 limits.maxcounter = SKBAG_COUNTER_MAX;
510
511 optctx_flags = (SK_OPTIONS_CTX_INPUT_BINARY | SK_OPTIONS_CTX_ALLOW_STDIN);
512
513 /* register the options */
514 if (skOptionsCtxCreate(&optctx, optctx_flags)
515 || skOptionsCtxOptionsRegister(optctx)
516 || skOptionsRegister(appOptions, &appOptionsHandler, NULL)
517 || sksiteOptionsRegister(SK_SITE_FLAG_CONFIG_FILE))
518 {
519 skAppPrintErr("Unable to register options");
520 exit(EXIT_FAILURE);
521 }
522
523 /* register the teardown handler */
524 if (atexit(appTeardown) < 0) {
525 skAppPrintErr("Unable to register appTeardown() with atexit()");
526 appTeardown();
527 exit(EXIT_FAILURE);
528 }
529
530 /* create the string map of the possible key formats */
531 if ((skStringMapCreate(&key_format_map) != SKSTRINGMAP_OK)
532 || (skStringMapAddEntries(key_format_map, -1, key_format_names)
533 != SKSTRINGMAP_OK))
534 {
535 skAppPrintOutOfMemory(NULL);
536 exit(EXIT_FAILURE);
537 }
538
539 /* parse options */
540 rv = skOptionsCtxOptionsParse(optctx, argc, argv);
541 if (rv < 0) {
542 skAppUsage(); /* never returns */
543 }
544
545 if (print_network) {
546 /* may not have --print-network and --bin-scheme */
547 if (bin_scheme != BINSCHEME_NONE) {
548 skAppPrintErr("Cannot specify both --%s and --%s",
549 appOptions[OPT_NETWORK_STRUCTURE].name,
550 appOptions[OPT_BIN_IPS].name);
551 exit(EXIT_FAILURE);
552 }
553 /* ensure key-format is an IP */
554 if (key_format && !(key_format & KEY_FORMAT_IP)) {
555 skAppPrintErr("Invalid %s: May only use an IP format with --%s",
556 appOptions[OPT_KEY_FORMAT].name,
557 appOptions[OPT_NETWORK_STRUCTURE].name);
558 exit(EXIT_FAILURE);
559 }
560 /* disable mapping of ::ffff:0:0/96 to IPv4 */
561 key_format = key_format & ~SKIPADDR_UNMAP_V6;
562 }
563
564 /* when printing of entries with counters of 0 is requested,
565 * either --mask-set or both --minkey and --maxkey must be
566 * given */
567 if (print_zero_counts
568 && (NULL == limits.mask_set)
569 && (0 == limits.key_is_min || 0 == limits.key_is_max))
570 {
571 skAppPrintErr("To use --%s, either --%s or both --%s and --%s"
572 " must be specified",
573 appOptions[OPT_ZERO_COUNTS].name,
574 appOptions[OPT_MASK_SET].name,
575 appOptions[OPT_MINKEY].name,
576 appOptions[OPT_MAXKEY].name);
577 skAppUsage(); /* never returns */
578 }
579
580 /* write an error message and exit when a minimum is greater than
581 * a maximum */
582 if (limits.mincounter > limits.maxcounter) {
583 skAppPrintErr(("Minimum counter greater than maximum: "
584 "%" PRIu64 " > %" PRIu64),
585 limits.mincounter, limits.maxcounter);
586 exit(EXIT_FAILURE);
587 }
588 if (limits.key_is_min && limits.key_is_max) {
589 if (skipaddrCompare(&limits.maxkey_ip, &limits.minkey_ip) < 0) {
590 skAppPrintErr("Minimum key greater than maximum: %s > %s",
591 min_key, max_key);
592 exit(EXIT_FAILURE);
593 }
594 }
595
596 /* when an output-path was given, disable the pager. If no
597 * output-path was given, set it to the default */
598 if (output) {
599 /* do not page the output */
600 pager_invoked = 1;
601 } else {
602 if (setOutput("stdout", &output)) {
603 skAppPrintErr("Unable to print to standard output");
604 exit(EXIT_FAILURE);
605 }
606 }
607
608 /* set stream and pager for --print-statistics */
609 if (print_statistics) {
610 if (NULL == stats_stream) {
611 /* Set the --print-stat output stream to stdout if the
612 * user did not set it */
613 if (setOutput("stdout", &stats_stream)) {
614 skAppPrintErr("Unable to print to standard output");
615 exit(EXIT_FAILURE);
616 }
617 } else if (bin_scheme == BINSCHEME_NONE
618 && !sort_counters && !print_network)
619 {
620 /* Disable the pager when the only output is --print-stat
621 * and an explicit stream was specified */
622 pager_invoked = 1;
623 }
624 }
625
626 rv = skStreamOpen(output);
627 if (rv) {
628 skStreamPrintLastErr(output, rv, &skAppPrintErr);
629 exit(EXIT_FAILURE);
630 }
631 if (stats_stream != NULL && stats_stream != output) {
632 rv = skStreamOpen(stats_stream);
633 if (rv) {
634 skStreamPrintLastErr(stats_stream, rv, &skAppPrintErr);
635 exit(EXIT_FAILURE);
636 }
637 }
638
639 return; /* OK */
640 }
641
642
643 /*
644 * status = appOptionsHandler(cData, opt_index, opt_arg);
645 *
646 * Called by skOptionsParse(), this handles a user-specified switch
647 * that the application has registered, typically by setting global
648 * variables. Returns 1 if the switch processing failed or 0 if it
649 * succeeded. Returning a non-zero from from the handler causes
650 * skOptionsParse() to return a negative value.
651 *
652 * The clientData in 'cData' is typically ignored; 'opt_index' is
653 * the index number that was specified as the last value for each
654 * struct option in appOptions[]; 'opt_arg' is the user's argument
655 * to the switch for options that have a REQUIRED_ARG or an
656 * OPTIONAL_ARG.
657 */
658 static int
appOptionsHandler(clientData UNUSED (cData),int opt_index,char * opt_arg)659 appOptionsHandler(
660 clientData UNUSED(cData),
661 int opt_index,
662 char *opt_arg)
663 {
664 skstream_t *stream = NULL;
665 uint64_t val64;
666 size_t len;
667 int rv;
668
669 switch ((appOptionsEnum)opt_index) {
670 case OPT_NETWORK_STRUCTURE:
671 net_structure = opt_arg;
672 print_network = 1;
673 break;
674
675 case OPT_PMAP_FILE:
676 if (prefix_map) {
677 skAppPrintErr("Invalid %s: Switch used multiple times",
678 appOptions[opt_index].name);
679 return 1;
680 }
681 if (IS_STDIN(opt_arg)) {
682 stdin_used = 1;
683 }
684 if (parsePmapFileOption(opt_arg)) {
685 return 1;
686 }
687 break;
688
689 case OPT_BIN_IPS:
690 if (NULL == opt_arg) {
691 bin_scheme = BINSCHEME_LINEAR;
692 } else if (0 == (len = strlen(opt_arg))) {
693 skAppPrintErr("Invalid %s: Switch requires an argument",
694 appOptions[opt_index].name);
695 return 1;
696 } else if (strncmp(opt_arg, "linear", len) == 0) {
697 bin_scheme = BINSCHEME_LINEAR;
698 } else if (strncmp(opt_arg, "binary", len) == 0) {
699 bin_scheme = BINSCHEME_BINARY;
700 } else if (strncmp(opt_arg, "decimal", len) == 0) {
701 bin_scheme = BINSCHEME_DECIMAL;
702 } else {
703 skAppPrintErr(
704 "Illegal %s '%s'. Valid schemes: linear, binary, decimal",
705 appOptions[opt_index].name, opt_arg);
706 return 1;
707 }
708 break;
709
710 case OPT_SORT_COUNTERS:
711 if (NULL == opt_arg) {
712 sort_counters = 1;
713 } else if (0 == (len = strlen(opt_arg))) {
714 skAppPrintErr("Invalid %s: Switch requires an argument",
715 appOptions[opt_index].name);
716 return 1;
717 } else if (0 == strncmp("decreasing", opt_arg, len)) {
718 sort_counters = 1;
719 } else if (0 == strncmp("increasing", opt_arg, len)) {
720 sort_counters = -1;
721 } else {
722 skAppPrintErr(
723 "Invalid %s '%s': Valid values: decreasing, increasing",
724 appOptions[opt_index].name, opt_arg);
725 return 1;
726 }
727 break;
728
729 case OPT_PRINT_STATISTICS:
730 if (opt_arg != NULL) {
731 if (stats_stream) {
732 skAppPrintErr("Invalid %s: Switch used multiple times",
733 appOptions[opt_index].name);
734 return 1;
735 }
736 if (setOutput(opt_arg, &stats_stream)) {
737 skAppPrintErr("Invalid %s '%s'",
738 appOptions[opt_index].name, opt_arg);
739 return 1;
740 }
741 }
742 print_statistics = 1;
743 break;
744
745 case OPT_MINCOUNTER:
746 rv = skStringParseUint64(&val64, opt_arg,
747 BAGCAT_MIN_COUNTER, SKBAG_COUNTER_MAX);
748 if (rv == SKUTILS_ERR_MINIMUM) {
749 skAppPrintErr(("Invalid %s: Smallest allowable value is %" PRIu64
750 ".\n"
751 "\tUse --%s to print records whose counters are 0"),
752 appOptions[opt_index].name, BAGCAT_MIN_COUNTER,
753 appOptions[OPT_ZERO_COUNTS].name);
754 return 1;
755 }
756 if (rv) {
757 goto PARSE_ERROR;
758 }
759 limits.mincounter = val64;
760 limits.active = 1;
761 break;
762
763 case OPT_MAXCOUNTER:
764 rv = skStringParseUint64(&val64, opt_arg,
765 BAGCAT_MIN_COUNTER, SKBAG_COUNTER_MAX);
766 if (rv) {
767 goto PARSE_ERROR;
768 }
769 limits.maxcounter = val64;
770 limits.active = 1;
771 break;
772
773 case OPT_MINKEY:
774 rv = skStringParseIP(&limits.minkey_ip, opt_arg);
775 if (rv) {
776 goto PARSE_ERROR;
777 }
778 if (skipaddrGetAsV4(&limits.minkey_ip, &limits.minkey_u32)) {
779 #if SK_ENABLE_IPV6
780 limits.minkey_u32 = 1;
781 #endif /* SK_ENABLE_IPV6 */
782 }
783 min_key = opt_arg;
784 limits.key_is_min = 1;
785 limits.active = 1;
786 break;
787
788 case OPT_MAXKEY:
789 rv = skStringParseIP(&limits.maxkey_ip, opt_arg);
790 if (rv) {
791 goto PARSE_ERROR;
792 }
793 if (skipaddrGetAsV4(&limits.maxkey_ip, &limits.maxkey_u32)) {
794 #if SK_ENABLE_IPV6
795 limits.maxkey_u32 = UINT32_MAX;
796 #endif /* SK_ENABLE_IPV6 */
797 }
798 max_key = opt_arg;
799 limits.key_is_max = 1;
800 limits.active = 1;
801 break;
802
803 case OPT_MASK_SET:
804 if (limits.mask_set) {
805 skAppPrintErr("Invalid %s: Switch used multiple times",
806 appOptions[opt_index].name);
807 return 1;
808 }
809 if ((rv = skStreamCreate(&stream, SK_IO_READ, SK_CONTENT_SILK))
810 || (rv = skStreamBind(stream, opt_arg))
811 || (rv = skStreamOpen(stream)))
812 {
813 skStreamPrintLastErr(stream, rv, &skAppPrintErr);
814 skStreamDestroy(&stream);
815 return 1;
816 }
817 rv = skIPSetRead(&limits.mask_set, stream);
818 if (rv) {
819 if (SKIPSET_ERR_FILEIO == rv) {
820 skStreamPrintLastErr(stream, skStreamGetLastReturnValue(stream),
821 &skAppPrintErr);
822 } else {
823 skAppPrintErr("Unable to read IPset from '%s': %s",
824 opt_arg, skIPSetStrerror(rv));
825 }
826 skStreamDestroy(&stream);
827 return 1;
828 }
829 skStreamDestroy(&stream);
830 limits.active = 1;
831 break;
832
833 case OPT_OUTPUT_PATH:
834 if (output) {
835 skAppPrintErr("Invalid %s: Switch used multiple times",
836 appOptions[opt_index].name);
837 return 1;
838 }
839 if (setOutput(opt_arg, &output)) {
840 skAppPrintErr("Invalid %s '%s'",
841 appOptions[opt_index].name, opt_arg);
842 return 1;
843 }
844 break;
845
846 case OPT_NO_COLUMNS:
847 no_columns = 1;
848 break;
849
850 case OPT_NO_FINAL_DELIMITER:
851 no_final_delimiter = 1;
852 break;
853
854 case OPT_COLUMN_SEPARATOR:
855 output_delimiter = opt_arg[0];
856 break;
857
858 case OPT_DELIMITED:
859 no_columns = 1;
860 no_final_delimiter = 1;
861 if (opt_arg) {
862 output_delimiter = opt_arg[0];
863 }
864 break;
865
866 case OPT_KEY_FORMAT:
867 key_format_arg = opt_arg;
868 if (keyFormatParse(opt_arg)) {
869 return 1;
870 }
871 break;
872
873 case OPT_INTEGER_KEYS:
874 if (keyFormatParse("decimal")) {
875 skAbort();
876 }
877 break;
878
879 case OPT_ZERO_PAD_IPS:
880 if (keyFormatParse("zero-padded")) {
881 skAbort();
882 }
883 break;
884
885 case OPT_ZERO_COUNTS:
886 print_zero_counts = 1;
887 break;
888
889 case OPT_PAGER:
890 pager = opt_arg;
891 break;
892 }
893
894 return 0; /* OK */
895
896 PARSE_ERROR:
897 skAppPrintErr("Invalid %s '%s': %s",
898 appOptions[opt_index].name, opt_arg,
899 skStringParseStrerror(rv));
900 return 1;
901 }
902
903
904 /*
905 * status = keyFormatParse(format_string);
906 *
907 * Parse the key-format value contained in 'format_string'. Return
908 * 0 on success, or -1 if parsing of the value fails.
909 */
910 static int
keyFormatParse(const char * format)911 keyFormatParse(
912 const char *format)
913 {
914 const unsigned format_timezone = (SKTIMESTAMP_UTC | SKTIMESTAMP_LOCAL);
915 const unsigned format_ip_mapping = (SKIPADDR_MAP_V4 | SKIPADDR_UNMAP_V6);
916 char *errmsg;
917 sk_stringmap_iter_t *iter = NULL;
918 sk_stringmap_entry_t *found_entry;
919 int rv = -1;
920
921 /* attempt to match the user's format */
922 if (skStringMapParse(
923 key_format_map, format, SKSTRINGMAP_DUPES_ERROR, &iter, &errmsg))
924 {
925 skAppPrintErr("Invalid %s: %s",
926 appOptions[OPT_KEY_FORMAT].name, errmsg);
927 goto END;
928 }
929
930 /* determine which value(s) the user specified and check for
931 * conflicting values. The only multiple-value possibility that is
932 * allowed is a timezone with a time format. */
933 while (skStringMapIterNext(iter, &found_entry, NULL) == SK_ITERATOR_OK) {
934 if (!key_format) {
935 key_format = found_entry->id;
936 } else if ((KEY_FORMAT_MASK & key_format)
937 != (KEY_FORMAT_MASK & found_entry->id))
938 {
939 skAppPrintErr("Invalid %s '%s': Combination is nonsensical",
940 appOptions[OPT_KEY_FORMAT].name, format);
941 goto END;
942 } else if ((KEY_FORMAT_MASK & key_format) == KEY_FORMAT_IP) {
943 if (found_entry->id == (KEY_FORMAT_IP | SKIPADDR_ZEROPAD)) {
944 key_format |= found_entry->id;
945 } else if ((key_format & format_ip_mapping)
946 && (found_entry->id & format_ip_mapping))
947 {
948 skAppPrintErr(
949 "Invalid %s '%s': May only specify one IP mapping option",
950 appOptions[OPT_KEY_FORMAT].name, format);
951 goto END;
952 } else if (0 == ((format_ip_mapping | SKIPADDR_ZEROPAD)
953 & (key_format | found_entry->id)))
954 {
955 skAppPrintErr("Invalid %s '%s': May only specify one IP format",
956 appOptions[OPT_KEY_FORMAT].name, format);
957 goto END;
958 } else {
959 key_format |= found_entry->id;
960 }
961 } else if (SKTIMESTAMP_EPOCH & (key_format | found_entry->id)) {
962 skAppPrintErr(
963 "Invalid %s '%s': May not use another time format with '%s'",
964 appOptions[OPT_KEY_FORMAT].name, format,
965 skStringMapGetFirstName(
966 key_format_map, (KEY_FORMAT_TIME | SKTIMESTAMP_EPOCH)));
967 goto END;
968 } else if ((key_format & format_timezone)
969 && (found_entry->id & format_timezone))
970 {
971 skAppPrintErr(
972 "Invalid %s '%s': May only specify one timezone format",
973 appOptions[OPT_KEY_FORMAT].name, format);
974 goto END;
975 } else if (0 == (format_timezone & (key_format | found_entry->id))) {
976 skAppPrintErr(
977 "Invalid %s '%s': May only specify one time format",
978 appOptions[OPT_KEY_FORMAT].name, format);
979 goto END;
980 } else {
981 key_format |= found_entry->id;
982 }
983 }
984
985 rv = 0;
986
987 END:
988 skStringMapIterDestroy(iter);
989 return rv;
990 }
991
992
993 /*
994 * keyFormatUsage(fh);
995 *
996 * Print the description of the argument to the --key-format
997 * switch to the 'fh' file handle.
998 */
999 static void
keyFormatUsage(FILE * fh)1000 keyFormatUsage(
1001 FILE *fh)
1002 {
1003 char bagtype[SKBAG_MAX_FIELD_BUFLEN];
1004
1005 fprintf(
1006 fh, ("Print keys in specified format. Default is determined by\n"
1007 "\tthe type of key in the bag;"
1008 " the '%s' format is used when bag's\n"
1009 "\tkey is %s or has no type, '%s' format otherwise. Choices:\n"),
1010 skStringMapGetFirstName(
1011 key_format_map, KEY_FORMAT_IP | SKIPADDR_CANONICAL),
1012 skBagFieldTypeAsString(SKBAG_FIELD_CUSTOM, bagtype, sizeof(bagtype)),
1013 skStringMapGetFirstName(
1014 key_format_map, KEY_FORMAT_IP | SKIPADDR_DECIMAL));
1015 skStringMapPrintDetailedUsage(key_format_map, fh);
1016 }
1017
1018
1019 /*
1020 * Parse the [MAPNAME:]PMAP_PATH option and set the result in the
1021 * global 'prefix_map'. Return 0 on success or -1 on error.
1022 */
1023 static int
parsePmapFileOption(const char * opt_arg)1024 parsePmapFileOption(
1025 const char *opt_arg)
1026 {
1027 skPrefixMapErr_t rv_map;
1028 skstream_t *stream;
1029 const char *sep;
1030 const char *filename;
1031 int rv;
1032
1033 /* check for a leading mapname */
1034 sep = strchr(opt_arg, ':');
1035 if (NULL == sep) {
1036 /* no mapname; check for one in the pmap once we read it */
1037 filename = opt_arg;
1038 } else if (sep == opt_arg) {
1039 /* treat a 0-length mapname on the command as having none.
1040 * Allows use of default mapname for files that contain the
1041 * separator. */
1042 filename = sep + 1;
1043 } else {
1044 /* a mapname was supplied on the command line */
1045 filename = sep + 1;
1046 #if 0
1047 /* no need to keep the mapname */
1048 size_t namelen;
1049 char *mapname;
1050
1051 if (memchr(opt_arg, ',', sep - opt_arg) != NULL) {
1052 skAppPrintErr("Invalid %s: The map-name may not include a comma",
1053 appOptions[OPT_PMAP_FILE].name);
1054 goto END;
1055 }
1056 namelen = sep - opt_arg;
1057 mapname = (char *)malloc(namelen + 1);
1058 if (NULL == mapname) {
1059 skAppPrintOutOfMemory(NULL);
1060 goto END;
1061 }
1062 strncpy(mapname, opt_arg, namelen);
1063 mapname[namelen] = '\0';
1064 #endif /* 0 */
1065 }
1066
1067 /* open the file and read the prefix map */
1068 if ((rv = skStreamCreate(&stream, SK_IO_READ, SK_CONTENT_SILK))
1069 || (rv = skStreamBind(stream, filename))
1070 || (rv = skStreamOpen(stream)))
1071 {
1072 skStreamPrintLastErr(stream, rv, &skAppPrintErr);
1073 skStreamDestroy(&stream);
1074 return -1;
1075 }
1076 rv_map = skPrefixMapRead(&prefix_map, stream);
1077 if (SKPREFIXMAP_OK != rv_map) {
1078 if (SKPREFIXMAP_ERR_IO == rv_map) {
1079 skStreamPrintLastErr(
1080 stream, skStreamGetLastReturnValue(stream), &skAppPrintErr);
1081 } else {
1082 skAppPrintErr("Failed to read the prefix map file '%s': %s",
1083 filename, skPrefixMapStrerror(rv_map));
1084 }
1085 skStreamDestroy(&stream);
1086 return -1;
1087 }
1088 skStreamDestroy(&stream);
1089
1090 #if 0
1091 /* get the mapname from the file when none on command line */
1092 if (NULL == mapname) {
1093 if (NULL == skPrefixMapGetMapName(prefix_map)) {
1094 skAppPrintErr(("Invalid %s '%s': Prefix map file does not contain"
1095 " a map-name and none provided on the command line"),
1096 appOptions[OPT_PMAP_FILE].name, filename);
1097 goto END;
1098 }
1099 mapname = strdup(skPrefixMapGetMapName(prefix_map));
1100 if (NULL == mapname) {
1101 skAppPrintOutOfMemory(NULL);
1102 goto END;
1103 }
1104 }
1105 #endif /* 0 */
1106
1107 return 0;
1108 }
1109
1110
1111 /*
1112 * status = setOutput(name, &stream);
1113 *
1114 * Set stream's output to 'name'. If 'name' is the standard output
1115 * and an existing stream is already open to the standard output,
1116 * set 'stream' to that existing stream. Return 0 on success, -1
1117 * otherwise.
1118 */
1119 static int
setOutput(const char * filename,skstream_t ** stream)1120 setOutput(
1121 const char *filename,
1122 skstream_t **stream)
1123 {
1124 int rv;
1125
1126 assert(stream);
1127
1128 if (filename == NULL || filename[0] == '\0') {
1129 skAppPrintErr("Empty filename");
1130 return -1;
1131 }
1132
1133 /* compare 'filename' with known streams */
1134 if (output) {
1135 if ((0 == strcmp(skStreamGetPathname(output), filename))
1136 || (0 == strcmp(filename, "stdout")
1137 && 0 == strcmp(skStreamGetPathname(output), "-"))
1138 || (0 == strcmp(filename, "-")
1139 && 0 == strcmp(skStreamGetPathname(output), "stdout")))
1140 {
1141 *stream = output;
1142 return 0;
1143 }
1144 }
1145 if (stats_stream) {
1146 if ((0 == strcmp(skStreamGetPathname(stats_stream), filename))
1147 || (0 == strcmp(filename, "stdout")
1148 && 0 == strcmp(skStreamGetPathname(stats_stream), "-"))
1149 || (0 == strcmp(filename, "-")
1150 && 0 == strcmp(skStreamGetPathname(stats_stream), "stdout")))
1151 {
1152 *stream = stats_stream;
1153 return 0;
1154 }
1155 }
1156
1157 if ((rv = skStreamCreate(stream, SK_IO_WRITE, SK_CONTENT_TEXT))
1158 || (rv = skStreamBind(*stream, filename)))
1159 {
1160 skStreamPrintLastErr(*stream, rv, &skAppPrintErr);
1161 skStreamDestroy(stream);
1162 return -1;
1163 }
1164
1165 return 0;
1166 }
1167
1168
1169 /*
1170 * Create and print a temporary bag whose keys are related to the
1171 * counters of the input bag and the counters are the number of
1172 * unique keys in the input bag.
1173 */
1174 static int
bagcatInvertBag(const skBag_t * bag)1175 bagcatInvertBag(
1176 const skBag_t *bag)
1177 {
1178 skBag_t *inverted_bag = NULL;
1179 skBagIterator_t *iter = NULL;
1180 skBagTypedKey_t key;
1181 skBagTypedCounter_t counter;
1182 skBagTypedKey_t bin;
1183 char s_label[64];
1184 char final_delim[] = {'\0', '\0'};
1185 int rv = 1;
1186
1187 INVOKE_PAGER();
1188
1189 if (!no_final_delimiter) {
1190 final_delim[0] = output_delimiter;
1191 }
1192
1193 /* Create an inverted bag */
1194 if (skBagCreate(&inverted_bag) != SKBAG_OK) {
1195 goto END;
1196 }
1197 if (skBagIteratorCreate(bag, &iter) != SKBAG_OK) {
1198 goto END;
1199 }
1200
1201 /* get key from bag as an ip address */
1202 key.type = SKBAG_KEY_IPADDR;
1203 counter.type = SKBAG_COUNTER_U64;
1204
1205 bin.type = SKBAG_KEY_U32;
1206
1207 /* loop over the entries, check whether they are in limits, and if
1208 * so, add the inverted entry to the bag */
1209 while (skBagIteratorNextTyped(iter, &key, &counter) == SKBAG_OK) {
1210 if (!CHECK_LIMITS_IPADDR(&key, &counter)) {
1211 continue;
1212 }
1213 switch (bin_scheme) {
1214 case BINSCHEME_LINEAR:
1215 bin.val.u32 = (uint32_t)((counter.val.u64 < UINT32_MAX)
1216 ? counter.val.u64
1217 : UINT32_MAX);
1218 break;
1219 case BINSCHEME_BINARY:
1220 bin.val.u32 = skIntegerLog2(counter.val.u64);
1221 break;
1222 case BINSCHEME_DECIMAL:
1223 if (counter.val.u64 < 100) {
1224 bin.val.u32 = (uint32_t)counter.val.u64;
1225 } else {
1226 bin.val.u32
1227 = (uint32_t)floor((log10((double)counter.val.u64) - 1.0)
1228 * 100.0);
1229 }
1230 break;
1231 case BINSCHEME_NONE:
1232 skAbortBadCase(bin_scheme);
1233 }
1234 if (skBagCounterIncrement(inverted_bag, &bin) != SKBAG_OK) {
1235 goto END;
1236 }
1237 }
1238
1239 skBagIteratorDestroy(iter);
1240 iter = NULL;
1241
1242 /* iterate over inverted bag to print entries */
1243 if (skBagIteratorCreate(inverted_bag, &iter) != SKBAG_OK) {
1244 goto END;
1245 }
1246
1247 while (skBagIteratorNextTyped(iter, &bin, &counter) == SKBAG_OK) {
1248 switch (bin_scheme) {
1249 case BINSCHEME_LINEAR:
1250 /* label is just bin number */
1251 snprintf(s_label, sizeof(s_label), ("%" PRIu32), bin.val.u32);
1252 break;
1253
1254 case BINSCHEME_BINARY:
1255 /* label is range of values "2^03 to 2^04-1" */
1256 snprintf(s_label, sizeof(s_label),
1257 ("2^%02" PRIu32 " to 2^%02" PRIu32 "-1"),
1258 bin.val.u32, (bin.val.u32 + 1));
1259 break;
1260
1261 case BINSCHEME_DECIMAL:
1262 /* label is the median value of possible keys in that bin */
1263 if (bin.val.u32 < 100) {
1264 snprintf(s_label, sizeof(s_label), ("%" PRIu32),bin.val.u32);
1265 } else {
1266 double min, max, mid;
1267 min = ceil(pow(10, (((double)bin.val.u32 / 100.0) + 1.0)));
1268 max = floor(pow(10, ((((double)bin.val.u32 + 1.0)/100.0)+1.0)));
1269 mid = floor((min + max) / 2.0);
1270 snprintf(s_label, sizeof(s_label), "%.0f", mid);
1271 }
1272 break;
1273
1274 case BINSCHEME_NONE:
1275 skAbortBadCase(bin_scheme);
1276 }
1277
1278 if (!no_columns) {
1279 skStreamPrint(output, ("%*s%c%*" PRIu64 "%s\n"),
1280 COUNT_WIDTH, s_label, output_delimiter,
1281 COUNT_WIDTH, counter.val.u64, final_delim);
1282 } else {
1283 skStreamPrint(output, ("%s%c%" PRIu64 "%s\n"),
1284 s_label, output_delimiter,
1285 counter.val.u64, final_delim);
1286 }
1287 }
1288
1289 rv = 0;
1290
1291 END:
1292 skBagDestroy(&inverted_bag);
1293 skBagIteratorDestroy(iter);
1294
1295 return rv;
1296 }
1297
1298
1299 /*
1300 * Print a single key-counter pair. Used when the key is printed
1301 * as a non-IP and non-number.
1302 *
1303 * Helper for bagcatPrintBag().
1304 */
1305 static void
bagcatPrintBagRow(const state_t * state,const skBagTypedKey_t * key,const skBagTypedCounter_t * counter)1306 bagcatPrintBagRow(
1307 const state_t *state,
1308 const skBagTypedKey_t *key,
1309 const skBagTypedCounter_t *counter)
1310 {
1311 switch (state->bc_key->formatter) {
1312 case BAGCAT_FMT_ATTRIBUTES:
1313 skTCPStateString(key->val.u32, state->buf,
1314 state->bc_key->formatter_flags);
1315 skStreamPrint(output, "%*s%c%*" PRIu64 "%s\n",
1316 state->width[0], state->buf, output_delimiter,
1317 state->width[1], counter->val.u64, state->end_of_line);
1318 break;
1319
1320 case BAGCAT_FMT_COUNTRY:
1321 skCountryCodeToName(key->val.u32, state->buf, state->buflen);
1322 skStreamPrint(output, "%*s%c%*" PRIu64 "%s\n",
1323 state->width[0], state->buf, output_delimiter,
1324 state->width[1], counter->val.u64, state->end_of_line);
1325 break;
1326
1327 case BAGCAT_FMT_IPADDR:
1328 skipaddrString(state->buf, &key->val.addr,
1329 state->bc_key->formatter_flags);
1330 skStreamPrint(output, "%*s%c%*" PRIu64 "%s\n",
1331 state->width[0], state->buf, output_delimiter,
1332 state->width[1], counter->val.u64, state->end_of_line);
1333 break;
1334
1335 case BAGCAT_FMT_PMAP:
1336 skPrefixMapDictionaryGetEntry(prefix_map, key->val.u32, state->buf,
1337 state->buflen);
1338 skStreamPrint(output, "%*s%c%*" PRIu64 "%s\n",
1339 state->width[0], state->buf, output_delimiter,
1340 state->width[1], counter->val.u64, state->end_of_line);
1341 break;
1342
1343 case BAGCAT_FMT_SENSOR:
1344 sksiteSensorGetName(state->buf, state->buflen, key->val.u32);
1345 skStreamPrint(output, "%*s%c%*" PRIu64 "%s\n",
1346 state->width[0], state->buf, output_delimiter,
1347 state->width[1], counter->val.u64, state->end_of_line);
1348 break;
1349
1350 case BAGCAT_FMT_TCPFLAGS:
1351 skTCPFlagsString(key->val.u32, state->buf,
1352 state->bc_key->formatter_flags);
1353 skStreamPrint(output, "%*s%c%*" PRIu64 "%s\n",
1354 state->width[0], state->buf, output_delimiter,
1355 state->width[1], counter->val.u64, state->end_of_line);
1356 break;
1357
1358 case BAGCAT_FMT_TIME:
1359 sktimestamp_r(state->buf, sktimeCreate(key->val.u32, 0),
1360 state->bc_key->formatter_flags);
1361 skStreamPrint(output, "%*s%c%*" PRIu64 "%s\n",
1362 state->width[0], state->buf, output_delimiter,
1363 state->width[1], counter->val.u64, state->end_of_line);
1364 break;
1365 }
1366 }
1367
1368 /*
1369 * Print the contents of a bag file when the key is being displayed
1370 * as something other than an IP address or a number.
1371 */
1372 static int
bagcatPrintBag(const state_t * state,const skBag_t * bag)1373 bagcatPrintBag(
1374 const state_t *state,
1375 const skBag_t *bag)
1376 {
1377 skBagTypedKey_t key;
1378 skBagTypedCounter_t counter;
1379
1380 INVOKE_PAGER();
1381
1382 /* set type for key and counter */
1383 key.type = SKBAG_KEY_U32;
1384 counter.type = SKBAG_COUNTER_U64;
1385
1386 if (!limits.active) {
1387 /* print contents of the bag */
1388 skBagIterator_t *b_iter;
1389
1390 if (skBagIteratorCreate(bag, &b_iter) != SKBAG_OK) {
1391 return 1;
1392 }
1393 while (skBagIteratorNextTyped(b_iter, &key, &counter) == SKBAG_OK) {
1394 bagcatPrintBagRow(state, &key, &counter);
1395 }
1396 skBagIteratorDestroy(b_iter);
1397
1398 } else if (0 == print_zero_counts) {
1399 /* print contents of the bag, subject to limits */
1400 skBagIterator_t *b_iter;
1401
1402 if (skBagIteratorCreate(bag, &b_iter) != SKBAG_OK) {
1403 return 1;
1404 }
1405 while (skBagIteratorNextTyped(b_iter, &key, &counter) == SKBAG_OK) {
1406 if (CHECK_LIMITS_UINT32(&key, &counter)) {
1407 bagcatPrintBagRow(state, &key, &counter);
1408 }
1409 }
1410 skBagIteratorDestroy(b_iter);
1411
1412 } else {
1413 /* print keys between two key values, subject to maximum
1414 * counter limit */
1415
1416 /* handle first key */
1417 key.val.u32 = limits.minkey_u32;
1418 skBagCounterGet(bag, &key, &counter);
1419 if (counter.val.u64 <= limits.maxcounter) {
1420 bagcatPrintBagRow(state, &key, &counter);
1421 }
1422 /* handle remaining keys */
1423 while (key.val.u32 < limits.maxkey_u32) {
1424 ++key.val.u32;
1425 skBagCounterGet(bag, &key, &counter);
1426 if (counter.val.u64 <= limits.maxcounter) {
1427 bagcatPrintBagRow(state, &key, &counter);
1428 }
1429 }
1430 }
1431
1432 return 0;
1433 }
1434
1435
1436 /*
1437 * Print a single key-counter pair. Used when the key is printed
1438 * as either an IP or as a decimal or hexadecimal number.
1439 *
1440 * Helper for bagcatPrintNetwork().
1441 */
1442 static void
bagcatPrintNetworkRow(sk_netstruct_t * ns,const skBagTypedKey_t * key,const skBagTypedCounter_t * counter)1443 bagcatPrintNetworkRow(
1444 sk_netstruct_t *ns,
1445 const skBagTypedKey_t *key,
1446 const skBagTypedCounter_t *counter)
1447 {
1448 skNetStructureAddKeyCounter(ns, &key->val.addr, &counter->val.u64);
1449 }
1450
1451 /*
1452 * Print the contents of a bag file using the print-network code
1453 * from libsilk.
1454 */
1455 static int
bagcatPrintNetwork(sk_netstruct_t * ns,const skBag_t * bag)1456 bagcatPrintNetwork(
1457 sk_netstruct_t *ns,
1458 const skBag_t *bag)
1459 {
1460 skBagTypedKey_t key;
1461 skBagTypedCounter_t counter;
1462
1463 /* set type for key and counter */
1464 key.type = SKBAG_KEY_IPADDR;
1465 counter.type = SKBAG_COUNTER_U64;
1466
1467 if (!limits.active) {
1468 /* print all entries in the bag */
1469 skBagIterator_t *b_iter;
1470
1471 if (skBagIteratorCreate(bag, &b_iter) != SKBAG_OK) {
1472 return 1;
1473 }
1474 while (skBagIteratorNextTyped(b_iter, &key, &counter) == SKBAG_OK) {
1475 bagcatPrintNetworkRow(ns, &key, &counter);
1476 }
1477 skBagIteratorDestroy(b_iter);
1478
1479 } else if (0 == print_zero_counts) {
1480 /* print entries in the bag that meet the limits */
1481 skBagIterator_t *b_iter;
1482
1483 if (skBagIteratorCreate(bag, &b_iter) != SKBAG_OK) {
1484 return 1;
1485 }
1486 while (skBagIteratorNextTyped(b_iter, &key, &counter) == SKBAG_OK) {
1487 if (CHECK_LIMITS_IPADDR(&key, &counter)) {
1488 bagcatPrintNetworkRow(ns, &key, &counter);
1489 }
1490 }
1491 skBagIteratorDestroy(b_iter);
1492
1493 } else if (NULL == limits.mask_set) {
1494 /* print entries whose keys are between two key values,
1495 * subject to maximum counter limit */
1496
1497 /* handle first key */
1498 skipaddrCopy(&key.val.addr, &limits.minkey_ip);
1499 skBagCounterGet(bag, &key, &counter);
1500 if (counter.val.u64 <= limits.maxcounter) {
1501 bagcatPrintNetworkRow(ns, &key, &counter);
1502 }
1503
1504 /* handle remaining keys */
1505 while (skipaddrCompare(&key.val.addr, &limits.maxkey_ip) < 0) {
1506 skipaddrIncrement(&key.val.addr);
1507 skBagCounterGet(bag, &key, &counter);
1508 if (counter.val.u64 <= limits.maxcounter) {
1509 bagcatPrintNetworkRow(ns, &key, &counter);
1510 }
1511 }
1512
1513 } else if (0 == limits.key_is_min && 0 == limits.key_is_max) {
1514 /* print entries whose keys appear in the IPset, subject to
1515 * the maximum counter limit */
1516 skipset_iterator_t s_iter;
1517 uint32_t cidr;
1518
1519 skIPSetIteratorBind(&s_iter, limits.mask_set, 0, SK_IPV6POLICY_MIX);
1520 while (skIPSetIteratorNext(&s_iter, &key.val.addr, &cidr)
1521 == SK_ITERATOR_OK)
1522 {
1523 skBagCounterGet(bag, &key, &counter);
1524 if (counter.val.u64 <= limits.maxcounter) {
1525 bagcatPrintNetworkRow(ns, &key, &counter);
1526 }
1527 }
1528
1529 } else {
1530 /* print entries whose keys appear in the IPset and are within
1531 * the --minkey and --maxkey range, subject to the maximum
1532 * counter limit */
1533 skipset_iterator_t s_iter;
1534 uint32_t cidr;
1535 int s_rv;
1536
1537 skIPSetIteratorBind(&s_iter, limits.mask_set, 0, SK_IPV6POLICY_MIX);
1538 /* ignore IPs less then --minkey */
1539 while (((s_rv = skIPSetIteratorNext(&s_iter, &key.val.addr, &cidr))
1540 == SK_ITERATOR_OK)
1541 && (skipaddrCompare(&key.val.addr, &limits.minkey_ip) < 0))
1542 ; /* empty */
1543 if (SK_ITERATOR_OK == s_rv) {
1544 do {
1545 skBagCounterGet(bag, &key, &counter);
1546 if (counter.val.u64 <= limits.maxcounter) {
1547 bagcatPrintNetworkRow(ns, &key, &counter);
1548 }
1549 } while ((skIPSetIteratorNext(&s_iter, &key.val.addr, &cidr)
1550 == SK_ITERATOR_OK)
1551 && (skipaddrCompare(&key.val.addr, &limits.maxkey_ip)
1552 < 0));
1553 }
1554 }
1555
1556 skNetStructurePrintFinalize(ns);
1557
1558 return 0;
1559 }
1560
1561
1562 /*
1563 * Comparison function used by the heap that is used to implement
1564 * the --sort-counters switch. Return a positive value if 'a'
1565 * should be closer to the root of the tree.
1566 *
1567 * To have a decreasing sort order, the root of the tree should
1568 * contain the smallest value. 'sort_counters' is 1 for decreasing
1569 * sort order and -1 for increasing order.
1570 */
1571 static int
bagcatHeapCompare(const skheapnode_t a,const skheapnode_t b)1572 bagcatHeapCompare(
1573 const skheapnode_t a,
1574 const skheapnode_t b)
1575 {
1576 const bagcat_heapnode_t *a_node = (bagcat_heapnode_t *)a;
1577 const bagcat_heapnode_t *b_node = (bagcat_heapnode_t *)b;
1578
1579 if (a_node->counter != b_node->counter) {
1580 return ((a_node->counter < b_node->counter)
1581 ? sort_counters
1582 : -sort_counters);
1583 }
1584 if (SKBAG_KEY_IPADDR == a_node->key.type) {
1585 return (skipaddrCompare(&a_node->key.val.addr, &b_node->key.val.addr));
1586 }
1587 return ((b_node->key.val.u32 < a_node->key.val.u32)
1588 ? -1
1589 : (b_node->key.val.u32 > a_node->key.val.u32));
1590 }
1591
1592
1593 /*
1594 * Add a 'key' and 'counter' pair to 'heap'.
1595 */
1596 static void
bagcatHeapAdd(skheap_t * heap,const skBagTypedKey_t * key,const skBagTypedCounter_t * counter)1597 bagcatHeapAdd(
1598 skheap_t *heap,
1599 const skBagTypedKey_t *key,
1600 const skBagTypedCounter_t *counter)
1601 {
1602 static int not_full = 1;
1603 static bagcat_heapnode_t *top_heap;
1604 bagcat_heapnode_t heap_entry;
1605
1606 /* the new entry */
1607 heap_entry.counter = counter->val.u64;
1608 memcpy(&heap_entry.key, key, sizeof(*key));
1609
1610 if (not_full) {
1611 if (skHeapInsert(heap, &heap_entry) == SKHEAP_OK) {
1612 return;
1613 }
1614 not_full = 0;
1615
1616 /* Cannot grow the heap any more; process remaining
1617 * records using this fixed heap size */
1618 skAppPrintErr("Cannot grow heap; limiting to %u entries",
1619 skHeapGetNumberEntries(heap));
1620
1621 /* Get the node at the top of heap and its value. For
1622 * decreasing sort order, this is the smallest value. */
1623 skHeapPeekTop(heap, (skheapnode_t *)&top_heap);
1624 }
1625
1626 if (bagcatHeapCompare(top_heap, &heap_entry) > 0) {
1627 /* The element we just read is "better" */
1628 skHeapReplaceTop(heap, &heap_entry, NULL);
1629
1630 /* the top may have changed; get the new top */
1631 skHeapPeekTop(heap, (skheapnode_t*)&top_heap);
1632 }
1633 }
1634
1635
1636 /*
1637 * Provide implementation of --sort-counters.
1638 *
1639 * Process the entries in 'bag', adding each key/value pair that
1640 * meets the limits to a heap data structure. Once all entries are
1641 * process, sort the heap and print the results.
1642 */
1643 static int
bagcatSortCounters(const skBag_t * bag,const bagcat_key_t * bc_key)1644 bagcatSortCounters(
1645 const skBag_t *bag,
1646 const bagcat_key_t *bc_key)
1647 {
1648 uint32_t count;
1649 state_t state;
1650 skBagFieldType_t key_field;
1651 skBagTypedKey_t key;
1652 skBagTypedCounter_t counter;
1653 skheap_t *heap;
1654 skheapiterator_t *itheap;
1655 bagcat_heapnode_t *heap_entry;
1656 int rv = 1;
1657
1658 heap = NULL;
1659 itheap = NULL;
1660
1661 memset(&key, 0, sizeof(key));
1662
1663 memset(&state, 0, sizeof(state));
1664 state.bc_key = bc_key;
1665 if (!no_final_delimiter) {
1666 state.end_of_line[0] = output_delimiter;
1667 }
1668 if (!no_columns) {
1669 state.width[0] = bc_key->width;
1670 state.width[1] = COUNT_WIDTH;
1671 }
1672 state.buflen = bc_key->buflen;
1673 state.buf = (char *)malloc(state.buflen);
1674 state.buf[0] = '\0';
1675
1676 switch (skBagKeyFieldLength(bag)) {
1677 case 1:
1678 count = (1u << 8);
1679 break;
1680 case 2:
1681 count = (1u << 16);
1682 break;
1683 default:
1684 count = BAGCAT_HEAP_INITIAL_SIZE;
1685 break;
1686 }
1687
1688 /* create the heap */
1689 while (NULL == (heap = skHeapCreate(bagcatHeapCompare, count,
1690 sizeof(bagcat_heapnode_t), NULL)))
1691 {
1692 count >>= 1;
1693 if (count < UINT8_MAX) {
1694 skAppPrintOutOfMemory("creating heap");
1695 exit(EXIT_FAILURE);
1696 }
1697 }
1698
1699 key_field = skBagKeyFieldType(bag);
1700
1701 /* set type for key and counter */
1702 counter.type = SKBAG_COUNTER_U64;
1703 switch (key_field) {
1704 case SKBAG_FIELD_CUSTOM:
1705 case SKBAG_FIELD_SIPv4:
1706 case SKBAG_FIELD_DIPv4:
1707 case SKBAG_FIELD_NHIPv4:
1708 case SKBAG_FIELD_ANY_IPv4:
1709 case SKBAG_FIELD_SIPv6:
1710 case SKBAG_FIELD_DIPv6:
1711 case SKBAG_FIELD_NHIPv6:
1712 case SKBAG_FIELD_ANY_IPv6:
1713 key.type = SKBAG_KEY_IPADDR;
1714 break;
1715 default:
1716 key.type = SKBAG_KEY_U32;
1717 break;
1718 }
1719
1720 /* Process the bag */
1721 if (!limits.active) {
1722 /* handle all entries in the bag */
1723 skBagIterator_t *b_iter;
1724
1725 if (skBagIteratorCreate(bag, &b_iter) != SKBAG_OK) {
1726 goto END;
1727 }
1728 while (skBagIteratorNextTyped(b_iter, &key, &counter) == SKBAG_OK) {
1729 bagcatHeapAdd(heap, &key, &counter);
1730 }
1731 skBagIteratorDestroy(b_iter);
1732
1733 } else if (0 == print_zero_counts) {
1734 /* handle entries in the bag that meet the limits */
1735 skBagIterator_t *b_iter;
1736
1737 if (skBagIteratorCreate(bag, &b_iter) != SKBAG_OK) {
1738 goto END;
1739 }
1740 if (SKBAG_KEY_U32 == key.type) {
1741 while (skBagIteratorNextTyped(b_iter, &key, &counter) == SKBAG_OK){
1742 if (CHECK_LIMITS_UINT32(&key, &counter)) {
1743 bagcatHeapAdd(heap, &key, &counter);
1744 }
1745 }
1746 } else {
1747 while (skBagIteratorNextTyped(b_iter, &key, &counter) == SKBAG_OK){
1748 if (CHECK_LIMITS_IPADDR(&key, &counter)) {
1749 bagcatHeapAdd(heap, &key, &counter);
1750 }
1751 }
1752 }
1753 skBagIteratorDestroy(b_iter);
1754
1755 } else if (SKBAG_KEY_U32 == key.type) {
1756 /* handle entries whose (numeric) keys are between two values,
1757 * subject to maximum counter limit */
1758
1759 /* handle first key */
1760 key.val.u32 = limits.minkey_u32;
1761 skBagCounterGet(bag, &key, &counter);
1762 if (counter.val.u64 <= limits.maxcounter) {
1763 bagcatHeapAdd(heap, &key, &counter);
1764 }
1765 /* handle remaining keys */
1766 while (key.val.u32 < limits.maxkey_u32) {
1767 ++key.val.u32;
1768 skBagCounterGet(bag, &key, &counter);
1769 if (counter.val.u64 <= limits.maxcounter) {
1770 bagcatHeapAdd(heap, &key, &counter);
1771 }
1772 }
1773
1774 } else if (NULL == limits.mask_set) {
1775 /* handle entries whose (IP) keys are between two values,
1776 * subject to maximum counter limit */
1777
1778 /* handle first key */
1779 skipaddrCopy(&key.val.addr, &limits.minkey_ip);
1780 skBagCounterGet(bag, &key, &counter);
1781 if (counter.val.u64 <= limits.maxcounter) {
1782 bagcatHeapAdd(heap, &key, &counter);
1783 }
1784 /* handle remaining keys */
1785 while (skipaddrCompare(&key.val.addr, &limits.maxkey_ip) < 0) {
1786 skipaddrIncrement(&key.val.addr);
1787 skBagCounterGet(bag, &key, &counter);
1788 if (counter.val.u64 <= limits.maxcounter) {
1789 bagcatHeapAdd(heap, &key, &counter);
1790 }
1791 }
1792
1793 } else if (0 == limits.key_is_min && 0 == limits.key_is_max) {
1794 /* handle entries whose keys appear in the IPset, subject to
1795 * the maximum counter limit */
1796 skipset_iterator_t s_iter;
1797 uint32_t cidr;
1798
1799 skIPSetIteratorBind(&s_iter, limits.mask_set, 0, SK_IPV6POLICY_MIX);
1800 while (skIPSetIteratorNext(&s_iter, &key.val.addr, &cidr)
1801 == SK_ITERATOR_OK)
1802 {
1803 skBagCounterGet(bag, &key, &counter);
1804 if (counter.val.u64 <= limits.maxcounter) {
1805 bagcatHeapAdd(heap, &key, &counter);
1806 }
1807 }
1808
1809 } else {
1810 /* handle entries whose keys appear in the IPset and are
1811 * within the --minkey and --maxkey range, subject to the
1812 * maximum counter limit */
1813 skipset_iterator_t s_iter;
1814 uint32_t cidr;
1815 int s_rv;
1816
1817 /* ensure minimum counter is 0*/
1818 limits.mincounter = SKBAG_COUNTER_MIN;
1819
1820 skIPSetIteratorBind(&s_iter, limits.mask_set, 0, SK_IPV6POLICY_MIX);
1821 while (((s_rv = skIPSetIteratorNext(&s_iter, &key.val.addr, &cidr))
1822 == SK_ITERATOR_OK)
1823 && (skipaddrCompare(&key.val.addr, &limits.minkey_ip) < 0))
1824 ; /* empty */
1825 if (SK_ITERATOR_OK == s_rv) {
1826 do {
1827 skBagCounterGet(bag, &key, &counter);
1828 if (counter.val.u64 <= limits.maxcounter) {
1829 bagcatHeapAdd(heap, &key, &counter);
1830 }
1831 } while ((skIPSetIteratorNext(&s_iter, &key.val.addr, &cidr)
1832 == SK_ITERATOR_OK)
1833 && (skipaddrCompare(&key.val.addr, &limits.maxkey_ip)< 0));
1834 }
1835 }
1836
1837 INVOKE_PAGER();
1838
1839 /* output the values in the heap */
1840 skHeapSortEntries(heap);
1841
1842 itheap = skHeapIteratorCreate(heap, -1);
1843 while (skHeapIteratorNext(itheap, (skheapnode_t *)&heap_entry)
1844 == SKHEAP_OK)
1845 {
1846 counter.val.u64 = heap_entry->counter;
1847 bagcatPrintBagRow(&state, &heap_entry->key, &counter);
1848 }
1849
1850 rv = 0;
1851
1852 END:
1853 skHeapIteratorFree(itheap);
1854 skHeapFree(heap);
1855 free(state.buf);
1856
1857 return rv;
1858 }
1859
1860
1861 /*
1862 * Output bag using current state of options
1863 */
1864 static int
bagcatProcessBag(const skBag_t * bag,const bagcat_key_t * bc_key)1865 bagcatProcessBag(
1866 const skBag_t *bag,
1867 const bagcat_key_t *bc_key)
1868 {
1869 char field_name[SKBAG_MAX_FIELD_BUFLEN];
1870 skBagFieldType_t key_field;
1871 const char *this_net_structure;
1872 state_t state;
1873 sk_netstruct_t *ns;
1874 int rv = 0;
1875
1876 /* structures that contain state to use while printing */
1877 memset(&state, 0, sizeof(state));
1878 ns = NULL;
1879
1880 key_field = skBagKeyFieldName(bag, field_name, sizeof(field_name));
1881
1882 /* it is an error when --network-structure is given and the bag
1883 * does not contain IP addresses */
1884 switch (key_field) {
1885 case SKBAG_FIELD_CUSTOM:
1886 case SKBAG_FIELD_SIPv4:
1887 case SKBAG_FIELD_DIPv4:
1888 case SKBAG_FIELD_NHIPv4:
1889 case SKBAG_FIELD_ANY_IPv4:
1890 case SKBAG_FIELD_SIPv6:
1891 case SKBAG_FIELD_DIPv6:
1892 case SKBAG_FIELD_NHIPv6:
1893 case SKBAG_FIELD_ANY_IPv6:
1894 if (net_structure) {
1895 this_net_structure = net_structure;
1896 } else if (print_network) {
1897 if (16 == skBagKeyFieldLength(bag)) {
1898 this_net_structure = "v6:";
1899 } else {
1900 this_net_structure = "v4:";
1901 }
1902 } else if (16 == skBagKeyFieldLength(bag)) {
1903 this_net_structure = "v6:H";
1904 } else {
1905 this_net_structure = "v4:H";
1906 }
1907 break;
1908 default:
1909 /* set 'this_net_structure' in case bagcatPrintNetwork() is
1910 * used to print values in decimal or hex */
1911 this_net_structure = "v4:H";
1912 if (print_network) {
1913 skAppPrintErr("Cannot use --%s with a Bag containing %s keys",
1914 appOptions[OPT_NETWORK_STRUCTURE].name, field_name);
1915 rv = 1;
1916 goto END;
1917 }
1918 break;
1919 }
1920
1921 if (BAGCAT_FMT_IPADDR == bc_key->formatter) {
1922 /* Set up the skNetStruct */
1923 if (skNetStructureCreate(&ns, 1)) {
1924 skAppPrintErr("Error creating network-structure");
1925 rv = 1;
1926 goto END;
1927 }
1928 skNetStructureSetCountWidth(ns, COUNT_WIDTH);
1929 if (skNetStructureParse(ns, this_net_structure)) {
1930 rv = 1;
1931 goto END;
1932 }
1933 skNetStructureSetOutputStream(ns, output);
1934 skNetStructureSetDelimiter(ns, output_delimiter);
1935 if (no_columns) {
1936 skNetStructureSetNoColumns(ns);
1937 }
1938 if (no_final_delimiter) {
1939 skNetStructureSetNoFinalDelimiter(ns);
1940 }
1941 skNetStructureSetIpFormat(ns, bc_key->formatter_flags);
1942
1943 INVOKE_PAGER();
1944
1945 if (bagcatPrintNetwork(ns, bag) != 0) {
1946 rv = 1;
1947 goto END;
1948 }
1949
1950 } else {
1951 /* state is the print state used by bagcatPrintBag() */
1952 state.bc_key = bc_key;
1953 if (!no_final_delimiter) {
1954 state.end_of_line[0] = output_delimiter;
1955 }
1956 if (!no_columns) {
1957 state.width[0] = bc_key->width;
1958 state.width[1] = COUNT_WIDTH;
1959 }
1960 state.buflen = bc_key->buflen;
1961 state.buf = (char *)malloc(state.buflen);
1962 state.buf[0] = '\0';
1963
1964 if (bagcatPrintBag(&state, bag)) {
1965 rv = 1;
1966 goto END;
1967 }
1968 }
1969
1970 END:
1971 skNetStructureDestroy(&ns);
1972 free(state.buf);
1973 return rv;
1974 }
1975
1976
1977 static int
printStatistics(const skBag_t * bag,const bagcat_key_t * bc_key,skstream_t * stream_out)1978 printStatistics(
1979 const skBag_t *bag,
1980 const bagcat_key_t *bc_key,
1981 skstream_t *stream_out)
1982 {
1983 double counter_temp = 0.0;
1984 double counter_mult = 0.0;
1985 double sum = 0.0; /* straight sum */
1986 double sum2 = 0.0; /* sum of squares */
1987 double sum3 = 0.0; /* sum of cubes */
1988
1989 double key_count = 0.0;
1990 double mean = 0.0;
1991 double stddev = 0.0;
1992 double temp = 0.0;
1993 double variance = 0.0;
1994 double skew = 0.0;
1995 double kurtosis = 0.0;
1996
1997 skBagIterator_t *iter;
1998 skBagTypedKey_t key;
1999 skBagTypedCounter_t counter;
2000
2001 skipaddr_t min_max_key[2];
2002 uint64_t min_seen_counter, max_seen_counter;
2003 char *key_buf[2];
2004 skBagErr_t rv;
2005 size_t i;
2006
2007 #define SUMS_OF_COUNTERS(soc_count) \
2008 { \
2009 /* straight sum */ \
2010 counter_temp = (double)(soc_count); \
2011 sum += counter_temp; \
2012 /* sum of squares */ \
2013 counter_mult = counter_temp * counter_temp; \
2014 sum2 += counter_mult; \
2015 /* sum of cubes */ \
2016 counter_mult *= counter_temp; \
2017 sum3 += counter_mult; \
2018 }
2019
2020 INVOKE_PAGER();
2021
2022 assert(bag != NULL);
2023 assert(stream_out != NULL);
2024
2025 if (skBagIteratorCreateUnsorted(bag, &iter) != SKBAG_OK) {
2026 return 1;
2027 }
2028
2029 key.type = SKBAG_KEY_IPADDR;
2030 counter.type = SKBAG_COUNTER_U64;
2031
2032 /* find first entry within limits */
2033 while ((rv = skBagIteratorNextTyped(iter, &key, &counter))
2034 == SKBAG_OK)
2035 {
2036 if (CHECK_LIMITS_IPADDR(&key, &counter)) {
2037 break;
2038 }
2039 ++key_count;
2040 }
2041
2042 if (SKBAG_ERR_KEY_NOT_FOUND == rv) {
2043 /* reached end of bag */
2044 skStreamPrint(stream_out, "\nStatistics\n");
2045 if (key_count < 1.0) {
2046 skStreamPrint(stream_out, " No entries in bag.\n");
2047 } else {
2048 skStreamPrint(stream_out,
2049 " No entries in bag within limits.\n");
2050 }
2051 skBagIteratorDestroy(iter);
2052 return 0;
2053 }
2054 if (SKBAG_OK != rv) {
2055 /* some other unexpected error */
2056 skAppPrintErr("Error iterating over bag: %s",
2057 skBagStrerror(rv));
2058 skBagIteratorDestroy(iter);
2059 return 1;
2060 }
2061
2062 key_count = 1;
2063 skipaddrCopy(&min_max_key[0], &key.val.addr);
2064 skipaddrCopy(&min_max_key[1], &key.val.addr);
2065 min_seen_counter = max_seen_counter = counter.val.u64;
2066 SUMS_OF_COUNTERS(counter.val.u64);
2067
2068 while (skBagIteratorNextTyped(iter, &key, &counter) == SKBAG_OK) {
2069 if (!CHECK_LIMITS_IPADDR(&key, &counter)) {
2070 continue;
2071 }
2072
2073 ++key_count;
2074 SUMS_OF_COUNTERS(counter.val.u64);
2075
2076 if (counter.val.u64 < min_seen_counter) {
2077 min_seen_counter = counter.val.u64;
2078 } else if (counter.val.u64 > max_seen_counter) {
2079 max_seen_counter = counter.val.u64;
2080 }
2081 if (skipaddrCompare(&key.val.addr, &min_max_key[0]) < 0) {
2082 skipaddrCopy(&min_max_key[0], &key.val.addr);
2083 } else if (skipaddrCompare(&key.val.addr, &min_max_key[1]) > 0) {
2084 skipaddrCopy(&min_max_key[1], &key.val.addr);
2085 }
2086 }
2087
2088 if (skBagIteratorDestroy(iter) != SKBAG_OK) {
2089 return 1;
2090 }
2091
2092 /* convert min/max keys to strings */
2093 for (i = 0; i < 2; ++i) {
2094 uint32_t u32;
2095
2096 key_buf[i] = (char *)malloc(bc_key->buflen);
2097 if (NULL == key_buf[i]) {
2098 skAppPrintOutOfMemory(NULL);
2099 exit(EXIT_FAILURE);
2100 }
2101 if (bc_key->formatter == BAGCAT_FMT_IPADDR) {
2102 skipaddrString(key_buf[i], &min_max_key[i],
2103 bc_key->formatter_flags);
2104
2105 } else if (skipaddrGetAsV4(&min_max_key[i], &u32)) {
2106 #if SK_ENABLE_IPV6
2107 skAppPrintErr("Cannot convert IP to 32bit number");
2108 skipaddrString(key_buf[i], &min_max_key[i], SKIPADDR_DECIMAL);
2109 #endif /* SK_ENABLE_IPV6 */
2110 } else {
2111 switch (bc_key->formatter) {
2112 case BAGCAT_FMT_ATTRIBUTES:
2113 skTCPStateString(u32, key_buf[i], bc_key->formatter_flags);
2114 break;
2115
2116 case BAGCAT_FMT_COUNTRY:
2117 skCountryCodeToName(u32, key_buf[i], bc_key->buflen);
2118 break;
2119
2120 case BAGCAT_FMT_IPADDR:
2121 skAbortBadCase(bc_key->formatter);
2122
2123 case BAGCAT_FMT_PMAP:
2124 skPrefixMapDictionaryGetEntry(prefix_map, u32, key_buf[i],
2125 bc_key->buflen);
2126 break;
2127
2128 case BAGCAT_FMT_SENSOR:
2129 sksiteSensorGetName(key_buf[i], bc_key->buflen, u32);
2130 break;
2131
2132 case BAGCAT_FMT_TCPFLAGS:
2133 skTCPFlagsString(u32, key_buf[i], bc_key->formatter_flags);
2134 break;
2135
2136 case BAGCAT_FMT_TIME:
2137 sktimestamp_r(key_buf[i], sktimeCreate(u32, 0),
2138 bc_key->formatter_flags);
2139 break;
2140 }
2141 }
2142 }
2143
2144 skStreamPrint(stream_out, "\nStatistics\n");
2145
2146 /* formulae derived from HyperStat Online - David M. Lane */
2147
2148 /* http://davidmlane.com/hyperstat/A15885.html (mean) */
2149 mean = sum / key_count;
2150
2151 /* http://davidmlane.com/hyperstat/A16252.html (variance) */
2152
2153 temp = (sum2
2154 - (2.0 * mean * sum)
2155 + (key_count * mean * mean));
2156
2157 variance = temp / (key_count - 1.0);
2158
2159 /* http://davidmlane.com/hyperstat/A16252.html (standard deviation) */
2160 stddev = sqrt(variance);
2161
2162 /* http://davidmlane.com/hyperstat/A11284.html (skew) */
2163 skew = ((sum3
2164 - (3.0 * mean * sum2)
2165 + (3.0 * mean * mean * sum)
2166 - (key_count * mean * mean * mean))
2167 / (key_count * variance * stddev));
2168
2169 /* http://davidmlane.com/hyperstat/A53638.html (kurtosis) */
2170 kurtosis = (temp * temp) / (key_count * variance * variance);
2171
2172 skStreamPrint(stream_out, ("%18s: %" PRIu64 "\n"
2173 "%18s: %" PRIu64 "\n"
2174 "%18s: %s\n"
2175 "%18s: %s\n"
2176 "%18s: %" PRIu64 "\n"
2177 "%18s: %" PRIu64 "\n"
2178 "%18s: %.4g\n"
2179 "%18s: %.4g\n"
2180 "%18s: %.4g\n"
2181 "%18s: %.4g\n"
2182 "%18s: %.4g\n"),
2183 "number of keys", (uint64_t)key_count,
2184 "sum of counters", (uint64_t)sum,
2185 "minimum key", key_buf[0],
2186 "maximum key", key_buf[1],
2187 "minimum counter", (uint64_t)min_seen_counter,
2188 "maximum counter", (uint64_t)max_seen_counter,
2189 "mean", mean,
2190 "variance", variance,
2191 "standard deviation", stddev,
2192 "skew", skew,
2193 "kurtosis", kurtosis);
2194 skBagPrintTreeStats(bag, stream_out);
2195
2196 free(key_buf[0]);
2197 free(key_buf[1]);
2198
2199 return 0;
2200 }
2201
2202
2203 /*
2204 * Verify that the bag key-format makes sense for the bag we
2205 * loaded; determine the number of bytes necessary to hold the key.
2206 */
2207 static int
bagcatCheckKeyFormat(const skBag_t * bag,bagcat_key_t * bc_key)2208 bagcatCheckKeyFormat(
2209 const skBag_t *bag,
2210 bagcat_key_t *bc_key)
2211 {
2212 char field_name[SKBAG_MAX_FIELD_BUFLEN];
2213 skBagFieldType_t key_field;
2214 int bad_format = 0;
2215 int as_integer = 0;
2216 int as_ipv6 = 0;
2217 int as_ipv4 = 0;
2218 int as_time = 0;
2219
2220 memset(bc_key, 0, sizeof(*bc_key));
2221 key_field = skBagKeyFieldName(bag, field_name, sizeof(field_name));
2222 switch (key_field) {
2223 case SKBAG_FIELD_SIPv6:
2224 case SKBAG_FIELD_DIPv6:
2225 case SKBAG_FIELD_NHIPv6:
2226 case SKBAG_FIELD_ANY_IPv6:
2227 as_ipv6 = 1;
2228 break;
2229
2230 case SKBAG_FIELD_SIPv4:
2231 case SKBAG_FIELD_DIPv4:
2232 case SKBAG_FIELD_NHIPv4:
2233 case SKBAG_FIELD_ANY_IPv4:
2234 as_ipv4 = 1;
2235 break;
2236
2237 case SKBAG_FIELD_STARTTIME:
2238 case SKBAG_FIELD_ENDTIME:
2239 case SKBAG_FIELD_ANY_TIME:
2240 as_time = 1;
2241 break;
2242
2243 case SKBAG_FIELD_FLAGS:
2244 case SKBAG_FIELD_INIT_FLAGS:
2245 case SKBAG_FIELD_REST_FLAGS:
2246 bc_key->key_type = SKBAG_KEY_U32;
2247 if (0 == key_format) {
2248 bc_key->buflen = 1 + SK_TCPFLAGS_STRLEN;
2249 bc_key->formatter = BAGCAT_FMT_TCPFLAGS;
2250 bc_key->formatter_flags = 0;
2251 bc_key->width = 8;
2252 } else {
2253 as_integer = 1;
2254 }
2255 break;
2256
2257 case SKBAG_FIELD_TCP_STATE:
2258 bc_key->key_type = SKBAG_KEY_U32;
2259 if (0 == key_format) {
2260 bc_key->buflen = 1 + SK_TCP_STATE_STRLEN;
2261 bc_key->formatter = BAGCAT_FMT_ATTRIBUTES;
2262 bc_key->formatter_flags = 0;
2263 bc_key->width = 8;
2264 } else {
2265 as_integer = 1;
2266 }
2267 break;
2268
2269 case SKBAG_FIELD_SID:
2270 bc_key->key_type = SKBAG_KEY_U32;
2271 if (0 == key_format) {
2272 /* set key-column width */
2273 sksiteConfigure(0);
2274 bc_key->width = sksiteSensorGetMaxNameStrLen();
2275 bc_key->buflen = 1 + bc_key->width;
2276 bc_key->formatter = BAGCAT_FMT_SENSOR;
2277 bc_key->formatter_flags = 0;
2278 } else {
2279 as_integer = 1;
2280 }
2281 break;
2282
2283 case SKBAG_FIELD_SIP_COUNTRY:
2284 case SKBAG_FIELD_DIP_COUNTRY:
2285 case SKBAG_FIELD_ANY_COUNTRY:
2286 if (0 == key_format) {
2287 bc_key->key_type = SKBAG_KEY_U32;
2288 bc_key->buflen = 3;
2289 bc_key->formatter = BAGCAT_FMT_COUNTRY;
2290 bc_key->formatter_flags = 0;
2291 bc_key->width = 2;
2292 } else {
2293 bad_format = 1;
2294 }
2295 break;
2296
2297 case SKBAG_FIELD_SIP_PMAP:
2298 case SKBAG_FIELD_DIP_PMAP:
2299 case SKBAG_FIELD_ANY_IP_PMAP:
2300 case SKBAG_FIELD_SPORT_PMAP:
2301 case SKBAG_FIELD_DPORT_PMAP:
2302 case SKBAG_FIELD_ANY_PORT_PMAP:
2303 if (0 == key_format) {
2304 bc_key->width = skPrefixMapDictionaryGetMaxWordSize(prefix_map);
2305 bc_key->key_type = SKBAG_KEY_U32;
2306 bc_key->buflen = 1 + bc_key->width;
2307 bc_key->formatter = BAGCAT_FMT_PMAP;
2308 bc_key->formatter_flags = 0;
2309 } else {
2310 bad_format = 1;
2311 }
2312 break;
2313
2314 case SKBAG_FIELD_CUSTOM:
2315 if ((0 == key_format) || (key_format & KEY_FORMAT_IP)) {
2316 if (16 == skBagKeyFieldLength(bag)) {
2317 as_ipv6 = 1;
2318 } else {
2319 as_ipv4 = 1;
2320 }
2321 } else if (16 == skBagKeyFieldLength(bag)) {
2322 skAppPrintErr(("Invalid %s '%s':"
2323 " Bag's key length is too long for format"),
2324 appOptions[OPT_KEY_FORMAT].name, key_format_arg);
2325 return -1;
2326 } else {
2327 assert(key_format & KEY_FORMAT_TIME);
2328 as_time = 1;
2329 }
2330 break;
2331
2332 default:
2333 as_integer = 1;
2334 }
2335
2336 if (as_ipv4 || as_ipv6) {
2337 bc_key->key_type = SKBAG_KEY_IPADDR;
2338 bc_key->buflen = 1 + SKIPADDR_STRLEN;
2339 bc_key->formatter = BAGCAT_FMT_IPADDR;
2340 if (0 != key_format && !(key_format & KEY_FORMAT_IP)) {
2341 bad_format = 1;
2342 } else {
2343 if (0 == key_format) {
2344 bc_key->formatter_flags = SKIPADDR_CANONICAL;
2345 } else {
2346 bc_key->formatter_flags = key_format & ~KEY_FORMAT_MASK;
2347 }
2348 bc_key->width = skipaddrStringMaxlen(as_ipv6,
2349 bc_key->formatter_flags);
2350 }
2351 }
2352
2353 if (as_time) {
2354 bc_key->key_type = SKBAG_KEY_U32;
2355 bc_key->buflen = 1 + SKTIMESTAMP_STRLEN;
2356 bc_key->formatter = BAGCAT_FMT_TIME;
2357 if (0 != key_format && !(key_format & KEY_FORMAT_TIME)) {
2358 bad_format = 1;
2359 } else {
2360 if (0 == key_format) {
2361 assert(SKBAG_FIELD_CUSTOM != key_format);
2362 bc_key->formatter_flags = SKTIMESTAMP_NOMSEC;
2363 } else {
2364 bc_key->formatter_flags
2365 = (SKTIMESTAMP_NOMSEC | (key_format & ~KEY_FORMAT_MASK));
2366 }
2367 bc_key->width
2368 = ((SKTIMESTAMP_EPOCH & bc_key->formatter_flags) ? 10 : 19);
2369 }
2370 }
2371
2372 if (as_integer) {
2373 bc_key->key_type = SKBAG_KEY_U32;
2374 bc_key->buflen = 1 + SKIPADDR_STRLEN;
2375 bc_key->formatter = BAGCAT_FMT_IPADDR;
2376 if (0 == key_format) {
2377 bc_key->formatter_flags = SKIPADDR_DECIMAL;
2378 bc_key->width = 10;
2379 } else {
2380 switch (key_format) {
2381 case KEY_FORMAT_IP | SKIPADDR_DECIMAL:
2382 case KEY_FORMAT_IP | SKIPADDR_DECIMAL | SKIPADDR_ZEROPAD:
2383 bc_key->formatter_flags = key_format & ~KEY_FORMAT_IP;
2384 bc_key->width = 10;
2385 break;
2386 case KEY_FORMAT_IP | SKIPADDR_HEXADECIMAL:
2387 case KEY_FORMAT_IP | SKIPADDR_HEXADECIMAL | SKIPADDR_ZEROPAD:
2388 bc_key->formatter_flags = key_format & ~KEY_FORMAT_IP;
2389 bc_key->width = 8;
2390 break;
2391 case KEY_FORMAT_IP | SKIPADDR_ZEROPAD:
2392 bc_key->formatter_flags = SKIPADDR_DECIMAL | SKIPADDR_ZEROPAD;
2393 bc_key->width = 10;
2394 break;
2395 default:
2396 bad_format = 1;
2397 break;
2398 }
2399 }
2400 }
2401
2402 if (bad_format) {
2403 skAppPrintErr("Invalid %s '%s': Nonsensical for Bag containing %s keys",
2404 appOptions[OPT_KEY_FORMAT].name, key_format_arg,
2405 field_name);
2406 return -1;
2407 }
2408 return 0;
2409 }
2410
2411
2412 /*
2413 * Verify we have a prefix map and that the prefix map is the
2414 * correct type for the type of bag.
2415 */
2416 static int
bagcatCheckPrefixMap(const skBag_t * bag)2417 bagcatCheckPrefixMap(
2418 const skBag_t *bag)
2419 {
2420 char field_name[SKBAG_MAX_FIELD_BUFLEN];
2421 skBagFieldType_t key_field;
2422 int key_is_ip_pmap = 0;
2423
2424 key_field = skBagKeyFieldName(bag, field_name, sizeof(field_name));
2425
2426 switch (key_field) {
2427 case SKBAG_FIELD_SIP_PMAP:
2428 case SKBAG_FIELD_DIP_PMAP:
2429 case SKBAG_FIELD_ANY_IP_PMAP:
2430 key_is_ip_pmap = 1;
2431 /* FALLTHROUGH */
2432 case SKBAG_FIELD_SPORT_PMAP:
2433 case SKBAG_FIELD_DPORT_PMAP:
2434 case SKBAG_FIELD_ANY_PORT_PMAP:
2435 break;
2436
2437 default:
2438 return 0;
2439 }
2440
2441 if (!prefix_map) {
2442 skAppPrintErr("The --%s switch is required for Bags containing %s keys",
2443 appOptions[OPT_PMAP_FILE].name, field_name);
2444 return -1;
2445 }
2446 if ((skPrefixMapGetContentType(prefix_map) == SKPREFIXMAP_CONT_PROTO_PORT)
2447 ? key_is_ip_pmap
2448 : !key_is_ip_pmap)
2449 {
2450 skAppPrintErr("Cannot use %s prefix map for Bag containing %s keys",
2451 skPrefixMapGetContentName(
2452 skPrefixMapGetContentType(prefix_map)), field_name);
2453 return -1;
2454 }
2455
2456 return 0;
2457 }
2458
2459
2460 /*
2461 * Verify that the bag contains IP keys when the the --mask-set
2462 * switch is provided.
2463 */
2464 static int
bagcatCheckMaskSet(const skBag_t * bag)2465 bagcatCheckMaskSet(
2466 const skBag_t *bag)
2467 {
2468 char field_name[SKBAG_MAX_FIELD_BUFLEN];
2469 skBagFieldType_t key_field;
2470
2471 if (limits.mask_set) {
2472 key_field = skBagKeyFieldName(bag, field_name, sizeof(field_name));
2473
2474 switch (key_field) {
2475 case SKBAG_FIELD_CUSTOM:
2476 case SKBAG_FIELD_SIPv4:
2477 case SKBAG_FIELD_DIPv4:
2478 case SKBAG_FIELD_NHIPv4:
2479 case SKBAG_FIELD_ANY_IPv4:
2480 case SKBAG_FIELD_SIPv6:
2481 case SKBAG_FIELD_DIPv6:
2482 case SKBAG_FIELD_NHIPv6:
2483 case SKBAG_FIELD_ANY_IPv6:
2484 break;
2485 default:
2486 skAppPrintErr("Cannot use --%s with a Bag containing %s keys",
2487 appOptions[OPT_NETWORK_STRUCTURE].name, field_name);
2488 return -1;
2489 }
2490 }
2491 return 0;
2492 }
2493
2494
main(int argc,char ** argv)2495 int main(int argc, char **argv)
2496 {
2497 skBagErr_t err;
2498 skBag_t *bag = NULL;
2499 char *filename;
2500 bagcat_key_t bagcat_key;
2501 int rv;
2502
2503 appSetup(argc, argv); /* never returns on error */
2504
2505 while ((rv = skOptionsCtxNextArgument(optctx, &filename)) == 0) {
2506 if (IS_STDIN(filename)) {
2507 if (stdin_used) {
2508 skAppPrintErr("Multiple streams attempt"
2509 " to read from the standard input");
2510 }
2511 stdin_used = 1;
2512 }
2513 err = skBagLoad(&bag, filename);
2514 if (err != SKBAG_OK) {
2515 skAppPrintErr("Error reading bag from input stream '%s': %s",
2516 filename, skBagStrerror(err));
2517 exit(EXIT_FAILURE);
2518 }
2519
2520 /* --mask-set only allowed with IP bag */
2521 if (bagcatCheckMaskSet(bag)) {
2522 skBagDestroy(&bag);
2523 exit(EXIT_FAILURE);
2524 }
2525
2526 /* check features needed when producing output other than
2527 * inverting the bag. */
2528 if (sort_counters || print_statistics || bin_scheme == BINSCHEME_NONE){
2529 /* check for a valid prefix map if needed */
2530 if (bagcatCheckPrefixMap(bag)) {
2531 skBagDestroy(&bag);
2532 exit(EXIT_FAILURE);
2533 }
2534
2535 /* verify that the key-format makes sense */
2536 if (bagcatCheckKeyFormat(bag, &bagcat_key)) {
2537 skBagDestroy(&bag);
2538 exit(EXIT_FAILURE);
2539 }
2540 }
2541
2542 /* handle --bin-ips */
2543 if (bin_scheme != BINSCHEME_NONE) {
2544 if (bagcatInvertBag(bag)) {
2545 skAppPrintErr("Error inverting bag '%s'", filename);
2546 skBagDestroy(&bag);
2547 exit(EXIT_FAILURE);
2548 }
2549 } else if (sort_counters) {
2550 bagcatSortCounters(bag, &bagcat_key);
2551 } else if (print_network || !print_statistics) {
2552 /* either --network-structure explicitly given or there
2553 * was no other output selected */
2554 if (bagcatProcessBag(bag, &bagcat_key)) {
2555 skAppPrintErr("Error processing bag '%s'", filename);
2556 skBagDestroy(&bag);
2557 exit(EXIT_FAILURE);
2558 }
2559 }
2560 if (print_statistics) {
2561 printStatistics(bag, &bagcat_key, stats_stream);
2562 }
2563 skBagDestroy(&bag);
2564 }
2565
2566 /* done */
2567 return 0;
2568 }
2569
2570
2571 /*
2572 ** Local Variables:
2573 ** mode:c
2574 ** indent-tabs-mode:nil
2575 ** c-basic-offset:4
2576 ** End:
2577 */
2578