A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
pcap-file.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2009 University of Washington
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Craig Dowell (craigdo@ee.washington.edu)
7 */
8
9#include "pcap-file.h"
10
11#include "ns3/assert.h"
12#include "ns3/buffer.h"
13#include "ns3/build-profile.h"
14#include "ns3/fatal-error.h"
15#include "ns3/fatal-impl.h"
16#include "ns3/header.h"
17#include "ns3/log.h"
18#include "ns3/packet.h"
19
20#include <cstring>
21#include <iostream>
22
23//
24// This file is used as part of the ns-3 test framework, so please refrain from
25// adding any ns-3 specific constructs such as Packet to this file.
26//
27
28namespace ns3
29{
30
31NS_LOG_COMPONENT_DEFINE("PcapFile");
32
33const uint32_t MAGIC = 0xa1b2c3d4; /**< Magic number identifying standard pcap file format */
34const uint32_t SWAPPED_MAGIC = 0xd4c3b2a1; /**< Looks this way if byte swapping is required */
35
37 0xa1b23c4d; /**< Magic number identifying nanosec resolution pcap file format */
38const uint32_t NS_SWAPPED_MAGIC = 0x4d3cb2a1; /**< Looks this way if byte swapping is required */
39
40const uint16_t VERSION_MAJOR = 2; /**< Major version of supported pcap file format */
41const uint16_t VERSION_MINOR = 4; /**< Minor version of supported pcap file format */
42
44 : m_file(),
45 m_swapMode(false),
46 m_nanosecMode(false)
47{
48 NS_LOG_FUNCTION(this);
50}
51
58
59bool
61{
62 NS_LOG_FUNCTION(this);
63 return m_file.fail();
64}
65
66bool
68{
69 NS_LOG_FUNCTION(this);
70 return m_file.eof();
71}
72
73void
75{
76 NS_LOG_FUNCTION(this);
77 m_file.clear();
78}
79
80void
82{
83 NS_LOG_FUNCTION(this);
84 m_file.close();
85}
86
93
94uint16_t
100
101uint16_t
107
114
121
128
135
136bool
138{
139 NS_LOG_FUNCTION(this);
140 return m_swapMode;
141}
142
143bool
145{
146 NS_LOG_FUNCTION(this);
147 return m_nanosecMode;
148}
149
150uint8_t
151PcapFile::Swap(uint8_t val)
152{
153 NS_LOG_FUNCTION(this << static_cast<uint32_t>(val));
154 return val;
155}
156
157uint16_t
158PcapFile::Swap(uint16_t val)
159{
160 NS_LOG_FUNCTION(this << val);
161 return ((val >> 8) & 0x00ff) | ((val << 8) & 0xff00);
162}
163
166{
167 NS_LOG_FUNCTION(this << val);
168 return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) |
169 ((val << 24) & 0xff000000);
170}
171
172void
174{
175 NS_LOG_FUNCTION(this << from << to);
176 to->m_magicNumber = Swap(from->m_magicNumber);
179 to->m_zone = Swap(uint32_t(from->m_zone));
180 to->m_sigFigs = Swap(from->m_sigFigs);
181 to->m_snapLen = Swap(from->m_snapLen);
182 to->m_type = Swap(from->m_type);
183}
184
185void
187{
188 NS_LOG_FUNCTION(this << from << to);
189 to->m_tsSec = Swap(from->m_tsSec);
190 to->m_tsUsec = Swap(from->m_tsUsec);
191 to->m_inclLen = Swap(from->m_inclLen);
192 to->m_origLen = Swap(from->m_origLen);
193}
194
195void
197{
198 NS_LOG_FUNCTION(this);
199 //
200 // If we're initializing the file, we need to write the pcap file header
201 // at the start of the file.
202 //
203 m_file.seekp(0, std::ios::beg);
204
205 //
206 // We have the ability to write out the pcap file header in a foreign endian
207 // format, so we need a temp place to swap on the way out.
208 //
209 PcapFileHeader header;
210
211 //
212 // the pointer headerOut selects either the swapped or non-swapped version of
213 // the pcap file header.
214 //
215 PcapFileHeader* headerOut = nullptr;
216
217 if (!m_swapMode)
218 {
219 headerOut = &m_fileHeader;
220 }
221 else
222 {
223 Swap(&m_fileHeader, &header);
224 headerOut = &header;
225 }
226
227 //
228 // Watch out for memory alignment differences between machines, so write
229 // them all individually.
230 //
231 m_file.write((const char*)&headerOut->m_magicNumber, sizeof(headerOut->m_magicNumber));
232 m_file.write((const char*)&headerOut->m_versionMajor, sizeof(headerOut->m_versionMajor));
233 m_file.write((const char*)&headerOut->m_versionMinor, sizeof(headerOut->m_versionMinor));
234 m_file.write((const char*)&headerOut->m_zone, sizeof(headerOut->m_zone));
235 m_file.write((const char*)&headerOut->m_sigFigs, sizeof(headerOut->m_sigFigs));
236 m_file.write((const char*)&headerOut->m_snapLen, sizeof(headerOut->m_snapLen));
237 m_file.write((const char*)&headerOut->m_type, sizeof(headerOut->m_type));
238}
239
240void
242{
243 NS_LOG_FUNCTION(this);
244 //
245 // Pcap file header is always at the start of the file
246 //
247 m_file.seekg(0, std::ios::beg);
248
249 //
250 // Watch out for memory alignment differences between machines, so read
251 // them all individually.
252 //
256 m_file.read((char*)&m_fileHeader.m_zone, sizeof(m_fileHeader.m_zone));
257 m_file.read((char*)&m_fileHeader.m_sigFigs, sizeof(m_fileHeader.m_sigFigs));
258 m_file.read((char*)&m_fileHeader.m_snapLen, sizeof(m_fileHeader.m_snapLen));
259 m_file.read((char*)&m_fileHeader.m_type, sizeof(m_fileHeader.m_type));
260
261 if (m_file.fail())
262 {
263 return;
264 }
265
266 //
267 // There are four possible magic numbers that can be there. Normal and byte
268 // swapped versions of the standard magic number, and normal and byte swapped
269 // versions of the magic number indicating nanosecond resolution timestamps.
270 //
273 {
274 m_file.setstate(std::ios::failbit);
275 }
276
277 //
278 // If the magic number is swapped, then we can assume that everything else we read
279 // is swapped.
280 //
283
284 if (m_swapMode)
285 {
287 }
288
289 //
290 // Timestamps can either be microsecond or nanosecond
291 //
294
295 //
296 // We only deal with one version of the pcap file format.
297 //
300 {
301 m_file.setstate(std::ios::failbit);
302 }
303
304 //
305 // A quick test of reasonablness for the time zone offset corresponding to
306 // a real place on the planet.
307 //
308 if (m_fileHeader.m_zone < -12 || m_fileHeader.m_zone > 12)
309 {
310 m_file.setstate(std::ios::failbit);
311 }
312
313 if (m_file.fail())
314 {
315 m_file.close();
316 }
317}
318
319void
320PcapFile::Open(const std::string& filename, std::ios::openmode mode)
321{
322 NS_LOG_FUNCTION(this << filename << mode);
323 NS_ASSERT((mode & std::ios::app) == 0);
324 NS_ASSERT(!m_file.fail());
325 //
326 // All pcap files are binary files, so we just do this automatically.
327 //
328 mode |= std::ios::binary;
329
330 m_filename = filename;
331 m_file.open(filename, mode);
332 if (mode & std::ios::in)
333 {
334 // will set the fail bit if file header is invalid.
336 }
337}
338
339void
341 uint32_t snapLen,
342 int32_t timeZoneCorrection,
343 bool swapMode,
344 bool nanosecMode)
345{
346 NS_LOG_FUNCTION(this << dataLinkType << snapLen << timeZoneCorrection << swapMode);
347
348 //
349 // Initialize the magic number and nanosecond mode flag
350 //
351 m_nanosecMode = nanosecMode;
352 if (nanosecMode)
353 {
355 }
356 else
357 {
359 }
360
361 //
362 // Initialize remainder of the in-memory file header.
363 //
366 m_fileHeader.m_zone = timeZoneCorrection;
368 m_fileHeader.m_snapLen = snapLen;
369 m_fileHeader.m_type = dataLinkType;
370
371 //
372 // We use pcap files for regression testing. We do byte-for-byte comparisons
373 // in those tests to determine pass or fail. If we allow big endian systems
374 // to write big endian headers, they will end up byte-swapped and the
375 // regression tests will fail. Until we get rid of the regression tests, we
376 // have to pick an endianness and stick with it. The precedent is little
377 // endian, so we set swap mode if required to pick little endian.
378 //
379 // We do want to allow a user or test suite to enable swapmode irrespective
380 // of what we decide here, so we allow setting swapmode from formal parameter
381 // as well.
382 //
383 // So, determine the endianness of the running system.
384 //
385 union {
386 uint32_t a;
387 uint8_t b[4];
388 } u;
389
390 u.a = 1;
391 bool bigEndian = u.b[3];
392
393 //
394 // And set swap mode if requested or we are on a big-endian system.
395 //
396 m_swapMode = swapMode || bigEndian;
397
399}
400
403{
404 NS_LOG_FUNCTION(this << tsSec << tsUsec << totalLen);
405 NS_ASSERT(m_file.good());
406
407 uint32_t inclLen = totalLen > m_fileHeader.m_snapLen ? m_fileHeader.m_snapLen : totalLen;
408
409 PcapRecordHeader header;
410 header.m_tsSec = tsSec;
411 header.m_tsUsec = tsUsec;
412 header.m_inclLen = inclLen;
413 header.m_origLen = totalLen;
414
415 if (m_swapMode)
416 {
417 Swap(&header, &header);
418 }
419
420 //
421 // Watch out for memory alignment differences between machines, so write
422 // them all individually.
423 //
424 m_file.write((const char*)&header.m_tsSec, sizeof(header.m_tsSec));
425 m_file.write((const char*)&header.m_tsUsec, sizeof(header.m_tsUsec));
426 m_file.write((const char*)&header.m_inclLen, sizeof(header.m_inclLen));
427 m_file.write((const char*)&header.m_origLen, sizeof(header.m_origLen));
428 NS_BUILD_DEBUG(m_file.flush());
429 return inclLen;
430}
431
432void
433PcapFile::Write(uint32_t tsSec, uint32_t tsUsec, const uint8_t* const data, uint32_t totalLen)
434{
435 NS_LOG_FUNCTION(this << tsSec << tsUsec << &data << totalLen);
436 uint32_t inclLen = WritePacketHeader(tsSec, tsUsec, totalLen);
437 m_file.write((const char*)data, inclLen);
438 NS_BUILD_DEBUG(m_file.flush());
439}
440
441void
443{
444 NS_LOG_FUNCTION(this << tsSec << tsUsec << p);
445 uint32_t inclLen = WritePacketHeader(tsSec, tsUsec, p->GetSize());
446 p->CopyData(&m_file, inclLen);
447 NS_BUILD_DEBUG(m_file.flush());
448}
449
450void
452{
453 NS_LOG_FUNCTION(this << tsSec << tsUsec << &header << p);
454 uint32_t headerSize = header.GetSerializedSize();
455 uint32_t totalSize = headerSize + p->GetSize();
456 uint32_t inclLen = WritePacketHeader(tsSec, tsUsec, totalSize);
457
458 Buffer headerBuffer;
459 headerBuffer.AddAtStart(headerSize);
460 header.Serialize(headerBuffer.Begin());
461 uint32_t toCopy = std::min(headerSize, inclLen);
462 headerBuffer.CopyData(&m_file, toCopy);
463 inclLen -= toCopy;
464 p->CopyData(&m_file, inclLen);
465}
466
467void
468PcapFile::Read(uint8_t* const data,
469 uint32_t maxBytes,
470 uint32_t& tsSec,
471 uint32_t& tsUsec,
472 uint32_t& inclLen,
473 uint32_t& origLen,
474 uint32_t& readLen)
475{
476 NS_LOG_FUNCTION(this << &data << maxBytes << tsSec << tsUsec << inclLen << origLen << readLen);
477 NS_ASSERT(m_file.good());
478
479 PcapRecordHeader header;
480
481 //
482 // Watch out for memory alignment differences between machines, so read
483 // them all individually.
484 //
485 m_file.read((char*)&header.m_tsSec, sizeof(header.m_tsSec));
486 m_file.read((char*)&header.m_tsUsec, sizeof(header.m_tsUsec));
487 m_file.read((char*)&header.m_inclLen, sizeof(header.m_inclLen));
488 m_file.read((char*)&header.m_origLen, sizeof(header.m_origLen));
489
490 if (m_file.fail())
491 {
492 return;
493 }
494
495 if (m_swapMode)
496 {
497 Swap(&header, &header);
498 }
499
500 tsSec = header.m_tsSec;
501 tsUsec = header.m_tsUsec;
502 inclLen = header.m_inclLen;
503 origLen = header.m_origLen;
504
505 //
506 // We don't always want to force the client to keep a maximum length buffer
507 // around so we allow her to specify a minimum number of bytes to read.
508 // Usually 64 bytes is enough information to print all of the headers, so
509 // it isn't typically necessary to read all thousand bytes of an echo packet,
510 // for example, to figure out what is going on.
511 //
512 readLen = maxBytes < header.m_inclLen ? maxBytes : header.m_inclLen;
513 m_file.read((char*)data, readLen);
514
515 //
516 // To keep the file pointer pointed in the right place, however, we always
517 // need to account for the entire packet as stored originally.
518 //
519 if (readLen < header.m_inclLen)
520 {
521 m_file.seekg(header.m_inclLen - readLen, std::ios::cur);
522 }
523}
524
525bool
526PcapFile::Diff(const std::string& f1,
527 const std::string& f2,
528 uint32_t& sec,
529 uint32_t& usec,
530 uint32_t& packets,
531 uint32_t snapLen)
532{
533 NS_LOG_FUNCTION(f1 << f2 << sec << usec << snapLen);
534 PcapFile pcap1;
535 PcapFile pcap2;
536 pcap1.Open(f1, std::ios::in);
537 pcap2.Open(f2, std::ios::in);
538 bool bad = pcap1.Fail() || pcap2.Fail();
539 if (bad)
540 {
541 return true;
542 }
543
544 auto data1 = new uint8_t[snapLen]();
545 auto data2 = new uint8_t[snapLen]();
546 uint32_t tsSec1 = 0;
547 uint32_t tsSec2 = 0;
548 uint32_t tsUsec1 = 0;
549 uint32_t tsUsec2 = 0;
550 uint32_t inclLen1 = 0;
551 uint32_t inclLen2 = 0;
552 uint32_t origLen1 = 0;
553 uint32_t origLen2 = 0;
554 uint32_t readLen1 = 0;
555 uint32_t readLen2 = 0;
556 bool diff = false;
557
558 while (!pcap1.Eof() && !pcap2.Eof())
559 {
560 pcap1.Read(data1, snapLen, tsSec1, tsUsec1, inclLen1, origLen1, readLen1);
561 pcap2.Read(data2, snapLen, tsSec2, tsUsec2, inclLen2, origLen2, readLen2);
562
563 bool same = pcap1.Fail() == pcap2.Fail();
564 if (!same)
565 {
566 diff = true;
567 break;
568 }
569 if (pcap1.Eof())
570 {
571 break;
572 }
573
574 ++packets;
575
576 if (tsSec1 != tsSec2 || tsUsec1 != tsUsec2)
577 {
578 diff = true; // Next packet timestamps do not match
579 break;
580 }
581
582 if (readLen1 != readLen2)
583 {
584 diff = true; // Packet lengths do not match
585 break;
586 }
587
588 if (std::memcmp(data1, data2, readLen1) != 0)
589 {
590 diff = true; // Packet data do not match
591 break;
592 }
593 }
594 sec = tsSec1;
595 usec = tsUsec1;
596
597 bad = pcap1.Fail() || pcap2.Fail();
598 bool eof = pcap1.Eof() && pcap2.Eof();
599 if (bad && !eof)
600 {
601 diff = true;
602 }
603
604 delete[] data1;
605 delete[] data2;
606
607 return diff;
608}
609
610} // namespace ns3
automatically resized byte buffer
Definition buffer.h:83
void CopyData(std::ostream *os, uint32_t size) const
Copy the specified amount of data from the buffer to the given output stream.
Definition buffer.cc:702
void AddAtStart(uint32_t start)
Definition buffer.cc:303
Buffer::Iterator Begin() const
Definition buffer.h:1063
Protocol header serialization and deserialization.
Definition header.h:33
virtual uint32_t GetSerializedSize() const =0
virtual void Serialize(Buffer::Iterator start) const =0
A class representing a pcap file.
Definition pcap-file.h:32
bool IsNanoSecMode()
Get the nanosecond mode of the file.
Definition pcap-file.cc:144
void Close()
Close the underlying file.
Definition pcap-file.cc:81
static bool Diff(const std::string &f1, const std::string &f2, uint32_t &sec, uint32_t &usec, uint32_t &packets, uint32_t snapLen=SNAPLEN_DEFAULT)
Compare two PCAP files packet-by-packet.
Definition pcap-file.cc:526
void Open(const std::string &filename, std::ios::openmode mode)
Create a new pcap file or open an existing pcap file.
Definition pcap-file.cc:320
uint32_t GetDataLinkType()
Returns the data link type field of the pcap file as defined by the network field in the pcap global ...
Definition pcap-file.cc:130
bool m_swapMode
swap mode
Definition pcap-file.h:368
void Read(uint8_t *const data, uint32_t maxBytes, uint32_t &tsSec, uint32_t &tsUsec, uint32_t &inclLen, uint32_t &origLen, uint32_t &readLen)
Read next packet from file.
Definition pcap-file.cc:468
uint32_t GetMagic()
Returns the magic number of the pcap file as defined by the magic_number field in the pcap global hea...
Definition pcap-file.cc:88
uint16_t GetVersionMajor()
Returns the major version of the pcap file as defined by the version_major field in the pcap global h...
Definition pcap-file.cc:95
std::string m_filename
file name
Definition pcap-file.h:365
uint16_t GetVersionMinor()
Returns the minor version of the pcap file as defined by the version_minor field in the pcap global h...
Definition pcap-file.cc:102
PcapFileHeader m_fileHeader
file header
Definition pcap-file.h:367
void Clear()
Clear all state bits of the underlying iostream.
Definition pcap-file.cc:74
uint32_t WritePacketHeader(uint32_t tsSec, uint32_t tsUsec, uint32_t totalLen)
Write a Pcap packet header.
Definition pcap-file.cc:402
void Init(uint32_t dataLinkType, uint32_t snapLen=SNAPLEN_DEFAULT, int32_t timeZoneCorrection=ZONE_DEFAULT, bool swapMode=false, bool nanosecMode=false)
Initialize the pcap file associated with this object.
Definition pcap-file.cc:340
void Write(uint32_t tsSec, uint32_t tsUsec, const uint8_t *const data, uint32_t totalLen)
Write next packet to file.
Definition pcap-file.cc:433
bool Eof() const
Definition pcap-file.cc:67
bool Fail() const
Definition pcap-file.cc:60
uint32_t GetSnapLen()
Returns the max length of saved packets field of the pcap file as defined by the snaplen field in the...
Definition pcap-file.cc:123
bool GetSwapMode()
Get the swap mode of the file.
Definition pcap-file.cc:137
bool m_nanosecMode
nanosecond timestamp mode
Definition pcap-file.h:369
int32_t GetTimeZoneOffset()
Returns the time zone offset of the pcap file as defined by the thiszone field in the pcap global hea...
Definition pcap-file.cc:109
std::fstream m_file
file stream
Definition pcap-file.h:366
void ReadAndVerifyFileHeader()
Read and verify a Pcap file header.
Definition pcap-file.cc:241
void WriteFileHeader()
Write a Pcap file header.
Definition pcap-file.cc:196
uint32_t GetSigFigs()
Returns the accuracy of timestamps field of the pcap file as defined by the sigfigs field in the pcap...
Definition pcap-file.cc:116
uint8_t Swap(uint8_t val)
Swap a value byte order.
Definition pcap-file.cc:151
Smart pointer class similar to boost::intrusive_ptr.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_BUILD_DEBUG(code)
Execute a code snippet in debug builds.
void UnregisterStream(std::ostream *stream)
Unregister a stream for flushing on abnormal exit.
void RegisterStream(std::ostream *stream)
Register a stream to be flushed on abnormal exit.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
Every class exported by the ns3 library is enclosed in the ns3 namespace.
const uint32_t MAGIC
Magic number identifying standard pcap file format.
Definition pcap-file.cc:33
const uint32_t NS_SWAPPED_MAGIC
Looks this way if byte swapping is required.
Definition pcap-file.cc:38
const uint32_t SWAPPED_MAGIC
Looks this way if byte swapping is required.
Definition pcap-file.cc:34
const uint16_t VERSION_MINOR
Minor version of supported pcap file format.
Definition pcap-file.cc:41
const uint32_t NS_MAGIC
Magic number identifying nanosec resolution pcap file format.
Definition pcap-file.cc:36
const uint16_t VERSION_MAJOR
Major version of supported pcap file format.
Definition pcap-file.cc:40
uint8_t data[writeSize]
uint32_t m_magicNumber
Magic number identifying this as a pcap file.
Definition pcap-file.h:290
uint32_t m_sigFigs
Unused by pretty much everybody.
Definition pcap-file.h:296
int32_t m_zone
Time zone correction to be applied to timestamps of packets.
Definition pcap-file.h:295
uint16_t m_versionMajor
Major version identifying the version of pcap used in this file.
Definition pcap-file.h:292
uint32_t m_type
Data link type of packet data.
Definition pcap-file.h:298
uint32_t m_snapLen
Maximum length of packet data stored in records.
Definition pcap-file.h:297
uint16_t m_versionMinor
Minor version identifying the version of pcap used in this file.
Definition pcap-file.h:294
uint32_t m_inclLen
number of octets of packet saved in file
Definition pcap-file.h:308
uint32_t m_tsUsec
microseconds part of timestamp (nsecs for PCAP_NSEC_MAGIC)
Definition pcap-file.h:307
uint32_t m_tsSec
seconds part of timestamp
Definition pcap-file.h:306
uint32_t m_origLen
actual length of original packet
Definition pcap-file.h:309