File Coverage

blib/lib/Net/Frame/Layer/ICMPv6.pm
Criterion Covered Total %
statement 155 244 63.5
branch 2 74 2.7
condition 0 45 0.0
subroutine 51 58 87.9
pod 10 10 100.0
total 218 431 50.5


line stmt bran cond sub pod time code
1             #
2             # $Id: ICMPv6.pm,v b9194b248a66 2017/10/06 16:26:50 gomor $
3             #
4             package Net::Frame::Layer::ICMPv6;
5 2     2   26417 use strict; use warnings;
  2     2   14  
  2         72  
  2         15  
  2         7  
  2         736  
6              
7             our $VERSION = '1.10';
8              
9 2     2   1733 use Net::Frame::Layer qw(:consts :subs);
  2         188042  
  2         537  
10 2     2   20 use Exporter;
  2         5  
  2         270  
11             our @ISA = qw(Net::Frame::Layer Exporter);
12              
13             our %EXPORT_TAGS = (
14             consts => [qw(
15             NF_ICMPv6_CODE_ZERO
16             NF_ICMPv6_TYPE_DESTUNREACH
17             NF_ICMPv6_CODE_NOROUTE
18             NF_ICMPv6_CODE_ADMINPROHIBITED
19             NF_ICMPv6_CODE_NOTASSIGNED
20             NF_ICMPv6_CODE_ADDRESSUNREACH
21             NF_ICMPv6_CODE_PORTUNREACH
22             NF_ICMPv6_CODE_FAILPOLICY
23             NF_ICMPv6_CODE_REJECTROUTE
24             NF_ICMPv6_TYPE_TOOBIG
25             NF_ICMPv6_TYPE_TIMEEXCEED
26             NF_ICMPv6_CODE_HOPLIMITEXCEED
27             NF_ICMPv6_CODE_FRAGREASSEMBLYEXCEEDED
28             NF_ICMPv6_TYPE_PARAMETERPROBLEM
29             NF_ICMPv6_CODE_ERRONEOUSHERDERFIELD
30             NF_ICMPv6_CODE_UNKNOWNNEXTHEADER
31             NF_ICMPv6_CODE_UNKNOWNOPTION
32             NF_ICMPv6_TYPE_ECHO_REQUEST
33             NF_ICMPv6_TYPE_ECHO_REPLY
34             NF_ICMPv6_TYPE_MLDQUERY
35             NF_ICMPv6_TYPE_MLDREPORTv1
36             NF_ICMPv6_TYPE_MLDDONE
37             NF_ICMPv6_TYPE_ROUTERSOLICITATION
38             NF_ICMPv6_TYPE_ROUTERADVERTISEMENT
39             NF_ICMPv6_TYPE_NEIGHBORSOLICITATION
40             NF_ICMPv6_TYPE_NEIGHBORADVERTISEMENT
41             NF_ICMPv6_TYPE_MLDREPORTv2
42             NF_ICMPv6_OPTION_SOURCELINKLAYERADDRESS
43             NF_ICMPv6_OPTION_TARGETLINKLAYERADDRESS
44             NF_ICMPv6_OPTION_PREFIXINFORMATION
45             NF_ICMPv6_OPTION_REDIRECTEDHEADER
46             NF_ICMPv6_OPTION_MTU
47             NF_ICMPv6_FLAG_ROUTER
48             NF_ICMPv6_FLAG_SOLICITED
49             NF_ICMPv6_FLAG_OVERRIDE
50             NF_ICMPv6_FLAG_MANAGEDADDRESSCONFIGURATION
51             NF_ICMPv6_FLAG_OTHERCONFIGURATION
52             )],
53             );
54             our @EXPORT_OK = (
55             @{$EXPORT_TAGS{consts}},
56             );
57              
58 2     2   14 use constant NF_ICMPv6_CODE_ZERO => 0;
  2         6  
  2         131  
59 2     2   13 use constant NF_ICMPv6_TYPE_DESTUNREACH => 1;
  2         4  
  2         101  
60 2     2   13 use constant NF_ICMPv6_CODE_NOROUTE => 0;
  2         5  
  2         97  
61 2     2   13 use constant NF_ICMPv6_CODE_ADMINPROHIBITED => 1;
  2         11  
  2         92  
62 2     2   12 use constant NF_ICMPv6_CODE_NOTASSIGNED => 2;
  2         5  
  2         105  
63 2     2   15 use constant NF_ICMPv6_CODE_ADDRESSUNREACH => 3;
  2         5  
  2         98  
64 2     2   12 use constant NF_ICMPv6_CODE_PORTUNREACH => 4;
  2         5  
  2         98  
65 2     2   13 use constant NF_ICMPv6_CODE_FAILPOLICY => 5;
  2         4  
  2         102  
66 2     2   12 use constant NF_ICMPv6_CODE_REJECTROUTE => 6;
  2         4  
  2         109  
67 2     2   13 use constant NF_ICMPv6_TYPE_TOOBIG => 2;
  2         4  
  2         101  
68 2     2   11 use constant NF_ICMPv6_TYPE_TIMEEXCEED => 3;
  2         4  
  2         104  
69 2     2   12 use constant NF_ICMPv6_CODE_HOPLIMITEXCEED => 0;
  2         5  
  2         123  
70 2     2   16 use constant NF_ICMPv6_CODE_FRAGREASSEMBLYEXCEEDED => 1;
  2         5  
  2         125  
71 2     2   17 use constant NF_ICMPv6_TYPE_PARAMETERPROBLEM => 4;
  2         6  
  2         131  
72 2     2   14 use constant NF_ICMPv6_CODE_ERRONEOUSHERDERFIELD => 0;
  2         3  
  2         103  
73 2     2   12 use constant NF_ICMPv6_CODE_UNKNOWNNEXTHEADER => 1;
  2         5  
  2         113  
74 2     2   29 use constant NF_ICMPv6_CODE_UNKNOWNOPTION => 2;
  2         5  
  2         103  
75 2     2   12 use constant NF_ICMPv6_TYPE_ECHO_REQUEST => 128;
  2         4  
  2         101  
76 2     2   14 use constant NF_ICMPv6_TYPE_ECHO_REPLY => 129;
  2         3  
  2         98  
77 2     2   13 use constant NF_ICMPv6_TYPE_MLDQUERY => 130;
  2         3  
  2         85  
78 2     2   12 use constant NF_ICMPv6_TYPE_MLDREPORTv1 => 131;
  2         4  
  2         103  
79 2     2   12 use constant NF_ICMPv6_TYPE_MLDDONE => 132;
  2         3  
  2         99  
80 2     2   12 use constant NF_ICMPv6_TYPE_ROUTERSOLICITATION => 133;
  2         4  
  2         88  
81 2     2   11 use constant NF_ICMPv6_TYPE_ROUTERADVERTISEMENT => 134;
  2         5  
  2         100  
82 2     2   11 use constant NF_ICMPv6_TYPE_NEIGHBORSOLICITATION => 135;
  2         5  
  2         145  
83 2     2   15 use constant NF_ICMPv6_TYPE_NEIGHBORADVERTISEMENT => 136;
  2         4  
  2         101  
84 2     2   13 use constant NF_ICMPv6_TYPE_MLDREPORTv2 => 143;
  2         26  
  2         98  
85              
86 2     2   12 use constant NF_ICMPv6_OPTION_SOURCELINKLAYERADDRESS => 0x01;
  2         5  
  2         96  
87 2     2   11 use constant NF_ICMPv6_OPTION_TARGETLINKLAYERADDRESS => 0x02;
  2         5  
  2         109  
88 2     2   11 use constant NF_ICMPv6_OPTION_PREFIXINFORMATION => 0x03;
  2         5  
  2         89  
89 2     2   11 use constant NF_ICMPv6_OPTION_REDIRECTEDHEADER => 0x04;
  2         4  
  2         93  
90 2     2   11 use constant NF_ICMPv6_OPTION_MTU => 0x05;
  2         4  
  2         97  
91              
92 2     2   15 use constant NF_ICMPv6_FLAG_ROUTER => 0x04;
  2         4  
  2         125  
93 2     2   16 use constant NF_ICMPv6_FLAG_SOLICITED => 0x02;
  2         6  
  2         125  
94 2     2   14 use constant NF_ICMPv6_FLAG_OVERRIDE => 0x01;
  2         4  
  2         112  
95              
96 2     2   16 use constant NF_ICMPv6_FLAG_MANAGEDADDRESSCONFIGURATION => 1 << 5;
  2         5  
  2         129  
97 2     2   16 use constant NF_ICMPv6_FLAG_OTHERCONFIGURATION => 1 << 4;
  2         6  
  2         130  
98 2     2   15 use constant NF_ICMPv6_FLAG_MOBILEIPv6HOMEAGENT => 1 << 3;
  2         5  
  2         118  
99 2     2   15 use constant NF_ICMPv6_FLAG_ROUTERSELECTIONPREFHIGH => 1 << 1; # 01b
  2         4  
  2         114  
100 2     2   17 use constant NF_ICMPv6_FLAG_ROUTERSELECTIONPREFMEDIUM => 0; # 00b
  2         6  
  2         137  
101 2     2   16 use constant NF_ICMPv6_FLAG_ROUTERSELECTIONPREFLOW => 3 << 1; # 11b
  2         5  
  2         131  
102 2     2   16 use constant NF_ICMPv6_FLAG_ROUTERSELECTIONPREFRESERVED => 2 << 1; # 10b
  2         6  
  2         130  
103 2     2   17 use constant NF_ICMPv6_FLAG_NEIGHBORDISCOVERYPROXY => 1;
  2         5  
  2         231  
104              
105             our @AS = qw(
106             type
107             code
108             checksum
109             );
110             __PACKAGE__->cgBuildIndices;
111             __PACKAGE__->cgBuildAccessorsScalar(\@AS);
112              
113 2     2   854 use Bit::Vector;
  2         2191  
  2         2998  
114              
115             sub new {
116             shift->SUPER::new(
117 1     1 1 23 type => NF_ICMPv6_TYPE_ECHO_REQUEST,
118             code => NF_ICMPv6_CODE_ZERO,
119             checksum => 0,
120             @_,
121             );
122             }
123              
124             # XXX: may be better, by keying on type also
125 0     0 1 0 sub getKey { shift->layer }
126 0     0 1 0 sub getKeyReverse { shift->layer }
127              
128             sub match {
129 0     0 1 0 my $self = shift;
130 0         0 my ($with) = @_;
131 0         0 my $sType = $self->type;
132 0         0 my $wType = $with->type;
133 0 0 0     0 if ($sType eq NF_ICMPv6_TYPE_ECHO_REQUEST
    0 0        
    0 0        
    0 0        
      0        
134             && $wType eq NF_ICMPv6_TYPE_ECHO_REPLY) {
135 0         0 return 1;
136             }
137             elsif ($sType eq NF_ICMPv6_TYPE_NEIGHBORSOLICITATION
138             && $wType eq NF_ICMPv6_TYPE_NEIGHBORADVERTISEMENT) {
139 0         0 return 1;
140             }
141             elsif ($sType eq NF_ICMPv6_TYPE_ROUTERSOLICITATION
142             && $wType eq NF_ICMPv6_TYPE_ROUTERADVERTISEMENT) {
143 0         0 return 1;
144             }
145             elsif ($sType eq NF_ICMPv6_TYPE_MLDQUERY
146             && (($wType eq NF_ICMPv6_TYPE_MLDREPORTv1)
147             || ($wType eq NF_ICMPv6_TYPE_MLDREPORTv2)) ) {
148 0         0 return 1;
149             }
150 0         0 return 0;
151             }
152              
153 0     0 1 0 sub getLength { 4 }
154              
155             sub pack {
156 1     1 1 324 my $self = shift;
157              
158 1 50       5 my $raw = $self->SUPER::pack('CCn',
159             $self->type, $self->code, $self->checksum,
160             ) or return;
161              
162 1         77 return $self->raw($raw);
163             }
164              
165             sub unpack {
166 1     1 1 19 my $self = shift;
167              
168 1 50       5 my ($type, $code, $checksum, $payload) =
169             $self->SUPER::unpack('CCn a*', $self->raw)
170             or return;
171              
172 1         41 $self->type($type);
173 1         16 $self->code($code);
174 1         13 $self->checksum($checksum);
175 1         16 $self->payload($payload);
176              
177 1         19 return $self;
178             }
179              
180             sub computeChecksums {
181 0     0 1   my $self = shift;
182 0           my ($layers) = @_;
183              
184 0           my $ip;
185             my $rh0;
186 0           my $hbh; # Hop-by-hop Ext Hdr
187 0           my $dst; # Destination Ext Hdr
188 0           my $mob; # Mobility Ext Hdr
189 0           my $lastNextHeader;
190 0           my $fragmentFlag = 0;
191 0           my $start = 0;
192 0           my $last = $self;
193 0           my $payload = '';
194 0           for my $l (@$layers) {
195 0 0 0       if (! $ip && $l->layer eq 'IPv6') { $ip = $l; }
  0            
196 0 0 0       if (! $rh0 && $l->layer eq 'IPv6::Routing') { $rh0 = $l; }
  0            
197 0 0 0       if (! $hbh && $l->layer eq 'IPv6::HopByHop') { $hbh = $l; }
  0            
198 0 0 0       if (! $dst && $l->layer eq 'IPv6::Destination') { $dst = $l; }
  0            
199 0 0 0       if (! $mob && $l->layer eq 'IPv6::Mobility') { $mob = $l; }
  0            
200              
201 0 0         if ($l->can('nextHeader')) { $lastNextHeader = $l->nextHeader; }
  0            
202              
203 0 0         if ($l->layer eq 'IPv6::Fragment') { $fragmentFlag = 1; }
  0            
204              
205 0           $last = $l;
206 0 0         if (! $start) {
207 0 0         $start++ if $l->layer eq 'ICMPv6';
208 0           next;
209             }
210 0           $payload .= $l->pack;
211             }
212              
213 0           my $lastIpDst = $ip->dst;
214 0           my $ipPayloadLength = $ip->payloadLength;
215             # If RH0, need to set $ip->dst to last $rh0->addresses
216             # unless segmentsLeft == 0 (RFC 2460 sec 8.1)
217 0 0 0       if ($rh0 && $rh0->segmentsLeft != 0) {
218 0           for ($rh0->addresses) {
219 0           $lastIpDst = $_;
220             }
221             # Pseudo header length is upper layer minus any EH (RFC 2460 sec 8.1)
222 0           $ipPayloadLength -= $rh0->getLength;
223             }
224             # Pseudo header length is upper layer minus any EH (RFC 2460 sec 8.1)
225 0 0         if ($fragmentFlag) {
226 0           $ipPayloadLength -= 8; # 8 = length of fragment EH
227             }
228 0 0         if ($hbh) {
229 0           $ipPayloadLength -= $hbh->getLength;
230             }
231 0 0         if ($dst) {
232 0           $ipPayloadLength -= $dst->getLength;
233             }
234 0 0         if ($mob) {
235 0           $ipPayloadLength -= $mob->getLength;
236             }
237              
238             # Build pseudo-header and pack ICMPv6 packet
239 0           my $zero = Bit::Vector->new_Dec(24, 0);
240 0           my $nextHeader = Bit::Vector->new_Dec( 8, $lastNextHeader);
241 0           my $v32 = $zero->Concat_List($nextHeader);
242              
243 0 0         my $packed = $self->SUPER::pack('a*a*NNCCn',
244             inet6Aton($ip->src), inet6Aton($lastIpDst), $ipPayloadLength,
245             $v32->to_Dec, $self->type, $self->code, 0
246             ) or return;
247              
248 0 0 0       if (defined($last->payload) && length($last->payload)) {
249 0           $payload .= $last->payload;
250             }
251 0 0         if (length($payload)) {
252 0 0         $packed .= $self->SUPER::pack('a*', $payload)
253             or return;
254             }
255              
256 0           $self->checksum(inetChecksum($packed));
257              
258 0           return 1;
259             }
260              
261             sub encapsulate {
262 0     0 1   my $self = shift;
263              
264 0 0         return $self->nextLayer if $self->nextLayer;
265              
266 0 0         if ($self->payload) {
267 0           my $type = $self->type;
268             # if ($type eq NF_ICMPv6_TYPE_REDIRECT) {
269             # return 'IPv6';
270             # }
271 0 0 0       if ($type eq NF_ICMPv6_TYPE_ECHO_REQUEST
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
272             || $type eq NF_ICMPv6_TYPE_ECHO_REPLY) {
273 0           return 'ICMPv6::Echo';
274             }
275             elsif ($type eq NF_ICMPv6_TYPE_NEIGHBORSOLICITATION) {
276 0           return 'ICMPv6::NeighborSolicitation';
277             }
278             elsif ($type eq NF_ICMPv6_TYPE_NEIGHBORADVERTISEMENT) {
279 0           return 'ICMPv6::NeighborAdvertisement';
280             }
281             elsif ($type eq NF_ICMPv6_TYPE_ROUTERSOLICITATION) {
282 0           return 'ICMPv6::RouterSolicitation';
283             }
284             elsif ($type eq NF_ICMPv6_TYPE_ROUTERADVERTISEMENT) {
285 0           return 'ICMPv6::RouterAdvertisement';
286             }
287             elsif ($type eq NF_ICMPv6_TYPE_DESTUNREACH) {
288 0           return 'ICMPv6::DestUnreach';
289             }
290             elsif ($type eq NF_ICMPv6_TYPE_TIMEEXCEED) {
291 0           return 'ICMPv6::TimeExceed';
292             }
293             elsif ($type eq NF_ICMPv6_TYPE_TOOBIG) {
294 0           return 'ICMPv6::TooBig';
295             }
296             elsif ($type eq NF_ICMPv6_TYPE_PARAMETERPROBLEM) {
297 0           return 'ICMPv6::ParameterProblem';
298             }
299             elsif ($type eq NF_ICMPv6_TYPE_MLDQUERY
300             || $type eq NF_ICMPv6_TYPE_MLDREPORTv1
301             || $type eq NF_ICMPv6_TYPE_MLDDONE) {
302 0           return 'ICMPv6::MLD';
303             }
304             elsif ($type eq NF_ICMPv6_TYPE_MLDREPORTv2) {
305 0           return 'ICMPv6::MLD::Report';
306             }
307             }
308              
309 0           return NF_LAYER_NONE;
310             }
311              
312             sub print {
313 0     0 1   my $self = shift;
314              
315 0           my $l = $self->layer;
316 0           my $buf = sprintf "$l: type:%d code:%d checksum:0x%04x",
317             $self->type, $self->code, $self->checksum;
318              
319 0           return $buf;
320             }
321              
322             1;
323              
324             __END__