A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
pcap-file-test-suite.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 as
5  * published by the Free Software Foundation;
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15  *
16  * Author: Craig Dowell (craigdo@ee.washington.edu)
17  */
18 
19 #include <iostream>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <sstream>
23 #include <cstring>
24 
25 #include "ns3/log.h"
26 #include "ns3/test.h"
27 #include "ns3/pcap-file.h"
28 
29 using namespace ns3;
30 
31 NS_LOG_COMPONENT_DEFINE ("pcap-file-test-suite");
32 
33 // ===========================================================================
34 // Some utility functions for the tests.
35 // ===========================================================================
36 
37 static uint16_t
38 Swap (uint16_t val)
39 {
40  return ((val >> 8) & 0x00ff) | ((val << 8) & 0xff00);
41 }
42 
43 static uint32_t
44 Swap (uint32_t val)
45 {
46  return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | ((val << 24) & 0xff000000);
47 }
48 
49 static bool
50 CheckFileExists (std::string filename)
51 {
52  FILE * p = std::fopen (filename.c_str (), "rb");
53  if (p == 0)
54  {
55  return false;
56  }
57 
58  std::fclose (p);
59  return true;
60 }
61 
62 
63 static bool
64 CheckFileLength (std::string filename, uint64_t sizeExpected)
65 {
66  FILE * p = std::fopen (filename.c_str (), "rb");
67  if (p == 0)
68  {
69  return false;
70  }
71 
72  std::fseek (p, 0, SEEK_END);
73 
74  uint64_t sizeActual = std::ftell (p);
75  std::fclose (p);
76 
77  return sizeActual == sizeExpected;
78 }
79 
80 // ===========================================================================
81 // Test case to make sure that the Pcap File Object can do its most basic job
82 // and create an empty pcap file.
83 // ===========================================================================
85 {
86 public:
88  virtual ~WriteModeCreateTestCase ();
89 
90 private:
91  virtual void DoSetup (void);
92  virtual void DoRun (void);
93  virtual void DoTeardown (void);
94 
95  std::string m_testFilename;
96 };
97 
99  : TestCase ("Check to see that PcapFile::Open with mode std::ios::out works")
100 {
101 }
102 
104 {
105 }
106 
107 void
109 {
110  std::stringstream filename;
111  uint32_t n = rand ();
112  filename << n;
113  m_testFilename = CreateTempDirFilename (filename.str () + ".pcap");
114 }
115 
116 void
118 {
119  if (remove (m_testFilename.c_str ()))
120  {
121  NS_LOG_ERROR ("Failed to delete file " << m_testFilename);
122  }
123 }
124 
125 void
127 {
128  PcapFile f;
129 
130  //
131  // Opening a new file in write mode should result in an empty file of the
132  // given name.
133  //
134  f.Open (m_testFilename, std::ios::out);
135 
136  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename << ", \"w\") returns error");
137  f.Close ();
138 
140  "Open (" << m_testFilename << ", \"std::ios::out\") does not create file");
142  "Open (" << m_testFilename << ", \"std::ios::out\") does not result in an empty file");
143 
144  //
145  // Calling Init() on a file created with "std::ios::out" should result in a file just
146  // long enough to contain the pcap file header.
147  //
148  f.Open (m_testFilename, std::ios::out);
149  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
150  ", \"std::ios::out\") returns error");
151 
152  f.Init (1234, 5678, 7);
153  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Init (1234, 5678, 7) returns error");
154 
155  f.Close ();
156 
158  "Init () does not result in a file with a pcap file header");
159 
160  //
161  // Opening an existing file in write mode should result in that file being
162  // emptied.
163  //
164  f.Open (m_testFilename, std::ios::out);
165  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
166  ", \"std::ios::out\") returns error");
167 
168  f.Close ();
169 
171  "Open (" << m_testFilename << ", \"w\") does not result in an empty file");
172 
173  //
174  // Initialize the file again.
175  //
176  f.Open (m_testFilename, std::ios::out);
177  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false,
178  "Open (" << m_testFilename << ", \"w\") returns error");
179 
180  f.Init (1234, 5678, 7);
181  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Init (1234, 5678, 7) returns error");
182 
183  //
184  // Now we should be able to write to it since it was opened in std::ios::out mode.
185  // This is just a permissions check so we don't actually look at the
186  // data.
187  //
188  uint8_t buffer[128];
189  memset (buffer, 0, sizeof(buffer));
190  f.Write (0, 0, buffer, 128);
191  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Write (write-only-file " << m_testFilename <<
192  ") returns error");
193 }
194 
195 // ===========================================================================
196 // Test case to make sure that the Pcap File Object can open an existing pcap
197 // file.
198 // ===========================================================================
200 {
201 public:
203  virtual ~ReadModeCreateTestCase ();
204 
205 private:
206  virtual void DoSetup (void);
207  virtual void DoRun (void);
208  virtual void DoTeardown (void);
209 
210  std::string m_testFilename;
211 };
212 
214  : TestCase ("Check to see that PcapFile::Open with mode \"std::ios::in\" works")
215 {
216 }
217 
219 {
220 }
221 
222 void
224 {
225  std::stringstream filename;
226  uint32_t n = rand ();
227  filename << n;
228  m_testFilename = CreateTempDirFilename (filename.str () + ".pcap");
229 }
230 
231 void
233 {
234  if (remove (m_testFilename.c_str ()))
235  {
236  NS_LOG_ERROR ("Failed to delete file " << m_testFilename);
237  }
238 }
239 
240 void
242 {
243  PcapFile f;
244 
245  //
246  // Opening a non-existing file in read mode should result in an error.
247  //
248  f.Open (m_testFilename, std::ios::in);
249  NS_TEST_ASSERT_MSG_EQ (f.Fail (), true, "Open (non-existing-filename " << m_testFilename <<
250  ", \"std::ios::in\") does not return error");
251  f.Close ();
252  f.Clear ();
254  ", \"std::ios::in\") unexpectedly created a file");
255 
256  //
257  // Okay, now create an uninitialized file using previously tested operations
258  //
259  f.Open (m_testFilename, std::ios::out);
260  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (filename, \"std::ios::out\") returns error");
261  f.Close ();
262 
263  //
264  // Opening this file should result in an error since it has no pcap file header.
265  //
266  f.Open (m_testFilename, std::ios::in);
267  NS_TEST_ASSERT_MSG_EQ (f.Fail (), true, "Open (non-initialized-filename " << m_testFilename <<
268  ", \"std::ios::in\") does not return error");
269  f.Close ();
270  f.Clear ();
271 
272  //
273  // Okay, now open that non-initialized file in write mode and initialize it
274  // Note that we open it in write mode to initialize it.
275  //
276  f.Open (m_testFilename, std::ios::out);
277  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
278  ", \"std::ios::out\") returns error");
279 
280  f.Init (1234, 5678, 7);
281  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Init (1234, 5678, 7) returns error");
282  f.Close ();
283 
284  //
285  // Opening this file should now work since it has a pcap file header.
286  //
287  f.Open (m_testFilename, std::ios::in);
288  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (initialized-filename " << m_testFilename <<
289  ", \"std::ios::in\") returns error");
290 
291  //
292  // Now we should not be able to write to it since it was opened in "r" mode
293  // even if it has been initialized..
294  //
295  uint8_t buffer[128];
296  f.Write (0, 0, buffer, 128);
297  NS_TEST_ASSERT_MSG_EQ (f.Fail (), true, "Write (read-only-file " << m_testFilename <<
298  ") does not return error");
299  f.Close ();
300  f.Clear ();
301 }
302 
303 #if 0
304 // ===========================================================================
305 // Test case to make sure that the Pcap File Object can open an existing pcap
306 // file for appending.
307 // ===========================================================================
308 class AppendModeCreateTestCase : public TestCase
309 {
310 public:
311  AppendModeCreateTestCase ();
312  virtual ~AppendModeCreateTestCase ();
313 
314 private:
315  virtual void DoSetup (void);
316  virtual void DoRun (void);
317  virtual void DoTeardown (void);
318 
319  std::string m_testFilename;
320 };
321 
322 AppendModeCreateTestCase::AppendModeCreateTestCase ()
323  : TestCase ("Check to see that PcapFile::Open with mode \"std::ios::app\" works")
324 {
325 }
326 
327 AppendModeCreateTestCase::~AppendModeCreateTestCase ()
328 {
329 }
330 
331 void
333 {
334  std::stringstream filename;
335  uint32_t n = rand ();
336  filename << n;
337  m_testFilename = CreateTempDirFilename (filename.str () + ".pcap");
338 }
339 
340 void
342 {
343  if (remove (m_testFilename.c_str ()))
344  {
345  NS_LOG_ERROR ("Failed to delete file " << m_testFilename);
346  }
347 }
348 
349 void
351 {
352  PcapFile f;
353 
354  //
355  // Opening a non-existing file in append mode should result in an error.
356  //
357  f.Open (m_testFilename, std::ios::out | std::ios::app);
358  NS_TEST_ASSERT_MSG_EQ (f.Fail (), true, "Open (non-existing-filename " << m_testFilename <<
359  ", \"std::ios::app\") does not return error");
360  f.Close ();
361  f.Clear ();
362 
363  NS_TEST_ASSERT_MSG_EQ (CheckFileExists (m_testFilename), false,
364  "Open (" << m_testFilename << ", \"std::ios::app\") unexpectedly created a file");
365 
366  //
367  // Okay, now create an uninitialized file using previously tested operations
368  //
369  f.Open (m_testFilename, std::ios::out);
370  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
371  ", \"std::ios::out\") returns error");
372  f.Close ();
373 
374  //
375  // Opening this file should result in an error since it has no pcap file header.
376  //
377  f.Open (m_testFilename, std::ios::out | std::ios::app);
378  NS_TEST_ASSERT_MSG_EQ (f.Fail (), true, "Open (non-initialized-filename " << m_testFilename <<
379  ", \"std::ios::app\") does not return error");
380  f.Close ();
381  f.Clear ();
382 
383  //
384  // Okay, now open that non-initialized file in write mode and initialize it.
385  //
386  f.Open (m_testFilename, std::ios::out);
387  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (non-initialized-filename " << m_testFilename <<
388  ", \"std::ios::out\") returns error");
389 
390  f.Init (1234, 5678, 7);
391  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Init (1234, 5678, 7) returns error");
392  f.Close ();
393 
394  //
395  // Opening this file should now work since it has a pcap file header.
396  //
397  f.Open (m_testFilename, std::ios::out | std::ios::app);
398  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (initialized-filename " << m_testFilename <<
399  ", \"std::ios::app\") returns error");
400 
401  //
402  // We should be able to write to it since it was opened in "std::ios::app" mode.
403  //
404  uint8_t buffer[128];
405  memset (buffer, 0, sizeof(buffer));
406  f.Write (0, 0, buffer, 128);
407  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Write (append-mode-file " << m_testFilename << ") returns error");
408 
409  f.Close ();
410 }
411 #endif
412 
413 // ===========================================================================
414 // Test case to make sure that the Pcap File Object can write out correct pcap
415 // file headers in both endian cases, and then read them in correctly.
416 // ===========================================================================
418 {
419 public:
421  virtual ~FileHeaderTestCase ();
422 
423 private:
424  virtual void DoSetup (void);
425  virtual void DoRun (void);
426  virtual void DoTeardown (void);
427 
428  std::string m_testFilename;
429 };
430 
432  : TestCase ("Check to see that PcapFileHeader is managed correctly")
433 {
434 }
435 
437 {
438 }
439 
440 void
442 {
443  std::stringstream filename;
444  uint32_t n = rand ();
445  filename << n;
446  m_testFilename = CreateTempDirFilename (filename.str () + ".pcap");
447 }
448 
449 void
451 {
452  if (remove (m_testFilename.c_str ()))
453  {
454  NS_LOG_ERROR ("Failed to delete file " << m_testFilename);
455  }
456 }
457 
458 void
460 {
461  PcapFile f;
462 
463  //
464  // Create an uninitialized file using previously tested operations
465  //
466  f.Open (m_testFilename, std::ios::out);
467  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
468  ", \"std::ios::out\") returns error");
469 
470  //
471  // Initialize the pcap file header.
472  //
473  f.Init (1234, 5678, 7);
474  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false,
475  "Init (1234, 5678, 7) returns error");
476  f.Close ();
477 
478  //
479  // Take a look and see what was done to the file
480  //
481  FILE *p = std::fopen (m_testFilename.c_str (), "r+b");
482  NS_TEST_ASSERT_MSG_NE (p, 0, "fopen(" << m_testFilename << ") should have been able to open a correctly created pcap file");
483 
484  uint32_t val32;
485  uint16_t val16;
486 
487  //
488  // Because the regression tests require that pcap file output be compared
489  // byte-by-byte, we had to decide on a single format for written pcap files.
490  // This was little endian. So we have to do something special with big-
491  // endian machines here.
492  //
493  // When a big endian machine writes a pcap file, it is forced into swap
494  // mode and actually writes little endian files. This is automagically
495  // fixed up when using a PcapFile to read the values, but when a big-
496  // endian machine reads these values directly, they will be swapped.
497  //
498  // We can remove this nonsense when we get rid of the pcap-file-comparison
499  // regression tests.
500  //
501  // So, determine the endian-ness of the running system, and if we're on
502  // a big-endian machine, swap all of the results below before checking.
503  //
504  union {
505  uint32_t a;
506  uint8_t b[4];
507  } u;
508 
509  u.a = 1;
510  bool bigEndian = u.b[3];
511 
512  size_t result = std::fread (&val32, sizeof(val32), 1, p);
513  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() magic number");
514  if (bigEndian) val32 = Swap (val32);
515  NS_TEST_ASSERT_MSG_EQ (val32, 0xa1b2c3d4, "Magic number written incorrectly");
516 
517  result = std::fread (&val16, sizeof(val16), 1, p);
518  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version major");
519  if (bigEndian) val16 = Swap (val16);
520  NS_TEST_ASSERT_MSG_EQ (val16, 2, "Version major written incorrectly");
521 
522  result = std::fread (&val16, sizeof(val16), 1, p);
523  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version minor");
524  if (bigEndian) val16 = Swap (val16);
525  NS_TEST_ASSERT_MSG_EQ (val16, 4, "Version minor written incorrectly");
526 
527  result = std::fread (&val32, sizeof(val32), 1, p);
528  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() time zone correction");
529  if (bigEndian) val32 = Swap (val32);
530  NS_TEST_ASSERT_MSG_EQ (val32, 7, "Version minor written incorrectly");
531 
532  result = std::fread (&val32, sizeof(val32), 1, p);
533  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() sig figs");
534  if (bigEndian) val32 = Swap (val32);
535  NS_TEST_ASSERT_MSG_EQ (val32, 0, "Sig figs written incorrectly");
536 
537  result = std::fread (&val32, sizeof(val32), 1, p);
538  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() snap length");
539  if (bigEndian) val32 = Swap (val32);
540  NS_TEST_ASSERT_MSG_EQ (val32, 5678, "Snap length written incorrectly");
541 
542  result = std::fread (&val32, sizeof(val32), 1, p);
543  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() data link type");
544  if (bigEndian) val32 = Swap (val32);
545  NS_TEST_ASSERT_MSG_EQ (val32, 1234, "Data length type written incorrectly");
546 
547  std::fclose (p);
548  p = 0;
549 
550  //
551  // We wrote a little-endian file out correctly, now let's see if we can read
552  // it back in correctly.
553  //
554  // As mentioned above, when a big endian machine writes a pcap file, it is
555  // forced into swap mode and actually writes little endian files. This is
556  // automagically fixed up when using a PcapFile to read the values, so we
557  // don't have to do anything special here.
558  //
559  f.Open (m_testFilename, std::ios::in);
560  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (existing-initialized-file " << m_testFilename <<
561  ", \"std::ios::in\") returns error");
562 
563  NS_TEST_ASSERT_MSG_EQ (f.GetMagic (), 0xa1b2c3d4, "Read back magic number incorrectly");
564  NS_TEST_ASSERT_MSG_EQ (f.GetVersionMajor (), 2, "Read back version major incorrectly");
565  NS_TEST_ASSERT_MSG_EQ (f.GetVersionMinor (), 4, "Read back version minor incorrectly");
566  NS_TEST_ASSERT_MSG_EQ (f.GetTimeZoneOffset (), 7, "Read back time zone offset incorrectly");
567  NS_TEST_ASSERT_MSG_EQ (f.GetSigFigs (), 0, "Read back sig figs incorrectly");
568  NS_TEST_ASSERT_MSG_EQ (f.GetSnapLen (), 5678, "Read back snap len incorrectly");
569  NS_TEST_ASSERT_MSG_EQ (f.GetDataLinkType (), 1234, "Read back data link type incorrectly");
570  f.Close ();
571 
572  //
573  // Re-open the file to erase its contents.
574  //
575  f.Open (m_testFilename, std::ios::out);
576  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
577  ", \"std::ios::out\") returns error");
578 
579  //
580  // Initialize the pcap file header, turning on swap mode manually to force
581  // the pcap file header to be written out in foreign-endian form, whichever
582  // endian-ness that might be. Since big-endian machines are automatically
583  // forced into swap mode, the <true> parameter to f.Init() below is actually
584  // a no-op and we're always writing foreign-endian files. In that case,
585  // this test case is really just a duplicate of the previous.
586  //
587  f.Init (1234, 5678, 7, true);
588  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Init (1234, 5678, 7) returns error");
589  f.Close ();
590 
591  //
592  // Take a look and see what was done to the file. Everything should now
593  // appear byte-swapped.
594  //
595  p = std::fopen (m_testFilename.c_str (), "r+b");
596  NS_TEST_ASSERT_MSG_NE (p, 0, "fopen(" << m_testFilename << ") should have been able to open a correctly created pcap file");
597 
598  result = std::fread (&val32, sizeof(val32), 1, p);
599  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() magic number");
600  NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (0xa1b2c3d4)), "Magic number written incorrectly");
601 
602  result = std::fread (&val16, sizeof(val16), 1, p);
603  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version major");
604  NS_TEST_ASSERT_MSG_EQ (val16, Swap (uint16_t (2)), "Version major written incorrectly");
605 
606  result = std::fread (&val16, sizeof(val16), 1, p);
607  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version minor");
608  NS_TEST_ASSERT_MSG_EQ (val16, Swap (uint16_t (4)), "Version minor written incorrectly");
609 
610  result = std::fread (&val32, sizeof(val32), 1, p);
611  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() time zone correction");
612  NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (7)), "Version minor written incorrectly");
613 
614  result = std::fread (&val32, sizeof(val32), 1, p);
615  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() sig figs");
616  NS_TEST_ASSERT_MSG_EQ (val32, 0, "Sig figs written incorrectly");
617 
618  result = std::fread (&val32, sizeof(val32), 1, p);
619  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() snap length");
620  NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (5678)), "Snap length written incorrectly");
621 
622  result = std::fread (&val32, sizeof(val32), 1, p);
623  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() data link type");
624  NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (1234)), "Data length type written incorrectly");
625 
626  std::fclose (p);
627  p = 0;
628 
629  //
630  // We wrote an opposite-endian file out correctly, now let's see if we can read
631  // it back in correctly. Again, in the case of a big-endian machine, we already
632  // did this test and it is just a duplicate. What we don't test on a big endian
633  // machine is writing out a big-endian file by default, but we can't do that
634  // since it breaks regression testing.
635  //
636  f.Open (m_testFilename, std::ios::in);
637  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (existing-initialized-file " << m_testFilename <<
638  ", \"std::ios::in\") returns error");
639 
640  NS_TEST_ASSERT_MSG_EQ (f.GetSwapMode (), true, "Byte-swapped file not correctly indicated");
641 
642  NS_TEST_ASSERT_MSG_EQ (f.GetMagic (), 0xa1b2c3d4, "Read back magic number incorrectly");
643  NS_TEST_ASSERT_MSG_EQ (f.GetVersionMajor (), 2, "Read back version major incorrectly");
644  NS_TEST_ASSERT_MSG_EQ (f.GetVersionMinor (), 4, "Read back version minor incorrectly");
645  NS_TEST_ASSERT_MSG_EQ (f.GetTimeZoneOffset (), 7, "Read back time zone offset incorrectly");
646  NS_TEST_ASSERT_MSG_EQ (f.GetSigFigs (), 0, "Read back sig figs incorrectly");
647  NS_TEST_ASSERT_MSG_EQ (f.GetSnapLen (), 5678, "Read back snap len incorrectly");
648  NS_TEST_ASSERT_MSG_EQ (f.GetDataLinkType (), 1234, "Read back data link type incorrectly");
649 
650  f.Close ();
651 }
652 
653 // ===========================================================================
654 // Test case to make sure that the Pcap File Object can write pcap packet
655 // records in both endian cases, and then read them in correctly.
656 // ===========================================================================
658 {
659 public:
661  virtual ~RecordHeaderTestCase ();
662 
663 private:
664  virtual void DoSetup (void);
665  virtual void DoRun (void);
666  virtual void DoTeardown (void);
667 
668  std::string m_testFilename;
669 };
670 
672  : TestCase ("Check to see that PcapRecordHeader is managed correctly")
673 {
674 }
675 
677 {
678 }
679 
680 void
682 {
683  std::stringstream filename;
684  uint32_t n = rand ();
685  filename << n;
686  m_testFilename = CreateTempDirFilename (filename.str () + ".pcap");
687 }
688 
689 void
691 {
692  if (remove (m_testFilename.c_str ()))
693  {
694  NS_LOG_ERROR ("Failed to delete file " << m_testFilename);
695  }
696 }
697 
698 void
700 {
701  PcapFile f;
702 
703  //
704  // Create an uninitialized file using previously tested operations
705  //
706  f.Open (m_testFilename, std::ios::out);
707  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
708  ", \"std::ios::out\") returns error");
709 
710  //
711  // Initialize the pcap file header.
712  //
713  f.Init (37, 43, -7);
714  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Init (37, 43, -7) returns error");
715 
716  //
717  // Initialize a buffer with a counting pattern to check the data later.
718  //
719  uint8_t bufferOut[128];
720  for (uint32_t i = 0; i < 128; ++i)
721  {
722  bufferOut[i] = i;
723  }
724 
725  //
726  // Now we should be able to write a packet to it since it was opened in "w"
727  // mode. The packet data written should be limited to 43 bytes in length
728  // by the Init() call above.
729  //
730  f.Write (1234, 5678, bufferOut, 128);
731  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Write (write-only-file " << m_testFilename << ") returns error");
732  f.Close ();
733 
734  //
735  // Let's peek into the file and see what actually went out for that
736  // packet.
737  //
738  FILE *p = std::fopen (m_testFilename.c_str (), "r+b");
739  NS_TEST_ASSERT_MSG_NE (p, 0, "fopen() should have been able to open a correctly created pcap file");
740 
741  //
742  // A pcap file header takes up 24 bytes, a pcap record header takes up 16 bytes
743  // and we wrote in 43 bytes, so the file must be 83 bytes long. Let's just
744  // double check that this is exactly what happened.
745  //
746  std::fseek (p, 0, SEEK_END);
747  uint64_t size = std::ftell (p);
748  NS_TEST_ASSERT_MSG_EQ (size, 83, "Pcap file with one 43 byte packet is incorrect size");
749 
750  //
751  // A pcap file header takes up 24 bytes, so we should see a pcap record header
752  // starting there in the file. We've tested this all before so we just assume
753  // it's all right and just seek to just past that point..
754  //
755  std::fseek (p, 24, SEEK_SET);
756 
757  uint32_t val32;
758 
759  //
760  // Because the regression tests require that pcap file output be compared
761  // byte-by-byte, we had to decide on a single format for written pcap files.
762  // This was little endian. So we have to do something special with big-
763  // endian machines here.
764  //
765  // When a big endian machine writes a pcap file, it is forced into swap
766  // mode and actually writes little endian files. This is automagically
767  // fixed up when using a PcapFile to read the values, but when a big-
768  // endian machine reads these values directly, they will be swapped.
769  //
770  // We can remove this nonsense when we get rid of the pcap-file-comparison
771  // regression tests.
772  //
773  // So, determine the endian-ness of the running system, and if we're on
774  // a big-endian machine, swap all of the results below before checking.
775  //
776  union {
777  uint32_t a;
778  uint8_t b[4];
779  } u;
780 
781  u.a = 1;
782  bool bigEndian = u.b[3];
783 
784  size_t result = std::fread (&val32, sizeof(val32), 1, p);
785  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() seconds timestamp");
786  if (bigEndian) val32 = Swap (val32);
787  NS_TEST_ASSERT_MSG_EQ (val32, 1234, "Seconds timestamp written incorrectly");
788 
789  result = std::fread (&val32, sizeof(val32), 1, p);
790  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() microseconds timestamp");
791  if (bigEndian) val32 = Swap (val32);
792  NS_TEST_ASSERT_MSG_EQ (val32, 5678, "Microseconds timestamp written incorrectly");
793 
794  result = std::fread (&val32, sizeof(val32), 1, p);
795  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() included length");
796  if (bigEndian) val32 = Swap (val32);
797  NS_TEST_ASSERT_MSG_EQ (val32, 43, "Included length written incorrectly");
798 
799  result = std::fread (&val32, sizeof(val32), 1, p);
800  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() actual length");
801  if (bigEndian) val32 = Swap (val32);
802  NS_TEST_ASSERT_MSG_EQ (val32, 128, "Actual length written incorrectly");
803 
804  //
805  // Take a look and see what went out into the file. The packet data
806  // should be unchanged (unswapped).
807  //
808  uint8_t bufferIn[128];
809 
810  result = std::fread (bufferIn, 1, 43, p);
811  NS_TEST_ASSERT_MSG_EQ (result, 43, "Unable to fread() packet data of expected length");
812 
813  for (uint32_t i = 0; i < 43; ++i)
814  {
815  NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data written");
816  }
817 
818  std::fclose (p);
819  p = 0;
820 
821  //
822  // Let's see if the PcapFile object can figure out how to do the same thing and
823  // correctly read in a packet.
824  //
825  f.Open (m_testFilename, std::ios::in);
826  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
827  ", \"std::ios::in\") of existing good file returns error");
828 
829  uint32_t tsSec, tsUsec, inclLen, origLen, readLen;
830 
831  f.Read (bufferIn, sizeof(bufferIn), tsSec, tsUsec, inclLen, origLen, readLen);
832  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Read() of known good packet returns error");
833  NS_TEST_ASSERT_MSG_EQ (tsSec, 1234, "Incorrectly read seconds timestap from known good packet");
834  NS_TEST_ASSERT_MSG_EQ (tsUsec, 5678, "Incorrectly read microseconds timestap from known good packet");
835  NS_TEST_ASSERT_MSG_EQ (inclLen, 43, "Incorrectly read included length from known good packet");
836  NS_TEST_ASSERT_MSG_EQ (origLen, 128, "Incorrectly read original length from known good packet");
837  NS_TEST_ASSERT_MSG_EQ (readLen, 43, "Incorrectly constructed actual read length from known good packet given buffer size");
838  f.Close ();
839 
840  //
841  // Did the data come back correctly?
842  //
843  for (uint32_t i = 0; i < 43; ++i)
844  {
845  NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data read from known good packet");
846  }
847 
848  //
849  // We have to check to make sure that the pcap record header is swapped
850  // correctly. Since big-endian machines are automatically forced into
851  // swap mode, the <true> parameter to f.Init() below is actually
852  // a no-op and we're always writing foreign-endian files. In that case,
853  // this test case is really just a duplicate of the previous.
854  //
855  // Open the file in write mode to clear the data.
856  //
857  f.Open (m_testFilename, std::ios::out);
858  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
859  ", \"std::ios::out\") returns error");
860 
861  //
862  // Initialize the pcap file header, forcing the object into swap mode.
863  //
864  f.Init (37, 43, -7, true);
865  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Init (37, 43, -7) returns error");
866 
867  //
868  // Now we should be able to write a packet to it since it was opened in "w"
869  // mode. The packet data written should be limited to 43 bytes in length
870  // by the Init() call above.
871  //
872  f.Write (1234, 5678, bufferOut, 128);
873  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Write (write-only-file " << m_testFilename << ") returns error");
874  f.Close ();
875 
876  //
877  // Let's peek into the file and see what actually went out for that
878  // packet.
879  //
880  p = std::fopen (m_testFilename.c_str (), "r+b");
881  NS_TEST_ASSERT_MSG_NE (p, 0, "fopen() should have been able to open a correctly created pcap file");
882 
883  //
884  // A pcap file header takes up 24 bytes, a pcap record header takes up 16 bytes
885  // and we wrote in 43 bytes, so the file must be 83 bytes long. Let's just
886  // double check that this is exactly what happened.
887  //
888  std::fseek (p, 0, SEEK_END);
889  size = std::ftell (p);
890  NS_TEST_ASSERT_MSG_EQ (size, 83, "Pcap file with one 43 byte packet is incorrect size");
891 
892  //
893  // A pcap file header takes up 24 bytes, so we should see a pcap record header
894  // starting there in the file. We've tested this all before so we just assume
895  // it's all right and just seek past it.
896  //
897  result = std::fseek (p, 24, SEEK_SET);
898  NS_TEST_ASSERT_MSG_EQ (result, 0, "Failed seeking past pcap header");
899 
900  result = std::fread (&val32, sizeof(val32), 1, p);
901  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() seconds timestamp");
902  NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (1234)), "Swapped seconds timestamp written incorrectly");
903 
904  result = std::fread (&val32, sizeof(val32), 1, p);
905  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() microseconds timestamp");
906  NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (5678)), "Swapped microseconds timestamp written incorrectly");
907 
908  result = std::fread (&val32, sizeof(val32), 1, p);
909  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() included length");
910  NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (43)), "Swapped included length written incorrectly");
911 
912  result = std::fread (&val32, sizeof(val32), 1, p);
913  NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() actual length");
914  NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (128)), "Swapped Actual length written incorrectly");
915 
916  //
917  // Take a look and see what went out into the file. The packet data
918  // should be unchanged (unswapped).
919  //
920  result = std::fread (bufferIn, 1, 43, p);
921  NS_TEST_ASSERT_MSG_EQ (result, 43, "Unable to fread() packet data of expected length");
922 
923  for (uint32_t i = 0; i < 43; ++i)
924  {
925  NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data written");
926  }
927 
928  std::fclose (p);
929  p = 0;
930 
931  //
932  // Let's see if the PcapFile object can figure out how to do the same thing and
933  // correctly read in a packet. The record header info should come back to us
934  // swapped back into correct form.
935  //
936  f.Open (m_testFilename, std::ios::in);
937  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << m_testFilename <<
938  ", \"std::ios::in\") of existing good file returns error");
939 
940  f.Read (bufferIn, sizeof(bufferIn), tsSec, tsUsec, inclLen, origLen, readLen);
941  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Read() of known good packet returns error");
942  NS_TEST_ASSERT_MSG_EQ (tsSec, 1234, "Incorrectly read seconds timestap from known good packet");
943  NS_TEST_ASSERT_MSG_EQ (tsUsec, 5678, "Incorrectly read microseconds timestap from known good packet");
944  NS_TEST_ASSERT_MSG_EQ (inclLen, 43, "Incorrectly read included length from known good packet");
945  NS_TEST_ASSERT_MSG_EQ (origLen, 128, "Incorrectly read original length from known good packet");
946  NS_TEST_ASSERT_MSG_EQ (readLen, 43, "Incorrectly constructed actual read length from known good packet given buffer size");
947 
948  //
949  // Did the data come back correctly (unchanged / unswapped)?
950  //
951  for (uint32_t i = 0; i < 43; ++i)
952  {
953  NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data read from known good packet");
954  }
955 
956  f.Close ();
957 }
958 
959 // ===========================================================================
960 // Test case to make sure that the Pcap File Object can read out the contents
961 // of a known good pcap file.
962 // ===========================================================================
964 {
965 public:
966  ReadFileTestCase ();
967  virtual ~ReadFileTestCase ();
968 
969 private:
970  virtual void DoSetup (void);
971  virtual void DoRun (void);
972  virtual void DoTeardown (void);
973 
974  std::string m_testFilename;
975 };
976 
978  : TestCase ("Check to see that PcapFile can read out a known good pcap file")
979 {
980 }
981 
983 {
984 }
985 
986 void
988 {
989 }
990 
991 void
993 {
994 }
995 
996 const uint32_t N_KNOWN_PACKETS = 6;
997 const uint32_t N_PACKET_BYTES = 16;
998 
999 typedef struct PACKET_ENTRY {
1000  uint32_t tsSec;
1001  uint32_t tsUsec;
1002  uint32_t inclLen;
1003  uint32_t origLen;
1005 } PacketEntry;
1006 
1008  { 2, 3696, 46, 46, { 0x0001, 0x0800, 0x0604, 0x0001, 0x0000, 0x0000, 0x0003, 0x0a01,
1009  0x0201, 0xffff, 0xffff, 0xffff, 0x0a01, 0x0204, 0x0000, 0x0000}},
1010  { 2, 3707, 46, 46, { 0x0001, 0x0800, 0x0604, 0x0002, 0x0000, 0x0000, 0x0006, 0x0a01,
1011  0x0204, 0x0000, 0x0000, 0x0003, 0x0a01, 0x0201, 0x0000, 0x0000}},
1012  { 2, 3801, 1070, 1070, { 0x4500, 0x041c, 0x0000, 0x0000, 0x3f11, 0x0000, 0x0a01, 0x0101,
1013  0x0a01, 0x0204, 0xc001, 0x0009, 0x0408, 0x0000, 0x0000, 0x0000}},
1014  { 2, 3811, 46, 46, { 0x0001, 0x0800, 0x0604, 0x0001, 0x0000, 0x0000, 0x0006, 0x0a01,
1015  0x0204, 0xffff, 0xffff, 0xffff, 0x0a01, 0x0201, 0x0000, 0x0000}},
1016  { 2, 3822, 46, 46, { 0x0001, 0x0800, 0x0604, 0x0002, 0x0000, 0x0000, 0x0003, 0x0a01,
1017  0x0201, 0x0000, 0x0000, 0x0006, 0x0a01, 0x0204, 0x0000, 0x0000}},
1018  { 2, 3915, 1070, 1070, { 0x4500, 0x041c, 0x0000, 0x0000, 0x4011, 0x0000, 0x0a01, 0x0204,
1019  0x0a01, 0x0101, 0x0009, 0xc001, 0x0408, 0x0000, 0x0000, 0x0000}}
1020 };
1021 
1022 
1023 void
1025 {
1026  PcapFile f;
1027 
1028  //
1029  //
1030  std::string filename = CreateDataDirFilename ("known.pcap");
1031  f.Open (filename, std::ios::in);
1032  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << filename <<
1033  ", \"std::ios::in\") returns error");
1034 
1035  //
1036  // We are going to read out the file header and all of the packets to make
1037  // sure that we read what we know, a priori, to be there.
1038  //
1039  // The packet data was gotten using "tcpdump -nn -tt -r known.pcap -x"
1040  // and the timestamp and first 32 bytes of the resulting dump were
1041  // duplicated in the structure above.
1042  //
1043  uint8_t data[N_PACKET_BYTES];
1044  uint32_t tsSec, tsUsec, inclLen, origLen, readLen;
1045 
1047 
1048  for (uint32_t i = 0; i < N_KNOWN_PACKETS; ++i, ++p)
1049  {
1050  f.Read (data, sizeof(data), tsSec, tsUsec, inclLen, origLen, readLen);
1051  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Read() of known good pcap file returns error");
1052  NS_TEST_ASSERT_MSG_EQ (tsSec, p->tsSec, "Incorrectly read seconds timestap from known good pcap file");
1053  NS_TEST_ASSERT_MSG_EQ (tsUsec, p->tsUsec, "Incorrectly read microseconds timestap from known good pcap file");
1054  NS_TEST_ASSERT_MSG_EQ (inclLen, p->inclLen, "Incorrectly read included length from known good packet");
1055  NS_TEST_ASSERT_MSG_EQ (origLen, p->origLen, "Incorrectly read original length from known good packet");
1056  NS_TEST_ASSERT_MSG_EQ (readLen, N_PACKET_BYTES, "Incorrect actual read length from known good packet given buffer size");
1057  }
1058 
1059  //
1060  // The file should now be at EOF since we've read all of the packets.
1061  // Another packet read should return an error.
1062  //
1063  f.Read (data, 1, tsSec, tsUsec, inclLen, origLen, readLen);
1064  NS_TEST_ASSERT_MSG_EQ (f.Eof (), true, "Read() of known good pcap file at EOF does not return error");
1065 
1066  f.Close ();
1067 }
1068 
1069 // ===========================================================================
1070 // Test case to make sure that the Pcap::Diff method works as expected
1071 // ===========================================================================
1072 class DiffTestCase : public TestCase
1073 {
1074 public:
1075  DiffTestCase ();
1076 
1077 private:
1078  virtual void DoRun (void);
1079 };
1080 
1082  : TestCase ("Check that PcapFile::Diff works as expected")
1083 {
1084 }
1085 
1086 void
1088 {
1089  //
1090  // Check that PcapDiff(file, file) is false
1091  //
1092  std::string filename = CreateDataDirFilename ("known.pcap");
1093  uint32_t sec (0), usec (0);
1094  bool diff = PcapFile::Diff (filename, filename, sec, usec);
1095  NS_TEST_EXPECT_MSG_EQ (diff, false, "PcapDiff(file, file) must always be false");
1096 
1097  //
1098  // Create different PCAP file (with the same timestamps, but different packets) and check that it is indeed different
1099  //
1100  std::string filename2 = "different.pcap";
1101  PcapFile f;
1102 
1103  f.Open (filename2, std::ios::out);
1104  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Open (" << filename2 << ", \"std::ios::out\") returns error");
1105  f.Init (1, N_PACKET_BYTES);
1106  NS_TEST_ASSERT_MSG_EQ (f.Fail (), false, "Init (1, " << N_PACKET_BYTES << ") returns error");
1107 
1108  for (uint32_t i = 0; i < N_KNOWN_PACKETS; ++i)
1109  {
1110  PacketEntry const & p = knownPackets[i];
1111 
1112  f.Write (p.tsSec, p.tsUsec, (uint8_t const *)p.data, p.origLen);
1113  NS_TEST_EXPECT_MSG_EQ (f.Fail (), false, "Write must not fail");
1114  }
1115  f.Close ();
1116 
1117  diff = PcapFile::Diff (filename, filename2, sec, usec);
1118  NS_TEST_EXPECT_MSG_EQ (diff, true, "PcapDiff(file, file2) must be true");
1119  NS_TEST_EXPECT_MSG_EQ (sec, 2, "Files are different from 2.3696 seconds");
1120  NS_TEST_EXPECT_MSG_EQ (usec, 3696, "Files are different from 2.3696 seconds");
1121 }
1122 
1124 {
1125 public:
1126  PcapFileTestSuite ();
1127 };
1128 
1130  : TestSuite ("pcap-file", UNIT)
1131 {
1132  SetDataDir (NS_TEST_SOURCEDIR);
1133  AddTestCase (new WriteModeCreateTestCase, TestCase::QUICK);
1134  AddTestCase (new ReadModeCreateTestCase, TestCase::QUICK);
1135  //AddTestCase (new AppendModeCreateTestCase, TestCase::QUICK);
1136  AddTestCase (new FileHeaderTestCase, TestCase::QUICK);
1137  AddTestCase (new RecordHeaderTestCase, TestCase::QUICK);
1138  AddTestCase (new ReadFileTestCase, TestCase::QUICK);
1139  AddTestCase (new DiffTestCase, TestCase::QUICK);
1140 }
1141