From 40af76cb834f8f148f72991ea9452f100eab9e77 Mon Sep 17 00:00:00 2001 From: Quentin Garnier Date: Mon, 16 Feb 2015 16:07:51 +0100 Subject: [PATCH] Refs #6124 --- apps/protocols/dhcp/mode/connection.pm | 245 +++++++++++++++++++------ apps/protocols/imap/mode/login.pm | 2 +- 2 files changed, 189 insertions(+), 58 deletions(-) diff --git a/apps/protocols/dhcp/mode/connection.pm b/apps/protocols/dhcp/mode/connection.pm index 1299f2fbe..3b93b5075 100644 --- a/apps/protocols/dhcp/mode/connection.pm +++ b/apps/protocols/dhcp/mode/connection.pm @@ -39,12 +39,11 @@ use base qw(centreon::plugins::mode); use strict; use warnings; - +use Time::HiRes qw(gettimeofday tv_interval); use IO::Socket; use IO::Select; use Net::DHCP::Packet; use Net::DHCP::Constants; -use Data::Dumper; sub new { my ($class, %options) = @_; @@ -54,70 +53,185 @@ sub new { $self->{version} = '1.0'; $options{options}->add_options(arguments => { - "serverip:s" => { name => 'serverip' }, - "timeout:s" => { name => 'timeout', default => '3'}, - "macaddr:s" => { name => 'macaddr', default => '999999000000'}, + "serverip:s@" => { name => 'serverip' }, + "out-first-valid" => { name => 'out_first_valid' }, + "timeout:s" => { name => 'timeout', default => 15 }, + "macaddr:s" => { name => 'macaddr', default => '999999100000'}, + "interface:s" => { name => 'interface', default => 'eth0' }, + "cidr-match:s@" => { name => 'cidr_match' }, }); + $self->{unicast} = 0; return $self; } sub check_options { my ($self, %options) = @_; $self->SUPER::init(%options); + + if (defined($self->{option_results}->{serverip}) && scalar(@{$self->{option_results}->{serverip}}) > 0) { + $self->{unicast} = 1; + } + $self->{subnet_matcher} = undef; + if (defined($self->{option_results}->{cidr_match}) && scalar(@{$self->{option_results}->{cidr_match}}) > 0) { + centreon::plugins::misc::mymodule_load(output => $self->{output}, module => 'Net::Subnet', + error_msg => "Cannot load module 'Net::Subnet'."); + $self->{subnet_matcher} = Net::Subnet::subnet_matcher(@{$self->{option_results}->{cidr_match}}); + } +} + +sub send_discover { + my ($self, %options) = @_; + + $self->{random_number} = int(rand(0xFFFFFFFF)); + #Create DHCP Discover Packet + my $discover = Net::DHCP::Packet->new( + Xid => $self->{random_number}, + Flags => $self->{unicast} == 1 ? 0 : 0x8000, + Chaddr => $self->{option_results}->{macaddr}, + Giaddr => $self->{my_ip}, + Hops => $self->{unicast} == 1 ? 1 : 0, + DHO_HOST_NAME() => 'centreon', + DHO_VENDOR_CLASS_IDENTIFIER() => 'foo', + DHO_DHCP_MESSAGE_TYPE() => DHCPDISCOVER(), + ); + my $str = $discover->serialize(); + #Send UDP Packet + $self->{timing0} = [gettimeofday]; + if ($self->{unicast} == 0) { + my $remoteipaddr = sockaddr_in(67, INADDR_BROADCAST); + send($self->{socket}, $str, 0, $remoteipaddr); + } else { + foreach my $server_ip (@{$self->{option_results}->{serverip}}) { + my $remoteipaddr = sockaddr_in(67, inet_aton($server_ip)); + send($self->{socket}, $str, 0, $remoteipaddr); + } + } +} + +sub create_socket { + my ($self, %options) = @_; + + #Create UDP Socket + socket($self->{socket}, AF_INET, SOCK_DGRAM, getprotobyname('udp')); + setsockopt($self->{socket}, SOL_SOCKET, SO_REUSEADDR, 1); + if ($self->{unicast} == 0) { + setsockopt($self->{socket}, SOL_SOCKET, SO_BROADCAST, 1); + } + + $self->{my_ip} = undef; + if ($self->{unicast} == 1) { + $self->{my_ip} = $self->get_interface_address(interface => $self->{option_results}->{interface}); + } + my $port = $self->{unicast} == 1 ? '67' : '68'; + my $addr = $self->{unicast} == 1 ? inet_aton($self->{my_ip}) : INADDR_ANY; + my $binding = bind($self->{socket}, sockaddr_in($port, $addr)); +} + +sub get_interface_address { + my ($self, %options) = @_; + + require 'sys/ioctl.ph'; + my $socket; + if (!socket($socket, PF_INET, SOCK_STREAM, (getprotobyname('tcp'))[2])) { + $self->{output}->output_add(severity => 'UNKNOWN', + short_msg => "cannot get interface address: $!"); + $self->{output}->display(); + $self->{output}->exit(); + } + my $buf = pack('a256', $options{interface}); + if (ioctl($socket, SIOCGIFADDR(), $buf) && (my @address = unpack('x20 C4', $buf))) { + return join('.', @address); + } + + $self->{output}->output_add(severity => 'UNKNOWN', + short_msg => "cannot get interface address: $!"); + $self->{output}->display(); + $self->{output}->exit(); +} + +sub get_offer { + my ($self, %options) = @_; + $self->{discresponse} = []; + + #Wait DHCP OFFER Packet + my $wait = IO::Select->new($self->{socket}); + my $timeout = $self->{option_results}->{timeout}; + my $time = time(); + $self->{random_number} = sprintf("%x", $self->{random_number}); + while (my ($found) = $wait->can_read($timeout)) { + $timeout -= time() - $time; + $time = time(); + my $srcpaddr = recv($self->{socket}, my $data, 4096, 0); + my $response = new Net::DHCP::Packet($data); + my $response_readable = $response->toString(); + # Need to get same Xid and MacAddr in response. Otherwise not for me + if ($response_readable =~ /^xid\s+=\s+$self->{random_number}/mi && + $response_readable =~ /^chaddr\s+=\s+$self->{option_results}->{macaddr}/mi) { + $self->{timeelapsed} = tv_interval($self->{timing0}, [gettimeofday]); + push @{$self->{discresponse}}, $response_readable; + last if (defined($self->{option_results}->{out_first_valid})); + } + } + close $self->{socket}; +} + +sub check_results { + my ($self, %options) = @_; + + foreach my $response (@{$self->{discresponse}}) { + $response =~ /DHO_DHCP_LEASE_TIME.*?=\s+(.*?)\n/m; + my $lease_time = $1; + my $yiaddr; + if ($response =~ /^yiaddr\s+=\s+(.*?)\n/m) { + $yiaddr = $1; + } + $response =~ /^siaddr\s+=\s+(.*?)\n/m; + my $siaddr = $1; + + $self->{output}->output_add(long_msg => sprintf("Response from %s : offer address %s (lease time: %s)", $siaddr, $yiaddr, $lease_time)); + if (defined($self->{subnet_matcher})) { + if (!$self->{subnet_matcher}->($yiaddr)) { + $self->{output}->output_add(severity => 'CRITICAL', + short_msg => sprintf("Offer address %s not matching (from: %s)", $yiaddr, $siaddr)); + } + } else { + if (!defined($yiaddr) || $yiaddr eq '' || $yiaddr eq '0.0.0.0' ) { + $self->{output}->output_add(severity => 'CRITICAL', + short_msg => sprintf("No free lease from %s server", $siaddr)); + } + } + } +} + +sub result { + my ($self, %options) = @_; + + $self->{output}->output_add(severity => 'OK', + short_msg => sprintf("DHCP Server found with free lease")); + if (scalar(@{$self->{discresponse}}) == 0) { + $self->{output}->output_add(severity => 'CRITICAL', + short_msg => sprintf("No DHCPOFFERs were received")); + } elsif ($self->{unicast} == 1) { + if (scalar(@{$self->{discresponse}}) != scalar(@{$self->{option_results}->{serverip}})) { + $self->{output}->output_add(severity => 'CRITICAL', + short_msg => sprintf("%d of %d requested servers responded", + scalar(@{$self->{discresponse}}), scalar(@{$self->{option_results}->{serverip}}))); + } + } + + $self->check_results(); + $self->{output}->perfdata_add(label => "time", unit => 'ms', + value => sprintf('%.3f', $self->{timeelapsed})); + } sub run { my ($self, %options) = @_; - my ($discover, $socket, $listen, $response, $discresponse, $buf, $serverip); - - #Create DHCP Discover Packet - $discover = Net::DHCP::Packet->new( - Xid => int(rand(0xFFFFFFFF)), - Flags => 0x8000, - Chaddr => $self->{option_results}->{macaddr}, - DHO_HOST_NAME() => 'perl test', - DHO_VENDOR_CLASS_IDENTIFIER() => 'foo', - DHO_DHCP_MESSAGE_TYPE() => DHCPDISCOVER(), - ); - - #Create UDP Socket - socket($socket, AF_INET, SOCK_DGRAM, getprotobyname('udp')); - setsockopt($socket, SOL_SOCKET, SO_REUSEADDR, 1); - setsockopt($socket, SOL_SOCKET, SO_BROADCAST, 1); - my $remoteipaddr = sockaddr_in(67, INADDR_BROADCAST); - my $str = $discover->serialize(); - my $binding = bind($socket, sockaddr_in('68', INADDR_ANY)); - - #Send UDP Packet - send($socket, $str, 0, $remoteipaddr); - - #Wait DHCP OFFER Packet - my $wait = IO::Select->new($socket); - while (my ($found) = $wait->can_read($self->{option_results}->{timeout})) { - my $srcpaddr = recv($socket, my $data, 4096, 0); - $response = new Net::DHCP::Packet($data); - #$discresponse = $response->toString(); - } - - - foreach my $k (keys(%{$response})) { - print "Clef=$k Valeur=$response{$k}\n"; - } - - close $socket; - - #Output - if ($discresponse !~ /siaddr = $self->{option_results}->{serverip}/) { - $self->{output}->output_add(severity => 'CRITICAL', - short_msg => sprintf("No DHCP Server found")); - } elsif ( $discresponse =~ /yiaddr = 0.0.0.0/ ) { - $self->{output}->output_add(severity => 'WARNING', - short_msg => sprintf("DHCP Server found with no free lease")); - } else { - $self->{output}->output_add(severity => 'OK', - short_msg => sprintf("DHCP Server found with free lease")); - } + $self->create_socket(); + $self->send_discover(); + $self->get_offer(); + $self->result(); $self->{output}->display(); $self->{output}->exit(); @@ -132,13 +246,30 @@ Check DHCP server availability =over 8 -=item B<--hostname> +=item B<--serverip> -IP Addr of the DHCP server +IP Addr of the DHCP server to query (do a unicast mode) =item B<--timeout> -Connection timeout in seconds (Default: 3) +How much time to check dhcp responses (Default: 15 seconds) + +=item B<--out-first-valid> + +Stop after first valid dhcp response + +=item B<--macaddr> + +MAC address to use in the DHCP request + +=item B<--interface> + +Interface to to use for listening (Default: eth0) + +=item B<--cidr-match> + +Match ip addresses offered (can be used multiple times). +Returns critical for each ip addresses with no match. =back diff --git a/apps/protocols/imap/mode/login.pm b/apps/protocols/imap/mode/login.pm index 3349fb19d..ce3882686 100644 --- a/apps/protocols/imap/mode/login.pm +++ b/apps/protocols/imap/mode/login.pm @@ -89,7 +89,7 @@ sub run { apps::protocols::imap::lib::imap::connect($self, connection_exit => 'critical'); apps::protocols::imap::lib::imap::quit(); - my $timeelapsed = tv_interval ($timing0, [gettimeofday]); + my $timeelapsed = tv_interval($timing0, [gettimeofday]); my $exit = $self->{perfdata}->threshold_check(value => $timeelapsed, threshold => [ { label => 'critical', 'exit_litteral' => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);