Add support for reading pppd log files in wiretap.

svn path=/trunk/; revision=2448
This commit is contained in:
Gilbert Ramirez 2000-09-19 17:22:11 +00:00
parent 706bd5e1fc
commit e191760a7b
6 changed files with 735 additions and 5 deletions

View File

@ -1,7 +1,7 @@
# Makefile.am
# Automake file for Wiretap
#
# $Id: Makefile.am,v 1.30 2000/08/08 22:16:41 mhall Exp $
# $Id: Makefile.am,v 1.31 2000/09/19 17:22:09 gram Exp $
#
# Ethereal - Network traffic analyzer
# By Gerald Combs <gerald@zing.org>
@ -62,6 +62,8 @@ libwiretap_a_SOURCES = \
netxray.h \
ngsniffer.c \
ngsniffer.h \
pppdump.c \
pppdump.h \
radcom.c \
radcom.h \
snoop.c \

View File

@ -1,6 +1,6 @@
/* file.c
*
* $Id: file.c,v 1.61 2000/09/15 07:52:41 guy Exp $
* $Id: file.c,v 1.62 2000/09/19 17:22:09 gram Exp $
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
@ -58,6 +58,7 @@
#include "toshiba.h"
#include "i4btrace.h"
#include "csids.h"
#include "pppdump.h"
/* The open_file_* routines should return:
*
@ -94,6 +95,7 @@ static int (*open_routines[])(wtap *, int *) = {
netxray_open,
radcom_open,
nettl_open,
pppdump_open,
/* Files whose magic headers are in text *somewhere* in the
* file (usually because the trace is just a saved copy of
@ -339,6 +341,10 @@ const static struct file_type_info {
{ "CSIDS IPLog", NULL,
NULL, NULL },
/* WTAP_FILE_PPPDUMP */
{ "pppd log (pppdump format)", NULL,
NULL, NULL },
};
/* Name that should be somewhat descriptive. */

664
wiretap/pppdump.c Normal file
View File

@ -0,0 +1,664 @@
/* pppdump.c
*
* $Id: pppdump.c,v 1.1 2000/09/19 17:22:10 gram Exp $
*
* Copyright (c) 2000 by Gilbert Ramirez <gram@xiexie.org>
*
* 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
#include "wtap-int.h"
#include "buffer.h"
#include "pppdump.h"
#include "file_wrappers.h"
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
/*#define DEBUG 1 */
#ifdef DEBUG
#define dbg_print(...) g_print(##args)
#else
#define dbg_print(...) ;
#endif
/*
pppdump records
Daniel Thompson (STMicroelectronics) <daniel.thompson@st.com>
+------+
| 0x07 +------+------+------+ Reset time
| t3 | t2 | t1 | t0 | t = time_t
+------+------+------+------+
+------+
| 0x06 | Time step (short)
| ts | ts = time step (tenths)
+------+
+------+
| 0x05 +------+------+------+ Time step (long)
| ts3 | ts2 | ts1 | ts0 | ts = time step (tenths)
+------+------+------+------+
+------+
| 0x04 | Receive deliminator (not seen in practice)
+------+
+------+
| 0x03 | Send deliminator (not seen in practice)
+------+
+------+
| 0x02 +------+ Received data
| n1 | n0 | n = number of bytes following
| data |
| |
+------+
| 0x01 +------+ Sent data
| n1 | n0 | n = number of bytes following
| data |
| |
*/
#define PPPD_SENT_DATA 0x01
#define PPPD_RECV_DATA 0x02
#define PPPD_SEND_DELIM 0x03
#define PPPD_RECV_DELIM 0x04
#define PPPD_TIME_STEP_LONG 0x05
#define PPPD_TIME_STEP_SHORT 0x06
#define PPPD_RESET_TIME 0x07
#define PPPD_NULL 0x00 /* For my own use */
typedef enum {
DIRECTION_SENT,
DIRECTION_RECV
} direction_enum;
static gboolean pppdump_read(wtap *wth, int *err, int *data_offset);
static int pppdump_seek_read(wtap *wth, int seek_off,
union wtap_pseudo_header *pseudo_header, guint8 *pd, int len);
typedef struct {
long offset;
int num_saved_states;
direction_enum dir;
} pkt_id;
typedef struct {
direction_enum dir;
int cnt;
gboolean esc;
guint8 buf[8192];
long id_offset;
} pkt_t;
/* Partial-record state */
typedef struct {
int num_bytes;
pkt_t *pkt;
} prec_state;
struct _pppdump_t;
typedef struct _pppdump_t {
time_t timestamp;
guint tenths;
pkt_t spkt;
pkt_t rpkt;
long offset;
GList *precs;
struct _pppdump_t *seek_state;
GPtrArray *pids;
guint pkt_cnt;
int num_saved_states;
} pppdump_t;
static int
process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd, int *err,
gboolean *state_saved);
static gboolean
collate(pppdump_t*, FILE_T fh, int *err, guint8 *pd, int *num_bytes,
direction_enum *direction, pkt_id *pid);
static void
pppdump_close(wtap *wth);
static void
init_state(pppdump_t *state)
{
dbg_print("INITIALIZING STATE 0x%08x\n", (unsigned int) state);
state->precs = NULL;
state->spkt.dir = DIRECTION_SENT;
state->spkt.cnt = 0;
state->spkt.esc = FALSE;
state->spkt.id_offset = 0;
state->rpkt.dir = DIRECTION_RECV;
state->rpkt.cnt = 0;
state->rpkt.esc = FALSE;
state->rpkt.id_offset = 0;
state->seek_state = NULL;
state->offset = 0x100000; /* to detect errors during development */
}
#ifdef DEBUG
static
void print_hex_data_text(const u_char *cp, unsigned int length)
{
register int ad, i, j, k;
u_char c;
u_char line[60];
static u_char binhex[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
memset (line, ' ', sizeof line);
line[sizeof (line)-1] = 0;
for (ad=i=j=k=0; i<length; i++) {
c = *cp++;
line[j++] = binhex[c>>4];
line[j++] = binhex[c&0xf];
if (i&1) j++;
line[42+k++] = c >= ' ' && c < 0x7f ? c : '.';
if ((i & 15) == 15) {
printf ("\n%4x %s", ad, line);
/*if (i==15) printf (" %d", length);*/
memset (line, ' ', sizeof line);
line[sizeof (line)-1] = j = k = 0;
ad += 16;
}
}
if (line[0] != ' ') printf ("\n%4x %s", ad, line);
printf("\n");
return;
}
#endif
int
pppdump_open(wtap *wth, int *err)
{
guint8 buffer[6]; /* Looking for: 0x07 t3 t2 t1 t0 ID */
pppdump_t *state;
/* There is no file header, only packet records. Fortunately for us,
* timestamp records are separated from packet records, so we should
* find an "initial time stamp" (i.e., a "reset time" record, or
* record type 0x07) at the beginning of the file. We'll check for
* that, plus a valid record following the 0x07 and the four bytes
* representing the timestamp.
*/
file_seek(wth->fh, 0, SEEK_SET);
wtap_file_read_unknown_bytes(buffer, sizeof(buffer), wth->fh, err);
if (buffer[0] == PPPD_RESET_TIME &&
(buffer[5] == PPPD_SENT_DATA ||
buffer[5] == PPPD_RECV_DATA ||
buffer[5] == PPPD_TIME_STEP_LONG ||
buffer[5] == PPPD_TIME_STEP_SHORT ||
buffer[5] == PPPD_RESET_TIME)) {
goto my_file_type;
}
else {
return 0;
}
my_file_type:
state = wth->capture.generic = g_malloc(sizeof(pppdump_t));
state->timestamp = pntohl(&buffer[1]);
state->tenths = 0;
dbg_print("pppdump time is %lu\n", state->start_time);
init_state(state);
state->offset = 5;
file_seek(wth->fh, 5, SEEK_SET);
wth->file_encap = WTAP_ENCAP_PPP;
wth->file_type = WTAP_FILE_PPPDUMP;
wth->snapshot_length = 8192; /* just guessing */
wth->subtype_read = pppdump_read;
wth->subtype_seek_read = pppdump_seek_read;
wth->subtype_close = pppdump_close;
state->seek_state = g_malloc(sizeof(pppdump_t));
state->pids = g_ptr_array_new();
state->pkt_cnt = 0;
state->num_saved_states = 0;
return 1;
}
/* Find the next packet and parse it; called from wtap_loop(). */
static gboolean
pppdump_read(wtap *wth, int *err, int *data_offset)
{
gboolean retval;
int num_bytes;
direction_enum direction;
guint8 *buf;
pppdump_t *state;
pkt_id *pid;
dbg_print("======================================================\n");
buffer_assure_space(wth->frame_buffer, 8192);
buf = buffer_start_ptr(wth->frame_buffer);
state = wth->capture.generic;
pid = g_new(pkt_id, 1);
if (!pid) {
return FALSE;
}
pid->offset = 0;
pid->num_saved_states = 0;
retval = collate(state, wth->fh, err, buf, &num_bytes, &direction, pid);
dbg_print("Record %u ended with pid offset = 0x%lx num_ss = %d\n",
state->pkt_cnt, pid->offset, pid->num_saved_states);
if (!retval) {
g_free(pid);
return FALSE;
}
pid->dir = direction;
g_ptr_array_add(state->pids, pid);
/* The user's data_offset is not really an offset, but a packet number. */
*data_offset = state->pkt_cnt;
state->pkt_cnt++;
wth->phdr.len = num_bytes;
wth->phdr.caplen = num_bytes;
wth->phdr.ts.tv_sec = state->timestamp;
wth->phdr.ts.tv_usec = state->tenths * 100000;
wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
return TRUE;
}
#define PKT(x) (x)->dir == DIRECTION_SENT ? "SENT" : "RECV"
static gboolean
save_prec_state(pppdump_t *state, int num_bytes, pkt_t *pkt)
{
prec_state *prec;
prec = g_new(prec_state, 1);
if (!prec) {
return FALSE;
}
prec->num_bytes = num_bytes;
prec->pkt = pkt;
dbg_print("saved state of num_bytes=%d pkt=0x%08x (%s) pkt->cnt=%d\n",
num_bytes, (unsigned int) pkt, PKT(pkt), pkt->cnt);
state->precs = g_list_append(state->precs, prec);
return TRUE;
}
static int
process_data_from_prec_state(pppdump_t *state, FILE_T fh, guint8* pd, int *err,
gboolean *state_saved, pkt_t **ppkt)
{
prec_state *prec;
prec = state->precs->data;
state->precs = g_list_remove(state->precs, prec);
dbg_print("retrieved state of num_bytes=%d ", prec->num_bytes);
*ppkt = prec->pkt;
dbg_print("pkt=0x%08x (%s) pkt->cnt = %d\n", (unsigned int) prec->pkt, PKT(prec->pkt), prec->pkt->cnt);
return process_data(state, fh, prec->pkt, prec->num_bytes, pd, err, state_saved);
}
/* Returns number of bytes copied for record, -1 if failure.
*
* This is modeled after pppdump.c, the utility to parse pppd log files; it comes with the ppp
* distribution.
*/
static int
process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd, int *err,
gboolean *state_saved)
{
int c;
int num_bytes = n;
int num_written;
*state_saved = FALSE;
for (; num_bytes > 0; --num_bytes) {
c = file_getc(fh);
dbg_print("PD At offset 0x%lx got %c (0x%02x)\n", state->offset, c, c);
state->offset++;
switch (c) {
case EOF:
dbg_print("Unexpected EOF\n");
if (*err == 0) {
*err = WTAP_ERR_SHORT_READ;
}
return -1;
break;
case '~':
if (pkt->cnt > 0) {
pkt->esc = FALSE;
num_written = pkt->cnt - 2;
pkt->cnt = 0;
if (num_written <= 0) {
return 0;
}
memcpy(pd, pkt->buf, num_written);
dbg_print("\n%s:\n", PKT(pkt));
#ifdef DEBUG
print_hex_data_text(pd, num_written);
#endif
num_bytes--;
if (num_bytes > 0) {
if (!save_prec_state(state, num_bytes, pkt)) {
return -1;
}
*state_saved = TRUE;
}
dbg_print("returning with num_bytes = %d\n", num_bytes);
dbg_print("returning with num_written = %d\n", num_written);
return num_written;
}
break;
case '}':
if (!pkt->esc) {
pkt->esc = TRUE;
break;
}
/* else fall through */
default:
if (pkt->esc) {
c ^= 0x20;
dbg_print("Changed 0x%02x\t%c\n", c, c);
pkt->esc = FALSE;
}
pkt->buf[pkt->cnt++] = c;
break;
}
}
dbg_print("PD returning 0; no out bytes. pkt=0x%08x (%s) pkt->cnt=%d\n",
(unsigned int) pkt, PKT(pkt), pkt->cnt);
/* we could have run out of bytes to read */
return 0;
}
/* Returns TRUE if packet data copied, FALSE if error occurred or EOF (no more records). */
static gboolean
collate(pppdump_t* state, FILE_T fh, int *err, guint8 *pd, int *num_bytes,
direction_enum *direction, pkt_id *pid)
{
int id;
pkt_t *pkt = NULL;
int n, num_written = 0;
gboolean ss = FALSE;
guint32 time_long;
guint8 time_short;
if (!state->precs) {
state->num_saved_states = 0;
}
if (pid) {
pid->num_saved_states = state->num_saved_states;
}
while (state->precs) {
dbg_print("I see a saved state.\n");
num_written = process_data_from_prec_state(state, fh, pd, err, &ss, &pkt);
state->num_saved_states++;
if (pid) {
pid->num_saved_states++;
}
if (num_written < 0) {
return FALSE;
}
else if (num_written > 0) {
*num_bytes = num_written;
*direction = pkt->dir;
if (pid) {
pid->offset = pkt->id_offset;
}
if (!ss) {
pkt->id_offset = 0;
}
dbg_print("Returning, state->offset = 0x%lx\n", state->offset);
return TRUE;
}
/* if 0 bytes written, keep processing */
}
dbg_print("No saved states.\n");
while ((id = file_getc(fh)) != EOF) {
dbg_print("CL At offset 0x%lx got %c (0x%02x)\n", state->offset, id, id);
state->offset++;
switch (id) {
case PPPD_SENT_DATA:
case PPPD_RECV_DATA:
pkt = id == PPPD_SENT_DATA ? &state->spkt : &state->rpkt;
if (pkt->id_offset == 0) {
pkt->id_offset = state->offset - 1;
}
n = file_getc(fh);
n = (n << 8) + file_getc(fh);
state->offset += 2;
dbg_print("ID: Going to read %d bytes for pkt=0x%08x (%s)\n", n,
(unsigned int) pkt, PKT(pkt));
num_written = process_data(state, fh, pkt, n, pd, err, &ss);
if (num_written < 0) {
return FALSE;
}
else if (num_written > 0) {
*num_bytes = num_written;
*direction = pkt->dir;
if (pid) {
pid->offset = pkt->id_offset;
}
if (!ss) {
pkt->id_offset = 0;
}
dbg_print("Returning, state->offset = 0x%lx\n", state->offset);
return TRUE;
}
/* if 0 bytes written, keep looping */
break;
case PPPD_SEND_DELIM:
case PPPD_RECV_DELIM:
/* What can we do? */
dbg_print("GOT *_DELIM\n");
break;
case PPPD_TIME_STEP_LONG:
dbg_print("GOT *_TIME 32\n");
wtap_file_read_unknown_bytes(&time_long, sizeof(guint32), fh, err);
state->offset += sizeof(guint32);
state->timestamp = time_long;
state->tenths = 0;
break;
case PPPD_RESET_TIME:
dbg_print("GOT *_TIME 32\n");
wtap_file_read_unknown_bytes(&time_long, sizeof(guint32), fh, err);
state->offset += sizeof(guint32);
state->tenths += time_long;
if (state->tenths >= 10) {
state->timestamp += state->tenths / 10;
state->tenths = state->tenths % 10;
}
break;
case PPPD_TIME_STEP_SHORT:
dbg_print("GOT *_TIME 8\n");
wtap_file_read_unknown_bytes(&time_short, sizeof(guint8), fh, err);
state->offset += sizeof(guint8);
state->tenths += time_short;
if (state->tenths >= 10) {
state->timestamp += state->tenths / 10;
state->tenths = state->tenths % 10;
}
break;
default:
dbg_print("BAD ID: 0x%02x\n", id);
/* XXX - bad file */
g_assert_not_reached();
}
}
return FALSE;
}
/* Used to read packets in random-access fashion */
static int
pppdump_seek_read (wtap *wth,
int seek_off,
union wtap_pseudo_header *pseudo_header,
guint8 *pd,
int len)
{
int err = 0;
int num_bytes;
direction_enum direction;
gboolean retval;
pppdump_t *state;
pkt_id *pid;
int i;
dbg_print(">>>>>>>>>>>> SEEKING to packet # %d\n", seek_off);
state = wth->capture.generic;
pid = g_ptr_array_index(state->pids, seek_off);
if (!pid) {
return -1;
}
dbg_print(">>>>>>>>>>>> SEEKING to offset %ld (0x%lx), num_ss=%d\n",
pid->offset, pid->offset, pid->num_saved_states);
file_seek(wth->random_fh, pid->offset, SEEK_SET);
init_state(state->seek_state);
for (i = 0 ; i <= pid->num_saved_states; i++) {
dbg_print("Loop=%d\n", i);
again:
retval = collate(state->seek_state, wth->random_fh, &err, pd, &num_bytes,
&direction, NULL);
if (!retval) {
return -1;
}
if (direction != pid->dir) {
dbg_print("Looping because wrong direction.\n");
goto again;
}
dbg_print("Got right direction.\n");
}
if (len != num_bytes) {
return -1;
}
dbg_print(">>>>>>>>>>>> COPIED %d bytes\n", num_bytes);
return 0;
}
static void
simple_g_free(gpointer data, gpointer junk)
{
if (data)
g_free(data);
}
static void
pppdump_close(wtap *wth)
{
pppdump_t *state;
state = wth->capture.generic;
if (state->precs) {
g_list_foreach(state->precs, simple_g_free, NULL);
g_list_free(state->precs);
}
if (state->seek_state) { /* should always be TRUE */
g_free(state->seek_state);
}
if (state->pids) { /* should always be TRUE */
g_ptr_array_free(state->pids, TRUE); /* free data, too */
}
g_free(state);
}

28
wiretap/pppdump.h Normal file
View File

@ -0,0 +1,28 @@
/* pppdump.h
*
* $Id: pppdump.h,v 1.1 2000/09/19 17:22:10 gram Exp $
*
* Copyright (c) 2000 by Gilbert Ramirez <gram@xiexie.org>
*
* 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.
*
*/
#ifndef __PPPDUMP_H__
#define __PPPDUMP_H__
int pppdump_open(wtap *wth, int *err);
#endif

View File

@ -1,6 +1,6 @@
/* wtap-int.h
*
* $Id: wtap-int.h,v 1.8 2000/09/07 05:34:21 gram Exp $
* $Id: wtap-int.h,v 1.9 2000/09/19 17:22:10 gram Exp $
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
@ -141,6 +141,7 @@ struct wtap {
netxray_t *netxray;
ascend_t *ascend;
csids_t *csids;
void *generic;
} capture;
subtype_read_func subtype_read;
@ -267,4 +268,32 @@ struct wtap_dumper {
(guint32)*((guint8 *)p+0)<<0)
#endif
#define wtap_file_read_unknown_bytes(target, num_bytes, fh, err) \
G_STMT_START \
{ \
int _bytes_read; \
_bytes_read = file_read((target), 1, (num_bytes), (fh)); \
if (_bytes_read != (num_bytes)) { \
*(err) = file_error((fh)); \
return FALSE; \
} \
} \
G_STMT_END
#define wtap_file_read_expected_bytes(target, num_bytes, fh, err) \
G_STMT_START \
{ \
int _bytes_read; \
_bytes_read = file_read((target), 1, (num_bytes), (fh)); \
if (_bytes_read != (num_bytes)) { \
*(err) = file_error((fh)); \
if (*(err) == 0 && _bytes_read > 0) { \
*(err) = WTAP_ERR_SHORT_READ; \
} \
return FALSE; \
} \
} \
G_STMT_END
#endif /* __WTAP_INT_H__ */

View File

@ -1,6 +1,6 @@
/* wtap.h
*
* $Id: wtap.h,v 1.79 2000/09/15 07:52:43 guy Exp $
* $Id: wtap.h,v 1.80 2000/09/19 17:22:11 gram Exp $
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
@ -124,9 +124,10 @@
#define WTAP_FILE_TOSHIBA 21
#define WTAP_FILE_I4BTRACE 22
#define WTAP_FILE_CSIDS 23
#define WTAP_FILE_PPPDUMP 24
/* last WTAP_FILE_ value + 1 */
#define WTAP_NUM_FILE_TYPES 24
#define WTAP_NUM_FILE_TYPES 25
/*
* Maximum packet size we'll support.