File Coverage

blib/lib/Suricata/Ping.pm
Criterion Covered Total %
statement 23 100 23.0
branch 0 54 0.0
condition 0 9 0.0
subroutine 8 10 80.0
pod 2 2 100.0
total 33 175 18.8


line stmt bran cond sub pod time code
1             package Suricata::Ping;
2              
3 1     1   159016 use 5.006;
  1         5  
4 1     1   13 use strict;
  1         3  
  1         47  
5 1     1   7 use warnings;
  1         2  
  1         70  
6 1     1   814 use Regexp::IPv6 qw($IPv6_re);
  1         2040  
  1         180  
7 1     1   738 use Regexp::IPv4 qw($IPv4_re);
  1         388  
  1         127  
8 1     1   747 use YAML::XS qw(Load);
  1         4827  
  1         81  
9 1     1   916 use File::Slurp qw(read_file);
  1         44009  
  1         56  
10 1     1   590 use Hash::Merge ();
  1         6850  
  1         916  
11              
12             =head1 NAME
13              
14             Suricata::Ping - Reads in a suricata config and sends a ping to the specifid interface.
15              
16             =head1 VERSION
17              
18             Version 0.0.1
19              
20             =cut
21              
22             our $VERSION = '0.0.1';
23              
24             =head1 SYNOPSIS
25              
26             Quick summary of what the module does.
27              
28             Perhaps a little code snippet.
29              
30             use Suricata::Ping;
31              
32             my $suricata_ping;
33             eval {
34             $suricata_ping=Suricata::Ping->new(
35             config_file=>'/usr/local/etc/suricata/config.yaml'
36             );
37             };
38             if ($@) {
39             print $@."\n";
40             exit 1;
41             }
42             $suricata_ping->ping;
43              
44             =head1 SUBROUTINES/METHODS
45              
46             =head2 new
47              
48             Initiates the object.
49              
50             - config_file :: The Suricata config to read in.
51             - default :: undef
52              
53             - pattern :: Pattern to use with the ping.
54             - default :: e034o31qwe9034oldlAd31qdgf3
55              
56             - ip :: The IP to ping.
57             - default :: 8.8.8.8
58              
59             - section :: The config section of the config to use.
60             - default, Linux :: af_packet
61             - default, other :: pcap
62              
63             - count :: Number of packets to send.
64             - default :: 5
65              
66             =cut
67              
68             sub new {
69 0     0 1   my ( $empty, %opts ) = @_;
70              
71             # make sure the value passed for the config file looks sane
72 0 0         if ( !defined( $opts{config_file} ) ) {
    0          
    0          
73 0           die('$opts{config_file} is undef');
74             } elsif ( !-f $opts{config_file} ) {
75 0           die( '$opts{config_file}, "' . $opts{config_file} . '", is not a file' );
76             } elsif ( !-r $opts{config_file} ) {
77 0           die( '$opts{config_file}, "' . $opts{config_file} . '", is readable' );
78             }
79              
80             # make sure we have a IP specified for IP or use the default
81 0 0         if ( defined( $opts{ip} ) ) {
82 0 0 0       if ( ref( $opts{ip} ) ne '' ) {
    0          
83 0           die( '$opts{ip} has a ref of "' . ref( $opts{ip} ) . '" instead of ""' );
84             } elsif ( $opts{ip} !~ /^$IPv4_re$/
85             && $opts{ip} !~ /^$IPv6_re$/ )
86             {
87 0           die( '$opts{ip}, "' . $opts{ip} . '", does not appear to IPv4 or 6' );
88             }
89             } else {
90 0           $opts{ip} = '8.8.8.8';
91             }
92              
93             # make sure we have a count specified for count or use the default
94 0 0         if ( defined( $opts{count} ) ) {
95 0 0         if ( ref( $opts{count} ) ne '' ) {
    0          
    0          
96 0           die( '$opts{count} has a ref of "' . ref( $opts{count} ) . '" instead of ""' );
97             } elsif ( $opts{count} !~ /^[0-9]+$/ ) {
98 0           die( '$opts{count}, "' . $opts{count} . '", does not appear to appear to be a integer' );
99             } elsif ( $opts{count} < 1 ) {
100 0           die( '$opts{count}, "' . $opts{count} . '", may not be less than 1' );
101             }
102             } else {
103 0           $opts{count} = 5;
104             }
105              
106             # make sure we have a sane value for pattern or use the default
107 0 0         if ( defined( $opts{pattern} ) ) {
108 0 0         if ( ref( $opts{pattern} ) ne '' ) {
109 0           die( '$opts{pattern} has a ref of "' . ref( $opts{pattern} ) . '" instead of ""' );
110             }
111             } else {
112 0           $opts{pattern} = 'e034o31qwe9034oldlAd31qdgf3';
113             }
114              
115             # make sure we have something sane for the section or use the default
116 0 0         if ( defined( $opts{section} ) ) {
117 0 0         if ( ref( $opts{section} ) ne '' ) {
118 0           die( '$opts{section} has a ref of "' . ref( $opts{section} ) . '" instead of ""' );
119             }
120             } else {
121 0 0         if ( $^O eq 'linux' ) {
122 0           $opts{section} = 'af-packet';
123             } else {
124 0           $opts{section} = 'pcap';
125             }
126             }
127              
128             # read in the base config
129 0           my $raw_config;
130 0           eval { $raw_config = read_file( $opts{config_file} ); };
  0            
131 0 0         if ($@) {
132 0           die( 'Failed to read in "' . $opts{config_file} . '"... ' . $@ );
133             }
134              
135             # parse the base config
136 0           my $parsed_config;
137 0           eval { $parsed_config = Load($raw_config); };
  0            
138 0 0         if ($@) {
139 0           die( 'Parsing "' . $opts{config_file} . '" failed... ' . $@ );
140             }
141              
142             # read in the includes if needed
143 0 0         if ( defined( $parsed_config->{include} ) ) {
144 0 0         if ( ref( $parsed_config->{include} ) ne 'ARRAY' ) {
145             die( '.include is defined but is of ref type "'
146             . ref( $parsed_config->{include} )
147 0           . '" instead of "ARRAY"' );
148             }
149              
150 0           my $merger = Hash::Merge->new('RIGHT_PRECEDENT');
151              
152 0           my $include_int = 0;
153 0           while ( defined( $parsed_config->{include}[$include_int] ) ) {
154 0 0         if ( ref( $parsed_config->{include}[$include_int] ) ne '' ) {
155             die( '.include['
156             . $include_int
157             . '] is defined but is of ref type "'
158             . ref( $parsed_config->{include} )
159 0           . '" instead of ""' );
160             }
161              
162 0           my $raw_include;
163 0           eval { $raw_include = read_file( $parsed_config->{include}[$include_int] ); };
  0            
164 0 0         if ($@) {
165             die( 'Failed to read in include['
166             . $include_int . '], "'
167 0           . $parsed_config->{include}[$include_int] . '"... '
168             . $@ );
169             }
170              
171 0           my $parsed_include;
172 0           eval { $parsed_include = Load($raw_include); };
  0            
173 0 0         if ($@) {
174             die( 'Parsing .include['
175             . $include_int . '], "'
176 0           . $parsed_config->{include}[$include_int]
177             . '", failed... '
178             . $@ );
179             }
180              
181 0           $parsed_config = $merger->merge( $parsed_config, $parsed_include );
182              
183 0           $include_int++;
184             } ## end while ( defined( $parsed_config->{include}[$include_int...]))
185             } ## end if ( defined( $parsed_config->{include} ) )
186              
187 0 0         if ( !defined( $parsed_config->{ $opts{section} } ) ) {
188 0           die( '.' . $opts{section} . ' not found in the config file ' . $opts{config_file} );
189             }
190 0 0         if ( ref( $parsed_config->{ $opts{section} } ) ne 'ARRAY' ) {
191 0           die( 'section .' . $opts{section} . ' ref is "' . $parsed_config->{ $opts{section} } . '" and not "ARRAY"' );
192             }
193 0 0         if ( !defined( $parsed_config->{ $opts{section} }[0] ) ) {
194 0           die( '.' . $opts{section} . '[0] is undef so this config has no configured interfaces' );
195             }
196              
197 0           my @interfaces;
198 0           my $interfaces_int = 0;
199 0           while ( defined( $parsed_config->{ $opts{section} }[$interfaces_int] ) ) {
200 0 0 0       if ( ( ref( $parsed_config->{ $opts{section} }[$interfaces_int] ) eq 'HASH' )
      0        
201             && defined( $parsed_config->{ $opts{section} }[$interfaces_int]{interface} )
202             && ( ref( $parsed_config->{ $opts{section} }[$interfaces_int]{interface} ) eq '' ) )
203             {
204 0           push( @interfaces, $parsed_config->{ $opts{section} }[$interfaces_int]{interface} );
205             }
206              
207 0           $interfaces_int++;
208             } ## end while ( defined( $parsed_config->{ $opts{section...}}))
209              
210 0 0         if ( !defined( $interfaces[0] ) ) {
211             die( 'No configured interfaces found in the config file "'
212             . $opts{config_file}
213             . '" under the section .'
214 0           . $opts{section} );
215             }
216              
217             my $self = {
218             config_file => $opts{config_file},
219             pattern => $opts{pattern},
220             ip => $opts{ip},
221             section => $opts{section},
222             interfaces => \@interfaces,
223             count => $opts{count},
224 0           };
225 0           bless $self;
226              
227 0           return $self;
228             } ## end sub new
229              
230             =head2 ping
231              
232             Pings each interface.
233              
234             This does not return any status or the like.
235              
236             This requires ping be in the current path.
237              
238             $suricata_ping->ping;
239              
240             =cut
241              
242             sub ping {
243 0     0 1   my ( $self, %opts ) = @_;
244              
245 0           foreach my $interface ( @{ $self->{interfaces} } ) {
  0            
246 0           system( 'ping', '-c', $self->{count}, '-p', $self->{pattern}, '-I', $interface, $self->{ip} );
247             }
248             }
249              
250             =head1 AUTHOR
251              
252             Zane C. Bowers-Hadley, C<< >>
253              
254             =head1 BUGS
255              
256             Please report any bugs or feature requests to C, or through
257             the web interface at L. I will be notified, and then you'll
258             automatically be notified of progress on your bug as I make changes.
259              
260              
261              
262              
263             =head1 SUPPORT
264              
265             You can find documentation for this module with the perldoc command.
266              
267             perldoc Suricata::Ping
268              
269              
270             You can also look for information at:
271              
272             =over 4
273              
274             =item * RT: CPAN's request tracker (report bugs here)
275              
276             L
277              
278             =item * CPAN Ratings
279              
280             L
281              
282             =item * Search CPAN
283              
284             L
285              
286             =back
287              
288              
289             =head1 ACKNOWLEDGEMENTS
290              
291              
292             =head1 LICENSE AND COPYRIGHT
293              
294             This software is Copyright (c) 2026 by Zane C. Bowers-Hadley.
295              
296             This is free software, licensed under:
297              
298             The GNU Lesser General Public License, Version 2.1, February 1999
299              
300              
301             =cut
302              
303             1; # End of Suricata::Ping