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