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