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