3 * CoSine IPNOS L2 debug output parsing
4 * Copyright (c) 2002 by Motonori Shindo <motonori@shin.do>
7 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <wsutil/buffer.h>
28 #include "file_wrappers.h"
37 IPNOS: CONFIG VPN(100) VR(1.1.1.1)# diags
38 ipnos diags: Control (1/0) :: layer-2 ?
39 Registered commands for area "layer-2"
40 apply-pkt-log-profile Configure packet logging on an interface
41 create-pkt-log-profile Set packet-log-profile to be used for packet logging (see layer-2 pkt-log)
42 detail Get Layer 2 low-level details
44 ipnos diags: Control (1/0) :: layer-2 create ?
45 create-pkt-log-profile <pkt-log-profile-id ctl-tx-trace-length ctl-rx-trace-length data-tx-trace-length data-rx-trace-length pe-logging-or-control-blade>
47 ipnos diags: Control (1/0) :: layer-2 create 1 32 32 0 0 0
48 ipnos diags: Control (1/0) :: layer-2 create 2 32 32 100 100 0
49 ipnos diags: Control (1/0) :: layer-2 apply ?
50 apply-pkt-log-profile <slot port channel subif pkt-log-profile-id>
52 ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 1
53 Successfully applied packet-log-profile on LI
55 -- Note that only the control packets are logged because the data packet size parameters are 0 in profile 1
56 IPNOS: CONFIG VPN(200) VR(3.3.3.3)# ping 20.20.20.43
57 vpn 200 : [max tries 4, timeout 5 seconds, data length 64 bytes, ttl 255]
58 ping #1 ok, RTT 0.000 seconds
59 ping #2 ok, RTT 0.000 seconds
60 ping #3 ok, RTT 0.000 seconds
61 ping #4 ok, RTT 0.000 seconds
64 IPNOS: CONFIG VPN(200) VR(3.3.3.3)# 2000-2-1,18:19:46.8: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
67 2000-2-1,18:19:46.8: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
69 2000-2-1,18:19:46.8: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
71 2000-2-1,18:19:46.8: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x8030000]
73 ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 0
74 Successfully applied packet-log-profile on LI
75 ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 2
76 Successfully applied packet-log-profile on LI
78 -- Note that both control and data packets are logged because the data packet size parameter is 100 in profile 2
79 Please ignore the event-log messages getting mixed up with the ping command
80 ping 20.20.20.43 cou2000-2-1,18:20:17.0: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
82 00 D0 D8 D2 FF 03 C0 21 09 29 00 08 6B 60 84 AA
84 2000-2-1,18:20:17.0: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
85 00 D0 D8 D2 FF 03 C0 21 09 29 00 08 6D FE FA AA
87 2000-2-1,18:20:17.0: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
88 00 D0 D8 D2 FF 03 C0 21 0A 29 00 08 6B 60 84 AA
90 2000-2-1,18:20:17.0: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x8030000]
91 00 D0 D8 D2 FF 03 C0 21 0A 29 00 08 6D FE FA AA
94 vpn 200 : [max tries 1, timeout 5 seconds, data length 500 bytes, ttl 255]
95 2000-2-1,18:20:24.1: l2-tx (PPP:3/7/1:100), Length:536, Pro:1, Off:8, Pri:7, RM:0, Err:0 [0x4070, 0x801]
96 00 D0 D8 D2 FF 03 00 21 45 00 02 10 00 27 00 00
97 FF 01 69 51 14 14 14 22 14 14 14 2B 08 00 AD B8
98 00 03 00 01 10 11 12 13 14 15 16 17 18 19 1A 1B
99 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B
100 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B
101 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B
104 ping #1 ok, RTT 0.010 seconds
105 2000-2-1,18:20:24.1: l2-rx (PPP:3/7/1:100), Length:536, Pro:1, Off:8, Pri:7, RM:0, Err:0 [0x4071, 0x30801]
106 00 D0 D8 D2 FF 03 00 21 45 00 02 10 00 23 00 00
107 FF 01 69 55 14 14 14 2B 14 14 14 22 00 00 B5 B8
108 00 03 00 01 10 11 12 13 14 15 16 17 18 19 1A 1B
109 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B
110 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B
111 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B
116 IPNOS: CONFIG VPN(200) VR(3.3.3.3)# 2000-2-1,18:20:27.0: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
118 00 D0 D8 D2 FF 03 C0 21 09 2A 00 08 6B 60 84 AA
120 2000-2-1,18:20:27.0: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
121 00 D0 D8 D2 FF 03 C0 21 09 2A 00 08 6D FE FA AA
123 2000-2-1,18:20:27.0: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
124 00 D0 D8 D2 FF 03 C0 21 0A 2A 00 08 6B 60 84 AA
126 2000-2-1,18:20:27.0: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
127 00 D0 D8 D2 FF 03 C0 21 0A 2A 00 08 6D FE FA AA
130 ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 0
131 Successfully applied packet-log-profile on LI
132 ipnos diags: Control (1/0) ::
138 o Handle a case where an empty line doesn't exists as a delimiter of
139 each packet. If the output is sent to a control blade and
140 displayed as an event log, there's always an empty line between
141 each packet output, but it may not be true when it is an PE
144 o Some telnet client on Windows may put in a line break at 80
145 columns when it save the session to a text file ("CRT" is such an
146 example). I don't think it's a good idea for the telnet client to
147 do so, but CRT is widely used in Windows community, I should
148 take care of that in the future.
152 /* Magic text to check for CoSine L2 debug output */
153 #define COSINE_HDR_MAGIC_STR1 "l2-tx"
154 #define COSINE_HDR_MAGIC_STR2 "l2-rx"
156 /* Magic text for start of packet */
157 #define COSINE_REC_MAGIC_STR1 COSINE_HDR_MAGIC_STR1
158 #define COSINE_REC_MAGIC_STR2 COSINE_HDR_MAGIC_STR2
160 #define COSINE_HEADER_LINES_TO_CHECK 200
161 #define COSINE_LINE_LENGTH 240
163 #define COSINE_MAX_PACKET_LEN 65536
165 static gboolean empty_line(const gchar *line);
166 static gint64 cosine_seek_next_packet(wtap *wth, int *err, gchar **err_info,
168 static gboolean cosine_check_file_type(wtap *wth, int *err, gchar **err_info);
169 static gboolean cosine_read(wtap *wth, int *err, gchar **err_info,
170 gint64 *data_offset);
171 static gboolean cosine_seek_read(wtap *wth, gint64 seek_off,
172 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
173 static int parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
174 int *err, gchar **err_info);
175 static gboolean parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr,
176 int pkt_len, Buffer* buf, int *err, gchar **err_info);
177 static int parse_single_hex_dump_line(char* rec, guint8 *buf,
180 /* Returns TRUE if the line appears to be an empty line. Otherwise it
182 static gboolean empty_line(const gchar *line)
185 if (isspace((guchar)*line)) {
198 /* Seeks to the beginning of the next packet, and returns the
199 byte offset. Copy the header line to hdr. Returns -1 on failure,
200 and sets "*err" to the error and sets "*err_info" to null or an
201 additional error string. */
202 static gint64 cosine_seek_next_packet(wtap *wth, int *err, gchar **err_info,
206 char buf[COSINE_LINE_LENGTH];
209 cur_off = file_tell(wth->fh);
212 *err = file_error(wth->fh, err_info);
215 if (file_gets(buf, sizeof(buf), wth->fh) == NULL) {
216 *err = file_error(wth->fh, err_info);
219 if (strstr(buf, COSINE_REC_MAGIC_STR1) ||
220 strstr(buf, COSINE_REC_MAGIC_STR2)) {
221 g_strlcpy(hdr, buf, COSINE_LINE_LENGTH);
228 /* Look through the first part of a file to see if this is
229 * a CoSine L2 debug output.
231 * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
232 * if we get an I/O error, "*err" will be set to a non-zero value and
233 * "*err_info" will be set to null or an additional error string.
235 static gboolean cosine_check_file_type(wtap *wth, int *err, gchar **err_info)
237 char buf[COSINE_LINE_LENGTH];
241 buf[COSINE_LINE_LENGTH-1] = '\0';
243 for (line = 0; line < COSINE_HEADER_LINES_TO_CHECK; line++) {
244 if (file_gets(buf, COSINE_LINE_LENGTH, wth->fh) == NULL) {
246 *err = file_error(wth->fh, err_info);
250 reclen = strlen(buf);
251 if (reclen < strlen(COSINE_HDR_MAGIC_STR1) ||
252 reclen < strlen(COSINE_HDR_MAGIC_STR2)) {
256 if (strstr(buf, COSINE_HDR_MAGIC_STR1) ||
257 strstr(buf, COSINE_HDR_MAGIC_STR2)) {
266 int cosine_open(wtap *wth, int *err, gchar **err_info)
268 /* Look for CoSine header */
269 if (!cosine_check_file_type(wth, err, err_info)) {
270 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
275 if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) /* rewind */
278 wth->file_encap = WTAP_ENCAP_COSINE;
279 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_COSINE;
280 wth->snapshot_length = 0; /* not known */
281 wth->subtype_read = cosine_read;
282 wth->subtype_seek_read = cosine_seek_read;
283 wth->tsprecision = WTAP_FILE_TSPREC_CSEC;
288 /* Find the next packet and parse it; called from wtap_read(). */
289 static gboolean cosine_read(wtap *wth, int *err, gchar **err_info,
294 char line[COSINE_LINE_LENGTH];
296 /* Find the next packet */
297 offset = cosine_seek_next_packet(wth, err, err_info, line);
300 *data_offset = offset;
302 /* Parse the header */
303 pkt_len = parse_cosine_rec_hdr(&wth->phdr, line, err, err_info);
307 /* Convert the ASCII hex dump to binary data */
308 return parse_cosine_hex_dump(wth->fh, &wth->phdr, pkt_len,
309 wth->frame_buffer, err, err_info);
312 /* Used to read packets in random-access fashion */
314 cosine_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *phdr,
315 Buffer *buf, int *err, gchar **err_info)
318 char line[COSINE_LINE_LENGTH];
320 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
323 if (file_gets(line, COSINE_LINE_LENGTH, wth->random_fh) == NULL) {
324 *err = file_error(wth->random_fh, err_info);
326 *err = WTAP_ERR_SHORT_READ;
331 /* Parse the header */
332 pkt_len = parse_cosine_rec_hdr(phdr, line, err, err_info);
336 /* Convert the ASCII hex dump to binary data */
337 return parse_cosine_hex_dump(wth->random_fh, phdr, pkt_len, buf, err,
341 /* Parses a packet record header. There are two possible formats:
342 1) output to a control blade with date and time
343 2002-5-10,20:1:31.4: l2-tx (FR:3/7/1:1), Length:18, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
344 2) output to PE without date and time
345 l2-tx (FR:3/7/1:1), Length:18, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0] */
347 parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
348 int *err, gchar **err_info)
350 union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
351 int num_items_scanned;
352 int yy, mm, dd, hr, min, sec, csec, pkt_len;
353 int pro, off, pri, rm, error;
355 char if_name[COSINE_MAX_IF_NAME_LEN] = "", direction[6] = "";
358 if (sscanf(line, "%4d-%2d-%2d,%2d:%2d:%2d.%9d:",
359 &yy, &mm, &dd, &hr, &min, &sec, &csec) == 7) {
360 /* appears to be output to a control blade */
361 num_items_scanned = sscanf(line,
362 "%4d-%2d-%2d,%2d:%2d:%2d.%9d: %5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
363 &yy, &mm, &dd, &hr, &min, &sec, &csec,
364 direction, if_name, &pkt_len,
365 &pro, &off, &pri, &rm, &error,
368 if (num_items_scanned != 17) {
369 *err = WTAP_ERR_BAD_FILE;
370 *err_info = g_strdup("cosine: purported control blade line doesn't have code values");
374 /* appears to be output to PE */
375 num_items_scanned = sscanf(line,
376 "%5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
377 direction, if_name, &pkt_len,
378 &pro, &off, &pri, &rm, &error,
381 if (num_items_scanned != 10) {
382 *err = WTAP_ERR_BAD_FILE;
383 *err_info = g_strdup("cosine: header line is neither control blade nor PE output");
386 yy = mm = dd = hr = min = sec = csec = 0;
389 phdr->rec_type = REC_TYPE_PACKET;
390 phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
391 tm.tm_year = yy - 1900;
398 phdr->ts.secs = mktime(&tm);
399 phdr->ts.nsecs = csec * 10000000;
402 /* XXX need to handle other encapsulations like Cisco HDLC,
403 Frame Relay and ATM */
404 if (strncmp(if_name, "TEST:", 5) == 0) {
405 pseudo_header->cosine.encap = COSINE_ENCAP_TEST;
406 } else if (strncmp(if_name, "PPoATM:", 7) == 0) {
407 pseudo_header->cosine.encap = COSINE_ENCAP_PPoATM;
408 } else if (strncmp(if_name, "PPoFR:", 6) == 0) {
409 pseudo_header->cosine.encap = COSINE_ENCAP_PPoFR;
410 } else if (strncmp(if_name, "ATM:", 4) == 0) {
411 pseudo_header->cosine.encap = COSINE_ENCAP_ATM;
412 } else if (strncmp(if_name, "FR:", 3) == 0) {
413 pseudo_header->cosine.encap = COSINE_ENCAP_FR;
414 } else if (strncmp(if_name, "HDLC:", 5) == 0) {
415 pseudo_header->cosine.encap = COSINE_ENCAP_HDLC;
416 } else if (strncmp(if_name, "PPP:", 4) == 0) {
417 pseudo_header->cosine.encap = COSINE_ENCAP_PPP;
418 } else if (strncmp(if_name, "ETH:", 4) == 0) {
419 pseudo_header->cosine.encap = COSINE_ENCAP_ETH;
421 pseudo_header->cosine.encap = COSINE_ENCAP_UNKNOWN;
423 if (strncmp(direction, "l2-tx", 5) == 0) {
424 pseudo_header->cosine.direction = COSINE_DIR_TX;
425 } else if (strncmp(direction, "l2-rx", 5) == 0) {
426 pseudo_header->cosine.direction = COSINE_DIR_RX;
428 g_strlcpy(pseudo_header->cosine.if_name, if_name,
429 COSINE_MAX_IF_NAME_LEN);
430 pseudo_header->cosine.pro = pro;
431 pseudo_header->cosine.off = off;
432 pseudo_header->cosine.pri = pri;
433 pseudo_header->cosine.rm = rm;
434 pseudo_header->cosine.err = error;
439 /* Converts ASCII hex dump to binary data. Returns TRUE on success,
440 FALSE if any error is encountered. */
442 parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr, int pkt_len,
443 Buffer* buf, int *err, gchar **err_info)
446 gchar line[COSINE_LINE_LENGTH];
447 int i, hex_lines, n, caplen = 0;
449 /* Make sure we have enough room for the packet */
450 ws_buffer_assure_space(buf, COSINE_MAX_PACKET_LEN);
451 pd = ws_buffer_start_ptr(buf);
453 /* Calculate the number of hex dump lines, each
454 * containing 16 bytes of data */
455 hex_lines = pkt_len / 16 + ((pkt_len % 16) ? 1 : 0);
457 for (i = 0; i < hex_lines; i++) {
458 if (file_gets(line, COSINE_LINE_LENGTH, fh) == NULL) {
459 *err = file_error(fh, err_info);
461 *err = WTAP_ERR_SHORT_READ;
465 if (empty_line(line)) {
468 if ((n = parse_single_hex_dump_line(line, pd, i*16)) == -1) {
469 *err = WTAP_ERR_BAD_FILE;
470 *err_info = g_strdup("cosine: hex dump line doesn't have 16 numbers");
475 phdr->caplen = caplen;
480 /* Take a string representing one line from a hex dump and converts
481 * the text to binary data. We place the bytes in the buffer at the
484 * Returns number of bytes successfully read, -1 if bad. */
486 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
488 int num_items_scanned, i;
489 unsigned int bytes[16];
491 num_items_scanned = sscanf(rec, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
492 &bytes[0], &bytes[1], &bytes[2], &bytes[3],
493 &bytes[4], &bytes[5], &bytes[6], &bytes[7],
494 &bytes[8], &bytes[9], &bytes[10], &bytes[11],
495 &bytes[12], &bytes[13], &bytes[14], &bytes[15]);
496 if (num_items_scanned == 0)
499 if (num_items_scanned > 16)
500 num_items_scanned = 16;
502 for (i=0; i<num_items_scanned; i++) {
503 buf[byte_offset + i] = (guint8)bytes[i];
506 return num_items_scanned;