summaryrefslogtreecommitdiffstats
path: root/lib/probes/Radius.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/probes/Radius.pm')
-rw-r--r--lib/probes/Radius.pm184
1 files changed, 184 insertions, 0 deletions
diff --git a/lib/probes/Radius.pm b/lib/probes/Radius.pm
new file mode 100644
index 0000000..2c4fb96
--- /dev/null
+++ b/lib/probes/Radius.pm
@@ -0,0 +1,184 @@
+package probes::Radius;
+
+=head1 NAME
+
+probes::Radius - a RADIUS authentication probe for SmokePing
+
+=head1 OVERVIEW
+
+Measures RADIUS authentication latency for SmokePing
+
+=head1 SYNOPSYS
+
+ *** Probes ***
+ + Radius
+
+ passwordfile = /usr/share/smokeping/etc/password
+ secretfile = /etc/raddb/secret
+ sleeptime = 0.5 # optional, 1 second by default
+ username = test-user # optional, overridden by target
+ password = test-password # optional, overridden by target
+ secret = test-secret # optional, overridden by target
+
+ *** Targets ***
+
+ probe = Radius
+
+ + PROBE_CONF
+ username = testuser
+ secret = myRadiusSecret # if not present in <secretfile>
+ password = testuserPass # if not present in <passwordfile>
+ port = 1645 # optional
+ nas_ip_address = 1.2.3.4 # optional
+
+=head1 DESCRIPTION
+
+This probe measures RADIUS (RFC 2865) authentication latency for SmokePing.
+
+The username to be tested is specified in either the probe-specific or the
+target-specific variable `username', with the target-specific one overriding
+the probe-specific one.
+
+The password can be specified either (in order of precedence, with the latter
+overriding the former) in the probe-specific variable `password', in the
+target-specific variable `password' or in an external file. The location of
+this file is given in the probe-specific variable `passwordfile'. See
+probes::passwordchecker(3pm) for the format of this file (summary:
+colon-separated triplets of the form `<host>:<username>:<password>')
+
+The RADIUS protocol requires a shared secret between the server and the client.
+This secret can be specified either (in order of precedence, with the latter
+overriding the former) in the probe-specific variable `secret', in the
+target-specific variable `secret' or in an external file.
+This external file is located by the probe-specific variable `secretfile', and it should
+contain whitespace-separated pairs of the form `<host> <secret>'. Comments and blank lines
+are OK.
+
+If the optional probe-specific variable `nas_ip_address' is specified, its
+value is inserted into the authentication requests as the `NAS-IP-Address'
+RADIUS attribute.
+
+The probe tries to be nice to the server and sleeps for the probe-specific
+variable `sleeptime' (one second by default) between each authentication
+request.
+
+=head1 AUTHOR
+
+Niko Tyni E<lt>ntyni@iki.fiE<gt>
+
+=head1 BUGS
+
+There should be a more general way of specifying RADIUS attributes.
+
+=cut
+
+use strict;
+use probes::passwordchecker;
+use base qw(probes::passwordchecker);
+use Authen::Radius;
+use Time::HiRes qw(gettimeofday sleep);
+use Carp;
+
+sub ProbeDesc {
+ return "RADIUS queries";
+}
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = $class->SUPER::new(@_);
+
+ # no need for this if we run as a cgi
+ unless ($ENV{SERVER_SOFTWARE}) {
+ if (defined $self->{properties}{secretfile}) {
+ open(S, "<$self->{properties}{secretfile}")
+ or croak("Error opening specified secret file $self->{properties}{secretfile}: $!");
+ while (<S>) {
+ chomp;
+ next unless /\S/;
+ next if /^\s*#/;
+ my ($host, $secret) = split;
+ carp("Line $. in $self->{properties}{secretfile} is invalid"), next
+ unless defined $host and defined $secret;
+ $self->secret($host, $secret);
+ }
+ close S;
+ }
+
+ my $sleeptime = $self->{properties}{sleeptime};
+ $sleeptime = 1 unless defined $sleeptime;
+ $self->sleeptime($sleeptime);
+
+ }
+
+ return $self;
+}
+
+sub secret {
+ my $self = shift;
+ my $host = shift;
+ my $newval = shift;
+
+ $self->{secret}{$host} = $newval if defined $newval;
+ return $self->{secret}{$host};
+}
+
+sub sleeptime {
+ my $self = shift;
+ my $newval = shift;
+
+ $self->{sleeptime} = $newval if defined $newval;
+ return $self->{sleeptime};
+}
+
+sub pingone {
+ my $self = shift;
+ my $target = shift;
+ my $host = $target->{addr};
+ my $vars = $target->{vars};
+ my $username = $vars->{username} || $self->{properties}->{username};
+ my $secret = $vars->{secret} || $self->secret($host) || $self->{properties}->{secret};
+
+ $self->do_log("Missing RADIUS secret for $host"), return
+ unless defined $secret;
+
+ $self->do_log("Missing RADIUS username for $host"), return
+ unless defined $username;
+
+ my $password = $vars->{password} || $self->password($host, $username) || $self->{properties}->{password};
+
+ my $port = $vars->{port};
+ $host .= ":$port" if defined $port;
+
+ $self->do_log("Missing RADIUS password for $host/$username"), return
+ unless defined $password;
+
+ my @times;
+ for (1..$self->pings($target)) {
+ my $r = new Authen::Radius(Host => $host, Secret => $secret);
+ $r->add_attributes(
+ { Name => 1, Value => $username, Type => 'string' },
+ { Name => 2, Value => $password, Type => 'string' },
+ );
+ $r->add_attributes( { Name => 4, Type => 'ipaddr', Value => $vars->{nas_ip_address} })
+ if exists $vars->{nas_ip_address};
+ my $c;
+ my $start = gettimeofday();
+ $r->send_packet(ACCESS_REQUEST) and $c = $r->recv_packet;
+ my $end = gettimeofday();
+ my $result;
+ if (defined $c) {
+ $result = $c;
+ $result = "OK" if $c == ACCESS_ACCEPT;
+ $result = "fail" if $c == ACCESS_REJECT;
+ } else {
+ $result = "no reply";
+ }
+ $self->do_debug("$host: radius query $_: $result, " . ($end - $start));
+ push @times, $end - $start if (defined $c and $c == ACCESS_ACCEPT);
+ sleep $self->sleeptime; # be nice
+ }
+ return sort { $a <=> $b } @times;
+}
+
+1;