File Coverage

blib/lib/NetPacket/SpanningTree.pm
Criterion Covered Total %
statement 18 53 33.9
branch 0 8 0.0
condition n/a
subroutine 6 9 66.6
pod n/a
total 24 70 34.2


line stmt bran cond sub pod time code
1             #
2             # NetPacket::SpanningTree - Decode and encode spanning tree protocol packets
3             #
4             # Comments/suggestions to cganesan@cpan.org
5             #
6              
7             package NetPacket::SpanningTree;
8              
9             #
10             # Copyright (c) 2002 Chander Ganesan.
11             #
12             # This package is free software and is provided "as is" without express
13             # or implied warranty. It may be used, redistributed and/or modified
14             # under the terms of the Perl Artistic License (see
15             # http://www.perl.com/perl/misc/Artistic.html)
16             #
17             # This software and all associated data and documentation
18             # ('Software') is available free of charge. You may make copies of the
19             # Software but you must include all of this notice on any copy.
20             #
21             # The Software was developed for research purposes does not
22             # warrant that it is error free or fit for any purpose. The author
23             # disclaims any liability for all claims, expenses, losses, damages
24             # and costs any user may incur as a result of using, copying or
25             # modifying the Software.
26             #
27              
28 1     1   18866 use 5.006;
  1         4  
  1         198  
29 1     1   9 use strict;
  1         1  
  1         40  
30 1     1   6 use warnings;
  1         15  
  1         292  
31 1     1   6 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         131  
32              
33             my $myclass;
34             BEGIN {
35 1     1   3 $myclass = __PACKAGE__;
36 1         132 $VERSION = "0.01";
37             }
38 0     0     sub Version () { "$myclass v$VERSION" }
39              
40             BEGIN {
41 1     1   24 @ISA = qw(Exporter NetPacket);
42              
43             # Items to export into callers namespace by default
44             # (move infrequently used names to @EXPORT_OK below)
45              
46 1         2 @EXPORT = qw(
47             );
48              
49             # Other items we are prepared to export if requested
50              
51 1         2 @EXPORT_OK = qw(st_strip
52             );
53              
54             # Tags:
55              
56 1         3140 %EXPORT_TAGS = (
57             ALL => [@EXPORT, @EXPORT_OK],
58             strip => [qw(st_strip)],
59             types => [qw(
60             )],
61             );
62              
63             }
64              
65             #
66             # Decode the packet
67             #
68              
69             sub decode {
70 0     0     my $class = shift;
71 0           my($pkt, $parent, @rest) = @_;
72 0           my $self = {};
73              
74             # Class fields
75              
76 0           $self->{_parent} = $parent;
77 0           $self->{_frame} = $pkt;
78              
79             # Decode Spanning Tree packet
80              
81 0 0         if (defined($pkt)) {
82 0           my ($root_pri, $bridge_pri, $port_pri, $port_num, $root_path_cost,
83             $message_age, $max_age, $bpdu_type,
84             $fwd_delay, $hello_time ,
85             $root_mac, $bridge_mac, $version, $version_length);
86            
87 0           ($self->{protocol_id}, $version, $bpdu_type,
88             $self->{bpdu_flags},
89             $root_pri, $self->{root_mac}, $self->{root_path_cost},
90             $bridge_pri, $bridge_mac, $port_pri, $port_num,
91             $message_age, $max_age, $hello_time, $fwd_delay, $version_length) =
92             unpack('nH2H2a1H4H12NH4H12H2H2nnnnH2' , $pkt);
93             # print "unpacked: " . unpack ("H*" , $pkt) . "\n";
94 0           $self->{protocol_version} = hex($version);
95 0           $self->{bpdu_type} = hex ($bpdu_type);
96 0 0         if ($self->{bpdu_type} != 128) { # This isn't a topology change...
97 0 0         if ($self->{bpdu_type} == 2) { # Rapid...
    0          
98 0           my ($prole1, $prole2);
99 0           $self->{version_1_length} = hex($version_length);
100 0           ($self->{topology_change_ack},
101             $self->{agreement},
102             $self->{forwarding},
103             $self->{learning},
104             $prole1,
105             $prole2,
106             $self->{proposal},
107             $self->{topology_change}) =
108             split //, unpack ("B*", $self->{bpdu_flags});
109 0           $self->{port_role} = $prole1*2 + $prole2;
110             } elsif ($self->{bpdu_type} == 0) {
111 0           ($self->{topology_change_ack},
112             undef, undef, undef, undef, undef, undef,
113             $self->{topology_change}) =
114             split //, unpack ("B*", $self->{bpdu_flags});
115             }
116 0           $self->{data} = undef;
117 0           $self->{root_priority} = hex($root_pri);
118 0           $self->{root_id} = $root_pri . $self->{root_mac};
119 0           $self->{bridge_id} = $bridge_pri . $bridge_mac;
120 0           $self->{bridge_priority} = hex($bridge_pri);
121 0           $self->{bridge_mac} = $bridge_mac;
122 0           $self->{port_priority} = hex($port_pri);
123 0           $self->{port_num} = hex($port_num);
124 0           $self->{port_id} = sprintf("%02lx%02lx", hex($port_pri) , hex($port_num));
125            
126            
127 0           $self->{bpdu_flags} = unpack ("B*", $self->{bpdu_flags});
128            
129 0           $self->{message_age} = $message_age/256;
130 0           $self->{max_age} = $max_age/256;
131 0           $self->{forward_delay} = $fwd_delay/256;
132 0           $self->{hello_time} = $hello_time/256;
133             }
134             }
135             # Return a blessed object
136            
137 0           bless($self, $class);
138 0           return $self;
139             }
140              
141             #
142             # Strip header from packet and return the data contained in it. Spanning
143             # Tree packets contain no encapsulated data.
144             #
145              
146             undef &st_strip; # Create st_strip alias
147             *st_strip = \&strip;
148              
149             sub strip {
150 0     0     return undef;
151             }
152              
153             #
154             # Encode a packet
155             #
156              
157             sub encode {
158             my ($self, $data) = @_;
159             my $defaults = {
160             protocol_id => 0,
161             protocol_version => 0,
162             bpdu_type => 0,
163             topology_change_ack => 1,
164             root_priority => 32768,
165             bridge_priority => 32768,
166             port_priority => 128,
167             port_num => 1,
168             root_mac => "000000000000",
169             root_path_cost => 10,
170             bridge_mac => "000000000000",
171             message_age => 0,
172             max_age => 20,
173             hello_time => 2,
174             forward_delay => 15,
175             topology_change => 0,
176             };
177              
178             my $packStruct = {
179             1 => { 'protocol_id' => 'n' },
180             2 => { 'protocol_version' => 'H2' },
181             3 => { 'bpdu_type' => 'H2' },
182             4 => { 'bpdu_flags' => 'B8' },
183             5 => { 'root_priority' => 'n' },
184             6 => { 'root_mac' => 'H12' },
185             7 => { 'root_path_cost' => 'N', },
186             8 => { 'bridge_priority' => 'n', },
187             9 => { 'bridge_mac' => 'H12', },
188             10 => { 'port_id' => 'H4', },
189             12 => { 'message_age' => 'n', },
190             13 => { 'max_age' => 'n', },
191             14 => { 'hello_time' => 'n', },
192             15 => { 'forward_delay' => 'n', },
193             };
194              
195              
196             #
197             # Ensure all required parameters are passed, and those that aren't
198             # are defaulted.
199             #
200             foreach my $name (keys %$defaults) {
201             if (defined $data->{$name}) {
202             next;
203             } else {
204             if (defined $defaults->{$name}) { # We have a default...
205             $data->{$name} = $defaults->{$name};
206             } else {
207             die "$name parameter is required to encode spanning tree\n";
208             }
209             }
210             }
211              
212              
213             $data->{bpdu_type} = sprintf("%02lx", $data->{bpdu_type});
214             if ($data->{bpdu_type} eq "80") { # topo change notification
215             return pack("nH2H2", $data->{protocol_id},
216             $data->{protocol_version},
217             $data->{bpdu_type});
218             }
219            
220             #
221             # Build a port ID from the number and priority.
222             #
223              
224             $data->{port_id} = sprintf("%02lx%02lx",
225             $data->{port_priority},
226             $data->{port_num});
227             #
228             # Build the Bridge PDU flags.
229             #
230             if ($data->{bpdu_type} == 0) {
231             $data->{bpdu_flags} = $data->{topology_change_ack} . "000000" .
232             $data->{topology_change};
233             } elsif ($data->{bpdu_type} == 2) {
234             my $prole2=$data->{port_role} % 2;
235             my $prole1=int ($data->{port_role}/2);
236             $data->{bpdu_flags} = $data->{topology_change_ack} .
237             $data->{agreement} .
238             $data->{forwarding} .
239             $data->{learning} .
240             $prole1 .
241             $prole2 .
242             $data->{proposal} .
243             $data->{topology_change};
244             }
245              
246             #
247             # Invert the message age for encoding.
248             #
249              
250             foreach my $name qw(message_age hello_time max_age forward_delay) {
251             $data->{$name} = $data->{$name} * 256;
252             }
253              
254             my $packed_data = "";
255             foreach my $key (sort { $a <=> $b; } keys %$packStruct) {
256             foreach my $subkey (keys %{$packStruct->{$key}}) {
257             $packed_data .= pack ($packStruct->{$key}{$subkey},
258             $data->{$subkey});
259             }
260             }
261              
262             if ($data->{bpdu_type} == 2) { # Rapid Spanning Tree
263             my $len = sprintf("%02lx", $data->{version_1_length});
264             $packed_data .= pack('H2', $len);
265             }
266              
267             #
268             # Put back the message age.
269             #
270             foreach my $name qw(message_age hello_time max_age forward_delay) {
271             $data->{$name} = $data->{$name} / 256;
272             }
273             $data->{bpdu_type} = hex ($data->{bpdu_type});
274             return $packed_data;
275             }
276              
277             #
278             # Module return value
279             #
280             1;
281              
282             # autoloaded methods go after the END token (&& pod) below
283              
284             __END__