4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
10 #include "ascendtext.h"
13 #include "ascend-int.h"
14 #include "file_wrappers.h"
22 /* Last updated: Feb 03 2005: Josh Bailey (joshbailey@lucent.com).
24 This module reads the text hex dump output of various TAOS
25 (Avaya/Alcatel/Lucent/Ascend Max, Max TNT, APX, etc) debug commands, including:
27 * pridisplay traces primary rate ISDN
28 * ether-display traces Ethernet packets (dangerous! CPU intensive)
29 * wanopening, wandisplay, wannext, wandsess
30 traces PPP or other WAN connections
32 Please see ascend_parser.lemon for examples.
34 Detailed documentation on TAOS products was at http://support.lucent.com;
35 that no longer works, and appears not to be available on the Wayback
38 Some online manuals and other information include:
40 MAX Administration Guide:
41 https://downloads.avaya.com/elmodocs2/definity/def_r10_new/max/0678_002.pdf
43 Other MAX documentation:
44 https://support.avaya.com/products/P1192/max
45 https://web.archive.org/web/20201127014004/https://support.avaya.com/products/P1192/max#Tab4
47 Ascend Router Information:
48 http://maxrouter.rde.net/
49 https://web.archive.org/web/20200807215418/http://maxrouter.rde.net/
53 typedef struct _ascend_magic_string
{
57 } ascend_magic_string
;
59 /* these magic strings signify the headers of a supported debug commands */
60 #define ASCEND_MAGIC_ENTRY(type, string) \
61 { type, string, sizeof string - 1 } /* strlen of a constant string */
62 static const ascend_magic_string ascend_magic
[] = {
63 ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_X
, "PRI-XMIT-"),
64 ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_R
, "PRI-RCV-"),
65 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X
, "XMIT-"),
66 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R
, "RECV-"),
67 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X
, "XMIT:"),
68 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R
, "RECV:"),
69 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X
, "PPP-OUT"),
70 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R
, "PPP-IN"),
71 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDD
, "WD_DIALOUT_DISP:"),
72 ASCEND_MAGIC_ENTRY(ASCEND_PFX_ETHER
, "ETHER"),
75 #define ASCEND_MAGIC_STRINGS G_N_ELEMENTS(ascend_magic)
77 #define ASCEND_DATE "Date:"
79 static bool ascend_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
80 int *err
, char **err_info
, int64_t *data_offset
);
81 static bool ascend_seek_read(wtap
*wth
, int64_t seek_off
,
82 wtap_rec
*rec
, Buffer
*buf
,
83 int *err
, char **err_info
);
85 static int ascend_file_type_subtype
= -1;
87 void register_ascend(void);
89 /* Seeks to the beginning of the next packet, and returns the
90 byte offset at which the header for that packet begins.
91 Returns -1 on failure. */
92 static int64_t ascend_find_next_packet(wtap
*wth
, int *err
, char **err_info
)
95 int64_t date_off
= -1, cur_off
, packet_off
;
96 size_t string_level
[ASCEND_MAGIC_STRINGS
];
97 unsigned string_i
= 0;
98 static const char ascend_date
[] = ASCEND_DATE
;
99 size_t ascend_date_len
= sizeof ascend_date
- 1; /* strlen of a constant string */
100 size_t ascend_date_string_level
;
101 unsigned excessive_read_count
= 262144;
103 memset(&string_level
, 0, sizeof(string_level
));
104 ascend_date_string_level
= 0;
106 while (((byte
= file_getc(wth
->fh
)) != EOF
)) {
107 excessive_read_count
--;
109 if (!excessive_read_count
) {
115 * See whether this is the string_level[string_i]th character of
116 * Ascend magic string string_i.
118 for (string_i
= 0; string_i
< ASCEND_MAGIC_STRINGS
; string_i
++) {
119 const char *strptr
= ascend_magic
[string_i
].strptr
;
120 size_t len
= ascend_magic
[string_i
].strlength
;
122 if (byte
== *(strptr
+ string_level
[string_i
])) {
124 * Yes, it is, so we need to check for the next character of
127 string_level
[string_i
]++;
130 * Have we matched the entire string?
132 if (string_level
[string_i
] >= len
) {
136 cur_off
= file_tell(wth
->fh
);
139 *err
= file_error(wth
->fh
, err_info
);
143 /* We matched some other type of header. */
144 if (date_off
== -1) {
145 /* We haven't yet seen a date header, so this packet
147 Back up over the header we just read; that's where a read
148 of this packet should start. */
149 packet_off
= cur_off
- len
;
151 /* This packet has a date/time header; a read of it should
152 start at the beginning of *that* header. */
153 packet_off
= date_off
;
160 * Not a match for this string, so reset the match process.
162 string_level
[string_i
] = 0;
167 * See whether this is the date_string_level'th character of
170 if (byte
== *(ascend_date
+ ascend_date_string_level
)) {
172 * Yes, it is, so we need to check for the next character of
175 ascend_date_string_level
++;
178 * Have we matched the entire string?
180 if (ascend_date_string_level
>= ascend_date_len
) {
181 /* We matched a Date: header. It's a special case;
182 remember the offset, but keep looking for other
185 Reset the amount of Date: header that we've matched,
186 so that we start the process of matching a Date:
187 header all over again.
189 XXX - what if we match multiple Date: headers before
190 matching some other header? */
191 cur_off
= file_tell(wth
->fh
);
194 *err
= file_error(wth
->fh
, err_info
);
198 date_off
= cur_off
- ascend_date_len
;
199 ascend_date_string_level
= 0;
203 * Not a match for the Date: string, so reset the match process.
205 ascend_date_string_level
= 0;
209 *err
= file_error(wth
->fh
, err_info
);
214 * Move to where the read for this packet should start, and return
217 if (file_seek(wth
->fh
, packet_off
, SEEK_SET
, err
) == -1)
223 wtap_open_return_val
ascend_open(wtap
*wth
, int *err
, char **err_info
)
226 uint8_t buf
[ASCEND_MAX_PKT_LEN
];
227 ascend_state_t parser_state
= {0};
232 /* We haven't yet allocated a data structure for our private stuff;
233 set the pointer to null, so that "ascend_find_next_packet()" knows
234 not to fill it in. */
237 offset
= ascend_find_next_packet(wth
, err
, err_info
);
239 if (*err
!= 0 && *err
!= WTAP_ERR_SHORT_READ
)
240 return WTAP_OPEN_ERROR
; /* read error */
241 return WTAP_OPEN_NOT_MINE
; /* EOF */
244 /* Do a trial parse of the first packet just found to see if we might
245 really have an Ascend file. If it fails with an actual error,
246 fail; those will be I/O errors. */
247 parser_state
.fh
= wth
->fh
;
248 parser_state
.pseudo_header
= &rec
.rec_header
.packet_header
.pseudo_header
.ascend
;
249 if (run_ascend_parser(buf
, &parser_state
, err
, err_info
) != 0 && *err
!= 0) {
251 return WTAP_OPEN_ERROR
;
254 /* Either the parse succeeded, or it failed but didn't get an I/O
257 If we got at least some data, return success even if the parser
258 reported an error. This is because the debug header gives the
259 number of bytes on the wire, not actually how many bytes are in
260 the trace. We won't know where the data ends until we run into
262 if (parser_state
.caplen
== 0) {
263 /* We read no data, so this presumably isn't an Ascend file. */
264 return WTAP_OPEN_NOT_MINE
;
267 wth
->file_type_subtype
= ascend_file_type_subtype
;
268 wth
->file_encap
= WTAP_ENCAP_ASCEND
;
270 wth
->snapshot_length
= ASCEND_MAX_PKT_LEN
;
271 wth
->subtype_read
= ascend_read
;
272 wth
->subtype_seek_read
= ascend_seek_read
;
273 ascend
= g_new(ascend_t
, 1);
274 wth
->priv
= (void *)ascend
;
276 /* The first packet we want to read is the one that
277 "ascend_find_next_packet()" just found; start searching
278 for it at the offset at which it found it. */
279 ascend
->next_packet_seek_start
= offset
;
281 /* MAXen and Pipelines report the time since reboot. In order to keep
282 from reporting packet times near the epoch, we subtract the first
283 packet's timestamp from the capture file's ctime, which gives us an
284 offset that we can apply to each packet.
286 if (wtap_fstat(wth
, &statbuf
, err
) == -1) {
287 return WTAP_OPEN_ERROR
;
289 ascend
->inittime
= statbuf
.st_ctime
;
290 ascend
->adjusted
= false;
291 wth
->file_tsprec
= WTAP_TSPREC_USEC
;
294 * Add an IDB; we don't know how many interfaces were
295 * involved, so we just say one interface, about which
296 * we only know the link-layer type, snapshot length,
297 * and time stamp resolution.
299 wtap_add_generated_idb(wth
);
301 return WTAP_OPEN_MINE
;
304 /* Parse the capture file.
305 Returns true if we got a packet, false otherwise. */
307 parse_ascend(ascend_t
*ascend
, FILE_T fh
, wtap_rec
*rec
, Buffer
*buf
,
308 unsigned length
, int64_t *next_packet_seek_start_ret
,
309 int *err
, char **err_info
)
311 ascend_state_t parser_state
= {0};
314 ws_buffer_assure_space(buf
, length
);
315 parser_state
.fh
= fh
;
316 parser_state
.pseudo_header
= &rec
->rec_header
.packet_header
.pseudo_header
.ascend
;
318 retval
= run_ascend_parser(ws_buffer_start_ptr(buf
), &parser_state
, err
, err_info
);
320 /* Did we see any data (hex bytes)? */
321 if (parser_state
.first_hexbyte
) {
322 /* Yes. Provide the offset of the first byte so that our caller can
323 tip off ascend_find_next_packet() as to where to look for the next
325 if (next_packet_seek_start_ret
!= NULL
)
326 *next_packet_seek_start_ret
= parser_state
.first_hexbyte
;
328 /* No. Maybe this record was broken; sometimes, a header will be
329 printed but the data will be omitted, or worse -- two headers will
330 be printed, followed by the data for each.
332 Because of this, we need to be fairly tolerant of what we accept
333 here. Provide our current offset so that our caller can tell
334 ascend_find_next_packet() to skip over what we've read so far so
335 we can try reading a new packet.
337 . That keeps us from getting into an infinite loop reading a broken
339 if (next_packet_seek_start_ret
!= NULL
)
340 *next_packet_seek_start_ret
= file_tell(fh
);
342 /* Don't treat that as a fatal error; pretend the parse succeeded. */
346 /* if we got at least some data, return success even if the parser
347 reported an error. This is because the debug header gives the number
348 of bytes on the wire, not actually how many bytes are in the trace.
349 We won't know where the data ends until we run into the next packet. */
350 if (parser_state
.caplen
) {
351 if (! ascend
->adjusted
) {
352 ascend
->adjusted
= true;
353 if (parser_state
.saw_timestamp
) {
355 * Capture file contained a date and time.
356 * We do this only if this is the very first packet we've seen -
357 * i.e., if "ascend->adjusted" is false - because
358 * if we get a date and time after the first packet, we can't
359 * go back and adjust the time stamps of the packets we've already
360 * processed, and basing the time stamps of this and following
361 * packets on the time stamp from the file text rather than the
362 * ctime of the capture file means times before this and after
363 * this can't be compared.
365 ascend
->inittime
= parser_state
.timestamp
;
367 if (ascend
->inittime
> parser_state
.secs
)
368 ascend
->inittime
-= parser_state
.secs
;
370 rec
->rec_type
= REC_TYPE_PACKET
;
371 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
372 rec
->presence_flags
= WTAP_HAS_TS
|WTAP_HAS_CAP_LEN
;
373 rec
->ts
.secs
= parser_state
.secs
+ ascend
->inittime
;
374 rec
->ts
.nsecs
= parser_state
.usecs
* 1000;
375 rec
->rec_header
.packet_header
.caplen
= parser_state
.caplen
;
376 rec
->rec_header
.packet_header
.len
= parser_state
.wirelen
;
381 /* Didn't see any data. Still, perhaps the parser was happy. */
384 /* Parser failed, but didn't report an I/O error, so a parse error.
385 Return WTAP_ERR_BAD_FILE, with the parse error as the error string. */
386 *err
= WTAP_ERR_BAD_FILE
;
387 *err_info
= g_strdup((parser_state
.ascend_parse_error
!= NULL
) ? parser_state
.ascend_parse_error
: "parse error");
391 /* Parser succeeded, but got no data, and didn't report an I/O error.
392 Return WTAP_ERR_BAD_FILE, with a "got no data" error string. */
393 *err
= WTAP_ERR_BAD_FILE
;
394 *err_info
= g_strdup("no data returned by parse");
400 /* Read the next packet; called from wtap_read(). */
401 static bool ascend_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
, int *err
,
402 char **err_info
, int64_t *data_offset
)
404 ascend_t
*ascend
= (ascend_t
*)wth
->priv
;
407 /* parse_ascend() will advance the point at which to look for the next
408 packet's header, to just after the last packet's header (ie. at the
409 start of the last packet's data). We have to get past the last
410 packet's header because we might mistake part of it for a new header. */
411 if (file_seek(wth
->fh
, ascend
->next_packet_seek_start
,
412 SEEK_SET
, err
) == -1)
415 offset
= ascend_find_next_packet(wth
, err
, err_info
);
417 /* EOF or read error */
420 if (!parse_ascend(ascend
, wth
->fh
, rec
, buf
, wth
->snapshot_length
,
421 &ascend
->next_packet_seek_start
, err
, err_info
))
424 /* Flex might have gotten an EOF and caused *err to be set to
425 WTAP_ERR_SHORT_READ. If so, that's not an error, as the parser
426 didn't return an error; set *err to 0, and get rid of any error
429 if (*err_info
!= NULL
) {
433 *data_offset
= offset
;
437 static bool ascend_seek_read(wtap
*wth
, int64_t seek_off
,
438 wtap_rec
*rec
, Buffer
*buf
,
439 int *err
, char **err_info
)
441 ascend_t
*ascend
= (ascend_t
*)wth
->priv
;
443 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
445 if (!parse_ascend(ascend
, wth
->random_fh
, rec
, buf
,
446 wth
->snapshot_length
, NULL
, err
, err_info
))
449 /* Flex might have gotten an EOF and caused *err to be set to
450 WTAP_ERR_SHORT_READ. If so, that's not an error, as the parser
451 didn't return an error; set *err to 0, and get rid of any error
454 if (*err_info
!= NULL
) {
461 static const struct supported_block_type ascend_blocks_supported
[] = {
463 * We support packet blocks, with no comments or other options.
465 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
468 static const struct file_type_subtype_info ascend_info
= {
469 "Lucent/Ascend access server trace", "ascend", "txt", NULL
,
470 false, BLOCKS_SUPPORTED(ascend_blocks_supported
),
474 void register_ascend(void)
476 ascend_file_type_subtype
= wtap_register_file_type_subtype(&ascend_info
);
479 * Register name for backwards compatibility with the
480 * wtap_filetypes table in Lua.
482 wtap_register_backwards_compatibility_lua_name("ASCEND",
483 ascend_file_type_subtype
);