summaryrefslogtreecommitdiffstats
path: root/wiretap/snoop.c
blob: 8621be6eaf3cfb115ff029aeccc967ad143d343d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
/* snoop.c
 *
 * Wiretap Library
 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"
#include <string.h>
#include "wtap-int.h"
#include "file_wrappers.h"
#include "atm.h"
#include "snoop.h"
#include <wsutil/802_11-utils.h>
#include <wsutil/ws_roundup.h>

/* See RFC 1761 for a description of the "snoop" file format. */

typedef struct {
	gboolean is_shomiti;
} snoop_t;

/* Magic number in "snoop" files. */
static const char snoop_magic[] = {
	's', 'n', 'o', 'o', 'p', '\0', '\0', '\0'
};

/* "snoop" file header (minus magic number). */
struct snoop_hdr {
	guint32	version;	/* version number (should be 2) */
	guint32	network;	/* network type */
};

/* "snoop" record header. */
struct snooprec_hdr {
	guint32	orig_len;	/* actual length of packet */
	guint32	incl_len;	/* number of octets captured in file */
	guint32	rec_len;	/* length of record */
	guint32	cum_drops;	/* cumulative number of dropped packets */
	guint32	ts_sec;		/* timestamp seconds */
	guint32	ts_usec;	/* timestamp microseconds */
};

/*
 * The link-layer header on ATM packets.
 */
struct snoop_atm_hdr {
	guint8	flags;		/* destination and traffic type */
	guint8	vpi;		/* VPI */
	guint16	vci;		/* VCI */
};

/*
 * Extra information stuffed into the padding in Shomiti/Finisar Surveyor
 * captures.
 */
struct shomiti_trailer {
	guint16	phy_rx_length;	/* length on the wire, including FCS? */
	guint16	phy_rx_status;	/* status flags */
	guint32	ts_40_ns_lsb;	/* 40 ns time stamp, low-order bytes? */
	guint32	ts_40_ns_msb;	/* 40 ns time stamp, low-order bytes? */
	gint32	frame_id;	/* "FrameID"? */
};

/*
 * phy_rx_status flags.
 */
#define RX_STATUS_OVERFLOW		0x8000	/* overflow error */
#define RX_STATUS_BAD_CRC		0x4000	/* CRC error */
#define RX_STATUS_DRIBBLE_NIBBLE	0x2000	/* dribble/nibble bits? */
#define RX_STATUS_SHORT_FRAME		0x1000	/* frame < 64 bytes */
#define RX_STATUS_OVERSIZE_FRAME	0x0800	/* frame > 1518 bytes */
#define RX_STATUS_GOOD_FRAME		0x0400	/* frame OK */
#define RX_STATUS_N12_BYTES_RECEIVED	0x0200	/* first 12 bytes of frame received? */
#define RX_STATUS_RXABORT		0x0100	/* RXABORT during reception */
#define RX_STATUS_FIFO_ERROR		0x0080	/* receive FIFO error */
#define RX_STATUS_TRIGGERED		0x0001	/* frame did trigger */

static gboolean snoop_read(wtap *wth, wtap_rec *rec, Buffer *buf,
    int *err, gchar **err_info, gint64 *data_offset);
static gboolean snoop_seek_read(wtap *wth, gint64 seek_off,
    wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
static int snoop_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
    Buffer *buf, int *err, gchar **err_info);
static gboolean snoop_read_atm_pseudoheader(FILE_T fh,
    union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
static gboolean snoop_read_shomiti_wireless_pseudoheader(FILE_T fh,
    union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info,
    int *header_size);
static gboolean snoop_dump(wtap_dumper *wdh, const wtap_rec *rec,
    const guint8 *pd, int *err, gchar **err_info);

static int snoop_file_type_subtype = -1;
static int shomiti_file_type_subtype = -1;

void register_snoop(void);

/*
 * See
 *
 *	https://pubs.opengroup.org/onlinepubs/9638599/apdxf.htm
 *
 * for the "dlpi.h" header file specified by The Open Group, which lists
 * the DL_ values for various protocols; Solaris 7 uses the same values.
 *
 * See
 *
 *	https://www.iana.org/assignments/snoop-datalink-types
 *
 * for the IETF list of snoop datalink types.
 *
 * The page at
 *
 *	http://mrpink.lerc.nasa.gov/118x/support.html
 *
 * had links to modified versions of "tcpdump" and "libpcap" for SUNatm
 * DLPI support; they suggested that the 3.0 version of SUNatm uses those
 * values.  The Wayback Machine archived that page, but not the stuff
 * to which it linked, unfortunately.
 *
 * It also has a link to "convert.c", which is a program to convert files
 * from the format written by the "atmsnoop" program that comes with the
 * SunATM package to regular "snoop" format, claims that "SunATM 2.1 claimed
 * to be DL_FDDI (don't ask why).  SunATM 3.0 claims to be DL_IPATM, which
 * is 0x12".
 *
 * It also says that "ATM Mac header is 12 bytes long.", and seems to imply
 * that in an "atmsnoop" file, the header contains 2 bytes (direction and
 * VPI?), 2 bytes of VCI, 6 bytes of something, and 2 bytes of Ethernet
 * type; if those 6 bytes are 2 bytes of DSAP, 2 bytes of LSAP, 1 byte
 * of LLC control, and 3 bytes of SNAP OUI, that'd mean that an ATM
 * pseudo-header in an "atmsnoop" file is probably 1 byte of direction,
 * 1 byte of VPI, and 2 bytes of VCI.
 *
 * The aforementioned page also has a link to some capture files from
 * "atmsnoop"; this version of "snoop.c" appears to be able to read them.
 *
 * Source to an "atmdump" package, which includes a modified version of
 * "libpcap" to handle SunATM DLPI and an ATM driver for FreeBSD, and
 * also includes "atmdump", which is a modified "tcpdump", was available
 * at
 *
 *	ftp://ftp.cs.ndsu.nodak.edu/pub/freebsd/atm/atm-bpf.tgz
 *
 * (the host name is no longer valid) and that code also indicated that
 * DL_IPATM is used, and that an ATM packet handed up from the Sun driver
 * for the Sun SBus ATM card on Solaris 2.5.1 has 1 byte of direction,
 * 1 byte of VPI, 2 bytes of VCI, and then the ATM PDU, and suggests that
 * the direction flag is 0x80 for "transmitted" (presumably meaning
 * DTE->DCE) and presumably not 0x80 for "received" (presumably meaning
 * DCE->DTE).  That code was used as the basis for the SunATM support in
 * later versions of libpcap and tcpdump, and it worked at the time the
 * development was done with the SunATM code on the system on which the
 * development was done.
 *
 * In fact, the "direction" byte appears to have some other stuff, perhaps
 * a traffic type, in the lower 7 bits, with the 8th bit indicating the
 * direction.  That appears to be the case.
 *
 * I don't know what the encapsulation of any of the other types is, so I
 * leave them all as WTAP_ENCAP_UNKNOWN, except for those for which Brian
 * Ginsbach has supplied information about the way UNICOS/mp uses them.
 * I also don't know whether "snoop" can handle any of them (it presumably
 * can't handle ATM, otherwise Sun wouldn't have supplied "atmsnoop"; even
 * if it can't, this may be useful reference information for anybody doing
 * code to use DLPI to do raw packet captures on those network types.
 *
 *	https://web.archive.org/web/20010906213807/http://www.shomiti.com/support/TNCapFileFormat.htm
 *
 * gives information on Shomiti's mutant flavor of snoop.  For some unknown
 * reason, they decided not to just Go With The DLPI Flow, and instead used
 * the types unspecified in RFC 1461 for their own nefarious purposes, such
 * as distinguishing 10MB from 100MB from 1000MB Ethernet and distinguishing
 * 4MB from 16MB Token Ring, and distinguishing both of them from the
 * "Shomiti" versions of same.
 */
wtap_open_return_val snoop_open(wtap *wth, int *err, gchar **err_info)
{
	char magic[sizeof snoop_magic];
	struct snoop_hdr hdr;
	struct snooprec_hdr rec_hdr;
	guint padbytes;
	gboolean is_shomiti;
	static const int snoop_encap[] = {
		WTAP_ENCAP_ETHERNET,	/* IEEE 802.3 */
		WTAP_ENCAP_UNKNOWN,	/* IEEE 802.4 Token Bus */
		WTAP_ENCAP_TOKEN_RING,
		WTAP_ENCAP_UNKNOWN,	/* IEEE 802.6 Metro Net */
		WTAP_ENCAP_ETHERNET,
		WTAP_ENCAP_UNKNOWN,	/* HDLC */
		WTAP_ENCAP_UNKNOWN,	/* Character Synchronous, e.g. bisync */
		WTAP_ENCAP_UNKNOWN,	/* IBM Channel-to-Channel */
		WTAP_ENCAP_FDDI_BITSWAPPED,
		WTAP_ENCAP_NULL,	/* Other */
		WTAP_ENCAP_UNKNOWN,	/* Frame Relay LAPF */
		WTAP_ENCAP_UNKNOWN,	/* Multi-protocol over Frame Relay */
		WTAP_ENCAP_UNKNOWN,	/* Character Async (e.g., SLIP and PPP?) */
		WTAP_ENCAP_UNKNOWN,	/* X.25 Classical IP */
		WTAP_ENCAP_NULL,	/* software loopback */
		WTAP_ENCAP_UNKNOWN,	/* not defined in "dlpi.h" */
		WTAP_ENCAP_IP_OVER_FC,	/* Fibre Channel */
		WTAP_ENCAP_UNKNOWN,	/* ATM */
		WTAP_ENCAP_ATM_PDUS,	/* ATM Classical IP */
		WTAP_ENCAP_UNKNOWN,	/* X.25 LAPB */
		WTAP_ENCAP_UNKNOWN,	/* ISDN */
		WTAP_ENCAP_UNKNOWN,	/* HIPPI */
		WTAP_ENCAP_UNKNOWN,	/* 100VG-AnyLAN Ethernet */
		WTAP_ENCAP_UNKNOWN,	/* 100VG-AnyLAN Token Ring */
		WTAP_ENCAP_UNKNOWN,	/* "ISO 8802/3 and Ethernet" */
		WTAP_ENCAP_UNKNOWN,	/* 100BaseT (but that's just Ethernet) */
		WTAP_ENCAP_IP_OVER_IB_SNOOP,	/* Infiniband */
	};
	#define NUM_SNOOP_ENCAPS (sizeof snoop_encap / sizeof snoop_encap[0])
	#define SNOOP_PRIVATE_BIT 0x80000000
	static const int snoop_private_encap[] = {
		WTAP_ENCAP_UNKNOWN,	/* Not Used */
		WTAP_ENCAP_UNKNOWN,	/* IPv4 Tunnel Link */
		WTAP_ENCAP_UNKNOWN,	/* IPv6 Tunnel Link */
		WTAP_ENCAP_UNKNOWN,	/* Virtual network interface */
		WTAP_ENCAP_UNKNOWN,	/* IEEE 802.11 */
		WTAP_ENCAP_IPNET,	/* ipnet(7D) link */
		WTAP_ENCAP_UNKNOWN,	/* IPMP stub interface */
		WTAP_ENCAP_UNKNOWN,	/* 6to4 Tunnel Link */
	};
	#define NUM_SNOOP_PRIVATE_ENCAPS (sizeof snoop_private_encap / sizeof snoop_private_encap[0])
	static const int shomiti_encap[] = {
		WTAP_ENCAP_ETHERNET,	/* IEEE 802.3 */
		WTAP_ENCAP_UNKNOWN,	/* IEEE 802.4 Token Bus */
		WTAP_ENCAP_TOKEN_RING,
		WTAP_ENCAP_UNKNOWN,	/* IEEE 802.6 Metro Net */
		WTAP_ENCAP_ETHERNET,
		WTAP_ENCAP_UNKNOWN,	/* HDLC */
		WTAP_ENCAP_UNKNOWN,	/* Character Synchronous, e.g. bisync */
		WTAP_ENCAP_UNKNOWN,	/* IBM Channel-to-Channel */
		WTAP_ENCAP_FDDI_BITSWAPPED,
		WTAP_ENCAP_UNKNOWN,	/* Other */
		WTAP_ENCAP_ETHERNET,	/* Fast Ethernet */
		WTAP_ENCAP_TOKEN_RING,	/* 4MB 802.5 token ring */
		WTAP_ENCAP_ETHERNET,	/* Gigabit Ethernet */
		WTAP_ENCAP_TOKEN_RING,	/* "IEEE 802.5 Shomiti" */
		WTAP_ENCAP_TOKEN_RING,	/* "4MB IEEE 802.5 Shomiti" */
		WTAP_ENCAP_UNKNOWN,	/* Other */
		WTAP_ENCAP_UNKNOWN,	/* Other */
		WTAP_ENCAP_UNKNOWN,	/* Other */
		WTAP_ENCAP_IEEE_802_11_WITH_RADIO, /* IEEE 802.11 with Radio Header */
		WTAP_ENCAP_ETHERNET,	/* 10 Gigabit Ethernet */
	};
	#define NUM_SHOMITI_ENCAPS (sizeof shomiti_encap / sizeof shomiti_encap[0])
	int file_encap;
	gint64 saved_offset;
	snoop_t *snoop;

	/* Read in the string that should be at the start of a "snoop" file */
	if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info)) {
		if (*err != WTAP_ERR_SHORT_READ)
			return WTAP_OPEN_ERROR;
		return WTAP_OPEN_NOT_MINE;
	}

	if (memcmp(magic, snoop_magic, sizeof snoop_magic) != 0) {
		return WTAP_OPEN_NOT_MINE;
	}

	/* Read the rest of the header. */
	if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
		return WTAP_OPEN_ERROR;

	/*
	 * Make sure it's a version we support.
	 */
	hdr.version = g_ntohl(hdr.version);
	switch (hdr.version) {

	case 2:		/* Solaris 2.x and later snoop, and Shomiti
			   Surveyor prior to 3.0, or 3.0 and later
			   with NDIS card */
	case 3:		/* Surveyor 3.0 and later, with Shomiti CMM2 hardware */
	case 4:		/* Surveyor 3.0 and later, with Shomiti GAM hardware */
	case 5:		/* Surveyor 3.0 and later, with Shomiti THG hardware */
		break;

	default:
		*err = WTAP_ERR_UNSUPPORTED;
		*err_info = ws_strdup_printf("snoop: version %u unsupported", hdr.version);
		return WTAP_OPEN_ERROR;
	}

	/*
	 * Oh, this is lovely.
	 *
	 * I suppose Shomiti could give a bunch of lawyerly noise about
	 * how "well, RFC 1761 said they were unassigned, and that's
	 * the standard, not the DLPI header file, so it's perfectly OK
	 * for us to use them, blah blah blah", but it's still irritating
	 * as hell that they used the unassigned-in-RFC-1761 values for
	 * their own purposes - especially given that Sun also used
	 * one of them in atmsnoop.
	 *
	 * We can't determine whether it's a Shomiti capture based on
	 * the version number, as, according to their documentation on
	 * their capture file format, Shomiti uses a version number of 2
	 * if the data "was captured using an NDIS card", which presumably
	 * means "captured with an ordinary boring network card via NDIS"
	 * as opposed to "captured with our whizzo special capture
	 * hardware".
	 *
	 * The only way I can see to determine that is to check how much
	 * padding there is in the first packet - if there's enough
	 * padding for a Shomiti trailer, it's probably a Shomiti
	 * capture, and otherwise, it's probably from Snoop.
	 */

	/*
	 * Start out assuming it's not a Shomiti capture.
	 */
	is_shomiti = FALSE;

	/* Read first record header. */
	saved_offset = file_tell(wth->fh);
	if (!wtap_read_bytes_or_eof(wth->fh, &rec_hdr, sizeof rec_hdr, err, err_info)) {
		if (*err != 0)
			return WTAP_OPEN_ERROR;

		/*
		 * The file ends after the record header, which means this
		 * is a capture with no packets.
		 *
		 * We assume it's a snoop file; the actual type of file is
		 * irrelevant, as there are no records in it, and thus no
		 * extra information if it's a Shomiti capture, and no
		 * link-layer headers whose type we have to know, and no
		 * Ethernet frames that might have an FCS.
		 */
	} else {
		/*
		 * Compute the number of bytes of padding in the
		 * record.  If it's at least the size of a Shomiti
		 * trailer record, we assume this is a Shomiti
		 * capture.  (Some atmsnoop captures appear
		 * to have 4 bytes of padding, and at least one
		 * snoop capture appears to have 6 bytes of padding;
		 * the Shomiti header is larger than either of those.)
		 */
		if (g_ntohl(rec_hdr.rec_len) >
		    (sizeof rec_hdr + g_ntohl(rec_hdr.incl_len))) {
			/*
			 * Well, we have padding; how much?
			 */
			padbytes = g_ntohl(rec_hdr.rec_len) -
			    ((guint)sizeof rec_hdr + g_ntohl(rec_hdr.incl_len));

			/*
			 * Is it at least the size of a Shomiti trailer?
			 */
			is_shomiti =
			    (padbytes >= sizeof (struct shomiti_trailer));
		}
	}

	/*
	 * Seek back to the beginning of the first record.
	 */
	if (file_seek(wth->fh, saved_offset, SEEK_SET, err) == -1)
		return WTAP_OPEN_ERROR;

	hdr.network = g_ntohl(hdr.network);
	if (is_shomiti) {
		if (hdr.network >= NUM_SHOMITI_ENCAPS
		    || shomiti_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
			*err = WTAP_ERR_UNSUPPORTED;
			*err_info = ws_strdup_printf("snoop: Shomiti network type %u unknown or unsupported",
			    hdr.network);
			return WTAP_OPEN_ERROR;
		}
		file_encap = shomiti_encap[hdr.network];
	} else if (hdr.network & SNOOP_PRIVATE_BIT) {
		if ((hdr.network^SNOOP_PRIVATE_BIT) >= NUM_SNOOP_PRIVATE_ENCAPS
		    || snoop_private_encap[hdr.network^SNOOP_PRIVATE_BIT] == WTAP_ENCAP_UNKNOWN) {
			*err = WTAP_ERR_UNSUPPORTED;
			*err_info = ws_strdup_printf("snoop: private network type %u unknown or unsupported",
			    hdr.network);
			return WTAP_OPEN_ERROR;
		}
		file_encap = snoop_private_encap[hdr.network^SNOOP_PRIVATE_BIT];
	} else {
		if (hdr.network >= NUM_SNOOP_ENCAPS
		    || snoop_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
			*err = WTAP_ERR_UNSUPPORTED;
			*err_info = ws_strdup_printf("snoop: network type %u unknown or unsupported",
			    hdr.network);
			return WTAP_OPEN_ERROR;
		}
		file_encap = snoop_encap[hdr.network];
	}

	/*
	 * We don't currently use the extra information in Shomiti
	 * records, so we use the same routines to read snoop and
	 * Shomiti files.
	 */
	wth->file_type_subtype = is_shomiti ? shomiti_file_type_subtype : snoop_file_type_subtype;
	snoop = g_new0(snoop_t, 1);
	wth->priv = (void *)snoop;
	wth->subtype_read = snoop_read;
	wth->subtype_seek_read = snoop_seek_read;
	wth->file_encap = file_encap;
	wth->snapshot_length = 0;	/* not available in header */
	wth->file_tsprec = WTAP_TSPREC_USEC;
	snoop->is_shomiti = is_shomiti;

	/*
	 * Add an IDB; we don't know how many interfaces were
	 * involved, so we just say one interface, about which
	 * we only know the link-layer type, snapshot length,
	 * and time stamp resolution.
	 */
	wtap_add_generated_idb(wth);

	return WTAP_OPEN_MINE;
}

/*
 * XXX - pad[3] is the length of the header, not including
 * the length of the pad field; is it a 1-byte field, a 2-byte
 * field with pad[2] usually being 0, a 3-byte field with
 * pad[1] and pad[2] usually being 0, or a 4-byte field?
 *
 * If it's not a 4-byte field, is there anything significant
 * in the other bytes?
 *
 * Can the header length ever be less than 8, so that not
 * all the fields following pad are present?
 *
 * What's in undecrypt?  In captures I've seen, undecrypt[0]
 * is usually 0x00 but sometimes 0x02 or 0x06, and undecrypt[1]
 * is either 0x00 or 0x02.
 *
 * What's in preamble?  In captures I've seen, it's 0x00.
 *
 * What's in code?  In captures I've seen, it's 0x01 or 0x03.
 *
 * If the header is longer than 8 bytes, what are the other fields?
 */
typedef struct {
	guint8 pad[4];
	guint8 undecrypt[2];
	guint8 rate;
	guint8 preamble;
	guint8 code;
	guint8 signal;
	guint8 qual;
	guint8 channel;
} shomiti_wireless_header;


/* Read the next packet */
static gboolean snoop_read(wtap *wth, wtap_rec *rec, Buffer *buf,
    int *err, gchar **err_info, gint64 *data_offset)
{
	int	padbytes;

	*data_offset = file_tell(wth->fh);

	padbytes = snoop_read_packet(wth, wth->fh, rec, buf, err, err_info);
	if (padbytes == -1)
		return FALSE;

	/*
	 * Skip over the padding, if any.
	 */
	if (padbytes != 0) {
		if (!wtap_read_bytes(wth->fh, NULL, padbytes, err, err_info))
			return FALSE;
	}

	return TRUE;
}

static gboolean
snoop_seek_read(wtap *wth, gint64 seek_off,
    wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
{
	if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
		return FALSE;

	if (snoop_read_packet(wth, wth->random_fh, rec, buf, err, err_info) == -1) {
		if (*err == 0)
			*err = WTAP_ERR_SHORT_READ;
		return FALSE;
	}
	return TRUE;
}

static int
snoop_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
    Buffer *buf, int *err, gchar **err_info)
{
	snoop_t *snoop = (snoop_t *)wth->priv;
	struct snooprec_hdr hdr;
	guint32 rec_size;
	guint32	packet_size;
	guint32 orig_size;
	int header_size;

	/* Read record header. */
	if (!wtap_read_bytes_or_eof(fh, &hdr, sizeof hdr, err, err_info))
		return -1;

	rec_size = g_ntohl(hdr.rec_len);
	orig_size = g_ntohl(hdr.orig_len);
	packet_size = g_ntohl(hdr.incl_len);
	if (orig_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
		/*
		 * Probably a corrupt capture file; don't blow up trying
		 * to allocate space for an immensely-large packet.
		 */
		*err = WTAP_ERR_BAD_FILE;
		*err_info = ws_strdup_printf("snoop: File has %u-byte original length, bigger than maximum of %u",
		    orig_size, WTAP_MAX_PACKET_SIZE_STANDARD);
		return -1;
	}
	if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
		/*
		 * Probably a corrupt capture file; don't blow up trying
		 * to allocate space for an immensely-large packet.
		 */
		*err = WTAP_ERR_BAD_FILE;
		*err_info = ws_strdup_printf("snoop: File has %u-byte packet, bigger than maximum of %u",
		    packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
		return -1;
	}
	if (packet_size > rec_size) {
		/*
		 * Probably a corrupt capture file.
		 */
		*err = WTAP_ERR_BAD_FILE;
		*err_info = ws_strdup_printf("snoop: File has %u-byte packet, bigger than record size %u",
		    packet_size, rec_size);
		return -1;
	}

	switch (wth->file_encap) {

	case WTAP_ENCAP_ATM_PDUS:
		/*
		 * This is an ATM packet, so the first four bytes are
		 * the direction of the packet (transmit/receive), the
		 * VPI, and the VCI; read them and generate the
		 * pseudo-header from them.
		 */
		if (packet_size < sizeof (struct snoop_atm_hdr)) {
			/*
			 * Uh-oh, the packet isn't big enough to even
			 * have a pseudo-header.
			 */
			*err = WTAP_ERR_BAD_FILE;
			*err_info = ws_strdup_printf("snoop: atmsnoop file has a %u-byte packet, too small to have even an ATM pseudo-header",
			    packet_size);
			return -1;
		}
		if (!snoop_read_atm_pseudoheader(fh, &rec->rec_header.packet_header.pseudo_header,
		    err, err_info))
			return -1;	/* Read error */

		/*
		 * Don't count the pseudo-header as part of the packet.
		 */
		rec_size -= (guint32)sizeof (struct snoop_atm_hdr);
		orig_size -= (guint32)sizeof (struct snoop_atm_hdr);
		packet_size -= (guint32)sizeof (struct snoop_atm_hdr);
		break;

	case WTAP_ENCAP_ETHERNET:
		/*
		 * If this is a snoop file, we assume there's no FCS in
		 * this frame; if this is a Shomit file, we assume there
		 * is.  (XXX - or should we treat it a "maybe"?)
		 */
		if (snoop->is_shomiti)
			rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 4;
		else
			rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
		break;

	case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
		if (packet_size < sizeof (shomiti_wireless_header)) {
			/*
			 * Uh-oh, the packet isn't big enough to even
			 * have a pseudo-header.
			 */
			*err = WTAP_ERR_BAD_FILE;
			*err_info = ws_strdup_printf("snoop: Shomiti wireless file has a %u-byte packet, too small to have even a wireless pseudo-header",
			    packet_size);
			return -1;
		}
		if (!snoop_read_shomiti_wireless_pseudoheader(fh,
		    &rec->rec_header.packet_header.pseudo_header, err, err_info, &header_size))
			return -1;	/* Read error */

		/*
		 * Don't count the pseudo-header as part of the packet.
		 */
		rec_size -= header_size;
		orig_size -= header_size;
		packet_size -= header_size;
		break;
	}

	rec->rec_type = REC_TYPE_PACKET;
	rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
	rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
	rec->ts.secs = g_ntohl(hdr.ts_sec);
	rec->ts.nsecs = g_ntohl(hdr.ts_usec) * 1000;
	rec->rec_header.packet_header.caplen = packet_size;
	rec->rec_header.packet_header.len = orig_size;

	if (rec_size < (sizeof hdr + packet_size)) {
		/*
		 * What, *negative* padding?  Bogus.
		 */
		*err = WTAP_ERR_BAD_FILE;
		*err_info = ws_strdup_printf("snoop: File has %u-byte record with packet size of %u",
		    rec_size, packet_size);
		return -1;
	}

	/*
	 * Read the packet data.
	 */
	if (!wtap_read_packet_bytes(fh, buf, packet_size, err, err_info))
		return -1;	/* failed */

	/*
	 * If this is ATM LANE traffic, try to guess what type of LANE
	 * traffic it is based on the packet contents.
	 */
	if (wth->file_encap == WTAP_ENCAP_ATM_PDUS &&
	    rec->rec_header.packet_header.pseudo_header.atm.type == TRAF_LANE) {
		atm_guess_lane_type(rec, ws_buffer_start_ptr(buf));
	}

	return rec_size - ((guint)sizeof hdr + packet_size);
}

static gboolean
snoop_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
    int *err, gchar **err_info)
{
	struct snoop_atm_hdr atm_phdr;
	guint8	vpi;
	guint16	vci;

	if (!wtap_read_bytes(fh, &atm_phdr, sizeof atm_phdr, err, err_info))
		return FALSE;

	vpi = atm_phdr.vpi;
	vci = pntoh16(&atm_phdr.vci);

	/*
	 * The lower 4 bits of the first byte of the header indicate
	 * the type of traffic, as per the "atmioctl.h" header in
	 * SunATM.
	 */
	switch (atm_phdr.flags & 0x0F) {

	case 0x01:	/* LANE */
		pseudo_header->atm.aal = AAL_5;
		pseudo_header->atm.type = TRAF_LANE;
		break;

	case 0x02:	/* RFC 1483 LLC multiplexed traffic */
		pseudo_header->atm.aal = AAL_5;
		pseudo_header->atm.type = TRAF_LLCMX;
		break;

	case 0x05:	/* ILMI */
		pseudo_header->atm.aal = AAL_5;
		pseudo_header->atm.type = TRAF_ILMI;
		break;

	case 0x06:	/* Signalling AAL */
		pseudo_header->atm.aal = AAL_SIGNALLING;
		pseudo_header->atm.type = TRAF_UNKNOWN;
		break;

	case 0x03:	/* MARS (RFC 2022) */
		pseudo_header->atm.aal = AAL_5;
		pseudo_header->atm.type = TRAF_UNKNOWN;
		break;

	case 0x04:	/* IFMP (Ipsilon Flow Management Protocol; see RFC 1954) */
		pseudo_header->atm.aal = AAL_5;
		pseudo_header->atm.type = TRAF_UNKNOWN;	/* XXX - TRAF_IPSILON? */
		break;

	default:
		/*
		 * Assume it's AAL5, unless it's VPI 0 and VCI 5, in which
		 * case assume it's AAL_SIGNALLING; we know nothing more
		 * about it.
		 *
		 * XXX - is this necessary?  Or are we guaranteed that
		 * all signalling traffic has a type of 0x06?
		 *
		 * XXX - is this guaranteed to be AAL5?  Or, if the type is
		 * 0x00 ("raw"), might it be non-AAL5 traffic?
		 */
		if (vpi == 0 && vci == 5)
			pseudo_header->atm.aal = AAL_SIGNALLING;
		else
			pseudo_header->atm.aal = AAL_5;
		pseudo_header->atm.type = TRAF_UNKNOWN;
		break;
	}
	pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;

	pseudo_header->atm.vpi = vpi;
	pseudo_header->atm.vci = vci;
	pseudo_header->atm.channel = (atm_phdr.flags & 0x80) ? 0 : 1;

	/* We don't have this information */
	pseudo_header->atm.flags = 0;
	pseudo_header->atm.cells = 0;
	pseudo_header->atm.aal5t_u2u = 0;
	pseudo_header->atm.aal5t_len = 0;
	pseudo_header->atm.aal5t_chksum = 0;

	return TRUE;
}

static gboolean
snoop_read_shomiti_wireless_pseudoheader(FILE_T fh,
    union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info,
    int *header_size)
{
	shomiti_wireless_header whdr;
	int	rsize;

	if (!wtap_read_bytes(fh, &whdr, sizeof whdr, err, err_info))
		return FALSE;

	/* the 4th byte of the pad is actually a header length,
	 * we've already read 8 bytes of it, and it must never
	 * be less than 8.
	 *
	 * XXX - presumably that means that the header length
	 * doesn't include the length field, as we've read
	 * 12 bytes total.
	 */
	if (whdr.pad[3] < 8) {
		*err = WTAP_ERR_BAD_FILE;
		*err_info = ws_strdup_printf("snoop: Header length in Surveyor record is %u, less than minimum of 8",
		    whdr.pad[3]);
		return FALSE;
	}
	/* Skip the header. */
	rsize = ((int) whdr.pad[3]) - 8;
	if (!wtap_read_bytes(fh, NULL, rsize, err, err_info))
		return FALSE;

	memset(&pseudo_header->ieee_802_11, 0, sizeof(pseudo_header->ieee_802_11));
	pseudo_header->ieee_802_11.fcs_len = 4;
	pseudo_header->ieee_802_11.decrypted = FALSE;
	pseudo_header->ieee_802_11.datapad = FALSE;
	pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
	pseudo_header->ieee_802_11.has_channel = TRUE;
	pseudo_header->ieee_802_11.channel = whdr.channel;
	pseudo_header->ieee_802_11.has_data_rate = TRUE;
	pseudo_header->ieee_802_11.data_rate = whdr.rate;
	pseudo_header->ieee_802_11.has_signal_percent = TRUE;
	pseudo_header->ieee_802_11.signal_percent = whdr.signal;

	/*
	 * We don't know they PHY, but we do have the data rate;
	 * try to guess the PHY based on the data rate and channel.
	 */
	if (RATE_IS_DSSS(pseudo_header->ieee_802_11.data_rate)) {
		/* 11b */
		pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11B;
		pseudo_header->ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
	} else if (RATE_IS_OFDM(pseudo_header->ieee_802_11.data_rate)) {
		/* 11a or 11g, depending on the band. */
		if (CHAN_IS_BG(pseudo_header->ieee_802_11.channel)) {
			/* 11g */
			pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11G;
			pseudo_header->ieee_802_11.phy_info.info_11g.has_mode = FALSE;
		} else {
			/* 11a */
			pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11A;
			pseudo_header->ieee_802_11.phy_info.info_11a.has_channel_type = FALSE;
			pseudo_header->ieee_802_11.phy_info.info_11a.has_turbo_type = FALSE;
		}
	}

	/* add back the header and don't forget the pad as well */
	*header_size = rsize + 8 + 4;

	return TRUE;
}

static const int wtap_encap[] = {
	-1,		/* WTAP_ENCAP_UNKNOWN -> unsupported */
	0x04,		/* WTAP_ENCAP_ETHERNET -> DL_ETHER */
	0x02,		/* WTAP_ENCAP_TOKEN_RING -> DL_TPR */
	-1,		/* WTAP_ENCAP_SLIP -> unsupported */
	-1,		/* WTAP_ENCAP_PPP -> unsupported */
	0x08,		/* WTAP_ENCAP_FDDI -> DL_FDDI */
	0x08,		/* WTAP_ENCAP_FDDI_BITSWAPPED -> DL_FDDI */
	-1,		/* WTAP_ENCAP_RAW_IP -> unsupported */
	-1,		/* WTAP_ENCAP_ARCNET -> unsupported */
	-1,		/* WTAP_ENCAP_ARCNET_LINUX -> unsupported */
	-1,		/* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
	-1,		/* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
	-1,		/* WTAP_ENCAP_LAPB -> unsupported*/
	0x12,		/* WTAP_ENCAP_ATM_PDUS -> DL_IPATM */
};
#define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])

/* Returns 0 if we could write the specified encapsulation type,
   an error indication otherwise. */
static int snoop_dump_can_write_encap(int encap)
{
	/* Per-packet encapsulations aren't supported. */
	if (encap == WTAP_ENCAP_PER_PACKET)
		return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;

	if (encap < 0 || (unsigned)encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
		return WTAP_ERR_UNWRITABLE_ENCAP;

	return 0;
}

/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
   failure */
static gboolean snoop_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
{
	struct snoop_hdr file_hdr;

	/* This is a snoop file */
	wdh->subtype_write = snoop_dump;

	/* Write the file header. */
	if (!wtap_dump_file_write(wdh, &snoop_magic, sizeof snoop_magic, err))
		return FALSE;

	/* current "snoop" format is 2 */
	file_hdr.version = g_htonl(2);
	file_hdr.network = g_htonl(wtap_encap[wdh->file_encap]);
	if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
		return FALSE;

	return TRUE;
}

/* Write a record for a packet to a dump file.
   Returns TRUE on success, FALSE on failure. */
static gboolean snoop_dump(wtap_dumper *wdh,
	const wtap_rec *rec,
	const guint8 *pd, int *err, gchar **err_info _U_)
{
	const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
	struct snooprec_hdr rec_hdr;
	int reclen;
	guint padlen;
	static const char zeroes[4] = {0};
	struct snoop_atm_hdr atm_hdr;
	int atm_hdrsize;

	/* We can only write packet records. */
	if (rec->rec_type != REC_TYPE_PACKET) {
		*err = WTAP_ERR_UNWRITABLE_REC_TYPE;
		return FALSE;
	}

	/*
	 * Make sure this packet doesn't have a link-layer type that
	 * differs from the one for the file.
	 */
	if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
		*err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
		return FALSE;
	}

	if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS)
		atm_hdrsize = sizeof (struct snoop_atm_hdr);
	else
		atm_hdrsize = 0;

	/* Record length = header length plus data length... */
	reclen = (int)sizeof rec_hdr + rec->rec_header.packet_header.caplen + atm_hdrsize;


	/* ... plus enough bytes to pad it to a 4-byte boundary. */
	padlen = WS_ROUNDUP_4(reclen) - reclen;
	reclen += padlen;

	/* Don't write anything we're not willing to read. */
	if (rec->rec_header.packet_header.caplen + atm_hdrsize > WTAP_MAX_PACKET_SIZE_STANDARD) {
		*err = WTAP_ERR_PACKET_TOO_LARGE;
		return FALSE;
	}

	rec_hdr.orig_len = g_htonl(rec->rec_header.packet_header.len + atm_hdrsize);
	rec_hdr.incl_len = g_htonl(rec->rec_header.packet_header.caplen + atm_hdrsize);
	rec_hdr.rec_len = g_htonl(reclen);
	rec_hdr.cum_drops = 0;
	rec_hdr.ts_sec = g_htonl(rec->ts.secs);
	rec_hdr.ts_usec = g_htonl(rec->ts.nsecs / 1000);
	if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
		return FALSE;

	if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS) {
		/*
		 * Write the ATM header.
		 */
		atm_hdr.flags =
		    (pseudo_header->atm.channel == 0) ? 0x80 : 0x00;
		switch (pseudo_header->atm.aal) {

		case AAL_SIGNALLING:
			/* Signalling AAL */
			atm_hdr.flags |= 0x06;
			break;

		case AAL_5:
			switch (pseudo_header->atm.type) {

			case TRAF_LANE:
				/* LANE */
				atm_hdr.flags |= 0x01;
				break;

			case TRAF_LLCMX:
				/* RFC 1483 LLC multiplexed traffic */
				atm_hdr.flags |= 0x02;
				break;

			case TRAF_ILMI:
				/* ILMI */
				atm_hdr.flags |= 0x05;
				break;
			}
			break;
		}
		atm_hdr.vpi = (guint8) pseudo_header->atm.vpi;
		atm_hdr.vci = g_htons(pseudo_header->atm.vci);
		if (!wtap_dump_file_write(wdh, &atm_hdr, sizeof atm_hdr, err))
			return FALSE;
	}

	if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
		return FALSE;

	/* Now write the padding. */
	if (!wtap_dump_file_write(wdh, zeroes, padlen, err))
		return FALSE;
	return TRUE;
}

static const struct supported_block_type snoop_blocks_supported[] = {
	/*
	 * We support packet blocks, with no comments or other options.
	 */
	{ WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
};

static const struct file_type_subtype_info snoop_info = {
	"Sun snoop", "snoop", "snoop", "cap",
	FALSE, BLOCKS_SUPPORTED(snoop_blocks_supported),
	snoop_dump_can_write_encap, snoop_dump_open, NULL
};

static const struct supported_block_type shomiti_blocks_supported[] = {
	/*
	 * We support packet blocks, with no comments or other options.
	 */
	{ WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
};

static const struct file_type_subtype_info shomiti_info = {
	"Shomiti/Finisar Surveyor", "shomiti", "cap", NULL,
	FALSE, BLOCKS_SUPPORTED(shomiti_blocks_supported),
	NULL, NULL, NULL
};

void register_snoop(void)
{
	snoop_file_type_subtype = wtap_register_file_type_subtype(&snoop_info);
	shomiti_file_type_subtype = wtap_register_file_type_subtype(&shomiti_info);

	/*
	 * Register names for backwards compatibility with the
	 * wtap_filetypes table in Lua.
	 */
	wtap_register_backwards_compatibility_lua_name("SNOOP",
	    snoop_file_type_subtype);
	wtap_register_backwards_compatibility_lua_name("SHOMITI",
	    shomiti_file_type_subtype);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */