1package Classes::IFMIB::Component::InterfaceSubsystem;
2our @ISA = qw(Monitoring::GLPlugin::SNMP::Item);
3use strict;
4use JSON;
5use File::Slurp qw(read_file);
6
7sub init {
8  my ($self) = @_;
9  $self->{interfaces} = [];
10  $self->{etherstats} = [];
11  #$self->session_translate(['-octetstring' => 1]);
12  my @iftable_columns = qw(ifDescr ifAlias ifName);
13  my @ethertable_columns = qw();
14  my @ethertablehc_columns = qw();
15  my @rmontable_columns = qw();
16  my @ipaddress_columns = qw();
17
18  my @iftable_traffic_columns = qw(ifInOctets ifOutOctets ifSpeed);
19  my @iftable_traffic_hc_columns = qw(ifHCInOctets ifHCOutOctets ifHighSpeed);
20  my @iftable_status_columns = qw(ifOperStatus ifAdminStatus);
21  my @iftable_packets_columns = qw(ifInUcastPkts ifOutUcastPkts
22      ifInMulticastPkts ifOutMulticastPkts
23      ifInBroadcastPkts ifOutBroadcastPkts);
24  my @iftable_packets_hc_columns = qw(ifHCInUcastPkts ifHCOutUcastPkts
25      ifHCInMulticastPkts ifHCOutMulticastPkts
26      ifHCInBroadcastPkts ifHCOutBroadcastPkts);
27  my @iftable_error_columns = qw(ifInErrors ifOutErrors);
28  my @iftable_discard_columns = qw(ifInDiscards ifOutDiscards);
29
30  $self->implements_mib('INET-ADDRESS-MIB');
31  if ($self->mode =~ /device::interfaces::list/) {
32  } elsif ($self->mode =~ /device::interfaces::complete/) {
33    push(@iftable_columns, @iftable_status_columns);
34    push(@iftable_columns, @iftable_traffic_columns);
35    push(@iftable_columns, @iftable_packets_columns);
36    push(@iftable_columns, @iftable_traffic_hc_columns);
37    push(@iftable_columns, @iftable_packets_hc_columns);
38    push(@iftable_columns, @iftable_error_columns);
39    push(@iftable_columns, @iftable_discard_columns);
40    # kostenpflichtiges feature # push(@ethertable_columns, qw(
41    #    dot3StatsDuplexStatus
42    #));
43  } elsif ($self->mode =~ /device::interfaces::usage/) {
44    push(@iftable_columns, @iftable_status_columns);
45    push(@iftable_columns, @iftable_traffic_columns);
46    push(@iftable_columns, @iftable_traffic_hc_columns);
47  } elsif ($self->mode =~ /device::interfaces::errors/) {
48    push(@iftable_columns, @iftable_status_columns);
49    push(@iftable_columns, @iftable_traffic_columns);
50    push(@iftable_columns, @iftable_packets_columns);
51    push(@iftable_columns, @iftable_traffic_hc_columns);
52    push(@iftable_columns, @iftable_packets_hc_columns);
53    push(@iftable_columns, @iftable_error_columns);
54  } elsif ($self->mode =~ /device::interfaces::discards/) {
55    push(@iftable_columns, @iftable_status_columns);
56    push(@iftable_columns, @iftable_traffic_columns);
57    push(@iftable_columns, @iftable_packets_columns);
58    push(@iftable_columns, @iftable_traffic_hc_columns);
59    push(@iftable_columns, @iftable_packets_hc_columns);
60    push(@iftable_columns, @iftable_discard_columns);
61  } elsif ($self->mode =~ /device::interfaces::broadcast/) {
62    push(@iftable_columns, @iftable_status_columns);
63    push(@iftable_columns, @iftable_traffic_columns);
64    push(@iftable_columns, @iftable_packets_columns);
65    push(@iftable_columns, @iftable_traffic_hc_columns);
66    push(@iftable_columns, @iftable_packets_hc_columns);
67  } elsif ($self->mode =~ /device::interfaces::operstatus/) {
68    push(@iftable_columns, @iftable_status_columns);
69  } elsif ($self->mode =~ /device::interfaces::availability/) {
70    push(@iftable_columns, qw(
71        ifType ifOperStatus ifAdminStatus
72        ifLastChange ifHighSpeed ifSpeed
73    ));
74  } elsif ($self->mode =~ /device::interfaces::etherstats/) {
75    push(@iftable_columns, @iftable_status_columns);
76    push(@iftable_columns, @iftable_packets_columns);
77    push(@iftable_columns, @iftable_packets_hc_columns);
78    push(@ethertable_columns, qw(
79        dot3StatsAlignmentErrors dot3StatsFCSErrors
80        dot3StatsSingleCollisionFrames dot3StatsMultipleCollisionFrames
81        dot3StatsSQETestErrors dot3StatsDeferredTransmissions
82        dot3StatsLateCollisions dot3StatsExcessiveCollisions
83        dot3StatsInternalMacTransmitErrors dot3StatsCarrierSenseErrors
84        dot3StatsFrameTooLongs dot3StatsInternalMacReceiveErrors
85    ));
86    push(@ethertablehc_columns, qw(
87        dot3HCStatsFCSErrors
88    ));
89    push(@rmontable_columns, qw(
90        etherStatsCRCAlignErrors
91    ));
92    if ($self->opts->report !~ /^(long|short|html)$/) {
93      my @reports = split(',', $self->opts->report);
94      @ethertable_columns = grep {
95        my $ec = $_;
96        grep {
97          $ec eq $_;
98        } @reports;
99      } @ethertable_columns;
100      @ethertablehc_columns = grep {
101        my $ec = $_;
102        grep {
103          $ec eq $_;
104        } @reports;
105      } @ethertablehc_columns;
106      @rmontable_columns = grep {
107        my $ec = $_;
108        grep {
109          $ec eq $_;
110        } @reports;
111      } @rmontable_columns;
112    }
113    if (grep /dot3HCStatsFCSErrors/, @ethertablehc_columns) {
114      # wenn ifSpeed == 4294967295, dann 10GBit, dann dot3HCStatsFCSErrors
115      push(@iftable_columns, qw(
116          ifSpeed
117      ));
118    }
119    if (@rmontable_columns) {
120      push(@rmontable_columns, qw(
121          etherStatsIndex
122          etherStatsDataSource
123      ));
124    }
125  } elsif ($self->mode =~ /device::interfaces::duplex/) {
126    push(@iftable_columns, qw(
127        ifType ifSpeed ifOperStatus ifAdminStatus ifHighSpeed
128    ));
129    push(@ethertable_columns, qw(
130        dot3StatsDuplexStatus
131    ));
132  } elsif ($self->mode =~ /device::interfaces::uptime/) {
133    push(@iftable_columns, qw(
134        ifLastChange
135    ));
136  } else {
137    @iftable_columns = ();
138  }
139  if ($self->mode =~ /device::interfaces::list/) {
140    $self->update_interface_cache(1);
141    my @indices = $self->get_interface_indices();
142    foreach my $ifIndex (map { $_->[0] } @indices) {
143      my $ifDescr = $self->{interface_cache}->{$ifIndex}->{ifDescr};
144      my $ifName = $self->{interface_cache}->{$ifIndex}->{ifName} || '________';
145      my $ifAlias = $self->{interface_cache}->{$ifIndex}->{ifAlias} || '________';
146      my $interface_class = ref($self)."::Interface";
147      my $interface = $interface_class->new(
148          ifIndex => $ifIndex,
149          ifDescr => $ifDescr,
150          ifName => $ifName,
151          ifAlias => $ifAlias,
152          indices => [$ifIndex],
153          flat_indices => $ifIndex,
154      );
155      $self->enrich_interface_attributes($interface);
156      push(@{$self->{interfaces}}, $interface);
157    }
158    # die sind mit etherStatsDataSource verknuepft
159  } elsif ($self->mode =~ /device::interfaces/) {
160    my $if_has_changed = $self->update_interface_cache(0);
161    my $only_admin_up =
162        $self->opts->name && $self->opts->name eq '_adminup_' ? 1 : 0;
163    my $only_oper_up =
164        $self->opts->name && $self->opts->name eq '_operup_' ? 1 : 0;
165    # --name '(lan|wan|_adminup_)'
166    # alle mit match auf lan|wan, und davon dann die mit admin up
167    my $plus_admin_up =
168        $self->opts->name && ! $only_admin_up &&
169        $self->opts->name =~ /_adminup_/ ? 1 : 0;
170    my $plus_oper_up =
171        $self->opts->name && ! $only_oper_up &&
172        $self->opts->name =~ /_operup_/ ? 1 : 0;
173    if ($only_admin_up || $only_oper_up) {
174      $self->override_opt('name', undef);
175      $self->override_opt('drecksptkdb', undef);
176    }
177    my @indices = $self->get_interface_indices();
178    my @all_indices = @indices;
179    my @selected_indices = ();
180    if (! $self->opts->name && ! $self->opts->name3) {
181      # get_table erzwingen
182      @indices = ();
183      $self->bulk_is_baeh(10);
184    }
185    @iftable_columns = do { my %seen; grep { !$seen{$_}++ } @iftable_columns }; # uniq
186    if ((! $self->opts->name && ! $self->opts->name3) || scalar(@indices) > 0) {
187      my @save_indices = @indices; # die werden in get_snmp_table_objects geshiftet
188      if ($plus_admin_up || $plus_oper_up) {
189        # mit minimalen columns schnell vorfiltern -> @indices evt. reduzieren
190        # nicht fuer only_admin/oper_up, sonst wird aus @indices = ()
191        # eine riesige liste, deren abarbeitung laenger dauert als
192        # ein get_table bei @indices = ()
193        my @up_indices = ();
194        foreach ($self->get_snmp_table_objects(
195            'IFMIB', 'ifTable+ifXTable', \@indices, \@iftable_status_columns)) {
196          next if $plus_admin_up && $_->{ifAdminStatus} ne 'up';
197          next if $plus_oper_up && $_->{ifOperStatus} ne 'up';
198          push(@up_indices, [$_->{indices}->[0]]);
199        }
200        @indices = @up_indices;
201      }
202      foreach ($self->get_snmp_table_objects(
203          'IFMIB', 'ifTable+ifXTable', \@indices, \@iftable_columns)) {
204        next if $only_admin_up && $_->{ifAdminStatus} ne 'up';
205        next if $only_oper_up && $_->{ifOperStatus} ne 'up';
206        $self->make_ifdescr_unique($_);
207        $self->enrich_interface_attributes($_);
208        my $interface_class = ref($self)."::Interface";
209        my $interface = $interface_class->new(%{$_});
210        $interface->{columns} = [@iftable_columns];
211        push(@{$self->{interfaces}}, $interface);
212      }
213      # kostenpflichtiges feature # if ($self->mode =~ /device::interfaces::(duplex|etherstats|complete)/) {
214      if ($self->mode =~ /device::interfaces::(duplex|etherstats)/) {
215        @indices = @save_indices;
216        my @etherindices = ();
217        my @etherhcindices = ();
218        foreach my $interface (@{$self->{interfaces}}) {
219          push(@selected_indices, [$interface->{ifIndex}]);
220          if (@ethertablehc_columns && $interface->{ifSpeed} == 4294967295) {
221            push(@etherhcindices, [$interface->{ifIndex}]);
222          }
223          push(@etherindices, [$interface->{ifIndex}]);
224        }
225        $self->debug(
226            sprintf 'all_interfaces %d, selected %d, ether %d, etherhc %d',
227                scalar(@all_indices), scalar(@selected_indices),
228                scalar(@etherindices), scalar(@etherhcindices));
229        my @rmonpatterns = map {
230            '([\.]*1.3.6.1.2.1.2.2.1.1.'.$_.')';
231        } map {
232            $_->[0];
233        } @selected_indices;
234        if ($only_admin_up || $only_oper_up) {
235          if (scalar(@etherindices) > scalar(@all_indices) * 0.70) {
236            $self->bulk_is_baeh(20);
237            @etherindices = ();
238          }
239          if (scalar(@etherhcindices) > scalar(@all_indices) * 0.70) {
240            $self->bulk_is_baeh(20);
241            @etherhcindices = ();
242          }
243          if (scalar(@rmonpatterns) > scalar(@all_indices) * 0.70) {
244            $self->bulk_is_baeh(20);
245            @rmonpatterns = ();
246          }
247        } elsif (! @indices) {
248            $self->bulk_is_baeh(20);
249          @etherindices = ();
250          if (scalar(@etherhcindices) > scalar(@all_indices) * 0.70) {
251            @etherhcindices = ();
252          }
253          @rmonpatterns = ();
254        }
255        if (@ethertable_columns) {
256          # es gibt interfaces mit ifSpeed == 4294967295
257          # aber nix in dot3HCStatsTable. also dann dot3StatsTable fuer alle
258          if ($self->implements_mib('EtherLike-MIB', 'dot3StatsTable')) {
259            foreach my $etherstat ($self->get_snmp_table_objects(
260                'EtherLike-MIB', 'dot3StatsTable', \@etherindices, \@ethertable_columns)) {
261              foreach my $interface (@{$self->{interfaces}}) {
262                if ($interface->{ifIndex} == $etherstat->{flat_indices}) {
263                  foreach my $key (grep /^dot3/, keys %{$etherstat}) {
264                    $interface->{$key} = $etherstat->{$key};
265                    push(@{$interface->{columns}}, $key);
266                  }
267                  last;
268                }
269              }
270            }
271          }
272        }
273        if (@ethertablehc_columns && scalar(@etherhcindices)) {
274          if ($self->implements_mib('EtherLike-MIB', 'dot3HCStatsTable')) {
275            foreach my $etherstat ($self->get_snmp_table_objects(
276                'EtherLike-MIB', 'dot3HCStatsTable', \@etherhcindices, \@ethertablehc_columns)) {
277              foreach my $interface (@{$self->{interfaces}}) {
278                if ($interface->{ifIndex} == $etherstat->{flat_indices}) {
279                  foreach my $key (grep /^dot3/, keys %{$etherstat}) {
280                    $interface->{$key} = $etherstat->{$key};
281                    push(@{$interface->{columns}}, $key);
282                  }
283                  if (grep /^dot3HCStatsFCSErrors/, @{$interface->{columns}}) {
284                    @{$interface->{columns}} = grep {
285                      $_ if $_ ne 'dot3StatsFCSErrors';
286                    } @{$interface->{columns}};
287                  }
288                  last;
289                }
290              }
291            }
292          }
293        }
294        if (@rmontable_columns) {
295          if ($self->opts->name) {
296            $self->override_opt('drecksptkdb', '^('.join('|', @rmonpatterns).')$');
297            $self->override_opt('name', '^('.join('|', @rmonpatterns).')$');
298            $self->override_opt('regexp', 1);
299          }
300          # Value von etherStatsDataSource entspricht ifIndex 1.3.6.1.2.1.2.2.1.1.idx
301          if ($self->implements_mib('RMON-MIB', 'etherStatsTable')) {
302            foreach my $etherstat ($self->get_snmp_table_objects_with_cache(
303                'RMON-MIB', 'etherStatsTable', 'etherStatsDataSource', \@rmontable_columns, $if_has_changed ? 1 : -1)) {
304                $etherstat->{etherStatsDataSource} =~ s/^\.//g;
305              foreach my $interface (@{$self->{interfaces}}) {
306                if ('1.3.6.1.2.1.2.2.1.1.'.$interface->{ifIndex} eq $etherstat->{etherStatsDataSource}) {
307                  foreach my $key (grep /^etherStats/, keys %{$etherstat}) {
308                    $interface->{$key} = $etherstat->{$key};
309                    push(@{$interface->{columns}}, $key);
310                  }
311                  last;
312                }
313              }
314            }
315          }
316        }
317        # @{$self->{interfaces}} haben ein ->{columns}
318        # alle ausfiltern, die _keine_ der gewuenschten oids haben
319        @{$self->{interfaces}} = grep {
320            # check (@ethertable_columns, @rmontable_columns)
321            my $found = undef;
322            foreach my $oid (@ethertable_columns, @rmontable_columns) {
323              if (grep { $oid eq $_ } @{$_->{columns}}) {
324                $found = 1;
325              }
326            }
327            $found;
328        } @{$self->{interfaces}};
329        foreach my $interface (@{$self->{interfaces}}) {
330          delete $interface->{dot3StatsIndex};
331          delete $interface->{etherStatsIndex};
332          delete $interface->{etherStatsDataSource};
333          @{$interface->{columns}} = grep {
334              $_ !~ /^(dot3StatsIndex|etherStatsIndex|etherStatsDataSource)$/;
335          } @{$interface->{columns}};
336          $interface->init_etherstats;
337        }
338        if (scalar(@{$self->{interfaces}}) == 0) {
339          $self->add_unknown('device probably has no RMON-MIB or EtherLike-MIB');
340        }
341      }
342    }
343  }
344  if ($self->opts->report =~ /^(\w+)\+address/) {
345    $self->override_opt('report', $1);
346    # flat_indices, weil die Schluesselelemente ipAddressAddrType+ipAddressAddr
347    # not-accessible sind und im Index stecken.
348    if (scalar(@{$self->{interfaces}}) > 0) {
349      my $interfaces_by_index = {};
350      map {
351          $interfaces_by_index->{$_->{ifIndex}} = $_;
352      } @{$self->{interfaces}};
353      my $indexpattern = join('|', map {
354          $_->{ifIndex}
355      } @{$self->{interfaces}});
356      $self->override_opt('name', '^('.$indexpattern.')$');
357      $self->override_opt('drecksptkdb', '^('.$indexpattern.')$');
358      $self->override_opt('regexp', 1);
359
360      $self->get_snmp_objects('IP-MIB', qw(ipv4InterfaceTableLastChange ipv6InterfaceTableLastChange));
361      $self->{ipv4InterfaceTableLastChange} ||= 0;
362      $self->{ipv6InterfaceTableLastChange} ||= 0;
363      $self->{ipv46InterfaceTableLastChange} =
364          $self->{ipv4InterfaceTableLastChange} > $self->{ipv6InterfaceTableLastChange} ?
365          $self->{ipv4InterfaceTableLastChange} : $self->{ipv6InterfaceTableLastChange};
366      $self->{bootTime} = time - $self->uptime();
367      $self->{ipAddressTableLastChange} = $self->{bootTime} + $self->timeticks($self->{ipv46InterfaceTableLastChange} / 100);
368
369      $self->update_entry_cache(0, 'IP-MIB', 'ipAddressTable', 'ipAddressIfIndex', $self->{ipAddressTableLastChange});
370      my @address_indices = $self->get_cache_indices('IP-MIB', 'ipAddressTable', 'ipAddressIfIndex');
371      $self->{addresses} = [];
372      if (@address_indices) {
373        # es gibt adressen zu den ausgewaehlten interfaces
374        foreach ($self->get_snmp_table_objects_with_cache(
375            'IP-MIB', 'ipAddressTable', 'ipAddressIfIndex', ['ipAddressIfIndex'], 0)) {
376          my $address = Classes::IFMIB::Component::InterfaceSubsystem::Address->new(%{$_});
377          push(@{$self->{addresses}}, $address);
378          if (exists $interfaces_by_index->{$address->{ipAddressIfIndex}}) {
379            if (exists  $interfaces_by_index->{$address->{ipAddressIfIndex}}->{ifAddresses}) {
380              push(@{$interfaces_by_index->{$address->{ipAddressIfIndex}}->{ifAddresses}}, $address->{ipAddressAddr});
381            } else {
382              $interfaces_by_index->{$address->{ipAddressIfIndex}}->{ifAddresses} = [$address->{ipAddressAddr}];
383            }
384          }
385        }
386      }
387      foreach (@{$self->{interfaces}}) {
388        $_->{ifAddresses} = exists $_->{ifAddresses} ? join(", ", @{$_->{ifAddresses}}) : "";
389      }
390    }
391  }
392}
393
394sub check {
395  my ($self) = @_;
396  $self->add_info('checking interfaces');
397  if (scalar(@{$self->{interfaces}}) == 0) {
398    $self->add_unknown('no interfaces');
399    return;
400  }
401  if ($self->mode =~ /device::interfaces::list/) {
402    foreach (sort {$a->{ifIndex} <=> $b->{ifIndex}} @{$self->{interfaces}}) {
403    #foreach (sort @{$self->{interfaces}}) {
404      $_->list();
405    }
406    $self->add_ok("have fun");
407  } elsif ($self->mode =~ /device::interfaces::availability/) {
408    foreach (@{$self->{interfaces}}) {
409      $_->check();
410    }
411    my $num_interfaces = scalar(@{$self->{interfaces}});
412    my $up_interfaces =
413        scalar(grep { $_->{ifAdminStatus} eq "up" } @{$self->{interfaces}});
414    my $available_interfaces =
415        scalar(grep { $_->{ifAvailable} eq "true" } @{$self->{interfaces}});
416    $self->add_info(sprintf "%d of %d (%d adm. up) interfaces are available",
417        $available_interfaces, $num_interfaces, $up_interfaces);
418    $self->set_thresholds(warning => "3:", critical => "2:");
419    $self->add_message($self->check_thresholds($available_interfaces));
420    $self->add_perfdata(
421        label => 'num_interfaces',
422        value => $num_interfaces,
423        thresholds => 0,
424    );
425    $self->add_perfdata(
426        label => 'available_interfaces',
427        value => $available_interfaces,
428    );
429
430    printf "%s\n", $self->{info};
431    printf "<table style=\"border-collapse:collapse; border: 1px solid black;\">";
432    printf "<tr>";
433    foreach (qw(Index Descr Type Speed AdminStatus OperStatus Duration Available)) {
434      printf "<th style=\"text-align: right; padding-left: 4px; padding-right: 6px;\">%s</th>", $_;
435    }
436    printf "</tr>";
437    foreach (sort {$a->{ifIndex} <=> $b->{ifIndex}} @{$self->{interfaces}}) {
438      printf "<tr>";
439      printf "<tr style=\"border: 1px solid black;\">";
440      foreach my $attr (qw(ifIndex ifDescr ifType ifSpeedText ifAdminStatus ifOperStatus ifStatusDuration ifAvailable)) {
441        if ($_->{ifAvailable} eq "false") {
442          printf "<td style=\"text-align: right; padding-left: 4px; padding-right: 6px;\">%s</td>", $_->{$attr};
443        } else {
444          printf "<td style=\"text-align: right; padding-left: 4px; padding-right: 6px; background-color: #00ff33;\">%s</td>", $_->{$attr};
445        }
446      }
447      printf "</tr>";
448    }
449    printf "</table>\n";
450    printf "<!--\nASCII_NOTIFICATION_START\n";
451    my $column_length = {};
452    foreach (qw(ifIndex ifDescr ifType ifSpeed ifAdminStatus ifOperStatus Duration ifAvailable ifSpeedText ifStatusDuration)) {
453      $column_length->{$_} = length($_);
454    }
455    foreach (sort {$a->{ifIndex} <=> $b->{ifIndex}} @{$self->{interfaces}}) {
456      foreach my $attr (qw(ifIndex ifDescr ifType ifSpeedText ifAdminStatus ifOperStatus ifStatusDuration ifAvailable)) {
457        if (length($_->{$attr}) > $column_length->{$attr}) {
458          $column_length->{$attr} = length($_->{$attr});
459        }
460      }
461    }
462    foreach (qw(ifIndex ifDescr ifType ifSpeed ifAdminStatus ifOperStatus Duration ifStatusDuration ifAvailable ifSpeedText)) {
463      $column_length->{$_} = "%".($column_length->{$_} + 3)."s I";
464    }
465    $column_length->{ifSpeed} = $column_length->{ifSpeedText};
466    $column_length->{Duration} = $column_length->{ifStatusDuration};
467    foreach (qw(ifIndex ifDescr ifType ifSpeed ifAdminStatus ifOperStatus Duration ifAvailable)) {
468      printf $column_length->{$_}, $_;
469    }
470    printf "\n";
471    foreach (sort {$a->{ifIndex} <=> $b->{ifIndex}} @{$self->{interfaces}}) {
472      foreach my $attr (qw(ifIndex ifDescr ifType ifSpeedText ifAdminStatus ifOperStatus ifStatusDuration ifAvailable)) {
473        printf $column_length->{$attr}, $_->{$attr};
474      }
475      printf "\n";
476    }
477    printf "ASCII_NOTIFICATION_END\n-->\n";
478  } else {
479    if (scalar (@{$self->{interfaces}}) == 0) {
480    } else {
481      foreach (sort {$a->{ifIndex} <=> $b->{ifIndex}} @{$self->{interfaces}}) {
482        $_->check();
483      }
484      if ($self->opts->report =~ /^short/) {
485        $self->clear_ok();
486        $self->add_ok('no problems') if ! $self->check_messages();
487      }
488    }
489  }
490}
491
492sub update_interface_cache {
493  my ($self, $force) = @_;
494  my $statefile = $self->create_interface_cache_file();
495  $self->bulk_is_baeh(10);
496  $self->get_snmp_objects('IFMIB', qw(ifTableLastChange));
497  # "The value of sysUpTime at the time of the last creation or
498  # deletion of an entry in the ifTable. If the number of
499  # entries has been unchanged since the last re-initialization
500  # of the local network management subsystem, then this object
501  # contains a zero value."
502  $self->{ifTableLastChange} ||= 0;
503  $self->{ifCacheLastChange} = -f $statefile ? (stat $statefile)[9] : 0;
504  $self->{bootTime} = time - $self->uptime();
505  $self->debug(sprintf 'boot time was %s', scalar localtime $self->{bootTime});
506  $self->debug(sprintf 'if last change is %s', scalar localtime $self->{ifTableLastChange});
507  $self->{ifTableLastChange} = $self->{bootTime} + $self->timeticks($self->{ifTableLastChange});
508  $self->debug(sprintf 'if last change is %s', scalar localtime $self->{ifTableLastChange});
509  my $update_deadline = time - 3600;
510  my $must_update = 0;
511  if ($self->{ifCacheLastChange} < $update_deadline) {
512    # file older than 1h or file does not exist
513    $must_update = 1;
514    $self->debug(sprintf 'interface cache is older than 1h (%s < %s)',
515        scalar localtime $self->{ifCacheLastChange}, scalar localtime $update_deadline);
516  }
517  if ($self->{ifTableLastChange} >= $self->{ifCacheLastChange}) {
518    $must_update = 1;
519    $self->debug(sprintf 'interface table changes newer than cache file (%s >= %s)',
520        scalar localtime $self->{ifTableLastChange}, scalar localtime $self->{ifCacheLastChange});
521  }
522  if ($force) {
523    $must_update = 1;
524    $self->debug(sprintf 'interface table update forced');
525  }
526  if ($must_update) {
527    $self->debug('update of interface cache');
528    $self->{interface_cache} = {};
529    foreach ($self->get_snmp_table_objects('MINI-IFMIB', 'ifTable+ifXTable', [-1], ['ifDescr', 'ifName', 'ifAlias'])) {
530      # auch hier explizit ifIndex vermeiden, sonst fliegen dem Rattabratha Singh die Nexus um die Ohren
531      # neuerdings index+descr, weil die drecksscheiss allied telesyn ports
532      # alle gleich heissen
533      # und noch so ein hirnbrand: --mode list-interfaces
534      # 000003 Adaptive Security Appliance 'GigabitEthernet0/0' interface
535      # ....
536      # der ASA-schlonz ist ueberfluessig, also brauchen wir eine hintertuer
537      # um die namen auszuputzen
538      if ($self->opts->name2 && $self->opts->name2 =~ /\(\.\*\?*\)/) {
539        if ($_->{ifDescr} =~ $self->opts->name2) {
540          $_->{ifDescr} = $1;
541        }
542      }
543      $self->{interface_cache}->{$_->{flat_indices}}->{ifDescr} = unpack("Z*", $_->{ifDescr});
544      $self->{interface_cache}->{$_->{flat_indices}}->{ifName} = unpack("Z*", $_->{ifName}) if exists $_->{ifName};
545      $self->{interface_cache}->{$_->{flat_indices}}->{ifAlias} = unpack("Z*", $_->{ifAlias}) if exists $_->{ifAlias};
546    }
547    $self->enrich_interface_cache();
548    $self->save_interface_cache();
549  }
550  $self->load_interface_cache();
551  $self->{duplicates} = {};
552  foreach my $index (keys %{$self->{interface_cache}}) {
553    my $ifDescr = $self->{interface_cache}->{$index}->{ifDescr};
554    if (! exists $self->{duplicates}->{$ifDescr}) {
555      $self->{duplicates}->{$ifDescr} = 1;
556    } else {
557      $self->{duplicates}->{$ifDescr}++;
558    }
559  }
560  foreach my $index (keys %{$self->{interface_cache}}) {
561    $self->{interface_cache}->{$index}->{flat_indices} = $index;
562    $self->make_ifdescr_unique($self->{interface_cache}->{$index});
563  }
564  return $must_update;
565}
566
567sub enrich_interface_cache {
568  my ($self) = @_;
569  # a dummy method. it can be used in Classes::XY::Component::InterfaceSubsystem
570  # to add for example vendor-specific port names to the interface cache
571  # which has been collected by get_snmp_tables(vendor-mib, tablexy, xyPortName
572}
573
574sub save_interface_cache {
575  my ($self) = @_;
576  $self->create_statefilesdir();
577  my $statefile = $self->create_interface_cache_file();
578  my $tmpfile = $self->statefilesdir().'/check_nwc_health_tmp_'.$$;
579  my $fh = IO::File->new();
580  if ($fh->open($tmpfile, "w")) {
581    my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
582    my $jsonscalar = $coder->encode($self->{interface_cache});
583    $fh->print($jsonscalar);
584    $fh->flush();
585    $fh->close();
586  }
587  rename $tmpfile, $statefile;
588  $self->debug(sprintf "saved %s to %s",
589      Data::Dumper::Dumper($self->{interface_cache}), $statefile);
590}
591
592sub load_interface_cache {
593  my ($self) = @_;
594  my $statefile = $self->create_interface_cache_file();
595  if ( -f $statefile) {
596    my $jsonscalar = read_file($statefile);
597    our $VAR1;
598    eval {
599      my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
600      $VAR1 = $coder->decode($jsonscalar);
601    };
602    if($@) {
603      $self->debug(sprintf "json load from %s failed. fallback", $statefile);
604      delete $INC{$statefile} if exists $INC{$statefile}; # else unit tests fail
605      eval "$jsonscalar";
606      if($@) {
607        printf "FATAL: Could not load interface cache in perl format!\n";
608        $self->debug(sprintf "fallback perl load from %s failed", $statefile);
609      }
610    }
611    $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
612    $self->{interface_cache} = $VAR1;
613  }
614}
615
616sub make_ifdescr_unique {
617  my ($self, $if) = @_;
618  $if->{ifDescr} = $if->{ifDescr}.' '.$if->{flat_indices} if $self->{duplicates}->{$if->{ifDescr}} > 1;
619}
620
621sub get_interface_indices {
622  my ($self) = @_;
623  my @indices = ();
624  foreach my $ifIndex (keys %{$self->{interface_cache}}) {
625    my $ifDescr = $self->{interface_cache}->{$ifIndex}->{ifDescr};
626    my $ifUniqDescr = $self->{interface_cache}->{$ifIndex}->{ifUniqDescr};
627    my $ifAlias = $self->{interface_cache}->{$ifIndex}->{ifAlias} || '________';
628    # Check ifDescr (using --name)
629    if ($self->opts->name) {
630      if ($self->opts->regexp) {
631        my $pattern = $self->opts->name;
632        if ($ifDescr =~ /$pattern/i) {
633          push(@indices, [$ifIndex]);
634        }
635      } else {
636        if ($self->opts->name =~ /^\d+$/) {
637          if ($ifIndex == 1 * $self->opts->name) {
638            push(@indices, [1 * $self->opts->name]);
639          }
640        } else {
641          if (lc $ifDescr eq lc $self->opts->name) {
642            push(@indices, [$ifIndex]);
643          }
644        }
645      }
646    # Check ifAlias (using --name3)
647    } elsif ($self->opts->name3) {
648      if ($self->opts->regexp) {
649        my $pattern = $self->opts->name3;
650        if ($ifAlias =~ /$pattern/i) {
651          push(@indices, [$ifIndex]);
652        }
653      } else {
654        if (lc $ifAlias eq lc $self->opts->name3) {
655          push(@indices, [$ifIndex]);
656        }
657      }
658    # take all interfaces
659    } else {
660      push(@indices, [$ifIndex]);
661    }
662  }
663  return @indices;
664}
665
666sub enrich_interface_attributes {
667  my ($self, $interface) = @_;
668  # can be used by vendor-specific InterfaceSubsystem to add extra
669  # attributes
670}
671
672
673package Classes::IFMIB::Component::InterfaceSubsystem::Interface;
674our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);
675use strict;
676use Digest::MD5 qw(md5_hex);
677
678sub finish {
679  my ($self) = @_;
680  foreach my $key (keys %{$self}) {
681    next if $key !~ /^if/;
682    $self->{$key} = 0 if ! defined $self->{$key};
683  }
684  # Nexus 5k/6k - Memory leak in pfstat process causing hap reset CSCur11599
685  # Nexus 6.x crashen, wenn man ifIndex abfragt. Kein Kommentar
686  $self->{ifIndex} = $self->{flat_indices} if ! exists $self->{ifIndex};
687  $self->{ifDescr} = unpack("Z*", $self->{ifDescr}); # windows has trailing nulls
688  if ($self->opts->name2 && $self->opts->name2 =~ /\(\.\*\?*\)/) {
689    if ($self->{ifDescr} =~ $self->opts->name2) {
690      $self->{ifDescr} = $1;
691    }
692  }
693  if ($self->mode =~ /device::interfaces::duplex/) {
694  } elsif ($self->mode =~ /device::interfaces::uptime/) {
695    $self->{sysUptime} = $self->get_snmp_object('MIB-2-MIB', 'sysUpTime', 0) / 100;
696    $self->{sysUptime64} = $self->uptime();
697  } else {
698    # Manche Stinkstiefel haben ifName, ifHighSpeed und z.b. ifInMulticastPkts,
699    # aber keine ifHC*Octets. Gesehen bei Cisco Switch Interface Nul0 o.ae.
700    if ($self->{ifName} && defined $self->{ifHCInOctets} &&
701        defined $self->{ifHCOutOctets} && $self->{ifHCInOctets} ne "noSuchObject") {
702      $self->{ifAlias} ||= $self->{ifName};
703      $self->{ifName} = unpack("Z*", $self->{ifName});
704      $self->{ifAlias} = unpack("Z*", $self->{ifAlias});
705      $self->{ifAlias} =~ s/\|/!/g if $self->{ifAlias};
706      bless $self, 'Classes::IFMIB::Component::InterfaceSubsystem::Interface::64bit';
707    }
708    if ($self->mode =~ /device::interfaces::(broadcast|complete)/ &&
709        ! exists $self->{ifInErrors} && ! exists $self->{ifOutErrors} &&
710        ! exists $self->{ifInDiscards} && ! exists $self->{ifOutDiscards} &&
711        $self->{ifDescr} =~ /.*Ethernet[\/\d]+\.\d+$/) {
712      # Urspruenglich wies sowas klar auf so Pseudo-bundle-channel-sonstwas hin.
713      # Aber dann tauchte im Schwaebischen ein TenGigabitEthernet auf, bei dem
714      # Errors und Discards fehlten. Erst dachte ich, die haetten sich gesagt:
715      # "Mir naehmet desch guenschtigere Modell ohne Counter", dann dachte ich,
716      # die haben billigen Chinaschrott gekauft, weil ifHighSpeed statt 10000
717      # bei den drei in Frage kommenden Interfaces nur 275, 1000 und 25 anzeigt.
718      # Aber anscheinend hat das alles seine Richtigkeit in einem Szenario wie
719      # 1 Haupt-Interface mit 3 VRFs mit jeweils eigenen VLANs
720      # also TenGigabitEthernet0/0/0.10, TenGigabitEthernet0/0/0.20 und
721      # TenGigabitEthernet0/0/0.30 unter TenGigabitEthernet0/0/0, laut
722      # ifAlias so MPLS mit Vodafone.
723      $self->{ifInErrors} = 0;
724      $self->{ifOutErrors} = 0;
725      $self->{ifInDiscards} = 0;
726      $self->{ifOutDiscards} = 0;
727    } elsif ((! exists $self->{ifInOctets} && ! exists $self->{ifOutOctets} &&
728        $self->mode =~ /device::interfaces::(usage|complete)/) ||
729        (! exists $self->{ifInErrors} && ! exists $self->{ifOutErrors} &&
730        $self->mode =~ /device::interfaces::(errors|complete)/) ||
731        (! exists $self->{ifInDiscards} && ! exists $self->{ifOutDiscards} &&
732        $self->mode =~ /device::interfaces::(discards|complete)/) ||
733        (! exists $self->{ifInUcastPkts} && ! exists $self->{ifOutUcastPkts} &&
734        $self->mode =~ /device::interfaces::(broadcast|complete)/)) {
735      bless $self, 'Classes::IFMIB::Component::InterfaceSubsystem::Interface::StackSub';
736    }
737    if ($self->{ifPhysAddress}) {
738      $self->{ifPhysAddress} = join(':', unpack('(H2)*', $self->{ifPhysAddress}));
739    }
740  }
741  $self->init();
742}
743
744sub calc_usage {
745  my ($self) = @_;
746  $self->valdiff({name => $self->{ifIndex}.'#'.$self->{ifDescr}}, qw(ifInOctets ifOutOctets));
747  $self->{delta_ifInBits} = $self->{delta_ifInOctets} * 8;
748  $self->{delta_ifOutBits} = $self->{delta_ifOutOctets} * 8;
749  if ($self->{ifSpeed} == 0) {
750    # vlan graffl
751    $self->{inputUtilization} = 0;
752    $self->{outputUtilization} = 0;
753    $self->{maxInputRate} = 0;
754    $self->{maxOutputRate} = 0;
755  } else {
756    $self->{inputUtilization} = 100 * $self->{delta_ifInBits} /
757        ($self->{delta_timestamp} * $self->{ifSpeed});
758    $self->{outputUtilization} = 100 * $self->{delta_ifOutBits} /
759        ($self->{delta_timestamp} * $self->{ifSpeed});
760    $self->{maxInputRate} = $self->{ifSpeed};
761    $self->{maxOutputRate} = $self->{ifSpeed};
762  }
763  if (defined $self->opts->ifspeed) {
764    $self->override_opt('ifspeedin', $self->opts->ifspeed);
765    $self->override_opt('ifspeedout', $self->opts->ifspeed);
766  }
767  if (defined $self->opts->ifspeedin) {
768    $self->{inputUtilization} = 100 * $self->{delta_ifInBits} /
769        ($self->{delta_timestamp} * $self->opts->ifspeedin);
770    $self->{maxInputRate} = $self->opts->ifspeedin;
771  }
772  if (defined $self->opts->ifspeedout) {
773    $self->{outputUtilization} = 100 * $self->{delta_ifOutBits} /
774        ($self->{delta_timestamp} * $self->opts->ifspeedout);
775    $self->{maxOutputRate} = $self->opts->ifspeedout;
776  }
777  $self->{inputRate} = $self->{delta_ifInBits} / $self->{delta_timestamp};
778  $self->{outputRate} = $self->{delta_ifOutBits} / $self->{delta_timestamp};
779  $self->override_opt("units", "bit") if ! $self->opts->units;
780  $self->{inputRate} /= $self->number_of_bits($self->opts->units);
781  $self->{outputRate} /= $self->number_of_bits($self->opts->units);
782  $self->{maxInputRate} /= $self->number_of_bits($self->opts->units);
783  $self->{maxOutputRate} /= $self->number_of_bits($self->opts->units);
784  if ($self->{ifOperStatus} eq 'down') {
785    $self->{inputUtilization} = 0;
786    $self->{outputUtilization} = 0;
787    $self->{inputRate} = 0;
788    $self->{outputRate} = 0;
789    $self->{maxInputRate} = 0;
790    $self->{maxOutputRate} = 0;
791  }
792}
793
794sub get_mub_pkts {
795  my ($self) = @_;
796  foreach my $key (qw(ifInUcastPkts
797      ifInMulticastPkts ifInBroadcastPkts ifOutUcastPkts
798      ifOutMulticastPkts ifOutBroadcastPkts)) {
799    $self->{$key} = 0 if (! exists $self->{$key} || ! defined $self->{$key});
800  }
801  $self->valdiff({name => 'mub_'.$self->{ifDescr}}, qw(ifInUcastPkts
802      ifInMulticastPkts ifInBroadcastPkts ifOutUcastPkts
803      ifOutMulticastPkts ifOutBroadcastPkts));
804  $self->{delta_ifInPkts} = $self->{delta_ifInUcastPkts} +
805      $self->{delta_ifInMulticastPkts} +
806      $self->{delta_ifInBroadcastPkts};
807  $self->{delta_ifOutPkts} = $self->{delta_ifOutUcastPkts} +
808      $self->{delta_ifOutMulticastPkts} +
809      $self->{delta_ifOutBroadcastPkts};
810}
811
812sub init {
813  my ($self) = @_;
814  if ($self->mode =~ /device::interfaces::complete/) {
815    # uglatto, but $self->mode is an lvalue
816    $Monitoring::GLPlugin::mode = "device::interfaces::operstatus";
817    $self->init();
818    if ($self->{ifOperStatus} eq "up") {
819      foreach my $mode (qw(device::interfaces::usage
820          device::interfaces::errors device::interfaces::discards
821          device::interfaces::broadcasts)) {
822        $Monitoring::GLPlugin::mode = $mode;
823        $self->init();
824      }
825    }
826    $Monitoring::GLPlugin::mode = "device::interfaces::complete";
827  } elsif ($self->mode =~ /device::interfaces::usage/) {
828    $self->calc_usage();
829  } elsif ($self->mode =~ /device::interfaces::errors/) {
830    $self->calc_usage() if ! defined $self->{inputUtilization};
831    $self->get_mub_pkts() if ! defined $self->{delta_ifOutPkts};
832    $self->valdiff({name => $self->{ifDescr}}, qw(ifInErrors ifOutErrors));
833    $self->{inputErrorsPercent} = $self->{delta_ifInPkts} == 0 ? 0 :
834        100 * $self->{delta_ifInErrors} / $self->{delta_ifInPkts};
835    $self->{outputErrorsPercent} = $self->{delta_ifOutPkts} == 0 ? 0 :
836        100 * $self->{delta_ifOutErrors} / $self->{delta_ifOutPkts};
837    $self->{inputErrorRate} = $self->{delta_ifInErrors}
838        / $self->{delta_timestamp};
839    $self->{outputErrorRate} = $self->{delta_ifOutErrors}
840        / $self->{delta_timestamp};
841  } elsif ($self->mode =~ /device::interfaces::discards/) {
842    $self->calc_usage() if ! defined $self->{inputUtilization};
843    $self->get_mub_pkts() if ! defined $self->{delta_ifOutPkts};
844    $self->valdiff({name => $self->{ifDescr}}, qw(ifInDiscards ifOutDiscards));
845    $self->{inputDiscardsPercent} = $self->{delta_ifInPkts} == 0 ? 0 :
846        100 * $self->{delta_ifInDiscards} / $self->{delta_ifInPkts};
847    $self->{outputDiscardsPercent} = $self->{delta_ifOutPkts} == 0 ? 0 :
848        100 * $self->{delta_ifOutDiscards} / $self->{delta_ifOutPkts};
849    $self->{inputDiscardRate} = $self->{delta_ifInDiscards}
850        / $self->{delta_timestamp};
851    $self->{outputDiscardRate} = $self->{delta_ifOutDiscards}
852        / $self->{delta_timestamp};
853  } elsif ($self->mode =~ /device::interfaces::broadcasts/) {
854    $self->calc_usage() if ! defined $self->{inputUtilization};
855    $self->get_mub_pkts() if ! defined $self->{delta_ifOutPkts};
856    $self->{inputBroadcastPercent} = $self->{delta_ifInPkts} == 0 ? 0 :
857        100 * $self->{delta_ifInBroadcastPkts} / $self->{delta_ifInPkts};
858    $self->{outputBroadcastPercent} = $self->{delta_ifOutPkts} == 0 ? 0 :
859        100 * $self->{delta_ifOutBroadcastPkts} / $self->{delta_ifOutPkts};
860    $self->{inputBroadcastUtilizationPercent} = $self->{inputBroadcastPercent}
861        * $self->{inputUtilization} / 100;
862    $self->{outputBroadcastUtilizationPercent} = $self->{outputBroadcastPercent}
863        * $self->{outputUtilization} / 100;
864  } elsif ($self->mode =~ /device::interfaces::operstatus/) {
865  } elsif ($self->mode =~ /device::interfaces::availability/) {
866    $self->{ifStatusDuration} =
867        $self->uptime() - $self->timeticks($self->{ifLastChange});
868    $self->opts->override_opt('lookback', 1800) if ! $self->opts->lookback;
869    if ($self->{ifAdminStatus} eq "down") {
870      $self->{ifAvailable} = "true";
871    } elsif ($self->{ifAdminStatus} eq "up" && $self->{ifOperStatus} ne "up" &&
872        $self->{ifStatusDuration} > $self->opts->lookback) {
873      # and ifLastChange schon ein wenig laenger her
874      $self->{ifAvailable} = "true";
875    } else {
876      $self->{ifAvailable} = "false";
877    }
878    my $gb = 1000 * 1000 * 1000;
879    my $mb = 1000 * 1000;
880    my $kb = 1000;
881    my $speed = $self->{ifHighSpeed} ?
882        ($self->{ifHighSpeed} * $mb) : $self->{ifSpeed};
883    if ($speed >= $gb) {
884      $self->{ifSpeedText} = sprintf "%.2fGB", $speed / $gb;
885    } elsif ($speed >= $mb) {
886      $self->{ifSpeedText} = sprintf "%.2fMB", $speed / $mb;
887    } elsif ($speed >= $kb) {
888      $self->{ifSpeedText} = sprintf "%.2fKB", $speed / $kb;
889    } else {
890      $self->{ifSpeedText} = sprintf "%.2fB", $speed;
891    }
892    $self->{ifSpeedText} =~ s/\.00//g;
893  } elsif ($self->mode =~ /device::interfaces::uptime/) {
894    $self->{ifLastChangeRaw} = $self->{ifLastChange};
895    $self->{ifLastChange} = time -
896        $self->ago_sysuptime($self->{ifLastChange});
897    # Alter Text:
898    # Wenn sysUptime ueberlaeuft, dann wird's schwammig. Denn dann kann
899    # ich nicht sagen, ob ein ifLastChange ganz am Anfang passiert ist,
900    # unmittelbar nach dem Booten, oder grad eben vor drei Minuten, als
901    # der Ueberlauf stattfand. Ergo ist dieser Mode nach einer Uptime von
902    # 497 Tagen nicht mehr brauchbar.
903    # Und tatsaechlich gibt es Typen die lassen ihre Switche in den
904    # Filialen ueber ein Jahr durchlaufen und machen dann reihenweise Tickets auf.
905    # boot                   ifchange1  overflow  ifchange2
906    # |                      |          |         |
907    # |---------------------------------^---------------------------------^-----
908    #                                          |
909    #                                          check
910    # Zum Zeitpunkt des Checks ist ifchange1 groesser als die sysUptime
911    # Damit wird ifLastChange negativ.
912    # Eine Chance gibts dann noch, man geht davon aus, dass das der
913    # einzige Overflow war (tatsaechlich koennten ja mehrere passiert sein)
914    # Also: max(32bit) - ifchange1 + sysUptime
915    # ago_sysuptime fackelt das ganz gut ab.
916    $self->{ifLastChangeHuman} = scalar localtime $self->{ifLastChange};
917    $self->{ifDuration} = time - $self->{ifLastChange};
918    $self->{ifDurationMinutes} = $self->{ifDuration} / 60; # minutes
919  }
920  return $self;
921}
922
923sub init_etherstats {
924  my ($self) = @_;
925  if ($self->mode =~ /device::interfaces::etherstats/) {
926    $Monitoring::GLPlugin::mode = "device::interfaces::broadcasts";
927    $self->init();
928    $Monitoring::GLPlugin::mode = "device::interfaces::etherstats";
929    # in the beginning we start 32/64bit-unaware, so columns contain
930    # also ifHC-names, but there are no such attributes in the interface object
931    @{$self->{columns}} = grep {
932      ! /^ifHC(In|Out).*castPkts$/
933    } grep {
934      ! /^(ifOperStatus|ifAdminStatus|ifIndex|ifDescr|ifAlias|ifName)$/
935    } @{$self->{columns}};
936    # z.b. Serial2/3/2 in Singapore, broadcastet nicht
937    my $ident = $self->{ifDescr}.md5_hex(join('_', @{$self->{columns}}));
938    $self->valdiff({name => $ident}, @{$self->{columns}});
939    $self->{delta_InPkts} = $self->{delta_ifInUcastPkts} +
940        $self->{delta_ifInMulticastPkts} + $self->{delta_ifInBroadcastPkts};
941    $self->{delta_OutPkts} = $self->{delta_ifOutUcastPkts} +
942        $self->{delta_ifOutMulticastPkts} + $self->{delta_ifOutBroadcastPkts};
943    for my $stat (grep { /^(dot3|etherStats)/ } @{$self->{columns}}) {
944      next if ! defined $self->{'delta_'.$stat};
945      $self->{$stat.'Percent'} = $self->{delta_InPkts} + $self->{delta_OutPkts} ?
946          100 * $self->{'delta_'.$stat} /
947          ($self->{delta_InPkts} + $self->{delta_OutPkts}) : 0;
948    }
949  } elsif ($self->mode =~ /device::interfaces::duplex/) {
950    if (defined $self->{dot3StatsDuplexStatus}) {
951    } elsif (! defined $self->{dot3StatsDuplexStatus} && $self->{ifType} !~ /ether/i) {
952        $self->{dot3StatsDuplexStatus} = "notApplicable";
953    } elsif (! defined $self->{dot3StatsDuplexStatus} && $self->implements_mib('EtherLike-MIB')) {
954      if (defined $self->opts->mitigation() &&
955          $self->opts->mitigation() eq 'ok') {
956        $self->{dot3StatsDuplexStatus} = "fullDuplex";
957      } else {
958        $self->{dot3StatsDuplexStatus} = "unknown";
959      }
960    } else {
961      $self->{dot3StatsDuplexStatus} = "unknown";
962    }
963  }
964  return $self;
965}
966
967sub check {
968  my ($self) = @_;
969  my $full_descr = sprintf "%s%s%s",
970      $self->{ifDescr},
971      $self->{ifAlias} && $self->{ifAlias} ne $self->{ifDescr} ?
972          " (alias ".$self->{ifAlias}.")" : "",
973      $self->{ifAddresses} ? " (addresses ".$self->{ifAddresses}.")" : "";
974  if ($self->mode =~ /device::interfaces::complete/) {
975    # uglatto, but $self->mode is an lvalue
976    $Monitoring::GLPlugin::mode = "device::interfaces::operstatus";
977    $self->check();
978    if ($self->{ifOperStatus} eq "up") {
979          # kostenpflichtiges feature # device::interfaces::duplex
980      foreach my $mode (qw(device::interfaces::usage
981          device::interfaces::errors device::interfaces::discards
982          device::interfaces::broadcast)) {
983        $Monitoring::GLPlugin::mode = $mode;
984        $self->check();
985      }
986    }
987    $Monitoring::GLPlugin::mode = "device::interfaces::complete";
988  } elsif ($self->mode =~ /device::interfaces::usage/) {
989    $self->add_info(sprintf 'interface %s usage is in:%.2f%% (%s) out:%.2f%% (%s)%s',
990        $full_descr,
991        $self->{inputUtilization},
992        sprintf("%.2f%s/s", $self->{inputRate}, $self->opts->units),
993        $self->{outputUtilization},
994        sprintf("%.2f%s/s", $self->{outputRate}, $self->opts->units),
995        $self->{ifOperStatus} eq 'down' ? ' (down)' : '');
996    $self->set_thresholds(
997        metric => $self->{ifDescr}.'_usage_in',
998        warning => 80,
999        critical => 90
1000    );
1001    my $in = $self->check_thresholds(
1002        metric => $self->{ifDescr}.'_usage_in',
1003        value => $self->{inputUtilization}
1004    );
1005    $self->set_thresholds(
1006        metric => $self->{ifDescr}.'_usage_out',
1007        warning => 80,
1008        critical => 90
1009    );
1010    my $out = $self->check_thresholds(
1011        metric => $self->{ifDescr}.'_usage_out',
1012        value => $self->{outputUtilization}
1013    );
1014    my $level = ($in > $out) ? $in : ($out > $in) ? $out : $in;
1015    $self->add_message($level);
1016    $self->add_perfdata(
1017        label => $self->{ifDescr}.'_usage_in',
1018        value => $self->{inputUtilization},
1019        uom => '%',
1020    );
1021    $self->add_perfdata(
1022        label => $self->{ifDescr}.'_usage_out',
1023        value => $self->{outputUtilization},
1024        uom => '%',
1025    );
1026    my ($inwarning, $incritical) = $self->get_thresholds(
1027        metric => $self->{ifDescr}.'_usage_in',
1028    );
1029    $self->set_thresholds(
1030        metric => $self->{ifDescr}.'_traffic_in',
1031        warning => $self->{maxInputRate} / 100 * $inwarning,
1032        critical => $self->{maxInputRate} / 100 * $incritical
1033    );
1034    $self->add_perfdata(
1035        label => $self->{ifDescr}.'_traffic_in',
1036        value => $self->{inputRate},
1037        uom => $self->opts->units =~ /^(B|KB|MB|GB|TB)$/ ? $self->opts->units : undef,
1038        places => 2,
1039        min => 0,
1040        max => $self->{maxInputRate},
1041    );
1042    my ($outwarning, $outcritical) = $self->get_thresholds(
1043        metric => $self->{ifDescr}.'_usage_out',
1044    );
1045    $self->set_thresholds(
1046        metric => $self->{ifDescr}.'_traffic_out',
1047        warning => $self->{maxOutputRate} / 100 * $outwarning,
1048        critical => $self->{maxOutputRate} / 100 * $outcritical,
1049    );
1050    $self->add_perfdata(
1051        label => $self->{ifDescr}.'_traffic_out',
1052        value => $self->{outputRate},
1053        uom => $self->opts->units =~ /^(B|KB|MB|GB|TB)$/ ? $self->opts->units : undef,
1054        places => 2,
1055        min => 0,
1056        max => $self->{maxOutputRate},
1057    );
1058  } elsif ($self->mode =~ /device::interfaces::errors/) {
1059    $self->add_info(sprintf 'interface %s errors in:%.2f%% out:%.2f%% ',
1060        $full_descr,
1061        $self->{inputErrorsPercent} , $self->{outputErrorsPercent});
1062    $self->set_thresholds(
1063        metric => $self->{ifDescr}.'_errors_in',
1064        warning => 1,
1065        critical => 10,
1066    );
1067    my $in = $self->check_thresholds(
1068        metric => $self->{ifDescr}.'_errors_in',
1069        value => $self->{inputErrorsPercent}
1070    );
1071    $self->set_thresholds(
1072        metric => $self->{ifDescr}.'_errors_out',
1073        warning => 1,
1074        critical => 10,
1075    );
1076    my $out = $self->check_thresholds(
1077        metric => $self->{ifDescr}.'_errors_out',
1078        value => $self->{outputErrorsPercent}
1079    );
1080    my $level = ($in > $out) ? $in : ($out > $in) ? $out : $in;
1081    $self->add_message($level);
1082    $self->add_perfdata(
1083        label => $self->{ifDescr}.'_errors_in',
1084        value => $self->{inputErrorsPercent},
1085        uom => '%',
1086    );
1087    $self->add_perfdata(
1088        label => $self->{ifDescr}.'_errors_out',
1089        value => $self->{outputErrorsPercent},
1090        uom => '%',
1091    );
1092  } elsif ($self->mode =~ /device::interfaces::discards/) {
1093    $self->add_info(sprintf 'interface %s discards in:%.2f%% out:%.2f%% ',
1094        $full_descr,
1095        $self->{inputDiscardsPercent} , $self->{outputDiscardsPercent});
1096    $self->set_thresholds(
1097        metric => $self->{ifDescr}.'_discards_in',
1098        warning => 5,
1099        critical => 10,
1100    );
1101    my $in = $self->check_thresholds(
1102        metric => $self->{ifDescr}.'_discards_in',
1103        value => $self->{inputDiscardsPercent}
1104    );
1105    $self->set_thresholds(
1106        metric => $self->{ifDescr}.'_discards_out',
1107        warning => 5,
1108        critical => 10,
1109    );
1110    my $out = $self->check_thresholds(
1111        metric => $self->{ifDescr}.'_discards_out',
1112        value => $self->{outputDiscardsPercent}
1113    );
1114    my $level = ($in > $out) ? $in : ($out > $in) ? $out : $in;
1115    $self->add_message($level);
1116    $self->add_perfdata(
1117        label => $self->{ifDescr}.'_discards_in',
1118        value => $self->{inputDiscardsPercent},
1119        uom => '%',
1120    );
1121    $self->add_perfdata(
1122        label => $self->{ifDescr}.'_discards_out',
1123        value => $self->{outputDiscardsPercent},
1124        uom => '%',
1125    );
1126  } elsif ($self->mode =~ /device::interfaces::broadcast/) {
1127    # BroadcastPercent
1128    #  -> TenGigabitEthernet0/0/0.10_broadcast_in
1129    #  wieviel % der ein/ausgehenden Pakete sind Broadcasts?
1130    #  das kann bei standby-Firewall-Interfaces sehr hoch sein, wenn regulaerer
1131    #  Traffic nicht stattfindet, aber viel Clustergeschwaetz.
1132    # BroadcastUtilizationPercent = wieviel % der verfuegbaren Bandbreite
1133    #  -> TenGigabitEthernet0/0/0.10_broadcast_usage_in
1134    #  nehmen die Broadcasts ein?
1135    #  Der Schwellwert ist hoch eingestellt, wenn der gerissen wird, dann ist
1136    #  definitiv was faul.
1137    $self->add_info(sprintf 'interface %s broadcast in:%.2f%% out:%.2f%% (%% of traffic) in:%.2f%% out:%.2f%% (%% of bandwidth)',
1138        $full_descr,
1139        $self->{inputBroadcastPercent} , $self->{outputBroadcastPercent},
1140        $self->{inputBroadcastUtilizationPercent} , $self->{outputBroadcastUtilizationPercent});
1141    $self->set_thresholds(
1142        metric => $self->{ifDescr}.'_broadcast_in',
1143        warning => 10,
1144        critical => 20
1145    );
1146    my $uin = $self->check_thresholds(
1147        metric => $self->{ifDescr}.'_broadcast_in',
1148        value => $self->{inputBroadcastPercent}
1149    );
1150    $self->set_thresholds(
1151        metric => $self->{ifDescr}.'_broadcast_out',
1152        warning => 10,
1153        critical => 20
1154    );
1155    my $uout = $self->check_thresholds(
1156        metric => $self->{ifDescr}.'_broadcast_out',
1157        value => $self->{outputBroadcastPercent}
1158    );
1159    $self->add_perfdata(
1160        label => $self->{ifDescr}.'_broadcast_in',
1161        value => $self->{inputBroadcastPercent},
1162        uom => '%',
1163    );
1164    $self->add_perfdata(
1165        label => $self->{ifDescr}.'_broadcast_out',
1166        value => $self->{outputBroadcastPercent},
1167        uom => '%',
1168    );
1169    my $ulevel = ($uin > $uout) ? $uin : ($uout > $uin) ? $uout : $uin;
1170    $self->set_thresholds(
1171        metric => $self->{ifDescr}.'_broadcast_usage_in',
1172        warning => 10,
1173        critical => 20
1174    );
1175    my $bin = $self->check_thresholds(
1176        metric => $self->{ifDescr}.'_broadcast_usage_in',
1177        value => $self->{inputBroadcastUtilizationPercent}
1178    );
1179    $self->set_thresholds(
1180        metric => $self->{ifDescr}.'_broadcast_usage_out',
1181        warning => 10,
1182        critical => 20
1183    );
1184    my $bout = $self->check_thresholds(
1185        metric => $self->{ifDescr}.'_broadcast_usage_out',
1186        value => $self->{outputBroadcastUtilizationPercent}
1187    );
1188    $self->add_perfdata(
1189        label => $self->{ifDescr}.'_broadcast_usage_in',
1190        value => $self->{inputBroadcastUtilizationPercent},
1191        uom => '%',
1192    );
1193    $self->add_perfdata(
1194        label => $self->{ifDescr}.'_broadcast_usage_out',
1195        value => $self->{outputBroadcastUtilizationPercent},
1196        uom => '%',
1197    );
1198    my $blevel = ($bin > $bout) ? $bin : ($bout > $bin) ? $bout : $bin;
1199    $self->add_message(($blevel > $ulevel) ? $blevel : $ulevel);
1200  } elsif ($self->mode =~ /device::interfaces::operstatus/) {
1201    #rfc2863
1202    #(1)   if ifAdminStatus is not down and ifOperStatus is down then a
1203    #     fault condition is presumed to exist on the interface.
1204    #(2)   if ifAdminStatus is down, then ifOperStatus will normally also
1205    #     be down (or notPresent) i.e., there is not (necessarily) a
1206    #     fault condition on the interface.
1207    # --warning onu,anu
1208    # Admin: admindown,admin
1209    # Admin: --warning
1210    #        --critical admindown
1211    # !ad+od  ad+!(od*on)
1212    # warn & warnbitfield
1213#    if ($self->opts->critical) {
1214#      if ($self->opts->critical =~ /^u/) {
1215#      } elsif ($self->opts->critical =~ /^u/) {
1216#      }
1217#    }
1218#    if ($self->{ifOperStatus} ne 'up') {
1219#      }
1220#    }
1221    $self->add_info(sprintf '%s is %s/%s',
1222        $full_descr,
1223        $self->{ifOperStatus}, $self->{ifAdminStatus});
1224    $self->add_ok();
1225    if ($self->{ifOperStatus} eq 'down' && $self->{ifAdminStatus} ne 'down') {
1226      $self->add_critical(
1227          sprintf 'fault condition is presumed to exist on %s',
1228          $full_descr);
1229    }
1230    if ($self->{ifAdminStatus} eq 'down') {
1231      $self->add_message(
1232          defined $self->opts->mitigation() ? $self->opts->mitigation() : 2,
1233          sprintf '%s is admin down', $full_descr);
1234    }
1235  } elsif ($self->mode =~ /device::interfaces::availability/) {
1236    $self->{ifStatusDuration} =
1237        $self->human_timeticks($self->{ifStatusDuration});
1238    $self->add_info(sprintf '%s is %savailable (%s/%s, since %s)',
1239        $self->{ifDescr}, ($self->{ifAvailable} eq "true" ? "" : "un"),
1240        $self->{ifOperStatus}, $self->{ifAdminStatus},
1241        $self->{ifStatusDuration});
1242  } elsif ($self->mode =~ /device::interfaces::etherstats/) {
1243    for my $stat (grep { /^(dot3|etherStats)/ } @{$self->{columns}}) {
1244      next if ! defined $self->{$stat.'Percent'};
1245      my $label = $stat.'Percent';
1246      $label =~ s/^(dot3Stats|etherStats)//g;
1247      $label =~ s/(?:\b|(?<=([a-z])))([A-Z][a-z]+)/(defined($1) ? "_" : "") . lc($2)/eg;
1248      $label = $self->{ifDescr}.'_'.$label;
1249      $self->add_info(sprintf 'interface %s %s is %.2f%%',
1250          $full_descr, $stat.'Percent', $self->{$stat.'Percent'});
1251      $self->set_thresholds(
1252          metric => $label,
1253          warning => 1,
1254          critical => 10
1255      );
1256      $self->add_message(
1257          $self->check_thresholds(metric => $label, value => $self->{$stat.'Percent'}));
1258      $self->add_perfdata(
1259          label => $label,
1260          value => $self->{$stat.'Percent'},
1261          uom => '%',
1262      );
1263    }
1264  } elsif ($self->mode =~ /device::interfaces::duplex/) {
1265    $self->add_info(sprintf "%s duplex status is %s",
1266        $self->{ifDescr}, $self->{dot3StatsDuplexStatus}
1267    );
1268    if ($self->{ifOperStatus} ne "up") {
1269      $self->annotate_info(sprintf "oper %s", $self->{ifOperStatus});
1270      $self->add_ok();
1271    } elsif ($self->{dot3StatsDuplexStatus} eq "notApplicable") {
1272      $self->add_ok();
1273    } else {
1274      if ($self->{dot3StatsDuplexStatus} eq "unknown") {
1275        $self->add_unknown();
1276      } elsif ($self->{dot3StatsDuplexStatus} eq "fullDuplex") {
1277        $self->add_ok();
1278      } else {
1279        # kein critical, weil so irgendwie funktionierts ja
1280        $self->add_warning();
1281      }
1282    }
1283  } elsif ($self->mode =~ /device::interfaces::uptime/) {
1284    $self->add_info(sprintf "%s was changed %s ago",
1285        $full_descr, $self->human_timeticks($self->{ifDuration}));
1286    $self->set_thresholds(metric => $self->{ifDescr}."_duration",
1287        warning => "15:", critical => "5:");
1288    $self->add_message($self->check_thresholds(
1289        metric => $self->{ifDescr}."_duration",
1290        value => $self->{ifDurationMinutes}));
1291    $self->add_perfdata(
1292        label => $self->{ifDescr}."_duration",
1293        value => $self->{ifDurationMinutes},
1294    );
1295  }
1296}
1297
1298sub list {
1299  my ($self) = @_;
1300  if ($self->mode =~ /device::interfaces::listdetail/) {
1301    my $cL2L3IfModeOper = $self->get_snmp_object('CISCO-L2L3-INTERFACE-CONFIG-MIB', 'cL2L3IfModeOper', $self->{ifIndex}) || "unknown";
1302    my $vlanTrunkPortDynamicStatus = $self->get_snmp_object('CISCO-VTP-MIB', 'vlanTrunkPortDynamicStatus', $self->{ifIndex}) || "unknown";
1303    printf "%06d %s %s %s %s\n", $self->{ifIndex}, $self->{ifDescr}, $self->{ifAlias},
1304        $cL2L3IfModeOper, $vlanTrunkPortDynamicStatus;
1305  } else {
1306    printf "%06d %s\n", $self->{ifIndex}, $self->{ifDescr};
1307  }
1308}
1309
1310
1311package Classes::IFMIB::Component::InterfaceSubsystem::Interface::64bit;
1312our @ISA = qw(Classes::IFMIB::Component::InterfaceSubsystem::Interface);
1313use strict;
1314use Digest::MD5 qw(md5_hex);
1315
1316sub calc_usage {
1317  my ($self) = @_;
1318  $self->valdiff({name => $self->{ifIndex}.'#'.$self->{ifDescr}}, qw(ifHCInOctets ifHCOutOctets));
1319  $self->{delta_ifInBits} = $self->{delta_ifHCInOctets} * 8;
1320  $self->{delta_ifOutBits} = $self->{delta_ifHCOutOctets} * 8;
1321  # ifSpeed = Bits/sec
1322  # ifHighSpeed = 1000000Bits/sec
1323  if ($self->{ifSpeed} == 0) {
1324    # vlan graffl
1325    $self->{inputUtilization} = 0;
1326    $self->{outputUtilization} = 0;
1327    $self->{maxInputRate} = 0;
1328    $self->{maxOutputRate} = 0;
1329  } elsif ($self->{ifSpeed} == 4294967295) {
1330    $self->{maxInputRate} = $self->{ifHighSpeed} * 1000000;
1331    $self->{maxOutputRate} = $self->{ifHighSpeed} * 1000000;
1332    $self->{inputUtilization} = 100 * $self->{delta_ifInBits} /
1333        ($self->{delta_timestamp} * $self->{maxInputRate});
1334    $self->{outputUtilization} = 100 * $self->{delta_ifOutBits} /
1335        ($self->{delta_timestamp} * $self->{maxOutputRate});
1336  } else {
1337    $self->{maxInputRate} = $self->{ifSpeed};
1338    $self->{maxOutputRate} = $self->{ifSpeed};
1339    $self->{inputUtilization} = 100 * $self->{delta_ifInBits} /
1340        ($self->{delta_timestamp} * $self->{maxInputRate});
1341    $self->{outputUtilization} = 100 * $self->{delta_ifOutBits} /
1342        ($self->{delta_timestamp} * $self->{maxOutputRate});
1343  }
1344  if (defined $self->opts->ifspeed) {
1345    $self->override_opt('ifspeedin', $self->opts->ifspeed);
1346    $self->override_opt('ifspeedout', $self->opts->ifspeed);
1347  }
1348  if (defined $self->opts->ifspeedin) {
1349    $self->{inputUtilization} = 100 * $self->{delta_ifInBits} /
1350        ($self->{delta_timestamp} * $self->opts->ifspeedin);
1351    $self->{maxInputRate} = $self->opts->ifspeedin;
1352  }
1353  if (defined $self->opts->ifspeedout) {
1354    $self->{outputUtilization} = 100 * $self->{delta_ifOutBits} /
1355        ($self->{delta_timestamp} * $self->opts->ifspeedout);
1356    $self->{maxOutputRate} = $self->opts->ifspeedout;
1357  }
1358  $self->{inputRate} = $self->{delta_ifInBits} / $self->{delta_timestamp};
1359  $self->{outputRate} = $self->{delta_ifOutBits} / $self->{delta_timestamp};
1360  $self->override_opt("units", "bit") if ! $self->opts->units;
1361  $self->{inputRate} /= $self->number_of_bits($self->opts->units);
1362  $self->{outputRate} /= $self->number_of_bits($self->opts->units);
1363  $self->{maxInputRate} /= $self->number_of_bits($self->opts->units);
1364  $self->{maxOutputRate} /= $self->number_of_bits($self->opts->units);
1365  if ($self->{ifOperStatus} eq 'down') {
1366    $self->{inputUtilization} = 0;
1367    $self->{outputUtilization} = 0;
1368    $self->{inputRate} = 0;
1369    $self->{outputRate} = 0;
1370    $self->{maxInputRate} = 0;
1371    $self->{maxOutputRate} = 0;
1372  }
1373}
1374
1375sub get_mub_pkts {
1376  my ($self) = @_;
1377  foreach my $key (qw(
1378      ifHCInUcastPkts ifHCInMulticastPkts ifHCInBroadcastPkts
1379      ifHCOutUcastPkts ifHCOutMulticastPkts ifHCOutBroadcastPkts)) {
1380    $self->{$key} = 0 if (! exists $self->{$key} || ! defined $self->{$key});
1381  }
1382  $self->valdiff({name => 'mub_'.$self->{ifDescr}}, qw(
1383      ifHCInUcastPkts ifHCInMulticastPkts ifHCInBroadcastPkts
1384      ifHCOutUcastPkts ifHCOutMulticastPkts ifHCOutBroadcastPkts));
1385  $self->{delta_ifInPkts} = $self->{delta_ifHCInUcastPkts} +
1386      $self->{delta_ifHCInMulticastPkts} +
1387      $self->{delta_ifHCInBroadcastPkts};
1388  $self->{delta_ifOutPkts} = $self->{delta_ifHCOutUcastPkts} +
1389      $self->{delta_ifHCOutMulticastPkts} +
1390      $self->{delta_ifHCOutBroadcastPkts};
1391}
1392
1393sub init {
1394  my ($self) = @_;
1395  if ($self->mode =~ /device::interfaces::usage/) {
1396    $self->calc_usage();
1397  } elsif ($self->mode =~ /device::interfaces::broadcasts/) {
1398    $self->calc_usage() if ! defined $self->{inputUtilization};
1399    $self->get_mub_pkts() if ! defined $self->{delta_ifOutPkts};
1400    $self->{inputBroadcastPercent} = $self->{delta_ifInPkts} == 0 ? 0 :
1401        100 * $self->{delta_ifHCInBroadcastPkts} / $self->{delta_ifInPkts};
1402    $self->{outputBroadcastPercent} = $self->{delta_ifOutPkts} == 0 ? 0 :
1403        100 * $self->{delta_ifHCOutBroadcastPkts} / $self->{delta_ifOutPkts};
1404    $self->{inputBroadcastUtilizationPercent} = $self->{inputBroadcastPercent}
1405        * $self->{inputUtilization} / 100;
1406    $self->{outputBroadcastUtilizationPercent} = $self->{outputBroadcastPercent}
1407        * $self->{inputUtilization} / 100;
1408  } else {
1409    $self->SUPER::init();
1410  }
1411  return $self;
1412}
1413
1414sub init_etherstats {
1415  my ($self) = @_;
1416  if ($self->mode =~ /device::interfaces::etherstats/) {
1417    $Monitoring::GLPlugin::mode = "device::interfaces::broadcasts";
1418    $self->init();
1419    $Monitoring::GLPlugin::mode = "device::interfaces::etherstats";
1420    # 32bit-cast ausputzen. es gibt welche, die haben nur 64bit
1421    @{$self->{columns}} = grep {
1422      ! /^if(In|Out).*castPkts$/
1423    } grep {
1424      ! /^(ifOperStatus|ifAdminStatus|ifIndex|ifDescr|ifAlias|ifName)$/
1425    } @{$self->{columns}};
1426    my $ident = $self->{ifDescr}.md5_hex(join('_', @{$self->{columns}}));
1427    $self->valdiff({name => $ident}, @{$self->{columns}});
1428    $self->{delta_InPkts} = $self->{delta_ifHCInUcastPkts} +
1429        $self->{delta_ifHCInMulticastPkts} + $self->{delta_ifHCInBroadcastPkts};
1430    $self->{delta_OutPkts} = $self->{delta_ifHCOutUcastPkts} +
1431        $self->{delta_ifHCOutMulticastPkts} + $self->{delta_ifHCOutBroadcastPkts};
1432    for my $stat (grep { /^(dot3|etherStats)/ } @{$self->{columns}}) {
1433      next if ! defined $self->{'delta_'.$stat};
1434      $self->{$stat.'Percent'} = $self->{delta_InPkts} + $self->{delta_OutPkts} ?
1435          100 * $self->{'delta_'.$stat} /
1436          ($self->{delta_InPkts} + $self->{delta_OutPkts}) : 0;
1437    }
1438  }
1439  return $self;
1440}
1441
1442package Classes::IFMIB::Component::InterfaceSubsystem::Interface::StackSub;
1443our @ISA = qw(Classes::IFMIB::Component::InterfaceSubsystem::Interface);
1444use strict;
1445
1446sub init {
1447  my ($self) = @_;
1448}
1449
1450sub init_etherstats {
1451  my ($self) = @_;
1452  # hat sowieso keine broadcastcounter, ist sinnlos
1453}
1454
1455sub check {
1456  my ($self) = @_;
1457  my $full_descr = sprintf "%s%s%s",
1458      $self->{ifDescr},
1459      $self->{ifAlias} && $self->{ifAlias} ne $self->{ifDescr} ?
1460          " (alias ".$self->{ifAlias}.")" : "",
1461      $self->{ifAddresses} ? " (addresses ".$self->{ifAddresses}.")" : "";
1462  if ($self->mode =~ /device::interfaces::operstatus/) {
1463    $self->SUPER::check();
1464  } elsif ($self->mode =~ /device::interfaces::duplex/) {
1465    $self->SUPER::check();
1466  } else {
1467    $self->add_ok(sprintf '%s has no traffic', $full_descr);
1468  }
1469}
1470
1471
1472package Classes::IFMIB::Component::InterfaceSubsystem::Address;
1473our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);
1474use strict;
1475
1476sub finish {
1477  my ($self) = @_;
1478  # INDEX { ipAddressAddrType, ipAddressAddr }
1479  my @tmp_indices = @{$self->{indices}};
1480  my $last_tmp = scalar(@tmp_indices) - 1;
1481  # .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
1482  # IP-FORWARD-MIB::inetCidrRouteIfIndex.ipv4."0.0.0.0".32.2.0.0.ipv4."10.208.143.81" = INTEGER: 25337
1483  # Frag mich jetzt keiner, warum dem ipv4 ein 1.4 entspricht. Ich kann
1484  # jedenfalls der IP-FORWARD-MIB bzw. RFC4001 nicht entnehmen, dass fuer
1485  # InetAddressType zwei Stellen des Index vorgesehen sind. Zumal nur die
1486  # erste Stelle für die Textual Convention relevant ist. Aergert mich ziemlich,
1487  # daß jeder bloede /usr/bin/snmpwalk das besser hinbekommt als ich.
1488  # Was dazugelernt: 1=InetAddressType, 4=gehoert zur folgenden InetAddressIPv4
1489  # und gibt die Laenge an. Noch mehr gelernt: wenn eine Table mit Integer und
1490  # Octet String indiziert ist, dann ist die Groeße des Octet String Bestandteil
1491  # der OID. Diese _kann_ weggelassen werden für den _letzten_ Index. Der ist
1492  # halt dann so lang wie der Rest der OID.
1493  # Mit einem IMPLIED-Keyword koennte die Laenge auch weggelassen werden.
1494
1495  $self->{ipAddressAddrType} = $self->mibs_and_oids_definition(
1496      'INET-ADDRESS-MIB', 'InetAddressType', $tmp_indices[0]);
1497  shift @tmp_indices;
1498
1499  $self->{ipAddressAddr} = $self->mibs_and_oids_definition(
1500      'INET-ADDRESS-MIB', 'InetAddressMaker',
1501      $self->{ipAddressAddrType}, @tmp_indices);
1502}
1503
1504