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