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