6 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
8 * File format support for ipfix file format
9 * Copyright (c) 2010 by Hadriel Kaplan <hadrielk@yahoo.com>
10 * with generous copying from other wiretaps, such as pcapng
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.
27 /* File format reference:
29 * http://tools.ietf.org/rfc/rfc5655
30 * http://tools.ietf.org/rfc/rfc5101
32 * This wiretap is for an ipfix file format reader, per RFC 5655/5101.
33 * All "records" in the file are IPFIX messages, beginning with an IPFIX
34 * message header of 16 bytes as follows from RFC 5101:
36 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
37 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 | Version Number | Length |
39 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44 | Observation Domain ID |
45 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 Figure F: IPFIX Message Header Format
49 * which is then followed by one or more "Sets": Data Sets, Template Sets,
50 * and Options Template Sets. Each Set then has one or more Records in
53 * All IPFIX files are recorded in big-endian form (network byte order),
54 * per the RFCs. That means if we're on a little-endian system, all
55 * hell will break loose if we don't g_ntohX.
57 * Since wireshark already has an IPFIX dissector (implemented in
58 * packet-netflow.c), this reader will just set that dissector upon
59 * reading each message. Thus, an IPFIX Message is treated as a packet
60 * as far as the dissector is concerned.
69 #include "file_wrappers.h"
72 #include "pcap-common.h"
73 #include "pcap-encap.h"
77 #define ipfix_debug0(str) g_warning(str)
78 #define ipfix_debug1(str,p1) g_warning(str,p1)
79 #define ipfix_debug2(str,p1,p2) g_warning(str,p1,p2)
80 #define ipfix_debug3(str,p1,p2,p3) g_warning(str,p1,p2,p3)
82 #define ipfix_debug0(str)
83 #define ipfix_debug1(str,p1)
84 #define ipfix_debug2(str,p1,p2)
85 #define ipfix_debug3(str,p1,p2,p3)
88 #define RECORDS_FOR_IPFIX_CHECK 20
91 ipfix_read(wtap
*wth
, int *err
, gchar
**err_info
,
94 ipfix_seek_read(wtap
*wth
, gint64 seek_off
,
95 struct wtap_pkthdr
*phdr
, Buffer
*buf
, int length
,
96 int *err
, gchar
**err_info
);
98 ipfix_close(wtap
*wth
);
100 #define IPFIX_VERSION 10
102 /* ipfix: message header */
103 typedef struct ipfix_message_header_s
{
105 guint16 message_length
;
106 guint32 export_time_secs
;
107 guint32 sequence_number
;
108 guint32 observation_id
; /* might be 0 for none */
109 /* x bytes msg_body */
110 } ipfix_message_header_t
;
111 #define IPFIX_MSG_HDR_SIZE 16
113 /* ipfix: common Set header for every Set type */
114 typedef struct ipfix_set_header_s
{
117 /* x bytes set_body */
118 } ipfix_set_header_t
;
119 #define IPFIX_SET_HDR_SIZE 4
122 /* Read IPFIX message header from file. Return true on success. Set *err to
123 * 0 on EOF, any other value for "real" errors (EOF is ok, since return
124 * value is still FALSE)
127 ipfix_read_message_header(ipfix_message_header_t
*pfx_hdr
, FILE_T fh
, int *err
, gchar
**err_info
)
129 wtap_file_read_expected_bytes(pfx_hdr
, IPFIX_MSG_HDR_SIZE
, fh
, err
, err_info
); /* macro which does a return if read fails */
131 /* fix endianess, because IPFIX files are always big-endian */
132 pfx_hdr
->version
= g_ntohs(pfx_hdr
->version
);
133 pfx_hdr
->message_length
= g_ntohs(pfx_hdr
->message_length
);
134 pfx_hdr
->export_time_secs
= g_ntohl(pfx_hdr
->export_time_secs
);
135 pfx_hdr
->sequence_number
= g_ntohl(pfx_hdr
->sequence_number
);
136 pfx_hdr
->observation_id
= g_ntohl(pfx_hdr
->observation_id
);
138 /* is the version number one we expect? */
139 if (pfx_hdr
->version
!= IPFIX_VERSION
) {
140 /* Not an ipfix file. */
141 *err
= WTAP_ERR_BAD_FILE
;
142 *err_info
= g_strdup_printf("ipfix: wrong version %d", pfx_hdr
->version
);
146 if (pfx_hdr
->message_length
< 16) {
147 *err
= WTAP_ERR_BAD_FILE
;
148 *err_info
= g_strdup_printf("ipfix: message length %u is too short", pfx_hdr
->message_length
);
152 /* go back to before header */
153 if (file_seek(fh
, 0 - IPFIX_MSG_HDR_SIZE
, SEEK_CUR
, err
) == -1) {
154 ipfix_debug0("ipfix_read: couldn't go back in file before header");
162 /* Read IPFIX message header from file and fill in the struct wtap_pkthdr
163 * for the packet, and, if that succeeds, read the packet data.
164 * Return true on success. Set *err to 0 on EOF, any other value for "real"
165 * errors (EOF is ok, since return value is still FALSE).
168 ipfix_read_message(FILE_T fh
, struct wtap_pkthdr
*phdr
, Buffer
*buf
, int *err
, gchar
**err_info
)
170 ipfix_message_header_t msg_hdr
;
172 if (!ipfix_read_message_header(&msg_hdr
, fh
, err
, err_info
))
175 phdr
->presence_flags
= WTAP_HAS_TS
;
176 phdr
->len
= msg_hdr
.message_length
;
177 phdr
->caplen
= msg_hdr
.message_length
;
178 phdr
->ts
.secs
= msg_hdr
.export_time_secs
;
181 return wtap_read_packet_bytes(fh
, buf
, msg_hdr
.message_length
, err
, err_info
);
186 /* classic wtap: open capture file. Return 1 on success, 0 on normal failure
187 * like malformed format, -1 on bad error like file system
190 ipfix_open(wtap
*wth
, int *err
, gchar
**err_info
)
192 gint i
, n
, records_for_ipfix_check
= RECORDS_FOR_IPFIX_CHECK
;
194 guint16 checked_len
= 0;
195 ipfix_message_header_t msg_hdr
;
196 ipfix_set_header_t set_hdr
;
198 ipfix_debug0("ipfix_open: opening file");
200 /* number of records to scan before deciding if this really is IPFIX */
201 if ((s
= getenv("IPFIX_RECORDS_TO_CHECK")) != NULL
) {
202 if ((n
= atoi(s
)) > 0 && n
< 101) {
203 records_for_ipfix_check
= n
;
208 * IPFIX is a little hard because there's no magic number; we look at
209 * the first few records and see if they look enough like IPFIX
212 for (i
= 0; i
< records_for_ipfix_check
; i
++) {
213 /* read first message header to check version */
214 if (!ipfix_read_message_header(&msg_hdr
, wth
->fh
, err
, err_info
)) {
215 ipfix_debug3("ipfix_open: couldn't read message header #%d with err code #%d (%s)",
217 if (*err
== WTAP_ERR_BAD_FILE
) {
218 *err
= 0; /* not actually an error in this case */
223 if (*err
!= 0 && *err
!= WTAP_ERR_SHORT_READ
)
224 return -1; /* real failure */
227 /* we haven't seen enough to prove this is a ipfix file */
231 * If we got here, it's EOF and we haven't yet seen anything
232 * that doesn't look like an IPFIX record - i.e. everything
233 * we've seen looks like an IPFIX record - so we assume this
238 if (file_seek(wth
->fh
, IPFIX_MSG_HDR_SIZE
, SEEK_CUR
, err
) == -1) {
239 ipfix_debug1("ipfix_open: failed seek to next message in file, %d bytes away",
240 msg_hdr
.message_length
);
243 checked_len
= IPFIX_MSG_HDR_SIZE
;
245 /* check each Set in IPFIX Message for sanity */
246 while (checked_len
< msg_hdr
.message_length
) {
247 wtap_file_read_expected_bytes(&set_hdr
, IPFIX_SET_HDR_SIZE
, wth
->fh
, err
, err_info
);
248 set_hdr
.set_length
= g_ntohs(set_hdr
.set_length
);
249 if ((set_hdr
.set_length
< IPFIX_SET_HDR_SIZE
) ||
250 ((set_hdr
.set_length
+ checked_len
) > msg_hdr
.message_length
)) {
251 ipfix_debug1("ipfix_open: found invalid set_length of %d",
256 if (file_seek(wth
->fh
, set_hdr
.set_length
- IPFIX_SET_HDR_SIZE
,
257 SEEK_CUR
, err
) == -1)
259 ipfix_debug1("ipfix_open: failed seek to next set in file, %d bytes away",
260 set_hdr
.set_length
- IPFIX_SET_HDR_SIZE
);
263 checked_len
+= set_hdr
.set_length
;
267 /* all's good, this is a IPFIX file */
268 wth
->file_encap
= WTAP_ENCAP_RAW_IPFIX
;
269 wth
->snapshot_length
= 0;
270 wth
->tsprecision
= WTAP_FILE_TSPREC_SEC
;
271 wth
->subtype_read
= ipfix_read
;
272 wth
->subtype_seek_read
= ipfix_seek_read
;
273 wth
->subtype_close
= ipfix_close
;
274 wth
->file_type_subtype
= WTAP_FILE_TYPE_SUBTYPE_IPFIX
;
276 /* go back to beginning of file */
277 if (file_seek (wth
->fh
, 0, SEEK_SET
, err
) != 0)
285 /* classic wtap: read packet */
287 ipfix_read(wtap
*wth
, int *err
, gchar
**err_info
, gint64
*data_offset
)
289 *data_offset
= file_tell(wth
->fh
);
290 ipfix_debug1("ipfix_read: data_offset is initially %" G_GINT64_MODIFIER
"d", *data_offset
);
292 if (!ipfix_read_message(wth
->fh
, &wth
->phdr
, wth
->frame_buffer
, err
, err_info
)) {
293 ipfix_debug2("ipfix_read: couldn't read message header with code: %d\n, and error '%s'",
302 /* classic wtap: seek to file position and read packet */
304 ipfix_seek_read(wtap
*wth
, gint64 seek_off
, struct wtap_pkthdr
*phdr
,
305 Buffer
*buf
, int length _U_
, int *err
, gchar
**err_info
)
307 /* seek to the right file position */
308 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1) {
309 ipfix_debug2("ipfix_seek_read: couldn't read message header with code: %d\n, and error '%s'",
311 return FALSE
; /* Seek error */
314 ipfix_debug1("ipfix_seek_read: reading at offset %" G_GINT64_MODIFIER
"u", seek_off
);
316 if (!ipfix_read_message(wth
->random_fh
, phdr
, buf
, err
, err_info
)) {
317 ipfix_debug0("ipfix_seek_read: couldn't read message header");
319 *err
= WTAP_ERR_SHORT_READ
;
326 /* classic wtap: close capture file */
328 ipfix_close(wtap
*wth _U_
)
330 ipfix_debug0("ipfix_close: closing file");