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