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