![Guy Harris](/assets/img/avatar_default.png)
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
366 lines
11 KiB
C
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);
|
|
}
|