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