4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
11 #define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
12 #include "lanalyzer.h"
17 #include "file_wrappers.h"
19 /* The LANalyzer format is documented (at least in part) in Novell document
20 TID022037, which can be found at, among other places:
22 http://www.blacksheepnetworks.com/security/info/nw/lan/trace.txt
25 /* Record header format */
28 uint8_t record_type
[2];
29 uint8_t record_length
[2];
32 #define LA_RecordHeaderSize 4
34 /* Record type codes: */
36 #define RT_HeaderRegular 0x1001
37 #define RT_HeaderCyclic 0x1007
38 #define RT_RxChannelName 0x1006
39 #define RT_TxChannelName 0x100b
40 #define RT_FilterName 0x1032
41 #define RT_RxTemplateName 0x1035
42 #define RT_TxTemplateName 0x1036
43 #define RT_DisplayOptions 0x100a
44 #define RT_Summary 0x1002
45 #define RT_SubfileSummary 0x1003
46 #define RT_CyclicInformation 0x1009
47 #define RT_Index 0x1004
48 #define RT_PacketData 0x1005
50 #define LA_ProFileLimit (1024 * 1024 * 32)
52 typedef uint8_t Eadr
[6];
53 typedef uint16_t TimeStamp
[3]; /* 0.5 microseconds since start of trace */
56 * These records have only 2-byte alignment for 4-byte quantities,
57 * so the structures aren't necessarily valid; they're kept as comments
58 * for reference purposes.
96 * int16_t board_version;
97 * int8_t reserved[18];
101 #define SummarySize (18+22+(4*36)+6+6+6+4+4)
108 * } LA_SummaryRecord;
111 #define LA_SummaryRecordSize (SummarySize + 4)
113 /* LANalyzer board types (which indicate the type of network on which
114 the capture was done). */
115 #define BOARD_325 226 /* LANalyzer 325 (Ethernet) */
116 #define BOARD_325TR 227 /* LANalyzer 325TR (Token-ring) */
125 * } LA_SubfileSummaryRecord;
128 #define LA_SubfileSummaryRecordSize 10
131 #define LA_IndexSize 500
137 * int16_t idxsp; = LA_IndexSize
141 * int32_t trcidx[LA_IndexSize + 2]; +2 undocumented but used by La 2.2
145 #define LA_IndexRecordSize (10 + 4 * (LA_IndexSize + 2))
150 * uint16_t rx_channels;
151 * uint16_t rx_errors;
152 * int16_t rx_frm_len;
153 * int16_t rx_frm_sln;
159 * int16_t rx_filters;
162 * int16_t hwcollschans;
167 #define LA_PacketRecordSize 32
177 static const uint8_t LA_HeaderRegularFake
[] = {
178 0x01,0x10,0x4c,0x00,0x01,0x05,0x54,0x72,0x61,0x63,0x65,0x20,0x44,0x69,0x73,0x70,
179 0x6c,0x61,0x79,0x20,0x54,0x72,0x61,0x63,0x65,0x20,0x46,0x69,0x6c,0x65,0x00,0x00,
180 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
181 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
182 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
185 static const uint8_t LA_RxChannelNameFake
[] = {
186 0x06,0x10,0x80,0x00,0x43,0x68,0x61,0x6e ,0x6e,0x65,0x6c,0x31,0x00,0x43,0x68,0x61,
187 0x6e,0x6e,0x65,0x6c,0x32,0x00,0x43,0x68 ,0x61,0x6e,0x6e,0x65,0x6c,0x33,0x00,0x43,
188 0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x00 ,0x43,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x35,
189 0x00,0x43,0x68,0x61,0x6e,0x6e,0x65,0x6c ,0x36,0x00,0x43,0x68,0x61,0x6e,0x6e,0x65,
190 0x6c,0x37,0x00,0x43,0x68,0x61,0x6e,0x6e ,0x65,0x6c,0x38,0x00,0x00,0x00,0x00,0x00,
191 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
192 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
193 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
197 static const uint8_t LA_TxChannelNameFake
[] = {
198 0x0b,0x10,0x36,0x00 ,0x54,0x72,0x61,0x6e,0x73,0x31,0x00,0x00,
199 0x00,0x54,0x72,0x61,0x6e,0x73,0x32,0x00 ,0x00,0x00,0x54,0x72,0x61,0x6e,0x73,0x33,
200 0x00,0x00,0x00,0x54,0x72,0x61,0x6e,0x73 ,0x34,0x00,0x00,0x00,0x54,0x72,0x61,0x6e,
201 0x73,0x35,0x00,0x00,0x00,0x54,0x72,0x61 ,0x6e,0x73,0x36,0x00,0x00,0x00
204 static const uint8_t LA_RxTemplateNameFake
[] = {
206 0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
207 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
208 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
209 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
210 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
211 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
212 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
213 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
214 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
218 static const uint8_t LA_TxTemplateNameFake
[] = {
219 0x36,0x10,0x36,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
220 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
221 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
222 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00
225 static const uint8_t LA_DisplayOptionsFake
[] = {
227 0x00,0x00,0x01,0x00,0x01,0x02,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
228 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
229 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
230 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
231 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
232 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
233 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
234 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
235 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
236 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
237 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
238 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
239 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
240 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
241 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
242 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
243 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00
246 static const uint8_t LA_CyclicInformationFake
[] = {
247 0x09,0x10,0x1a,0x00,0x00,0x00,
248 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
249 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
252 static const uint8_t z64
[64] = {
253 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
254 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
255 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
256 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
263 static bool lanalyzer_read(wtap
*wth
, wtap_rec
*rec
,
264 Buffer
*buf
, int *err
, char **err_info
, int64_t *data_offset
);
265 static bool lanalyzer_seek_read(wtap
*wth
, int64_t seek_off
,
266 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
);
267 static bool lanalyzer_dump_finish(wtap_dumper
*wdh
, int *err
,
270 static int lanalyzer_file_type_subtype
= -1;
272 void register_lanalyzer(void);
274 wtap_open_return_val
lanalyzer_open(wtap
*wth
, int *err
, char **err_info
)
276 LA_RecordHeader rec_header
;
277 char header_fixed
[2];
281 uint16_t board_type
, mxslc
;
282 uint16_t record_type
, record_length
;
283 uint8_t cr_day
, cr_month
;
288 lanalyzer_t
*lanalyzer
;
290 if (!wtap_read_bytes(wth
->fh
, &rec_header
, LA_RecordHeaderSize
,
292 if (*err
!= WTAP_ERR_SHORT_READ
)
293 return WTAP_OPEN_ERROR
;
294 return WTAP_OPEN_NOT_MINE
;
296 record_type
= pletoh16(rec_header
.record_type
);
297 record_length
= pletoh16(rec_header
.record_length
); /* make sure to do this for while() loop */
299 if (record_type
!= RT_HeaderRegular
&& record_type
!= RT_HeaderCyclic
) {
300 return WTAP_OPEN_NOT_MINE
;
303 /* Read the major and minor version numbers */
304 if (record_length
< sizeof header_fixed
) {
306 * Not enough room for the major and minor version numbers.
307 * Just treat that as a "not a LANalyzer file" indication.
309 return WTAP_OPEN_NOT_MINE
;
311 if (!wtap_read_bytes(wth
->fh
, &header_fixed
, sizeof header_fixed
,
313 if (*err
!= WTAP_ERR_SHORT_READ
)
314 return WTAP_OPEN_ERROR
;
315 return WTAP_OPEN_NOT_MINE
;
317 record_length
-= sizeof header_fixed
;
319 if (record_length
!= 0) {
320 /* Read the rest of the record as a comment. */
321 comment
= (char *)g_malloc(record_length
+ 1);
322 if (!wtap_read_bytes(wth
->fh
, comment
, record_length
,
324 if (*err
!= WTAP_ERR_SHORT_READ
) {
326 return WTAP_OPEN_ERROR
;
329 return WTAP_OPEN_NOT_MINE
;
331 wtap_block_add_string_option(g_array_index(wth
->shb_hdrs
, wtap_block_t
, 0), OPT_COMMENT
, comment
, record_length
);
336 * Read records until we find the start of packets.
337 * The document cited above claims that the first 11 records are
338 * in a particular sequence of types, but at least one capture
339 * doesn't have all the types listed in the order listed.
341 * If we don't have a summary record, we don't know the link-layer
342 * header type, so we can't read the file.
344 found_summary
= false;
346 if (!wtap_read_bytes_or_eof(wth
->fh
, &rec_header
,
347 LA_RecordHeaderSize
, err
, err_info
)) {
350 * End of file and no packets;
355 return WTAP_OPEN_ERROR
;
358 record_type
= pletoh16(rec_header
.record_type
);
359 record_length
= pletoh16(rec_header
.record_length
);
361 /*ws_message("Record 0x%04X Length %d", record_type, record_length);*/
362 switch (record_type
) {
363 /* Trace Summary Record */
365 if (record_length
< sizeof summary
) {
366 *err
= WTAP_ERR_BAD_FILE
;
367 *err_info
= ws_strdup_printf("lanalyzer: summary record length %u is too short",
369 return WTAP_OPEN_ERROR
;
371 if (!wtap_read_bytes(wth
->fh
, summary
,
372 sizeof summary
, err
, err_info
))
373 return WTAP_OPEN_ERROR
;
375 /* Assume that the date of the creation of the trace file
376 * is the same date of the trace. Lanalyzer doesn't
377 * store the creation date/time of the trace, but only of
378 * the file. Unless you traced at 11:55 PM and saved at 00:05
379 * AM, the assumption that trace.date == file.date is true.
382 cr_month
= summary
[1];
383 cr_year
= pletoh16(&summary
[2]);
384 /*ws_message("Day %d Month %d Year %d (%04X)", cr_day, cr_month,
387 /* Get capture start time. I learned how to do
388 * this from Guy's code in ngsniffer.c
390 tm
.tm_year
= cr_year
- 1900;
391 tm
.tm_mon
= cr_month
- 1;
398 /*ws_message("Day %d Month %d Year %d", tm.tm_mday,
399 tm.tm_mon, tm.tm_year);*/
400 mxslc
= pletoh16(&summary
[30]);
402 board_type
= pletoh16(&summary
[188]);
403 switch (board_type
) {
405 file_encap
= WTAP_ENCAP_ETHERNET
;
408 file_encap
= WTAP_ENCAP_TOKEN_RING
;
411 *err
= WTAP_ERR_UNSUPPORTED
;
412 *err_info
= ws_strdup_printf("lanalyzer: board type %u unknown",
414 return WTAP_OPEN_ERROR
;
418 *err
= WTAP_ERR_BAD_FILE
;
419 *err_info
= ws_strdup_printf("lanalyzer: file has more than one summary record");
420 return WTAP_OPEN_ERROR
;
422 found_summary
= true;
424 /* Skip the rest of the record */
425 record_length
-= sizeof summary
;
426 if (record_length
!= 0) {
427 if (!wtap_read_bytes(wth
->fh
, NULL
, record_length
, err
, err_info
)) {
428 return WTAP_OPEN_ERROR
;
433 /* Trace Packet Data Record */
435 /* Go back header number of bytes so that lanalyzer_read
436 * can read this header */
437 if (file_seek(wth
->fh
, -LA_RecordHeaderSize
, SEEK_CUR
, err
) == -1) {
438 return WTAP_OPEN_ERROR
;
443 /* Unknown record type - skip it */
444 if (!wtap_read_bytes(wth
->fh
, NULL
, record_length
, err
, err_info
)) {
445 return WTAP_OPEN_ERROR
;
452 if (!found_summary
) {
453 *err
= WTAP_ERR_BAD_FILE
;
454 *err_info
= ws_strdup_printf("lanalyzer: file has no summary record");
455 return WTAP_OPEN_ERROR
;
458 /* If we made it this far, then the file is a readable LANAlyzer file.
459 * Let's get some info from it. Note that we get wth->snapshot_length
460 * from a record later in the file. */
461 wth
->file_type_subtype
= lanalyzer_file_type_subtype
;
462 lanalyzer
= g_new(lanalyzer_t
, 1);
463 lanalyzer
->start
= start
;
464 wth
->priv
= (void *)lanalyzer
;
465 wth
->subtype_read
= lanalyzer_read
;
466 wth
->subtype_seek_read
= lanalyzer_seek_read
;
467 wth
->file_encap
= file_encap
;
468 wth
->snapshot_length
= mxslc
;
469 wth
->file_tsprec
= WTAP_TSPREC_NSEC
;
472 * Add an IDB; we don't know how many interfaces were involved,
473 * so we just say one interface, about which we only know
474 * the link-layer type, snapshot length, and time stamp
477 wtap_add_generated_idb(wth
);
479 return WTAP_OPEN_MINE
;
482 #define DESCRIPTOR_LEN 32
484 static bool lanalyzer_read_trace_record(wtap
*wth
, FILE_T fh
,
485 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
487 char LE_record_type
[2];
488 char LE_record_length
[2];
489 uint16_t record_type
, record_length
;
490 int record_data_size
;
492 char descriptor
[DESCRIPTOR_LEN
];
493 lanalyzer_t
*lanalyzer
;
494 uint16_t time_low
, time_med
, time_high
, true_size
;
498 /* read the record type and length. */
499 if (!wtap_read_bytes_or_eof(fh
, LE_record_type
, 2, err
, err_info
))
501 if (!wtap_read_bytes(fh
, LE_record_length
, 2, err
, err_info
))
504 record_type
= pletoh16(LE_record_type
);
505 record_length
= pletoh16(LE_record_length
);
507 /* Only Trace Packet Data Records should occur now that we're in
508 * the middle of reading packets. If any other record type exists
509 * after a Trace Packet Data Record, mark it as an error. */
510 if (record_type
!= RT_PacketData
) {
511 *err
= WTAP_ERR_BAD_FILE
;
512 *err_info
= ws_strdup_printf("lanalyzer: record type %u seen after trace summary record",
517 if (record_length
< DESCRIPTOR_LEN
) {
519 * Uh-oh, the record isn't big enough to even have a
522 *err
= WTAP_ERR_BAD_FILE
;
523 *err_info
= ws_strdup_printf("lanalyzer: file has a %u-byte record, too small to have even a packet descriptor",
527 record_data_size
= record_length
- DESCRIPTOR_LEN
;
529 /* Read the descriptor data */
530 if (!wtap_read_bytes(fh
, descriptor
, DESCRIPTOR_LEN
, err
, err_info
))
533 true_size
= pletoh16(&descriptor
[4]);
534 packet_size
= pletoh16(&descriptor
[6]);
536 * The maximum value of packet_size is 65535, which is less than
537 * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
542 * OK, is the frame data size greater than what's left of the
545 if (packet_size
> record_data_size
) {
547 * Yes - treat this as an error.
549 *err
= WTAP_ERR_BAD_FILE
;
550 *err_info
= g_strdup("lanalyzer: Record length is less than packet size");
554 rec
->rec_type
= REC_TYPE_PACKET
;
555 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
556 rec
->presence_flags
= WTAP_HAS_TS
|WTAP_HAS_CAP_LEN
;
558 time_low
= pletoh16(&descriptor
[8]);
559 time_med
= pletoh16(&descriptor
[10]);
560 time_high
= pletoh16(&descriptor
[12]);
561 t
= (((uint64_t)time_low
) << 0) + (((uint64_t)time_med
) << 16) +
562 (((uint64_t)time_high
) << 32);
563 tsecs
= (time_t) (t
/2000000);
564 lanalyzer
= (lanalyzer_t
*)wth
->priv
;
565 rec
->ts
.secs
= tsecs
+ lanalyzer
->start
;
566 rec
->ts
.nsecs
= ((uint32_t) (t
- tsecs
*2000000)) * 500;
568 if (true_size
- 4 >= packet_size
) {
570 * It appears that the "true size" includes the FCS;
571 * make it reflect the non-FCS size (the "packet size"
572 * appears never to include the FCS, even if no slicing
577 rec
->rec_header
.packet_header
.len
= true_size
;
578 rec
->rec_header
.packet_header
.caplen
= packet_size
;
580 switch (wth
->file_encap
) {
582 case WTAP_ENCAP_ETHERNET
:
583 /* We assume there's no FCS in this frame. */
584 rec
->rec_header
.packet_header
.pseudo_header
.eth
.fcs_len
= 0;
588 /* Read the packet data */
589 return wtap_read_packet_bytes(fh
, buf
, packet_size
, err
, err_info
);
592 /* Read the next packet */
593 static bool lanalyzer_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
594 int *err
, char **err_info
, int64_t *data_offset
)
596 *data_offset
= file_tell(wth
->fh
);
598 /* Read the record */
599 return lanalyzer_read_trace_record(wth
, wth
->fh
, rec
, buf
, err
,
603 static bool lanalyzer_seek_read(wtap
*wth
, int64_t seek_off
,
604 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
606 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
609 /* Read the record */
610 if (!lanalyzer_read_trace_record(wth
, wth
->random_fh
, rec
, buf
,
613 *err
= WTAP_ERR_SHORT_READ
;
619 /*---------------------------------------------------
620 * Returns true on success, false on error
621 * Write "cnt" bytes of zero with error control
622 *---------------------------------------------------*/
623 static bool s0write(wtap_dumper
*wdh
, size_t cnt
, int *err
)
628 snack
= cnt
> 64 ? 64 : cnt
;
630 if (!wtap_dump_file_write(wdh
, z64
, snack
, err
))
634 return true; /* ok */
637 /*---------------------------------------------------
638 * Returns true on success, false on error
639 * Write an 8-bit value
640 *---------------------------------------------------*/
641 static bool s8write(wtap_dumper
*wdh
, const uint8_t s8
, int *err
)
643 return wtap_dump_file_write(wdh
, &s8
, 1, err
);
645 /*---------------------------------------------------
646 * Returns true on success, false on error
647 * Write a 16-bit value as little-endian
648 *---------------------------------------------------*/
649 static bool s16write(wtap_dumper
*wdh
, const uint16_t s16
, int *err
)
651 uint16_t s16_le
= GUINT16_TO_LE(s16
);
652 return wtap_dump_file_write(wdh
, &s16_le
, 2, err
);
654 /*---------------------------------------------------
655 * Returns true on success, false on error
656 * Write a 32-bit value as little-endian
657 *---------------------------------------------------*/
658 static bool s32write(wtap_dumper
*wdh
, const uint32_t s32
, int *err
)
660 uint32_t s32_le
= GUINT32_TO_LE(s32
);
661 return wtap_dump_file_write(wdh
, &s32_le
, 4, err
);
663 /*---------------------------------------------------
664 * Returns true on success, false on error
665 * Write a 48-bit value as little-endian
666 *---------------------------------------------------*/
667 static bool s48write(wtap_dumper
*wdh
, const uint64_t s48
, int *err
)
669 #if G_BYTE_ORDER == G_BIG_ENDIAN
670 uint16_t s48_upper_le
= GUINT16_SWAP_LE_BE((uint16_t) (s48
>> 32));
671 uint32_t s48_lower_le
= GUINT32_SWAP_LE_BE((uint32_t) (s48
& 0xFFFFFFFF));
673 uint16_t s48_upper_le
= (uint16_t) (s48
>> 32);
674 uint32_t s48_lower_le
= (uint32_t) (s48
& 0xFFFFFFFF);
676 return wtap_dump_file_write(wdh
, &s48_lower_le
, 4, err
) &&
677 wtap_dump_file_write(wdh
, &s48_upper_le
, 2, err
);
679 /*---------------------------------------------------
680 * Write a record for a packet to a dump file.
681 * Returns true on success, false on failure.
682 *---------------------------------------------------*/
683 static bool lanalyzer_dump(wtap_dumper
*wdh
,
685 const uint8_t *pd
, int *err
, char **err_info _U_
)
690 LA_TmpInfo
*itmp
= (LA_TmpInfo
*)(wdh
->priv
);
692 int thisSize
= rec
->rec_header
.packet_header
.caplen
+ LA_PacketRecordSize
+ LA_RecordHeaderSize
;
694 /* We can only write packet records. */
695 if (rec
->rec_type
!= REC_TYPE_PACKET
) {
696 *err
= WTAP_ERR_UNWRITABLE_REC_TYPE
;
701 * Make sure this packet doesn't have a link-layer type that
702 * differs from the one for the file.
704 if (wdh
->file_encap
!= rec
->rec_header
.packet_header
.pkt_encap
) {
705 *err
= WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
709 if (wdh
->bytes_dumped
+ thisSize
> LA_ProFileLimit
) {
710 /* printf(" LA_ProFileLimit reached\n"); */
712 return false; /* and don't forget the header */
715 len
= rec
->rec_header
.packet_header
.caplen
+ (rec
->rec_header
.packet_header
.caplen
? LA_PacketRecordSize
: 0);
717 /* len goes into a 16-bit field, so there's a hard limit of 65535. */
719 *err
= WTAP_ERR_PACKET_TOO_LARGE
;
723 if (!s16write(wdh
, 0x1005, err
))
725 if (!s16write(wdh
, (uint16_t)len
, err
))
729 /* collect some information for the
730 * finally written header
732 itmp
->start
= rec
->ts
;
735 itmp
->encap
= wdh
->file_encap
;
739 if (!s16write(wdh
, 0x0001, err
)) /* pr.rx_channels */
741 if (!s16write(wdh
, 0x0008, err
)) /* pr.rx_errors */
743 if (!s16write(wdh
, (uint16_t) (rec
->rec_header
.packet_header
.len
+ 4), err
)) /* pr.rx_frm_len */
745 if (!s16write(wdh
, (uint16_t) rec
->rec_header
.packet_header
.caplen
, err
)) /* pr.rx_frm_sln */
748 nstime_delta(&td
, &rec
->ts
, &itmp
->start
);
750 /* Convert to half-microseconds, rounded up. */
751 x
= (td
.nsecs
+ 250) / 500; /* nanoseconds -> half-microseconds, rounded */
752 x
+= td
.secs
* 2000000; /* seconds -> half-microseconds */
754 if (!s48write(wdh
, x
, err
)) /* pr.rx_time[i] */
757 if (!s32write(wdh
, ++itmp
->pkts
, err
)) /* pr.pktno */
759 if (!s16write(wdh
, (uint16_t)itmp
->lastlen
, err
)) /* pr.prlen */
763 if (!s0write(wdh
, 12, err
))
766 if (!wtap_dump_file_write(wdh
, pd
, rec
->rec_header
.packet_header
.caplen
, err
))
772 /*---------------------------------------------------
773 * Returns 0 if we could write the specified encapsulation type,
774 * an error indication otherwise.
775 *---------------------------------------------------*/
776 static int lanalyzer_dump_can_write_encap(int encap
)
778 /* Per-packet encapsulations aren't supported. */
779 if (encap
== WTAP_ENCAP_PER_PACKET
)
780 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
782 if ( encap
!= WTAP_ENCAP_ETHERNET
783 && encap
!= WTAP_ENCAP_TOKEN_RING
)
784 return WTAP_ERR_UNWRITABLE_ENCAP
;
786 * printf("lanalyzer_dump_can_write_encap(%d)\n",encap);
791 /*---------------------------------------------------
792 * Returns true on success, false on failure; sets "*err" to an
793 * error code on failure
794 *---------------------------------------------------*/
795 static bool lanalyzer_dump_open(wtap_dumper
*wdh
, int *err
, char **err_info _U_
)
800 tmp
= g_malloc(sizeof(LA_TmpInfo
));
806 ((LA_TmpInfo
*)tmp
)->init
= false;
808 wdh
->subtype_write
= lanalyzer_dump
;
809 wdh
->subtype_finish
= lanalyzer_dump_finish
;
811 /* Some of the fields in the file header aren't known yet so
812 just skip over it for now. It will be created after all
813 of the packets have been written. */
815 jump
= sizeof (LA_HeaderRegularFake
)
816 + sizeof (LA_RxChannelNameFake
)
817 + sizeof (LA_TxChannelNameFake
)
818 + sizeof (LA_RxTemplateNameFake
)
819 + sizeof (LA_TxTemplateNameFake
)
820 + sizeof (LA_DisplayOptionsFake
)
821 + LA_SummaryRecordSize
822 + LA_SubfileSummaryRecordSize
823 + sizeof (LA_CyclicInformationFake
)
824 + LA_IndexRecordSize
;
826 if (wtap_dump_file_seek(wdh
, jump
, SEEK_SET
, err
) == -1)
829 wdh
->bytes_dumped
= jump
;
833 /*---------------------------------------------------
835 *---------------------------------------------------*/
836 static bool lanalyzer_dump_header(wtap_dumper
*wdh
, int *err
)
838 LA_TmpInfo
*itmp
= (LA_TmpInfo
*)(wdh
->priv
);
839 uint16_t board_type
= itmp
->encap
== WTAP_ENCAP_TOKEN_RING
840 ? BOARD_325TR
/* LANalyzer Board Type */
841 : BOARD_325
; /* LANalyzer Board Type */
844 fT
= localtime(&itmp
->start
.secs
);
848 if (wtap_dump_file_seek(wdh
, 0, SEEK_SET
, err
) == -1)
851 if (!wtap_dump_file_write(wdh
, &LA_HeaderRegularFake
,
852 sizeof LA_HeaderRegularFake
, err
))
854 if (!wtap_dump_file_write(wdh
, &LA_RxChannelNameFake
,
855 sizeof LA_RxChannelNameFake
, err
))
857 if (!wtap_dump_file_write(wdh
, &LA_TxChannelNameFake
,
858 sizeof LA_TxChannelNameFake
, err
))
860 if (!wtap_dump_file_write(wdh
, &LA_RxTemplateNameFake
,
861 sizeof LA_RxTemplateNameFake
, err
))
863 if (!wtap_dump_file_write(wdh
, &LA_TxTemplateNameFake
,
864 sizeof LA_TxTemplateNameFake
, err
))
866 if (!wtap_dump_file_write(wdh
, &LA_DisplayOptionsFake
,
867 sizeof LA_DisplayOptionsFake
, err
))
869 /*-----------------------------------------------------------------*/
870 if (!s16write(wdh
, RT_Summary
, err
)) /* rid */
872 if (!s16write(wdh
, SummarySize
, err
)) /* rlen */
874 if (!s8write(wdh
, (uint8_t) fT
->tm_mday
, err
)) /* s.datcre.day */
876 if (!s8write(wdh
, (uint8_t) (fT
->tm_mon
+1), err
)) /* s.datcre.mon */
878 if (!s16write(wdh
, (uint16_t) (fT
->tm_year
+ 1900), err
)) /* s.datcre.year */
880 if (!s8write(wdh
, (uint8_t) fT
->tm_mday
, err
)) /* s.datclo.day */
882 if (!s8write(wdh
, (uint8_t) (fT
->tm_mon
+1), err
)) /* s.datclo.mon */
884 if (!s16write(wdh
, (uint16_t) (fT
->tm_year
+ 1900), err
)) /* s.datclo.year */
886 if (!s8write(wdh
, (uint8_t) fT
->tm_sec
, err
)) /* s.timeopn.second */
888 if (!s8write(wdh
, (uint8_t) fT
->tm_min
, err
)) /* s.timeopn.minute */
890 if (!s8write(wdh
, (uint8_t) fT
->tm_hour
, err
)) /* s.timeopn.hour */
892 if (!s8write(wdh
, (uint8_t) fT
->tm_mday
, err
)) /* s.timeopn.mday */
894 if (!s0write(wdh
, 2, err
))
896 if (!s8write(wdh
, (uint8_t) fT
->tm_sec
, err
)) /* s.timeclo.second */
898 if (!s8write(wdh
, (uint8_t) fT
->tm_min
, err
)) /* s.timeclo.minute */
900 if (!s8write(wdh
, (uint8_t) fT
->tm_hour
, err
)) /* s.timeclo.hour */
902 if (!s8write(wdh
, (uint8_t) fT
->tm_mday
, err
)) /* s.timeclo.mday */
904 if (!s0write(wdh
, 2, err
))
906 if (!s0write(wdh
, 6, err
)) /* EAddr == 0 */
908 if (!s16write(wdh
, 1, err
)) /* s.mxseqno */
910 if (!s16write(wdh
, 0, err
)) /* s.slcoffo */
912 if (!s16write(wdh
, 1514, err
)) /* s.mxslc */
914 if (!s32write(wdh
, itmp
->pkts
, err
)) /* s.totpktt */
921 if (!s0write(wdh
, 12, err
))
923 if (!s32write(wdh
, itmp
->pkts
, err
)) /* sr.s.mxpkta[1] */
925 if (!s0write(wdh
, 34*4, err
)) /* s.mxpkta[2-33]=0 */
927 if (!s16write(wdh
, board_type
, err
))
929 if (!s0write(wdh
, 20, err
)) /* board_version == 0 */
931 /*-----------------------------------------------------------------*/
932 if (!s16write(wdh
, RT_SubfileSummary
, err
)) /* ssr.rid */
934 if (!s16write(wdh
, LA_SubfileSummaryRecordSize
-4, err
)) /* ssr.rlen */
936 if (!s16write(wdh
, 1, err
)) /* ssr.seqno */
938 if (!s32write(wdh
, itmp
->pkts
, err
)) /* ssr.totpkts */
940 /*-----------------------------------------------------------------*/
941 if (!wtap_dump_file_write(wdh
, &LA_CyclicInformationFake
,
942 sizeof LA_CyclicInformationFake
, err
))
944 /*-----------------------------------------------------------------*/
945 if (!s16write(wdh
, RT_Index
, err
)) /* rid */
947 if (!s16write(wdh
, LA_IndexRecordSize
-4, err
)) /* rlen */
949 if (!s16write(wdh
, LA_IndexSize
, err
)) /* idxsp */
951 if (!s0write(wdh
, LA_IndexRecordSize
- 6, err
))
957 /*---------------------------------------------------
958 * Finish writing to a dump file.
959 * Returns true on success, false on failure.
960 *---------------------------------------------------*/
961 static bool lanalyzer_dump_finish(wtap_dumper
*wdh
, int *err
,
964 /* bytes_dumped already accounts for the size of the header,
965 * but lanalyzer_dump_header() (via wtap_dump_file_write())
966 * will keep incrementing it.
968 int64_t saved_bytes_dumped
= wdh
->bytes_dumped
;
969 lanalyzer_dump_header(wdh
,err
);
970 wdh
->bytes_dumped
= saved_bytes_dumped
;
971 return *err
? false : true;
974 static const struct supported_block_type lanalyzer_blocks_supported
[] = {
976 * We support packet blocks, with no comments or other options.
978 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
981 static const struct file_type_subtype_info lanalyzer_info
= {
982 "Novell LANalyzer","lanalyzer", "tr1", NULL
,
983 true, BLOCKS_SUPPORTED(lanalyzer_blocks_supported
),
984 lanalyzer_dump_can_write_encap
, lanalyzer_dump_open
, NULL
987 void register_lanalyzer(void)
989 lanalyzer_file_type_subtype
= wtap_register_file_type_subtype(&lanalyzer_info
);
992 * Register name for backwards compatibility with the
993 * wtap_filetypes table in Lua.
995 wtap_register_backwards_compatibility_lua_name("LANALYZER",
996 lanalyzer_file_type_subtype
);
1000 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1005 * indent-tabs-mode: nil
1008 * vi: set shiftwidth=6 tabstop=8 expandtab:
1009 * :indentSize=6:tabSize=8:noTabs=true: