1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Vincent Perot - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.internal
.pcap
.core
.trace
;
15 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
17 import java
.io
.Closeable
;
18 import java
.io
.IOException
;
19 import java
.nio
.ByteBuffer
;
20 import java
.nio
.ByteOrder
;
21 import java
.nio
.channels
.SeekableByteChannel
;
22 import java
.nio
.file
.Files
;
23 import java
.nio
.file
.Path
;
24 import java
.util
.TreeMap
;
26 import org
.eclipse
.jdt
.annotation
.Nullable
;
27 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.packet
.BadPacketException
;
28 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.protocol
.pcap
.PcapPacket
;
29 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.util
.ConversionHelper
;
30 import org
.eclipse
.tracecompass
.internal
.pcap
.core
.util
.PcapTimestampScale
;
33 * Class that allows the interaction with a pcap file.
35 * @author Vincent Perot
37 public class PcapFile
implements Closeable
{
39 // TODO add pcapng support.
40 // TODO Make parsing faster by buffering the data.
42 private final Path fPcapFilePath
;
43 private final ByteOrder fByteOrder
;
44 private final SeekableByteChannel fFileChannel
;
45 private final PcapTimestampScale fTimestampPrecision
;
47 private final int fMajorVersion
;
48 private final int fMinorVersion
;
49 private final long fTimeAccuracy
;
50 private final long fTimeZoneCorrection
;
51 private final long fSnapshotLength
;
52 private final long fDataLinkType
;
54 private final TreeMap
<Long
, Long
> fFileIndex
;
56 private long fCurrentRank
;
57 private long fTotalNumberPackets
;
60 * Constructor of the PcapFile Class.
63 * The path to the pcap file.
65 * @throws BadPcapFileException
66 * Thrown if the Pcap File is not valid.
68 * Thrown if there is an IO error while reading the file.
70 public PcapFile(Path filePath
) throws BadPcapFileException
, IOException
{
72 fFileIndex
= new TreeMap
<>();
74 fTotalNumberPackets
= -1;
75 fPcapFilePath
= filePath
;
77 // Check file validity
78 if (Files
.notExists(fPcapFilePath
) || !Files
.isRegularFile(fPcapFilePath
) ||
79 Files
.size(fPcapFilePath
) < PcapFileValues
.GLOBAL_HEADER_SIZE
) {
80 throw new BadPcapFileException("Bad Pcap File."); //$NON-NLS-1$
83 if (!Files
.isReadable(fPcapFilePath
)) {
84 throw new BadPcapFileException("File is not readable."); //$NON-NLS-1$
87 // File is not empty. Try to open.
88 fFileChannel
= checkNotNull(Files
.newByteChannel(fPcapFilePath
));
90 // Parse the global header.
91 // Read the magic number (4 bytes) from the input stream
92 // and determine the mode (big endian or little endian)
93 ByteBuffer globalHeader
= ByteBuffer
.allocate(PcapFileValues
.GLOBAL_HEADER_SIZE
);
95 fFileChannel
.read(globalHeader
);
97 int magicNumber
= globalHeader
.getInt();
99 switch (magicNumber
) {
100 case PcapFileValues
.MAGIC_BIG_ENDIAN_MICRO
: // file is big endian
101 fByteOrder
= ByteOrder
.BIG_ENDIAN
;
102 fTimestampPrecision
= PcapTimestampScale
.MICROSECOND
;
104 case PcapFileValues
.MAGIC_LITTLE_ENDIAN_MICRO
: // file is little endian
105 fByteOrder
= ByteOrder
.LITTLE_ENDIAN
;
106 fTimestampPrecision
= PcapTimestampScale
.MICROSECOND
;
108 case PcapFileValues
.MAGIC_BIG_ENDIAN_NANO
: // file is big endian
109 fByteOrder
= ByteOrder
.BIG_ENDIAN
;
110 fTimestampPrecision
= PcapTimestampScale
.NANOSECOND
;
112 case PcapFileValues
.MAGIC_LITTLE_ENDIAN_NANO
: // file is little endian
113 fByteOrder
= ByteOrder
.LITTLE_ENDIAN
;
114 fTimestampPrecision
= PcapTimestampScale
.NANOSECOND
;
118 throw new BadPcapFileException(String
.format("%08x", magicNumber
) + " is not a known magic number."); //$NON-NLS-1$ //$NON-NLS-2$
121 // Put the rest of the buffer in file endian.
122 globalHeader
.order(fByteOrder
);
124 // Initialization of global header fields.
125 fMajorVersion
= ConversionHelper
.unsignedShortToInt(globalHeader
.getShort());
126 fMinorVersion
= ConversionHelper
.unsignedShortToInt(globalHeader
.getShort());
127 fTimeAccuracy
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
128 fTimeZoneCorrection
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
129 fSnapshotLength
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
130 fDataLinkType
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
132 fFileIndex
.put(fCurrentRank
, fFileChannel
.position());
137 * Method that allows the parsing of a packet at the current position.
139 * @return The parsed Pcap Packet.
140 * @throws IOException
141 * Thrown when there is an error while reading the file.
142 * @throws BadPcapFileException
143 * Thrown when a packet header is invalid.
144 * @throws BadPacketException
145 * Thrown when the packet is erroneous.
147 public synchronized @Nullable PcapPacket
parseNextPacket() throws IOException
, BadPcapFileException
, BadPacketException
{
149 // Parse the packet header
150 if (fFileChannel
.size() - fFileChannel
.position() == 0) {
153 if (fFileChannel
.size() - fFileChannel
.position() < PcapFileValues
.PACKET_HEADER_SIZE
) {
154 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
157 ByteBuffer pcapPacketHeader
= ByteBuffer
.allocate(PcapFileValues
.PACKET_HEADER_SIZE
);
158 pcapPacketHeader
.clear();
159 pcapPacketHeader
.order(fByteOrder
);
161 fFileChannel
.read(pcapPacketHeader
);
163 pcapPacketHeader
.flip();
164 pcapPacketHeader
.position(PcapFileValues
.INCLUDED_LENGTH_POSITION
);
165 long includedPacketLength
= ConversionHelper
.unsignedIntToLong(pcapPacketHeader
.getInt());
167 if (fFileChannel
.size() - fFileChannel
.position() < includedPacketLength
) {
168 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
171 if (includedPacketLength
> Integer
.MAX_VALUE
) {
172 throw new BadPacketException("Packets that are bigger than 2^31-1 bytes are not supported."); //$NON-NLS-1$
175 ByteBuffer pcapPacketData
= ByteBuffer
.allocate((int) includedPacketLength
);
176 pcapPacketData
.clear();
177 pcapPacketHeader
.order(ByteOrder
.BIG_ENDIAN
); // Not really needed.
178 fFileChannel
.read(pcapPacketData
);
180 pcapPacketData
.flip();
182 fFileIndex
.put(++fCurrentRank
, fFileChannel
.position());
184 return new PcapPacket(this, null, pcapPacketHeader
, pcapPacketData
, fCurrentRank
- 1);
189 * Method that allows to skip a packet at the current position.
191 * @throws IOException
192 * Thrown when there is an error while reading the file.
193 * @throws BadPcapFileException
194 * Thrown when a packet header is invalid.
196 public synchronized void skipNextPacket() throws IOException
, BadPcapFileException
{
198 // Parse the packet header
199 if (fFileChannel
.size() - fFileChannel
.position() == 0) {
202 if (fFileChannel
.size() - fFileChannel
.position() < PcapFileValues
.PACKET_HEADER_SIZE
) {
203 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
206 ByteBuffer pcapPacketHeader
= ByteBuffer
.allocate(PcapFileValues
.PACKET_HEADER_SIZE
);
207 pcapPacketHeader
.clear();
208 pcapPacketHeader
.order(fByteOrder
);
210 fFileChannel
.read(pcapPacketHeader
);
212 pcapPacketHeader
.flip();
213 pcapPacketHeader
.position(PcapFileValues
.INCLUDED_LENGTH_POSITION
);
214 long includedPacketLength
= ConversionHelper
.unsignedIntToLong(pcapPacketHeader
.getInt());
216 if (fFileChannel
.size() - fFileChannel
.position() < includedPacketLength
) {
217 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
220 fFileChannel
.position(fFileChannel
.position() + includedPacketLength
);
222 fFileIndex
.put(++fCurrentRank
, fFileChannel
.position());
227 * Method that moves the position to the specified rank.
230 * The rank of the packet.
232 * @throws IOException
233 * Thrown when there is an error while reading the file.
234 * @throws BadPcapFileException
235 * Thrown when a packet header is invalid.
237 public synchronized void seekPacket(long rank
) throws IOException
, BadPcapFileException
{
241 throw new IllegalArgumentException();
244 Long positionInBytes
= fFileIndex
.get(rank
);
246 if (positionInBytes
!= null) {
247 // Index is known. Move to position.
248 fFileChannel
.position(positionInBytes
.longValue());
251 // Index is unknown. Find the corresponding position.
252 // Find closest index
253 fCurrentRank
= fFileIndex
.floorKey(rank
);
254 // skip until wanted packet is found
257 } while (fCurrentRank
!= rank
&& hasNextPacket());
262 * Method that indicates if there are packets remaining to read. It is an
263 * end of file indicator.
265 * @return Whether the pcap still has packets or not.
266 * @throws IOException
267 * If some IO error occurs.
269 public synchronized boolean hasNextPacket() throws IOException
{
270 return ((fFileChannel
.size() - fFileChannel
.position()) > 0);
274 * Getter method for the Byte Order of the file.
276 * @return The byte Order of the file.
278 public ByteOrder
getByteOrder() {
283 * Getter method for the Major Version of the file.
285 * @return The Major Version of the file.
287 public int getMajorVersion() {
288 return fMajorVersion
;
292 * Getter method for the Minor Version of the file.
294 * @return The Minor Version of the file.
296 public int getMinorVersion() {
297 return fMinorVersion
;
301 * Getter method for the time accuracy of the file.
303 * @return The time accuracy of the file.
305 public long getTimeAccuracy() {
306 return fTimeAccuracy
;
310 * Getter method for the time zone correction of the file.
312 * @return The time zone correction of the file.
314 public long getTimeZoneCorrection() {
315 return fTimeZoneCorrection
;
319 * Getter method for the snapshot length of the file.
321 * @return The snapshot length of the file.
323 public long getSnapLength() {
324 return fSnapshotLength
;
328 * Getter method for the datalink type of the file. This parameter is used
329 * to determine higher-level protocols (Ethernet, WLAN, SLL).
331 * @return The datalink type of the file.
333 public long getDataLinkType() {
334 return fDataLinkType
;
338 * Getter method for the path of the file.
340 * @return The path of the file.
342 public Path
getPath() {
343 return fPcapFilePath
;
347 * Method that returns the total number of packets in the file.
349 * @return The total number of packets.
350 * @throws IOException
351 * Thrown when some IO error occurs.
352 * @throws BadPcapFileException
353 * Thrown when a packet header is invalid.
355 public synchronized long getTotalNbPackets() throws IOException
, BadPcapFileException
{
356 if (fTotalNumberPackets
== -1) {
357 long rank
= fCurrentRank
;
358 fCurrentRank
= fFileIndex
.floorKey(rank
);
360 // skip until end of file.
361 while (hasNextPacket()) {
364 fTotalNumberPackets
= fCurrentRank
;
368 return fTotalNumberPackets
;
372 * Getter method that returns the current rank in the file (the packet
375 * @return The current rank.
377 public synchronized long getCurrentRank() {
382 * Getter method that returns the timestamp precision of the file.
384 * @return The the timestamp precision of the file.
386 public PcapTimestampScale
getTimestampPrecision() {
387 return fTimestampPrecision
;
391 public void close() throws IOException
{
392 fFileChannel
.close();