File Coverage

blib/lib/Net/DHCPv6/DUID/Parser.pm
Criterion Covered Total %
statement 75 78 96.1
branch 27 40 67.5
condition 36 51 70.5
subroutine 15 15 100.0
pod 8 8 100.0
total 161 192 83.8


line stmt bran cond sub pod time code
1             package Net::DHCPv6::DUID::Parser;
2              
3             # Copyright 2010 Tom Wright. All rights reserved.
4             #
5             # Redistribution and use in source and binary forms, with or without modification, are
6             # permitted provided that the following conditions are met:
7             #
8             # 1. Redistributions of source code must retain the above copyright notice, this list of
9             # conditions and the following disclaimer.
10             #
11             # 2. Redistributions in binary form must reproduce the above copyright notice, this list
12             # of conditions and the following disclaimer in the documentation and/or other materials
13             # provided with the distribution.
14             #
15             # THIS SOFTWARE IS PROVIDED BY TOM WRIGHT ``AS IS'' AND ANY EXPRESS OR IMPLIED
16             # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17             # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TOM WRIGHT OR
18             # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20             # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21             # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22             # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23             # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24             #
25             # The views and conclusions contained in the software and documentation are those of the
26             # authors and should not be interpreted as representing official policies, either expressed
27             # or implied, of Tom Wright.
28              
29             our $VERSION = "1.01";
30              
31 1     1   25035 use strict;
  1         3  
  1         29  
32 1     1   5 use warnings;
  1         1  
  1         26  
33 1     1   5 use Carp;
  1         6  
  1         1830  
34              
35             =head1 NAME
36              
37             Net::DHCPv6::DUID::Parser - Parse DHCPv6 Unique Identifiers
38              
39             =head1 SYNOPSIS
40              
41             use Net::DHCPv6::DUID::Parser;
42              
43             my $p = new Net::DHCPv6::DUID::Parser;
44              
45             # Decode an example DUID
46             $p->decode('000300010004ED9F7622');
47            
48             # Print the type
49             print "TYPE: ".$p->type(format => 'text')."\n";
50              
51             ### prints 'TYPE: DUID-LL'
52              
53             if ($p->type == 1 || $p->type == 3) {
54              
55             # Format this like a MAC address if the link type was Ethernet
56             if ($p->iana_hw_type == 1) {
57             print "MAC ADDRESS: ".$p->local_link_address(format => 'ethernet_mac')."\n";
58             } else {
59             print "LOCAL LINK ADDRESS: ".$p->local_link_address."\n";
60             }
61              
62             }
63              
64             ### prints 'MAC ADDRESS: 00-04-ed-9f-76-22'
65              
66             =head1 DESCRIPTION
67              
68             Object oriented interface to parse RFC3315
69             compliant DHCPv6 Unique Identifiers (DUIDs)
70              
71             This module was written for the purpose of
72             splitting the DUID into its constituent parts,
73             and shared here for convenience. It does
74             some textual conversions that may save you
75             some time.
76              
77             =cut
78              
79             ## Accept the following DUID input formats
80             my %pack_templates = (
81             'hex' => 'H*',
82             'bin' => 'B*',
83             );
84              
85             ## Decoders registered for each DUID type
86             my %decoders = (
87             1 => \&_decode_type_1,
88             2 => \&_decode_type_2,
89             3 => \&_decode_type_3,
90             );
91              
92             ## IETF DUID types
93             my %duid_types = (
94             1 => 'DUID-LLT',
95             2 => 'DUID-EN',
96             3 => 'DUID-LL',
97             );
98              
99             ## IANA hardware types
100             my %iana_hw_types = (
101             0 => 'Reserved',
102             1 => 'Ethernet (10Mb)',
103             2 => 'Experimental Ethernet (3Mb)',
104             3 => 'Amateur Radio AX.25',
105             4 => 'Proteon ProNET Token Ring',
106             5 => 'Chaos',
107             6 => 'IEEE 802 Networks',
108             7 => 'ARCNET',
109             8 => 'Hyperchannel',
110             9 => 'Lanstar',
111             10 => 'Autonet Short Address',
112             11 => 'LocalTalk',
113             12 => 'LocalNet (IBM PCNet or SYTEK LocalNET)',
114             13 => 'Ultra link',
115             14 => 'SMDS',
116             15 => 'Frame Relay',
117             16 => 'Asynchronous Transmission Mode (ATM)',
118             17 => 'HDLC',
119             18 => 'Fibre Channel',
120             19 => 'Asynchronous Transmission Mode (ATM)',
121             20 => 'Serial Line',
122             21 => 'Asynchronous Transmission Mode (ATM)',
123             22 => 'MIL-STD-188-220',
124             23 => 'Metricom',
125             24 => 'IEEE 1394.1995',
126             25 => 'MAPOS',
127             26 => 'Twinaxial',
128             27 => 'EUI-64',
129             28 => 'HIPARP',
130             29 => 'IP and ARP over ISO 7816-3',
131             30 => 'ARPSec',
132             31 => 'IPsec tunnel',
133             32 => 'InfiniBand (TM)',
134             33 => 'TIA-102 Project 25 Common Air Interface (CAI)',
135             34 => 'Wiegand Interface',
136             35 => 'Pure IP',
137             36 => 'HW_EXP1',
138             37 => 'HFI',
139             256 => 'HW_EXP2',
140             );
141              
142             =head1 USAGE
143              
144             =head2 Methods
145              
146             =head3 Constructor
147              
148             =over 4
149              
150             =item * Net::DHCPv6::DUID::Parser->new(..)
151              
152             my $p = new Net::DHCPv6::DUID::Parser (decode => 'hex');
153              
154             The constructor class method accepts two parameters.
155              
156             The 'decode' parameter tells the parser the format of the
157             DUID you're intending to parse using the 'decode' object method.
158             Valid attributes are 'hex' and 'bin'. The default value is 'hex'.
159              
160             The 'warnings' parameter can be set to 0 to disable
161             output to STDERR. The default value is 1.
162              
163             =back
164              
165             =cut
166              
167             sub new {
168              
169 3     3 1 1960 my $invocant = shift;
170              
171 3   33     13 my $class = ref($invocant) || $invocant;
172              
173 3         12 my $self = {
174             decode => 'hex',
175             warnings => 1,
176             @_,
177             };
178              
179 3         9 my %params = ( 'decode' => 1, warnings => 1 );
180              
181 3         8 foreach (keys %$self) {
182 6 50       15 croak "valid parameters are '". (join "' OR '", keys %params) ."'"
183             unless $params{$_};
184             }
185              
186 3 50       11 croak "valid attributes for parameter 'decode' are '"
187             . (join "' OR '", keys %pack_templates) ."'"
188             unless $pack_templates{$self->{decode}};
189              
190 3         11 return bless $self, $class;
191              
192             }
193              
194             =head3 Object Methods
195              
196             Each method returns undef if it encounters a failure, or if a requested DUID component wasn't
197             relevant to the decoded DUID type.
198              
199             Warnings are emitted by default, unless turned off in the object constructor.
200              
201             =over 4
202              
203             =item * $p->decode($duid)
204              
205             Accepts a single scalar, which should contain the DUID in the
206             format indicated by the constructor.
207              
208             Returns 1 on success.
209              
210             =back
211              
212             =cut
213              
214             sub decode {
215              
216 4     4 1 846 my ($self, $duid) = @_;
217              
218 4         12 $self->{type} = $self->_decode_type($duid);
219              
220 4         10 foreach (qw/iana_hw_type local_link_address enterprise_number identifier time/) {
221 20         37 $self->{$_} = undef;
222             }
223              
224 4 100 66     26 if ($decoders{$self->{type}} && $duid_types{$self->{type}}) {
225              
226             ## type 1
227 3 100       15 if ($self->{type} == 1) {
    100          
    50          
228 1         6 ( $self->{iana_hw_type},
229             $self->{local_link_address},
230             $self->{time}
231 1         3 ) = &{$decoders{$self->{type}}}($self, $duid);
232 1         5 return 1;
233              
234             ## type 2
235             } elsif ($self->{type} == 2) {
236 1         5 ( $self->{enterprise_number},
237             $self->{identifier}
238 1         1 ) = &{$decoders{$self->{type}}}($self, $duid);
239 1         5 return 1;
240              
241             ## type 3
242             } elsif ($self->{type} == 3) {
243 1         4 ( $self->{iana_hw_type},
244             $self->{local_link_address}
245 1         1 ) = &{$decoders{$self->{type}}}($self, $duid);
246 1         4 return 1;
247             } else {
248             ## should never get here
249 0         0 die "ERROR: ".__PACKAGE__."->decoder registered, but not instructed to do anything! ".
250             "don't know how to decode type $self->{type}";
251             }
252            
253             } else {
254 1 50       4 carp "don't know how to decode type $self->{type}" if ($self->{warnings});
255             }
256              
257 1         5 return undef;
258             }
259              
260             =over 4
261              
262             =item * $p->type(..)
263              
264             Applies to: DUID-LL, DUID-LLT and DUID-EN.
265              
266             Returns the DUID type.
267              
268             Specify "format => 'text'" to return the textual representation
269             of the DUID type. The default return value is numeric.
270              
271             =back
272              
273             =cut
274              
275             sub type {
276 7     7 1 17 my ($self, %opts) = @_;
277              
278 7 100 66     67 if ($self->{type} && !($duid_types{$self->{type}})) {
    100 66        
      66        
279 1 50       4 carp "type $self->{type} is not valid" if ($self->{warnings});
280 1         4 return undef;
281             } elsif ($self->{type} && $opts{format} && $opts{format} eq 'text') {
282 3         20 return $duid_types{$self->{type}};
283             } else {
284 3         14 return $self->{type};
285             }
286             }
287              
288             =over 4
289              
290             =item * $p->time()
291              
292             Applies to: DUID-LLT.
293              
294             Returns time ticks in seconds since midnight 1st January 2000.
295              
296             =back
297              
298             =cut
299              
300             sub time {
301 4     4 1 9 my ($self) = @_;
302              
303 4 50 66     41 carp "time is irrelevant for DUID type $self->{type}"
304             unless ($self->{type} == 1 || !($self->{warnings}));
305              
306 4         551 return $self->{time};
307             }
308              
309             =over 4
310              
311             =item * $p->iana_hw_type(..)
312              
313             Applies to: DUID-LL and DUID-LLT.
314              
315             Returns the IANA hardware type or undef if this parameter is irrelevant.
316              
317             Specify "format => 'text'" for a textual representation of this value.
318             The default return value is numeric.
319              
320             =back
321              
322             =cut
323              
324              
325             sub iana_hw_type {
326 10     10 1 37 my ($self, %opts) = @_;
327              
328 10 50 100     55 carp "iana_hw_type is irrelevant for DUID type $self->{type}"
      66        
329             unless ($self->{type} == 1 || $self->{type} == 3 || !($self->{warnings}));
330              
331 10 50 66     81 if ($self->{iana_hw_type} && !($iana_hw_types{$self->{iana_hw_type}})) {
    100 100        
      66        
332 0         0 carp "iana_hw_type $self->{iana_hw_type} is UNKNOWN for DUID type $self->{type}";
333 0         0 return undef;
334             } elsif ($self->{iana_hw_type} && $opts{format} && $opts{format} eq 'text') {
335 2         10 return $iana_hw_types{$self->{iana_hw_type}};
336             } else {
337 8         36 return $self->{iana_hw_type};
338             }
339             }
340              
341             =over 4
342              
343             =item * $p->enterprise_number()
344              
345             Applies to: DUID-EN.
346              
347             Returns the enterprise number.
348              
349             =back
350              
351             =cut
352              
353              
354             sub enterprise_number {
355 4     4 1 7 my ($self) = @_;
356              
357 4 50 66     21 carp "enterprise_number is irrelevant for DUID type $self->{type}"
358             unless ($self->{type} == 2 || !($self->{warnings}));
359              
360 4         15 return $self->{enterprise_number};
361             }
362              
363             =over 4
364              
365             =item * $p->identifier()
366              
367             Applies to: DUID-EN.
368              
369             Returns the identifier.
370              
371             =back
372              
373             =cut
374              
375             sub identifier {
376 4     4 1 23 my ($self) = @_;
377              
378 4 50 66     22 carp "enterprise_number is irrelevant for DUID type $self->{type}"
379             unless ($self->{type} == 2 || !($self->{warnings}));
380              
381 4         16 return $self->{identifier};
382             }
383              
384             =over 4
385              
386             =item * $p->local_link_address(..)
387              
388             Applies to: DUID-LL and DUID-LLT
389              
390             Returns the local link address.
391              
392             Specify "format => 'ethernet_mac'" for a pretty representation of this value.
393              
394             The formatting will only apply if the IANA hardware type is '1' - i.e, if it's Ethernet.
395              
396             =back
397              
398             =cut
399              
400             sub local_link_address {
401 8     8 1 18 my ($self, %opts) = @_;
402              
403 8 50 100     49 carp "local_link_address is irrelevant for DUID type $self->{type}"
      66        
404             unless ($self->{type} == 1 || $self->{type} == 3 || !($self->{warnings}));
405              
406 8         17 my %formats = (
407             ethernet_mac => 1,
408             );
409              
410 8 100       14 if ($opts{format}) {
411 4 50       11 croak "ERROR: ".__PACKAGE__."->local_link_address' valid options for 'format' are '"
412             . (join "' OR '", keys %formats) . "'"
413             unless ($formats{$opts{format}});
414              
415 4 50 66     21 if ($self->{local_link_address} && $opts{format} eq 'ethernet_mac' && $self->iana_hw_type == 1) {
      66        
416 2         14 my @ethernet_mac = unpack ('(A2)*', $self->{local_link_address});
417 2         15 return join "-", @ethernet_mac;
418             }
419             } else {
420 4         16 return $self->{local_link_address};
421             }
422              
423 2         8 return undef;
424              
425             }
426              
427             ##
428             ## PRIVATE METHODS
429             ##
430              
431             ## DUID Based on Link-layer Address Plus Time [DUID-LLT]
432             sub _decode_type_1 {
433 1     1   2 my ($self, $duid) = @_;
434              
435 1         9 my ($iana_hw_type, $time, $local_link_address) =
436             unpack ('xx (n) (N) (H*)',pack ($pack_templates{$self->{decode}},$duid));
437              
438 1         4 return ($iana_hw_type, $local_link_address, $time);
439             }
440              
441             ## DUID Assigned by Vendor Based on Enterprise Number [DUID-EN]
442             sub _decode_type_2 {
443 1     1   2 my ($self, $duid) = @_;
444              
445 1         9 my ($enterprise_number, $identifier) =
446             unpack ('xx (N) (H*)',pack($pack_templates{$self->{decode}},$duid));
447              
448 1         5 return ($enterprise_number, $identifier);
449              
450             }
451              
452             ## DUID Based on Link-layer Address [DUID-LL]
453             sub _decode_type_3 {
454 1     1   2 my ($self, $duid) = @_;
455              
456 1         7 my ($iana_hw_type, $local_link_address) =
457             unpack('xx (n) (H*)',pack ($pack_templates{$self->{decode}},$duid));
458              
459 1         4 return ($iana_hw_type, $local_link_address);
460             }
461              
462             ## Determine only the DUID type
463             sub _decode_type {
464 4     4   5 my ($self, $duid) = @_;
465 4         32 return unpack ("n",pack("H*",$duid));
466             }
467              
468             =head1 CREDITS
469              
470             Mark Smith
471              
472             =head1 SEE ALSO
473              
474             http://tools.ietf.org/html/rfc3315#section-9
475              
476             =head1 AUTHOR
477              
478             Tom Wright, 2010
479              
480             =cut
481              
482              
483             1;