In "dissect_eth()", update "pi.len" and "pi.captured_len" regardless of
[obnox/wireshark/wip.git] / packet-icmpv6.c
1 /* packet-icmpv6.c
2  * Routines for ICMPv6 packet disassembly 
3  *
4  * $Id: packet-icmpv6.c,v 1.12 2000/01/16 02:54:46 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 #ifdef HAVE_SYS_TYPES_H
40 # include <sys/types.h>
41 #endif
42
43 #ifdef HAVE_NETINET_IN_H
44 # include <netinet/in.h>
45 #endif
46
47 #ifdef NEED_SNPRINTF_H
48 # ifdef HAVE_STDARG_H
49 #  include <stdarg.h>
50 # else
51 #  include <varargs.h>
52 # endif
53 # include "snprintf.h"
54 #endif
55
56 #include <glib.h>
57 #include "packet.h"
58 #include "packet-ipv6.h"
59 #include "resolv.h"
60
61 #ifndef offsetof
62 #define offsetof(type, member)  ((size_t)(&((type *)0)->member))
63 #endif
64
65 static int proto_icmpv6 = -1;
66 static int hf_icmpv6_type = -1;
67 static int hf_icmpv6_code = -1;
68 static int hf_icmpv6_checksum = -1;
69
70 static gint ett_icmpv6 = -1;
71 static gint ett_icmpv6opt = -1;
72 static gint ett_icmpv6flag = -1;
73
74 static void
75 dissect_icmpv6opt(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
76 {
77     proto_tree *icmp6opt_tree, *field_tree;
78         proto_item *ti, *tf;
79     struct nd_opt_hdr *opt;
80     int len;
81     char *typename;
82
83     if (!tree)
84         return;
85
86 again:
87     if (!(fd->cap_len > offset))
88         return;
89
90     opt = (struct nd_opt_hdr *)&pd[offset];
91     len = opt->nd_opt_len << 3;
92
93     /* !!! specify length */
94     ti = proto_tree_add_text(tree, offset, len, "ICMPv6 options");
95     icmp6opt_tree = proto_item_add_subtree(ti, ett_icmpv6opt);
96
97     switch (opt->nd_opt_type) {
98     case ND_OPT_SOURCE_LINKADDR:
99         typename = "Source link-layer address";
100         break;
101     case ND_OPT_TARGET_LINKADDR:
102         typename = "Target link-layer address";
103         break;
104     case ND_OPT_PREFIX_INFORMATION:
105         typename = "Prefix information";
106         break;
107     case ND_OPT_REDIRECTED_HEADER:
108         typename = "Redirected header";
109         break;
110     case ND_OPT_MTU:
111         typename = "MTU";
112         break;
113     default:
114         typename = "Unknown";
115         break;
116     }
117
118     proto_tree_add_text(icmp6opt_tree,
119         offset + offsetof(struct nd_opt_hdr, nd_opt_type), 1,
120         "Type: 0x%02x (%s)", opt->nd_opt_type, typename);
121     proto_tree_add_text(icmp6opt_tree,
122         offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
123         "Length: %d bytes (0x%02x)", opt->nd_opt_len << 3, opt->nd_opt_len);
124
125     /* decode... */
126     switch (opt->nd_opt_type) {
127     case ND_OPT_SOURCE_LINKADDR:
128     case ND_OPT_TARGET_LINKADDR:
129       {
130         char *t;
131         const char *p;
132         int len, i;
133         len = (opt->nd_opt_len << 3) - sizeof(*opt);
134         t = (char *)malloc(len * 3);
135         memset(t, 0, len * 3);
136         p = &pd[offset + sizeof(*opt)];
137         for (i = 0; i < len; i++) {
138             if (i)
139                 t[i * 3 - 1] = ':';
140             sprintf(&t[i * 3], "%02x", p[i] & 0xff);
141         }
142         proto_tree_add_text(icmp6opt_tree,
143             offset + sizeof(*opt), len, "Link-layer address: %s", t);
144         break;
145       }
146     case ND_OPT_PREFIX_INFORMATION:
147       {
148         struct nd_opt_prefix_info *pi = (struct nd_opt_prefix_info *)opt;
149         int flagoff;
150         proto_tree_add_text(icmp6opt_tree,
151             offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix_len),
152             1, "Prefix length: %d", pi->nd_opt_pi_prefix_len);
153
154         flagoff = offsetof(struct nd_opt_prefix_info, nd_opt_pi_flags_reserved);
155         tf = proto_tree_add_text(icmp6opt_tree, flagoff, 1, "Flags: 0x%02x",
156             pntohl(&pi->nd_opt_pi_flags_reserved));
157         field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
158         proto_tree_add_text(field_tree, flagoff, 1, "%s",
159             decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
160                     0x80, 8, "Onlink", "Not onlink"));
161         proto_tree_add_text(field_tree, flagoff, 1, "%s",
162             decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
163                     0x40, 8, "Auto", "Not auto"));
164
165         proto_tree_add_text(icmp6opt_tree,
166             offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
167             4, "Valid lifetime: 0x%08x",
168             pntohl(&pi->nd_opt_pi_valid_time));
169         proto_tree_add_text(icmp6opt_tree,
170             offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
171             4, "Preferred lifetime: 0x%08x",
172             pntohl(&pi->nd_opt_pi_preferred_time));
173         proto_tree_add_text(icmp6opt_tree,
174             offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix),
175             16, "Prefix: %s", ip6_to_str(&pi->nd_opt_pi_prefix));
176         break;
177       }
178     case ND_OPT_REDIRECTED_HEADER:
179         proto_tree_add_text(icmp6opt_tree,
180             offset + 8, (opt->nd_opt_len << 3) - 8, "Redirected packet");
181         /* tiny sanity check */
182         if ((pd[offset + 8] & 0xf0) == 0x60)
183             dissect_ipv6(pd, offset + 8, fd, icmp6opt_tree);
184         else
185             dissect_data(pd, offset + 8, fd, icmp6opt_tree);
186         break;
187     case ND_OPT_MTU:
188       {
189         struct nd_opt_mtu *pi = (struct nd_opt_mtu *)opt;
190         proto_tree_add_text(icmp6opt_tree,
191             offset + offsetof(struct nd_opt_mtu, nd_opt_mtu_mtu), 4,
192             "MTU: %d", pntohl(&pi->nd_opt_mtu_mtu));
193         break;
194       }
195     }
196
197     offset += (opt->nd_opt_len << 3);
198     goto again;
199 }
200
201 void
202 dissect_icmpv6(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
203 {
204     proto_tree *icmp6_tree, *field_tree;
205         proto_item *ti, *tf = NULL;
206     struct icmp6_hdr *dp;
207     char *codename, *typename;
208     int len;
209
210     dp = (struct icmp6_hdr *)&pd[offset];
211     codename = typename = "Unknown";
212     len = sizeof(*dp);
213     switch (dp->icmp6_type) {
214     case ICMP6_DST_UNREACH:
215         typename = "Unreachable";
216         switch (dp->icmp6_code) {
217         case ICMP6_DST_UNREACH_NOROUTE:
218             codename = "Route unreachable";
219             break;
220         case ICMP6_DST_UNREACH_ADMIN:
221             codename = "Administratively prohibited";
222             break;
223         case ICMP6_DST_UNREACH_NOTNEIGHBOR:
224             codename = "Not a neighbor";
225             break;
226         case ICMP6_DST_UNREACH_ADDR:
227             codename = "Address unreachable";
228             break;
229         case ICMP6_DST_UNREACH_NOPORT:
230             codename = "Port unreachable";
231             break;
232         }
233         break;
234     case ICMP6_PACKET_TOO_BIG:
235         typename = "Too big";
236         codename = NULL;
237         break;
238     case ICMP6_TIME_EXCEEDED:
239         typename = "Time exceeded";
240         switch (dp->icmp6_code) {
241         case ICMP6_TIME_EXCEED_TRANSIT:
242             codename = "In-transit";
243             break;
244         case ICMP6_TIME_EXCEED_REASSEMBLY:
245             codename = "Reassembly";
246             break;
247         }
248         break;
249     case ICMP6_PARAM_PROB:
250         typename = "Parameter problem";
251         switch (dp->icmp6_code) {
252         case ICMP6_PARAMPROB_HEADER:
253             codename = "Header";
254             break;
255         case ICMP6_PARAMPROB_NEXTHEADER:
256             codename = "Next header";
257             break;
258         case ICMP6_PARAMPROB_OPTION:
259             codename = "Option";
260             break;
261         }
262         break;
263     case ICMP6_ECHO_REQUEST:
264         typename = "Echo request";
265         codename = NULL;
266         break;
267     case ICMP6_ECHO_REPLY:
268         typename = "Echo reply";
269         codename = NULL;
270         break;
271     case ICMP6_MEMBERSHIP_QUERY:
272         typename = "Multicast listener query";
273         codename = NULL;
274         break;
275     case ICMP6_MEMBERSHIP_REPORT:
276         typename = "Multicast listener report";
277         codename = NULL;
278         break;
279     case ICMP6_MEMBERSHIP_REDUCTION:
280         typename = "Multicast listener done";
281         codename = NULL;
282         break;
283     case ND_ROUTER_SOLICIT:
284         typename = "Router solicitation";
285         codename = NULL;
286         len = sizeof(struct nd_router_solicit);
287         break;
288     case ND_ROUTER_ADVERT:
289         typename = "Router advertisement";
290         codename = NULL;
291         len = sizeof(struct nd_router_advert);
292         break;
293     case ND_NEIGHBOR_SOLICIT:
294         typename = "Neighbor solicitation";
295         codename = NULL;
296         len = sizeof(struct nd_neighbor_solicit);
297         break;
298     case ND_NEIGHBOR_ADVERT:
299         typename = "Neighbor advertisement";
300         codename = NULL;
301         len = sizeof(struct nd_neighbor_advert);
302         break;
303     case ND_REDIRECT:
304         typename = "Redirect";
305         codename = NULL;
306         len = sizeof(struct nd_redirect);
307         break;
308     case ICMP6_ROUTER_RENUMBERING:
309         typename = "Router renumbering";
310         switch (dp->icmp6_code) {
311         case ICMP6_ROUTER_RENUMBERING_COMMAND:
312             codename = "Command";
313             break;
314         case ICMP6_ROUTER_RENUMBERING_RESULT:
315             codename = "Result";
316             break;
317         }
318         len = sizeof(struct icmp6_router_renum);
319         break;
320     }
321
322     if (check_col(fd, COL_PROTOCOL))
323         col_add_str(fd, COL_PROTOCOL, "ICMPv6");
324     if (check_col(fd, COL_INFO)) {
325         char typebuf[256], codebuf[256];
326
327         if (typename && strcmp(typename, "Unknown") == 0) {
328             snprintf(typebuf, sizeof(typebuf), "Unknown (0x%02x)",
329                 dp->icmp6_type);
330             typename = typebuf;
331         }
332         if (codename && strcmp(codename, "Unknown") == 0) {
333             snprintf(codebuf, sizeof(codebuf), "Unknown (0x%02x)",
334                 dp->icmp6_code);
335             codename = codebuf;
336         }
337         if (codename) {
338             col_add_fstr(fd, COL_INFO, "%s (%s)",
339                 typename, codename);
340         } else {
341             col_add_fstr(fd, COL_INFO, "%s", typename);
342         }
343     }
344
345     if (tree) {
346         /* !!! specify length */
347         ti = proto_tree_add_item(tree, proto_icmpv6, offset, len, NULL);
348         icmp6_tree = proto_item_add_subtree(ti, ett_icmpv6);
349
350         proto_tree_add_item_format(icmp6_tree, hf_icmpv6_type,
351             offset + offsetof(struct icmp6_hdr, icmp6_type), 1,
352             dp->icmp6_type,
353             "Type: 0x%02x (%s)", dp->icmp6_type, typename);
354         if (codename) {
355             proto_tree_add_item_format(icmp6_tree, hf_icmpv6_code,
356                 offset + offsetof(struct icmp6_hdr, icmp6_code), 1,
357                 dp->icmp6_code,
358                 "Code: 0x%02x (%s)", dp->icmp6_code, codename);
359         }
360         proto_tree_add_item(icmp6_tree, hf_icmpv6_checksum,
361             offset + offsetof(struct icmp6_hdr, icmp6_cksum), 2,
362             (guint16)htons(dp->icmp6_cksum));
363
364         /* decode... */
365         switch (dp->icmp6_type) {
366         case ICMP6_DST_UNREACH:
367         case ICMP6_TIME_EXCEEDED:
368             /* tiny sanity check */
369             if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
370                 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
371             } else {
372                 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
373             }
374             break;
375         case ICMP6_PACKET_TOO_BIG:
376             proto_tree_add_text(icmp6_tree,
377                 offset + offsetof(struct icmp6_hdr, icmp6_mtu), 4,
378                 "MTU: %d", pntohl(&dp->icmp6_mtu));
379             /* tiny sanity check */
380             if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
381                 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
382             } else {
383                 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
384             }
385             break;
386         case ICMP6_PARAM_PROB:
387             proto_tree_add_text(icmp6_tree,
388                 offset + offsetof(struct icmp6_hdr, icmp6_pptr), 4,
389                 "Problem pointer: 0x%04x", pntohl(&dp->icmp6_pptr));
390             /* tiny sanity check */
391             if ((pd[offset + sizeof(*dp)] & 0xf0) == 0x60) {
392                 dissect_ipv6(pd, offset + sizeof(*dp), fd, icmp6_tree);
393             } else {
394                 dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
395             }
396             break;
397         case ICMP6_ECHO_REQUEST:
398         case ICMP6_ECHO_REPLY:
399             proto_tree_add_text(icmp6_tree,
400                 offset + offsetof(struct icmp6_hdr, icmp6_id), 2,
401                 "ID: 0x%04x", (guint16)ntohs(dp->icmp6_id));
402             proto_tree_add_text(icmp6_tree,
403                 offset + offsetof(struct icmp6_hdr, icmp6_seq), 2,
404                 "Sequence: 0x%04x", (guint16)ntohs(dp->icmp6_seq));
405             dissect_data(pd, offset + sizeof(*dp), fd, icmp6_tree);
406             break;
407         case ICMP6_MEMBERSHIP_QUERY:
408         case ICMP6_MEMBERSHIP_REPORT:
409         case ICMP6_MEMBERSHIP_REDUCTION:
410             proto_tree_add_text(icmp6_tree,
411                 offset + offsetof(struct icmp6_hdr, icmp6_maxdelay), 2,
412                 "Maximum response delay: %d",
413                 (guint16)ntohs(dp->icmp6_maxdelay));
414             proto_tree_add_text(icmp6_tree, offset + sizeof(*dp), 16,
415                 "Multicast Address: %s",
416                 ip6_to_str((struct e_in6_addr *)(dp + 1)));
417             break;
418         case ND_ROUTER_SOLICIT:
419             dissect_icmpv6opt(pd, offset + sizeof(*dp), fd, icmp6_tree);
420             break;
421         case ND_ROUTER_ADVERT:
422           {
423             struct nd_router_advert *ra = (struct nd_router_advert *)dp;
424             int flagoff;
425             guint32 ra_flags;
426
427             proto_tree_add_text(icmp6_tree,
428                 offset + offsetof(struct nd_router_advert, nd_ra_curhoplimit),
429                 1, "Cur hop limit: %d", ra->nd_ra_curhoplimit);
430
431             flagoff = offset + offsetof(struct nd_router_advert, nd_ra_flags_reserved);
432             ra_flags = pntohl(&pd[flagoff]);
433             tf = proto_tree_add_text(icmp6_tree, flagoff, 4, "Flags: 0x%08x", ra_flags);
434             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
435             proto_tree_add_text(field_tree, flagoff, 4, "%s",
436                 decode_boolean_bitfield(ra_flags,
437                         0x80000000, 32, "Managed", "Not managed"));
438             proto_tree_add_text(field_tree, flagoff, 4, "%s",
439                 decode_boolean_bitfield(ra_flags,
440                         0x40000000, 32, "Other", "Not other"));
441
442             proto_tree_add_text(icmp6_tree,
443                 offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
444                 2, "Router lifetime: %d",
445                 (guint16)ntohs(ra->nd_ra_router_lifetime));
446             proto_tree_add_text(icmp6_tree,
447                 offset + offsetof(struct nd_router_advert, nd_ra_reachable), 4,
448                 "Reachable time: %d", pntohl(&ra->nd_ra_reachable));
449             proto_tree_add_text(icmp6_tree,
450                 offset + offsetof(struct nd_router_advert, nd_ra_retransmit), 4,
451                 "Retrans time: %d", pntohl(&ra->nd_ra_retransmit));
452             dissect_icmpv6opt(pd, offset + sizeof(struct nd_router_advert), fd, icmp6_tree);
453             break;
454           }
455         case ND_NEIGHBOR_SOLICIT:
456           {
457             struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)dp;
458
459             proto_tree_add_text(icmp6_tree,
460                         offset + offsetof(struct nd_neighbor_solicit, nd_ns_target), 16,
461 #ifdef INET6
462                         "Target: %s (%s)",
463                         get_hostname6(&ns->nd_ns_target),
464 #else
465                         "Target: %s",
466 #endif
467                         ip6_to_str(&ns->nd_ns_target));
468
469             dissect_icmpv6opt(pd, offset + sizeof(*ns), fd, icmp6_tree);
470             break;
471           }
472         case ND_NEIGHBOR_ADVERT:
473           {
474             int flagoff, targetoff;
475             guint32 na_flags;
476                 struct e_in6_addr *na_target_p;
477
478             flagoff = offset + offsetof(struct nd_neighbor_advert, nd_na_flags_reserved);
479             na_flags = pntohl(&pd[flagoff]);
480
481             tf = proto_tree_add_text(icmp6_tree, flagoff, 4, "Flags: 0x%08x", na_flags);
482             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
483             proto_tree_add_text(field_tree, flagoff, 4, "%s",
484                 decode_boolean_bitfield(na_flags,
485                         0x80000000, 32, "Router", "Not router"));
486             proto_tree_add_text(field_tree, flagoff, 4, "%s",
487                 decode_boolean_bitfield(na_flags,
488                         0x40000000, 32, "Solicited", "Not adverted"));
489             proto_tree_add_text(field_tree, flagoff, 4, "%s",
490                 decode_boolean_bitfield(na_flags,
491                         0x20000000, 32, "Override", "Not override"));
492
493                 targetoff = offset + offsetof(struct nd_neighbor_advert, nd_na_target);
494             na_target_p = (struct e_in6_addr*) &pd[targetoff];
495             proto_tree_add_text(icmp6_tree, targetoff, 16,
496 #ifdef INET6
497                         "Target: %s (%s)",
498                         get_hostname6(na_target_p),
499 #else
500                         "Target: %s",
501 #endif
502                         ip6_to_str(na_target_p));
503
504             dissect_icmpv6opt(pd, offset + sizeof(struct nd_neighbor_advert), fd, icmp6_tree);
505             break;
506           }
507         case ND_REDIRECT:
508           {
509             struct nd_redirect *rd = (struct nd_redirect *)dp;
510
511             proto_tree_add_text(icmp6_tree,
512                         offset + offsetof(struct nd_redirect, nd_rd_target), 16,
513 #ifdef INET6
514                         "Target: %s (%s)",
515                         get_hostname6(&rd->nd_rd_target),
516 #else
517                         "Target: %s",
518 #endif
519                         ip6_to_str(&rd->nd_rd_target));
520
521             proto_tree_add_text(icmp6_tree,
522                         offset + offsetof(struct nd_redirect, nd_rd_dst), 16,
523 #ifdef INET6
524                         "Destination: %s (%s)",
525                         get_hostname6(&rd->nd_rd_dst),
526 #else
527                         "Destination: %s",
528 #endif
529                         ip6_to_str(&rd->nd_rd_dst));
530
531             dissect_icmpv6opt(pd, offset + sizeof(*rd), fd, icmp6_tree);
532             break;
533           }
534         case ICMP6_ROUTER_RENUMBERING:
535           {
536             struct icmp6_router_renum *rr = (struct icmp6_router_renum *)dp;
537             int flagoff;
538             proto_tree_add_text(icmp6_tree,
539                 offset + offsetof(struct icmp6_router_renum, rr_seqnum), 4,
540                 /*"Sequence number: 0x%08x", (u_int32_t)htonl(rr->rr_seqnum));*/
541                 "Sequence number: 0x%08x", pntohl(&rr->rr_seqnum));
542             proto_tree_add_text(icmp6_tree,
543                 offset + offsetof(struct icmp6_router_renum, rr_segnum), 1,
544                 "Segment number: 0x%02x", rr->rr_segnum);
545
546             flagoff = offset + offsetof(struct icmp6_router_renum, rr_segnum) + 1;
547             tf = proto_tree_add_text(icmp6_tree, flagoff, 4, "Flags: 0x%08x",
548                 pd[flagoff]);
549             field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
550             proto_tree_add_text(field_tree, flagoff, 1, "%s",
551                 decode_boolean_bitfield(pd[flagoff], 0x80, 8,
552                     "Test command", "Not test command"));
553             proto_tree_add_text(field_tree, flagoff, 1, "%s",
554                 decode_boolean_bitfield(pd[flagoff], 0x40, 8,
555                     "Result requested", "Result not requested"));
556             proto_tree_add_text(field_tree, flagoff, 1, "%s",
557                 decode_boolean_bitfield(pd[flagoff], 0x20, 8,
558                     "All interfaces", "Not all interfaces"));
559             proto_tree_add_text(field_tree, flagoff, 1, "%s",
560                 decode_boolean_bitfield(pd[flagoff], 0x10, 8,
561                     "Site specific", "Not site specific"));
562             proto_tree_add_text(field_tree, flagoff, 1, "%s",
563                 decode_boolean_bitfield(pd[flagoff], 0x08, 8,
564                     "Processed previously", "Complete result"));
565
566             proto_tree_add_text(icmp6_tree,
567                 offset + offsetof(struct icmp6_router_renum, rr_segnum), 2,
568                 "Max delay: 0x%04x", pntohs(&rr->rr_maxdelay));
569             dissect_data(pd, offset + sizeof(*rr), fd, tree);   /*XXX*/
570           }
571         default:
572             dissect_data(pd, offset + sizeof(*dp), fd, tree);
573             break;
574         }
575     }
576 }
577
578 void
579 proto_register_icmpv6(void)
580 {
581   static hf_register_info hf[] = {
582     { &hf_icmpv6_type,
583       { "Type",           "icmpv6.type",        FT_UINT8,  BASE_HEX, NULL, 0x0,
584         "" }},
585     { &hf_icmpv6_code,
586       { "Code",           "icmpv6.code",        FT_UINT8,  BASE_HEX, NULL, 0x0,
587         "" }},
588     { &hf_icmpv6_checksum,
589       { "Checksum",       "icmpv6.checksum",    FT_UINT16, BASE_HEX, NULL, 0x0,
590         "" }}
591   };
592   static gint *ett[] = {
593     &ett_icmpv6,
594     &ett_icmpv6opt,
595     &ett_icmpv6flag,
596   };
597
598   proto_icmpv6 = proto_register_protocol("Internet Control Message Protocol v6",
599                                          "icmpv6");
600   proto_register_field_array(proto_icmpv6, hf, array_length(hf));
601   proto_register_subtree_array(ett, array_length(ett));
602 }