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