File Coverage

blib/lib/Net/SNMP/Mixin/Dot1qVlanStatic.pm
Criterion Covered Total %
statement 67 160 41.8
branch 20 62 32.2
condition 3 6 50.0
subroutine 20 23 86.9
pod 6 6 100.0
total 116 257 45.1


line stmt bran cond sub pod time code
1             package Net::SNMP::Mixin::Dot1qVlanStatic;
2              
3 4     4   395208 use strict;
  4         10  
  4         123  
4 4     4   21 use warnings;
  4         8  
  4         150  
5              
6             #
7             # store this package name in a handy variable,
8             # used for unambiguous prefix of mixin attributes
9             # storage in object hash
10             #
11             my $prefix = __PACKAGE__;
12              
13             #
14             # this module import config
15             #
16 4     4   23 use Carp ();
  4         9  
  4         52  
17 4     4   933 use Net::SNMP ();
  4         78632  
  4         111  
18 4     4   630 use Net::SNMP::Mixin::Util qw/idx2val hex2octet get_init_slot/;
  4         14526  
  4         34  
19              
20             #
21             # this module export config
22             #
23             my @mixin_methods;
24              
25             BEGIN {
26 4     4   1920 @mixin_methods = (
27             qw/
28             map_vlan_id2name
29             map_if_idx2vlan_id
30             map_vlan_id2if_idx
31             /
32             );
33              
34             # DEPRECATED methods, will get deleted in later versions
35 4         184 push @mixin_methods, qw/
36             map_vlan_static_ids2names
37             map_vlan_static_ports2ids
38             map_vlan_static_ids2ports
39             /;
40             }
41              
42 4         41 use Sub::Exporter -setup => {
43             exports => [@mixin_methods],
44             groups => { default => [@mixin_methods], },
45 4     4   32 };
  4         10  
46              
47             #
48             # SNMP oid constants used in this module
49             #
50             use constant {
51 4         4495 DOT1D_BASE_PORT_IF_INDEX => '1.3.6.1.2.1.17.1.4.1.2',
52              
53             DOT1Q_VLAN_STATIC_NAME => '1.3.6.1.2.1.17.7.1.4.3.1.1',
54             DOT1Q_VLAN_STATIC_EGRESS_PORTS => '1.3.6.1.2.1.17.7.1.4.3.1.2',
55             DOT1Q_VLAN_STATIC_UNTAGGED_PORTS => '1.3.6.1.2.1.17.7.1.4.3.1.4',
56             DOT1Q_VLAN_STATIC_ROW_STATUS => '1.3.6.1.2.1.17.7.1.4.3.1.5',
57 4     4   2422 };
  4         8  
58              
59             =head1 NAME
60              
61             Net::SNMP::Mixin::Dot1qVlanStatic - mixin class for 802.1-Q static vlan infos
62              
63             =head1 VERSION
64              
65             Version 0.05
66              
67             =cut
68              
69             our $VERSION = '0.05';
70              
71             =head1 SYNOPSIS
72              
73             use Net::SNMP;
74             use Net::SNMP::Mixin qw/mixer init_mixins/;
75              
76             my $session = Net::SNMP->session( -hostname => 'foo.bar.com');
77             $session->mixer('Net::SNMP::Mixin::Dot1qVlanStatic');
78             $session->init_mixins;
79             snmp_dispatcher() if $session->nonblocking;
80             $session->init_ok();
81             die $session->errors if $session->errors;
82              
83             my $vlan_id2name = $session->map_vlan_id2name();
84             foreach my $vlan_id ( keys %{$vlan_id2name} ) {
85             printf "Vlan-Id: %4d => Vlan-Name: %s\n",
86             $vlan_id, $vlan_id2name->{$vlan_id};
87             }
88              
89             my $vlan_ids2if_idx = $session->map_vlan_id2if_idx();
90             foreach my $id ( keys %{$vlan_ids2if_idx} ) {
91             printf "Vlan-Id: %4d\n", $id;
92             printf "\tTagged-Ports: %s\n", ( join ',', @{ $vlan_ids2if_idx->{$id}{tagged} } );
93             printf "\tUntagged-Ports: %s\n", ( join ',', @{ $vlan_ids2if_idx->{$id}{untagged} } );
94             }
95              
96             # sorted by interface
97             my $ports2ids = $session->map_if_idx2vlan_id();
98             foreach my $if_idx ( keys %{$ports2ids} ) {
99             printf "Interface: %10d\n", $if_idx;
100             printf "\tTagged-Vlans: %s\n", ( join ',', @{ $ports2ids->{$if_idx}{tagged} } );
101             printf "\tUntagged-Vlans: %s\n", ( join ',', @{ $ports2ids->{$if_idx}{untagged} } );
102             }
103              
104             =head1 DESCRIPTION
105              
106             A mixin class for vlan related infos from the dot1qVlanStaticTable within the Q-BRIDGE-MIB. The mixin-module provides methods for mapping between vlan-ids and vlan-names und relations between interface indexes and vlan-ids, tagged or untagged on these interfaces.
107              
108             =head1 MIXIN METHODS
109              
110             =head2 B<< OBJ->map_vlan_id2name() >>
111              
112             Returns a hash reference with statically configured vlan-ids as keys and the corresponing vlan-names as values:
113              
114             {
115             vlan_id => vlan_name,
116             vlan_id => vlan_name,
117             ... ,
118             }
119              
120             =cut
121              
122             sub map_vlan_id2name {
123 2     2 1 32553 my $session = shift;
124 2         10 my $agent = $session->hostname;
125              
126 2 50       28 Carp::croak "$agent: '$prefix' not initialized,"
127             unless $session->init_ok($prefix);
128              
129 0         0 my @active_vlan_ids = @{ $session->{$prefix}{activeVlanIds} };
  0         0  
130              
131 0         0 my $result = {};
132 0         0 foreach my $vlan_id (@active_vlan_ids) {
133              
134 0         0 $result->{$vlan_id} = $session->{$prefix}{dot1qVlanStaticNames}{$vlan_id};
135             }
136              
137 0         0 return $result;
138             }
139              
140             =head2 B<< OBJ->map_vlan_id2if_idx() >>
141              
142             Returns a hash reference with the vlan-ids as keys and tagged and untagged if_idx as values:
143              
144             {
145             vlan_id => {
146             tagged => [if_idx, ..., ],
147             untagged => [if_idx, ..., ],
148             },
149              
150             ... ,
151             }
152            
153             =cut
154              
155             sub map_vlan_id2if_idx {
156 1     1 1 786 my $session = shift;
157 1         5 my $agent = $session->hostname;
158              
159 1 50       8 Carp::croak "$agent: '$prefix' not initialized,"
160             unless $session->init_ok($prefix);
161              
162 0         0 my @active_vlan_ids = @{ $session->{$prefix}{activeVlanIds} };
  0         0  
163 0         0 my $bridge_port2if_idx = $session->{$prefix}{dot1dBasePortIfIndex};
164              
165 0         0 my $result;
166              
167             # loop over all active vlan ids
168 0         0 foreach my $vlan_id (@active_vlan_ids) {
169              
170             # tagged/untagged ports for this vlan_id
171 0         0 my @tagged_ports;
172             my @untagged_ports;
173              
174             # loop over all possible bridge-ports
175 0         0 foreach my $bridge_port ( sort { $a <=> $b } keys %$bridge_port2if_idx ) {
  0         0  
176 0         0 my $if_idx = $bridge_port2if_idx->{$bridge_port};
177              
178 0 0       0 push @tagged_ports, $if_idx
179             if _is_tagged( $session, $bridge_port, $vlan_id );
180              
181 0 0       0 push @untagged_ports, $if_idx
182             if _is_untagged( $session, $bridge_port, $vlan_id );
183             }
184              
185 0         0 $result->{$vlan_id} = { tagged => \@tagged_ports, untagged => \@untagged_ports };
186             }
187 0         0 return $result;
188             }
189              
190             =head2 B<< OBJ->map_if_idx2vlan_id() >>
191              
192             Returns a hash reference with the interfaces as keys and tagged and untagged vlan-ids as values:
193              
194             {
195             if_idx => {
196             tagged => [vlan_id, ..., ],
197             untagged => [vlan_id, ..., ],
198             },
199              
200             ... ,
201             }
202            
203             =cut
204              
205             sub map_if_idx2vlan_id {
206 1     1 1 837 my $session = shift;
207 1         4 my $agent = $session->hostname;
208              
209 1 50       8 Carp::croak "$agent: '$prefix' not initialized,"
210             unless $session->init_ok($prefix);
211              
212 0         0 my @active_vlan_ids = @{ $session->{$prefix}{activeVlanIds} };
  0         0  
213 0         0 my $bridge_port2if_idx = $session->{$prefix}{dot1dBasePortIfIndex};
214              
215 0         0 my $result = {};
216              
217             # loop over all possible bridge-ports
218 0         0 foreach my $bridge_port ( sort { $a <=> $b } keys %$bridge_port2if_idx ) {
  0         0  
219              
220 0         0 my @tagged_vlans;
221             my @untagged_vlans;
222              
223             # loop over all active vlans
224 0         0 foreach my $vlan_id (@active_vlan_ids) {
225              
226 0 0       0 push @tagged_vlans, $vlan_id
227             if _is_tagged( $session, $bridge_port, $vlan_id );
228              
229 0 0       0 push @untagged_vlans, $vlan_id
230             if _is_untagged( $session, $bridge_port, $vlan_id );
231             }
232              
233 0         0 my $if_idx = $bridge_port2if_idx->{$bridge_port};
234 0         0 $result->{$if_idx} = { tagged => \@tagged_vlans, untagged => \@untagged_vlans };
235             }
236 0         0 return $result;
237             }
238              
239             =head2 B<< OBJ->map_vlan_static_ids2names() >>
240              
241             DEPRECATED: C<< map_vlan_static_ids2names >> is DEPRECATED in favor of C<< map_vlan_id2name >>
242              
243             =cut
244              
245             sub map_vlan_static_ids2names {
246              
247             #Carp::carp('map_vlan_static_ids2names is DEPRECATED in favor of map_vlan_id2name');
248 1     1 1 718 goto &map_vlan_id2name;
249             }
250              
251             =head2 B<< OBJ->map_vlan_static_ids2ports() >>
252              
253             DEPRECATED: C<< map_vlan_static_ids2ports >> is DEPRECATED in favor of C<< map_vlan_id2if_idx >>
254              
255             Returns a hash reference with the vlan-ids as keys and tagged and untagged bridge-port-lists as values:
256              
257             =cut
258              
259             sub map_vlan_static_ids2ports {
260              
261             #Carp::carp('map_vlan_static_ids2ports is DEPRECATED in favor of map_vlan_id2if_idx');
262              
263 1     1 1 696 my $session = shift;
264 1         4 my $agent = $session->hostname;
265              
266 1 50       8 Carp::croak "$agent: '$prefix' not initialized,"
267             unless $session->init_ok($prefix);
268              
269 0         0 my @active_vlan_ids = @{ $session->{$prefix}{activeVlanIds} };
  0         0  
270              
271 0         0 my $result;
272              
273             # loop over all active vlan ids
274 0         0 foreach my $vlan_id (@active_vlan_ids) {
275              
276             # tagged/untagged ports for this vlan_id
277 0         0 my @tagged_ports;
278             my @untagged_ports;
279              
280             # loop over all possible bridge-ports
281 0         0 foreach my $bridge_port ( sort { $a <=> $b } keys %{ $session->{$prefix}{dot1dBasePortIfIndex} } ) {
  0         0  
  0         0  
282              
283 0 0       0 push @tagged_ports, $bridge_port
284             if _is_tagged( $session, $bridge_port, $vlan_id );
285              
286 0 0       0 push @untagged_ports, $bridge_port
287             if _is_untagged( $session, $bridge_port, $vlan_id );
288             }
289              
290 0         0 $result->{$vlan_id} = { tagged => \@tagged_ports, untagged => \@untagged_ports };
291             }
292 0         0 return $result;
293             }
294              
295             =head2 B<< OBJ->map_vlan_static_ports2ids() >>
296              
297             DEPRECATED: C<< map_vlan_static_ports2ids >> is DEPRECATED in favor of C<< map_if_idx2vlan_id >>
298              
299             Returns a hash reference with the bridge-ports as keys and tagged and untagged vlan-ids as values:
300              
301             =cut
302              
303             sub map_vlan_static_ports2ids {
304              
305             #Carp::carp('map_vlan_static_ports2ids is DEPRECATED in favor of map_if_idx2vlan_id');
306              
307 1     1 1 719 my $session = shift;
308 1         4 my $agent = $session->hostname;
309              
310 1 50       7 Carp::croak "$agent: '$prefix' not initialized,"
311             unless $session->init_ok($prefix);
312              
313 0         0 my @active_vlan_ids = @{ $session->{$prefix}{activeVlanIds} };
  0         0  
314              
315 0         0 my $result = {};
316              
317             # loop over all possible bridge-ports
318 0         0 foreach my $bridge_port ( sort { $a <=> $b } keys %{ $session->{$prefix}{dot1dBasePortIfIndex} } ) {
  0         0  
  0         0  
319              
320 0         0 my @tagged_vlans;
321             my @untagged_vlans;
322              
323             # loop over all active vlans
324 0         0 foreach my $vlan_id (@active_vlan_ids) {
325              
326 0 0       0 push @tagged_vlans, $vlan_id
327             if _is_tagged( $session, $bridge_port, $vlan_id );
328              
329 0 0       0 push @untagged_vlans, $vlan_id
330             if _is_untagged( $session, $bridge_port, $vlan_id );
331             }
332              
333 0         0 $result->{$bridge_port} = { tagged => \@tagged_vlans, untagged => \@untagged_vlans };
334             }
335 0         0 return $result;
336             }
337              
338             =head1 INITIALIZATION
339              
340             =head2 B<< OBJ->_init($reload) >>
341              
342             Fetch basic Dot1Q Vlan related snmp values from the host. Don't call this method direct!
343              
344             =cut
345              
346             #
347             # due to the asynchron nature, we don't know what init job is really the last, we decrement
348             # the value after each callback
349             #
350 4     4   36 use constant THIS_INIT_JOBS => 2;
  4         16  
  4         3910  
351              
352             sub _init {
353 4     4   11587 my ( $session, $reload ) = @_;
354 4         18 my $agent = $session->hostname;
355              
356             die "$agent: $prefix already initialized and reload not forced.\n"
357             if exists get_init_slot($session)->{$prefix}
358 4 50 66     42 && get_init_slot($session)->{$prefix} == 0
      33        
359             && not $reload;
360              
361             # set number of async init jobs for proper initialization
362 4         123 get_init_slot($session)->{$prefix} = THIS_INIT_JOBS;
363              
364             # bridge port table to count the number of bridge ports
365 4         50 _fetch_dot1d_base_ports($session);
366              
367 4 100       35 return if $session->error;
368              
369             # initialize the object for current vlan tag infos
370 2         16 _fetch_dot1q_vlan_static_tbl_entries($session);
371 2 50       13 return if $session->error;
372              
373 2         14 return 1;
374             }
375              
376             =head1 PRIVATE METHODS
377              
378             Only for developers or maintainers.
379              
380             =cut
381              
382             sub _fetch_dot1d_base_ports {
383 4     4   9 my $session = shift;
384 4         14 my $result;
385              
386             # fetch the dot1dBasePorts, in blocking or nonblocking mode
387 4 100       26 $result = $session->get_entries(
388             -columns => [ DOT1D_BASE_PORT_IF_INDEX, ],
389              
390             # define callback if in nonblocking mode
391             $session->nonblocking ? ( -callback => \&_dot1d_base_ports_cb ) : (),
392             );
393              
394 4 100       2011078 return unless defined $result;
395 2 50       12 return 1 if $session->nonblocking;
396              
397             # call the callback funktion in blocking mode by hand
398 0         0 _dot1d_base_ports_cb($session);
399              
400             }
401              
402             =head2 B<< _dot1d_base_ports_cb($session) >>
403              
404             The callback for _fetch_dot1d_base_ports.
405              
406             =cut
407              
408             sub _dot1d_base_ports_cb {
409 2     2   2005608 my $session = shift;
410 2         10 my $vbl = $session->var_bind_list;
411              
412 2 50       26 return unless defined $vbl;
413              
414             # mangle result table to get plain idx->value
415              
416 0         0 $session->{$prefix}{dot1dBasePortIfIndex} = idx2val( $vbl, DOT1D_BASE_PORT_IF_INDEX );
417              
418             # this init job is finished
419 0         0 get_init_slot($session)->{$prefix}--;
420              
421 0         0 return 1;
422             }
423              
424             =head2 B<< _fetch_dot1q_vlan_static_tbl_entries() >>
425              
426             Fetch the vlan tag info for current vlans.
427              
428             =cut
429              
430             sub _fetch_dot1q_vlan_static_tbl_entries {
431 2     2   5 my $session = shift;
432 2         4 my $result;
433              
434             # fetch the vlan tag info from dot1qVlanStaticTable
435 2 50       12 $result = $session->get_entries(
    50          
436             -columns => [
437             DOT1Q_VLAN_STATIC_NAME, DOT1Q_VLAN_STATIC_EGRESS_PORTS,
438             DOT1Q_VLAN_STATIC_UNTAGGED_PORTS, DOT1Q_VLAN_STATIC_ROW_STATUS,
439             ],
440              
441             # define callback if in nonblocking mode
442             $session->nonblocking
443             ? ( -callback => \&_dot1q_vlan_static_tbl_entries_cb )
444             : (),
445              
446             # dangerous for snmp version 2c and 3, big values
447             # snmp-error: Message size exceeded buffer maxMsgSize
448             #
449             $session->version ? ( -maxrepetitions => 3 ) : (),
450             );
451              
452 2 50       3016 return unless defined $result;
453 2 50       8 return 1 if $session->nonblocking;
454              
455             # call the callback function in blocking mode by hand
456 0         0 _dot1q_vlan_static_tbl_entries_cb($session);
457              
458             }
459              
460             =head2 B<< _dot1q_vlan_static_tbl_entries_cb($session) >>
461              
462             The callback for _fetch_dot1q_vlan_static_tbl_entries_cb.
463              
464             =cut
465              
466             sub _dot1q_vlan_static_tbl_entries_cb {
467 2     2   987 my $session = shift;
468 2         8 my $vbl = $session->var_bind_list;
469              
470 2 50       22 return unless defined $vbl;
471              
472             # mangle result table to get plain
473             # dot1qVlanIndex => value
474             #
475             $session->{$prefix}{dot1qVlanStaticNames} =
476 0           idx2val( $vbl, DOT1Q_VLAN_STATIC_NAME );
477              
478             # dot1qVlanIndex => dot1qVlanStaticEgressPorts
479             $session->{$prefix}{dot1qVlanStaticEgressPorts} =
480 0           idx2val( $vbl, DOT1Q_VLAN_STATIC_EGRESS_PORTS, );
481              
482             # dot1qVlanIndex => dot1qVlanStaticUntaggedPorts
483             $session->{$prefix}{dot1qVlanStaticUntaggedPorts} =
484 0           idx2val( $vbl, DOT1Q_VLAN_STATIC_UNTAGGED_PORTS, );
485              
486             # dot1qVlanIndex => dot1qVlanStaticRowStatus
487             $session->{$prefix}{dot1qVlanStaticRowStatus} =
488 0           idx2val( $vbl, DOT1Q_VLAN_STATIC_ROW_STATUS, );
489              
490             $session->{$prefix}{activeVlanIds} = [
491 0           grep { $session->{$prefix}{dot1qVlanStaticRowStatus}{$_} == 1 }
492 0           keys %{ $session->{$prefix}{dot1qVlanStaticRowStatus} }
  0            
493             ];
494              
495 0           _calc_tagged_untagged_ports($session);
496              
497             # this init job is finished
498 0           get_init_slot($session)->{$prefix}--;
499              
500 0           return 1;
501             }
502              
503             # Process tag/untag information for each bridge base port
504             # once during object initialization.
505             sub _calc_tagged_untagged_ports {
506 0     0     my $session = shift;
507              
508             # calculate the tagged ports for each vlan
509             # this is a XOR function: egress ^ untagged
510              
511             # for all vlans
512 0           foreach my $vlan ( @{ $session->{$prefix}{activeVlanIds} } ) {
  0            
513              
514             # calculate the tagged ports for each vlan
515             # this is a XOR function: egress ^ untagged
516             #
517 0           my $egress_ports = $session->{$prefix}{dot1qVlanStaticEgressPorts}{$vlan};
518             my $untagged_ports =
519 0           $session->{$prefix}{dot1qVlanStaticUntaggedPorts}{$vlan};
520              
521             # It's importend, that the returned SNMP OCTET_STRINGs
522             # were untranslated by Net::SNMP!
523             # if already translated, we must reconvert it to a
524             # pure OCTET-STRING.
525              
526 0           $egress_ports = hex2octet($egress_ports);
527 0           $untagged_ports = hex2octet($untagged_ports);
528              
529 0           my $tagged_ports = $egress_ports ^ $untagged_ports;
530              
531             # convert to bit-string
532 0           $session->{$prefix}{TaggedPorts}{$vlan} = unpack( 'B*', $tagged_ports );
533 0           $session->{$prefix}{UntaggedPorts}{$vlan} =
534             unpack( 'B*', $untagged_ports );
535             }
536             }
537              
538             # returns true if $vlan_id is tagged on $bride_port
539             sub _is_tagged {
540 0     0     my ( $session, $bridge_port, $vlan_id ) = @_;
541              
542 0 0         die "missing attribute 'bridge_port'" unless defined $bridge_port;
543 0 0         die "missing attribute 'vlan_id'" unless defined $vlan_id;
544              
545             # it's a bitstring, see the subroutine _calc_tagged_untagged_ports
546             # substr() counts from 0, bridge_ports from 1
547             my $is_tagged =
548 0           substr( $session->{$prefix}{TaggedPorts}{$vlan_id}, $bridge_port - 1, 1 );
549              
550 0 0         return 1 if $is_tagged;
551 0           return;
552             }
553              
554             # returns true if $vlan_id is untagged on $bride_port
555             sub _is_untagged {
556 0     0     my ( $session, $bridge_port, $vlan_id ) = @_;
557              
558 0 0         die "missing attribute 'bridge_port'" unless defined $bridge_port;
559 0 0         die "missing attribute 'vlan_id'" unless defined $vlan_id;
560              
561             # it's a bitstring, see the subroutine _calc_tagged_untagged_ports
562             # substr() counts from 0, bridge_ports from 1
563 0           my $is_untagged = substr( $session->{$prefix}{UntaggedPorts}{$vlan_id}, $bridge_port - 1, 1 );
564              
565 0 0         return 1 if $is_untagged;
566 0           return;
567             }
568              
569             =head1 SEE ALSO
570              
571             L<< Net::SNMP::Mixin::Dot1dBase >> for a mapping between ifIndexes and dot1dBasePorts.
572              
573             =head1 REQUIREMENTS
574              
575             L<< Net::SNMP >>, L<< Net::SNMP::Mixin >>
576              
577             =head1 BUGS, PATCHES & FIXES
578              
579             There are no known bugs at the time of this release. However, if you spot a bug or are experiencing difficulties that are not explained within the POD documentation, please submit a bug to the RT system (see link below). However, it would help greatly if you are able to pinpoint problems or even supply a patch.
580              
581             Fixes are dependant upon their severity and my availablity. Should a fix not be forthcoming, please feel free to (politely) remind me by sending an email to gaissmai@cpan.org .
582              
583             RT: http://rt.cpan.org/Public/Dist/Display.html?Name=Net-SNMP-Mixin-Dot1qVlanStatic
584              
585             =head1 AUTHOR
586              
587             Karl Gaissmaier
588              
589             =head1 COPYRIGHT & LICENSE
590              
591             Copyright 2008-2020 Karl Gaissmaier, all rights reserved.
592              
593             This program is free software; you can redistribute it and/or modify it
594             under the same terms as Perl itself.
595              
596             =cut
597              
598             unless ( caller() ) {
599             print "$prefix compiles and initializes successful.\n";
600             }
601              
602             1;
603              
604             # vim: sw=2