3 * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
5 * SPDX-License-Identifier: GPL-2.0-or-later
11 #include "file_wrappers.h"
19 Daniel Thompson (STMicroelectronics) <daniel.thompson@st.com>
23 +------+------+------+------+
24 | t3 | t2 | t1 | t0 | t = time_t
25 +------+------+------+------+
28 | 0x06 | Time step (short)
30 | ts | ts = time step (tenths of seconds)
34 | 0x05 | Time step (long)
35 +------+------+------+------+
36 | ts3 | ts2 | ts1 | ts0 | ts = time step (tenths of seconds)
37 +------+------+------+------+
40 | 0x04 | Receive deliminator (not seen in practice)
44 | 0x03 | Send deliminator (not seen in practice)
48 | 0x02 | Received data
50 | n1 | n0 | n = number of bytes following
58 | n1 | n0 | n = number of bytes following
64 #define PPPD_SENT_DATA 0x01
65 #define PPPD_RECV_DATA 0x02
66 #define PPPD_SEND_DELIM 0x03
67 #define PPPD_RECV_DELIM 0x04
68 #define PPPD_TIME_STEP_LONG 0x05
69 #define PPPD_TIME_STEP_SHORT 0x06
70 #define PPPD_RESET_TIME 0x07
72 /* this buffer must be at least (2*PPPD_MTU) + sizeof(ppp_header) +
73 * sizeof(lcp_header) + sizeof(ipcp_header). PPPD_MTU is *very* rarely
74 * larger than 1500 so this value is fine.
76 * It's less than WTAP_MAX_PACKET_SIZE_STANDARD, so we don't have to worry about
79 #define PPPD_BUF_SIZE 8192
86 static gboolean pppdump_read(wtap *wth, int *err, gchar **err_info,
88 static gboolean pppdump_seek_read(wtap *wth, gint64 seek_off,
89 wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
92 * Information saved about a packet, during the initial sequential pass
93 * through the file, to allow us to later re-read it when randomly
96 * "offset" is the offset in the file of the first data chunk containing data
97 * from that packet; note that it may also contain data from previous
100 * "num_bytes_to_skip" is the number of bytes from previous packets in that
103 * "dir" is the direction of the packet.
107 gint64 num_bytes_to_skip;
112 * Information about a packet currently being processed. There is one of
113 * these for the sent packet being processed and one of these for the
114 * received packet being processed, as we could be in the middle of
115 * processing both a received packet and a sent packet.
117 * "dir" is the direction of the packet.
119 * "cnt" is the number of bytes of packet data we've accumulated.
121 * "esc" is TRUE if the next byte we see is escaped (and thus must be XORed
122 * with 0x20 before saving it), FALSE otherwise.
124 * "buf" is a buffer containing the packet data we've accumulated.
126 * "id_offset" is the offset in the file of the first data chunk
127 * containing data from the packet we're processing.
129 * "sd_offset" is the offset in the file of the first data byte from
130 * the packet we're processing - which isn't necessarily right after
131 * the header of the first data chunk, as we may already have assembled
132 * packets from that chunk.
134 * "cd_offset" is the offset in the file of the current data chunk we're
141 guint8 buf[PPPD_BUF_SIZE];
148 * This keeps state used while processing records.
150 * "timestamp" is the seconds portion of the current time stamp value,
151 * as updated from PPPD_RESET_TIME, PPPD_TIME_STEP_LONG, and
152 * PPPD_TIME_STEP_SHORT records. "tenths" is the tenths-of-seconds
155 * "spkt" and "rpkt" are "pkt_t" structures for the sent and received
156 * packets we're currently working on.
158 * "offset" is the current offset in the file.
160 * "num_bytes" and "pkt" are information saved when we finish accumulating
161 * the data for a packet, if the data chunk we're working on still has more
164 * "num_bytes" is the number of bytes of additional data remaining
165 * in the chunk after we've finished accumulating the data for the
168 * "pkt" is the "pkt_t" for the type of packet the data chunk is for
169 * (sent or received packet).
171 * "seek_state" is another state structure used while processing records
172 * when doing a seek-and-read. (That structure doesn't itself have a
173 * "seek_state" structure.)
175 * "pids" is a GPtrArray of pointers to "pkt_id" structures for all the
176 * packets we've seen during the initial sequential pass, to allow us to
177 * later retrieve them with random accesses.
179 * "pkt_cnt" is the number of packets we've seen up to this point in the
182 typedef struct _pppdump_t {
190 struct _pppdump_t *seek_state;
196 process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd,
197 int *err, gchar **err_info, pkt_id *pid);
200 collate(pppdump_t*, FILE_T fh, int *err, gchar **err_info, guint8 *pd,
201 int *num_bytes, direction_enum *direction, pkt_id *pid,
202 gint64 num_bytes_to_skip);
205 pppdump_close(wtap *wth);
208 init_state(pppdump_t *state)
211 state->num_bytes = 0;
214 state->spkt.dir = DIRECTION_SENT;
216 state->spkt.esc = FALSE;
217 state->spkt.id_offset = 0;
218 state->spkt.sd_offset = 0;
219 state->spkt.cd_offset = 0;
221 state->rpkt.dir = DIRECTION_RECV;
223 state->rpkt.esc = FALSE;
224 state->rpkt.id_offset = 0;
225 state->rpkt.sd_offset = 0;
226 state->rpkt.cd_offset = 0;
228 state->seek_state = NULL;
229 state->offset = 0x100000; /* to detect errors during development */
234 pppdump_open(wtap *wth, int *err, gchar **err_info)
236 guint8 buffer[6]; /* Looking for: 0x07 t3 t2 t1 t0 ID */
239 /* There is no file header, only packet records. Fortunately for us,
240 * timestamp records are separated from packet records, so we should
241 * find an "initial time stamp" (i.e., a "reset time" record, or
242 * record type 0x07) at the beginning of the file. We'll check for
243 * that, plus a valid record following the 0x07 and the four bytes
244 * representing the timestamp.
247 if (!wtap_read_bytes(wth->fh, buffer, sizeof(buffer),
249 if (*err != WTAP_ERR_SHORT_READ)
250 return WTAP_OPEN_ERROR;
251 return WTAP_OPEN_NOT_MINE;
254 if (buffer[0] == PPPD_RESET_TIME &&
255 (buffer[5] == PPPD_SENT_DATA ||
256 buffer[5] == PPPD_RECV_DATA ||
257 buffer[5] == PPPD_TIME_STEP_LONG ||
258 buffer[5] == PPPD_TIME_STEP_SHORT ||
259 buffer[5] == PPPD_RESET_TIME)) {
264 return WTAP_OPEN_NOT_MINE;
269 if (file_seek(wth->fh, 5, SEEK_SET, err) == -1)
270 return WTAP_OPEN_ERROR;
272 state = (pppdump_t *)g_malloc(sizeof(pppdump_t));
273 wth->priv = (void *)state;
274 state->timestamp = pntoh32(&buffer[1]);
280 wth->file_encap = WTAP_ENCAP_PPP_WITH_PHDR;
281 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_PPPDUMP;
283 wth->snapshot_length = PPPD_BUF_SIZE; /* just guessing */
284 wth->subtype_read = pppdump_read;
285 wth->subtype_seek_read = pppdump_seek_read;
286 wth->subtype_close = pppdump_close;
287 wth->file_tsprec = WTAP_TSPREC_DSEC;
289 state->seek_state = g_new(pppdump_t,1);
291 /* If we have a random stream open, we're going to be reading
292 the file randomly; set up a GPtrArray of pointers to
293 information about how to retrieve the data for each packet. */
294 if (wth->random_fh != NULL)
295 state->pids = g_ptr_array_new();
300 return WTAP_OPEN_MINE;
303 /* Set part of the struct wtap_pkthdr. */
305 pppdump_set_phdr(wtap_rec *rec, int num_bytes,
306 direction_enum direction)
308 rec->rec_type = REC_TYPE_PACKET;
309 rec->rec_header.packet_header.len = num_bytes;
310 rec->rec_header.packet_header.caplen = num_bytes;
311 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP_WITH_PHDR;
313 rec->rec_header.packet_header.pseudo_header.p2p.sent = (direction == DIRECTION_SENT ? TRUE : FALSE);
316 /* Find the next packet and parse it; called from wtap_read(). */
318 pppdump_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
321 direction_enum direction;
326 state = (pppdump_t *)wth->priv;
328 /* If we have a random stream open, allocate a structure to hold
329 the information needed to read this packet's data again. */
330 if (wth->random_fh != NULL) {
331 pid = g_new(pkt_id, 1);
333 *err = errno; /* assume a malloc failed and set "errno" */
338 pid = NULL; /* sequential only */
340 ws_buffer_assure_space(wth->rec_data, PPPD_BUF_SIZE);
341 buf = ws_buffer_start_ptr(wth->rec_data);
343 if (!collate(state, wth->fh, err, err_info, buf, &num_bytes, &direction,
350 pid->dir = direction;
353 g_ptr_array_add(state->pids, pid);
354 /* The user's data_offset is not really an offset, but a packet number. */
355 *data_offset = state->pkt_cnt;
358 pppdump_set_phdr(&wth->rec, num_bytes, direction);
359 wth->rec.presence_flags = WTAP_HAS_TS;
360 wth->rec.ts.secs = state->timestamp;
361 wth->rec.ts.nsecs = state->tenths * 100000000;
366 /* Returns number of bytes copied for record, -1 if failure.
368 * This is modeled after pppdump.c, the utility to parse pppd log files; it
369 * comes with the ppp distribution.
372 process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd,
373 int *err, gchar **err_info, pkt_id *pid)
379 for (; num_bytes > 0; --num_bytes) {
382 *err = file_error(fh, err_info);
384 *err = WTAP_ERR_SHORT_READ;
392 * Flag Sequence for RFC 1662 HDLC-like
395 * As this is a raw trace of octets going
396 * over the wire, and that might include
397 * the login sequence, there is no
398 * guarantee that *only* PPP traffic
399 * appears in this file, so there is no
400 * guarantee that the first 0x7e we see is
401 * a start flag sequence, and therefore we
402 * cannot safely ignore all characters up
403 * to the first 0x7e, and therefore we
404 * might end up with some bogus PPP
409 * We've seen stuff before this,
410 * so this is the end of a frame.
411 * Make a frame out of that stuff.
415 num_written = pkt->cnt;
417 if (num_written <= 0) {
421 if (num_written > PPPD_BUF_SIZE) {
422 *err = WTAP_ERR_BAD_FILE;
423 *err_info = g_strdup_printf("pppdump: File has %u-byte packet, bigger than maximum of %u",
424 num_written, PPPD_BUF_SIZE);
428 memcpy(pd, pkt->buf, num_written);
431 * Remember the offset of the
432 * first record containing data
433 * for this packet, and how far
434 * into that record to skip to
435 * get to the beginning of the
436 * data for this packet; the number
437 * of bytes to skip into that record
438 * is the file offset of the first
439 * byte of this packet minus the
440 * file offset of the first byte of
441 * this record, minus 3 bytes for the
442 * header of this record (which, if
443 * we re-read this record, we will
444 * process, not skip).
447 pid->offset = pkt->id_offset;
448 pid->num_bytes_to_skip =
449 pkt->sd_offset - pkt->id_offset - 3;
450 g_assert(pid->num_bytes_to_skip >= 0);
456 * There's more data in this
458 * Set the initial data offset
459 * for the next packet.
461 pkt->id_offset = pkt->cd_offset;
462 pkt->sd_offset = state->offset;
465 * There is no more data in
467 * Thus, we don't have the
468 * initial data offset for
474 state->num_bytes = num_bytes;
482 * Control Escape octet for octet-stuffed
483 * RFC 1662 HDLC-like framing.
487 * Control Escape not preceded by
488 * Control Escape; discard it
489 * but XOR the next octet with
496 * Control Escape preceded by Control Escape;
497 * treat it as an ordinary character,
498 * by falling through.
505 * This character was preceded by
506 * Control Escape, so XOR it with
507 * 0x20, as per RFC 1662's octet-
508 * stuffed framing, and clear
509 * the flag saying that the
510 * character should be escaped.
516 if (pkt->cnt >= PPPD_BUF_SIZE) {
517 *err = WTAP_ERR_BAD_FILE;
518 *err_info = g_strdup_printf("pppdump: File has %u-byte packet, bigger than maximum of %u",
519 pkt->cnt - 1, PPPD_BUF_SIZE);
522 pkt->buf[pkt->cnt++] = c;
527 /* we could have run out of bytes to read */
531 /* Returns TRUE if packet data copied, FALSE if error occurred or EOF (no more records). */
533 collate(pppdump_t* state, FILE_T fh, int *err, gchar **err_info, guint8 *pd,
534 int *num_bytes, direction_enum *direction, pkt_id *pid,
535 gint64 num_bytes_to_skip)
540 int n, num_written = 0;
546 * Process any data left over in the current record when doing
547 * sequential processing.
549 if (state->num_bytes > 0) {
550 g_assert(num_bytes_to_skip == 0);
552 num_written = process_data(state, fh, pkt, state->num_bytes,
553 pd, err, err_info, pid);
555 if (num_written < 0) {
558 else if (num_written > 0) {
559 *num_bytes = num_written;
560 *direction = pkt->dir;
563 /* if 0 bytes written, keep processing */
566 * We didn't have any data left over, so the packet will
567 * start at the beginning of a record.
570 pid->num_bytes_to_skip = 0;
574 * That didn't get all the data for this packet, so process
575 * subsequent records.
577 start_offset = state->offset;
578 while ((id = file_getc(fh)) != EOF) {
583 pkt = id == PPPD_SENT_DATA ? &state->spkt : &state->rpkt;
586 * Save the offset of the beginning of
587 * the current record.
589 pkt->cd_offset = state->offset - 1;
592 * Get the length of the record.
594 byte0 = file_getc(fh);
598 byte1 = file_getc(fh);
602 n = (byte0 << 8) | byte1;
604 if (pkt->id_offset == 0) {
606 * We don't have the initial data
607 * offset for this packet, which
608 * means this is the first
609 * data record for that packet.
610 * Save the offset of the
611 * beginning of that record and
612 * the offset of the first data
613 * byte in the packet, which is
614 * the first data byte in the
617 pkt->id_offset = pkt->cd_offset;
618 pkt->sd_offset = state->offset;
624 g_assert(num_bytes_to_skip < n);
625 while (num_bytes_to_skip) {
626 if (file_getc(fh) == EOF)
632 num_written = process_data(state, fh, pkt, n,
633 pd, err, err_info, pid);
635 if (num_written < 0) {
638 else if (num_written > 0) {
639 *num_bytes = num_written;
640 *direction = pkt->dir;
643 /* if 0 bytes written, keep looping */
646 case PPPD_SEND_DELIM:
647 case PPPD_RECV_DELIM:
648 /* What can we do? */
651 case PPPD_RESET_TIME:
652 if (!wtap_read_bytes(fh, &time_long, sizeof(guint32), err, err_info))
654 state->offset += sizeof(guint32);
655 state->timestamp = pntoh32(&time_long);
659 case PPPD_TIME_STEP_LONG:
660 if (!wtap_read_bytes(fh, &time_long, sizeof(guint32), err, err_info))
662 state->offset += sizeof(guint32);
663 state->tenths += pntoh32(&time_long);
665 if (state->tenths >= 10) {
666 state->timestamp += state->tenths / 10;
667 state->tenths = state->tenths % 10;
672 case PPPD_TIME_STEP_SHORT:
673 if (!wtap_read_bytes(fh, &time_short, sizeof(guint8), err, err_info))
675 state->offset += sizeof(guint8);
676 state->tenths += time_short;
678 if (state->tenths >= 10) {
679 state->timestamp += state->tenths / 10;
680 state->tenths = state->tenths % 10;
687 *err = WTAP_ERR_BAD_FILE;
688 *err_info = g_strdup_printf("pppdump: bad ID byte 0x%02x", id);
695 *err = file_error(fh, err_info);
697 if (state->offset != start_offset) {
699 * We read at least one byte, so we were working
700 * on a record; an EOF means that record was
703 *err = WTAP_ERR_SHORT_READ;
711 /* Used to read packets in random-access fashion */
713 pppdump_seek_read(wtap *wth,
722 direction_enum direction;
725 gint64 num_bytes_to_skip;
727 state = (pppdump_t *)wth->priv;
729 pid = (pkt_id *)g_ptr_array_index(state->pids, seek_off);
731 *err = WTAP_ERR_BAD_FILE; /* XXX - better error? */
732 *err_info = g_strdup("pppdump: PID not found for record");
736 if (file_seek(wth->random_fh, pid->offset, SEEK_SET, err) == -1)
739 init_state(state->seek_state);
740 state->seek_state->offset = pid->offset;
742 ws_buffer_assure_space(buf, PPPD_BUF_SIZE);
743 pd = ws_buffer_start_ptr(buf);
746 * We'll start reading at the first record containing data from
747 * this packet; however, that doesn't mean "collate()" will
748 * stop only when we've read that packet, as there might be
749 * data for packets going in the other direction as well, and
750 * we might finish processing one of those packets before we
751 * finish processing the packet we're reading.
753 * Therefore, we keep reading until we get a packet that's
754 * going in the direction we want.
756 num_bytes_to_skip = pid->num_bytes_to_skip;
758 if (!collate(state->seek_state, wth->random_fh, err, err_info,
759 pd, &num_bytes, &direction, NULL, num_bytes_to_skip))
761 num_bytes_to_skip = 0;
762 } while (direction != pid->dir);
764 pppdump_set_phdr(rec, num_bytes, pid->dir);
770 pppdump_close(wtap *wth)
774 state = (pppdump_t *)wth->priv;
776 if (state->seek_state) { /* should always be TRUE */
777 g_free(state->seek_state);
782 for (i = 0; i < g_ptr_array_len(state->pids); i++) {
783 g_free(g_ptr_array_index(state->pids, i));
785 g_ptr_array_free(state->pids, TRUE);
790 * Editor modelines - http://www.wireshark.org/tools/modelines.html
795 * indent-tabs-mode: t
798 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
799 * :indentSize=8:tabSize=8:noTabs=false: