diff options
author | Guy Harris <guy@alum.mit.edu> | 1998-10-13 05:40:04 +0000 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 1998-10-13 05:40:04 +0000 |
commit | 7f2da15d6bb2b3828db092a5f22f4f958524edda (patch) | |
tree | 3522662c00d7d654a4a42fb99259b796381f42c1 /packet-ip.c | |
parent | d6907f93bc1c043972d4880b90b8a4d5a5df44df (diff) |
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).
svn path=/trunk/; revision=45
Diffstat (limited to 'packet-ip.c')
-rw-r--r-- | packet-ip.c | 390 |
1 files changed, 380 insertions, 10 deletions
diff --git a/packet-ip.c b/packet-ip.c index c3a73e6395..011d469b7e 100644 --- a/packet-ip.c +++ b/packet-ip.c @@ -1,7 +1,7 @@ /* packet-ip.c * Routines for IP and miscellaneous IP protocol packet disassembly * - * $Id: packet-ip.c,v 1.6 1998/10/10 18:23:42 gerald Exp $ + * $Id: packet-ip.c,v 1.7 1998/10/13 05:40:03 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -47,11 +47,364 @@ 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; + 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,6 +413,8 @@ dissect_ip(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) { 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,17 +452,16 @@ dissect_ip(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) { strcpy(tos_str, "Minimize cost"); break; default: - strcpy(tos_str, "Unknon. Malformed?"); + strcpy(tos_str, "Unknown. Malformed?"); break; } if (tree) { - ti = add_item_to_tree(GTK_WIDGET(tree), offset, (iph.ip_hl * 4), - "Internet Protocol"); + 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", iph.ip_hl); + 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,17 +469,33 @@ dissect_ip(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) { iph.ip_id); /* To do: add flags */ add_item_to_tree(ip_tree, offset + 6, 2, "Fragment offset: %d", - iph.ip_off & 0x1fff); + iph.ip_off & IP_OFFSET); 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); + 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,7 +505,7 @@ dissect_ip(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) { pi.iphdrlen = iph.ip_hl; pi.ip_src = iph.ip_src; - offset += iph.ip_hl * 4; + offset += hlen; switch (iph.ip_p) { case IP_PROTO_ICMP: dissect_icmp(pd, offset, fd, tree); |