Ethereal-dev: [ethereal-dev] IP(v4) and TCP option dissection

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: guy@xxxxxxxxxx (Guy Harris)
Date: Mon, 12 Oct 1998 22:44:35 -0700 (PDT)
I just checked in some changes to add IP and TCP option dissecting, and
make some other changes.  As indicated, it probably won't take too much
work to use it for IPv6 options as well.

Add a routine to dissect IP or TCP options (and, from a look at RFC
1883, it should, perhaps with some additions, be able to handle IPv6
options as well).

Make the IPv4 and TCP dissectors use it.

Fix a typo in the IP dissector ("Unknon" for "Unknown").

Show the IP and TCP header lengths as byte counts rather than
4-byte-word counts.

Show the protocol field value of an IP header as a name if it's a
protocol we know about.

List the acknowledgment and urgent pointer values in a TCP header only
if the corresponding flag is set.

Make the ETT_ values members of an enum, so that the compiler
automatically assigns them sequential integer values (at least if said
compiler conforms to the ANSI C standard).

The patch is:

Index: packet.h
===================================================================
RCS file: /usr/local/cvsroot/ethereal/packet.h,v
retrieving revision 1.12
diff -c -r1.12 packet.h
*** packet.h	1998/10/12 01:40:53	1.12
--- packet.h	1998/10/13 05:39:14
***************
*** 200,205 ****
--- 200,211 ----
    guint32 ip_dst;
  } e_ip;
  
+ /* IP flags. */
+ #define IP_CE		0x8000		/* Flag: "Congestion"		*/
+ #define IP_DF		0x4000		/* Flag: "Don't Fragment"	*/
+ #define IP_MF		0x2000		/* Flag: "More Fragments"	*/
+ #define IP_OFFSET	0x1FFF		/* "Fragment Offset" part	*/
+ 
  #define IPTOS_TOS_MASK    0x1E
  #define IPTOS_TOS(tos)    ((tos) & IPTOS_TOS_MASK)
  #define IPTOS_NONE        0x00
***************
*** 208,213 ****
--- 214,276 ----
  #define IPTOS_RELIABILITY 0x04
  #define IPTOS_LOWCOST     0x02
  
+ #define IPTOS_PREC_MASK		0xE0
+ #define IPTOS_PREC(tos)		((tos)&IPTOS_PREC_MASK)
+ #define IPTOS_PREC_NETCONTROL           0xe0
+ #define IPTOS_PREC_INTERNETCONTROL      0xc0
+ #define IPTOS_PREC_CRITIC_ECP           0xa0
+ #define IPTOS_PREC_FLASHOVERRIDE        0x80
+ #define IPTOS_PREC_FLASH                0x60
+ #define IPTOS_PREC_IMMEDIATE            0x40
+ #define IPTOS_PREC_PRIORITY             0x20
+ #define IPTOS_PREC_ROUTINE              0x00
+ 
+ /* IP options */
+ #define IPOPT_COPY		0x80
+ 
+ #define	IPOPT_CONTROL		0x00
+ #define	IPOPT_RESERVED1		0x20
+ #define	IPOPT_MEASUREMENT	0x40
+ #define	IPOPT_RESERVED2		0x60
+ 
+ #define IPOPT_END	(0 |IPOPT_CONTROL)
+ #define IPOPT_NOOP	(1 |IPOPT_CONTROL)
+ #define IPOPT_SEC	(2 |IPOPT_CONTROL|IPOPT_COPY)
+ #define IPOPT_LSRR	(3 |IPOPT_CONTROL|IPOPT_COPY)
+ #define IPOPT_TIMESTAMP	(4 |IPOPT_MEASUREMENT)
+ #define IPOPT_RR	(7 |IPOPT_CONTROL)
+ #define IPOPT_SID	(8 |IPOPT_CONTROL|IPOPT_COPY)
+ #define IPOPT_SSRR	(9 |IPOPT_CONTROL|IPOPT_COPY)
+ #define IPOPT_RA	(20|IPOPT_CONTROL|IPOPT_COPY)
+ 
+ /* IP option lengths */
+ #define IPOLEN_SEC      11
+ #define IPOLEN_LSRR_MIN 3
+ #define IPOLEN_TIMESTAMP_MIN 5
+ #define IPOLEN_RR_MIN   3
+ #define IPOLEN_SID      4
+ #define IPOLEN_SSRR_MIN 3
+ 
+ #define IPSEC_UNCLASSIFIED	0x0000
+ #define	IPSEC_CONFIDENTIAL	0xF135
+ #define	IPSEC_EFTO		0x789A
+ #define	IPSEC_MMMM		0xBC4D
+ #define	IPSEC_RESTRICTED	0xAF13
+ #define	IPSEC_SECRET		0xD788
+ #define	IPSEC_TOPSECRET		0x6BC5
+ #define	IPSEC_RESERVED1		0x35E2
+ #define	IPSEC_RESERVED2		0x9AF1
+ #define	IPSEC_RESERVED3		0x4D78
+ #define	IPSEC_RESERVED4		0x24BD
+ #define	IPSEC_RESERVED5		0x135E
+ #define	IPSEC_RESERVED6		0x89AF
+ #define	IPSEC_RESERVED7		0xC4D6
+ #define	IPSEC_RESERVED8		0xE26B
+ 
+ #define	IPOPT_TS_TSONLY		0		/* timestamps only */
+ #define	IPOPT_TS_TSANDADDR	1		/* timestamps and addresses */
+ #define	IPOPT_TS_PRESPEC	3		/* specified modules only */
+ 
  #define IP_PROTO_ICMP  1
  #define IP_PROTO_IGMP  2
  #define IP_PROTO_TCP   6
***************
*** 256,261 ****
--- 319,356 ----
    guint16 th_urp;
  } e_tcphdr;
  
+ /*
+  *	TCP option
+  */
+  
+ #define TCPOPT_NOP		1	/* Padding */
+ #define TCPOPT_EOL		0	/* End of options */
+ #define TCPOPT_MSS		2	/* Segment size negotiating */
+ #define TCPOPT_WINDOW		3	/* Window scaling */
+ #define TCPOPT_SACK_PERM        4       /* SACK Permitted */
+ #define TCPOPT_SACK             5       /* SACK Block */
+ #define TCPOPT_ECHO             6
+ #define TCPOPT_ECHOREPLY        7
+ #define TCPOPT_TIMESTAMP	8	/* Better RTT estimations/PAWS */
+ #define TCPOPT_CC               11
+ #define TCPOPT_CCNEW            12
+ #define TCPOPT_CCECHO           13
+ 
+ /*
+  *     TCP option lengths
+  */
+ 
+ #define TCPOLEN_MSS            4
+ #define TCPOLEN_WINDOW         3
+ #define TCPOLEN_SACK_PERM      2
+ #define TCPOLEN_SACK_MIN       2
+ #define TCPOLEN_ECHO           6
+ #define TCPOLEN_ECHOREPLY      6
+ #define TCPOLEN_TIMESTAMP      10
+ #define TCPOLEN_CC             6
+ #define TCPOLEN_CCNEW          6
+ #define TCPOLEN_CCECHO         6
+ 
  /* UDP structs and definitions */
  
  typedef struct _e_udphdr {
***************
*** 279,331 ****
  /* Tree types.  Each dissect_* routine should have one for each
     add_subtree() call. */
  
! #define ETT_FRAME          0
! #define ETT_IEEE8023       1
! #define ETT_ETHER2         2
! #define ETT_LLC            3
! #define ETT_TOKEN_RING     4
! #define ETT_TR_IERR_CNT    5
! #define ETT_TR_NERR_CNT    6
! #define ETT_TR_MAC         7
! #define ETT_PPP            8
! #define ETT_ARP            9
! #define ETT_IP            10
! #define ETT_UDP           11
! #define ETT_TCP           12
! #define ETT_ICMP          13
! #define ETT_IGMP          14
! #define ETT_IPX           15
! #define ETT_SPX           16
! #define ETT_NCP           17
! #define ETT_DNS           18
! #define ETT_DNS_ANS       19
! #define ETT_DNS_QRY       20
! #define ETT_RIP           21
! #define ETT_RIP_VEC       22
! #define ETT_OSPF          23
! #define ETT_OSPF_HDR      24
! #define ETT_OSPF_HELLO    25
! #define ETT_OSPF_DESC     26
! #define ETT_OSPF_LSR      27
! #define ETT_OSPF_LSA_UPD  28
! #define ETT_OSPF_LSA      29
! #define ETT_LPD           30
! #define ETT_RAW           31
! #define ETT_BOOTP         32
! #define ETT_BOOTP_OPTION  33
! #define ETT_IPv6	  34
! #define ETT_CLNP          35
! #define ETT_COTP          36
! #define ETT_VINES         37
! #define ETT_VSPP          38
! #define ETT_IPXRIP        39
! #define ETT_IPXSAP        40
! #define ETT_IPXSAP_SERVER 41
! #define ETT_NULL          42
! #define ETT_FDDI          43
! 
! /* Should be the last item number plus one */
! #define NUM_TREE_TYPES 44
  
  /* The version of pcap.h that comes with some systems is missing these
   * #defines.
--- 374,432 ----
  /* Tree types.  Each dissect_* routine should have one for each
     add_subtree() call. */
  
! enum {
! 	ETT_FRAME,
! 	ETT_IEEE8023,
! 	ETT_ETHER2,
! 	ETT_LLC,
! 	ETT_TOKEN_RING,
! 	ETT_TR_IERR_CNT,
! 	ETT_TR_NERR_CNT,
! 	ETT_TR_MAC,
! 	ETT_PPP,
! 	ETT_ARP,
! 	ETT_FDDI,
! 	ETT_NULL,
! 	ETT_IP,
! 	ETT_IP_OPTIONS,
! 	ETT_IP_OPTION_SEC,
! 	ETT_IP_OPTION_ROUTE,
! 	ETT_IP_OPTION_TIMESTAMP,
! 	ETT_UDP,
! 	ETT_TCP,
! 	ETT_TCP_OPTIONS,
! 	ETT_TCP_OPTION_SACK,
! 	ETT_ICMP,
! 	ETT_IGMP,
! 	ETT_IPX,
! 	ETT_SPX,
! 	ETT_NCP,
! 	ETT_DNS,
! 	ETT_DNS_ANS,
! 	ETT_DNS_QRY,
! 	ETT_RIP,
! 	ETT_RIP_VEC,
! 	ETT_OSPF,
! 	ETT_OSPF_HDR,
! 	ETT_OSPF_HELLO,
! 	ETT_OSPF_DESC,
! 	ETT_OSPF_LSR,
! 	ETT_OSPF_LSA_UPD,
! 	ETT_OSPF_LSA,
! 	ETT_LPD,
! 	ETT_RAW,
! 	ETT_BOOTP,
! 	ETT_BOOTP_OPTION,
! 	ETT_IPv6,
! 	ETT_CLNP,
! 	ETT_COTP,
! 	ETT_VINES,
! 	ETT_VSPP,
! 	ETT_IPXRIP,
! 	ETT_IPXSAP,
! 	ETT_IPXSAP_SERVER,
! 	NUM_TREE_TYPES	/* last item number plus one */
! };
  
  /* The version of pcap.h that comes with some systems is missing these
   * #defines.
***************
*** 342,347 ****
--- 443,468 ----
  #ifndef DLT_PPP_BSDOS
  #define DLT_PPP_BSDOS 14
  #endif
+ 
+ typedef enum {
+   NO_LENGTH,		/* option has no data, hence no length */
+   FIXED_LENGTH,		/* option always has the same length */
+   VARIABLE_LENGTH	/* option is variable-length - optlen is minimum */
+ } opt_len_type;
+ 
+ /* Member of table of IP or TCP options. */
+ typedef struct {
+   int   optcode;	/* code for option */
+   char *name;		/* name of option */
+   opt_len_type len_type; /* type of option length field */
+   int	optlen;		/* value length should be (minimum if VARIABLE) */
+   void	(*dissect)(GtkWidget *, const char *, const u_char *, int, guint);
+ 			/* routine to dissect option */
+ } ip_tcp_opt;
+ 
+ /* Routine to dissect IP or TCP options. */
+ void       dissect_ip_tcp_options(GtkWidget *, const u_char *, int, guint,
+     ip_tcp_opt *, int, int);
  
  /* Utility routines used by packet*.c */
  gchar*     ether_to_str(guint8 *);
Index: packet-ip.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/packet-ip.c,v
retrieving revision 1.6
diff -c -r1.6 packet-ip.c
*** packet-ip.c	1998/10/10 18:23:42	1.6
--- packet-ip.c	1998/10/13 05:39:15
***************
*** 47,57 ****
  
  extern packet_info pi;
  
  void
  dissect_ip(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
    e_ip       iph;
!   GtkWidget *ip_tree, *ti;
    gchar      tos_str[32];
  
    /* To do: check for runts, errs, etc. */
    /* Avoids alignment problems on many architectures. */
--- 47,410 ----
  
  extern packet_info pi;
  
+ static void
+ dissect_ipopt_security(GtkWidget *opt_tree, const char *name,
+     const u_char *opd, int offset, guint optlen)
+ {
+   GtkWidget *field_tree = NULL, *tf;
+   guint      val;
+   gchar     *secl_str;
+   static value_string secl_vals[] = {
+     {IPSEC_UNCLASSIFIED, "Unclassified"},
+     {IPSEC_CONFIDENTIAL, "Confidential"},
+     {IPSEC_EFTO,         "EFTO"        },
+     {IPSEC_MMMM,         "MMMM"        },
+     {IPSEC_RESTRICTED,   "Restricted"  },
+     {IPSEC_SECRET,       "Secret"      },
+     {IPSEC_TOPSECRET,    "Top secret"  },
+     {IPSEC_RESERVED1,    "Reserved"    },
+     {IPSEC_RESERVED2,    "Reserved"    },
+     {IPSEC_RESERVED3,    "Reserved"    },
+     {IPSEC_RESERVED4,    "Reserved"    },
+     {IPSEC_RESERVED5,    "Reserved"    },
+     {IPSEC_RESERVED6,    "Reserved"    },
+     {IPSEC_RESERVED7,    "Reserved"    },
+     {IPSEC_RESERVED8,    "Reserved"    } };
+ #define	N_SECL_VALS	(sizeof secl_vals / sizeof secl_vals[0])
+ 
+ 
+   tf = add_item_to_tree(opt_tree, offset,      optlen, "%s:", name);
+   field_tree = gtk_tree_new();
+   add_subtree(tf, field_tree, ETT_IP_OPTION_SEC);
+   offset += 2;
+ 
+   val = pntohs(opd);
+   if ((secl_str = match_strval(val, secl_vals, N_SECL_VALS)))
+     add_item_to_tree(field_tree, offset,       2,
+               "Security: %s", secl_str);
+   else
+     add_item_to_tree(field_tree, offset,       2,
+               "Security: Unknown (0x%x)", val);
+   offset += 2;
+   opd += 2;
+ 
+   val = pntohs(opd);
+   add_item_to_tree(field_tree, offset,         2,
+               "Compartments: %d", val);
+   offset += 2;
+   opd += 2;
+ 
+   add_item_to_tree(field_tree, offset,         2,
+               "Handling restrictions: %c%c", opd[0], opd[1]);
+   offset += 2;
+   opd += 2;
+ 
+   add_item_to_tree(field_tree, offset,         3,
+               "Transmission control code: %c%c%c", opd[0], opd[1], opd[2]);
+ }
+ 
+ static void
+ dissect_ipopt_route(GtkWidget *opt_tree, const char *name,
+     const u_char *opd, int offset, guint optlen)
+ {
+   GtkWidget *field_tree = NULL, *tf;
+   int ptr;
+   int optoffset = 0;
+   struct in_addr addr;
+ 
+   tf = add_item_to_tree(opt_tree, offset,      optlen, "%s (%d bytes)", name,
+               optlen);
+   field_tree = gtk_tree_new();
+   add_subtree(tf, field_tree, ETT_IP_OPTION_ROUTE);
+ 
+   optoffset += 2;	/* skip past type and length */
+   optlen -= 2;		/* subtract size of type and length */
+ 
+   ptr = *opd;
+   add_item_to_tree(field_tree, offset + optoffset, 1,
+               "Pointer: %d%s", ptr,
+               ((ptr < 4) ? " (points before first address)" :
+                ((ptr & 3) ? " (points to middle of address)" : "")));
+   optoffset++;
+   opd++;
+   optlen--;
+   ptr--;	/* ptr is 1-origin */
+ 
+   while (optlen > 0) {
+     if (optlen < 4) {
+       add_item_to_tree(field_tree, offset,      optlen,
+         "(suboption would go past end of option)");
+       break;
+     }
+ 
+     /* Avoids alignment problems on many architectures. */
+     memcpy((char *)&addr, (char *)opd, sizeof(addr));
+ 
+     add_item_to_tree(field_tree, offset + optoffset, 4,
+               "%s%s",
+               ((addr.s_addr == 0) ? "-" : (char *)get_hostname(addr.s_addr)),
+               ((optoffset == ptr) ? " <- (current)" : ""));
+     optoffset += 4;
+     opd += 4;
+     optlen -= 4;
+   }
+ }
+ 
+ static void
+ dissect_ipopt_sid(GtkWidget *opt_tree, const char *name, const u_char *opd,
+     int offset, guint optlen)
+ {
+   add_item_to_tree(opt_tree, offset,      optlen,
+     "%s: %d", name, pntohs(opd));
+   return;
+ }
+ 
+ static void
+ dissect_ipopt_timestamp(GtkWidget *opt_tree, const char *name, const u_char *opd,
+     int offset, guint optlen)
+ {
+   GtkWidget *field_tree = NULL, *tf;
+   int        ptr;
+   int        optoffset = 0;
+   int        flg;
+   gchar     *flg_str;
+   static value_string flag_vals[] = {
+     {IPOPT_TS_TSONLY,    "Time stamps only"                      },
+     {IPOPT_TS_TSANDADDR, "Time stamp and address"                },
+     {IPOPT_TS_PRESPEC,   "Time stamps for prespecified addresses"} };
+ #define	N_FLAG_VALS	(sizeof flag_vals / sizeof flag_vals[0])
+ 
+   struct in_addr addr;
+   guint ts;
+ 
+   tf = add_item_to_tree(opt_tree, offset,      optlen, "%s:", name);
+   field_tree = gtk_tree_new();
+   add_subtree(tf, field_tree, ETT_IP_OPTION_TIMESTAMP);
+ 
+   optoffset += 2;	/* skip past type and length */
+   optlen -= 2;		/* subtract size of type and length */
+ 
+   ptr = *opd;
+   add_item_to_tree(field_tree, offset + optoffset, 1,
+               "Pointer: %d%s", ptr,
+               ((ptr < 5) ? " (points before first address)" :
+                (((ptr - 1) & 3) ? " (points to middle of address)" : "")));
+   optoffset++;
+   opd++;
+   optlen--;
+   ptr--;	/* ptr is 1-origin */
+ 
+   flg = *opd;
+   add_item_to_tree(field_tree, offset + optoffset,   1,
+         "Overflow: %d", flg >> 4);
+   flg &= 0xF;
+   if ((flg_str = match_strval(flg, flag_vals, N_FLAG_VALS)))
+     add_item_to_tree(field_tree, offset + optoffset, 1,
+         "Flag: %s", flg_str);
+   else
+     add_item_to_tree(field_tree, offset + optoffset, 1,
+         "Flag: Unknown (0x%x)", flg);
+   optoffset++;
+   opd++;
+   optlen--;
+ 
+   while (optlen > 0) {
+     if (flg == IPOPT_TS_TSANDADDR) {
+       if (optlen < 4) {
+         add_item_to_tree(field_tree, offset + optoffset, optlen,
+           "(suboption would go past end of option)");
+         break;
+       }
+       /* XXX - check whether it goes past end of packet */
+       ts = pntohl(opd);
+       opd += 4;
+       optlen -= 4;
+       if (optlen < 4) {
+         add_item_to_tree(field_tree, offset + optoffset, optlen,
+           "(suboption would go past end of option)");
+         break;
+       }
+       /* XXX - check whether it goes past end of packet */
+       memcpy((char *)&addr, (char *)opd, sizeof(addr));
+       opd += 4;
+       optlen -= 4;
+       add_item_to_tree(field_tree, offset,      8,
+           "Address = %s, time stamp = %u",
+           ((addr.s_addr == 0) ? "-" :  (char *)get_hostname(addr.s_addr)),
+           ts);
+       optoffset += 8;
+     } else {
+       if (optlen < 4) {
+         add_item_to_tree(field_tree, offset + optoffset, optlen,
+           "(suboption would go past end of option)");
+         break;
+       }
+       /* XXX - check whether it goes past end of packet */
+       ts = pntohl(opd);
+       opd += 4;
+       optlen -= 4;
+       add_item_to_tree(field_tree, offset + optoffset, 4,
+           "Time stamp = %u", ts);
+       optoffset += 4;
+     }
+   }
+ }
+ 
+ static ip_tcp_opt ipopts[] = {
+   {
+     IPOPT_END,
+     "EOL",
+     NO_LENGTH,
+     0,
+     NULL,
+   },
+   {
+     IPOPT_NOOP,
+     "NOP",
+     NO_LENGTH,
+     0,
+     NULL,
+   },
+   {
+     IPOPT_SEC,
+     "Security",
+     FIXED_LENGTH,
+     IPOLEN_SEC,
+     dissect_ipopt_security
+   },
+   {
+     IPOPT_SSRR,
+     "Strict source route",
+     VARIABLE_LENGTH,
+     IPOLEN_SSRR_MIN,
+     dissect_ipopt_route
+   },
+   {
+     IPOPT_LSRR,
+     "Loose source route",
+     VARIABLE_LENGTH,
+     IPOLEN_LSRR_MIN,
+     dissect_ipopt_route
+   },
+   {
+     IPOPT_RR,
+     "Record route",
+     VARIABLE_LENGTH,
+     IPOLEN_RR_MIN,
+     dissect_ipopt_route
+   },
+   {
+     IPOPT_SID,
+     "Stream identifier",
+     FIXED_LENGTH,
+     IPOLEN_SID,
+     dissect_ipopt_sid
+   },
+   {
+     IPOPT_TIMESTAMP,
+     "Time stamp",
+     VARIABLE_LENGTH,
+     IPOLEN_TIMESTAMP_MIN,
+     dissect_ipopt_timestamp
+   }
+ };
+ 
+ #define N_IP_OPTS	(sizeof ipopts / sizeof ipopts[0])
+ 
+ /* Dissect the IP or TCP options in a packet. */
+ void
+ dissect_ip_tcp_options(GtkWidget *opt_tree, const u_char *opd, int offset,
+     guint length, ip_tcp_opt *opttab, int nopts, int eol)
+ {
+   u_char      opt;
+   ip_tcp_opt *optp;
+   guint       len;
+ 
+   while (length > 0) {
+     opt = *opd++;
+     for (optp = &opttab[0]; optp < &opttab[nopts]; optp++) {
+       if (optp->optcode == opt)
+         break;
+     }
+     if (optp == &opttab[nopts]) {
+       add_item_to_tree(opt_tree, offset,      1, "Unknown");
+       /* We don't know how long this option is, so we don't know how much
+          of it to skip, so we just bail. */
+       return;
+     }
+     --length;      /* account for type byte */
+     if (optp->len_type != NO_LENGTH) {
+       /* Option has a length. Is it in the packet? */
+       if (length == 0) {
+         /* Bogus - packet must at least include option code byte and
+            length byte! */
+         add_item_to_tree(opt_tree, offset,      1,
+               "%s (length byte past end of header)", optp->name);
+         return;
+       }
+       len = *opd++;  /* total including type, len */
+       --length;    /* account for length byte */
+       if (len < 2) {
+         /* Bogus - option length is too short to include option code and
+            option length. */
+         add_item_to_tree(opt_tree, offset,      2,
+               "%s (with too-short option length = %u bytes)", optp->name, 2);
+         return;
+       } else if (len - 2 > length) {
+         /* Bogus - option goes past the end of the header. */
+         add_item_to_tree(opt_tree, offset,      length,
+               "%s (option goes past end of header)", optp->name);
+         return;
+       } else if (optp->len_type == FIXED_LENGTH && len != optp->optlen) {
+         /* Bogus - option length isn't what it's supposed to be for this
+            option. */
+         add_item_to_tree(opt_tree, offset,      len,
+               "%s (with option length = %u bytes; should be %u)", optp->name,
+               len, optp->optlen);
+         return;
+       } else if (optp->len_type == VARIABLE_LENGTH && len < optp->optlen) {
+         /* Bogus - option length is less than what it's supposed to be for
+            this option. */
+         add_item_to_tree(opt_tree, offset,      len,
+               "%s (with option length = %u bytes; should be >= %u)", optp->name,
+               len, optp->optlen);
+         return;
+       } else {
+         if (optp->dissect != NULL) {
+           /* Option has a dissector. */
+           (*optp->dissect)(opt_tree, optp->name, opd, offset, len);
+         } else {
+           /* Option has no data, hence no dissector. */
+           add_item_to_tree(opt_tree, offset,      len, "%s", optp->name);
+         }
+         len -= 2;	/* subtract size of type and length */
+         offset += 2 + len;
+       }
+       opd += len;
+       length -= len;
+     } else {
+       add_item_to_tree(opt_tree, offset,      1, "%s", optp->name);
+       offset += 1;
+     }
+     if (opt == eol)
+       break;
+   }
+ }
+ 
  void
  dissect_ip(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
    e_ip       iph;
!   GtkWidget *ip_tree, *ti, *field_tree, *tf;
    gchar      tos_str[32];
+   guint      hlen, optlen;
+   gchar     *proto_str;
+   static value_string proto_vals[] = { {IP_PROTO_ICMP, "ICMP"},
+                                        {IP_PROTO_IGMP, "IGMP"},
+                                        {IP_PROTO_TCP,  "TCP" },
+                                        {IP_PROTO_UDP,  "UDP" },
+                                        {IP_PROTO_OSPF, "OSPF"} };
+ #define	N_PROTO_VALS	(sizeof proto_vals / sizeof proto_vals[0])
+ 
  
    /* To do: check for runts, errs, etc. */
    /* Avoids alignment problems on many architectures. */
***************
*** 60,65 ****
--- 413,420 ----
    iph.ip_id  = ntohs(iph.ip_id);
    iph.ip_off = ntohs(iph.ip_off);
    iph.ip_sum = ntohs(iph.ip_sum);
+ 
+   hlen = iph.ip_hl * 4;	/* IP header length, in bytes */
    
    if (fd->win_info[COL_NUM]) {
      switch (iph.ip_p) {
***************
*** 97,113 ****
        strcpy(tos_str, "Minimize cost");
        break;
      default:
!       strcpy(tos_str, "Unknon.  Malformed?");
        break;
    }
    
    if (tree) {
!     ti = add_item_to_tree(GTK_WIDGET(tree), offset, (iph.ip_hl * 4),
!       "Internet Protocol");
      ip_tree = gtk_tree_new();
      add_subtree(ti, ip_tree, ETT_IP);
      add_item_to_tree(ip_tree, offset,      1, "Version: %d", iph.ip_v);
!     add_item_to_tree(ip_tree, offset,      1, "Header length: %d", iph.ip_hl); 
      add_item_to_tree(ip_tree, offset +  1, 1, "Type of service: 0x%02x (%s)",
        iph.ip_tos, tos_str);
      add_item_to_tree(ip_tree, offset +  2, 2, "Total length: %d", iph.ip_len);
--- 452,467 ----
        strcpy(tos_str, "Minimize cost");
        break;
      default:
!       strcpy(tos_str, "Unknown.  Malformed?");
        break;
    }
    
    if (tree) {
!     ti = add_item_to_tree(GTK_WIDGET(tree), offset, hlen, "Internet Protocol");
      ip_tree = gtk_tree_new();
      add_subtree(ti, ip_tree, ETT_IP);
      add_item_to_tree(ip_tree, offset,      1, "Version: %d", iph.ip_v);
!     add_item_to_tree(ip_tree, offset,      1, "Header length: %d bytes", hlen); 
      add_item_to_tree(ip_tree, offset +  1, 1, "Type of service: 0x%02x (%s)",
        iph.ip_tos, tos_str);
      add_item_to_tree(ip_tree, offset +  2, 2, "Total length: %d", iph.ip_len);
***************
*** 115,131 ****
        iph.ip_id);
      /* To do: add flags */
      add_item_to_tree(ip_tree, offset +  6, 2, "Fragment offset: %d",
!       iph.ip_off & 0x1fff);
      add_item_to_tree(ip_tree, offset +  8, 1, "Time to live: %d",
        iph.ip_ttl);
!     add_item_to_tree(ip_tree, offset +  9, 1, "Protocol: 0x%02x",
!       iph.ip_p);
      add_item_to_tree(ip_tree, offset + 10, 2, "Header checksum: 0x%04x",
        iph.ip_sum);
      add_item_to_tree(ip_tree, offset + 12, 4, "Source address: %s",
  		     get_hostname(iph.ip_src));
      add_item_to_tree(ip_tree, offset + 16, 4, "Destination address: %s",
  		     get_hostname(iph.ip_dst));
    }
  
    pi.srcip = ip_to_str( (guint8 *) &iph.ip_src);
--- 469,501 ----
        iph.ip_id);
      /* To do: add flags */
      add_item_to_tree(ip_tree, offset +  6, 2, "Fragment offset: %d",
!       iph.ip_off & IP_OFFSET);
      add_item_to_tree(ip_tree, offset +  8, 1, "Time to live: %d",
        iph.ip_ttl);
!     if ((proto_str = match_strval(iph.ip_p, proto_vals, N_PROTO_VALS)))
!       add_item_to_tree(ip_tree, offset +  9, 1, "Protocol: %s", proto_str);
!     else
!       add_item_to_tree(ip_tree, offset +  9, 1, "Protocol: Unknown (%x)",
!         iph.ip_p);
      add_item_to_tree(ip_tree, offset + 10, 2, "Header checksum: 0x%04x",
        iph.ip_sum);
      add_item_to_tree(ip_tree, offset + 12, 4, "Source address: %s",
  		     get_hostname(iph.ip_src));
      add_item_to_tree(ip_tree, offset + 16, 4, "Destination address: %s",
  		     get_hostname(iph.ip_dst));
+ 
+     /* Decode IP options, if any. */
+     if (hlen > sizeof (e_ip)) {
+       /* There's more than just the fixed-length header.  Decode the
+          options. */
+       optlen = hlen - sizeof (e_ip);	/* length of options, in bytes */
+       tf = add_item_to_tree(ip_tree, offset +  20, optlen,
+         "Options: (%d bytes)", optlen);
+       field_tree = gtk_tree_new();
+       add_subtree(tf, field_tree, ETT_IP_OPTIONS);
+       dissect_ip_tcp_options(field_tree, &pd[offset + 20], offset + 20, optlen,
+          ipopts, N_IP_OPTS, IPOPT_END);
+     }
    }
  
    pi.srcip = ip_to_str( (guint8 *) &iph.ip_src);
***************
*** 135,141 ****
    pi.iphdrlen = iph.ip_hl;
    pi.ip_src = iph.ip_src;
  
!   offset += iph.ip_hl * 4;
    switch (iph.ip_p) {
      case IP_PROTO_ICMP:
        dissect_icmp(pd, offset, fd, tree);
--- 505,511 ----
    pi.iphdrlen = iph.ip_hl;
    pi.ip_src = iph.ip_src;
  
!   offset += hlen;
    switch (iph.ip_p) {
      case IP_PROTO_ICMP:
        dissect_icmp(pd, offset, fd, tree);
Index: packet-tcp.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/packet-tcp.c,v
retrieving revision 1.4
diff -c -r1.4 packet-tcp.c
*** packet-tcp.c	1998/09/27 22:12:38	1.4
--- packet-tcp.c	1998/10/13 05:39:15
***************
*** 46,59 ****
  extern FILE* data_out_file;
  extern packet_info pi;
  
  void
  dissect_tcp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
    e_tcphdr   th;
!   GtkWidget *tcp_tree, *ti;
    gchar      flags[64] = "<None>";
    gchar     *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG"};
    gint       fpos = 0, i;
    guint      bpos;
  
    /* To do: Check for {cap len,pkt len} < struct len */
    /* Avoids alignment problems on many architectures. */
--- 46,230 ----
  extern FILE* data_out_file;
  extern packet_info pi;
  
+ static void
+ dissect_tcpopt_maxseg(GtkWidget *opt_tree, const char *name, const u_char *opd,
+     int offset, guint optlen)
+ {
+   add_item_to_tree(opt_tree, offset,      optlen,
+     "%s: %u bytes", name, pntohs(opd));
+ }
+ 
+ static void
+ dissect_tcpopt_wscale(GtkWidget *opt_tree, const char *name, const u_char *opd,
+     int offset, guint optlen)
+ {
+   add_item_to_tree(opt_tree, offset,      optlen,
+     "%s: %u bytes", name, *opd);
+ }
+ 
+ static void
+ dissect_tcpopt_sack(GtkWidget *opt_tree, const char *name, const u_char *opd,
+     int offset, guint optlen)
+ {
+   GtkWidget *field_tree = NULL, *tf;
+   guint leftedge, rightedge;
+ 
+   tf = add_item_to_tree(opt_tree, offset,      optlen, "%s:", name);
+   offset += 2;	/* skip past type and length */
+   optlen -= 2;	/* subtract size of type and length */
+   while (optlen > 0) {
+     if (field_tree == NULL) {
+       /* Haven't yet made a subtree out of this option.  Do so. */
+       field_tree = gtk_tree_new();
+       add_subtree(tf, field_tree, ETT_TCP_OPTION_SACK);
+     }
+     if (optlen < 4) {
+       add_item_to_tree(field_tree, offset,      optlen,
+         "(suboption would go past end of option)");
+       break;
+     }
+     /* XXX - check whether it goes past end of packet */
+     leftedge = pntohl(opd);
+     opd += 4;
+     optlen -= 4;
+     if (optlen < 4) {
+       add_item_to_tree(field_tree, offset,      optlen,
+         "(suboption would go past end of option)");
+       break;
+     }
+     /* XXX - check whether it goes past end of packet */
+     rightedge = pntohl(opd);
+     opd += 4;
+     optlen -= 4;
+     add_item_to_tree(field_tree, offset,      8,
+         "left edge = %u, right edge = %u", leftedge, rightedge);
+     offset += 8;
+   }
+ }
+ 
+ static void
+ dissect_tcpopt_echo(GtkWidget *opt_tree, const char *name, const u_char *opd,
+     int offset, guint optlen)
+ {
+   add_item_to_tree(opt_tree, offset,      optlen,
+     "%s: %u", name, pntohl(opd));
+ }
+ 
+ static void
+ dissect_tcpopt_timestamp(GtkWidget *opt_tree, const char *name,
+     const u_char *opd, int offset, guint optlen)
+ {
+   add_item_to_tree(opt_tree, offset,      optlen,
+     "%s: tsval %u, tsecr %u", name, pntohl(opd), pntohl(opd + 4));
+ }
+ 
+ static void
+ dissect_tcpopt_cc(GtkWidget *opt_tree, const char *name, const u_char *opd,
+     int offset, guint optlen)
+ {
+   add_item_to_tree(opt_tree, offset,      optlen,
+     "%s: %u", name, pntohl(opd));
+ }
+ 
+ static ip_tcp_opt tcpopts[] = {
+   {
+     TCPOPT_EOL,
+     "EOL",
+     NO_LENGTH,
+     0,
+     NULL,
+   },
+   {
+     TCPOPT_NOP,
+     "NOP",
+     NO_LENGTH,
+     0,
+     NULL,
+   },
+   {
+     TCPOPT_MSS,
+     "Maximum segment size",
+     FIXED_LENGTH,
+     TCPOLEN_MSS,
+     dissect_tcpopt_maxseg
+   },
+   {
+     TCPOPT_WINDOW,
+     "Window scale",
+     FIXED_LENGTH,
+     TCPOLEN_WINDOW,
+     dissect_tcpopt_wscale
+   },
+   {
+     TCPOPT_SACK_PERM,
+     "SACK permitted",
+     FIXED_LENGTH,
+     TCPOLEN_SACK_PERM,
+     NULL,
+   },
+   {
+     TCPOPT_SACK,
+     "SACK",
+     VARIABLE_LENGTH,
+     TCPOLEN_SACK_MIN,
+     dissect_tcpopt_sack
+   },
+   {
+     TCPOPT_ECHO,
+     "Echo",
+     FIXED_LENGTH,
+     TCPOLEN_ECHO,
+     dissect_tcpopt_echo
+   },
+   {
+     TCPOPT_ECHOREPLY,
+     "Echo reply",
+     FIXED_LENGTH,
+     TCPOLEN_ECHOREPLY,
+     dissect_tcpopt_echo
+   },
+   {
+     TCPOPT_TIMESTAMP,
+     "Time stamp",
+     FIXED_LENGTH,
+     TCPOLEN_TIMESTAMP,
+     dissect_tcpopt_timestamp
+   },
+   {
+     TCPOPT_CC,
+     "CC",
+     FIXED_LENGTH,
+     TCPOLEN_CC,
+     dissect_tcpopt_cc
+   },
+   {
+     TCPOPT_CCNEW,
+     "CC.NEW",
+     FIXED_LENGTH,
+     TCPOPT_CCNEW,
+     dissect_tcpopt_cc
+   },
+   {
+     TCPOPT_CCECHO,
+     "CC.ECHO",
+     FIXED_LENGTH,
+     TCPOLEN_CCECHO,
+     dissect_tcpopt_cc
+   }
+ };
+ 
+ #define N_TCP_OPTS	(sizeof tcpopts / sizeof tcpopts[0])
+ 
  void
  dissect_tcp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
    e_tcphdr   th;
!   GtkWidget *tcp_tree, *ti, *field_tree, *tf;
    gchar      flags[64] = "<None>";
    gchar     *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG"};
    gint       fpos = 0, i;
    guint      bpos;
+   guint      hlen;
+   guint      optlen;
  
    /* To do: Check for {cap len,pkt len} < struct len */
    /* Avoids alignment problems on many architectures. */
***************
*** 85,135 ****
        th.th_sport, th.th_dport);
    }
    
    if (tree) {
!     ti = add_item_to_tree(GTK_WIDGET(tree), offset, 20,
        "Transmission Control Protocol");
      tcp_tree = gtk_tree_new();
      add_subtree(ti, tcp_tree, ETT_TCP);
      add_item_to_tree(tcp_tree, offset,      2, "Source port: %d", th.th_sport);
      add_item_to_tree(tcp_tree, offset +  2, 2, "Destination port: %d", th.th_dport);
!     add_item_to_tree(tcp_tree, offset +  4, 4, "Sequence number: 0x%08x",
        th.th_seq);
!     add_item_to_tree(tcp_tree, offset +  8, 4, "Acknowledgement number: 0x%08x",
!       th.th_ack);
!     add_item_to_tree(tcp_tree, offset + 12, 1, "Header length: %d", th.th_off);
      add_item_to_tree(tcp_tree, offset + 13, 1, "Flags: %s", flags);
      add_item_to_tree(tcp_tree, offset + 14, 2, "Window size: %d", th.th_win);
      add_item_to_tree(tcp_tree, offset + 16, 2, "Checksum: 0x%04x", th.th_sum);
!     add_item_to_tree(tcp_tree, offset + 18, 2, "Urgent pointer: 0x%04x",
!       th.th_urp);
!     /* To do: TCP options */
! 
!   }
!     /* Skip over header + options */
! 	offset += 4 * th.th_off;
! 
! 	/* until we decode those options, I'll check the packet length
! 	to see if there's more data. -- gilbert */
! 	if (fd->cap_len > offset) {
! 		switch(MIN(th.th_sport, th.th_dport)) {
! 			case TCP_PORT_PRINTER:
! 				dissect_lpd(pd, offset, fd, tree);
! 				break;
! 			default:
! 				dissect_data(pd, offset, fd, tree);
! 		}
! 	}
!  
! 	pi.srcport = th.th_sport;
! 	pi.destport = th.th_dport;
! 	
! 	if( data_out_file ) {
! 	  reassemble_tcp( th.th_seq, /* sequence number */
! 			  ( pi.iplen -( pi.iphdrlen * 4 )-( th.th_off * 4 ) ), /* length */
! 			  ( pd+offset ), /* data */
! 			  ( th.th_flags & 0x02 ), /* is syn set? */
! 			  pi.ip_src ); /* src ip */
! 	}
  
  
  }
--- 256,320 ----
        th.th_sport, th.th_dport);
    }
    
+   hlen = th.th_off * 4;  /* TCP header length, in bytes */
+ 
    if (tree) {
!     ti = add_item_to_tree(GTK_WIDGET(tree), offset, hlen,
        "Transmission Control Protocol");
      tcp_tree = gtk_tree_new();
      add_subtree(ti, tcp_tree, ETT_TCP);
      add_item_to_tree(tcp_tree, offset,      2, "Source port: %d", th.th_sport);
      add_item_to_tree(tcp_tree, offset +  2, 2, "Destination port: %d", th.th_dport);
!     add_item_to_tree(tcp_tree, offset +  4, 4, "Sequence number: %u",
        th.th_seq);
!     if (th.th_flags & TH_ACK)
!       add_item_to_tree(tcp_tree, offset +  8, 4, "Acknowledgement number: %u",
!         th.th_ack);
!     add_item_to_tree(tcp_tree, offset + 12, 1, "Header length: %d bytes", hlen);
      add_item_to_tree(tcp_tree, offset + 13, 1, "Flags: %s", flags);
      add_item_to_tree(tcp_tree, offset + 14, 2, "Window size: %d", th.th_win);
      add_item_to_tree(tcp_tree, offset + 16, 2, "Checksum: 0x%04x", th.th_sum);
!     if (th.th_flags & TH_URG)
!       add_item_to_tree(tcp_tree, offset + 18, 2, "Urgent pointer: 0x%04x",
!         th.th_urp);
! 
!     /* Decode TCP options, if any. */
!     if (hlen > sizeof (e_tcphdr)) {
!       /* There's more than just the fixed-length header.  Decode the
!          options. */
!       optlen = hlen - sizeof (e_tcphdr); /* length of options, in bytes */
!       tf = add_item_to_tree(tcp_tree, offset +  20, optlen,
!         "Options: (%d bytes)", optlen);
!       field_tree = gtk_tree_new();
!       add_subtree(tf, field_tree, ETT_TCP_OPTIONS);
!       dissect_ip_tcp_options(field_tree, &pd[offset + 20], offset + 20, optlen,
!          tcpopts, N_TCP_OPTS, TCPOPT_EOL);
!     }
!   }
  
+   /* Skip over header + options */
+   offset += hlen;
  
+   /* until we decode those options, I'll check the packet length
+   to see if there's more data. -- gilbert */
+   if (fd->cap_len > offset) {
+     switch(MIN(th.th_sport, th.th_dport)) {
+       case TCP_PORT_PRINTER:
+         dissect_lpd(pd, offset, fd, tree);
+         break;
+       default:
+         dissect_data(pd, offset, fd, tree);
+     }
+   }
+  
+   pi.srcport = th.th_sport;
+   pi.destport = th.th_dport;
+   
+   if( data_out_file ) {
+     reassemble_tcp( th.th_seq, /* sequence number */
+         ( pi.iplen -( pi.iphdrlen * 4 )-( th.th_off * 4 ) ), /* length */
+         ( pd+offset ), /* data */
+         ( th.th_flags & 0x02 ), /* is syn set? */
+         pi.ip_src ); /* src ip */
+   }
  }