Un-comment "AC_CONFIG_SUBDIRS()", so that it'll run "configure" in the
[obnox/wireshark/wip.git] / packet-ip.c
1 /* packet-ip.c
2  * Routines for IP and miscellaneous IP protocol packet disassembly
3  *
4  * $Id: packet-ip.c,v 1.11 1998/11/12 00:06:28 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 <gtk/gtk.h>
31
32 #include <stdio.h>
33
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
40 #endif
41
42 #include "ethereal.h"
43 #include "packet.h"
44 #include "etypes.h"
45 #include "resolv.h"
46
47 extern packet_info pi;
48
49 static void
50 dissect_ipopt_security(GtkWidget *opt_tree, const char *name,
51     const u_char *opd, int offset, guint optlen)
52 {
53   GtkWidget *field_tree = NULL, *tf;
54   guint      val;
55   static const value_string secl_vals[] = {
56     {IPSEC_UNCLASSIFIED, "Unclassified"},
57     {IPSEC_CONFIDENTIAL, "Confidential"},
58     {IPSEC_EFTO,         "EFTO"        },
59     {IPSEC_MMMM,         "MMMM"        },
60     {IPSEC_RESTRICTED,   "Restricted"  },
61     {IPSEC_SECRET,       "Secret"      },
62     {IPSEC_TOPSECRET,    "Top secret"  },
63     {IPSEC_RESERVED1,    "Reserved"    },
64     {IPSEC_RESERVED2,    "Reserved"    },
65     {IPSEC_RESERVED3,    "Reserved"    },
66     {IPSEC_RESERVED4,    "Reserved"    },
67     {IPSEC_RESERVED5,    "Reserved"    },
68     {IPSEC_RESERVED6,    "Reserved"    },
69     {IPSEC_RESERVED7,    "Reserved"    },
70     {IPSEC_RESERVED8,    "Reserved"    },
71     {0,                  NULL          } };
72
73   tf = add_item_to_tree(opt_tree, offset,      optlen, "%s:", name);
74   field_tree = gtk_tree_new();
75   add_subtree(tf, field_tree, ETT_IP_OPTION_SEC);
76   offset += 2;
77
78   val = pntohs(opd);
79   add_item_to_tree(field_tree, offset,       2,
80               "Security: %s", val_to_str(val, secl_vals, "Unknown (0x%x)"));
81   offset += 2;
82   opd += 2;
83
84   val = pntohs(opd);
85   add_item_to_tree(field_tree, offset,         2,
86               "Compartments: %d", val);
87   offset += 2;
88   opd += 2;
89
90   add_item_to_tree(field_tree, offset,         2,
91               "Handling restrictions: %c%c", opd[0], opd[1]);
92   offset += 2;
93   opd += 2;
94
95   add_item_to_tree(field_tree, offset,         3,
96               "Transmission control code: %c%c%c", opd[0], opd[1], opd[2]);
97 }
98
99 static void
100 dissect_ipopt_route(GtkWidget *opt_tree, const char *name,
101     const u_char *opd, int offset, guint optlen)
102 {
103   GtkWidget *field_tree = NULL, *tf;
104   int ptr;
105   int optoffset = 0;
106   struct in_addr addr;
107
108   tf = add_item_to_tree(opt_tree, offset,      optlen, "%s (%d bytes)", name,
109               optlen);
110   field_tree = gtk_tree_new();
111   add_subtree(tf, field_tree, ETT_IP_OPTION_ROUTE);
112
113   optoffset += 2;       /* skip past type and length */
114   optlen -= 2;          /* subtract size of type and length */
115
116   ptr = *opd;
117   add_item_to_tree(field_tree, offset + optoffset, 1,
118               "Pointer: %d%s", ptr,
119               ((ptr < 4) ? " (points before first address)" :
120                ((ptr & 3) ? " (points to middle of address)" : "")));
121   optoffset++;
122   opd++;
123   optlen--;
124   ptr--;        /* ptr is 1-origin */
125
126   while (optlen > 0) {
127     if (optlen < 4) {
128       add_item_to_tree(field_tree, offset,      optlen,
129         "(suboption would go past end of option)");
130       break;
131     }
132
133     /* Avoids alignment problems on many architectures. */
134     memcpy((char *)&addr, (char *)opd, sizeof(addr));
135
136     add_item_to_tree(field_tree, offset + optoffset, 4,
137               "%s%s",
138               ((addr.s_addr == 0) ? "-" : (char *)get_hostname(addr.s_addr)),
139               ((optoffset == ptr) ? " <- (current)" : ""));
140     optoffset += 4;
141     opd += 4;
142     optlen -= 4;
143   }
144 }
145
146 static void
147 dissect_ipopt_sid(GtkWidget *opt_tree, const char *name, const u_char *opd,
148     int offset, guint optlen)
149 {
150   add_item_to_tree(opt_tree, offset,      optlen,
151     "%s: %d", name, pntohs(opd));
152   return;
153 }
154
155 static void
156 dissect_ipopt_timestamp(GtkWidget *opt_tree, const char *name, const u_char *opd,
157     int offset, guint optlen)
158 {
159   GtkWidget *field_tree = NULL, *tf;
160   int        ptr;
161   int        optoffset = 0;
162   int        flg;
163   static const value_string flag_vals[] = {
164     {IPOPT_TS_TSONLY,    "Time stamps only"                      },
165     {IPOPT_TS_TSANDADDR, "Time stamp and address"                },
166     {IPOPT_TS_PRESPEC,   "Time stamps for prespecified addresses"},
167     {0,                  NULL                                    } };
168
169   struct in_addr addr;
170   guint ts;
171
172   tf = add_item_to_tree(opt_tree, offset,      optlen, "%s:", name);
173   field_tree = gtk_tree_new();
174   add_subtree(tf, field_tree, ETT_IP_OPTION_TIMESTAMP);
175
176   optoffset += 2;       /* skip past type and length */
177   optlen -= 2;          /* subtract size of type and length */
178
179   ptr = *opd;
180   add_item_to_tree(field_tree, offset + optoffset, 1,
181               "Pointer: %d%s", ptr,
182               ((ptr < 5) ? " (points before first address)" :
183                (((ptr - 1) & 3) ? " (points to middle of address)" : "")));
184   optoffset++;
185   opd++;
186   optlen--;
187   ptr--;        /* ptr is 1-origin */
188
189   flg = *opd;
190   add_item_to_tree(field_tree, offset + optoffset,   1,
191         "Overflow: %d", flg >> 4);
192   flg &= 0xF;
193   add_item_to_tree(field_tree, offset + optoffset, 1,
194         "Flag: %s", val_to_str(flg, flag_vals, "Unknown (0x%x)"));
195   optoffset++;
196   opd++;
197   optlen--;
198
199   while (optlen > 0) {
200     if (flg == IPOPT_TS_TSANDADDR) {
201       if (optlen < 4) {
202         add_item_to_tree(field_tree, offset + optoffset, optlen,
203           "(suboption would go past end of option)");
204         break;
205       }
206       /* XXX - check whether it goes past end of packet */
207       ts = pntohl(opd);
208       opd += 4;
209       optlen -= 4;
210       if (optlen < 4) {
211         add_item_to_tree(field_tree, offset + optoffset, optlen,
212           "(suboption would go past end of option)");
213         break;
214       }
215       /* XXX - check whether it goes past end of packet */
216       memcpy((char *)&addr, (char *)opd, sizeof(addr));
217       opd += 4;
218       optlen -= 4;
219       add_item_to_tree(field_tree, offset,      8,
220           "Address = %s, time stamp = %u",
221           ((addr.s_addr == 0) ? "-" :  (char *)get_hostname(addr.s_addr)),
222           ts);
223       optoffset += 8;
224     } else {
225       if (optlen < 4) {
226         add_item_to_tree(field_tree, offset + optoffset, optlen,
227           "(suboption would go past end of option)");
228         break;
229       }
230       /* XXX - check whether it goes past end of packet */
231       ts = pntohl(opd);
232       opd += 4;
233       optlen -= 4;
234       add_item_to_tree(field_tree, offset + optoffset, 4,
235           "Time stamp = %u", ts);
236       optoffset += 4;
237     }
238   }
239 }
240
241 static ip_tcp_opt ipopts[] = {
242   {
243     IPOPT_END,
244     "EOL",
245     NO_LENGTH,
246     0,
247     NULL,
248   },
249   {
250     IPOPT_NOOP,
251     "NOP",
252     NO_LENGTH,
253     0,
254     NULL,
255   },
256   {
257     IPOPT_SEC,
258     "Security",
259     FIXED_LENGTH,
260     IPOLEN_SEC,
261     dissect_ipopt_security
262   },
263   {
264     IPOPT_SSRR,
265     "Strict source route",
266     VARIABLE_LENGTH,
267     IPOLEN_SSRR_MIN,
268     dissect_ipopt_route
269   },
270   {
271     IPOPT_LSRR,
272     "Loose source route",
273     VARIABLE_LENGTH,
274     IPOLEN_LSRR_MIN,
275     dissect_ipopt_route
276   },
277   {
278     IPOPT_RR,
279     "Record route",
280     VARIABLE_LENGTH,
281     IPOLEN_RR_MIN,
282     dissect_ipopt_route
283   },
284   {
285     IPOPT_SID,
286     "Stream identifier",
287     FIXED_LENGTH,
288     IPOLEN_SID,
289     dissect_ipopt_sid
290   },
291   {
292     IPOPT_TIMESTAMP,
293     "Time stamp",
294     VARIABLE_LENGTH,
295     IPOLEN_TIMESTAMP_MIN,
296     dissect_ipopt_timestamp
297   }
298 };
299
300 #define N_IP_OPTS       (sizeof ipopts / sizeof ipopts[0])
301
302 /* Dissect the IP or TCP options in a packet. */
303 void
304 dissect_ip_tcp_options(GtkWidget *opt_tree, const u_char *opd, int offset,
305     guint length, ip_tcp_opt *opttab, int nopts, int eol)
306 {
307   u_char      opt;
308   ip_tcp_opt *optp;
309   guint       len;
310
311   while (length > 0) {
312     opt = *opd++;
313     for (optp = &opttab[0]; optp < &opttab[nopts]; optp++) {
314       if (optp->optcode == opt)
315         break;
316     }
317     if (optp == &opttab[nopts]) {
318       add_item_to_tree(opt_tree, offset,      1, "Unknown");
319       /* We don't know how long this option is, so we don't know how much
320          of it to skip, so we just bail. */
321       return;
322     }
323     --length;      /* account for type byte */
324     if (optp->len_type != NO_LENGTH) {
325       /* Option has a length. Is it in the packet? */
326       if (length == 0) {
327         /* Bogus - packet must at least include option code byte and
328            length byte! */
329         add_item_to_tree(opt_tree, offset,      1,
330               "%s (length byte past end of header)", optp->name);
331         return;
332       }
333       len = *opd++;  /* total including type, len */
334       --length;    /* account for length byte */
335       if (len < 2) {
336         /* Bogus - option length is too short to include option code and
337            option length. */
338         add_item_to_tree(opt_tree, offset,      2,
339               "%s (with too-short option length = %u bytes)", optp->name, 2);
340         return;
341       } else if (len - 2 > length) {
342         /* Bogus - option goes past the end of the header. */
343         add_item_to_tree(opt_tree, offset,      length,
344               "%s (option goes past end of header)", optp->name);
345         return;
346       } else if (optp->len_type == FIXED_LENGTH && len != optp->optlen) {
347         /* Bogus - option length isn't what it's supposed to be for this
348            option. */
349         add_item_to_tree(opt_tree, offset,      len,
350               "%s (with option length = %u bytes; should be %u)", optp->name,
351               len, optp->optlen);
352         return;
353       } else if (optp->len_type == VARIABLE_LENGTH && len < optp->optlen) {
354         /* Bogus - option length is less than what it's supposed to be for
355            this option. */
356         add_item_to_tree(opt_tree, offset,      len,
357               "%s (with option length = %u bytes; should be >= %u)", optp->name,
358               len, optp->optlen);
359         return;
360       } else {
361         if (optp->dissect != NULL) {
362           /* Option has a dissector. */
363           (*optp->dissect)(opt_tree, optp->name, opd, offset, len);
364         } else {
365           /* Option has no data, hence no dissector. */
366           add_item_to_tree(opt_tree, offset,      len, "%s", optp->name);
367         }
368         len -= 2;       /* subtract size of type and length */
369         offset += 2 + len;
370       }
371       opd += len;
372       length -= len;
373     } else {
374       add_item_to_tree(opt_tree, offset,      1, "%s", optp->name);
375       offset += 1;
376     }
377     if (opt == eol)
378       break;
379   }
380 }
381
382 void
383 dissect_ip(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
384   e_ip       iph;
385   GtkWidget *ip_tree, *ti, *field_tree, *tf;
386   gchar      tos_str[32];
387   guint      hlen, optlen;
388   static const value_string proto_vals[] = { {IP_PROTO_ICMP, "ICMP"},
389                                              {IP_PROTO_IGMP, "IGMP"},
390                                              {IP_PROTO_TCP,  "TCP" },
391                                              {IP_PROTO_UDP,  "UDP" },
392                                              {IP_PROTO_OSPF, "OSPF"},
393                                              {0,             NULL  } };
394
395
396   /* To do: check for runts, errs, etc. */
397   /* Avoids alignment problems on many architectures. */
398   memcpy(&iph, &pd[offset], sizeof(e_ip));
399   iph.ip_len = ntohs(iph.ip_len);
400   iph.ip_id  = ntohs(iph.ip_id);
401   iph.ip_off = ntohs(iph.ip_off);
402   iph.ip_sum = ntohs(iph.ip_sum);
403
404   hlen = iph.ip_hl * 4; /* IP header length, in bytes */
405   
406   if (fd->win_info[COL_NUM]) {
407     switch (iph.ip_p) {
408       case IP_PROTO_ICMP:
409       case IP_PROTO_IGMP:
410       case IP_PROTO_TCP:
411       case IP_PROTO_UDP:
412       case IP_PROTO_OSPF:
413         /* Names are set in the associated dissect_* routines */
414         break;
415       default:
416         strcpy(fd->win_info[COL_PROTOCOL], "IP");
417         sprintf(fd->win_info[COL_INFO], "Unknown IP protocol (%02x)", iph.ip_p);
418     }
419
420     strcpy(fd->win_info[COL_SOURCE], get_hostname(iph.ip_src));
421     strcpy(fd->win_info[COL_DESTINATION], get_hostname(iph.ip_dst));
422   }
423   
424   iph.ip_tos = IPTOS_TOS(iph.ip_tos);
425   switch (iph.ip_tos) {
426     case IPTOS_NONE:
427       strcpy(tos_str, "None");
428       break;
429     case IPTOS_LOWCOST:
430       strcpy(tos_str, "Minimize cost");
431       break;
432     case IPTOS_RELIABILITY:
433       strcpy(tos_str, "Maximize reliability");
434       break;
435     case IPTOS_THROUGHPUT:
436       strcpy(tos_str, "Maximize throughput");
437       break;
438     case IPTOS_LOWDELAY:
439       strcpy(tos_str, "Minimize delay");
440       break;
441     case IPTOS_SECURITY:
442       strcpy(tos_str, "Maximize security");
443       break;
444     default:
445       strcpy(tos_str, "Unknown.  Malformed?");
446       break;
447   }
448   
449   if (tree) {
450     ti = add_item_to_tree(GTK_WIDGET(tree), offset, hlen, "Internet Protocol");
451     ip_tree = gtk_tree_new();
452     add_subtree(ti, ip_tree, ETT_IP);
453     add_item_to_tree(ip_tree, offset,      1, "Version: %d", iph.ip_v);
454     add_item_to_tree(ip_tree, offset,      1, "Header length: %d bytes", hlen); 
455     add_item_to_tree(ip_tree, offset +  1, 1, "Type of service: 0x%02x (%s)",
456       iph.ip_tos, tos_str);
457     add_item_to_tree(ip_tree, offset +  2, 2, "Total length: %d", iph.ip_len);
458     add_item_to_tree(ip_tree, offset +  4, 2, "Identification: 0x%04x",
459       iph.ip_id);
460     /* To do: add flags */
461     add_item_to_tree(ip_tree, offset +  6, 2, "Fragment offset: %d",
462       iph.ip_off & IP_OFFSET);
463     add_item_to_tree(ip_tree, offset +  8, 1, "Time to live: %d",
464       iph.ip_ttl);
465     add_item_to_tree(ip_tree, offset +  9, 1, "Protocol: %s",
466       val_to_str(iph.ip_p, proto_vals, "Unknown (%x)"));
467     add_item_to_tree(ip_tree, offset + 10, 2, "Header checksum: 0x%04x",
468       iph.ip_sum);
469     add_item_to_tree(ip_tree, offset + 12, 4, "Source address: %s",
470                      get_hostname(iph.ip_src));
471     add_item_to_tree(ip_tree, offset + 16, 4, "Destination address: %s",
472                      get_hostname(iph.ip_dst));
473
474     /* Decode IP options, if any. */
475     if (hlen > sizeof (e_ip)) {
476       /* There's more than just the fixed-length header.  Decode the
477          options. */
478       optlen = hlen - sizeof (e_ip);    /* length of options, in bytes */
479       tf = add_item_to_tree(ip_tree, offset +  20, optlen,
480         "Options: (%d bytes)", optlen);
481       field_tree = gtk_tree_new();
482       add_subtree(tf, field_tree, ETT_IP_OPTIONS);
483       dissect_ip_tcp_options(field_tree, &pd[offset + 20], offset + 20, optlen,
484          ipopts, N_IP_OPTS, IPOPT_END);
485     }
486   }
487
488   pi.srcip = ip_to_str( (guint8 *) &iph.ip_src);
489   pi.destip = ip_to_str( (guint8 *) &iph.ip_dst);
490   pi.ipproto = iph.ip_p;
491   pi.iplen = iph.ip_len;
492   pi.iphdrlen = iph.ip_hl;
493   pi.ip_src = iph.ip_src;
494
495   offset += hlen;
496   switch (iph.ip_p) {
497     case IP_PROTO_ICMP:
498       dissect_icmp(pd, offset, fd, tree);
499      break;
500     case IP_PROTO_IGMP:
501       dissect_igmp(pd, offset, fd, tree);
502      break;
503     case IP_PROTO_TCP:
504       dissect_tcp(pd, offset, fd, tree);
505      break;
506    case IP_PROTO_UDP:
507       dissect_udp(pd, offset, fd, tree);
508       break;
509     case IP_PROTO_OSPF:
510       dissect_ospf(pd, offset, fd, tree);
511      break;
512   }
513 }
514
515
516 const gchar *unreach_str[] = {"Network unreachable",
517                               "Host unreachable",
518                               "Protocol unreachable",
519                               "Port unreachable",
520                               "Fragmentation needed",
521                               "Source route failed",
522                               "Administratively prohibited",
523                               "Network unreachable for TOS",
524                               "Host unreachable for TOS",
525                               "Communication administratively filtered",
526                               "Host precedence violation",
527                               "Precedence cutoff in effect"};
528
529 const gchar *redir_str[] = {"Redirect for network",
530                             "Redirect for host",
531                             "Redirect for TOS and network",
532                             "Redirect for TOS and host"};
533
534 const gchar *ttl_str[] = {"TTL equals 0 during transit",
535                           "TTL equals 0 during reassembly"};
536
537 const gchar *par_str[] = {"IP header bad", "Required option missing"};
538
539 void
540 dissect_icmp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
541   e_icmp     ih;
542   GtkWidget *icmp_tree, *ti;
543   guint16    cksum;
544   gchar      type_str[64], code_str[64] = "";
545
546   /* Avoids alignment problems on many architectures. */
547   memcpy(&ih, &pd[offset], sizeof(e_icmp));
548   /* To do: check for runts, errs, etc. */
549   cksum = ntohs(ih.icmp_cksum);
550   
551   switch (ih.icmp_type) {
552     case ICMP_ECHOREPLY:
553       strcpy(type_str, "Echo (ping) reply");
554       break;
555     case ICMP_UNREACH:
556       strcpy(type_str, "Destination unreachable");
557       if (ih.icmp_code < 12) {
558         sprintf(code_str, "(%s)", unreach_str[ih.icmp_code]);
559       } else {
560         strcpy(code_str, "(Unknown - error?)");
561       }
562       break;
563     case ICMP_SOURCEQUENCH:
564       strcpy(type_str, "Source quench (flow control)");
565       break;
566     case ICMP_REDIRECT:
567       strcpy(type_str, "Redirect");
568       if (ih.icmp_code < 4) {
569         sprintf(code_str, "(%s)", redir_str[ih.icmp_code]);
570       } else {
571         strcpy(code_str, "(Unknown - error?)");
572       }
573       break;
574     case ICMP_ECHO:
575       strcpy(type_str, "Echo (ping) request");
576       break;
577     case ICMP_TIMXCEED:
578       strcpy(type_str, "Time-to-live exceeded");
579       if (ih.icmp_code < 2) {
580         sprintf(code_str, "(%s)", ttl_str[ih.icmp_code]);
581       } else {
582         strcpy(code_str, "(Unknown - error?)");
583       }
584       break;
585     case ICMP_PARAMPROB:
586       strcpy(type_str, "Parameter problem");
587       if (ih.icmp_code < 2) {
588         sprintf(code_str, "(%s)", par_str[ih.icmp_code]);
589       } else {
590         strcpy(code_str, "(Unknown - error?)");
591       }
592       break;
593     case ICMP_TSTAMP:
594       strcpy(type_str, "Timestamp request");
595       break;
596     case ICMP_TSTAMPREPLY:
597       strcpy(type_str, "Timestamp reply");
598       break;
599     case ICMP_MASKREQ:
600       strcpy(type_str, "Address mask request");
601       break;
602     case ICMP_MASKREPLY:
603       strcpy(type_str, "Address mask reply");
604       break;
605     default:
606       strcpy(type_str, "Unknown ICMP (obsolete or malformed?)");
607   }
608
609   if (fd->win_info[COL_NUM]) {    
610     strcpy(fd->win_info[COL_PROTOCOL], "ICMP");
611     strcpy(fd->win_info[COL_INFO], type_str);
612   }
613   
614   if (tree) {
615     ti = add_item_to_tree(GTK_WIDGET(tree), offset, 4,
616       "Internet Control Message Protocol");
617     icmp_tree = gtk_tree_new();
618     add_subtree(ti, icmp_tree, ETT_ICMP);
619     add_item_to_tree(icmp_tree, offset,      1, "Type: %d (%s)",
620       ih.icmp_type, type_str);
621     add_item_to_tree(icmp_tree, offset +  1, 1, "Code: %d %s",
622       ih.icmp_code, code_str);
623     add_item_to_tree(icmp_tree, offset +  2, 2, "Checksum: 0x%04x",
624       ih.icmp_cksum);
625   }
626 }
627
628 void
629 dissect_igmp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
630   e_igmp     ih;
631   GtkWidget *igmp_tree, *ti;
632   guint16    cksum;
633   gchar      type_str[64] = "";
634
635   /* Avoids alignment problems on many architectures. */
636   memcpy(&ih, &pd[offset], sizeof(e_igmp));
637   /* To do: check for runts, errs, etc. */
638   cksum = ntohs(ih.igmp_cksum);
639   
640   switch (ih.igmp_t) {
641     case IGMP_M_QRY:
642       strcpy(type_str, "Router query");
643       break;
644     case IGMP_V1_M_RPT:
645       strcpy(type_str, "Host response (v1)");
646       break;
647     case IGMP_V2_LV_GRP:
648       strcpy(type_str, "Leave group (v2)");
649       break;
650     case IGMP_DVMRP:
651       strcpy(type_str, "DVMRP");
652       break;
653     case IGMP_PIM:
654       strcpy(type_str, "PIM");
655       break;
656     case IGMP_V2_M_RPT:
657       strcpy(type_str, "Host reponse (v2)");
658       break;
659     case IGMP_MTRC_RESP:
660       strcpy(type_str, "Traceroute response");
661       break;
662     case IGMP_MTRC:
663       strcpy(type_str, "Traceroute message");
664       break;
665     default:
666       strcpy(type_str, "Unknown IGMP");
667   }
668
669   if (fd->win_info[COL_NUM]) {    
670     strcpy(fd->win_info[COL_PROTOCOL], "IGMP");
671   }
672   
673   if (tree) {
674     ti = add_item_to_tree(GTK_WIDGET(tree), offset, 4,
675       "Internet Group Management Protocol");
676     igmp_tree = gtk_tree_new();
677     add_subtree(ti, igmp_tree, ETT_IGMP);
678     add_item_to_tree(igmp_tree, offset,     1, "Version: %d",
679       ih.igmp_v);
680     add_item_to_tree(igmp_tree, offset    , 1, "Type: %d (%s)",
681       ih.igmp_t, type_str);
682     add_item_to_tree(igmp_tree, offset + 1, 1, "Unused: 0x%02x",
683       ih.igmp_unused);
684     add_item_to_tree(igmp_tree, offset + 2, 2, "Checksum: 0x%04x",
685       ih.igmp_cksum);
686     add_item_to_tree(igmp_tree, offset + 4, 4, "Group address: %s",
687       ip_to_str((guint8 *) &ih.igmp_gaddr));
688   }
689 }