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