File Coverage

blib/lib/Net/SNMP/Mixin/CiscoDot1qVlanStatic.pm
Criterion Covered Total %
statement 76 167 45.5
branch 30 74 40.5
condition 4 8 50.0
subroutine 18 21 85.7
pod 3 3 100.0
total 131 273 47.9


line stmt bran cond sub pod time code
1             package Net::SNMP::Mixin::CiscoDot1qVlanStatic;
2              
3 4     4   503867 use strict;
  4         12  
  4         127  
4 4     4   23 use warnings;
  4         10  
  4         152  
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   21 use Carp ();
  4         10  
  4         108  
17              
18 4     4   27 use Net::SNMP::Mixin::Util qw/idx2val hex2octet push_error get_init_slot/;
  4         8  
  4         33  
19              
20             #
21             # this module export config
22             #
23             my @mixin_methods;
24              
25             BEGIN {
26 4     4   2351 @mixin_methods = qw/
27             map_vlan_id2name
28             map_vlan_id2if_idx
29             map_if_idx2vlan_id
30             /;
31             }
32              
33 4         43 use Sub::Exporter -setup => {
34             exports => [@mixin_methods],
35             groups => { default => [@mixin_methods], },
36 4     4   31 };
  4         8  
37              
38             #
39             # SNMP oid constants from CISCO-VTP-MIB and CISCO-VLAN-MEMBERSHIP-MIB
40             #
41             use constant {
42             ###
43             # trunk ports: CISCO-VTP-MIB
44 4         1797 VLAN_TBL => '1.3.6.1.4.1.9.9.46.1.3.1',
45             VLAN_STATE => '1.3.6.1.4.1.9.9.46.1.3.1.1.2',
46             VLAN_NAME => '1.3.6.1.4.1.9.9.46.1.3.1.1.4',
47              
48             VLAN_TRUNK_PORT_TBL => '1.3.6.1.4.1.9.9.46.1.6.1',
49             VLAN_TRUNK_PORT_VLANS_ENABLED_1K => '1.3.6.1.4.1.9.9.46.1.6.1.1.4',
50             VLAN_TRUNK_PORT_NATIVE_VLAN => '1.3.6.1.4.1.9.9.46.1.6.1.1.5',
51             VLAN_TRUNK_PORT_ENCAPS_OPER_TYPE => '1.3.6.1.4.1.9.9.46.1.6.1.1.16',
52             VLAN_TRUNK_PORT_VLANS_ENABLED_2K => '1.3.6.1.4.1.9.9.46.1.6.1.1.17',
53             VLAN_TRUNK_PORT_VLANS_ENABLED_3K => '1.3.6.1.4.1.9.9.46.1.6.1.1.18',
54             VLAN_TRUNK_PORT_VLANS_ENABLED_4K => '1.3.6.1.4.1.9.9.46.1.6.1.1.19',
55              
56             ###
57             # access ports: CISCO-VLAN-MEMBERSHIP-MIB
58             # table maybe empty!
59             #
60             # "A table for configuring VLAN port membership.
61             # There is one row for each bridge port that is
62             # assigned to a static or dynamic access port. Trunk
63             # ports are not represented in this table. An entry
64             # may be created and deleted when ports are created or
65             # deleted via SNMP or the management console on a
66             # device."
67              
68             VM_MEMBERSHIP_TABLE => '1.3.6.1.4.1.9.9.68.1.2.2',
69             VM_VLAN_TYPE => '1.3.6.1.4.1.9.9.68.1.2.2.1.1',
70             VM_VLAN => '1.3.6.1.4.1.9.9.68.1.2.2.1.2',
71 4     4   2317 };
  4         11  
72              
73             =head1 NAME
74              
75             Net::SNMP::Mixin::CiscoDot1qVlanStatic - mixin class for static Cisco vlan info
76              
77             =cut
78              
79             our $VERSION = '0.10';
80              
81             =head1 SYNOPSIS
82              
83             use Net::SNMP;
84             use Net::SNMP::Mixin;
85              
86             # initialize session and mixin library
87             my $session = Net::SNMP->session( -hostname => 'foo.bar.com' );
88             $session->mixer('Net::SNMP::Mixin::CiscoDot1qVlanStatic');
89             $session->init_mixins;
90             snmp_dispatcher();
91             $session->init_ok();
92             die $session->errors if $session->errors;
93              
94             # show VLAN IDs and corresponding names
95             my $id2name = $session - map_vlan_id2name();
96             foreach my $id ( keys %{$id2name} ) {
97             printf "Vlan-Id: %4d => Vlan-Name: %s\n", $id, $id2name->{$id};
98             }
99              
100             # sorted by vlan_id
101             my $vlan_ids2if_idx = $session->map_vlan_id2if_idx();
102             foreach my $id ( keys %{$vlan_ids2if_idx} ) {
103             printf "Vlan-Id: %4d\n", $id;
104             printf "\tTagged-Ports: %s\n", ( join ',', @{ $vlan_ids2if_idx->{$id}{tagged} } );
105             printf "\tUntagged-Ports: %s\n", ( join ',', @{ $vlan_ids2if_idx->{$id}{untagged} } );
106             }
107              
108             # sorted by interface
109             my $ports2ids = $session->map_if_idx2vlan_id();
110             foreach my $if_idx ( keys %{$ports2ids} ) {
111             printf "Interface: %10d\n", $if_idx;
112             printf "\tTagged-Vlans: %s\n", ( join ',', @{ $ports2ids->{$if_idx}{tagged} } );
113             printf "\tUntagged-Vlans: %s\n", ( join ',', @{ $ports2ids->{$if_idx}{untagged} } );
114             }
115              
116             =head1 DESCRIPTION
117              
118             A mixin class for vlan related infos from the CISCO-VTP-MIB for 802.1Q-trunks and
119             CISCO-VLAN-MEMBERSHIP-MIB for access ports.
120             The mixin-module provides methods for mapping between vlan-ids and vlan-names und relations between
121             interfaces and vlan-ids, tagged or untagged on these ports.
122              
123             =head1 MIXIN METHODS
124              
125             =head2 B<< OBJ->map_vlan_id2name() >>
126              
127             Returns a hash reference with vlan-ids as keys and the corresponding vlan-names as values:
128              
129             {
130             vlan_id => vlan_name,
131             vlan_id => vlan_name,
132             ... ,
133             }
134              
135             =cut
136              
137             sub map_vlan_id2name {
138 1     1 1 30341 my $session = shift;
139             Carp::croak "'$prefix' not initialized,"
140 1 50       89 unless $session->{$prefix}{__initialized};
141              
142 0         0 return $session->{$prefix}{vlan_id2name};
143             }
144              
145             =head2 B<< OBJ->map_vlan_id2if_idx() >>
146              
147             Returns a hash reference with the vlan-ids as keys and tagged and untagged if_idx as values:
148              
149             {
150             vlan_id => {
151             tagged => [if_idx, ..., ],
152             untagged => [if_idx, ..., ],
153             },
154              
155             ... ,
156             }
157            
158             =cut
159              
160             sub map_vlan_id2if_idx {
161 1     1 1 595 my $session = shift;
162             Carp::croak "'$prefix' not initialized,"
163 1 50       84 unless $session->{$prefix}{__initialized};
164              
165 0         0 return _get_vlan_ids2if_idx($session);
166             }
167              
168             =head2 B<< OBJ->map_if_idx2vlan_id() >>
169              
170             Returns a hash reference with the interfaces as keys and tagged and untagged vlan-ids as values:
171              
172             {
173             if_idx => {
174             tagged => [vlan_id, ..., ],
175             untagged => [vlan_id, ..., ],
176             },
177              
178             ... ,
179             }
180            
181             =cut
182              
183             sub map_if_idx2vlan_id {
184 1     1 1 569 my $session = shift;
185             Carp::croak "'$prefix' not initialized,"
186 1 50       82 unless $session->{$prefix}{__initialized};
187              
188 0         0 return _get_if_idx2vlan_ids($session);
189             }
190              
191             =head1 INITIALIZATION
192              
193             =head2 B<< OBJ->_init($reload) >>
194              
195             Fetch basic Vlan related SNMP values from the host. Don't call this method direct!
196              
197             =cut
198              
199             #
200             # due to the asynchron nature, we don't know what init job is really the last, we decrement
201             # the value after each callback
202             #
203 4     4   34 use constant THIS_INIT_JOBS => 3;
  4         19  
  4         7725  
204              
205             sub _init {
206 4     4   11490 my ( $session, $reload ) = @_;
207              
208 4         49 my $agent = $session->hostname;
209              
210             die "$agent: $prefix already initialized and reload not forced.\n"
211             if exists get_init_slot($session)->{$prefix}
212 4 50 66     33 && get_init_slot($session)->{$prefix} == 0
      33        
213             && not $reload;
214              
215             # set number of async init jobs for proper initialization
216 4         141 get_init_slot($session)->{$prefix} = THIS_INIT_JOBS;
217              
218             # initialize the object for vtp vlan table
219 4         65 _fetch_vtp_vlan_tbl_entries($session);
220 4 100       35 return if $session->error;
221              
222             # initialize the object for vtp vlan trunk port table for trunk ports
223 2         24 _fetch_vtp_vlan_trunk_port_tbl_entries($session);
224 2 50       28 return if $session->error;
225              
226             # initialize the object for vlan membership table of access ports
227 2         29 _fetch_vm_membership_tbl_entries($session);
228 2 50       20 return if $session->error;
229              
230 2         21 return 1;
231             }
232              
233             =head1 PRIVATE METHODS
234              
235             Only for developers or maintainers.
236              
237             =head2 B<< _fetch_vtp_vlan_tbl_entries($session) >>
238              
239             Fetch selected rows from vtpVlanTable during object initialization.
240              
241             =cut
242              
243             sub _fetch_vtp_vlan_tbl_entries {
244 4     4   9 my $session = shift;
245 4         21 my $result;
246              
247             # fetch the vlan state and name from vlanTable
248 4 100       32 $result = $session->get_entries(
    50          
249             -columns => [ VLAN_STATE, VLAN_NAME, ],
250              
251             # define callback if in nonblocking mode
252             $session->nonblocking
253             ? ( -callback => \&_vtp_vlan_tbl_entries_cb )
254             : (),
255              
256             # dangerous for snmp version 2c and 3, big values
257             # snmp-error: Message size exceeded buffer maxMsgSize
258             #
259             $session->version ? ( -maxrepetitions => 3 ) : (),
260             );
261              
262 4 100       2013342 unless ( defined $result ) {
263 2 50       19 if ( my $err_msg = $session->error ) {
264 2         68 push_error( $session, "$prefix: $err_msg" );
265             }
266 2         144 return;
267             }
268              
269             # in nonblocking mode the callback will be called asynchronously
270 2 50       12 return 1 if $session->nonblocking;
271              
272             # call the callback function in blocking mode by hand
273 0         0 _vtp_vlan_tbl_entries_cb($session);
274              
275             }
276              
277             =head2 B<< _vtp_vlan_tbl_entries_cb($session) >>
278              
279             The callback for _fetch_vtp_vlan_tbl_entries.
280              
281             =cut
282              
283             sub _vtp_vlan_tbl_entries_cb {
284 2     2   2006126 my $session = shift;
285 2         12 my $vbl = $session->var_bind_list;
286              
287 2 50       33 unless ( defined $vbl ) {
288 2 50       14 if ( my $err_msg = $session->error ) {
289 2         39 push_error( $session, "$prefix: $err_msg" );
290             }
291 2         95 return;
292             }
293              
294             # mangle result table to get plain
295             # VlanIndex => vlan-state
296             #
297 0         0 $session->{$prefix}{_VlanState} = idx2val( $vbl, VLAN_STATE, 1 );
298              
299             # mangle result table to get plain
300             # VlanIndex => vlan-name
301             #
302 0         0 $session->{$prefix}{vlan_id2name} = idx2val( $vbl, VLAN_NAME, 1 );
303              
304             # purge non operational vlans, see CISCO-VTP-MIB
305 0         0 foreach my $vlan_id ( keys %{ $session->{$prefix}{vlan_id2name} } ) {
  0         0  
306             delete $session->{$prefix}{vlan_id2name}{$vlan_id}
307 0 0       0 unless $session->{$prefix}{_VlanState}{$vlan_id} == 1;
308             }
309              
310             # this init job is finished
311 0         0 get_init_slot($session)->{$prefix}--;
312              
313 0         0 return 1;
314             }
315              
316             =head2 B<< _fetch_vtp_vlan_trunk_port_tbl_entries($session) >>
317              
318             Fetch selected rows from vlanTrunkPortTable during object initialization.
319              
320             =cut
321              
322             sub _fetch_vtp_vlan_trunk_port_tbl_entries {
323 2     2   8 my $session = shift;
324 2         6 my $result;
325              
326             # fetch selected entries from vlanTrunkPortTable
327 2 50       17 $result = $session->get_entries(
    50          
328             -columns => [
329             VLAN_TRUNK_PORT_ENCAPS_OPER_TYPE,
330             VLAN_TRUNK_PORT_NATIVE_VLAN,
331              
332             VLAN_TRUNK_PORT_VLANS_ENABLED_1K,
333             VLAN_TRUNK_PORT_VLANS_ENABLED_2K,
334             VLAN_TRUNK_PORT_VLANS_ENABLED_3K,
335             VLAN_TRUNK_PORT_VLANS_ENABLED_4K,
336             ],
337              
338             # define callback if in nonblocking mode
339             $session->nonblocking ? ( -callback => \&_vtp_vlan_trunk_port_tbl_entries_cb ) : (),
340              
341             # dangerous for snmp version 2c and 3, big values
342             # snmp-error: Message size exceeded buffer maxMsgSize
343             #
344             $session->version ? ( -maxrepetitions => 3 ) : (),
345             );
346              
347 2 50       5562 unless ( defined $result ) {
348 0 0       0 if ( my $err_msg = $session->error ) {
349 0         0 push_error( $session, "$prefix: $err_msg" );
350             }
351 0         0 return;
352             }
353              
354             # in nonblocking mode the callback will be called asynchronously
355 2 50       12 return 1 if $session->nonblocking;
356              
357             # call the callback function in blocking mode by hand
358 0         0 _vtp_vlan_trunk_port_tbl_entries_cb($session);
359              
360             }
361              
362             =head2 B<< _vtp_vlan_trunk_port_tbl_entries_cb($session) >>
363              
364             The callback for _fetch_vtp_vlan_trunk_port_tbl_entries.
365              
366             =cut
367              
368             sub _vtp_vlan_trunk_port_tbl_entries_cb {
369 2     2   1193 my $session = shift;
370 2         10 my $vbl = $session->var_bind_list;
371              
372 2 50       29 unless ( defined $vbl ) {
373 2 50       9 if ( my $err_msg = $session->error ) {
374 2         27 push_error( $session, "$prefix: $err_msg" );
375             }
376 2         73 return;
377             }
378              
379             # mangle result table to get plain
380             # ifIndex => vlans-enabled-bitstring
381             #
382             $session->{$prefix}{_VlansEnabled1k} =
383 0         0 idx2val( $vbl, VLAN_TRUNK_PORT_VLANS_ENABLED_1K, );
384              
385             $session->{$prefix}{_VlansEnabled2k} =
386 0         0 idx2val( $vbl, VLAN_TRUNK_PORT_VLANS_ENABLED_2K, );
387              
388             $session->{$prefix}{_VlansEnabled3k} =
389 0         0 idx2val( $vbl, VLAN_TRUNK_PORT_VLANS_ENABLED_3K, );
390              
391             $session->{$prefix}{_VlansEnabled4k} =
392 0         0 idx2val( $vbl, VLAN_TRUNK_PORT_VLANS_ENABLED_4K, );
393              
394             $session->{$prefix}{_VlansEncapsOperType} =
395 0         0 idx2val( $vbl, VLAN_TRUNK_PORT_ENCAPS_OPER_TYPE, );
396              
397             $session->{$prefix}{NativeVlan} =
398 0         0 idx2val( $vbl, VLAN_TRUNK_PORT_NATIVE_VLAN, );
399              
400 0         0 $session->{$prefix}{__initialized}++;
401              
402 0         0 _calc_tagged_ports($session);
403              
404             # this init job is finished
405 0         0 get_init_slot($session)->{$prefix}--;
406              
407 0         0 return 1;
408             }
409              
410             sub _calc_tagged_ports {
411 0     0   0 my $session = shift;
412              
413             # prepare fillmask, see below
414 0         0 my $zeroes_1k = pack( 'B*', 0 x 1024 );
415              
416             # iterate over any hash to get the interfaces as keys
417 0         0 foreach my $if_idx ( keys %{ $session->{$prefix}{NativeVlan} } ) {
  0         0  
418              
419             # only dot1Q(4) is supported, see CISCO-VTP-MIB
420 0 0       0 if ( $session->{$prefix}{_VlansEncapsOperType}{$if_idx} != 4 ) {
421 0         0 $session->{$prefix}{TaggedVlans}{$if_idx} = undef;
422 0         0 next;
423             }
424              
425             # for all phys interfaces get the tagged vlans
426             # represented in OCTET-STRINGS
427              
428 0         0 my $vlans_1k = $session->{$prefix}{_VlansEnabled1k}{$if_idx};
429 0         0 my $vlans_2k = $session->{$prefix}{_VlansEnabled2k}{$if_idx};
430 0         0 my $vlans_3k = $session->{$prefix}{_VlansEnabled3k}{$if_idx};
431 0         0 my $vlans_4k = $session->{$prefix}{_VlansEnabled4k}{$if_idx};
432              
433             # It's important that the returned SNMP OCTET-STRINGs were untranslated by Net::SNMP!
434             # If already translated, we must convert it back to a pure OCTET-STRING
435             # and fill it with zeroes to a length of 128-OCTETS = 1024-BITS
436              
437 0         0 my $vlans_1k_octets = hex2octet($vlans_1k) ^ $zeroes_1k;
438 0         0 my $vlans_2k_octets = hex2octet($vlans_2k) ^ $zeroes_1k;
439 0         0 my $vlans_3k_octets = hex2octet($vlans_3k) ^ $zeroes_1k;
440 0         0 my $vlans_4k_octets = hex2octet($vlans_4k) ^ $zeroes_1k;
441              
442             # unpack it into a bit-string
443 0         0 my $vlans_1k_bits = unpack( 'B*', $vlans_1k_octets );
444 0         0 my $vlans_2k_bits = unpack( 'B*', $vlans_2k_octets );
445 0         0 my $vlans_3k_bits = unpack( 'B*', $vlans_3k_octets );
446 0         0 my $vlans_4k_bits = unpack( 'B*', $vlans_4k_octets );
447              
448             # concat all 4k possible vlan_ids as bitstring
449 0         0 $session->{$prefix}{TaggedVlans}{$if_idx} =
450             $vlans_1k_bits . $vlans_2k_bits . $vlans_3k_bits . $vlans_4k_bits;
451             }
452              
453             }
454              
455             =head2 B<< _fetch_vm_membership_tbl_entries($session) >>
456              
457             Fetch selected rows from vmMembershipTable during object initialization.
458             The table maybe empty if there is no switch port in access mode.
459              
460             =cut
461              
462             sub _fetch_vm_membership_tbl_entries {
463 2     2   6 my $session = shift;
464 2         5 my $result;
465              
466             # fetch selected entries from vmMembershipTable
467 2 50       12 $result = $session->get_entries(
    50          
468             -columns => [ VM_VLAN_TYPE, VM_VLAN ],
469              
470             # define callback if in nonblocking mode
471             $session->nonblocking ? ( -callback => \&_vm_membership_tbl_entries_cb ) : (),
472              
473             # dangerous for snmp version 2c and 3, big values
474             # snmp-error: Message size exceeded buffer maxMsgSize
475             #
476             $session->version ? ( -maxrepetitions => 3 ) : (),
477             );
478              
479 2 50       3608 unless ( defined $result ) {
480 0 0       0 if ( my $err_msg = $session->error ) {
481 0         0 push_error( $session, "$prefix: $err_msg" );
482             }
483 0         0 return;
484             }
485              
486             # in nonblocking mode the callback will be called asynchronously
487 2 50       14 return 1 if $session->nonblocking;
488              
489             # call the callback function in blocking mode by hand
490 0         0 _vm_membership_tbl_entries_cb($session);
491              
492             }
493              
494             =head2 B<< _vm_membership_tbl_entries_cb($session) >>
495              
496             The callback for _fetch_vm_membership_tbl_entries.
497              
498             =cut
499              
500             sub _vm_membership_tbl_entries_cb {
501 2     2   1108 my $session = shift;
502 2         10 my $vbl = $session->var_bind_list;
503              
504 2 50       25 unless ( defined $vbl ) {
505 2   50     12 my $err_msg = $session->error // '';
506              
507 2 50       20 if ( $err_msg !~ m/The requested entries are empty or do not exist/i ) {
508 2 50       19 push_error( $session, "$prefix: $err_msg" ) if defined $err_msg;
509             }
510             else {
511             # the table maybe empty if the device has no access ports
512 0         0 $session->{$prefix}{AccessVlan} = {};
513              
514             # this init slot is finished even if table is empty!
515 0         0 get_init_slot($session)->{$prefix}--;
516             }
517              
518 2         64 return;
519             }
520              
521             # mangle result table to get plain
522             # ifIndex => values
523             #
524             $session->{$prefix}{_VlanType} =
525 0           idx2val( $vbl, VM_VLAN_TYPE, );
526              
527             $session->{$prefix}{_VlanId} =
528 0           idx2val( $vbl, VM_VLAN, );
529              
530 0           foreach my $if_idx ( keys %{ $session->{$prefix}{_VlanType} } ) {
  0            
531              
532             # only static(1) vlans are supported, see CISCO-VLAN-MEMBERSHIP-MIB
533 0 0         next if $session->{$prefix}{_VlanType}{$if_idx} != 1;
534              
535 0           $session->{$prefix}{AccessVlan}{$if_idx} = $session->{$prefix}{_VlanId}{$if_idx};
536             }
537              
538             # this init job is finished
539 0           get_init_slot($session)->{$prefix}--;
540              
541 0           return 1;
542             }
543              
544             # Process tagged/untagged ports for each vlan
545             sub _get_vlan_ids2if_idx {
546 0     0     my $session = shift;
547              
548 0           my $result;
549 0           foreach my $vlan_id ( sort keys %{ $session->{$prefix}{vlan_id2name} } ) {
  0            
550 0           $result->{$vlan_id}{tagged} = [];
551 0           $result->{$vlan_id}{untagged} = [];
552              
553             # iterate over any hash from VTP table to get all interfaces
554 0           foreach my $if_idx ( sort keys %{ $session->{$prefix}{NativeVlan} } ) {
  0            
555              
556             # access ports
557 0 0         if ( my $access_vlan = $session->{$prefix}{AccessVlan}{$if_idx} ) {
558 0 0         push( @{ $result->{$vlan_id}{untagged} }, $if_idx ) if $access_vlan == $vlan_id;
  0            
559              
560             # next interface
561 0           next;
562             }
563              
564             # trunk ports
565 0 0         next unless defined $session->{$prefix}{TaggedVlans}{$if_idx};
566              
567 0 0         if ( substr( $session->{$prefix}{TaggedVlans}{$if_idx}, $vlan_id, 1 ) eq 1 ) {
568 0 0         if ( $session->{$prefix}{NativeVlan}{$if_idx} != $vlan_id ) {
569              
570             # ... and it's not the native vlan of this trunk
571 0           push @{ $result->{$vlan_id}{tagged} }, $if_idx;
  0            
572             }
573             else {
574             # ... it's the native vlan of this trunk
575 0           push @{ $result->{$vlan_id}{untagged} }, $if_idx;
  0            
576             }
577             }
578             }
579             }
580              
581 0           return $result;
582             }
583              
584             # Process tagged/untagged vlans for each interface
585             #
586             # reverse datastructure vlan_ids to ports ==> ports to vlan ids
587             #
588             # FROM:
589             # vlan_id => {
590             # tagged => [if_idx, ..., ],
591             # untagged => [if_idx, ..., ],
592             # },
593             #
594             # TO:
595             # if_idx => {
596             # tagged => [vlan_id, ..., ],
597             # untagged => [vlan_id, ..., ],
598             # },
599             #
600             sub _get_if_idx2vlan_ids {
601 0     0     my $vlan_ids2if_idx = _get_vlan_ids2if_idx(shift);
602              
603 0           my $result;
604 0           foreach my $vlan_id ( keys %$vlan_ids2if_idx ) {
605 0           foreach my $if_idx ( @{ $vlan_ids2if_idx->{$vlan_id}{tagged} } ) {
  0            
606 0           push @{ $result->{$if_idx}{tagged} }, $vlan_id;
  0            
607             }
608 0           foreach my $if_idx ( @{ $vlan_ids2if_idx->{$vlan_id}{untagged} } ) {
  0            
609 0           push @{ $result->{$if_idx}{untagged} }, $vlan_id;
  0            
610             }
611             }
612 0           return $result;
613             }
614              
615             =head1 REQUIREMENTS
616              
617             L<< Net::SNMP >>, L<< Net::SNMP::Mixin >>
618              
619             =head1 AUTHOR
620              
621             Karl Gaissmaier
622              
623             =head1 COPYRIGHT & LICENSE
624              
625             Copyright 2020-2021 Karl Gaissmaier, all rights reserved.
626              
627             This program is free software; you can redistribute it and/or modify it
628             under the same terms as Perl itself.
629              
630             =cut
631              
632             unless ( caller() ) {
633             print "$prefix compiles and initializes successful.\n";
634             }
635              
636             1;
637              
638             # vim: sw=2