1package Classes::IPFORWARDMIB::Component::RoutingSubsystem;
2our @ISA = qw(Monitoring::GLPlugin::SNMP::Item);
3use strict;
4# plugins-scripts/check_nwc_health  --mode list-routes --snmpwalk walks/simon.snmpwalk
5# ipRouteTable		1.3.6.1.2.1.4.21
6# replaced by
7# ipForwardTable	1.3.6.1.2.1.4.24.2
8# deprecated by
9# ipCidrRouteTable	1.3.6.1.2.1.4.24.4
10# deprecated by the ip4/6-neutral
11# inetCidrRouteTable	1.3.6.1.2.1.4.24.7
12
13sub init {
14  my ($self) = @_;
15  $self->implements_mib('INET-ADDRESS-MIB');
16  $self->get_snmp_tables('IP-FORWARD-MIB', [
17      ['routes', 'ipCidrRouteTable', 'Classes::IPFORWARDMIB::Component::RoutingSubsystem::ipCidrRoute',
18          sub {
19            my ($o) = @_;
20            if ($o->opts->name && $o->opts->name =~ /\//) {
21              my ($dest, $cidr) = split(/\//, $o->opts->name);
22              my $bits = ( 2 ** (32 - $cidr) ) - 1;
23              my ($full_mask) = unpack("N", pack("C4", split(/\./, '255.255.255.255')));
24              my $netmask = join('.', unpack("C4", pack("N", ($full_mask ^ $bits))));
25              return defined $o->{ipCidrRouteDest} && (
26                  $o->filter_namex($dest, $o->{ipCidrRouteDest}) &&
27                  $o->filter_namex($netmask, $o->{ipCidrRouteMask}) &&
28                  $o->filter_name2($o->{ipCidrRouteNextHop})
29              );
30            } else {
31              return defined $o->{ipCidrRouteDest} && (
32                  $o->filter_name($o->{ipCidrRouteDest}) &&
33                  $o->filter_name2($o->{ipCidrRouteNextHop})
34              );
35            }
36          }
37      ],
38      ['routes', 'inetCidrRouteTable', 'Classes::IPFORWARDMIB::Component::RoutingSubsystem::inetCidrRoute',
39          sub {
40            my ($o) = @_;
41            if ($o->opts->name && $o->opts->name =~ /\//) {
42              my ($dest, $cidr) = split(/\//, $o->opts->name);
43              return defined $o->{inetCidrRouteDest} && (
44                  $o->filter_namex($dest, $o->{inetCidrRouteDest}) &&
45                  $o->filter_namex($cidr, $o->{inetCidrRoutePfxLen}) &&
46                  $o->filter_name2($o->{inetCidrRouteNextHop})
47              );
48            } else {
49              return defined $o->{inetCidrRouteDest} && (
50                  $o->filter_name($o->{inetCidrRouteDest}) &&
51                  $o->filter_name2($o->{inetCidrRouteNextHop})
52              );
53            }
54          }
55      ],
56  ]);
57  # deprecated
58  #$self->get_snmp_tables('IP-FORWARD-MIB', [
59  #    ['routes', 'ipForwardTable', 'Classes::IPFORWARDMIB::Component::RoutingSubsystem::Route' ],
60  #]);
61  #$self->get_snmp_tables('IP-MIB', [
62  #    ['routes', 'ipRouteTable', 'Classes::IPFORWARDMIB::Component::RoutingSubsystem::Route' ],
63  #]);
64  #
65  # Hundsglump varreckts!!!!
66  # Es gibt so Kandidaten, bei denen stecken die v6-Routen in der neuen
67  # inetCidrRouteTable (was ja korrekt ist) und die v4-Routen in der
68  # ipCidrRouteTable. Das war der Grund, weshalb beim get_snmp_tables
69  # beide abgefragt werden und nicht wie frueher ein Fallback auf von inet
70  # auf ip stattfindet, falls die inet leer ist.
71  # Korrekt waere zumindest meiner Ansicht nach, wenn sowohl v4 als auch v6
72  # in inetCidrRouteTable stuenden. Solche gibt es tatsaechlich auch.
73  # Aber dank der Hornochsen bei Cisco mit ihrer o.g. Vorgehensweise darf ich
74  # jetzt die Doubletten rausfieseln.
75  my $found = {};
76  @{$self->{routes}} = grep {
77      if (exists $found->{$_->id()}) {
78        0;
79      } else {
80        $found->{$_->id()} = 1;
81	1;
82      }
83  } @{$self->{routes}};
84}
85
86sub check {
87  my ($self) = @_;
88  $self->add_info('checking routes');
89  if ($self->mode =~ /device::routes::list/) {
90    foreach (@{$self->{routes}}) {
91      $_->list();
92    }
93    $self->add_ok("have fun");
94  } elsif ($self->mode =~ /device::routes::count/) {
95    if (! $self->opts->name && $self->opts->name2) {
96      $self->add_info(sprintf "found %d routes via next hop %s",
97          scalar(@{$self->{routes}}), $self->opts->name2);
98    } elsif ($self->opts->name && ! $self->opts->name2) {
99      $self->add_info(sprintf "found %d routes to dest %s",
100          scalar(@{$self->{routes}}), $self->opts->name);
101    } elsif ($self->opts->name && $self->opts->name2) {
102      $self->add_info(sprintf "found %d routes to dest %s via hop %s",
103          scalar(@{$self->{routes}}), $self->opts->name, $self->opts->name2);
104    } else {
105      $self->add_info(sprintf "found %d routes",
106          scalar(@{$self->{routes}}));
107    }
108    $self->set_thresholds(warning => '1:', critical => '1:');
109    $self->add_message($self->check_thresholds(scalar(@{$self->{routes}})));
110    $self->add_perfdata(
111        label => 'routes',
112        value => scalar(@{$self->{routes}}),
113    );
114  } elsif ($self->mode =~ /device::routes::exists/) {
115    # geht auch mit count-routes. irgendwann mal....
116    $self->no_such_mode();
117  }
118}
119
120
121package Classes::IPFORWARDMIB::Component::RoutingSubsystem::Route;
122our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);
123
124package Classes::IPFORWARDMIB::Component::RoutingSubsystem::ipRoute;
125our @ISA = qw(Classes::IPFORWARDMIB::Component::RoutingSubsystem::Route);
126
127package Classes::IPFORWARDMIB::Component::RoutingSubsystem::ipCidrRoute;
128our @ISA = qw(Classes::IPFORWARDMIB::Component::RoutingSubsystem::Route);
129
130sub finish {
131  my ($self) = @_;
132  if (! defined $self->{ipCidrRouteDest}) {
133    # we can reconstruct a few attributes from the index
134    # one customer only made ipCidrRouteStatus visible
135    $self->{ipCidrRouteDest} = join(".", map { $self->{indices}->[$_] } (0, 1, 2, 3));
136    $self->{ipCidrRouteMask} = join(".", map { $self->{indices}->[$_] } (4, 5, 6, 7));
137    $self->{ipCidrRouteTos} = $self->{indices}->[8];
138    $self->{ipCidrRouteNextHop} = join(".", map { $self->{indices}->[$_] } (9, 10, 11, 12));
139    $self->{ipCidrRouteType} = "other"; # maybe not, who cares
140    $self->{ipCidrRouteProto} = "other"; # maybe not, who cares
141  }
142}
143
144sub list {
145  my ($self) = @_;
146  printf "%16s %16s %16s %11s %7s\n",
147      $self->{ipCidrRouteDest}, $self->{ipCidrRouteMask},
148      $self->{ipCidrRouteNextHop}, $self->{ipCidrRouteProto},
149      $self->{ipCidrRouteType};
150}
151
152sub id {
153  my ($self) = @_;
154  return sprintf "%s-%s", $self->{ipCidrRouteDest},
155      $self->{ipCidrRouteNextHop};
156}
157
158package Classes::IPFORWARDMIB::Component::RoutingSubsystem::inetCidrRoute;
159our @ISA = qw(Classes::IPFORWARDMIB::Component::RoutingSubsystem::Route);
160
161sub finish {
162  my ($self) = @_;
163  # http://www.mibdepot.com/cgi-bin/vendor_index.cgi?r=ietf_rfcs
164  # INDEX { inetCidrRouteDestType, inetCidrRouteDest, inetCidrRoutePfxLen, inetCidrRoutePolicy, inetCidrRouteNextHopType, inetCidrRouteNextHop }
165  my @tmp_indices = @{$self->{indices}};
166  my $last_tmp = scalar(@tmp_indices) - 1;
167  # .1.3.6.1.2.1.4.24.7.1.7.1.4.0.0.0.0.32.2.0.0.1.4.10.208.143.81 = INTEGER: 25337
168  # IP-FORWARD-MIB::inetCidrRouteIfIndex.ipv4."0.0.0.0".32.2.0.0.ipv4."10.208.143.81" = INTEGER: 25337
169  # Frag mich jetzt keiner, warum dem ipv4 ein 1.4 entspricht. Ich kann
170  # jedenfalls der IP-FORWARD-MIB bzw. RFC4001 nicht entnehmen, dass fuer
171  # InetAddressType zwei Stellen des Index vorgesehen sind. Zumal nur die
172  # erste Stelle für die Textual Convention relevant ist. Aergert mich ziemlich,
173  # daß jeder bloede /usr/bin/snmpwalk das besser hinbekommt als ich.
174  # Was dazugelernt: 1=InetAddressType, 4=gehoert zur folgenden InetAddressIPv4
175  # und gibt die Laenge an. Noch mehr gelernt: wenn eine Table mit Integer und
176  # Octet String indiziert ist, dann ist die Groeße des Octet String Bestandteil
177  # der OID. Diese _kann_ weggelassen werden für den _letzten_ Index. Der ist
178  # halt dann so lang wie der Rest der OID.
179  # Mit einem IMPLIED-Keyword koennte die Laenge auch weggelassen werden.
180
181  $self->{inetCidrRouteDestType} = $self->mibs_and_oids_definition(
182      'INET-ADDRESS-MIB', 'InetAddressType', $tmp_indices[0]);
183  shift @tmp_indices;
184
185  $self->{inetCidrRouteDest} = $self->mibs_and_oids_definition(
186      'INET-ADDRESS-MIB', 'InetAddressMaker',
187      $self->{inetCidrRouteDestType}, @tmp_indices);
188
189  # laenge plus adresse weg
190  splice @tmp_indices, 0, $tmp_indices[0]+1;
191
192  $self->{inetCidrRoutePfxLen} = shift @tmp_indices;
193  $self->{inetCidrRoutePolicy} = join(".", splice @tmp_indices, 0, $tmp_indices[0]+1);
194
195  $self->{inetCidrRouteNextHopType} = $self->mibs_and_oids_definition(
196      'INET-ADDRESS-MIB', 'InetAddressType', $tmp_indices[0]);
197  shift @tmp_indices;
198
199  $self->{inetCidrRouteNextHop} = $self->mibs_and_oids_definition(
200      'INET-ADDRESS-MIB', 'InetAddressMaker',
201      $self->{inetCidrRouteNextHopType}, @tmp_indices);
202
203  if ($self->{inetCidrRouteDestType} eq "ipv4") {
204  my $bits = ( 2 ** (32 - $self->{inetCidrRoutePfxLen}) ) - 1;
205  my ($full_mask) = unpack("N", pack("C4", split(/\./, '255.255.255.255')));
206  my $netmask = join('.', unpack("C4", pack("N", ($full_mask ^ $bits))));
207  $self->{inetCidrRouteMask} = $netmask;
208  }
209
210}
211
212sub list {
213  my ($self) = @_;
214  printf "%16s %16s %16s %11s %7s\n",
215      $self->{inetCidrRouteDest}, $self->{inetCidrRoutePfxLen},
216      $self->{inetCidrRouteNextHop}, $self->{inetCidrRouteProto},
217      $self->{inetCidrRouteType};
218}
219
220sub id {
221  my ($self) = @_;
222  return sprintf "%s-%s", $self->{inetCidrRouteDest},
223      $self->{inetCidrRouteNextHop};
224}
225