A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
pcap-file.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2009 University of Washington
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Craig Dowell (craigdo@ee.washington.edu)
19  */
20 
21 #include <iostream>
22 #include <cstring>
23 #include "ns3/assert.h"
24 #include "ns3/packet.h"
25 #include "ns3/fatal-error.h"
26 #include "ns3/fatal-impl.h"
27 #include "ns3/header.h"
28 #include "ns3/buffer.h"
29 #include "pcap-file.h"
30 #include "ns3/log.h"
31 //
32 // This file is used as part of the ns-3 test framework, so please refrain from
33 // adding any ns-3 specific constructs such as Packet to this file.
34 //
35 
36 NS_LOG_COMPONENT_DEFINE ("PcapFile");
37 
38 namespace ns3 {
39 
40 const uint32_t MAGIC = 0xa1b2c3d4;
41 const uint32_t SWAPPED_MAGIC = 0xd4c3b2a1;
43 const uint32_t NS_MAGIC = 0xa1b23cd4;
44 const uint32_t NS_SWAPPED_MAGIC = 0xd43cb2a1;
46 const uint16_t VERSION_MAJOR = 2;
47 const uint16_t VERSION_MINOR = 4;
50  : m_file (),
51  m_swapMode (false)
52 {
53  NS_LOG_FUNCTION (this);
55 }
56 
58 {
59  NS_LOG_FUNCTION (this);
61  Close ();
62 }
63 
64 
65 bool
66 PcapFile::Fail (void) const
67 {
68  NS_LOG_FUNCTION (this);
69  return m_file.fail ();
70 }
71 bool
72 PcapFile::Eof (void) const
73 {
74  NS_LOG_FUNCTION (this);
75  return m_file.eof ();
76 }
77 void
79 {
80  NS_LOG_FUNCTION (this);
81  m_file.clear ();
82 }
83 
84 
85 void
87 {
88  NS_LOG_FUNCTION (this);
89  m_file.close ();
90 }
91 
92 uint32_t
94 {
95  NS_LOG_FUNCTION (this);
97 }
98 
99 uint16_t
101 {
102  NS_LOG_FUNCTION (this);
104 }
105 
106 uint16_t
108 {
109  NS_LOG_FUNCTION (this);
111 }
112 
113 int32_t
115 {
116  NS_LOG_FUNCTION (this);
117  return m_fileHeader.m_zone;
118 }
119 
120 uint32_t
122 {
123  NS_LOG_FUNCTION (this);
124  return m_fileHeader.m_sigFigs;
125 }
126 
127 uint32_t
129 {
130  NS_LOG_FUNCTION (this);
131  return m_fileHeader.m_snapLen;
132 }
133 
134 uint32_t
136 {
137  NS_LOG_FUNCTION (this);
138  return m_fileHeader.m_type;
139 }
140 
141 bool
143 {
144  NS_LOG_FUNCTION (this);
145  return m_swapMode;
146 }
147 
148 uint8_t
149 PcapFile::Swap (uint8_t val)
150 {
151  NS_LOG_FUNCTION (this << static_cast<uint32_t> (val));
152  return val;
153 }
154 
155 uint16_t
156 PcapFile::Swap (uint16_t val)
157 {
158  NS_LOG_FUNCTION (this << val);
159  return ((val >> 8) & 0x00ff) | ((val << 8) & 0xff00);
160 }
161 
162 uint32_t
163 PcapFile::Swap (uint32_t val)
164 {
165  NS_LOG_FUNCTION (this << val);
166  return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | ((val << 24) & 0xff000000);
167 }
168 
169 void
171 {
172  NS_LOG_FUNCTION (this << from << to);
173  to->m_magicNumber = Swap (from->m_magicNumber);
174  to->m_versionMajor = Swap (from->m_versionMajor);
175  to->m_versionMinor = Swap (from->m_versionMinor);
176  to->m_zone = Swap (uint32_t (from->m_zone));
177  to->m_sigFigs = Swap (from->m_sigFigs);
178  to->m_snapLen = Swap (from->m_snapLen);
179  to->m_type = Swap (from->m_type);
180 }
181 
182 void
184 {
185  NS_LOG_FUNCTION (this << from << to);
186  to->m_tsSec = Swap (from->m_tsSec);
187  to->m_tsUsec = Swap (from->m_tsUsec);
188  to->m_inclLen = Swap (from->m_inclLen);
189  to->m_origLen = Swap (from->m_origLen);
190 }
191 
192 void
194 {
195  NS_LOG_FUNCTION (this);
196  //
197  // If we're initializing the file, we need to write the pcap file header
198  // at the start of the file.
199  //
200  m_file.seekp (0, std::ios::beg);
201 
202  //
203  // We have the ability to write out the pcap file header in a foreign endian
204  // format, so we need a temp place to swap on the way out.
205  //
206  PcapFileHeader header;
207 
208  //
209  // the pointer headerOut selects either the swapped or non-swapped version of
210  // the pcap file header.
211  //
212  PcapFileHeader *headerOut = 0;
213 
214  if (m_swapMode == false)
215  {
216  headerOut = &m_fileHeader;
217  }
218  else
219  {
220  Swap (&m_fileHeader, &header);
221  headerOut = &header;
222  }
223 
224  //
225  // Watch out for memory alignment differences between machines, so write
226  // them all individually.
227  //
228  m_file.write ((const char *)&headerOut->m_magicNumber, sizeof(headerOut->m_magicNumber));
229  m_file.write ((const char *)&headerOut->m_versionMajor, sizeof(headerOut->m_versionMajor));
230  m_file.write ((const char *)&headerOut->m_versionMinor, sizeof(headerOut->m_versionMinor));
231  m_file.write ((const char *)&headerOut->m_zone, sizeof(headerOut->m_zone));
232  m_file.write ((const char *)&headerOut->m_sigFigs, sizeof(headerOut->m_sigFigs));
233  m_file.write ((const char *)&headerOut->m_snapLen, sizeof(headerOut->m_snapLen));
234  m_file.write ((const char *)&headerOut->m_type, sizeof(headerOut->m_type));
235 }
236 
237 void
239 {
240  NS_LOG_FUNCTION (this);
241  //
242  // Pcap file header is always at the start of the file
243  //
244  m_file.seekg (0, std::ios::beg);
245 
246  //
247  // Watch out for memory alignment differences between machines, so read
248  // them all individually.
249  //
253  m_file.read ((char *)&m_fileHeader.m_zone, sizeof(m_fileHeader.m_zone));
254  m_file.read ((char *)&m_fileHeader.m_sigFigs, sizeof(m_fileHeader.m_sigFigs));
255  m_file.read ((char *)&m_fileHeader.m_snapLen, sizeof(m_fileHeader.m_snapLen));
256  m_file.read ((char *)&m_fileHeader.m_type, sizeof(m_fileHeader.m_type));
257 
258  if (m_file.fail ())
259  {
260  return;
261  }
262 
263  //
264  // There are four possible magic numbers that can be there. Normal and byte
265  // swapped versions of the standard magic number, and normal and byte swapped
266  // versions of the magic number indicating nanosecond resolution timestamps.
267  //
270  {
271  m_file.setstate (std::ios::failbit);
272  }
273 
274  //
275  // If the magic number is swapped, then we can assume that everything else we read
276  // is swapped.
277  //
279  || m_fileHeader.m_magicNumber == NS_SWAPPED_MAGIC) ? true : false;
280 
281  if (m_swapMode)
282  {
284  }
285 
286  //
287  // We only deal with one version of the pcap file format.
288  //
290  {
291  m_file.setstate (std::ios::failbit);
292  }
293 
294  //
295  // A quick test of reasonablness for the time zone offset corresponding to
296  // a real place on the planet.
297  //
298  if (m_fileHeader.m_zone < -12 || m_fileHeader.m_zone > 12)
299  {
300  m_file.setstate (std::ios::failbit);
301  }
302 
303  if (m_file.fail ())
304  {
305  m_file.close ();
306  }
307 }
308 
309 void
310 PcapFile::Open (std::string const &filename, std::ios::openmode mode)
311 {
312  NS_LOG_FUNCTION (this << filename << mode);
313  NS_ASSERT ((mode & std::ios::app) == 0);
314  NS_ASSERT (!m_file.fail ());
315  //
316  // All pcap files are binary files, so we just do this automatically.
317  //
318  mode |= std::ios::binary;
319 
320  m_file.open (filename.c_str (), mode);
321  if (mode & std::ios::in)
322  {
323  // will set the fail bit if file header is invalid.
325  }
326 }
327 
328 void
329 PcapFile::Init (uint32_t dataLinkType, uint32_t snapLen, int32_t timeZoneCorrection, bool swapMode)
330 {
331  NS_LOG_FUNCTION (this << dataLinkType << snapLen << timeZoneCorrection << swapMode);
332  //
333  // Initialize the in-memory file header.
334  //
338  m_fileHeader.m_zone = timeZoneCorrection;
340  m_fileHeader.m_snapLen = snapLen;
341  m_fileHeader.m_type = dataLinkType;
342 
343  //
344  // We use pcap files for regression testing. We do byte-for-byte comparisons
345  // in those tests to determine pass or fail. If we allow big endian systems
346  // to write big endian headers, they will end up byte-swapped and the
347  // regression tests will fail. Until we get rid of the regression tests, we
348  // have to pick an endianness and stick with it. The precedent is little
349  // endian, so we set swap mode if required to pick little endian.
350  //
351  // We do want to allow a user or test suite to enable swapmode irrespective
352  // of what we decide here, so we allow setting swapmode from formal parameter
353  // as well.
354  //
355  // So, determine the endianness of the running system.
356  //
357  union {
358  uint32_t a;
359  uint8_t b[4];
360  } u;
361 
362  u.a = 1;
363  bool bigEndian = u.b[3];
364 
365  //
366  // And set swap mode if requested or we are on a big-endian system.
367  //
368  m_swapMode = swapMode | bigEndian;
369 
370  WriteFileHeader ();
371 }
372 
373 uint32_t
374 PcapFile::WritePacketHeader (uint32_t tsSec, uint32_t tsUsec, uint32_t totalLen)
375 {
376  NS_LOG_FUNCTION (this << tsSec << tsUsec << totalLen);
377  NS_ASSERT (m_file.good ());
378 
379  uint32_t inclLen = totalLen > m_fileHeader.m_snapLen ? m_fileHeader.m_snapLen : totalLen;
380 
381  PcapRecordHeader header;
382  header.m_tsSec = tsSec;
383  header.m_tsUsec = tsUsec;
384  header.m_inclLen = inclLen;
385  header.m_origLen = totalLen;
386 
387  if (m_swapMode)
388  {
389  Swap (&header, &header);
390  }
391 
392  //
393  // Watch out for memory alignment differences between machines, so write
394  // them all individually.
395  //
396  m_file.write ((const char *)&header.m_tsSec, sizeof(header.m_tsSec));
397  m_file.write ((const char *)&header.m_tsUsec, sizeof(header.m_tsUsec));
398  m_file.write ((const char *)&header.m_inclLen, sizeof(header.m_inclLen));
399  m_file.write ((const char *)&header.m_origLen, sizeof(header.m_origLen));
400  return inclLen;
401 }
402 
403 void
404 PcapFile::Write (uint32_t tsSec, uint32_t tsUsec, uint8_t const * const data, uint32_t totalLen)
405 {
406  NS_LOG_FUNCTION (this << tsSec << tsUsec << &data << totalLen);
407  uint32_t inclLen = WritePacketHeader (tsSec, tsUsec, totalLen);
408  m_file.write ((const char *)data, inclLen);
409 }
410 
411 void
412 PcapFile::Write (uint32_t tsSec, uint32_t tsUsec, Ptr<const Packet> p)
413 {
414  NS_LOG_FUNCTION (this << tsSec << tsUsec << p);
415  uint32_t inclLen = WritePacketHeader (tsSec, tsUsec, p->GetSize ());
416  p->CopyData (&m_file, inclLen);
417 }
418 
419 void
420 PcapFile::Write (uint32_t tsSec, uint32_t tsUsec, Header &header, Ptr<const Packet> p)
421 {
422  NS_LOG_FUNCTION (this << tsSec << tsUsec << &header << p);
423  uint32_t headerSize = header.GetSerializedSize ();
424  uint32_t totalSize = headerSize + p->GetSize ();
425  uint32_t inclLen = WritePacketHeader (tsSec, tsUsec, totalSize);
426 
427  Buffer headerBuffer;
428  headerBuffer.AddAtStart (headerSize);
429  header.Serialize (headerBuffer.Begin ());
430  uint32_t toCopy = std::min (headerSize, inclLen);
431  headerBuffer.CopyData (&m_file, toCopy);
432  inclLen -= toCopy;
433  p->CopyData (&m_file, inclLen);
434 }
435 
436 void
438  uint8_t * const data,
439  uint32_t maxBytes,
440  uint32_t &tsSec,
441  uint32_t &tsUsec,
442  uint32_t &inclLen,
443  uint32_t &origLen,
444  uint32_t &readLen)
445 {
446  NS_LOG_FUNCTION (this << &data <<maxBytes << tsSec << tsUsec << inclLen << origLen << readLen);
447  NS_ASSERT (m_file.good ());
448 
449  PcapRecordHeader header;
450 
451  //
452  // Watch out for memory alignment differences between machines, so read
453  // them all individually.
454  //
455  m_file.read ((char *)&header.m_tsSec, sizeof(header.m_tsSec));
456  m_file.read ((char *)&header.m_tsUsec, sizeof(header.m_tsUsec));
457  m_file.read ((char *)&header.m_inclLen, sizeof(header.m_inclLen));
458  m_file.read ((char *)&header.m_origLen, sizeof(header.m_origLen));
459 
460  if (m_file.fail ())
461  {
462  return;
463  }
464 
465  if (m_swapMode)
466  {
467  Swap (&header, &header);
468  }
469 
470  tsSec = header.m_tsSec;
471  tsUsec = header.m_tsUsec;
472  inclLen = header.m_inclLen;
473  origLen = header.m_origLen;
474 
475  //
476  // We don't always want to force the client to keep a maximum length buffer
477  // around so we allow her to specify a minimum number of bytes to read.
478  // Usually 64 bytes is enough information to print all of the headers, so
479  // it isn't typically necessary to read all thousand bytes of an echo packet,
480  // for example, to figure out what is going on.
481  //
482  readLen = maxBytes < header.m_inclLen ? maxBytes : header.m_inclLen;
483  m_file.read ((char *)data, readLen);
484 
485  //
486  // To keep the file pointer pointed in the right place, however, we always
487  // need to account for the entire packet as stored originally.
488  //
489  if (readLen < header.m_inclLen)
490  {
491  m_file.seekg (header.m_inclLen - readLen, std::ios::cur);
492  }
493 }
494 
495 bool
496 PcapFile::Diff (std::string const & f1, std::string const & f2,
497  uint32_t & sec, uint32_t & usec,
498  uint32_t snapLen)
499 {
500  NS_LOG_FUNCTION (f1 << f2 << sec << usec << snapLen);
501  PcapFile pcap1, pcap2;
502  pcap1.Open (f1, std::ios::in);
503  pcap2.Open (f2, std::ios::in);
504  bool bad = pcap1.Fail () || pcap2.Fail ();
505  if (bad)
506  {
507  return true;
508  }
509 
510  uint8_t *data1 = new uint8_t [snapLen] ();
511  uint8_t *data2 = new uint8_t [snapLen] ();
512  uint32_t tsSec1 = 0;
513  uint32_t tsSec2 = 0;
514  uint32_t tsUsec1 = 0;
515  uint32_t tsUsec2 = 0;
516  uint32_t inclLen1 = 0;
517  uint32_t inclLen2 = 0;
518  uint32_t origLen1 = 0;
519  uint32_t origLen2 = 0;
520  uint32_t readLen1 = 0;
521  uint32_t readLen2 = 0;
522  bool diff = false;
523 
524  while (!pcap1.Eof () && !pcap2.Eof ())
525  {
526  pcap1.Read (data1, snapLen, tsSec1, tsUsec1, inclLen1, origLen1, readLen1);
527  pcap2.Read (data2, snapLen, tsSec2, tsUsec2, inclLen2, origLen2, readLen2);
528 
529  bool same = pcap1.Fail () == pcap2.Fail ();
530  if (!same)
531  {
532  diff = true;
533  break;
534  }
535  if (pcap1.Eof ())
536  {
537  break;
538  }
539 
540  if (tsSec1 != tsSec2 || tsUsec1 != tsUsec2)
541  {
542  diff = true; // Next packet timestamps do not match
543  break;
544  }
545 
546  if (readLen1 != readLen2)
547  {
548  diff = true; // Packet lengths do not match
549  break;
550  }
551 
552  if (std::memcmp (data1, data2, readLen1) != 0)
553  {
554  diff = true; // Packet data do not match
555  break;
556  }
557  }
558  sec = tsSec1;
559  usec = tsUsec1;
560 
561  bad = pcap1.Fail () || pcap2.Fail ();
562  bool eof = pcap1.Eof () && pcap2.Eof ();
563  if (bad && !eof)
564  {
565  diff = true;
566  }
567 
568  delete[] data1;
569  delete[] data2;
570 
571  return diff;
572 }
573 
574 } // namespace ns3
Protocol header serialization and deserialization.
Definition: header.h:42
#define NS_LOG_FUNCTION(parameters)
Definition: log.h:345
uint32_t m_tsSec
seconds part of timestamp
Definition: pcap-file.h:275
NS_LOG_COMPONENT_DEFINE("PcapFile")
uint8_t Swap(uint8_t val)
Definition: pcap-file.cc:149
void WriteFileHeader(void)
Definition: pcap-file.cc:193
uint16_t GetVersionMinor(void)
Definition: pcap-file.cc:107
automatically resized byte buffer
Definition: buffer.h:92
#define NS_ASSERT(condition)
Definition: assert.h:64
uint32_t GetSize(void) const
Definition: packet.h:650
const uint16_t VERSION_MAJOR
Major version of supported pcap file format.
Definition: pcap-file.cc:46
uint16_t m_versionMajor
Major version identifying the version of pcap used in this file.
Definition: pcap-file.h:266
uint32_t m_origLen
actual length of original packet
Definition: pcap-file.h:278
bool Eof(void) const
Definition: pcap-file.cc:72
const uint32_t SWAPPED_MAGIC
Looks this way if byte swapping is required.
Definition: pcap-file.cc:41
bool GetSwapMode(void)
Get the swap mode of the file.
Definition: pcap-file.cc:142
A class representing a pcap file.
Definition: pcap-file.h:42
uint32_t m_tsUsec
microseconds part of timestamp (nsecs for PCAP_NSEC_MAGIC)
Definition: pcap-file.h:276
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:437
uint8_t data[writeSize]
void RegisterStream(std::ostream *stream)
Register a stream to be flushed on abnormal exit.
Definition: fatal-impl.cc:80
virtual void Serialize(Buffer::Iterator start) const =0
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:739
Buffer::Iterator Begin(void) const
Definition: buffer.h:875
uint32_t m_snapLen
Maximum length of packet data stored in records.
Definition: pcap-file.h:270
bool m_swapMode
Definition: pcap-file.h:294
PcapFileHeader m_fileHeader
Definition: pcap-file.h:293
uint32_t GetMagic(void)
Definition: pcap-file.cc:93
uint32_t m_sigFigs
Unused by pretty much everybody.
Definition: pcap-file.h:269
void Clear(void)
Clear all state bits of the underlying iostream.
Definition: pcap-file.cc:78
virtual uint32_t GetSerializedSize(void) const =0
void ReadAndVerifyFileHeader(void)
Definition: pcap-file.cc:238
const uint32_t NS_SWAPPED_MAGIC
Looks this way if byte swapping is required.
Definition: pcap-file.cc:44
uint32_t WritePacketHeader(uint32_t tsSec, uint32_t tsUsec, uint32_t totalLen)
Definition: pcap-file.cc:374
void Init(uint32_t dataLinkType, uint32_t snapLen=SNAPLEN_DEFAULT, int32_t timeZoneCorrection=ZONE_DEFAULT, bool swapMode=false)
Initialize the pcap file associated with this object.
Definition: pcap-file.cc:329
uint32_t GetSigFigs(void)
Definition: pcap-file.cc:121
void Close(void)
Close the underlying file.
Definition: pcap-file.cc:86
uint16_t m_versionMinor
Minor version identifying the version of pcap used in this file.
Definition: pcap-file.h:267
uint16_t GetVersionMajor(void)
Definition: pcap-file.cc:100
uint32_t m_inclLen
number of octets of packet saved in file
Definition: pcap-file.h:277
static bool Diff(std::string const &f1, std::string const &f2, uint32_t &sec, uint32_t &usec, uint32_t snapLen=SNAPLEN_DEFAULT)
Compare two PCAP files packet-by-packet.
Definition: pcap-file.cc:496
void Open(std::string const &filename, std::ios::openmode mode)
Create a new pcap file or open an existing pcap file.
Definition: pcap-file.cc:310
uint32_t m_magicNumber
Magic number identifying this as a pcap file.
Definition: pcap-file.h:265
uint32_t m_type
Data link type of packet data.
Definition: pcap-file.h:271
bool Fail(void) const
Definition: pcap-file.cc:66
const uint16_t VERSION_MINOR
Minor version of supported pcap file format.
Definition: pcap-file.cc:47
std::fstream m_file
Definition: pcap-file.h:292
void UnregisterStream(std::ostream *stream)
Unregister a stream for flushing on abnormal exit.
Definition: fatal-impl.cc:87
const uint32_t NS_MAGIC
Magic number identifying nanosec resolution pcap file format.
Definition: pcap-file.cc:43
uint32_t CopyData(uint8_t *buffer, uint32_t size) const
Copy the packet contents to a byte buffer.
Definition: packet.cc:381
int32_t m_zone
Time zone correction to be applied to timestamps of packets.
Definition: pcap-file.h:268
const uint32_t MAGIC
Magic number identifying standard pcap file format.
Definition: pcap-file.cc:40
bool AddAtStart(uint32_t start)
Definition: buffer.cc:305
uint32_t GetDataLinkType(void)
Definition: pcap-file.cc:135
void Write(uint32_t tsSec, uint32_t tsUsec, uint8_t const *const data, uint32_t totalLen)
Write next packet to file.
Definition: pcap-file.cc:404
uint32_t GetSnapLen(void)
Definition: pcap-file.cc:128
int32_t GetTimeZoneOffset(void)
Definition: pcap-file.cc:114