tmf: Initial commit of Pcap Parser
[deliverable/tracecompass.git] / org.eclipse.linuxtools.pcap.core / src / org / eclipse / linuxtools / pcap / core / trace / PcapFile.java
1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
3 *
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
8 *
9 * Contributors:
10 * Vincent Perot - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.linuxtools.pcap.core.trace;
14
15 import java.io.Closeable;
16 import java.io.File;
17 import java.io.FileInputStream;
18 import java.io.IOException;
19 import java.nio.ByteBuffer;
20 import java.nio.ByteOrder;
21 import java.nio.channels.FileChannel;
22 import java.util.TreeMap;
23
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.linuxtools.pcap.core.packet.BadPacketException;
27 import org.eclipse.linuxtools.pcap.core.protocol.pcap.PcapPacket;
28 import org.eclipse.linuxtools.pcap.core.util.ConversionHelper;
29 import org.eclipse.linuxtools.pcap.core.util.PcapTimestampScale;
30
31 /**
32 * Class that allows the interaction with a pcap file.
33 *
34 * @author Vincent Perot
35 */
36 public class PcapFile implements Closeable {
37
38 // TODO add pcapng support.
39 // TODO Make parsing faster by buffering the data.
40
41 private final String fPcapFilePath;
42 private final ByteOrder fByteOrder;
43 private final FileChannel fFileChannel;
44 private final FileInputStream fFileInputStream;
45 private final PcapTimestampScale fTimestampPrecision;
46
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;
53
54 private final TreeMap<Long, Long> fFileIndex;
55
56 private long fCurrentRank;
57 private long fTotalNumberPackets;
58
59 /**
60 * Constructor of the PcapFile Class.
61 *
62 * @param filePath
63 * The path to the pcap file.
64 *
65 * @throws BadPcapFileException
66 * Thrown if the Pcap File is not valid.
67 * @throws IOException
68 * Thrown if there is an IO error while reading the file.
69 */
70 public PcapFile(String filePath) throws BadPcapFileException, IOException {
71
72 fFileIndex = new TreeMap<>();
73 fCurrentRank = 0;
74 fTotalNumberPackets = -1;
75 fPcapFilePath = filePath;
76
77 // Check file validity
78 File pcapFile = new File(fPcapFilePath);
79 if ((!fPcapFilePath.endsWith(".cap") && !fPcapFilePath.endsWith(".pcap")) || //$NON-NLS-1$ //$NON-NLS-2$
80 !pcapFile.exists() || !pcapFile.isFile() || pcapFile.length() < PcapFileValues.GLOBAL_HEADER_SIZE) {
81 throw new BadPcapFileException("Bad Pcap File."); //$NON-NLS-1$
82 }
83
84 if (!pcapFile.canRead()) {
85 throw new IOException("File is not readable."); //$NON-NLS-1$
86 }
87
88 // File is not empty. Try to open.
89 fFileInputStream = new FileInputStream(fPcapFilePath);
90
91 @SuppressWarnings("null")
92 @NonNull FileChannel fileChannel = fFileInputStream.getChannel();
93 fFileChannel = fileChannel;
94
95 // Parse the global header.
96 // Read the magic number (4 bytes) from the input stream
97 // and determine the mode (big endian or little endian)
98 ByteBuffer globalHeader = ByteBuffer.allocate(PcapFileValues.GLOBAL_HEADER_SIZE);
99 globalHeader.clear();
100 fFileChannel.read(globalHeader);
101 globalHeader.flip();
102 int magicNumber = globalHeader.getInt();
103
104 @SuppressWarnings("null")
105 @NonNull ByteOrder be = ByteOrder.BIG_ENDIAN;
106 @SuppressWarnings("null")
107 @NonNull ByteOrder le = ByteOrder.LITTLE_ENDIAN;
108
109 switch (magicNumber) {
110 case PcapFileValues.MAGIC_BIG_ENDIAN_MICRO: // file is big endian
111 fByteOrder = be;
112 fTimestampPrecision = PcapTimestampScale.MICROSECOND;
113 System.out.println();
114 break;
115 case PcapFileValues.MAGIC_LITTLE_ENDIAN_MICRO: // file is little endian
116 fByteOrder = le;
117 fTimestampPrecision = PcapTimestampScale.MICROSECOND;
118 break;
119 case PcapFileValues.MAGIC_BIG_ENDIAN_NANO: // file is big endian
120 fByteOrder = be;
121 fTimestampPrecision = PcapTimestampScale.NANOSECOND;
122 break;
123 case PcapFileValues.MAGIC_LITTLE_ENDIAN_NANO: // file is little endian
124 fByteOrder = le;
125 fTimestampPrecision = PcapTimestampScale.NANOSECOND;
126 break;
127 default:
128 this.close();
129 throw new BadPcapFileException(String.format("%08x", magicNumber) + " is not a known magic number."); //$NON-NLS-1$ //$NON-NLS-2$
130 }
131
132 // Put the rest of the buffer in file endian.
133 globalHeader.order(fByteOrder);
134
135 // Initialization of global header fields.
136 fMajorVersion = ConversionHelper.unsignedShortToInt(globalHeader.getShort());
137 fMinorVersion = ConversionHelper.unsignedShortToInt(globalHeader.getShort());
138 fTimeAccuracy = ConversionHelper.unsignedIntToLong(globalHeader.getInt());
139 fTimeZoneCorrection = ConversionHelper.unsignedIntToLong(globalHeader.getInt());
140 fSnapshotLength = ConversionHelper.unsignedIntToLong(globalHeader.getInt());
141 fDataLinkType = ConversionHelper.unsignedIntToLong(globalHeader.getInt());
142
143 fFileIndex.put(fCurrentRank, fFileChannel.position());
144
145 }
146
147 /**
148 * Method that allows the parsing of a packet at the current position.
149 *
150 * @return The parsed Pcap Packet.
151 * @throws IOException
152 * Thrown when there is an error while reading the file.
153 * @throws BadPcapFileException
154 * Thrown when a packet header is invalid.
155 * @throws BadPacketException
156 * Thrown when the packet is erroneous.
157 */
158 public synchronized @Nullable PcapPacket parseNextPacket() throws IOException, BadPcapFileException, BadPacketException {
159
160 // Parse the packet header
161 if (fFileChannel.size() - fFileChannel.position() == 0) {
162 return null;
163 }
164 if (fFileChannel.size() - fFileChannel.position() < PcapFileValues.PACKET_HEADER_SIZE) {
165 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
166 }
167
168 ByteBuffer pcapPacketHeader = ByteBuffer.allocate(PcapFileValues.PACKET_HEADER_SIZE);
169 pcapPacketHeader.clear();
170 pcapPacketHeader.order(fByteOrder);
171
172 fFileChannel.read(pcapPacketHeader);
173
174 pcapPacketHeader.flip();
175 pcapPacketHeader.position(PcapFileValues.INCLUDED_LENGTH_POSITION);
176 long includedPacketLength = ConversionHelper.unsignedIntToLong(pcapPacketHeader.getInt());
177
178 if (fFileChannel.size() - fFileChannel.position() < includedPacketLength) {
179 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
180 }
181
182 if (includedPacketLength > Integer.MAX_VALUE) {
183 throw new BadPacketException("Packets that are bigger than 2^31-1 bytes are not supported."); //$NON-NLS-1$
184 }
185
186 ByteBuffer pcapPacketData = ByteBuffer.allocate((int) includedPacketLength);
187 pcapPacketData.clear();
188 pcapPacketHeader.order(ByteOrder.BIG_ENDIAN); // Not really needed.
189 fFileChannel.read(pcapPacketData);
190
191 pcapPacketData.flip();
192
193 fFileIndex.put(++fCurrentRank, fFileChannel.position());
194
195 return new PcapPacket(this, null, pcapPacketHeader, pcapPacketData, fCurrentRank - 1);
196
197 }
198
199 /**
200 * Method that allows to skip a packet at the current position.
201 *
202 * @throws IOException
203 * Thrown when there is an error while reading the file.
204 * @throws BadPcapFileException
205 * Thrown when a packet header is invalid.
206 */
207 public synchronized void skipNextPacket() throws IOException, BadPcapFileException {
208
209 // Parse the packet header
210 if (fFileChannel.size() - fFileChannel.position() == 0) {
211 return;
212 }
213 if (fFileChannel.size() - fFileChannel.position() < PcapFileValues.PACKET_HEADER_SIZE) {
214 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
215 }
216
217 ByteBuffer pcapPacketHeader = ByteBuffer.allocate(PcapFileValues.PACKET_HEADER_SIZE);
218 pcapPacketHeader.clear();
219 pcapPacketHeader.order(fByteOrder);
220
221 fFileChannel.read(pcapPacketHeader);
222
223 pcapPacketHeader.flip();
224 pcapPacketHeader.position(PcapFileValues.INCLUDED_LENGTH_POSITION);
225 long includedPacketLength = ConversionHelper.unsignedIntToLong(pcapPacketHeader.getInt());
226
227 if (fFileChannel.size() - fFileChannel.position() < includedPacketLength) {
228 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
229 }
230
231 fFileChannel.position(fFileChannel.position() + includedPacketLength);
232
233 fFileIndex.put(++fCurrentRank, fFileChannel.position());
234
235 }
236
237 /**
238 * Method that moves the position to the specified rank.
239 *
240 * @param rank
241 * The rank of the packet.
242 *
243 * @throws IOException
244 * Thrown when there is an error while reading the file.
245 * @throws BadPcapFileException
246 * Thrown when a packet header is invalid.
247 */
248 public synchronized void seekPacket(long rank) throws IOException, BadPcapFileException {
249
250 // Verify argument
251 if (rank < 0) {
252 throw new IllegalArgumentException();
253 }
254
255 Long positionInBytes = fFileIndex.get(rank);
256
257 if (positionInBytes != null) {
258 // Index is known. Move to position.
259 fFileChannel.position(positionInBytes.longValue());
260 fCurrentRank = rank;
261 } else {
262 // Index is unknown. Find the corresponding position.
263 // Find closest index
264 fCurrentRank = fFileIndex.floorKey(rank);
265 // skip until wanted packet is found
266 do {
267 skipNextPacket();
268 } while (fCurrentRank != rank && hasNextPacket());
269 }
270 }
271
272 /**
273 * Method that indicates if there are packets remaining to read. It is an
274 * end of file indicator.
275 *
276 * @return Whether the pcap still has packets or not.
277 * @throws IOException
278 * If some IO error occurs.
279 */
280 public synchronized boolean hasNextPacket() throws IOException {
281 return ((fFileChannel.size() - fFileChannel.position()) > 0);
282 }
283
284 /**
285 * Getter method for the Byte Order of the file.
286 *
287 * @return The byte Order of the file.
288 */
289 public ByteOrder getByteOrder() {
290 return fByteOrder;
291 }
292
293 /**
294 * Getter method for the Major Version of the file.
295 *
296 * @return The Major Version of the file.
297 */
298 public int getMajorVersion() {
299 return fMajorVersion;
300 }
301
302 /**
303 * Getter method for the Minor Version of the file.
304 *
305 * @return The Minor Version of the file.
306 */
307 public int getMinorVersion() {
308 return fMinorVersion;
309 }
310
311 /**
312 * Getter method for the time accuracy of the file.
313 *
314 * @return The time accuracy of the file.
315 */
316 public long getTimeAccuracy() {
317 return fTimeAccuracy;
318 }
319
320 /**
321 * Getter method for the time zone correction of the file.
322 *
323 * @return The time zone correction of the file.
324 */
325 public long getTimeZoneCorrection() {
326 return fTimeZoneCorrection;
327 }
328
329 /**
330 * Getter method for the snapshot length of the file.
331 *
332 * @return The snapshot length of the file.
333 */
334 public long getSnapLength() {
335 return fSnapshotLength;
336 }
337
338 /**
339 * Getter method for the datalink type of the file. This parameter is used
340 * to determine higher-level protocols (Ethernet, WLAN, SLL).
341 *
342 * @return The datalink type of the file.
343 */
344 public long getDataLinkType() {
345 return fDataLinkType;
346 }
347
348 /**
349 * Getter method for the path of the file.
350 *
351 * @return The path of the file.
352 */
353 public String getPath() {
354 return fPcapFilePath;
355 }
356
357 /**
358 * Method that returns the total number of packets in the file.
359 *
360 * @return The total number of packets.
361 * @throws IOException
362 * Thrown when some IO error occurs.
363 * @throws BadPcapFileException
364 * Thrown when a packet header is invalid.
365 */
366 public synchronized long getTotalNbPackets() throws IOException, BadPcapFileException {
367 if (fTotalNumberPackets == -1) {
368 long rank = fCurrentRank;
369 fCurrentRank = fFileIndex.floorKey(rank);
370
371 // skip until end of file.
372 while (hasNextPacket()) {
373 skipNextPacket();
374 }
375 fTotalNumberPackets = fCurrentRank;
376 fCurrentRank = rank;
377 seekPacket(rank);
378 }
379 return fTotalNumberPackets;
380 }
381
382 /**
383 * Getter method that returns the current rank in the file (the packet
384 * number).
385 *
386 * @return The current rank.
387 */
388 public synchronized long getCurrentRank() {
389 return fCurrentRank;
390 }
391
392 /**
393 * Getter method that returns the timestamp precision of the file.
394 *
395 * @return The the timestamp precision of the file.
396 */
397 public PcapTimestampScale getTimestampPrecision() {
398 return fTimestampPrecision;
399 }
400
401 @Override
402 public void close() throws IOException {
403 fFileChannel.close();
404 fFileInputStream.close();
405 }
406
407 }
This page took 0.038565 seconds and 5 git commands to generate.