File Coverage

blib/lib/Net/Frame/Layer/CDP.pm
Criterion Covered Total %
statement 104 161 64.6
branch 4 58 6.9
condition 1 9 11.1
subroutine 33 36 91.6
pod 7 7 100.0
total 149 271 54.9


line stmt bran cond sub pod time code
1             #
2             # $Id: CDP.pm 1640 2013-03-28 17:58:27Z VinsWorldcom $
3             #
4             package Net::Frame::Layer::CDP;
5 19     19   30822 use strict; use warnings;
  19     19   37  
  19         693  
  19         92  
  19         28  
  19         905  
6              
7             our $VERSION = '1.01';
8              
9 19     19   1828 use Net::Frame::Layer qw(:consts :subs);
  19         431310  
  19         4213  
10 19     19   113 use Exporter;
  19         27  
  19         1083  
11             our @ISA = qw(Net::Frame::Layer Exporter);
12              
13 19     19   1482 use Net::Frame::Layer::CDP::Constants qw(:consts);
  19         36  
  19         4534  
14 19     19   11463 use Net::Frame::Layer::CDP::DeviceId;
  19         44  
  19         896  
15 19     19   11660 use Net::Frame::Layer::CDP::Addresses;
  19         48  
  19         884  
16 19     19   11481 use Net::Frame::Layer::CDP::Address qw(:consts);
  19         49  
  19         2649  
17 19     19   11728 use Net::Frame::Layer::CDP::PortId;
  19         45  
  19         830  
18 19     19   11517 use Net::Frame::Layer::CDP::Capabilities qw(:consts);
  19         50  
  19         2865  
19 19     19   10868 use Net::Frame::Layer::CDP::SoftwareVersion;
  19         49  
  19         792  
20 19     19   10973 use Net::Frame::Layer::CDP::Platform;
  19         47  
  19         809  
21 19     19   11141 use Net::Frame::Layer::CDP::IPNetPrefix;
  19         50  
  19         788  
22 19     19   11558 use Net::Frame::Layer::CDP::VTPDomain;
  19         53  
  19         934  
23 19     19   10872 use Net::Frame::Layer::CDP::NativeVlan;
  19         50  
  19         899  
24 19     19   11374 use Net::Frame::Layer::CDP::Duplex qw(:consts);
  19         54  
  19         2311  
25 19     19   13141 use Net::Frame::Layer::CDP::VoipVlanReply;
  19         54  
  19         880  
26 19     19   11365 use Net::Frame::Layer::CDP::VoipVlanQuery;
  19         59  
  19         989  
27 19     19   11058 use Net::Frame::Layer::CDP::Power;
  19         55  
  19         777  
28 19     19   11009 use Net::Frame::Layer::CDP::MTU;
  19         57  
  19         898  
29 19     19   11203 use Net::Frame::Layer::CDP::TrustBitmap qw(:consts);
  19         49  
  19         2196  
30 19     19   12127 use Net::Frame::Layer::CDP::UntrustedCos;
  19         152  
  19         845  
31 19     19   12377 use Net::Frame::Layer::CDP::ManagementAddresses;
  19         61  
  19         1019  
32 19     19   12497 use Net::Frame::Layer::CDP::Unknown;
  19         48  
  19         6828  
33              
34             my @consts;
35             for my $c (sort(keys(%constant::declared))) {
36             if ($c =~ /^Net::Frame::Layer::CDP::Constants::/) {
37             $c =~ s/^Net::Frame::Layer::CDP::Constants:://;
38             push @consts, $c
39             }
40             if ($c =~ /^Net::Frame::Layer::CDP::Address::/) {
41             $c =~ s/^Net::Frame::Layer::CDP::Address:://;
42             push @consts, $c
43             }
44             if ($c =~ /^Net::Frame::Layer::CDP::Capabilities::/) {
45             $c =~ s/^Net::Frame::Layer::CDP::Capabilities:://;
46             push @consts, $c
47             }
48             if ($c =~ /^Net::Frame::Layer::CDP::Duplex::/) {
49             $c =~ s/^Net::Frame::Layer::CDP::Duplex:://;
50             push @consts, $c
51             }
52             if ($c =~ /^Net::Frame::Layer::CDP::TrustBitmap::/) {
53             $c =~ s/^Net::Frame::Layer::CDP::TrustBitmap:://;
54             push @consts, $c
55             }
56             }
57             our %EXPORT_TAGS = (
58             consts => [qw(
59             NF_CDP_MAC
60             NF_CDP_VERSION_1
61             NF_CDP_VERSION_2
62             ), @consts],
63             );
64             our @EXPORT_OK = (
65             @{$EXPORT_TAGS{consts}},
66             );
67              
68 19     19   125 use constant NF_CDP_MAC => '01:00:0c:cc:cc:cc';
  19         40  
  19         1134  
69 19     19   103 use constant NF_CDP_VERSION_1 => 1;
  19         34  
  19         839  
70 19     19   92 use constant NF_CDP_VERSION_2 => 2;
  19         36  
  19         2429  
71              
72             our @AS = qw(
73             version
74             ttl
75             checksum
76             );
77             # Needed because subsequent NFL::CDP::Layers are stacked and can't
78             # return to NFL::CDP for dispatch. This isn't exposed for user
79             # configuration, only for storing values in unpack() for later display.
80             our @AA = qw(
81             tlvs
82             );
83             __PACKAGE__->cgBuildIndices;
84             __PACKAGE__->cgBuildAccessorsScalar(\@AS);
85             __PACKAGE__->cgBuildAccessorsArray(\@AA);
86              
87 19     19   102 no strict 'vars';
  19         42  
  19         25789  
88              
89             sub new {
90             shift->SUPER::new(
91 18     18 1 16699 version => NF_CDP_VERSION_2,
92             ttl => 180,
93             checksum => 0,
94             tlvs => [],
95             @_,
96             );
97             }
98              
99 0     0 1 0 sub getLength { 4 }
100              
101             sub pack {
102 1     1 1 259 my $self = shift;
103              
104 1 50       6 my $raw = $self->SUPER::pack('CCn',
105             $self->version,
106             $self->ttl,
107             $self->checksum
108             ) or return;
109              
110 1         70 return $self->raw($raw);
111             }
112              
113             sub _unpackOptions {
114 0     0   0 my $self = shift;
115 0         0 my ($payload) = @_;
116              
117 0         0 my @tlvs = ();
118 0   0     0 while (defined($payload) && (length($payload) > 0)) {
119 0         0 my $tlv;
120             # don't unpack $type from $payload as the entire $payload
121             # including $type must get dispatched in if/then below
122 0         0 my $type = substr $payload, 0, 2;
123 0         0 $type = CORE::unpack('n', $type);
124 0 0       0 if ($type == NF_CDP_TYPE_DEVICE_ID) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
125 0         0 $tlv = Net::Frame::Layer::CDP::DeviceId->new(raw => $payload)->unpack;
126             } elsif ($type == NF_CDP_TYPE_ADDRESSES) {
127 0         0 $tlv = Net::Frame::Layer::CDP::Addresses->new(raw => $payload)->unpack;
128             } elsif ($type == NF_CDP_TYPE_PORT_ID) {
129 0         0 $tlv = Net::Frame::Layer::CDP::PortId->new(raw => $payload)->unpack;
130             } elsif ($type == NF_CDP_TYPE_CAPABILITIES) {
131 0         0 $tlv = Net::Frame::Layer::CDP::Capabilities->new(raw => $payload)->unpack;
132             } elsif ($type == NF_CDP_TYPE_SOFTWARE_VERSION) {
133 0         0 $tlv = Net::Frame::Layer::CDP::SoftwareVersion->new(raw => $payload)->unpack;
134             } elsif ($type == NF_CDP_TYPE_PLATFORM) {
135 0         0 $tlv = Net::Frame::Layer::CDP::Platform->new(raw => $payload)->unpack;
136             } elsif ($type == NF_CDP_TYPE_IPNET_PREFIX) {
137 0         0 $tlv = Net::Frame::Layer::CDP::IPNetPrefix->new(raw => $payload)->unpack;
138             } elsif ($type == NF_CDP_TYPE_VTP_DOMAIN) {
139 0         0 $tlv = Net::Frame::Layer::CDP::VTPDomain->new(raw => $payload)->unpack;
140             } elsif ($type == NF_CDP_TYPE_NATIVE_VLAN) {
141 0         0 $tlv = Net::Frame::Layer::CDP::NativeVlan->new(raw => $payload)->unpack;
142             } elsif ($type == NF_CDP_TYPE_DUPLEX) {
143 0         0 $tlv = Net::Frame::Layer::CDP::Duplex->new(raw => $payload)->unpack;
144             } elsif ($type == NF_CDP_TYPE_VOIP_VLAN_REPLY) {
145 0         0 $tlv = Net::Frame::Layer::CDP::VoipVlanReply->new(raw => $payload)->unpack;
146             } elsif ($type == NF_CDP_TYPE_VOIP_VLAN_QUERY) {
147 0         0 $tlv = Net::Frame::Layer::CDP::VoipVlanQuery->new(raw => $payload)->unpack;
148             } elsif ($type == NF_CDP_TYPE_POWER) {
149 0         0 $tlv = Net::Frame::Layer::CDP::Power->new(raw => $payload)->unpack;
150             } elsif ($type == NF_CDP_TYPE_MTU) {
151 0         0 $tlv = Net::Frame::Layer::CDP::MTU->new(raw => $payload)->unpack;
152             } elsif ($type == NF_CDP_TYPE_TRUST_BITMAP) {
153 0         0 $tlv = Net::Frame::Layer::CDP::TrustBitmap->new(raw => $payload)->unpack;
154             } elsif ($type == NF_CDP_TYPE_UNTRUSTED_COS) {
155 0         0 $tlv = Net::Frame::Layer::CDP::UntrustedCos->new(raw => $payload)->unpack;
156             } elsif ($type == NF_CDP_TYPE_MANAGEMENT_ADDR) {
157 0         0 $tlv = Net::Frame::Layer::CDP::ManagementAddresses->new(raw => $payload)->unpack;
158             } else {
159 0         0 $tlv = Net::Frame::Layer::CDP::Unknown->new(raw => $payload)->unpack;
160             }
161 0         0 push @tlvs, $tlv;
162 0         0 $payload = $tlv->payload;
163 0         0 $tlv->payload(undef);
164             }
165 0         0 $self->tlvs(\@tlvs);
166              
167 0         0 return $payload;
168             }
169              
170             sub unpack {
171 1     1 1 18 my $self = shift;
172              
173 1 50       4 my ($version, $ttl, $checksum, $payload) =
174             $self->SUPER::unpack('CCn a*', $self->raw)
175             or return;
176              
177 1         32 $self->version($version);
178 1         14 $self->ttl($ttl);
179 1         13 $self->checksum($checksum);
180              
181 1 50 33     18 if (defined($payload) && length($payload)) {
182 0         0 $payload = $self->_unpackOptions($payload);
183             }
184              
185 1         8 $self->payload($payload);
186              
187 1         12 return $self;
188             }
189              
190             sub computeChecksums {
191 0     0 1 0 my $self = shift;
192 0         0 my ($layers) = @_;
193              
194 0 0       0 my $phpkt = $self->SUPER::pack('CCn',
195             $self->version, $self->ttl, 0)
196             or return;
197              
198 0         0 my $start = 0;
199 0         0 my $last = $self;
200 0         0 my $payload = '';
201 0         0 for my $l (@$layers) {
202 0         0 $last = $l;
203 0 0       0 if (! $start) {
204 0 0       0 $start++ if $l->layer eq 'CDP';
205 0         0 next;
206             }
207 0         0 $payload .= $l->pack;
208             }
209              
210 0 0 0     0 if (defined($last->payload) && length($last->payload)) {
211 0         0 $payload .= $last->payload;
212             }
213              
214             # From wireshark: packet-cdp.c
215             # http://fossies.org/dox/wireshark-1.9.1/packet-cdp_8c_source.html
216             # /* CDP doesn't adhere to RFC 1071 section 2. (B). It incorrectly assumes
217             # * checksums are calculated on a big endian platform, therefore i.s.o.
218             # * padding odd sized data with a zero byte _at the end_ it sets the last
219             # * big endian _word_ to contain the last network _octet_. This byteswap
220             # * has to be done on the last octet of network data before feeding it to
221             # * the Internet checksum routine.
222             # * CDP checksumming code has a bug in the addition of this last _word_
223             # * as a signed number into the long word intermediate checksum. When
224             # * reducing this long to word size checksum an off-by-one error can be
225             # * made. This off-by-one error is compensated for in the last _word_ of
226             # * the network data.
227             # */
228             # See: http://www.perlmonks.org/?node_id=1026156
229             ###DEBUG: printf "BEFORE = %s\n", (CORE::unpack "H*", $payload);
230 0 0       0 if (length( $payload )%2) {
231 0 0       0 if (substr($payload, -1) ge "\x80") {
232 0         0 substr $payload, -1, 1, chr(ord(substr $payload, -1) - 1);
233 0         0 substr $payload, -1, 0, "\xff";
234             } else {
235 0         0 substr $payload, -1, 0, "\0";
236             }
237             }
238             ###DEBUG: printf "AFTER = %s\n", (CORE::unpack "H*", $payload);
239              
240 0 0       0 if (length($payload)) {
241 0 0       0 $phpkt .= $self->SUPER::pack('a*', $payload)
242             or return;
243             }
244              
245 0         0 $self->checksum(inetChecksum($phpkt));
246              
247 0         0 return 1;
248             }
249              
250             sub encapsulate {
251 1     1 1 8 my $self = shift;
252              
253 1 50       10 return $self->nextLayer if $self->nextLayer;
254              
255             # if ($self->payload) {
256             # return 'CDP';
257             # }
258              
259 1         16 NF_LAYER_NONE;
260             }
261              
262             sub print {
263 35     35 1 3918 my $self = shift;
264              
265 35         182 my $l = $self->layer;
266 35         431 my $buf = sprintf
267             "$l: version:%d ttl:%d checksum:0x%04x",
268             $self->version, $self->ttl, $self->checksum;
269              
270 35         1355 for ($self->tlvs) {
271 0         0 $buf .= "\n" . $_->print;
272             }
273              
274 35         3473 return $buf;
275             }
276              
277             1;
278              
279             __END__