5 * Juniper NetScreen snoop output parser
6 * Created by re-using a lot of code from cosine.c
7 * Copyright (c) 2007 by Sake Blok <sake@euronet.nl>
10 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #include "netscreen.h"
33 #include "file_wrappers.h"
42 * o Create a wiki-page with instruction on how to make tracefiles
43 * on Juniper NetScreen devices. Also put a few examples up
44 * on the wiki (Done: wiki-page added 2007-08-03)
46 * o Use the interface names to properly detect the encapsulation
47 * type (ie adsl packets are now not properly dissected)
48 * (Done: adsl packets are now correctly seen as PPP, 2007-08-03)
50 * o Pass the interface names and the traffic direction to either
51 * the frame-structure, a pseudo-header or use PPI. This needs
52 * to be discussed on the dev-list first
53 * (Posted a message to wireshark-dev abou this 2007-08-03)
59 static gboolean empty_line(const gchar *line);
60 static gboolean info_line(const gchar *line);
61 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
63 static gboolean netscreen_check_file_type(wtap *wth, int *err,
65 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
67 static gboolean netscreen_seek_read(wtap *wth, gint64 seek_off,
68 union wtap_pseudo_header *pseudo_header, guint8 *pd,
69 int len, int *err, gchar **err_info);
70 static int parse_netscreen_rec_hdr(wtap *wth, const char *line,
71 char *cap_int, gboolean *cap_dir, char *cap_dst,
72 union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
73 static int parse_netscreen_hex_dump(FILE_T fh, int pkt_len, guint8* buf,
74 int *err, gchar **err_info);
75 static int parse_single_hex_dump_line(char* rec, guint8 *buf,
78 /* Returns TRUE if the line appears to be an empty line. Otherwise it
80 static gboolean empty_line(const gchar *line)
83 if (isspace((guchar)*line)) {
96 /* Returns TRUE if the line appears to be a line with protocol info.
97 Otherwise it returns FALSE. */
98 static gboolean info_line(const gchar *line)
100 int i=NETSCREEN_SPACES_ON_INFO_LINE;
103 if (isspace((guchar)*line)) {
113 /* Seeks to the beginning of the next packet, and returns the
114 byte offset. Copy the header line to hdr. Returns -1 on failure,
115 and sets "*err" to the error, sets "*err_info" to null or an
116 additional error string, and sets hdr to NULL. */
117 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
121 char buf[NETSCREEN_LINE_LENGTH];
124 cur_off = file_tell(wth->fh);
127 *err = file_error(wth->fh, err_info);
131 if (file_gets(buf, sizeof(buf), wth->fh) != NULL) {
132 if (strstr(buf, NETSCREEN_REC_MAGIC_STR1) ||
133 strstr(buf, NETSCREEN_REC_MAGIC_STR2)) {
134 g_strlcpy(hdr, buf, NETSCREEN_LINE_LENGTH);
138 if (file_eof(wth->fh)) {
142 /* We got an error. */
143 *err = file_error(wth->fh, err_info);
152 /* Look through the first part of a file to see if this is
153 * NetScreen snoop output.
155 * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
156 * if we get an I/O error, "*err" will be set to a non-zero value and
157 * "*err_info" is set to null or an additional error string.
159 static gboolean netscreen_check_file_type(wtap *wth, int *err, gchar **err_info)
161 char buf[NETSCREEN_LINE_LENGTH];
164 buf[NETSCREEN_LINE_LENGTH-1] = '\0';
166 for (line = 0; line < NETSCREEN_HEADER_LINES_TO_CHECK; line++) {
167 if (file_gets(buf, NETSCREEN_LINE_LENGTH, wth->fh) != NULL) {
169 reclen = (guint) strlen(buf);
170 if (reclen < strlen(NETSCREEN_HDR_MAGIC_STR1) ||
171 reclen < strlen(NETSCREEN_HDR_MAGIC_STR2)) {
175 if (strstr(buf, NETSCREEN_HDR_MAGIC_STR1) ||
176 strstr(buf, NETSCREEN_HDR_MAGIC_STR2)) {
181 if (file_eof(wth->fh))
184 *err = file_error(wth->fh, err_info);
193 int netscreen_open(wtap *wth, int *err, gchar **err_info)
196 /* Look for a NetScreen snoop header line */
197 if (!netscreen_check_file_type(wth, err, err_info)) {
204 if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) /* rewind */
207 wth->data_offset = 0;
208 wth->file_encap = WTAP_ENCAP_UNKNOWN;
209 wth->file_type = WTAP_FILE_NETSCREEN;
210 wth->snapshot_length = 0; /* not known */
211 wth->subtype_read = netscreen_read;
212 wth->subtype_seek_read = netscreen_seek_read;
213 wth->tsprecision = WTAP_FILE_TSPREC_DSEC;
218 /* Find the next packet and parse it; called from wtap_read(). */
219 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
225 char line[NETSCREEN_LINE_LENGTH];
226 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
231 /* Find the next packet */
232 offset = netscreen_seek_next_packet(wth, err, err_info, line);
236 /* Parse the header */
237 pkt_len = parse_netscreen_rec_hdr(wth, line, cap_int, &cap_dir, cap_dst,
238 &wth->pseudo_header, err, err_info);
242 /* Make sure we have enough room for the packet */
243 buffer_assure_space(wth->frame_buffer, NETSCREEN_MAX_PACKET_LEN);
244 buf = buffer_start_ptr(wth->frame_buffer);
246 /* Convert the ASCII hex dump to binary data */
247 if ((caplen = parse_netscreen_hex_dump(wth->fh, pkt_len, buf, err,
253 * Determine the encapsulation type, based on the
254 * first 4 characters of the interface name
256 * XXX convert this to a 'case' structure when adding more
257 * (non-ethernet) interfacetypes
259 if (strncmp(cap_int, "adsl", 4) == 0) {
260 /* The ADSL interface can be bridged with or without
261 * PPP encapsulation. Check whether the first six bytes
262 * of the hex data are the same as the destination mac
263 * address in the header. If they are, assume ethernet
264 * LinkLayer or else PPP
266 g_snprintf(dststr, 13, "%02x%02x%02x%02x%02x%02x",
267 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
268 if (strncmp(dststr, cap_dst, 12) == 0)
269 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
271 wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
273 else if (strncmp(cap_int, "seri", 4) == 0)
274 wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
276 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
279 * If the per-file encapsulation isn't known, set it to this
280 * packet's encapsulation.
282 * If it *is* known, and it isn't this packet's encapsulation,
283 * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
284 * have a single encapsulation for all packets in the file.
286 if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
287 wth->file_encap = wth->phdr.pkt_encap;
289 if (wth->file_encap != wth->phdr.pkt_encap)
290 wth->file_encap = WTAP_ENCAP_PER_PACKET;
293 wth->data_offset = offset;
294 wth->phdr.caplen = caplen;
295 *data_offset = offset;
299 /* Used to read packets in random-access fashion */
301 netscreen_seek_read (wtap *wth, gint64 seek_off,
302 union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
303 int *err, gchar **err_info)
305 char line[NETSCREEN_LINE_LENGTH];
306 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
310 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
314 if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) {
315 *err = file_error(wth->random_fh, err_info);
317 *err = WTAP_ERR_SHORT_READ;
322 if (parse_netscreen_rec_hdr(NULL, line, cap_int, &cap_dir, cap_dst,
323 pseudo_header, err, err_info) == -1) {
327 if (parse_netscreen_hex_dump(wth->random_fh, len, pd, err, err_info)
334 /* Parses a packet record header. There are a few possible formats:
336 * XXX list extra formats here!
337 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
338 192.168.1.1 -> 192.168.1.10/6
339 vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
340 tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
341 00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00 .`.h.Y.....2..E.
342 00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8 .T.k..@.c.......
343 01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18 ..........[..(P.
344 1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09 ..y!...v.d......
345 31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61 1..(.X....=e...a
346 2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9 ,!...`..5.......
352 parse_netscreen_rec_hdr(wtap *wth, const char *line, char *cap_int,
353 gboolean *cap_dir, char *cap_dst, union wtap_pseudo_header *pseudo_header _U_,
354 int *err, gchar **err_info)
361 if (sscanf(line, "%9d.%9d: %15[a-z0-9/:.](%1[io]) len=%9d:%12s->%12s/",
362 &sec, &dsec, cap_int, direction, &pkt_len, cap_src, cap_dst) < 5) {
363 *err = WTAP_ERR_BAD_RECORD;
364 *err_info = g_strdup("netscreen: Can't parse packet-header");
368 *cap_dir = (direction[0] == 'o' ? NETSCREEN_EGRESS : NETSCREEN_INGRESS);
371 wth->phdr.ts.secs = sec;
372 wth->phdr.ts.nsecs = dsec * 100000000;
373 wth->phdr.len = pkt_len;
379 /* Converts ASCII hex dump to binary data. Returns the capture length.
380 If any error is encountered, -1 is returned. */
382 parse_netscreen_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err, gchar **err_info)
384 gchar line[NETSCREEN_LINE_LENGTH];
385 int n, i = 0, offset = 0;
389 /* The last packet is not delimited by an empty line, but by EOF
390 * So accept EOF as a valid delimiter too
392 if (file_gets(line, NETSCREEN_LINE_LENGTH, fh) == NULL) {
396 /* packets are delimited with empty lines */
397 if (empty_line(line)) {
401 /* terminate the line before the ascii-data to prevent the
402 * parser from parsing one or more extra bytes from the
404 * Check for longer lines to prevent wireless hexdumps to
405 * be cut in the middle (they can have 14 extra spaces
406 * before the hex-data)
408 if(strlen(line) != 98)
413 n = parse_single_hex_dump_line(line, buf, offset);
415 /* the smallest packet has a length of 6 bytes, if
416 * the first hex-data is less then check whether
417 * it is a info-line and act accordingly
419 if (offset == 0 && n < 6) {
420 if (info_line(line)) {
421 if (++i <= NETSCREEN_MAX_INFOLINES) {
425 *err = WTAP_ERR_BAD_RECORD;
426 *err_info = g_strdup("netscreen: cannot parse hex-data");
431 /* If there is no more data and the line was not empty,
432 * then there must be an error in the file
435 *err = WTAP_ERR_BAD_RECORD;
436 *err_info = g_strdup("netscreen: cannot parse hex-data");
440 /* Adjust the offset to the data that was just added to the buffer */
443 /* If there was more hex-data than was announced in the len=x
444 * header, then then there must be an error in the file
446 if(offset > pkt_len) {
447 *err = WTAP_ERR_BAD_RECORD;
448 *err_info = g_strdup("netscreen: to much hex-data");
456 /* Take a string representing one line from a hex dump and converts
457 * the text to binary data. We place the bytes in the buffer at the
460 * Returns number of bytes successfully read, -1 if bad. */
462 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
464 int num_items_scanned, i;
465 unsigned int bytes[16];
467 num_items_scanned = sscanf(rec, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
468 &bytes[0], &bytes[1], &bytes[2], &bytes[3],
469 &bytes[4], &bytes[5], &bytes[6], &bytes[7],
470 &bytes[8], &bytes[9], &bytes[10], &bytes[11],
471 &bytes[12], &bytes[13], &bytes[14], &bytes[15]);
472 if (num_items_scanned == 0)
475 if (num_items_scanned > 16)
476 num_items_scanned = 16;
478 for (i=0; i<num_items_scanned; i++) {
479 buf[byte_offset + i] = (guint8)bytes[i];
482 return num_items_scanned;