File Coverage

blib/lib/Suricata/Monitoring.pm
Criterion Covered Total %
statement 26 220 11.8
branch 0 96 0.0
condition 0 48 0.0
subroutine 9 12 75.0
pod 3 3 100.0
total 38 379 10.0


line stmt bran cond sub pod time code
1             package Suricata::Monitoring;
2              
3 1     1   56614 use 5.006;
  1         3  
4 1     1   4 use strict;
  1         1  
  1         35  
5 1     1   5 use warnings;
  1         1  
  1         30  
6 1     1   576 use JSON;
  1         10207  
  1         5  
7 1     1   131 use File::Path qw(make_path);
  1         1  
  1         55  
8 1     1   1305 use File::ReadBackwards;
  1         1705  
  1         23  
9 1     1   5 use Carp;
  1         2  
  1         43  
10 1     1   431 use File::Slurp;
  1         24599  
  1         90  
11 1     1   428 use Time::Piece;
  1         8374  
  1         13  
12              
13             =head1 NAME
14              
15             Suricata::Monitoring - LibreNMS JSON SNMP extend and Nagios style check for Suricata stats
16              
17             =head1 VERSION
18              
19             Version 0.2.0
20              
21             =cut
22              
23             our $VERSION = '0.2.0';
24              
25             =head1 SYNOPSIS
26              
27             use Suricata::Monitoring;
28              
29             my $args = {
30             mode => 'librenms',
31             drop_percent_warn => .75;
32             drop_percent_crit => 1,
33             error_delta_warn => 1,
34             error_delta_crit => 2,
35             error_percent_warn => .05,
36             error_percent_crit => .1,
37             files=>{
38             'ids'=>'/var/log/suricata/alert-ids.json',
39             'foo'=>'/var/log/suricata/alert-foo.json',
40             },
41             };
42              
43             my $sm=Suricata::Monitoring->new( $args );
44             my $returned=$sm->run;
45             $sm->print;
46             exit $returned->{alert};
47              
48             =head1 METHODS
49              
50             =head2 new
51              
52             Initiate the object.
53              
54             The args are taken as a hash ref. The keys are documented as below.
55              
56             The only must have is 'files'.
57              
58             - mode :: Wether the print_output output should be for Nagios or LibreNMS.
59             - value :: 'librenms' or 'nagios'
60             - Default :: librenms
61            
62             - drop_percent_warn :: Drop percent warning threshold.
63             - Default :: .75;
64            
65             - drop_percent_crit :: Drop percent critical threshold.
66             - Default :: 1
67            
68             - error_delta_warn :: Error delta warning threshold.
69             - Default :: 1
70            
71             - error_delta_crit :: Error delta critical threshold.
72             - Default :: 2
73            
74             - error_percent_warn :: Error percent warning threshold.
75             - Default :: .05
76            
77             - error_percent_crit :: Error percent critical threshold.
78             - Default :: .1
79            
80             - files :: A hash with the keys being the instance name and the values
81             being the Eve files to read. ".total" is not a valid instance name.
82             Similarly anything starting with a "." should be considred reserved.
83              
84             my $args = {
85             mode => 'librenms',
86             drop_percent_warn => .75;
87             drop_percent_crit => 1,
88             error_delta_warn => 1,
89             error_delta_crit => 2,
90             error_percent_warn => .05,
91             error_percent_crit => .1,
92             files=>{
93             'ids'=>'/var/log/suricata/alert-ids.json',
94             'foo'=>'/var/log/suricata/alert-foo.json',
95             },
96             };
97              
98             my $sm=Suricata::Monitoring->new( $args );
99              
100             =cut
101              
102             sub new {
103 0     0 1   my %args;
104 0 0         if ( defined( $_[1] ) ) {
105 0           %args = %{ $_[1] };
  0            
106             }
107              
108             # init the object
109 0           my $self = {
110             'drop_percent_warn' => '.75',
111             'drop_percent_crit' => '1',
112             'error_delta_warn' => '1',
113             'error_delta_crit' => '2',
114             'error_percent_warn' => '0.05',
115             'error_percent_crit' => '0.1',
116             max_age => 360,
117             mode => 'librenms',
118             };
119 0           bless $self;
120              
121             # reel in the threshold values
122 0           my @thresholds = (
123             'drop_percent_warn', 'drop_percent_crit', 'error_delta_warn', 'error_delta_crit',
124             'error_percent_warn', 'error_percent_crit'
125             );
126 0           for my $threshold (@thresholds) {
127 0 0         if ( defined( $args{$threshold} ) ) {
128 0           $self->{$threshold} = $args{$threshold};
129 0 0         if ( $args{$threshold} !~ /[0-9\.]+/ ) {
130 0           confess( '"' . $threshold . '" with a value of "' . $args{$threshold} . '" is not numeric' );
131             }
132             }
133             }
134              
135             # get the mode and make sure it is valid
136 0 0 0       if (
    0 0        
137             defined( $args{mode} )
138             && ( ( $args{mode} ne 'librenms' )
139             && ( $args{mode} ne 'nagios' ) )
140             )
141             {
142 0           confess( '"' . $args{mode} . '" is not a understood mode' );
143             }
144             elsif ( defined( $args{mode} ) ) {
145 0           $self->{mode} = $args{mode};
146             }
147              
148             # make sure we have files specified
149 0 0 0       if ( ( !defined( $args{files} ) )
150             || ( !defined( keys( %{ $args{files} } ) ) ) )
151             {
152 0           confess('No files specified');
153             }
154             else {
155 0           $self->{files} = $args{files};
156             }
157              
158 0 0         if ( defined( $self->{files}{'.total'} ) ) {
159 0           confess('".total" is not a valid instance name');
160             }
161              
162             # pull in cache dir location
163 0 0         if ( !defined( $args{cache_dir} ) ) {
164 0           $args{cache_dir} = '/var/cache/suricata-monitoring/';
165             }
166 0           $self->{cache_dir} = $args{cache_dir};
167              
168             # if the cache dir does not exist, try to create it
169 0 0         if ( !-d $self->{cache_dir} ) {
170             make_path( $self->{cache_dir} )
171             or confess(
172 0 0         '"' . $args{cache_dir} . '" does not exist or is not a directory and could not be create... ' . $@ );
173             }
174              
175 0           return $self;
176             }
177              
178             =head2 run
179              
180             This runs it and collects the data. Also updates the cache.
181              
182             This will return a LibreNMS style hash.
183              
184             my $returned=$sm->run;
185              
186             =cut
187              
188             sub run {
189 0     0 1   my $self = $_[0];
190              
191             # this will be returned
192 0           my $to_return = {
193             data => { '.total' => {} },
194             version => 1,
195             error => '0',
196             errorString => '',
197             alert => '0',
198             alertString => ''
199             };
200              
201 0           my $previous;
202 0           my $previous_file = $self->{cache_dir} . '/stats.json';
203 0 0         if ( -f $previous_file ) {
204             #
205 0           eval {
206 0           my $previous_raw = read_file($previous_file);
207 0           $previous = decode_json($previous_raw);
208             };
209 0 0         if ($@) {
210 0           $to_return->{error} = '1';
211             $to_return->{errorString}
212 0           = 'Failed to read previous JSON file, "' . $previous_file . '", and decode it... ' . $@;
213 0           $self->{results} = $to_return;
214 0           return $to_return;
215             }
216             }
217              
218             # figure out the time slot we care about
219 0           my $from = time;
220 0           my $till = $from - $self->{max_age};
221              
222             # process the files for each instance
223 0           my @instances = keys( %{ $self->{files} } );
  0            
224 0           my @alerts;
225             my $current_till;
226 0           foreach my $instance (@instances) {
227              
228             # ends processing for this file
229 0           my $process_it = 1;
230              
231             # open the file for reading it backwards
232 0           my $bw;
233 0           eval {
234             $bw = File::ReadBackwards->new( $self->{files}{$instance} )
235 0 0         or die( 'Can not read "' . $self->{files}{$instance} . '"... ' . $! );
236             };
237 0 0         if ($@) {
238 0           $to_return->{error} = '2';
239 0 0         if ( $to_return->{errorString} ne '' ) {
240 0           $to_return->{errorString} = $to_return->{errorString} . "\n";
241             }
242 0           $to_return->{errorString} = $to_return->{errorString} . $instance . ': ' . $@;
243 0           $process_it = 0;
244             }
245              
246             # get the first line, if possible
247 0           my $line;
248 0 0         if ($process_it) {
249 0           $line = $bw->readline;
250             }
251 0   0       while ( $process_it
252             && defined($line) )
253             {
254 0           eval {
255 0           my $json = decode_json($line);
256 0           my $timestamp = $json->{timestamp};
257              
258             # if current till is not set, set it
259 0 0 0       if ( !defined($current_till)
      0        
260             && defined($timestamp)
261             && $timestamp =~ /^[0-9]+\-[0-9]+\-[0-9]+T[0-9]+\:[0-9]+\:[0-9\.]+[\-\+][0-9]+/ )
262             {
263              
264             # get the number of hours
265 0           my $hours = $timestamp;
266 0           $hours =~ s/.*[\-\+]//g;
267 0           $hours =~ s/^0//;
268 0           $hours =~ s/[0-9][0-9]$//;
269              
270             # get the number of minutes
271 0           my $minutes = $timestamp;
272 0           $minutes =~ s/.*[\-\+]//g;
273 0           $minutes =~ s/^[0-9][0-9]//;
274              
275 0           my $second_diff = ( $minutes * 60 ) + ( $hours * 60 * 60 );
276              
277 0 0         if ( $timestamp =~ /\+/ ) {
278 0           $current_till = $till + $second_diff;
279             }
280             else {
281 0           $current_till = $till - $second_diff;
282             }
283             }
284 0           $timestamp =~ s/\..*$//;
285 0           my $t = Time::Piece->strptime( $timestamp, '%Y-%m-%dT%H:%M:%S' );
286              
287             # stop process further lines as we've hit the oldest we care about
288 0 0         if ( $t->epoch <= $current_till ) {
289 0           $process_it = 0;
290             }
291              
292             # we found the entry we are looking for if
293             # this matches, so process it
294 0 0 0       if ( defined( $json->{event_type} )
295             && $json->{event_type} eq 'stats' )
296             {
297             # we can stop processing now as this is what we were looking for
298 0           $process_it = 0;
299              
300             # holds the found new alerts
301 0           my @new_alerts;
302              
303             my $new_stats = {
304             uptime => $json->{stats}{uptime},
305             packets => $json->{stats}{capture}{kernel_packets},
306             dropped => $json->{stats}{capture}{kernel_drops},
307             ifdropped => $json->{stats}{capture}{kernel_ifdrops},
308             errors => $json->{stats}{capture}{errors},
309             packet_delta => 0,
310             drop_delta => 0,
311             ifdrop_delta => 0,
312             error_delta => 0,
313             drop_percent => 0,
314             ifdrop_percent => 0,
315             error_percent => 0,
316             bytes => $json->{stats}{decoder}{bytes},
317             dec_packets => $json->{stats}{decoder}{pkts},
318             dec_invalid => $json->{stats}{decoder}{invalid},
319             dec_ipv4 => $json->{stats}{decoder}{ipv4},
320             dec_ipv6 => $json->{stats}{decoder}{ipv6},
321             dec_udp => $json->{stats}{decoder}{udp},
322             dec_tcp => $json->{stats}{decoder}{tcp},
323             dec_avg_pkt_size => $json->{stats}{decoder}{avg_pkt_size},
324             dec_max_pkt_size => $json->{stats}{decoder}{max_pkt_size},
325             dec_chdlc => $json->{stats}{decoder}{chdlc},
326             dec_ethernet => $json->{stats}{decoder}{ethernet},
327             dec_geneve => $json->{stats}{decoder}{geneve},
328             dec_ieee8021ah => $json->{stats}{decoder}{ieee8021ah},
329             dec_ipv4_in_ipv6 => $json->{stats}{decoder}{ipv6_in_ipv6},
330             dec_mx_mac_addrs_d => $json->{stats}{decoder}{max_mac_addrs_dst},
331             dec_mx_mac_addrs_s => $json->{stats}{decoder}{max_mac_addrs_src},
332             dec_mpls => $json->{stats}{decoder}{mpls},
333             dec_ppp => $json->{stats}{decoder}{ppp},
334             dec_pppoe => $json->{stats}{decoder}{pppoe},
335             dec_raw => $json->{stats}{decoder}{raw},
336             dec_sctp => $json->{stats}{decoder}{sctp},
337             dec_sll => $json->{stats}{decoder}{sll},
338             dec_teredo => $json->{stats}{decoder}{teredo},
339             dec_too_many_layer => $json->{stats}{decoder}{too_many_layers},
340             dec_vlan => $json->{stats}{decoder}{vlan},
341             dec_vlan_qinq => $json->{stats}{decoder}{vlan_qinq},
342             dec_vntag => $json->{stats}{decoder}{vntag},
343             dec_vxlan => $json->{stats}{decoder}{vxlan},
344             f_tcp => $json->{stats}{flow}{tcp},
345             f_udp => $json->{stats}{flow}{udp},
346             f_icmpv4 => $json->{stats}{flow}{icmpv4},
347             f_icmpv6 => $json->{stats}{flow}{icmpv6},
348             f_memuse => $json->{stats}{flow}{memuse},
349             ftp_memuse => $json->{stats}{ftp}{memuse},
350             http_memuse => $json->{stats}{http}{memuse},
351             tcp_memuse => $json->{stats}{tcp}{memuse},
352             tcp_reass_memuse => $json->{stats}{tcp}{reassembly_memuse},
353 0           alert => 0,
354             alertString => '',
355             };
356              
357 0           foreach my $flow_key ( keys( %{ $json->{stats}{app_layer}{flow} } ) ) {
  0            
358 0           my $new_key = $flow_key;
359 0           $new_key =~ s/\-/_/g;
360 0           $new_stats->{ 'af_' . $new_key } = $json->{stats}{app_layer}{flow}{$flow_key};
361             }
362 0           foreach my $tx_key ( keys( %{ $json->{stats}{app_layer}{tx} } ) ) {
  0            
363 0           my $new_key = $tx_key;
364 0           $new_key =~ s/\-/_/g;
365 0           $new_stats->{ 'at_' . $new_key } = $json->{stats}{app_layer}{tx}{$tx_key};
366             }
367              
368             # some this is a bit variable as to which will be present based on the system
369             # af-packet = error
370             # pcap = ifdrops
371 0           my @zero_if_undef = ( 'errors', 'ifdropped' );
372 0           foreach my $undef_check (@zero_if_undef) {
373 0 0         if ( !defined( $new_stats->{$undef_check} ) ) {
374 0           $new_stats->{$undef_check} = 0;
375             }
376             }
377              
378             # begin handling this if we have previous values
379 0 0 0       if ( defined($previous)
      0        
      0        
      0        
380             && defined( $previous->{data}{$instance} )
381             && defined( $previous->{data}{$instance}{packets} )
382             && defined( $previous->{data}{$instance}{bytes} )
383             && defined( $previous->{data}{$instance}{dropped} ) )
384             {
385             # find the change for packet count
386 0 0         if ( $new_stats->{packets} < $previous->{data}{$instance}{packets} ) {
387 0           $new_stats->{packet_delta} = $new_stats->{packets};
388             }
389             else {
390 0           $new_stats->{packet_delta} = $new_stats->{packets} - $previous->{data}{$instance}{packets};
391             }
392              
393             # find the change for drop count
394 0 0         if ( $new_stats->{dropped} < $previous->{data}{$instance}{dropped} ) {
395 0           $new_stats->{drop_delta} = $new_stats->{dropped};
396             }
397             else {
398 0           $new_stats->{drop_delta} = $new_stats->{dropped} - $previous->{data}{$instance}{dropped};
399             }
400              
401             # find the change for ifdrop count
402 0 0         if ( $new_stats->{ifdropped} < $previous->{data}{$instance}{ifdropped} ) {
403 0           $new_stats->{ifdrop_delta} = $new_stats->{ifdropped};
404             }
405             else {
406             $new_stats->{ifdrop_delta}
407 0           = $new_stats->{ifdropped} - $previous->{data}{$instance}{ifdropped};
408             }
409              
410             # find the change for errors count
411 0 0         if ( $new_stats->{errors} < $previous->{data}{$instance}{errors} ) {
412 0           $new_stats->{error_delta} = $new_stats->{errors};
413             }
414             else {
415 0           $new_stats->{error_delta} = $new_stats->{errors} - $previous->{data}{$instance}{errors};
416             }
417              
418             # find the percent of dropped
419 0 0         if ( $new_stats->{drop_delta} != 0 ) {
420             $new_stats->{drop_percent}
421 0           = ( $new_stats->{drop_delta} / $new_stats->{packet_delta} ) * 100;
422 0           $new_stats->{drop_percent} = sprintf( '%0.5f', $new_stats->{drop_percent} );
423             }
424              
425             # find the percent of dropped
426 0 0         if ( $new_stats->{ifdrop_delta} != 0 ) {
427             $new_stats->{ifdrop_percent}
428 0           = ( $new_stats->{ifdrop_delta} / $new_stats->{ifpacket_delta} ) * 100;
429 0           $new_stats->{ifdrop_percent} = sprintf( '%0.5f', $new_stats->{ifdrop_percent} );
430             }
431              
432             # find the percent of errored
433 0 0         if ( $new_stats->{error_delta} != 0 ) {
434             $new_stats->{error_percent}
435 0           = ( $new_stats->{error_delta} / $new_stats->{packet_delta} ) * 100;
436 0           $new_stats->{error_percent} = sprintf( '%0.5f', $new_stats->{error_percent} );
437             }
438              
439             # check for drop percent alerts
440 0 0 0       if ( $new_stats->{drop_percent} >= $self->{drop_percent_warn}
441             && $new_stats->{drop_percent} < $self->{drop_percent_crit} )
442             {
443 0           $new_stats->{alert} = 1;
444             push( @new_alerts,
445             $instance
446             . ' drop_percent warning '
447             . $new_stats->{drop_percent} . ' >= '
448 0           . $self->{drop_percent_warn} );
449             }
450 0 0         if ( $new_stats->{drop_percent} >= $self->{drop_percent_crit} ) {
451 0           $new_stats->{alert} = 2;
452             push( @new_alerts,
453             $instance
454             . ' drop_percent critical '
455             . $new_stats->{drop_percent} . ' >= '
456 0           . $self->{drop_percent_crit} );
457             }
458              
459             # check for drop percent alerts
460 0 0 0       if ( $new_stats->{ifdrop_percent} >= $self->{drop_percent_warn}
461             && $new_stats->{ifdrop_percent} < $self->{drop_percent_crit} )
462             {
463 0           $new_stats->{alert} = 1;
464             push( @new_alerts,
465             $instance
466             . ' ifdrop_percent warning '
467             . $new_stats->{ifdrop_percent} . ' >= '
468 0           . $self->{drop_percent_warn} );
469             }
470 0 0         if ( $new_stats->{ifdrop_percent} >= $self->{drop_percent_crit} ) {
471 0           $new_stats->{alert} = 2;
472             push( @new_alerts,
473             $instance
474             . ' ifdrop_percent critical '
475             . $new_stats->{ifdrop_percent} . ' >= '
476 0           . $self->{drop_percent_crit} );
477             }
478              
479             # check for error delta alerts
480 0 0 0       if ( $new_stats->{error_delta} >= $self->{error_delta_warn}
481             && $new_stats->{error_delta} < $self->{error_delta_crit} )
482             {
483 0           $new_stats->{alert} = 1;
484             push( @new_alerts,
485             $instance
486             . ' error_delta warning '
487             . $new_stats->{error_delta} . ' >= '
488 0           . $self->{error_delta_warn} );
489             }
490 0 0         if ( $new_stats->{error_delta} >= $self->{error_delta_crit} ) {
491 0           $new_stats->{alert} = 2;
492             push( @new_alerts,
493             $instance
494             . ' error_delta critical '
495             . $new_stats->{error_delta} . ' >= '
496 0           . $self->{error_delta_crit} );
497             }
498              
499             # check for drop percent alerts
500 0 0 0       if ( $new_stats->{error_percent} >= $self->{error_percent_warn}
501             && $new_stats->{error_percent} < $self->{error_percent_crit} )
502             {
503 0           $new_stats->{alert} = 1;
504             push( @new_alerts,
505             $instance
506             . ' error_percent warning '
507             . $new_stats->{error_percent} . ' >= '
508 0           . $self->{error_percent_warn} );
509             }
510 0 0         if ( $new_stats->{error_percent} >= $self->{error_percent_crit} ) {
511 0           $new_stats->{alert} = 2;
512             push( @new_alerts,
513             $instance
514             . ' error_percent critical '
515             . $new_stats->{error_percent} . ' >= '
516 0           . $self->{error_percent_crit} );
517             }
518              
519             # check for alert status
520 0 0         if ( $new_stats->{alert} > $to_return->{alert} ) {
521 0           $to_return->{alert} = $new_stats->{alert};
522 0           $new_stats->{alertString} = join( "\n", @new_alerts );
523 0           push( @alerts, @new_alerts );
524             }
525             }
526              
527             # add stuff to .total
528 0           my @intance_keys = keys( %{$new_stats} );
  0            
529 0           foreach my $total_key (@intance_keys) {
530 0 0         if ( $total_key ne 'alertString' ) {
531 0 0         if ( !defined( $to_return->{data}{'.total'}{$total_key} ) ) {
532 0           $to_return->{data}{'.total'}{$total_key} = $new_stats->{$total_key};
533             }
534             else {
535             $to_return->{data}{'.total'}{$total_key}
536 0           = $to_return->{data}{'.total'}{$total_key} + $new_stats->{$total_key};
537             }
538             }
539             }
540              
541 0           $to_return->{data}{$instance} = $new_stats;
542             }
543              
544             };
545              
546             # get the next line
547 0           $line = $bw->readline;
548             }
549              
550             }
551              
552             # compute percents for .total
553 0 0 0       if ( defined( $to_return->{data}{'.total'}{packet_delta} )
554             && ( $to_return->{data}{'.total'}{packet_delta} != 0 ) )
555             {
556             $to_return->{data}{'.total'}{drop_percent}
557 0           = ( $to_return->{data}{'.total'}{drop_delta} / $to_return->{data}{'.total'}{packet_delta} ) * 100;
558 0           $to_return->{data}{'.total'}{drop_percent} = sprintf( '%0.5f', $to_return->{data}{'.total'}{drop_percent} );
559              
560             $to_return->{data}{'.total'}{ifdrop_percent}
561 0           = ( $to_return->{data}{'.total'}{ifdrop_delta} / $to_return->{data}{'.total'}{packet_delta} ) * 100;
562 0           $to_return->{data}{'.total'}{ifdrop_percent} = sprintf( '%0.5f', $to_return->{data}{'.total'}{ifdrop_percent} );
563              
564             $to_return->{data}{'.total'}{error_percent}
565 0           = ( $to_return->{data}{'.total'}{error_delta} / $to_return->{data}{'.total'}{packet_delta} ) * 100;
566 0           $to_return->{data}{'.total'}{error_percent} = sprintf( '%0.5f', $to_return->{data}{'.total'}{error_percent} );
567             }
568             else {
569 0           $to_return->{data}{alert} = '3';
570 0           push( @alerts, 'Did not find a stats entry after searching back ' . $self->{max_age} . ' seconds' );
571             }
572              
573             # join any found alerts into the string
574 0           $to_return->{alertString} = join( "\n", @alerts );
575 0           $to_return->{data}{'.total'}{alert} = $to_return->{'alert'};
576              
577             # write the cache file on out
578 0           eval {
579 0           my $new_cache = encode_json($to_return);
580 0           open( my $fh, '>', $previous_file );
581 0           print $fh $new_cache . "\n";
582 0           close($fh);
583             };
584 0 0         if ($@) {
585 0           $to_return->{error} = '1';
586 0           $to_return->{alert} = '3';
587 0           $to_return->{errorString} = 'Failed to write new cache JSON file, "' . $previous_file . '".... ' . $@;
588              
589             # set the nagious style alert stuff
590 0           $to_return->{alert} = '3';
591 0 0         if ( $to_return->{alertString} eq '' ) {
592 0           $to_return->{alertString} = $to_return->{errorString};
593             }
594             else {
595 0           $to_return->{alertString} = $to_return->{errorString} . "\n" . $to_return->{alertString};
596             }
597             }
598              
599 0           $self->{results} = $to_return;
600              
601 0           return $to_return;
602             }
603              
604             =head2 print_output
605              
606             Prints the output.
607              
608             $sm->print_output;
609              
610             =cut
611              
612             sub print_output {
613 0     0 1   my $self = $_[0];
614              
615 0 0         if ( $self->{mode} eq 'nagios' ) {
616 0 0         if ( $self->{results}{alert} eq '0' ) {
    0          
    0          
    0          
617 0           print "OK - no alerts\n";
618 0           return;
619             }
620             elsif ( $self->{results}{alert} eq '1' ) {
621 0           print 'WARNING - ';
622             }
623             elsif ( $self->{results}{alert} eq '2' ) {
624 0           print 'CRITICAL - ';
625             }
626             elsif ( $self->{results}{alert} eq '3' ) {
627 0           print 'UNKNOWN - ';
628             }
629 0           my $alerts = $self->{results}{alertString};
630 0           chomp($alerts);
631 0           $alerts = s/\n/\, /g;
632 0           print $alerts. "\n";
633             }
634             else {
635 0           print encode_json( $self->{results} ) . "\n";
636             }
637             }
638              
639             =head1 LibreNMS HASH
640              
641             + $hash{'alert'} :: Alert status.
642             - 0 :: OK
643             - 1 :: WARNING
644             - 2 :: CRITICAL
645             - 3 :: UNKNOWN
646            
647             + $hash{'alertString'} :: A string describing the alert. Defaults to
648             '' if there is no alert.
649            
650             + $hash{'error'} :: A integer representing a error. '0' represents
651             everything is fine.
652            
653             + $hash{'errorString'} :: A string description of the error.
654            
655             + $hash{'data'}{$instance} :: Values migrated from the
656             instance. *_delta values are created via computing the difference
657             from the previously saved info. *_percent is based off of the delta
658             in question over the packet delta. Delta are created for packet,
659             drop, ifdrop, and error. Percents are made for drop, ifdrop, and
660             error.
661            
662             + $hash{'data'}{'.total'} :: Total values of from all the
663             intances. Any percents will be recomputed.
664            
665              
666             The stat keys are migrated as below.
667            
668             uptime => $json->{stats}{uptime},
669             packets => $json->{stats}{capture}{kernel_packets},
670             dropped => $json->{stats}{capture}{kernel_drops},
671             ifdropped => $json->{stats}{capture}{kernel_ifdrops},
672             errors => $json->{stats}{capture}{errors},
673             bytes => $json->{stats}{decoder}{bytes},
674             dec_packets => $json->{stats}{decoder}{pkts},
675             dec_invalid => $json->{stats}{decoder}{invalid},
676             dec_ipv4 => $json->{stats}{decoder}{ipv4},
677             dec_ipv6 => $json->{stats}{decoder}{ipv6},
678             dec_udp => $json->{stats}{decoder}{udp},
679             dec_tcp => $json->{stats}{decoder}{tcp},
680             dec_avg_pkt_size => $json->{stats}{decoder}{avg_pkt_size},
681             dec_max_pkt_size => $json->{stats}{decoder}{max_pkt_size},
682             dec_chdlc => $json->{stats}{decoder}{chdlc},
683             dec_ethernet => $json->{stats}{decoder}{ethernet},
684             dec_geneve => $json->{stats}{decoder}{geneve},
685             dec_ieee8021ah => $json->{stats}{decoder}{ieee8021ah},
686             dec_ipv4_in_ipv6 => $json->{stats}{decoder}{ipv6_in_ipv6},
687             dec_mx_mac_addrs_d => $json->{stats}{decoder}{max_mac_addrs_dst},
688             dec_mx_mac_addrs_s => $json->{stats}{decoder}{max_mac_addrs_src},
689             dec_mpls => $json->{stats}{decoder}{mpls},
690             dec_ppp => $json->{stats}{decoder}{ppp},
691             dec_pppoe => $json->{stats}{decoder}{pppoe},
692             dec_raw => $json->{stats}{decoder}{raw},
693             dec_sctp => $json->{stats}{decoder}{sctp},
694             dec_sll => $json->{stats}{decoder}{sll},
695             dec_teredo => $json->{stats}{decoder}{teredo},
696             dec_too_many_layer => $json->{stats}{decoder}{too_many_layers},
697             dec_vlan => $json->{stats}{decoder}{vlan},
698             dec_vlan_qinq => $json->{stats}{decoder}{vlan_qinq},
699             dec_vntag => $json->{stats}{decoder}{vntag},
700             dec_vxlan => $json->{stats}{decoder}{vxlan},
701             f_tcp => $json->{stats}{flow}{tcp},
702             f_udp => $json->{stats}{flow}{udp},
703             f_icmpv4 => $json->{stats}{flow}{icmpv4},
704             f_icmpv6 => $json->{stats}{flow}{icmpv6},
705             f_memuse => $json->{stats}{flow}{memuse},
706             ftp_memuse => $json->{stats}{ftp}{memuse},
707             http_memuse => $json->{stats}{http}{memuse},
708             tcp_memuse => $json->{stats}{tcp}{memuse},
709             tcp_reass_memuse => $json->{stats}{tcp}{reassembly_memuse},
710             af_* => $json->{stats}{app_layer}{flow}{*}
711             at_* => $json->{stats}{app_layer}{tx}{*}
712              
713             =head1 AUTHOR
714              
715             Zane C. Bowers-Hadley, C<< >>
716              
717             =head1 BUGS
718              
719             Please report any bugs or feature requests to C, or through
720             the web interface at L. I will be notified, and then you'll
721             automatically be notified of progress on your bug as I make changes.
722              
723              
724              
725              
726             =head1 SUPPORT
727              
728             You can find documentation for this module with the perldoc command.
729              
730             perldoc Suricata::Monitoring
731              
732              
733             You can also look for information at:
734              
735             =over 4
736              
737             =item * RT: CPAN's request tracker (report bugs here)
738              
739             L
740              
741             =item * CPAN Ratings
742              
743             L
744              
745             =item * Search CPAN
746              
747             L
748              
749             =back
750              
751              
752             =head * Git
753              
754             L
755              
756             =item * Web
757              
758             L
759              
760             =head1 ACKNOWLEDGEMENTS
761              
762              
763             =head1 LICENSE AND COPYRIGHT
764              
765             This software is Copyright (c) 2022 by Zane C. Bowers-Hadley.
766              
767             This is free software, licensed under:
768              
769             The Artistic License 2.0 (GPL Compatible)
770              
771              
772             =cut
773              
774             1; # End of Suricata::Monitoring