/****************************************************************************
** File: gre.c
**
** Author: Mike Borella
**
** Comments:
**
** $Id: gre.c,v 1.12 2001/11/15 00:22:36 mborella Exp $
**
** 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 Library 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.
**
*****************************************************************************/
#include "gre.h"
#include "ip_protocols.h"
#include "ethertypes.h"
extern struct arg_t * my_args;
extern strmap_t ipproto_map[];
extern strmap_t ethertype_map[];
/*----------------------------------------------------------------------------
**
** dump_gre()
**
** Displays both versions of GRE headers
**
**----------------------------------------------------------------------------
*/
void dump_gre(packet_t *pkt)
{
gre_v0_rfc2784_t gre0;
gre_v1_t gre1;
ethertype_func_t et;
/* Set the layer */
set_layer(LAYER_NETWORK);
/*
* Stats accounting
*/
stats_update(STATS_GRE);
/*
* Look at the first 2 bytes of the GRE header so that we can check the
* version
*/
if (look_packet_bytes((u_int8_t *) &gre0, pkt, 2) == 0)
return;
/*
* Check version number
*/
switch(gre0.version)
{
case 0:
{
u_int16_t reserved;
u_int16_t protocol;
ethertype_func_t et;
/*
* Please note: If the reserved field is 0, we assume that
* the GRE packet follows RFC2784 and act accordingly. Otherwise
* we assume the RFC1701 interpretation. If key and sequence
* number fields are used, ala RFC2890, then we parse according
* RFC1701. Got all that?
*/
reserved = (gre0.reserved_high << 5) || gre0.reserved_low;
protocol = ntohs(gre0.protocol);
if (reserved == 0)
{
gre_v0_rfc2784_t gre;
u_int8_t C;
/*
* Get the header
*/
if (get_packet_bytes((u_int8_t *) &gre, pkt,
sizeof(gre_v0_rfc2784_t)) == 0)
return;
/*
* Conversions
*/
gre.protocol = ntohs(gre.protocol);
protocol = gre.protocol;
C = gre.c;
/* Dump the header */
if (my_args->m)
{
display_minimal_string("| GRE v0 ");
display_minimal((u_int8_t *) &gre.protocol, 2, DISP_HEX);
}
else
{
display_header_banner("GREv0 Header");
display("C", &C, 1, DISP_DEC);
display("Reserved", (u_int8_t *) &reserved, 2, DISP_DEC);
display_strmap_hex("Protocol", gre.protocol,
ethertype_map);
}
/*
* Get the checksum and reserved fields, if they exist
*/
if (C)
{
u_int16_t checksum;
u_int16_t reserved;
/* Get the fields */
if (get_packet_bytes((u_int8_t *) &checksum, pkt, 2) == 0)
return;
if (get_packet_bytes((u_int8_t *) &reserved, pkt, 2) == 0)
return;
/* conversions */
checksum = ntohs(checksum);
reserved = ntohs(reserved);
if (!my_args->m)
{
display("Checksum", (u_int8_t *) &checksum, 2, DISP_DEC);
display("Reserved", (u_int8_t *) &reserved, 2, DISP_DEC);
}
}
}
else
{
gre_v0_rfc1701_t gre;
u_int8_t C, R, K, S, s, recur, flags, version;
/*
* Get the header
*/
if (get_packet_bytes((u_int8_t *) &gre, pkt,
sizeof(gre_v0_rfc1701_t)) == 0)
return;
/*
* Conversions
*/
gre.protocol = ntohs(gre.protocol);
protocol = gre.protocol;
C = gre.c;
R = gre.r;
K = gre.k;
S = gre.S;
s = gre.s;
recur = gre.recur;
flags = gre.flags;
version = gre.version;
/* Dump the header */
if (my_args->m)
{
display_minimal_string("| GRE v0 ");
display_minimal((u_int8_t *) &gre.protocol, 2, DISP_HEX);
display_minimal_string(" ");
}
else
{
display_header_banner("GREv0 Header");
display("C", &C, 1, DISP_DEC);
display("R", &R, 1, DISP_DEC);
display("K", &K, 1, DISP_DEC);
display("S", &S, 1, DISP_DEC);
display("s", &s, 1, DISP_DEC);
display("Recur", &recur, 1, DISP_DEC);
display("Flags", &flags, 1, DISP_DEC);
display("Version", &version, 1, DISP_DEC);
display_strmap_hex("Protocol", gre.protocol,
ethertype_map);
}
/*
* Parse options fields. Note that if the C or the R bits are
* present then both the checksum and offset fields are present
*/
if (C || R)
{
u_int16_t checksum;
u_int16_t offset;
/*
* Nothing to report for minimal mode here, so just get
* the checksum and offset and display them.
*/
if (get_packet_bytes((u_int8_t *) &checksum, pkt, 2)
== 0)
return;
checksum = ntohs(checksum);
if (!my_args->m)
display("Checksum", (u_int8_t *) &checksum, 2, DISP_DEC);
if (get_packet_bytes((u_int8_t *) &offset, pkt, 2)
== 0)
return;
offset = ntohs(offset);
if (!my_args->m)
display("Offset", (u_int8_t *) &offset, 2, DISP_DEC);
}
/* Check for the K bit (key field) */
if (K)
{
u_int32_t key;
if (get_packet_bytes((u_int8_t *) &key, pkt, 4) == 0)
return;
key = ntohl(key);
if (!my_args->m)
display("Key", (u_int8_t *) &key, 4, DISP_DEC);
}
/* Check for the sequence number field */
if (S)
{
u_int32_t seqno;
if (get_packet_bytes((u_int8_t *) &seqno, pkt, 4) == 0)
return;
seqno = ntohl(seqno);
if (!my_args->m)
display("Sequence number", (u_int8_t *) &seqno, 4,
DISP_DEC);
}
/* Check for routing field */
if (R)
{
u_int32_t routing;
if (get_packet_bytes((u_int8_t *) &routing, pkt, 4) == 0)
return;
routing = ntohs(routing);
if (!my_args->m)
display("Routing", (u_int8_t *) &routing, 4, DISP_DEC);
}
}
/* flush the hexbuffer */
hexbuffer_flush();
/*
* Display the rest of the packet
*/
et = ethertype2func(protocol);
if (et)
{
et(pkt);
}
}
break;
case 1: /* PPTP version */
{
u_int8_t C, R, K, S, s, recur, A, flags, version;
/*
* Get the header
*/
if (get_packet_bytes((u_int8_t *) &gre1, pkt, sizeof(gre_v1_t)) == 0)
return;
/*
* Conversions
*/
C = gre1.C_bit;
R = gre1.R_bit;
K = gre1.K_bit;
S = gre1.S_bit;
s = gre1.s_bit;
recur = gre1.recur;
A = gre1.A_bit;
flags = gre1.flags;
version = gre1.version;
gre1.protocol = ntohs(gre1.protocol);
gre1.payload_len = ntohs(gre1.payload_len);
gre1.call_id = ntohs(gre1.call_id);
/*
* Dump header
*/
if (my_args->m)
{
display_minimal_string("| GREv1 ");
}
else
{
/* announcement */
display_header_banner("GREv1 Header");
/* fields */
display("Checksum (C) bit", &C, 1, DISP_BINNLZ);
display("Routing (R) bit", &R, 1, DISP_BINNLZ);
display("Key present (K) bit", &K, 1, DISP_BINNLZ);
display("Seq. # present (S) bit", &S, 1, DISP_BINNLZ);
display("Strict src rte (s) bit", &s, 1, DISP_BINNLZ);
display("Recursion control", &recur, 1, DISP_DEC);
display("Ack. # present (A) bit", &A, 1, DISP_BINNLZ);
display("Flags", &flags, 1, DISP_DEC);
display("Version", &version, 1, DISP_DEC);
display_strmap_hex("Protocol", gre1.protocol, ethertype_map);
display("Payload length", (u_int8_t *) &gre1.payload_len, 2,
DISP_DEC);
display("Call ID", (u_int8_t *) &gre1.call_id, 2, DISP_DEC);
}
/*
* Check for sequence and acknowledgement number fields
*/
if (S)
{
u_int32_t seqno;
if (get_packet_bytes((u_int8_t *) &seqno, pkt, 4) == 0)
return;
seqno = ntohl(seqno);
if (my_args->m)
{
display_minimal_string("seq ");
display_minimal((u_int8_t *) &seqno, 4, DISP_DEC);
display_minimal_string(" ");
}
else
display("Sequence number", (u_int8_t *) &seqno, 4, DISP_DEC);
}
if (A)
{
u_int32_t ackno;
if (get_packet_bytes((u_int8_t *) &ackno, pkt, 4) == 0)
return;
ackno = ntohl(ackno);
if (my_args->m)
{
display_minimal_string("ack ");
display_minimal((u_int8_t *) &ackno, 4, DISP_DEC);
display_minimal_string(" ");
}
else
display("Acknowledgement number", (u_int8_t *) &ackno, 4,
DISP_DEC);
}
/* dump the hex buffer */
hexbuffer_flush();
/*
* Display the rest of the packet
*/
et = ethertype2func(gre1.protocol);
if (et)
{
et(pkt);
}
else
{
/*
* If we can't map the type, and we're in minimal mode,
* dump the protocol type
*/
if (my_args->m)
{
display_minimal_string("(");
display_minimal((u_int8_t *) &gre1.protocol, 2, DISP_HEX);
display_minimal_string(") ");
}
}
break;
default:
break;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1