File Coverage

blib/lib/Net/SNMP/Mixin/Dot1qVlanStatic.pm
Criterion Covered Total %
statement 76 172 44.1
branch 23 70 32.8
condition 3 6 50.0
subroutine 20 23 86.9
pod 6 6 100.0
total 128 277 46.2


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