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 wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
238 /* Parse the header */
239 pkt_len = parse_netscreen_rec_hdr(wth, line, cap_int, &cap_dir, cap_dst,
240 &wth->pseudo_header, err, err_info);
244 /* Make sure we have enough room for the packet */
245 buffer_assure_space(wth->frame_buffer, NETSCREEN_MAX_PACKET_LEN);
246 buf = buffer_start_ptr(wth->frame_buffer);
248 /* Convert the ASCII hex dump to binary data */
249 if ((caplen = parse_netscreen_hex_dump(wth->fh, pkt_len, buf, err,
255 * Determine the encapsulation type, based on the
256 * first 4 characters of the interface name
258 * XXX convert this to a 'case' structure when adding more
259 * (non-ethernet) interfacetypes
261 if (strncmp(cap_int, "adsl", 4) == 0) {
262 /* The ADSL interface can be bridged with or without
263 * PPP encapsulation. Check whether the first six bytes
264 * of the hex data are the same as the destination mac
265 * address in the header. If they are, assume ethernet
266 * LinkLayer or else PPP
268 g_snprintf(dststr, 13, "%02x%02x%02x%02x%02x%02x",
269 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
270 if (strncmp(dststr, cap_dst, 12) == 0)
271 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
273 wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
275 else if (strncmp(cap_int, "seri", 4) == 0)
276 wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
278 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
281 * If the per-file encapsulation isn't known, set it to this
282 * packet's encapsulation.
284 * If it *is* known, and it isn't this packet's encapsulation,
285 * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
286 * have a single encapsulation for all packets in the file.
288 if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
289 wth->file_encap = wth->phdr.pkt_encap;
291 if (wth->file_encap != wth->phdr.pkt_encap)
292 wth->file_encap = WTAP_ENCAP_PER_PACKET;
295 wth->data_offset = offset;
296 wth->phdr.caplen = caplen;
297 *data_offset = offset;
301 /* Used to read packets in random-access fashion */
303 netscreen_seek_read (wtap *wth, gint64 seek_off,
304 union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
305 int *err, gchar **err_info)
307 char line[NETSCREEN_LINE_LENGTH];
308 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
312 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
316 if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) {
317 *err = file_error(wth->random_fh, err_info);
319 *err = WTAP_ERR_SHORT_READ;
324 if (parse_netscreen_rec_hdr(NULL, line, cap_int, &cap_dir, cap_dst,
325 pseudo_header, err, err_info) == -1) {
329 if (parse_netscreen_hex_dump(wth->random_fh, len, pd, err, err_info)
336 /* Parses a packet record header. There are a few possible formats:
338 * XXX list extra formats here!
339 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
340 192.168.1.1 -> 192.168.1.10/6
341 vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
342 tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
343 00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00 .`.h.Y.....2..E.
344 00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8 .T.k..@.c.......
345 01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18 ..........[..(P.
346 1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09 ..y!...v.d......
347 31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61 1..(.X....=e...a
348 2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9 ,!...`..5.......
354 parse_netscreen_rec_hdr(wtap *wth, const char *line, char *cap_int,
355 gboolean *cap_dir, char *cap_dst, union wtap_pseudo_header *pseudo_header _U_,
356 int *err, gchar **err_info)
363 if (sscanf(line, "%9d.%9d: %15[a-z0-9/:.](%1[io]) len=%9d:%12s->%12s/",
364 &sec, &dsec, cap_int, direction, &pkt_len, cap_src, cap_dst) < 5) {
365 *err = WTAP_ERR_BAD_FILE;
366 *err_info = g_strdup("netscreen: Can't parse packet-header");
370 *cap_dir = (direction[0] == 'o' ? NETSCREEN_EGRESS : NETSCREEN_INGRESS);
373 wth->phdr.ts.secs = sec;
374 wth->phdr.ts.nsecs = dsec * 100000000;
375 wth->phdr.len = pkt_len;
381 /* Converts ASCII hex dump to binary data. Returns the capture length.
382 If any error is encountered, -1 is returned. */
384 parse_netscreen_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err, gchar **err_info)
386 gchar line[NETSCREEN_LINE_LENGTH];
387 int n, i = 0, offset = 0;
391 /* The last packet is not delimited by an empty line, but by EOF
392 * So accept EOF as a valid delimiter too
394 if (file_gets(line, NETSCREEN_LINE_LENGTH, fh) == NULL) {
398 /* packets are delimited with empty lines */
399 if (empty_line(line)) {
403 /* terminate the line before the ascii-data to prevent the
404 * parser from parsing one or more extra bytes from the
406 * Check for longer lines to prevent wireless hexdumps to
407 * be cut in the middle (they can have 14 extra spaces
408 * before the hex-data)
410 if(strlen(line) != 98)
415 n = parse_single_hex_dump_line(line, buf, offset);
417 /* the smallest packet has a length of 6 bytes, if
418 * the first hex-data is less then check whether
419 * it is a info-line and act accordingly
421 if (offset == 0 && n < 6) {
422 if (info_line(line)) {
423 if (++i <= NETSCREEN_MAX_INFOLINES) {
427 *err = WTAP_ERR_BAD_FILE;
428 *err_info = g_strdup("netscreen: cannot parse hex-data");
433 /* If there is no more data and the line was not empty,
434 * then there must be an error in the file
437 *err = WTAP_ERR_BAD_FILE;
438 *err_info = g_strdup("netscreen: cannot parse hex-data");
442 /* Adjust the offset to the data that was just added to the buffer */
445 /* If there was more hex-data than was announced in the len=x
446 * header, then then there must be an error in the file
448 if(offset > pkt_len) {
449 *err = WTAP_ERR_BAD_FILE;
450 *err_info = g_strdup("netscreen: to much hex-data");
458 /* Take a string representing one line from a hex dump and converts
459 * the text to binary data. We place the bytes in the buffer at the
462 * Returns number of bytes successfully read, -1 if bad. */
464 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
466 int num_items_scanned, i;
467 unsigned int bytes[16];
469 num_items_scanned = sscanf(rec, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
470 &bytes[0], &bytes[1], &bytes[2], &bytes[3],
471 &bytes[4], &bytes[5], &bytes[6], &bytes[7],
472 &bytes[8], &bytes[9], &bytes[10], &bytes[11],
473 &bytes[12], &bytes[13], &bytes[14], &bytes[15]);
474 if (num_items_scanned == 0)
477 if (num_items_scanned > 16)
478 num_items_scanned = 16;
480 for (i=0; i<num_items_scanned; i++) {
481 buf[byte_offset + i] = (guint8)bytes[i];
484 return num_items_scanned;