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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include "netscreen.h"
31 #include "file_wrappers.h"
40 * o Create a wiki-page with instruction on how to make tracefiles
41 * on Juniper NetScreen devices. Also put a few examples up
42 * on the wiki (Done: wiki-page added 2007-08-03)
44 * o Use the interface names to properly detect the encapsulation
45 * type (ie adsl packets are now not properly dissected)
46 * (Done: adsl packets are now correctly seen as PPP, 2007-08-03)
48 * o Pass the interface names and the traffic direction to either
49 * the frame-structure, a pseudo-header or use PPI. This needs
50 * to be discussed on the dev-list first
51 * (Posted a message to wireshark-dev abou this 2007-08-03)
57 static gboolean empty_line(const gchar *line);
58 static gboolean info_line(const gchar *line);
59 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
61 static gboolean netscreen_check_file_type(wtap *wth, int *err,
63 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
65 static gboolean netscreen_seek_read(wtap *wth, gint64 seek_off,
66 struct wtap_pkthdr *phdr, guint8 *pd,
67 int len, int *err, gchar **err_info);
68 static int parse_netscreen_rec_hdr(wtap *wth, const char *line,
69 char *cap_int, gboolean *cap_dir, char *cap_dst,
70 union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
71 static int parse_netscreen_hex_dump(FILE_T fh, int pkt_len, guint8* buf,
72 int *err, gchar **err_info);
73 static int parse_single_hex_dump_line(char* rec, guint8 *buf,
76 /* Returns TRUE if the line appears to be an empty line. Otherwise it
78 static gboolean empty_line(const gchar *line)
81 if (isspace((guchar)*line)) {
94 /* Returns TRUE if the line appears to be a line with protocol info.
95 Otherwise it returns FALSE. */
96 static gboolean info_line(const gchar *line)
98 int i=NETSCREEN_SPACES_ON_INFO_LINE;
101 if (isspace((guchar)*line)) {
111 /* Seeks to the beginning of the next packet, and returns the
112 byte offset. Copy the header line to hdr. Returns -1 on failure,
113 and sets "*err" to the error, sets "*err_info" to null or an
114 additional error string, and sets hdr to NULL. */
115 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
119 char buf[NETSCREEN_LINE_LENGTH];
122 cur_off = file_tell(wth->fh);
125 *err = file_error(wth->fh, err_info);
129 if (file_gets(buf, sizeof(buf), wth->fh) != NULL) {
130 if (strstr(buf, NETSCREEN_REC_MAGIC_STR1) ||
131 strstr(buf, NETSCREEN_REC_MAGIC_STR2)) {
132 g_strlcpy(hdr, buf, NETSCREEN_LINE_LENGTH);
136 if (file_eof(wth->fh)) {
140 /* We got an error. */
141 *err = file_error(wth->fh, err_info);
150 /* Look through the first part of a file to see if this is
151 * NetScreen snoop output.
153 * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
154 * if we get an I/O error, "*err" will be set to a non-zero value and
155 * "*err_info" is set to null or an additional error string.
157 static gboolean netscreen_check_file_type(wtap *wth, int *err, gchar **err_info)
159 char buf[NETSCREEN_LINE_LENGTH];
162 buf[NETSCREEN_LINE_LENGTH-1] = '\0';
164 for (line = 0; line < NETSCREEN_HEADER_LINES_TO_CHECK; line++) {
165 if (file_gets(buf, NETSCREEN_LINE_LENGTH, wth->fh) != NULL) {
167 reclen = (guint) strlen(buf);
168 if (reclen < strlen(NETSCREEN_HDR_MAGIC_STR1) ||
169 reclen < strlen(NETSCREEN_HDR_MAGIC_STR2)) {
173 if (strstr(buf, NETSCREEN_HDR_MAGIC_STR1) ||
174 strstr(buf, NETSCREEN_HDR_MAGIC_STR2)) {
179 if (file_eof(wth->fh))
182 *err = file_error(wth->fh, err_info);
191 int netscreen_open(wtap *wth, int *err, gchar **err_info)
194 /* Look for a NetScreen snoop header line */
195 if (!netscreen_check_file_type(wth, err, err_info)) {
202 if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) /* rewind */
205 wth->file_encap = WTAP_ENCAP_UNKNOWN;
206 wth->file_type = WTAP_FILE_NETSCREEN;
207 wth->snapshot_length = 0; /* not known */
208 wth->subtype_read = netscreen_read;
209 wth->subtype_seek_read = netscreen_seek_read;
210 wth->tsprecision = WTAP_FILE_TSPREC_DSEC;
215 /* Find the next packet and parse it; called from wtap_read(). */
216 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
222 char line[NETSCREEN_LINE_LENGTH];
223 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
228 /* Find the next packet */
229 offset = netscreen_seek_next_packet(wth, err, err_info, line);
233 wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
235 /* Parse the header */
236 pkt_len = parse_netscreen_rec_hdr(wth, line, cap_int, &cap_dir, cap_dst,
237 &wth->phdr.pseudo_header, err, err_info);
241 /* Make sure we have enough room for the packet */
242 buffer_assure_space(wth->frame_buffer, NETSCREEN_MAX_PACKET_LEN);
243 buf = buffer_start_ptr(wth->frame_buffer);
245 /* Convert the ASCII hex dump to binary data */
246 if ((caplen = parse_netscreen_hex_dump(wth->fh, pkt_len, buf, err,
252 * Determine the encapsulation type, based on the
253 * first 4 characters of the interface name
255 * XXX convert this to a 'case' structure when adding more
256 * (non-ethernet) interfacetypes
258 if (strncmp(cap_int, "adsl", 4) == 0) {
259 /* The ADSL interface can be bridged with or without
260 * PPP encapsulation. Check whether the first six bytes
261 * of the hex data are the same as the destination mac
262 * address in the header. If they are, assume ethernet
263 * LinkLayer or else PPP
265 g_snprintf(dststr, 13, "%02x%02x%02x%02x%02x%02x",
266 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
267 if (strncmp(dststr, cap_dst, 12) == 0)
268 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
270 wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
272 else if (strncmp(cap_int, "seri", 4) == 0)
273 wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
275 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
278 * If the per-file encapsulation isn't known, set it to this
279 * packet's encapsulation.
281 * If it *is* known, and it isn't this packet's encapsulation,
282 * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
283 * have a single encapsulation for all packets in the file.
285 if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
286 wth->file_encap = wth->phdr.pkt_encap;
288 if (wth->file_encap != wth->phdr.pkt_encap)
289 wth->file_encap = WTAP_ENCAP_PER_PACKET;
292 wth->phdr.caplen = caplen;
293 *data_offset = offset;
297 /* Used to read packets in random-access fashion */
299 netscreen_seek_read (wtap *wth, gint64 seek_off,
300 struct wtap_pkthdr *phdr, guint8 *pd, int len,
301 int *err, gchar **err_info)
303 union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
304 char line[NETSCREEN_LINE_LENGTH];
305 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
309 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
313 if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) {
314 *err = file_error(wth->random_fh, err_info);
316 *err = WTAP_ERR_SHORT_READ;
321 if (parse_netscreen_rec_hdr(NULL, line, cap_int, &cap_dir, cap_dst,
322 pseudo_header, err, err_info) == -1) {
326 if (parse_netscreen_hex_dump(wth->random_fh, len, pd, err, err_info)
333 /* Parses a packet record header. There are a few possible formats:
335 * XXX list extra formats here!
336 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
337 192.168.1.1 -> 192.168.1.10/6
338 vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
339 tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
340 00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00 .`.h.Y.....2..E.
341 00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8 .T.k..@.c.......
342 01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18 ..........[..(P.
343 1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09 ..y!...v.d......
344 31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61 1..(.X....=e...a
345 2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9 ,!...`..5.......
351 parse_netscreen_rec_hdr(wtap *wth, const char *line, char *cap_int,
352 gboolean *cap_dir, char *cap_dst, union wtap_pseudo_header *pseudo_header _U_,
353 int *err, gchar **err_info)
360 if (sscanf(line, "%9d.%9d: %15[a-z0-9/:.-](%1[io]) len=%9d:%12s->%12s/",
361 &sec, &dsec, cap_int, direction, &pkt_len, cap_src, cap_dst) < 5) {
362 *err = WTAP_ERR_BAD_FILE;
363 *err_info = g_strdup("netscreen: Can't parse packet-header");
367 *cap_dir = (direction[0] == 'o' ? NETSCREEN_EGRESS : NETSCREEN_INGRESS);
370 wth->phdr.ts.secs = sec;
371 wth->phdr.ts.nsecs = dsec * 100000000;
372 wth->phdr.len = pkt_len;
378 /* Converts ASCII hex dump to binary data. Returns the capture length.
379 If any error is encountered, -1 is returned. */
381 parse_netscreen_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err, gchar **err_info)
383 gchar line[NETSCREEN_LINE_LENGTH];
384 int n, i = 0, offset = 0;
388 /* The last packet is not delimited by an empty line, but by EOF
389 * So accept EOF as a valid delimiter too
391 if (file_gets(line, NETSCREEN_LINE_LENGTH, fh) == NULL) {
395 /* packets are delimited with empty lines */
396 if (empty_line(line)) {
400 /* terminate the line before the ascii-data to prevent the
401 * parser from parsing one or more extra bytes from the
403 * Check for longer lines to prevent wireless hexdumps to
404 * be cut in the middle (they can have 14 extra spaces
405 * before the hex-data)
407 if(strlen(line) != 98)
412 n = parse_single_hex_dump_line(line, buf, offset);
414 /* the smallest packet has a length of 6 bytes, if
415 * the first hex-data is less then check whether
416 * it is a info-line and act accordingly
418 if (offset == 0 && n < 6) {
419 if (info_line(line)) {
420 if (++i <= NETSCREEN_MAX_INFOLINES) {
424 *err = WTAP_ERR_BAD_FILE;
425 *err_info = g_strdup("netscreen: cannot parse hex-data");
430 /* If there is no more data and the line was not empty,
431 * then there must be an error in the file
434 *err = WTAP_ERR_BAD_FILE;
435 *err_info = g_strdup("netscreen: cannot parse hex-data");
439 /* Adjust the offset to the data that was just added to the buffer */
442 /* If there was more hex-data than was announced in the len=x
443 * header, then then there must be an error in the file
445 if(offset > pkt_len) {
446 *err = WTAP_ERR_BAD_FILE;
447 *err_info = g_strdup("netscreen: to much hex-data");
455 /* Take a string representing one line from a hex dump and converts
456 * the text to binary data. We place the bytes in the buffer at the
459 * Returns number of bytes successfully read, -1 if bad. */
461 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
463 int num_items_scanned, i;
464 unsigned int bytes[16];
466 num_items_scanned = sscanf(rec, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
467 &bytes[0], &bytes[1], &bytes[2], &bytes[3],
468 &bytes[4], &bytes[5], &bytes[6], &bytes[7],
469 &bytes[8], &bytes[9], &bytes[10], &bytes[11],
470 &bytes[12], &bytes[13], &bytes[14], &bytes[15]);
471 if (num_items_scanned == 0)
474 if (num_items_scanned > 16)
475 num_items_scanned = 16;
477 for (i=0; i<num_items_scanned; i++) {
478 buf[byte_offset + i] = (guint8)bytes[i];
481 return num_items_scanned;