wireshark/packet-gre.c
Guy Harris 79e1fdb9e5 Remove more "CHECK_DISPLAY_AS_DATA()" calls and "pinfo->current_proto ="
statements.

Move the setting of the Protocol column in the Appletalk ARP and IPX
dissectors before anything is fetched from the packet, and also clear
the Info column at that point in those and some other dissectors, so
that if an exception is thrown, the columns don't reflect the previous
protocol.

Fix the registration of the IPX RIP dissector to use the right protocol
ID.

svn path=/trunk/; revision=2928
2001-01-22 00:20:29 +00:00

366 lines
11 KiB
C

/* packet-gre.c
* Routines for the Generic Routing Encapsulation (GRE) protocol
* Brad Robel-Forrest <brad.robel-forrest@watchguard.com>
*
* $Id: packet-gre.c,v 1.39 2001/01/22 00:20:29 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
* Copyright 1998 Gerald Combs
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <glib.h>
#include "etypes.h"
#include "greproto.h"
#include "packet.h"
#include "packet-ip.h"
#include "packet-ipx.h"
#include "packet-wccp.h"
#include "in_cksum.h"
static int proto_gre = -1;
static int hf_gre_proto = -1;
static gint ett_gre = -1;
static gint ett_gre_flags = -1;
static gint ett_gre_wccp2_redirect_header = -1;
static dissector_table_t gre_dissector_table;
/* bit positions for flags in header */
#define GH_B_C 0x8000
#define GH_B_R 0x4000
#define GH_B_K 0x2000
#define GH_B_S 0x1000
#define GH_B_s 0x0800
#define GH_B_RECUR 0x0700
#define GH_P_A 0x0080 /* only in special PPTPized GRE header */
#define GH_P_FLAGS 0x0078 /* only in special PPTPized GRE header */
#define GH_R_FLAGS 0x00F8
#define GH_B_VER 0x0007
static void add_flags_and_ver(proto_tree *, guint16, tvbuff_t *, int, int);
static void dissect_gre_wccp2_redirect_header(tvbuff_t *, int, proto_tree *);
static const value_string typevals[] = {
{ ETHERTYPE_PPP, "PPP" },
{ ETHERTYPE_IP, "IP" },
{ GRE_WCCP, "WCCP"},
{ ETHERTYPE_IPX, "IPX"},
{ GRE_FR, "FR"},
{ 0, NULL }
};
static void
dissect_gre(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
int offset = 0;
guint16 flags_and_ver;
guint16 type;
gboolean is_ppp = FALSE;
gboolean is_wccp2 = FALSE;
guint len = 4;
proto_item *ti;
proto_tree *gre_tree = NULL;
guint16 sre_af;
guint8 sre_length;
tvbuff_t *next_tvb;
flags_and_ver = tvb_get_ntohs(tvb, offset);
type = tvb_get_ntohs(tvb, offset + sizeof(flags_and_ver));
if (check_col(pinfo->fd, COL_PROTOCOL))
col_set_str(pinfo->fd, COL_PROTOCOL, "GRE");
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "Encapsulated %s",
val_to_str(type, typevals, "0x%04X (unknown)"));
}
if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R)
len += 4;
if (flags_and_ver & GH_B_K)
len += 4;
if (flags_and_ver & GH_B_S)
len += 4;
switch (type) {
case ETHERTYPE_PPP:
if (flags_and_ver & GH_P_A)
len += 4;
is_ppp = TRUE;
break;
case GRE_WCCP:
/* WCCP2 puts an extra 4 octets into the header, but uses the same
encapsulation type; if it looks as if the first octet of the packet
isn't the beginning of an IPv4 header, assume it's WCCP2. */
if ((tvb_get_guint8(tvb, offset + sizeof(flags_and_ver) + sizeof(type)) & 0xF0) != 0x40) {
len += 4;
is_wccp2 = TRUE;
}
break;
}
if (tree) {
ti = proto_tree_add_protocol_format(tree, proto_gre, tvb, offset, len,
"Generic Routing Encapsulation (%s)",
val_to_str(type, typevals, "0x%04X - unknown"));
gre_tree = proto_item_add_subtree(ti, ett_gre);
add_flags_and_ver(gre_tree, flags_and_ver, tvb, offset, is_ppp);
}
offset += sizeof(flags_and_ver);
if (tree) {
proto_tree_add_uint(gre_tree, hf_gre_proto, tvb, offset, sizeof(type), type);
}
offset += sizeof(type);
if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) {
if (tree) {
guint length, reported_length;
vec_t cksum_vec[1];
guint16 cksum, computed_cksum;
cksum = tvb_get_ntohs(tvb, offset);
length = tvb_length(tvb);
reported_length = tvb_reported_length(tvb);
if ((flags_and_ver & GH_B_C) && !pinfo->fragmented
&& length >= reported_length) {
/* The Checksum Present bit is set, and the packet isn't part of a
fragmented datagram and isn't truncated, so we can checksum it. */
cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, reported_length);
cksum_vec[0].len = reported_length;
computed_cksum = in_cksum(cksum_vec, 1);
if (computed_cksum == 0) {
proto_tree_add_text(gre_tree, tvb, offset, 2,
"Checksum: 0x%04x (correct)", cksum);
} else {
proto_tree_add_text(gre_tree, tvb, offset, 2,
"Checksum: 0x%04x (incorrect, should be 0x%04x)",
cksum, in_cksum_shouldbe(cksum, computed_cksum));
}
} else {
proto_tree_add_text(gre_tree, tvb, offset, 2,
"Checksum: 0x%04x", cksum);
}
}
offset += 2;
}
if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) {
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, 2,
"Offset: %u", tvb_get_ntohs(tvb, offset));
}
offset += 2;
}
if (flags_and_ver & GH_B_K) {
if (is_ppp) {
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, 2,
"Payload length: %u", tvb_get_ntohs(tvb, offset));
}
offset += 2;
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, 2,
"Call ID: %u", tvb_get_ntohs(tvb, offset));
}
offset += 2;
}
else {
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, 4,
"Key: %u", tvb_get_ntohl(tvb, offset));
}
offset += 4;
}
}
if (flags_and_ver & GH_B_S) {
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, 4,
"Sequence number: %u", tvb_get_ntohl(tvb, offset));
}
offset += 4;
}
if (is_ppp && flags_and_ver & GH_P_A) {
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, 4,
"Acknowledgement number: %u", tvb_get_ntohl(tvb, offset));
}
offset += 4;
}
if (flags_and_ver & GH_B_R) {
for (;;) {
sre_af = tvb_get_ntohs(tvb, offset);
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, sizeof(guint16),
"Address family: %u", sre_af);
}
offset += sizeof(guint16);
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, 1,
"SRE offset: %u", tvb_get_guint8(tvb, offset));
}
offset += sizeof(guint8);
sre_length = tvb_get_guint8(tvb, offset);
if (tree) {
proto_tree_add_text(gre_tree, tvb, offset, sizeof(guint8),
"SRE length: %u", sre_length);
}
offset += sizeof(guint8);
if (sre_af == 0 && sre_length == 0)
break;
offset += sre_length;
}
}
if (type == GRE_WCCP) {
if (is_wccp2) {
if (tree)
dissect_gre_wccp2_redirect_header(tvb, offset, gre_tree);
offset += 4;
}
}
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
if (!dissector_try_port(gre_dissector_table, type, next_tvb, pinfo, tree))
dissect_data(next_tvb, 0, pinfo, gre_tree);
}
static void
add_flags_and_ver(proto_tree *tree, guint16 flags_and_ver, tvbuff_t *tvb,
int offset, int is_ppp)
{
proto_item * ti;
proto_tree * fv_tree;
int nbits = sizeof(flags_and_ver) * 8;
ti = proto_tree_add_text(tree, tvb, offset, 2,
"Flags and version: %#04x", flags_and_ver);
fv_tree = proto_item_add_subtree(ti, ett_gre_flags);
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_boolean_bitfield(flags_and_ver, GH_B_C, nbits,
"Checksum", "No checksum"));
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_boolean_bitfield(flags_and_ver, GH_B_R, nbits,
"Routing", "No routing"));
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_boolean_bitfield(flags_and_ver, GH_B_K, nbits,
"Key", "No key"));
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_boolean_bitfield(flags_and_ver, GH_B_S, nbits,
"Sequence number", "No sequence number"));
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_boolean_bitfield(flags_and_ver, GH_B_s, nbits,
"Strict source route", "No strict source route"));
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_numeric_bitfield(flags_and_ver, GH_B_RECUR, nbits,
"Recursion control: %u"));
if (is_ppp) {
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_boolean_bitfield(flags_and_ver, GH_P_A, nbits,
"Acknowledgment number", "No acknowledgment number"));
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_numeric_bitfield(flags_and_ver, GH_P_FLAGS, nbits,
"Flags: %u"));
}
else {
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_numeric_bitfield(flags_and_ver, GH_R_FLAGS, nbits,
"Flags: %u"));
}
proto_tree_add_text(fv_tree, tvb, offset, sizeof(flags_and_ver), "%s",
decode_numeric_bitfield(flags_and_ver, GH_B_VER, nbits,
"Version: %u"));
}
static void
dissect_gre_wccp2_redirect_header(tvbuff_t *tvb, int offset, proto_tree *tree)
{
proto_item * ti;
proto_tree * rh_tree;
guint8 rh_flags;
ti = proto_tree_add_text(tree, tvb, offset, 4, "Redirect header");
rh_tree = proto_item_add_subtree(ti, ett_gre_wccp2_redirect_header);
rh_flags = tvb_get_guint8(tvb, offset);
proto_tree_add_text(rh_tree, tvb, offset, 1, "%s",
decode_boolean_bitfield(rh_flags, 0x80, 8,
"Dynamic service", "Well-known service"));
proto_tree_add_text(rh_tree, tvb, offset, 1, "%s",
decode_boolean_bitfield(rh_flags, 0x40, 8,
"Alternative bucket used", "Alternative bucket not used"));
proto_tree_add_text(rh_tree, tvb, offset + 1, 1, "Service ID: %s",
val_to_str(tvb_get_guint8(tvb, offset + 1), service_id_vals, "Unknown (0x%02X)"));
if (rh_flags & 0x40)
proto_tree_add_text(rh_tree, tvb, offset + 2, 1, "Alternative bucket index: %u",
tvb_get_guint8(tvb, offset + 2));
proto_tree_add_text(rh_tree, tvb, offset + 3, 1, "Primary bucket index: %u",
tvb_get_guint8(tvb, offset + 3));
}
void
proto_register_gre(void)
{
static hf_register_info hf[] = {
{ &hf_gre_proto,
{ "Protocol Type", "gre.proto", FT_UINT16, BASE_HEX, VALS(typevals), 0x0,
"The protocol that is GRE encapsulated"}
},
};
static gint *ett[] = {
&ett_gre,
&ett_gre_flags,
&ett_gre_wccp2_redirect_header,
};
proto_gre = proto_register_protocol("Generic Routing Encapsulation",
"GRE", "gre");
proto_register_field_array(proto_gre, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
/* subdissector code */
gre_dissector_table = register_dissector_table("gre.proto");
}
void
proto_reg_handoff_gre(void)
{
dissector_add("ip.proto", IP_PROTO_GRE, dissect_gre, proto_gre);
}