1
2 /***************************************************************************
3 * ICMPv6Header.cc -- The ICMPv6Header Class represents an ICMP version 6 *
4 * packet. It contains methods to set any header field. In general, these *
5 * methods do error checkings and byte order conversion. *
6 * *
7 ***********************IMPORTANT NMAP LICENSE TERMS************************
8 * *
9 * The Nmap Security Scanner is (C) 1996-2020 Insecure.Com LLC ("The Nmap *
10 * Project"). Nmap is also a registered trademark of the Nmap Project. *
11 * *
12 * This program is distributed under the terms of the Nmap Public Source *
13 * License (NPSL). The exact license text applying to a particular Nmap *
14 * release or source code control revision is contained in the LICENSE *
15 * file distributed with that version of Nmap or source code control *
16 * revision. More Nmap copyright/legal information is available from *
17 * https://nmap.org/book/man-legal.html, and further information on the *
18 * NPSL license itself can be found at https://nmap.org/npsl. This header *
19 * summarizes some key points from the Nmap license, but is no substitute *
20 * for the actual license text. *
21 * *
22 * Nmap is generally free for end users to download and use themselves, *
23 * including commercial use. It is available from https://nmap.org. *
24 * *
25 * The Nmap license generally prohibits companies from using and *
26 * redistributing Nmap in commercial products, but we sell a special Nmap *
27 * OEM Edition with a more permissive license and special features for *
28 * this purpose. See https://nmap.org/oem *
29 * *
30 * If you have received a written Nmap license agreement or contract *
31 * stating terms other than these (such as an Nmap OEM license), you may *
32 * choose to use and redistribute Nmap under those terms instead. *
33 * *
34 * The official Nmap Windows builds include the Npcap software *
35 * (https://npcap.org) for packet capture and transmission. It is under *
36 * separate license terms which forbid redistribution without special *
37 * permission. So the official Nmap Windows builds may not be *
38 * redistributed without special permission (such as an Nmap OEM *
39 * license). *
40 * *
41 * Source is provided to this software because we believe users have a *
42 * right to know exactly what a program is going to do before they run it. *
43 * This also allows you to audit the software for security holes. *
44 * *
45 * Source code also allows you to port Nmap to new platforms, fix bugs, *
46 * and add new features. You are highly encouraged to submit your *
47 * changes as a Github PR or by email to the dev@nmap.org mailing list *
48 * for possible incorporation into the main distribution. Unless you *
49 * specify otherwise, it is understood that you are offering us very *
50 * broad rights to use your submissions as described in the Nmap Public *
51 * Source License Contributor Agreement. This is important because we *
52 * fund the project by selling licenses with various terms, and also *
53 * because the inability to relicense code has caused devastating *
54 * problems for other Free Software projects (such as KDE and NASM). *
55 * *
56 * The free version of Nmap is distributed in the hope that it will be *
57 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of *
58 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, *
59 * indemnification and commercial support are all available through the *
60 * Npcap OEM program--see https://nmap.org/oem. *
61 * *
62 ***************************************************************************/
63 /* This code was originally part of the Nping tool. */
64
65 #include "ICMPv6Header.h"
66 #include "IPv6Header.h"
67 #include <assert.h>
68
69 /******************************************************************************/
70 /* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */
71 /******************************************************************************/
ICMPv6Header()72 ICMPv6Header::ICMPv6Header() {
73 this->reset();
74 } /* End of ICMPv6Header constructor */
75
76
~ICMPv6Header()77 ICMPv6Header::~ICMPv6Header() {
78
79 } /* End of ICMPv6Header destructor */
80
81
82 /** Sets every attribute to its default value */
reset()83 void ICMPv6Header::reset(){
84 memset(&this->h, 0, sizeof(nping_icmpv6_hdr_t));
85 h_du = (dest_unreach_msg_t *)this->h.data;
86 h_ptb= (pkt_too_big_msg_t *)this->h.data;
87 h_te = (time_exceeded_msg_t *)this->h.data;
88 h_pp = (parameter_problem_msg_t *)this->h.data;
89 h_e = (echo_msg_t *)this->h.data;
90 h_ra = (router_advert_msg_t *)this->h.data;
91 h_rs = (router_solicit_msg_t *)this->h.data;
92 h_na = (neighbor_advert_msg_t *)this->h.data;
93 h_ns = (neighbor_solicit_msg_t *)this->h.data;
94 h_r = (redirect_msg_t *)this->h.data;
95 h_rr = (router_renumbering_msg_t *)this->h.data;
96 h_ni = (nodeinfo_msg_t *)this->h.data;
97 h_mld= (mld_msg_t *)this->h.data;
98 } /* End of reset() */
99
100
101 /******************************************************************************/
102 /* PacketElement:: OVERWRITTEN METHODS */
103 /******************************************************************************/
104
105 /** @warning This method is essential for the superclass getBinaryBuffer()
106 * method to work. Do NOT change a thing unless you know what you're doing */
getBufferPointer()107 u8 *ICMPv6Header::getBufferPointer(){
108 return (u8*)(&this->h);
109 } /* End of getBufferPointer() */
110
111
112 /** Stores supplied packet in the internal buffer so the information
113 * can be accessed using the standard get & set methods.
114 * @warning The ICMPv6Header class is able to hold a maximum of
115 * sizeof(nping_icmpv6_hdr_t) bytes. If the supplied buffer is longer than
116 * that, only the first 1508 bytes will be stored in the internal buffer.
117 * @warning Supplied len MUST be at least 8 bytes (min ICMPv6 header length).
118 * @return OP_SUCCESS on success and OP_FAILURE in case of error */
storeRecvData(const u8 * buf,size_t len)119 int ICMPv6Header::storeRecvData(const u8 *buf, size_t len){
120 if(buf==NULL || len<ICMPv6_MIN_HEADER_LEN){
121 this->length=0;
122 return OP_FAILURE;
123 }else{
124 int stored_len = MIN( sizeof(nping_icmpv6_hdr_t), len);
125 this->reset(); /* Re-init the object, just in case the caller had used it already */
126 this->length=stored_len;
127 memcpy(&(this->h), buf, stored_len);
128 }
129 return OP_SUCCESS;
130 } /* End of storeRecvData() */
131
132
133 /* Returns a protocol identifier. This is used by packet parsing funtions
134 * that return linked lists of PacketElement objects, to determine the protocol
135 * the object represents. */
protocol_id() const136 int ICMPv6Header::protocol_id() const {
137 return HEADER_TYPE_ICMPv6;
138 } /* End of protocol_id() */
139
140
141 /** Determines if the data stored in the object after an storeRecvData() call
142 * is valid and safe to use. This mainly checks the length of the data but may
143 * also test the value of certain protocol fields to ensure their correctness.
144 * @return the length, in bytes, of the header, if its found to be valid or
145 * OP_FAILURE (-1) otherwise. */
validate()146 int ICMPv6Header::validate(){
147 int should_have=this->getHeaderLengthFromType( this->getType() );
148 if(this->length < should_have){
149 return OP_FAILURE;
150 }else{
151 /* WARNING: If we extend this class to support new ICMPv6 types with
152 * a variable length header (not even sure they exist), we need to
153 * parse the objects data and return our actual size, not this size that
154 * is obtained from the type. */
155 return should_have;
156 }
157 } /* End of validate() */
158
159
160 /** Prints the contents of the header and calls print() on the next protocol
161 * header in the chain (if there is any).
162 * @return OP_SUCCESS on success and OP_FAILURE in case of error. */
print(FILE * output,int detail) const163 int ICMPv6Header::print(FILE *output, int detail) const {
164 u8 type=this->getType();
165 u8 code=this->getCode();
166 const char *typestr=this->type2string(type, code);
167
168 fprintf(output, "ICMPv6[%s", typestr);
169 if(detail>=PRINT_DETAIL_MED)
170 fprintf(output, " (type=%u/code=%u)", type, code);
171
172 switch(type) {
173
174 case ICMPv6_UNREACH:
175 case ICMPv6_TIMXCEED:
176 if(detail>=PRINT_DETAIL_HIGH)
177 fprintf(output, " unused=%lu", (long unsigned int)this->getUnused());
178 break;
179
180 case ICMPv6_ROUTERSOLICIT:
181 if(detail>=PRINT_DETAIL_HIGH)
182 fprintf(output, " reserved=%lu", (long unsigned int)this->getReserved());
183 break;
184
185 case ICMPv6_PKTTOOBIG:
186 fprintf(output, " mtu=%lu", (long unsigned int)this->getMTU());
187 break;
188
189 case ICMPv6_PARAMPROB:
190 fprintf(output, " pointer=%lu", (long unsigned int)this->getPointer());
191 break;
192
193 case ICMPv6_ECHO:
194 case ICMPv6_ECHOREPLY:
195 fprintf(output, " id=%u seq=%u", this->getIdentifier(), this->getSequence());
196 break;
197
198 case ICMPv6_NODEINFOQUERY:
199 case ICMPv6_NODEINFORESP:
200 if(this->getNodeInfoFlags()!=0){
201 fprintf(output, " flags=");
202 if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_T)
203 fprintf(output, "T");
204 if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_A)
205 fprintf(output, "A");
206 if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_C)
207 fprintf(output, "C");
208 if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_L)
209 fprintf(output, "L");
210 if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_G)
211 fprintf(output, "G");
212 if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_S)
213 fprintf(output, "S");
214 }
215 if(detail>=PRINT_DETAIL_HIGH){
216 #ifdef WIN32
217 fprintf(output, " nonce=%I64u", (long long unsigned int)this->getNonce());
218 #else
219 fprintf(output, " nonce=%llu", (long long unsigned int)this->getNonce());
220 #endif
221 }
222 break;
223
224 default:
225 /* Print nothing */
226 break;
227 }
228
229 if(detail>=PRINT_DETAIL_HIGH)
230 fprintf(output, " csum=0x%04X", ntohs(this->getSum()));
231 fprintf(output, "]");
232 if(this->next!=NULL){
233 print_separator(output, detail);
234 next->print(output, detail);
235 }
236 return OP_SUCCESS;
237 } /* End of print() */
238
239
240 /******************************************************************************/
241 /* PROTOCOL-SPECIFIC METHODS */
242 /******************************************************************************/
243
244 /******************************************************************************/
245 /* ICMPv6 COMMON HEADER */
246 /******************************************************************************/
247
248 /** Set ICMPv6 type field */
setType(u8 val)249 int ICMPv6Header::setType(u8 val){
250 this->h.type = val;
251 this->length = getHeaderLengthFromType(val);
252 return OP_SUCCESS;
253 } /* End of setType() */
254
255
256 /** Returns ICMPv6 type field */
getType() const257 u8 ICMPv6Header::getType() const {
258 return this->h.type;
259 } /* End of getType() */
260
261
262 /* Returns true if the supplied ICMPv6 type is supported by this class */
validateType(u8 val)263 bool ICMPv6Header::validateType(u8 val){
264 switch( val ){
265 case ICMPv6_UNREACH:
266 case ICMPv6_PKTTOOBIG:
267 case ICMPv6_TIMXCEED:
268 case ICMPv6_PARAMPROB:
269 case ICMPv6_ECHO:
270 case ICMPv6_ECHOREPLY:
271 case ICMPv6_ROUTERSOLICIT:
272 case ICMPv6_ROUTERADVERT:
273 case ICMPv6_NGHBRSOLICIT:
274 case ICMPv6_NGHBRADVERT:
275 case ICMPv6_REDIRECT:
276 case ICMPv6_RTRRENUM:
277 return true;
278 break;
279
280 default:
281 return false;
282 break;
283 }
284 return false;
285 } /* End of validateType() */
286
287
validateType()288 bool ICMPv6Header::validateType(){
289 return validateType(this->h.type);
290 } /* End of validateType() */
291
292
293 /** Set ICMPv6 code field */
setCode(u8 val)294 int ICMPv6Header::setCode(u8 val){
295 this->h.code = val;
296 return OP_SUCCESS;
297 } /* End of setCode() */
298
299
300 /** Returns ICMPv6 code field */
getCode() const301 u8 ICMPv6Header::getCode() const {
302 return this->h.code;
303 } /* End of getCode() */
304
305
306 /** Given an ICMP Type and a code, determines whether the code corresponds to
307 * a RFC compliant code (eg: code 0x03 for "port unreachable" in ICMP
308 * Unreachable messages) or just some other bogus code. */
validateCode(u8 type,u8 code)309 bool ICMPv6Header::validateCode(u8 type, u8 code){
310 // switch (type){
311 //
312 // case ICMPv6_UNREACH:
313 // return (code==0);
314 // break;
315 //
316 // case ICMPv6_PKTTOOBIG:
317 // switch( code ){
318 // case XXXXXXXXXXXX:
319 // case YYYYYYYYYYYY:
320 // case ZZZZZZZZZZZZ:
321 // return true;
322 // break;
323 // }
324 // break;
325 //
326 // case ICMPv6_TIMXCEED:
327 //
328 // break;
329 //
330 // case ICMPv6_PARAMPROB:
331 //
332 // break;
333 //
334 // case ICMPv6_ECHO:
335 //
336 // break;
337 //
338 // case ICMPv6_ECHOREPLY:
339 //
340 // break;
341 //
342 // case ICMPv6_ROUTERSOLICIT:
343 // case ICMPv6_ROUTERADVERT:
344 // case ICMPv6_NGHBRSOLICIT:
345 // case ICMPv6_NGHBRADVERT:
346 // case ICMPv6_REDIRECT:
347 // break;
348 //
349 // default:
350 // return false;
351 // break;
352 // }
353 return false;
354 } /* End of validateCode() */
355
356
357 /** Computes the ICMP header checksum and sets the checksum field to the right
358 * value.
359 * @warning This method requires the ICMPv6Object to be linked to an IPv6Header
360 * object, so make sure setNextElement() has been called like this:
361 *
362 * IPv6Header ip6;
363 * ICMPv6Header icmp6;
364 * [...] # Set header fields
365 * ip6.setNextElement(&icmp6);
366 * icmp6.setSum();
367 *
368 * Note that there can be a number of extension headers between the ICMPv6
369 * header and the IPv6 one, but all of them need to be linked in order for this
370 * method to traverse the list of headers and find the IPv6 source and
371 * destination address, required to compute the checksum. So things like the
372 * following are OK:
373 *
374 * IPv6Header ip6;
375 * HopByHopHeader hop;
376 * RoutingHeader rte;
377 * FragmentHeader frg;
378 * ICMPv6Header icmp6;
379 * [...] # Set whatever header fields you need
380 * ip6.setNextElement(&hop);
381 * hop.setNextElement(&rte);
382 * rte.setNextElement(&frg);
383 * frg.setNextElement(&icmp6);
384 * icmp6.setSum(); # setSum() will be able to reach the IPv6Header.
385 *
386 */
setSum()387 int ICMPv6Header::setSum(){
388 PacketElement *hdr;
389 hdr=this->getPrevElement();
390 /* Traverse the list of headers backwards until we find the IPv6 header */
391 while(hdr!=NULL){
392 if (hdr->protocol_id()==HEADER_TYPE_IPv6){
393 IPv6Header *v6hdr=(IPv6Header *)hdr;
394 struct in6_addr i6src, i6dst;
395 this->h.checksum=0;
396 memcpy(i6src.s6_addr, v6hdr->getSourceAddress(), 16);
397 memcpy(i6dst.s6_addr, v6hdr->getDestinationAddress(), 16);
398 u8 *buff=(u8 *)safe_malloc(this->getLen());
399 this->dumpToBinaryBuffer(buff, this->getLen());
400 this->h.checksum=ipv6_pseudoheader_cksum(&i6src, &i6dst, this->protocol_id(), this->getLen(), buff);
401 free(buff);
402 return OP_SUCCESS;
403 }else{
404 hdr=hdr->getPrevElement();
405 }
406 }
407 return OP_FAILURE;
408 } /* End of setSum() */
409
410
411 /** @warning Sum is set to supplied value with NO byte ordering conversion
412 * performed.
413 * @warning If sum is supplied this way, no error checks are made. Caller is
414 * responsible for the correctness of the value. */
setSum(u16 s)415 int ICMPv6Header::setSum(u16 s){
416 this->h.checksum=s;
417 return OP_SUCCESS;
418 } /* End of setSum() */
419
420
421 /** Returns the value of the checksum field.
422 * @warning The returned value is in NETWORK byte order, no conversion is
423 * performed */
getSum() const424 u16 ICMPv6Header::getSum() const{
425 return this->h.checksum;
426 } /* End of getSum() */
427
428
429 /** @warning Supplied value MUST be in host byte order because it will get
430 * converted by this method using htonl() */
setReserved(u32 val)431 int ICMPv6Header::setReserved(u32 val){
432 u32 aux32=0;
433 u8 *auxpnt=(u8 *)&aux32;
434
435 switch(this->h.type){
436
437 case ICMPv6_UNREACH:
438 this->h_du->unused=htonl(val);
439 break;
440
441 case ICMPv6_TIMXCEED:
442 this->h_te->unused=htonl(val);
443 break;
444
445 case ICMPv6_ROUTERSOLICIT:
446 this->h_rs->reserved=htonl(val);
447 break;
448
449 case ICMPv6_NGHBRSOLICIT:
450 this->h_ns->reserved=htonl(val);
451 break;
452
453 case ICMPv6_REDIRECT:
454 this->h_r->reserved=htonl(val);
455 break;
456
457
458 case ICMPv6_NGHBRADVERT:
459 /* The reserved field in Neighbor Advertisement messages is only
460 * 24-bits long so we convert the supplied value to big endian and
461 * use only the 24 least significant bits. */
462 aux32=htonl(val);
463 this->h_na->reserved[0]=auxpnt[1];
464 this->h_na->reserved[1]=auxpnt[2];
465 this->h_na->reserved[2]=auxpnt[3];
466 break;
467
468 case ICMPv6_RTRRENUM:
469 this->h_rr->reserved=htonl(val);
470 break;
471
472 /* Types that don't have a reserved field */
473 case ICMPv6_ROUTERADVERT:
474 case ICMPv6_ECHO:
475 case ICMPv6_ECHOREPLY:
476 case ICMPv6_PARAMPROB:
477 case ICMPv6_PKTTOOBIG:
478 default:
479 return OP_FAILURE;
480 break;
481 }
482 return OP_SUCCESS;
483 } /* End of setReserved() */
484
485
486 /** @warning Returned value is in host byte order */
getReserved() const487 u32 ICMPv6Header::getReserved() const {
488 u32 aux32=0;
489 u8 *auxpnt=(u8 *)&aux32;
490
491 switch(this->h.type){
492
493 case ICMPv6_UNREACH:
494 return ntohl(this->h_du->unused);
495 break;
496
497 case ICMPv6_TIMXCEED:
498 return ntohl(this->h_te->unused);
499 break;
500
501 case ICMPv6_ROUTERSOLICIT:
502 return ntohl(this->h_rs->reserved);
503 break;
504
505 case ICMPv6_NGHBRSOLICIT:
506 return ntohl(this->h_ns->reserved);
507 break;
508
509 case ICMPv6_REDIRECT:
510 return ntohl(this->h_r->reserved);
511 break;
512
513 case ICMPv6_NGHBRADVERT:
514 /* The reserved field in Neighbor Advertisement messages is only
515 * 24-bits long so we extract the stored value and convert it to host
516 * byte order. */
517 auxpnt[0]=0;
518 auxpnt[1]=this->h_na->reserved[0];
519 auxpnt[2]=this->h_na->reserved[1];
520 auxpnt[3]=this->h_na->reserved[2];
521 return ntohl(aux32);
522 break;
523
524 case ICMPv6_RTRRENUM:
525 return ntohl(this->h_rr->reserved);
526 break;
527
528 /* Types that don't have a reserved field */
529 case ICMPv6_ROUTERADVERT:
530 case ICMPv6_ECHO:
531 case ICMPv6_ECHOREPLY:
532 case ICMPv6_PARAMPROB:
533 case ICMPv6_PKTTOOBIG:
534 default:
535 return 0;
536 break;
537 }
538 } /* End of setReserved() */
539
setUnused(u32 val)540 int ICMPv6Header::setUnused(u32 val){
541 return this->setReserved(val);
542 } /* End of setUnused() */
543
544
getUnused() const545 u32 ICMPv6Header::getUnused() const {
546 return this->getReserved();
547 } /* End of getUnused() */
548
549
setFlags(u8 val)550 int ICMPv6Header::setFlags(u8 val){
551 switch(this->h.type){
552
553 case ICMPv6_ROUTERADVERT:
554 this->h_ra->autoconfig_flags=val;
555 break;
556
557 case ICMPv6_NGHBRADVERT:
558 this->h_na->flags=val;
559 break;
560
561 case ICMPv6_RTRRENUM:
562 this->h_rr->flags=val;
563 break;
564
565 case ICMPv6_NODEINFOQUERY:
566 case ICMPv6_NODEINFORESP:
567 netutil_fatal("setFlags() cannot be used in NI, use setNodeInfoFlags() instead\n");
568 break;
569
570 /* Types that don't have a flags field */
571 case ICMPv6_TIMXCEED:
572 case ICMPv6_UNREACH:
573 case ICMPv6_ROUTERSOLICIT:
574 case ICMPv6_NGHBRSOLICIT:
575 case ICMPv6_REDIRECT:
576 case ICMPv6_ECHO:
577 case ICMPv6_ECHOREPLY:
578 case ICMPv6_PARAMPROB:
579 case ICMPv6_PKTTOOBIG:
580 default:
581 return OP_FAILURE;
582 break;
583 }
584 return OP_SUCCESS;
585 } /* End of setFlags() */
586
587
getFlags() const588 u8 ICMPv6Header::getFlags() const {
589 switch(this->h.type){
590
591 case ICMPv6_ROUTERADVERT:
592 return this->h_ra->autoconfig_flags;
593 break;
594
595 case ICMPv6_NGHBRADVERT:
596 return this->h_na->flags;
597 break;
598
599 case ICMPv6_RTRRENUM:
600 return this->h_rr->flags;
601 break;
602
603 case ICMPv6_NODEINFOQUERY:
604 case ICMPv6_NODEINFORESP:
605 netutil_fatal("getFlags() cannot be used in NI, use getNodeInfoFlags() instead\n");
606 return 0;
607 break;
608
609 /* Types that don't have a flags field */
610 case ICMPv6_TIMXCEED:
611 case ICMPv6_UNREACH:
612 case ICMPv6_ROUTERSOLICIT:
613 case ICMPv6_NGHBRSOLICIT:
614 case ICMPv6_REDIRECT:
615 case ICMPv6_ECHO:
616 case ICMPv6_ECHOREPLY:
617 case ICMPv6_PARAMPROB:
618 case ICMPv6_PKTTOOBIG:
619 default:
620 return 0;
621 break;
622 }
623 } /* End of getFlags() */
624
625 /******************************************************************************/
626 /* ICMPv6 DESTINATION UNREACHABLE */
627 /******************************************************************************/
628
629 /******************************************************************************/
630 /* ICMPv6 PACKET TOO BIG */
631 /******************************************************************************/
setMTU(u32 mtu)632 int ICMPv6Header::setMTU(u32 mtu){
633 this->h_ptb->mtu=htonl(mtu);
634 return OP_SUCCESS;
635 } /* End of setMTU() */
636
getMTU() const637 u32 ICMPv6Header::getMTU() const {
638 return ntohl(this->h_ptb->mtu);
639 } /* End of getMTU() */
640
641 /******************************************************************************/
642 /* ICMPv6 TIME EXCEEDED */
643 /******************************************************************************/
644
645 /******************************************************************************/
646 /* ICMPv6 PARAMETER PROBLEM */
647 /******************************************************************************/
setPointer(u32 pnt)648 int ICMPv6Header::setPointer(u32 pnt){
649 this->h_pp->pointer=htonl(pnt);
650 return OP_SUCCESS;
651 } /* End of setPointer() */
652
653
getPointer() const654 u32 ICMPv6Header::getPointer() const {
655 return ntohl(this->h_pp->pointer);
656 } /* End of getPointer() */
657
658 /******************************************************************************/
659 /* ICMPv6 ECHO */
660 /******************************************************************************/
setIdentifier(u16 val)661 int ICMPv6Header::setIdentifier(u16 val){
662 this->h_e->id=htons(val);
663 return OP_SUCCESS;
664 } /* End of setIdentifier() */
665
666
getIdentifier() const667 u16 ICMPv6Header::getIdentifier() const{
668 return ntohs(this->h_e->id);
669 } /* End of getIdentifier() */
670
671
setSequence(u16 val)672 int ICMPv6Header::setSequence(u16 val){
673 switch(this->h.type){
674 case ICMPv6_RTRRENUM:
675 this->h_rr->seq=htonl( ((u32)val) );
676 break;
677
678 case ICMPv6_ECHO:
679 case ICMPv6_ECHOREPLY:
680 this->h_e->seq=htons(val);
681 break;
682
683 default:
684 return OP_FAILURE;
685 break;
686 }
687 return OP_SUCCESS;
688 } /* End of setSequence() */
689
690
setSequence(u32 val)691 int ICMPv6Header::setSequence(u32 val){
692 switch(this->h.type){
693 case ICMPv6_RTRRENUM:
694 this->h_rr->seq=htonl(val);
695 break;
696
697 case ICMPv6_ECHO:
698 case ICMPv6_ECHOREPLY:
699 this->h_e->seq=htons( ((u16)val) );
700 break;
701
702 default:
703 return OP_FAILURE;
704 break;
705 }
706 return OP_SUCCESS;
707 } /* End of setSequence() */
708
709
getSequence() const710 u32 ICMPv6Header::getSequence() const{
711 switch(this->h.type){
712 case ICMPv6_RTRRENUM:
713 return ntohl(this->h_rr->seq);
714 break;
715
716 case ICMPv6_ECHO:
717 case ICMPv6_ECHOREPLY:
718 return (u32)ntohs(this->h_e->seq);
719 break;
720 }
721 return 0;
722 } /* End of getSequence() */
723
724
725 /******************************************************************************/
726 /* ICMPv6 ROUTER ADVERTISEMENT */
727 /******************************************************************************/
setCurrentHopLimit(u8 val)728 int ICMPv6Header::setCurrentHopLimit(u8 val){
729 this->h_ra->current_hop_limit=val;
730 return OP_SUCCESS;
731 } /* End of setCurrentHopLimit() */
732
getCurrentHopLimit() const733 u8 ICMPv6Header::getCurrentHopLimit() const {
734 return this->h_ra->current_hop_limit;
735 } /* End of getCurrentHopLimit() */
736
setRouterLifetime(u16 val)737 int ICMPv6Header::setRouterLifetime(u16 val){
738 this->h_ra->router_lifetime=val;
739 return OP_SUCCESS;
740 } /* End of setRouterLifetime() */
741
getRouterLifetime() const742 u16 ICMPv6Header::getRouterLifetime() const {
743 return this->h_ra->router_lifetime;
744 } /* End of getRouterLifetime() */
745
setReachableTime(u32 val)746 int ICMPv6Header::setReachableTime(u32 val){
747 this->h_ra->reachable_time=val;
748 return OP_SUCCESS;
749 } /* End of setReachableTime() */
750
getReachableTime() const751 u32 ICMPv6Header::getReachableTime() const {
752 return this->h_ra->reachable_time;
753 } /* End of getReachableTime() */
754
setRetransmissionTimer(u32 val)755 int ICMPv6Header::setRetransmissionTimer(u32 val){
756 this->h_ra->retransmission_timer=val;
757 return OP_SUCCESS;
758 } /* End of setRetransmissionTimer() */
759
getRetransmissionTimer() const760 u32 ICMPv6Header::getRetransmissionTimer() const {
761 return this->h_ra->retransmission_timer;
762 } /* End of getRetransmissionTimer() */
763
764 /******************************************************************************/
765 /* ICMPv6 ROUTER SOLICITATION */
766 /******************************************************************************/
767
768 /******************************************************************************/
769 /* ICMPv6 NEIGHBOR ADVERTISEMENT */
770 /******************************************************************************/
771
setTargetAddress(struct in6_addr addr)772 int ICMPv6Header::setTargetAddress(struct in6_addr addr){
773 switch(this->h.type){
774 case ICMPv6_NGHBRADVERT:
775 memcpy(this->h_na->target_address, addr.s6_addr, 16);
776 break;
777
778 case ICMPv6_NGHBRSOLICIT:
779 memcpy(this->h_ns->target_address, addr.s6_addr, 16);
780 break;
781
782 case ICMPv6_REDIRECT:
783 memcpy(this->h_r->target_address, addr.s6_addr, 16);
784 break;
785
786 default:
787 return OP_FAILURE;
788 break;
789 }
790 return OP_SUCCESS;
791 } /* End of setTargetAddress() */
792
793
getTargetAddress() const794 struct in6_addr ICMPv6Header::getTargetAddress() const {
795 struct in6_addr addr;
796 memset(&addr, 0, sizeof(struct in6_addr));
797
798 switch(this->h.type){
799 case ICMPv6_NGHBRADVERT:
800 memcpy(addr.s6_addr, this->h_na->target_address, 16);
801 break;
802
803 case ICMPv6_NGHBRSOLICIT:
804 memcpy(addr.s6_addr, this->h_ns->target_address, 16);
805 break;
806
807 case ICMPv6_REDIRECT:
808 memcpy(addr.s6_addr, this->h_r->target_address, 16);
809 break;
810 }
811 return addr;
812 } /* End of setTargetAddress() */
813
814
setDestinationAddress(struct in6_addr addr)815 int ICMPv6Header::setDestinationAddress(struct in6_addr addr){
816 switch(this->h.type){
817 case ICMPv6_REDIRECT:
818 memcpy(this->h_r->destination_address, addr.s6_addr, 16);
819 break;
820
821 default:
822 return OP_FAILURE;
823 break;
824 }
825 return OP_SUCCESS;
826 } /* End of setDestinationAddress() */
827
828
getDestinationAddress() const829 struct in6_addr ICMPv6Header::getDestinationAddress() const {
830 struct in6_addr addr;
831 memset(&addr, 0, sizeof(struct in6_addr));
832
833 switch(this->h.type){
834 case ICMPv6_REDIRECT:
835 memcpy(addr.s6_addr, this->h_r->destination_address, 16);
836 break;
837 }
838 return addr;
839 } /* End of setTargetAddress() */
840
841
842 /******************************************************************************/
843 /* ICMPv6 NEIGHBOR SOLICITATION */
844 /******************************************************************************/
845
846 /******************************************************************************/
847 /* ICMPv6 REDIRECT */
848 /******************************************************************************/
849
850 /******************************************************************************/
851 /* ICMPv6 ROUTER RENUMBERING */
852 /******************************************************************************/
setSegmentNumber(u8 val)853 int ICMPv6Header::setSegmentNumber(u8 val){
854 this->h_rr->segment_number=val;
855 return OP_SUCCESS;
856 } /* End of setSegmentNumber() */
857
getSegmentNumber() const858 u8 ICMPv6Header::getSegmentNumber() const {
859 return this->h_rr->segment_number;
860 } /* End of getSegmentNumber() */
861
setMaxDelay(u16 val)862 int ICMPv6Header::setMaxDelay(u16 val){
863 switch(this->h.type){
864 case ICMPv6_RTRRENUM:
865 this->h_rr->max_delay=htons(val);
866 return OP_SUCCESS;
867 break;
868
869 case ICMPv6_GRPMEMBQUERY:
870 case ICMPv6_GRPMEMBREP:
871 case ICMPv6_GRPMEMBRED:
872 this->h_mld->max_response_delay=htons(val);
873 return OP_SUCCESS;
874 break;
875
876 default:
877 return OP_FAILURE;
878 break;
879 }
880 } /* End of setMaxDelay() */
881
882
getMaxDelay() const883 u16 ICMPv6Header::getMaxDelay() const {
884 switch(this->h.type){
885 case ICMPv6_RTRRENUM:
886 return ntohs(this->h_rr->max_delay);
887 break;
888
889 case ICMPv6_GRPMEMBQUERY:
890 case ICMPv6_GRPMEMBREP:
891 case ICMPv6_GRPMEMBRED:
892 return ntohs(this->h_mld->max_response_delay);
893 break;
894
895 default:
896 return 0;
897 break;
898 }
899 } /* End of getMaxDelay() */
900
901
902
903 /******************************************************************************/
904 /* ICMPv6 NODE INFORMATION QUERIES */
905 /******************************************************************************/
906 /** Set NI Qtype */
setQtype(u16 val)907 int ICMPv6Header::setQtype(u16 val){
908 this->h_ni->qtype = htons(val);
909 return OP_SUCCESS;
910 } /* End of setQtype() */
911
912
913 /** Returns NI Qtype */
getQtype() const914 u16 ICMPv6Header::getQtype() const {
915 return ntohs(this->h_ni->qtype);
916 } /* End of getQtype() */
917
918
919 /** Set NI Flags */
setNodeInfoFlags(u16 val)920 int ICMPv6Header::setNodeInfoFlags(u16 val){
921 this->h_ni->flags = htons(val);
922 return OP_SUCCESS;
923 } /* End of setNodeInfoFlags() */
924
925
926 /** Returns NI Flags */
getNodeInfoFlags() const927 u16 ICMPv6Header::getNodeInfoFlags() const {
928 return ntohs(this->h_ni->flags);
929 } /* End of getNodeInfoFlags() */
930
931
932 /* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
933 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
934 | unused |G|S|L|C|A|T|
935 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
936
937 /* Set NI Flag G */
setG(bool flag_value)938 int ICMPv6Header::setG(bool flag_value){
939 u16 current_flags = this->getNodeInfoFlags();
940 if(flag_value)
941 current_flags = current_flags | 0x0020;
942 else
943 current_flags = current_flags & ~0x0020;
944 this->setNodeInfoFlags(current_flags);
945 return OP_SUCCESS;
946 } /* End of setG() */
947
948
949 /* Get NI Flag G */
getG() const950 bool ICMPv6Header::getG() const {
951 return this->getNodeInfoFlags() & 0x0020;
952 } /* End of getG() */
953
954
955 /* Set NI Flag S */
setS(bool flag_value)956 int ICMPv6Header::setS(bool flag_value){
957 u16 current_flags = this->getNodeInfoFlags();
958 if(flag_value)
959 current_flags = current_flags | 0x0010;
960 else
961 current_flags = current_flags & ~0x0010;
962 this->setNodeInfoFlags(current_flags);
963 return OP_SUCCESS;
964 } /* End of setS() */
965
966
967 /* Get NI Flag S */
getS() const968 bool ICMPv6Header::getS() const {
969 return this->getNodeInfoFlags() & 0x0010;
970 } /* End of getS() */
971
972
973 /* Set NI Flag L */
setL(bool flag_value)974 int ICMPv6Header::setL(bool flag_value){
975 u16 current_flags = this->getNodeInfoFlags();
976 if(flag_value)
977 current_flags = current_flags | 0x0008;
978 else
979 current_flags = current_flags & ~0x0008;
980 this->setNodeInfoFlags(current_flags);
981 return OP_SUCCESS;
982 } /* End of setL() */
983
984
985 /* Get NI Flag L */
getL() const986 bool ICMPv6Header::getL() const {
987 return this->getNodeInfoFlags() & 0x0008;
988 } /* End of getL() */
989
990
991 /* Set NI Flag C */
setC(bool flag_value)992 int ICMPv6Header::setC(bool flag_value){
993 u16 current_flags = this->getNodeInfoFlags();
994 if(flag_value)
995 current_flags = current_flags | 0x0004;
996 else
997 current_flags = current_flags & ~0x0004;
998 this->setNodeInfoFlags(current_flags);
999 return OP_SUCCESS;
1000 } /* End of setC() */
1001
1002
1003 /* Get NI Flag C */
getC() const1004 bool ICMPv6Header::getC() const {
1005 return this->getNodeInfoFlags() & 0x0004;
1006 } /* End of getC() */
1007
1008
1009 /* Set NI Flag A */
setA(bool flag_value)1010 int ICMPv6Header::setA(bool flag_value){
1011 u16 current_flags = this->getNodeInfoFlags();
1012 if(flag_value)
1013 current_flags = current_flags | 0x0002;
1014 else
1015 current_flags = current_flags & ~0x0002;
1016 this->setNodeInfoFlags(current_flags);
1017 return OP_SUCCESS;
1018 } /* End of setA() */
1019
1020
1021 /* Get NI Flag A */
getA() const1022 bool ICMPv6Header::getA() const {
1023 return this->getNodeInfoFlags() & 0x0002;
1024 } /* End of getA() */
1025
1026
1027 /* Set NI Flag T */
setT(bool flag_value)1028 int ICMPv6Header::setT(bool flag_value){
1029 u16 current_flags = this->getNodeInfoFlags();
1030 if(flag_value)
1031 current_flags = current_flags | 0x0001;
1032 else
1033 current_flags = current_flags & ~0x0001;
1034 this->setNodeInfoFlags(current_flags);
1035 return OP_SUCCESS;
1036 } /* End of setT() */
1037
1038
1039 /* Get NI Flag T */
getT() const1040 bool ICMPv6Header::getT() const {
1041 return this->getNodeInfoFlags() & 0x0001;
1042 } /* End of getT() */
1043
1044
1045 /* Set the Nonce field. */
setNonce(u64 nonce_value)1046 int ICMPv6Header::setNonce(u64 nonce_value){
1047 this->h_ni->nonce=nonce_value;
1048 return OP_SUCCESS;
1049 } /* End of setNonce() */
1050
1051
1052 /* Set the Nonce field.
1053 * @warning: Supplied buffer must contain 8 bytes. */
setNonce(const u8 * nonce)1054 int ICMPv6Header::setNonce(const u8 *nonce){
1055 if(nonce==NULL)
1056 return OP_FAILURE;
1057 memcpy(&(this->h_ni->nonce), nonce, NI_NONCE_LEN);
1058 return OP_SUCCESS;
1059 } /* End of setNonce() */
1060
1061
1062 /* Returns a pointer to the nonce buffer.
1063 * @warning: The returned pointer is guaranteed to point to an 8-byte buffer.
1064 * However, what comes after the 8th byte is unspecified. */
getNonce() const1065 u64 ICMPv6Header::getNonce() const {
1066 return this->h_ni->nonce;
1067 } /* End of getNonce() */
1068
1069
1070 /******************************************************************************/
1071 /* MULTICAST LISTENER DISCOVERY */
1072 /******************************************************************************/
1073
setMulticastAddress(struct in6_addr addr)1074 int ICMPv6Header::setMulticastAddress(struct in6_addr addr){
1075 switch(this->h.type){
1076 case ICMPv6_GRPMEMBQUERY:
1077 case ICMPv6_GRPMEMBREP:
1078 case ICMPv6_GRPMEMBRED:
1079 memcpy(this->h_mld->mcast_address, addr.s6_addr, 16);
1080 break;
1081
1082 default:
1083 return OP_FAILURE;
1084 break;
1085 }
1086
1087 return OP_SUCCESS;
1088 } /* End of setMulticastAddress() */
1089
1090
getMulticastAddress() const1091 struct in6_addr ICMPv6Header::getMulticastAddress() const {
1092 struct in6_addr addr;
1093 memset(&addr, 0, sizeof(struct in6_addr));
1094
1095 switch(this->h.type){
1096 case ICMPv6_GRPMEMBQUERY:
1097 case ICMPv6_GRPMEMBREP:
1098 case ICMPv6_GRPMEMBRED:
1099 memcpy(addr.s6_addr, this->h_mld->mcast_address, 16);
1100 break;
1101 }
1102 return addr;
1103 } /* End of setMulticastAddress() */
1104
1105
1106 /******************************************************************************/
1107 /* MISCELLANEOUS STUFF */
1108 /******************************************************************************/
1109
1110 /** Returns the standard ICMPv6 header length for the supplied ICMP message type.
1111 * @warning Return value corresponds strictly to the ICMP header, this is,
1112 * the minimum length of the ICMP header, variable length payload is never
1113 * included. For example, an ICMPv6 Redirect has a fixed header of 40
1114 * bytes but then the packet may contain ICMPv6 options. We only return 40
1115 * because we don't know in advance the total number of bytes for the message.
1116 * Same applies to the rest of types. */
getHeaderLengthFromType(u8 type) const1117 int ICMPv6Header::getHeaderLengthFromType(u8 type) const {
1118
1119 switch( type ){
1120 case ICMPv6_UNREACH:
1121 return ICMPv6_UNREACH_LEN;
1122 break;
1123 case ICMPv6_PKTTOOBIG:
1124 return ICMPv6_PKTTOOBIG_LEN;
1125 break;
1126
1127 case ICMPv6_TIMXCEED:
1128 return ICMPv6_TIMXCEED_LEN;
1129 break;
1130
1131 case ICMPv6_PARAMPROB:
1132 return ICMPv6_PARAMPROB_LEN;
1133 break;
1134
1135 case ICMPv6_ECHO:
1136 return ICMPv6_ECHO_LEN;
1137 break;
1138
1139 case ICMPv6_ECHOREPLY:
1140 return ICMPv6_ECHOREPLY_LEN;
1141 break;
1142
1143 case ICMPv6_ROUTERSOLICIT:
1144 return ICMPv6_ROUTERSOLICIT_LEN;
1145 break;
1146
1147 case ICMPv6_ROUTERADVERT:
1148 return ICMPv6_ROUTERADVERT_LEN;
1149 break;
1150
1151 case ICMPv6_NGHBRSOLICIT:
1152 return ICMPv6_NGHBRSOLICIT_LEN;
1153 break;
1154
1155 case ICMPv6_NGHBRADVERT:
1156 return ICMPv6_NGHBRADVERT_LEN;
1157 break;
1158
1159 case ICMPv6_REDIRECT:
1160 return ICMPv6_REDIRECT_LEN;
1161 break;
1162
1163 case ICMPv6_RTRRENUM:
1164 return ICMPv6_RTRRENUM_LEN;
1165 break;
1166
1167 case ICMPv6_NODEINFOQUERY:
1168 case ICMPv6_NODEINFORESP:
1169 return ICMPv6_NODEINFO_LEN;
1170 break;
1171
1172 case ICMPv6_GRPMEMBQUERY:
1173 case ICMPv6_GRPMEMBREP:
1174 case ICMPv6_GRPMEMBRED:
1175 return ICMPv6_MLD_LEN;
1176 break;
1177
1178 /* Packets with non RFC-Compliant types will be represented as an 8-byte
1179 * ICMPv6 header, just like the types that don't include additional info */
1180 default:
1181 return ICMPv6_MIN_HEADER_LEN;
1182 break;
1183 }
1184 } /* End of getHeaderLengthFromType() */
1185
1186
1187 /* Returns true if the packet is an ICMPv6 error message. */
isError() const1188 bool ICMPv6Header::isError() const {
1189 switch( this->getType() ){
1190 case ICMPv6_UNREACH:
1191 case ICMPv6_PKTTOOBIG:
1192 case ICMPv6_TIMXCEED:
1193 case ICMPv6_PARAMPROB:
1194 return true;
1195 break;
1196
1197 default:
1198 return false;
1199 break;
1200 }
1201 } /* End of isError() */
1202
1203
type2string(int type,int code) const1204 const char *ICMPv6Header::type2string(int type, int code) const {
1205 switch(type) {
1206
1207 case ICMPv6_UNREACH:
1208 switch(code) {
1209 case ICMPv6_UNREACH_NO_ROUTE: return "Network unreachable"; break;
1210 case ICMPv6_UNREACH_PROHIBITED: return "Comm prohibited"; break;
1211 case ICMPv6_UNREACH_BEYOND_SCOPE: return "Beyond scope"; break;
1212 case ICMPv6_UNREACH_ADDR_UNREACH: return "Address unreachable"; break;
1213 case ICMPv6_UNREACH_PORT_UNREACH: return "Port unreachable"; break;
1214 case ICMPv6_UNREACH_SRC_ADDR_FAILED: return "Source address failed"; break;
1215 case ICMPv6_UNREACH_REJECT_ROUTE: return "Reject route"; break;
1216 default: return "Destination unreachable (unknown code)"; break;
1217 }
1218 break;
1219
1220 case ICMPv6_PKTTOOBIG:
1221 return "Packet too big";
1222 break;
1223
1224 case ICMPv6_TIMXCEED:
1225 switch(code){
1226 case ICMPv6_TIMXCEED_HOP_EXCEEDED: return "HopLimit=0 in transit"; break;
1227 case ICMPv6_TIMXCEED_REASS_EXCEEDED: return "Reassembly time exceeded"; break;
1228 default: return "Time exceeded (unknown code)"; break;
1229 }
1230 break;
1231
1232 case ICMPv6_PARAMPROB:
1233 switch(code){
1234 case ICMPv6_PARAMPROB_FIELD: return "Parameter problem (bad field)"; break;
1235 case ICMPv6_PARAMPROB_NEXT_HDR: return "Parameter problem (next header unknown)"; break;
1236 case ICMPv6_PARAMPROB_OPTION: return "Parameter problem (bad option)"; break;
1237 default: return "Parameter problem (unknown code)"; break;
1238 }
1239 break;
1240
1241 case ICMPv6_ECHO:
1242 return "Echo request";
1243 break;
1244 case ICMPv6_ECHOREPLY:
1245 return "Echo reply";
1246 break;
1247 case ICMPv6_GRPMEMBQUERY:
1248 return "Group membership query";
1249 break;
1250 case ICMPv6_GRPMEMBREP:
1251 return "Group membership report";
1252 break;
1253 case ICMPv6_GRPMEMBRED:
1254 return "Group membership reduction";
1255 break;
1256 case ICMPv6_ROUTERSOLICIT:
1257 return "Router sol";
1258 break;
1259 case ICMPv6_ROUTERADVERT:
1260 return "Router advert";
1261 break;
1262 case ICMPv6_NGHBRSOLICIT:
1263 return "Neighbor sol";
1264 break;
1265 case ICMPv6_NGHBRADVERT:
1266 return "Neighbor advert";
1267 break;
1268 case ICMPv6_REDIRECT:
1269 return "Redirect";
1270 break;
1271 case ICMPv6_RTRRENUM:
1272 switch(code){
1273 case ICMPv6_RTRRENUM_COMMAND: return "Renumbering command"; break;
1274 case ICMPv6_RTRRENUM_RESULT: return "Renumbering result"; break;
1275 case ICMPv6_RTRRENUM_SEQ_RESET: return "Renumbering reset"; break;
1276 default: return "Router Renumbering (unknown code)"; break;
1277 }
1278 break;
1279 case ICMPv6_NODEINFOQUERY:
1280 switch(code){
1281 case ICMPv6_NODEINFOQUERY_IPv6ADDR: return "Node info query (IPv6 addr)"; break;
1282 case ICMPv6_NODEINFOQUERY_NAME: return "Node info query (name)"; break;
1283 case ICMPv6_NODEINFOQUERY_IPv4ADDR: return "Node info query (IPv4 addr)"; break;
1284 default: return "Node info query (unknown code)"; break;
1285 }
1286 break;
1287
1288 case ICMPv6_NODEINFORESP:
1289 switch(code){
1290 case ICMPv6_NODEINFORESP_SUCCESS: return "Node info reply (success)"; break;
1291 case ICMPv6_NODEINFORESP_REFUSED: return "Node info reply (refused)"; break;
1292 case ICMPv6_NODEINFORESP_UNKNOWN: return "Node info reply (qtype unknown)"; break;
1293 default: return "Node info reply (unknown code)"; break;
1294 }
1295 break;
1296
1297 case ICMPv6_INVNGHBRSOLICIT:
1298 return "Inverse neighbor sol";
1299 break;
1300
1301 case ICMPv6_INVNGHBRADVERT:
1302 return "Inverse neighbor advert";
1303 break;
1304
1305 case ICMPv6_MLDV2:
1306 return "MLDv2 report";
1307 break;
1308
1309 case ICMPv6_AGENTDISCOVREQ:
1310 return "Home agent request";
1311 break;
1312
1313 case ICMPv6_AGENTDISCOVREPLY:
1314 return "Home agent reply";
1315 break;
1316
1317 case ICMPv6_MOBPREFIXSOLICIT:
1318 return "Prefix sol";
1319 break;
1320
1321 case ICMPv6_MOBPREFIXADVERT:
1322 return "Prefix advert";
1323 break;
1324
1325 case ICMPv6_CERTPATHSOLICIT:
1326 return "Cert path sol";
1327 break;
1328
1329 case ICMPv6_CERTPATHADVERT:
1330 return "Cert path advert";
1331 break;
1332
1333 case ICMPv6_EXPMOBILITY:
1334 return "Experimental mobility";
1335 break;
1336
1337 case ICMPv6_MRDADVERT:
1338 return "Multicast router advert";
1339 break;
1340
1341 case ICMPv6_MRDSOLICIT:
1342 return "Multicast router sol";
1343 break;
1344
1345 case ICMPv6_MRDTERMINATE:
1346 return "Multicast router term";
1347 break;
1348
1349 case ICMPv6_FMIPV6:
1350 return "FMIPv6";
1351 break;
1352
1353 default:
1354 return "Unknown ICMPv6 type";
1355 break;
1356 } /* End of ICMP Type switch */
1357 return "Unknown ICMPv6 type";
1358 } /* End of type2string() */
1359
1360
1361
1362