This file is indexed.

/usr/share/perl5/Munin/Node/SNMPConfig.pm is in munin-node 2.0.25-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
package Munin::Node::SNMPConfig;

# $Id$

use strict;
use warnings;

use Net::SNMP;
use Munin::Node::Configure::HostEnumeration;
use Munin::Node::Configure::Debug;


### SNMP Probing ###############################################################

sub new
{
    my ($class, %opts) = @_;

    my %sec_args;

    my $hosts    = $opts{hosts};
    die "No host list specified\n" unless scalar @$hosts;

    my $version  = $opts{version} || '2c';
    my $port     = $opts{port}    || 161;

    if ($version eq '3') {
        # Privacy
        my $privpw    = $opts{privpassword};
        my $privproto = $opts{privprotocol} || 'des';

        if ($privpw) {
            $sec_args{-privpassword} = $privpw;
            $sec_args{-privprotocol} = $privproto;
            DEBUG('Enabled SNMPv3 privacy');
        }

        # Authentication
        my $authpw    = $opts{authpassword} || $privpw;
        my $authproto = $opts{authprotocol} || 'md5';

        if ($authpw) {
            $sec_args{-authpassword} = $authpw;
            $sec_args{-authprotocol} = $authproto;
            DEBUG('Enabled SNMPv3 authentication');
        }

        # Username
        $sec_args{-username} = $opts{username};
    }
    else {
        # version 1 or 2c
        $sec_args{-community} = $opts{community} || 'public';
    }


    my %snmp = (
        hosts     => $hosts,
        port      => $port,
        version   => $version,
        sec_args  => \%sec_args,
    );

    return bless \%snmp, $class;
}


sub run_probes
{
    my ($self, $plugins) = @_;

    foreach my $host (expand_hosts(@{$self->{hosts}})) {
        $self->_probe_single_host($host, $plugins);
    }

    return;
}


# Checks each plugin in turn against the host, and make a note of
# those that are supported.
sub _probe_single_host
{
    my ($self, $host, $plugins) = @_;

    DEBUG("SNMP-probing $host");
	my ($session, $error) = Net::SNMP->session(
		-hostname  => $host,
        -port      => $self->{port},
        -version   => $self->{version},

        %{$self->{sec_args}},

		# Disable munging of responses into "human readable" form
		-translate => 0,
	);
	unless ($session) {
        DEBUG("Dropping host '$host': $error");
		return 0;
	}

	foreach my $plugin ($plugins->list) {
        DEBUG("Running autoconf on $plugin->{name} for $host");
		if (my @suggestions = _snmp_autoconf_plugin($plugin, $session)) {
			$plugin->{default} = 'yes';
            $plugin->add_suggestions(@suggestions);
		}
		else {
            DEBUG("Host '$host' doesn't support $plugin->{name}");
            # TODO: Check whether there was a timeout?  that would indicate a
            # bad community string, or no SNMP support (either no daemon, or
            # unsupported protocol version) -- any downsides?  are there
            # devices that timeout rather than return noSuchObject?  compromise
            # would be to have a --ignore-snmp-timeouts cmdline flag.
            #
            # TODO, redux: capture and handle SNMPv3 errors.
		}
	}

    DEBUG("Finished probing $host");
	$session->close;

	return 1;
}


# If the SNMP agent supports the plugin, returns a list of arrayrefs, each of
# which represents a valid plugin instance.
sub _snmp_autoconf_plugin
{
    my ($plugin, $session) = @_;

    my $hostname = $session->hostname;
    my @valid_indexes;

    # First round of requirements -- check for specific OIDs.
    if ($plugin->{require_oid}) {
        DEBUG("Checking required OIDs");
        foreach my $req (@{$plugin->{require_oid}}) {
            my ($oid, $filter) = @$req;
            unless (_snmp_check_require($session, $oid, $filter)) {
                DEBUG("Missing requirement.");
                return;
            }
        }
    }

    # Second round of requirements -- check for valid rows in a table.
    if ($plugin->{table}) {
        my @columns = map { $_->[0] } @{$plugin->{table}};

        DEBUG('Fetching columns: ' . join ', ', @columns);
        my $result = $session->get_entries(-columns => \@columns);
        unless ($result) {
            DEBUG('Failed to get required columns');
            return;
        }

        # sort into rows
        my $subtabs = join '|', map { quotemeta } @columns;
        my $re = qr/^($subtabs)\.(.*)/;
        my %table;

        while (my ($oid, $value) = each %$result) {
            my ($column, $index) = $oid =~ /$re/;
            DEBUG("Row '$index', column '$column', value '$value'");
            $table{$index}->{$column} = $value;
        }

        DEBUG('Checking for valid rows');

        # work out what rows are invalid
        # can shortcut unless it's a double-wildcard plugin
        while (my ($index, $row) = each %table) {
            if (_snmp_check_row($index, $row, @{$plugin->{table}})) {
                if ($plugin->is_wildcard) {
                    DEBUG(qq{Adding row '$index' to the list of valid indexes});
                    push @valid_indexes, $row->{$plugin->{index}};
                }
                else {
                    DEBUG('Table contains at least one valid row.');
                    return [ $hostname ];
                }
            }
        }

        # if we got here, there were no matching rows.
        unless ($plugin->is_wildcard and @valid_indexes) {
            DEBUG('No valid rows found');
            return;
        }
    }

    # return list of arrayrefs, one for each good suggestion
    return $plugin->is_wildcard ? map { [ $hostname, $_ ] } @valid_indexes
                                : [ $hostname ];
}


# returns true if the row in a table fulfils all the requirements, false
# otherwise.
sub _snmp_check_row
{
    my ($index, $row, @requirements) = @_;

    foreach my $req (@requirements) {
        my ($oid, $regex) = @$req;

        unless (defined $row->{$oid}) {
            DEBUG(qq{Row '$index' doesn't have an entry for column '$oid'});
            return 0;
        }

        if ($regex and $row->{$oid} !~ /$regex/) {
            DEBUG(qq{Row '$index', column '$oid'.  Value '$row->{$oid}' doesn't match '$regex'});
            return 0;
        }
    }

    DEBUG(qq{Row '$index' is valid});
    return 1;
}


# Returns true if the SNMP agent supports the 'require', false otherwise.
sub _snmp_check_require
{
	my ($session, $oid, $filter) = @_;

	my $value = _snmp_get_single($session, $oid);
	return !(!defined $value or ($filter and $value !~ /$filter/));
}


# Retrieves the value for the given OID from the session
sub _snmp_get_single
{
	my ($session, $oid) = @_;

	my $response = $session->get_request($oid);

	unless (defined $response and $session->error_status == 0) {
        DEBUG("Request failed for oid '$oid'");
		return;
	}

    DEBUG("Fetched $oid -> '$response->{$oid}'");
	return $response->{$oid};
}


1;


__END__

=head1 NAME

Munin::Node::SNMPConfig - Subroutines providing munin-node-configure's SNMP
scanning capabilities.


=head1 SYNOPSIS

  my $snmp = Munin::Node::SNMPConfig->new(
        community => 'secret',
        version   => 1,
  );
  $snmp->probe_hosts(\%plugins);

=head1 SUBROUTINES

=over

=item B<new(%arguments)>

Constructor.  Valid arguments are:

=over 4

=item hosts

The list of hosts to scan, in a format understood by
L<Munin::Node::Configure::HostEnumeration>.  Required.

=item port

Port to connect to.  Default is 161.

=item version

The SNMP version to use.  Default is '2c'.

=item community

The community string to use for SNMP version 1 or 2c.  Default is 'public'.

=item username

The SNMPv3 username to use.

=item authpassword

SNMPv3 Authentication password.  Optional when encryption is also enabled, in
which case defaults to the privacy password (C<privpassword>).  The
password is sent encrypted (one way hash) over the network.

=item authprotocol

SNMPv3 Authentication protocol.  One of 'md5' or 'sha' (HMAC-MD5-96, RFC1321
and SHA-1/HMAC-SHA-96, NIST FIPS PIB 180, RFC2264).  The default is 'md5'.

=item privpassword

SNMPv3 Privacy password to enable encryption.  An empty ('') password is
considered as no password and will not enable encryption.

Privacy requires a v3privprotocol as well as a v3authprotocol and a
v3authpassword, but all of these are defaulted (to 'des', 'md5', and the
v3privpassword value, respectively) and may therefore be left unspecified.

=item privprotocol

If the v3privpassword is set this setting controls what kind of encryption is
used to achieve privacy in the session.  Only the very weak 'des' encryption
method is supported officially.  The default is 'des'.

The implementing perl module (L<Net::SNMP>) also supports '3des' (CBC-3DES-EDE
aka Triple-DES, NIST FIPS 46-3) as specified in IETF
draft-reeder-snmpv3-usm-3desede.  Whether or not this works with any particular
device, we do not know.

=back

=item B<run_probes($plugins)>

Connects to each host in turn, and checks which plugins it supports, based on
the OIDs they reported during snmpconf.  If all the requirements are
fulfilled, it will added to the corresponding plugin's suggestions list.

$plugins should be a Munin::Node::Configure::PluginList object.

=back

=cut
# vim: ts=4 : sw=4 : expandtab