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