]> The Tcpdump Group git mirrors - tcpdump/blob - tests/TESTrun
NTP: Fix p_ntp_time_fmt() using epoch 1/epoch 2 convention (RFC 4330)
[tcpdump] / tests / TESTrun
1 #!/usr/bin/env perl
2
3 # Copyright (c) 2020-2025 The Tcpdump Group
4 # All rights reserved.
5 # SPDX-License-Identifier: BSD-2-Clause
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28
29 require 5.8.4; # Solaris 10
30 use sigtrap qw(die normal-signals);
31 use strict;
32 use warnings FATAL => qw(uninitialized);
33 use Getopt::Long;
34 use Time::HiRes;
35 use Config;
36 use FindBin;
37 use POSIX qw(WEXITSTATUS WIFEXITED);
38 require $FindBin::RealBin . '/TEST' . ($Config{useithreads} ? 'mt' : 'st') . '.pm';
39 require $FindBin::RealBin . '/TESTlib.pm';
40 # TESTlib.pm
41 use subs qw(
42 file_get_contents
43 get_diff_flags
44 init_tmpdir
45 mytmpfile
46 read_config_h
47 result_failed
48 result_passed
49 result_skipped
50 result_timed_out
51 run_skip_test
52 skip_config_def1
53 skip_config_undef
54 skip_os
55 test_and_report
56 );
57
58 my $testsdir = $FindBin::RealBin;
59 # These filenames use a prefix and are relative to the temporary directory.
60 my $filename_stdout = 'stdout.txt';
61 my $filename_stderr = 'stderr.txt';
62 my $filename_diags = 'diags.txt';
63
64 use constant {
65 EX_OK => 0,
66 EX_USAGE => 64,
67 };
68
69 #
70 # Make true and false work as Booleans.
71 #
72 use constant true => 1;
73 use constant false => 0;
74
75 # Set these later only if running any tests.
76 my $diff_flags;
77
78 sub usage_text {
79 my $detailed = shift;
80 my $myname = $FindBin::Script;
81
82 my $ret = "Usage: ${myname} [--passed]
83 (run all tests)
84 or: ${myname} --list
85 (print all test labels)
86 or: ${myname} --one <test_label> [--passed]
87 (run one test only)
88 or: ${myname} --config
89 (print the parsed contents of config.h)
90 or: ${myname} --help
91 (print the detailed help screen)
92
93 Options:
94 --passed print the passed tests as well (with timings)
95 ";
96 return $ret unless $detailed;
97 $ret .= "
98 TCPDUMP_BIN and CONFIG_H allow to specify custom paths to respective files
99 if the current working directory is not the directory where the build output
100 files go to. Otherwise by default this script finds the files for both
101 Autoconf and CMake, both in-tree and out-of-tree builds.
102
103 TESTRUN_JOBS allows to specify the number of tester threads (1 by default).
104 ";
105 return $ret;
106 }
107
108 my $config_h = defined $ENV{CONFIG_H} ? $ENV{CONFIG_H} : './config.h';
109 my $only_one = undef;
110 my $only_list = 0;
111 my $print_passed = 0;
112 if (! GetOptions (
113 'one=s' => \$only_one,
114 'list' => \$only_list,
115 'passed' => \$print_passed,
116 'config' => sub {
117 my %config = read_config_h $config_h;
118 printf "%-50s %s\n", $_, $config{$_} foreach sort keys %config;
119 exit EX_OK;
120 },
121 'help' => sub {print STDOUT usage_text 1; exit EX_OK;},
122 )) {
123 print STDERR usage_text 0;
124 exit EX_USAGE;
125 };
126
127 #
128 # Were we told where to find tcpdump?
129 #
130 my $TCPDUMP;
131 if (defined $ENV{TCPDUMP_BIN}) {
132 $TCPDUMP = $ENV{TCPDUMP_BIN};
133 } else {
134 #
135 # No. Use the appropriate path.
136 #
137 if ($^O eq 'msys') {
138 #
139 # XXX - assume, for now, a Visual Studio debug build, so that
140 # tcpdump is in the Debug subdirectory.
141 #
142 $TCPDUMP = "Debug\\tcpdump.exe"
143 } else {
144 $TCPDUMP = "./tcpdump"
145 }
146 }
147
148 sub pipe_tcpdump {
149 my $option = shift;
150 open (OPT_PIPE, "$TCPDUMP $option |") or die "ERROR: piping tcpdump $option failed at open\n";
151 my $ret = <OPT_PIPE>;
152 close (OPT_PIPE) or die "ERROR: piping tcpdump $option failed at close\n";
153 return $ret;
154 }
155
156 # Get the type of floating-point arithmetic we're doing.
157 my $fptype = pipe_tcpdump ('--fp-type') == '9877.895' ? 1 : 2;
158 printf "%s --fp-type => %s\n", $TCPDUMP, $fptype;
159
160 # Get the size of size_t in bits.
161 my $time_t_size = int (pipe_tcpdump '--time-t-size');
162 printf "%s --time-t-size => %s\n", $TCPDUMP, $time_t_size;
163
164 # Initialize now so that the skip functions in TESTlib.pm (and therefore the
165 # test declarations below) work as intended.
166 read_config_h ($config_h);
167
168 sub skip_fptype_not {
169 my $val = shift;
170 return $fptype != $val ? "fp-type!=$val" : '';
171 }
172
173 sub skip_time_t_not {
174 my $val = shift;
175 return $time_t_size != $val ? "time_t is not ${val}-bit" : '';
176 }
177
178 my @decode_tests = (
179 # -------- formerly crypto.tests --------
180 # Only attempt OpenSSL-specific tests when compiled with the library.
181 # Reading the secret(s) from a file does not work with Capsicum.
182
183 {
184 skip => skip_config_undef ('HAVE_LIBCRYPTO'),
185 name => 'esp1',
186 input => '02-sunrise-sunset-esp.pcap',
187 output => 'esp1.out',
188 args => '-E "0x12345678@192.1.2.45 3des-cbc-hmac96:0x4043434545464649494a4a4c4c4f4f515152525454575758"'
189 },
190
191 {
192 skip => skip_config_undef ('HAVE_LIBCRYPTO'),
193 name => 'esp2',
194 input => '08-sunrise-sunset-esp2.pcap',
195 output => 'esp2.out',
196 args => '-E "0x12345678@192.1.2.45 3des-cbc-hmac96:0x43434545464649494a4a4c4c4f4f51515252545457575840,0xabcdabcd@192.0.1.1 3des-cbc-hmac96:0x434545464649494a4a4c4c4f4f5151525254545757584043"'
197 },
198
199 {
200 skip => skip_config_undef ('HAVE_LIBCRYPTO'),
201 name => 'esp3',
202 input => '02-sunrise-sunset-esp.pcap',
203 output => 'esp1.out',
204 args => '-E "3des-cbc-hmac96:0x4043434545464649494a4a4c4c4f4f515152525454575758"',
205 },
206
207 {
208 skip => (skip_config_undef ('HAVE_LIBCRYPTO') || skip_config_def1 ('HAVE_CAPSICUM')),
209 name => 'esp4',
210 input => '08-sunrise-sunset-esp2.pcap',
211 output => 'esp2.out',
212 args => "-E 'file ${testsdir}/esp-secrets.txt'",
213 },
214
215 {
216 skip => (skip_config_undef ('HAVE_LIBCRYPTO') || skip_config_def1 ('HAVE_CAPSICUM')),
217 name => 'esp5',
218 input => '08-sunrise-sunset-aes.pcap',
219 output => 'esp5.out',
220 args => "-E 'file ${testsdir}/esp-secrets.txt'",
221 },
222
223 {
224 skip => (skip_config_undef ('HAVE_LIBCRYPTO') || skip_config_def1 ('HAVE_CAPSICUM')),
225 name => 'espudp1',
226 input => 'espudp1.pcap',
227 output => 'espudp1.out',
228 args => "-E 'file ${testsdir}/esp-secrets.txt'",
229 },
230
231 {
232 skip => (skip_config_undef ('HAVE_LIBCRYPTO') || skip_config_def1 ('HAVE_CAPSICUM')),
233 name => 'ikev2pI2',
234 input => 'ikev2pI2.pcap',
235 output => 'ikev2pI2.out',
236 args => "-v -v -v -v -E 'file ${testsdir}/ikev2pI2-secrets.txt'",
237 },
238
239 {
240 skip => (skip_config_undef ('HAVE_LIBCRYPTO') || skip_config_def1 ('HAVE_CAPSICUM')),
241 name => 'isakmp4',
242 input => 'isakmp4500.pcap',
243 output => 'isakmp4.out',
244 args => "-E 'file ${testsdir}/esp-secrets.txt'",
245 },
246
247 {
248 skip => skip_config_undef ('HAVE_LIBCRYPTO'),
249 name => 'bgp-as-path-oobr-ssl',
250 input => 'bgp-as-path-oobr.pcap',
251 output => 'bgp-as-path-oobr-ssl.out',
252 args => '-vvv -e'
253 },
254
255 {
256 skip => skip_config_undef ('HAVE_LIBCRYPTO'),
257 name => 'bgp-aigp-oobr-ssl',
258 input => 'bgp-aigp-oobr.pcap',
259 output => 'bgp-aigp-oobr-ssl.out',
260 args => '-vvv -e'
261 },
262
263 {
264 skip => skip_config_def1 ('HAVE_LIBCRYPTO'),
265 name => 'bgp-as-path-oobr-nossl',
266 input => 'bgp-as-path-oobr.pcap',
267 output => 'bgp-as-path-oobr-nossl.out',
268 args => '-vvv -e'
269 },
270
271 {
272 skip => skip_config_def1 ('HAVE_LIBCRYPTO'),
273 name => 'bgp-aigp-oobr-nossl',
274 input => 'bgp-aigp-oobr.pcap',
275 output => 'bgp-aigp-oobr-nossl.out',
276 args => '-vvv -e'
277 },
278
279 # -------- formerly isis-seg-fault-1-v.tests --------
280 # This "verbose" ISIS protocol test involves a float calculation that
281 # may produce a slightly different result depending on the compiler and
282 # the version of the instruction set for which it's generating code (see
283 # GitHub issue #333 for another example). The test is done only if we have
284 # a floating-point type, as reported by "./tcpdump --fp-type", of FPTYPE1.
285 #
286 # XXX - this works on my 32-bit x86 Linux virtual machine, so do this
287 # regardless of the floating-point type, so always do this. If it
288 # fails on some platform, we'll need to tweak tcpdump and tests/TESTrun
289 # to check for *that* floating-point difference.
290
291 {
292 name => 'isis-seg-fault-1-v',
293 input => 'isis-seg-fault-1.pcapng',
294 output => 'isis-seg-fault-1-v.out',
295 args => '-v'
296 },
297
298 # -------- formerly lmp-v.tests --------
299 # The "verbose" Link Management Protocol test involves a float calculation that
300 # may produce a slightly different result depending on the compiler and the
301 # version of the instruction set for which it's generating code (see GitHub
302 # issue #333). The test is done with an output file that depends on the
303 # floating-point type, as reported by "./tcpdump --fp-type".
304
305 {
306 skip => skip_fptype_not (1),
307 name => 'lmp-v-fptype1',
308 input => 'lmp.pcap',
309 output => 'lmp-v-fptype1.out',
310 args => '-T lmp -v'
311 },
312 {
313 skip => skip_fptype_not (2),
314 name => 'lmp-v-fptype2',
315 input => 'lmp.pcap',
316 output => 'lmp-v-fptype2.out',
317 args => '-T lmp -v'
318 },
319
320 # -------- formerly non-bsd.tests --------
321 # This specific test fails on OpenBSD because the .pcap file uses DLT_RAW,
322 # which OpenBSD treats as DLT_LOOP.
323
324 {
325 skip => skip_os ('openbsd'),
326 name => 'heap-overflow-1',
327 input => 'heap-overflow-1.pcap',
328 output => 'heap-overflow-1.out',
329 args => '-v'
330 },
331
332 # -------- formerly smb.tests --------
333 # Only attempt OpenSSL-specific tests when compiled with the library.
334 # Reading the secret(s) from a file does not work with Capsicum.
335
336 # EAP tests
337 {
338 skip => skip_config_undef ('ENABLE_SMB'),
339 name => 'eapon1',
340 input => 'eapon1.pcap',
341 output => 'eapon1.out',
342 },
343
344 {
345 skip => skip_config_def1 ('ENABLE_SMB'),
346 name => 'eapon1-nosmb',
347 input => 'eapon1.pcap',
348 output => 'eapon1-nosmb.out',
349 },
350
351 {
352 skip => skip_config_undef ('ENABLE_SMB'),
353 name => 'eapon1-v',
354 input => 'eapon1.pcap',
355 output => 'eapon1-v.out',
356 args => '-v'
357 },
358
359 {
360 skip => skip_config_def1 ('ENABLE_SMB'),
361 name => 'eapon1-v-nosmb',
362 input => 'eapon1.pcap',
363 output => 'eapon1-v-nosmb.out',
364 args => '-v'
365 },
366
367 # IPX/Netware packets
368 {
369 skip => skip_config_undef ('ENABLE_SMB'),
370 name => 'ipx',
371 input => 'ipx.pcap',
372 output => 'ipx.out',
373 },
374
375 {
376 skip => skip_config_def1 ('ENABLE_SMB'),
377 name => 'ipx-nosmb',
378 input => 'ipx.pcap',
379 output => 'ipx-nosmb.out',
380 },
381
382 # bad packets from Otto Airamo and Antti Levomäki
383 {
384 skip => skip_config_undef ('ENABLE_SMB'),
385 name => 'nbns-valgrind',
386 input => 'nbns-valgrind.pcap',
387 output => 'nbns-valgrind.out',
388 args => '-vvv -e',
389 },
390
391 {
392 skip => skip_config_def1 ('ENABLE_SMB'),
393 name => 'nbns-valgrind-nosmb',
394 input => 'nbns-valgrind.pcap',
395 output => 'nbns-valgrind-nosmb.out',
396 args => '-vvv -e',
397 },
398
399 # bad packets from Junjie Wang
400 {
401 skip => skip_config_undef ('ENABLE_SMB'),
402 name => 'smb_print_trans-oobr1',
403 input => 'smb_print_trans-oobr1.pcap',
404 output => 'smb_print_trans-oobr1.out',
405 args => '-vv',
406 },
407
408 {
409 skip => skip_config_def1 ('ENABLE_SMB'),
410 name => 'smb_print_trans-oobr1-nosmb',
411 input => 'smb_print_trans-oobr1.pcap',
412 output => 'smb_print_trans-oobr1-nosmb.out',
413 args => '-vv',
414 },
415
416 # bad packets from Philippe Antoine
417 {
418 skip => skip_config_undef ('ENABLE_SMB'),
419 name => 'smb_print_trans-oobr2',
420 input => 'smb_print_trans-oobr2.pcap',
421 output => 'smb_print_trans-oobr2.out',
422 args => '-vv',
423 },
424
425 {
426 skip => skip_config_def1 ('ENABLE_SMB'),
427 name => 'smb_print_trans-oobr2-nosmb',
428 input => 'smb_print_trans-oobr2.pcap',
429 output => 'smb_print_trans-oobr2-nosmb.out',
430 args => '-vv',
431 },
432
433 # bad packets from Luis Rocha
434 {
435 skip => skip_config_undef ('ENABLE_SMB'),
436 name => 'smb_data_print-oobr',
437 input => 'smb_data_print-oobr.pcapng',
438 output => 'smb_data_print-oobr.out',
439 args => '-vv',
440 },
441
442 {
443 skip => skip_config_def1 ('ENABLE_SMB'),
444 name => 'smb_data_print-oobr-nosmb',
445 input => 'smb_data_print-oobr.pcapng',
446 output => 'smb_data_print-oobr-nosmb.out',
447 args => '-vv',
448 },
449
450 {
451 skip => skip_config_undef ('ENABLE_SMB'),
452 name => 'smb_data_print-segv',
453 input => 'smb_data_print-segv.pcap',
454 output => 'smb_data_print-segv.out',
455 args => '-vv',
456 },
457
458 {
459 skip => skip_config_def1 ('ENABLE_SMB'),
460 name => 'smb_data_print-segv-nosmb',
461 input => 'smb_data_print-segv.pcap',
462 output => 'smb_data_print-segv-nosmb.out',
463 args => '-vv',
464 },
465
466 # WCCP redirect over GRE
467 {
468 skip => skip_config_undef ('ENABLE_SMB'),
469 name => 'wccp_redirect_gre',
470 input => 'wccp_redirect_gre.pcap',
471 output => 'wccp_redirect_gre.out',
472 args => '-v',
473 },
474
475 {
476 skip => skip_config_def1 ('ENABLE_SMB'),
477 name => 'wccp_redirect_gre-nosmb',
478 input => 'wccp_redirect_gre.pcap',
479 output => 'wccp_redirect_gre-nosmb.out',
480 args => '-v',
481 },
482
483 # -------- formerly time.tests --------
484 # The packet time when > 2038-01-19T03:14:07Z cannot be correctly printed
485 # if time_t size is 32 bits (overflow).
486 # Some tests are run only if time_t is 64-bit. it depends on the
487 # output of "./tcpdump --time-t-size" (32 or 64).
488
489 # A 32-bit unsigned time_t goes until 2106-02-07T06:28:15Z.
490 # All values above require a pcapng file.
491
492 {
493 name => 'time_2038',
494 input => 'time_2038.pcap',
495 output => 'time_2038.out',
496 args => '-q'
497 },
498 {
499 name => 'time_2038_max',
500 input => 'time_2038_max.pcap',
501 output => 'time_2038_max.out',
502 args => '-q'
503 },
504 {
505 skip => skip_time_t_not (64),
506 name => 'time_2038_overflow',
507 input => 'time_2038_overflow.pcap',
508 output => 'time_2038_overflow.out',
509 args => '-q'
510 },
511 {
512 skip => skip_time_t_not (64),
513 name => 'time_2039',
514 input => 'time_2039.pcap',
515 output => 'time_2039.out',
516 args => '-q'
517 },
518 {
519 skip => skip_time_t_not (64),
520 name => 'time_2106',
521 input => 'time_2106.pcap',
522 output => 'time_2106.out',
523 args => '-q'
524 },
525 {
526 skip => skip_time_t_not (64),
527 name => 'time_2106_max',
528 input => 'time_2106_max.pcap',
529 output => 'time_2106_max.out',
530 args => '-q'
531 },
532 {
533 skip => skip_time_t_not (64),
534 name => 'time_2106_overflow',
535 input => 'time_2106_overflow.pcapng',
536 output => 'time_2106_overflow.out',
537 args => '-q'
538 },
539 {
540 skip => skip_time_t_not (64),
541 name => 'time_2107',
542 input => 'time_2107.pcapng',
543 output => 'time_2107.out',
544 args => '-q'
545 },
546 {
547 skip => skip_time_t_not (64),
548 name => 'time_2106_overflow-tt',
549 input => 'time_2106_overflow.pcapng',
550 output => 'time_2106_overflow-tt.out',
551 args => '-tt -q SPECIAL_t'
552 },
553 {
554 skip => skip_time_t_not (64),
555 name => 'time_2107-tt',
556 input => 'time_2107.pcapng',
557 output => 'time_2107-tt.out',
558 args => '-tt -q SPECIAL_t'
559 },
560 {
561 skip => skip_time_t_not (64),
562 name => 'lspping-fec-ldp-v',
563 input => 'lspping-fec-ldp.pcap',
564 output => 'lspping-fec-ldp-v.out',
565 args => '-v'
566 },
567 {
568 skip => skip_time_t_not (64),
569 name => 'lspping-fec-ldp-vv',
570 input => 'lspping-fec-ldp.pcap',
571 output => 'lspping-fec-ldp-vv.out',
572 args => '-vv'
573 },
574 {
575 skip => skip_time_t_not (64),
576 name => 'lspping-fec-rsvp-v',
577 input => 'lspping-fec-rsvp.pcap',
578 output => 'lspping-fec-rsvp-v.out',
579 args => '-v'
580 },
581 {
582 skip => skip_time_t_not (64),
583 name => 'lspping-fec-rsvp-vv',
584 input => 'lspping-fec-rsvp.pcap',
585 output => 'lspping-fec-rsvp-vv.out',
586 args => '-vv'
587 },
588 {
589 skip => skip_time_t_not (64),
590 name => 'ntp-v',
591 input => 'ntp.pcap',
592 output => 'ntp-v.out',
593 args => '-v'
594 },
595 );
596
597 sub decode_exit_status {
598 my $r = shift;
599 my $status;
600 my $coredump = false;
601 if ($^O eq 'msys') {
602 #
603 # On Windows, the return value of system is the lower 8
604 # bits of the exit status of the process, shifted left
605 # 8 bits.
606 #
607 # If the process crashed, rather than exiting, the
608 # exit status will be one of the EXCEPTION_ values
609 # listed in the documentation for the GetExceptionCode()
610 # macro.
611 #
612 # Those are defined as STATUS_ values, which should have
613 # 0xC in the topmost 4 bits (being fatal error
614 # statuses); some of them have a value that fits in
615 # the lower 8 bits. We could, I guess, assume that
616 # any value that 1) isn't returned by tcpdump and 2)
617 # corresponds to the lower 8 bits of a STATUS_ value
618 # used as an EXCEPTION_ value indicates that tcpdump
619 # exited with that exception.
620 #
621 # However, as we're running tcpdump with system, which
622 # runs the command through cmd.exe, and as cmd.exe
623 # doesn't map the command's exit code to its own exit
624 # code in any straightforward manner, we can't get
625 # that information in any case, so there's no point
626 # in trying to interpret it in that fashion.
627 #
628 $status = $r >> 8;
629 } else {
630 #
631 # On UN*Xes, the return status is a POSIX as filled in
632 # by wait() or waitpid().
633 #
634 # POSIX offers some calls for analyzing it, such as
635 # WIFSIGNALED() to test whether it indicates that the
636 # process was terminated by a signal, WTERMSIG() to
637 # get the signal number from it, WIFEXITED() to test
638 # whether it indicates that the process exited normally,
639 # and WEXITSTATUS() to get the exit status from it.
640 #
641 # POSIX doesn't standardize core dumps, so the POSIX
642 # calls can't test whether a core dump occurred.
643 # However, all the UN*Xes we are likely to encounter
644 # follow Research UNIX in this regard, with the exit
645 # status containing either 0 or a signal number in
646 # the lower 7 bits, with 0 meaning "exited rather
647 # than being terminated by a signal", the "core dumped"
648 # flag in the 0x80 bit, and, if the signal number is
649 # 0, the exit status in the next 8 bits up.
650 #
651 # This should be cleaned up to use the POSIX calls
652 # from the Perl library - and to define an additional
653 # WCOREDUMP() call to test the "core dumped" bit and
654 # use that.
655 #
656 # But note also that, as we're running tcpdump with
657 # system, which runs the command through a shell, if
658 # tcpdump crashes, we'll only know that if the shell
659 # maps the signal indication and uses that as its
660 # exit status.
661 #
662 # The good news is that the Bourne shell, and compatible
663 # shells, have traditionally done that. If the process
664 # for which the shell reports the exit status terminates
665 # with a signal, it adds 128 to the signal number and
666 # returns that as its exit status. (This is why the
667 # "this is now working right" behavior described in a
668 # comment below is occurring.)
669 #
670 # As tcpdump itself never returns with an exit status
671 # >= 128, we can try checking for an exit status with
672 # the 0x80 bit set and, if we have one, get the signal
673 # number from the lower 7 bits of the exit status. We
674 # can't get the "core dumped" indication from the
675 # shell's exit status; all we can do is check whether
676 # there's a core file.
677 #
678 $coredump = $r & 127 if ($r & 128);
679 # This works as intended only if the caller has removed any
680 # pre-existing core dumps before running the command.
681 $coredump = 'present' if (! $coredump && -f 'core');
682 $status = WEXITSTATUS ($r) if WIFEXITED ($r);
683 }
684 return ($status, $coredump);
685 }
686
687 sub run_decode_test {
688 my $test = shift;
689 my $input = $testsdir . '/' . $test->{input};
690 my $output = $testsdir . '/' . $test->{output};
691
692 # we used to do this as a nice pipeline, but the problem is that $r fails to
693 # to be set properly if the tcpdump core dumps.
694 #
695 # Furthermore, on Windows, fc can't read the standard input, so we
696 # can't do it as a pipeline in any case.
697
698 unlink 'core';
699 my $cmdline = sprintf (
700 '%s -# -n -r "%s" %s >"%s" 2>"%s"',
701 $TCPDUMP,
702 $input,
703 $test->{options},
704 mytmpfile ($filename_stdout),
705 mytmpfile ($filename_stderr)
706 );
707 my $r;
708 my $T;
709 if (! $print_passed) {
710 $r = system $cmdline;
711 } else {
712 my $t0 = Time::HiRes::time;
713 $r = system $cmdline;
714 $T = Time::HiRes::time - $t0;
715 }
716
717 return result_failed ('failed to run tcpdump', $!) if $r == -1;
718
719 if ($r != 0) {
720 #
721 # Something other than "failed to start".
722 # Something other than "tcpdump opened the file, read it, and
723 # dissected all the packets". What happened?
724 #
725 my ($status, $coredump) = decode_exit_status $r;
726 return result_failed (
727 ($coredump || $status) ?
728 sprintf ('exit code 0x%08x (dump: %d, code: %d)', $r, $coredump, $status) :
729 sprintf ('exit code 0x%08x', $r),
730 file_get_contents mytmpfile $filename_stderr
731 );
732 }
733
734 #
735 # $r == 0
736 # Compare tcpdump's output with what we think it should be.
737 #
738 my $diffstat;
739 $cmdline = sprintf (
740 'diff %s "%s" "%s" >"%s" 2>&1',
741 $diff_flags,
742 $output,
743 mytmpfile ($filename_stdout),
744 mytmpfile ($filename_diags)
745 );
746 $diffstat = WEXITSTATUS (system $cmdline);
747 return result_failed (
748 "diff exited with $diffstat",
749 file_get_contents mytmpfile $filename_diags
750 ) if $diffstat;
751
752 # Anything other than the "reading from" line on stderr fails the test.
753 my $failed = false;
754 my $filename = mytmpfile $filename_stderr;
755 open (ERRORRAW, '<', $filename) || die "ERROR: failed opening ${filename}: $!\n";
756 while (<ERRORRAW>) {
757 next if /^reading from file /o;
758 $failed = true;
759 last;
760 }
761 close (ERRORRAW) || die "ERROR: failed closing '$filename'";;
762 return result_failed (
763 'stderr present',
764 file_get_contents mytmpfile $filename_stderr
765 ) if $failed;
766
767 return result_passed $T;
768 }
769
770 sub request_test {
771 my $testconfig = shift;
772
773 return {
774 label => $testconfig->{name},
775 func => \&run_skip_test,
776 skip => $testconfig->{skip},
777 } if defined $testconfig->{skip} && $testconfig->{skip} ne '';
778
779 my $options = defined ($testconfig->{args}) ? $testconfig->{args} : '';
780 if (index ($options, 'SPECIAL_t') != -1) {
781 # Hack to keep specific time options for tcp-handshake-micro-t, etc.
782 # -t, -tt, etc.
783 $options =~ s/ SPECIAL_t//o;
784 } else {
785 # No specific time option, use -tttt
786 $options .= ' -tttt';
787 }
788
789 return {
790 label => $testconfig->{name},
791 func => \&run_decode_test,
792 input => $testconfig->{input},
793 options => $options,
794 output => $testconfig->{output},
795 };
796 }
797
798 my $fn_testlist = "${testsdir}/TESTLIST";
799 open (TESTLIST, '<', $fn_testlist) || die "ERROR: failed opening ${fn_testlist}: $!\n";
800 while (<TESTLIST>) {
801 next if /^\#/o || /^$/o;
802 my ($name, $input, $output, @options) = split;
803 push @decode_tests, {
804 name => $name,
805 input => $input,
806 output => $output,
807 args => join (' ', @options)
808 };
809 }
810 close (TESTLIST) || die "ERROR failed closing '$fn_testlist'";
811
812 my @ready_to_run;
813 for (@decode_tests) {
814 next if defined ($only_one) && $only_one ne $_->{name};
815 push @ready_to_run, request_test $_
816 }
817
818 if (! scalar @ready_to_run) {
819 die "ERROR: Unknown test case '${only_one}'" if defined $only_one;
820 die 'Internal error: no tests defined to run!'
821 }
822 if ($only_list) {
823 print $_->{label} . "\n" foreach @ready_to_run;
824 exit EX_OK;
825 }
826
827 $diff_flags = get_diff_flags;
828
829 #
830 # Force UTC, so time stamps are printed in a standard time zone, and
831 # tests don't have to be run in the time zone in which the output
832 # file was generated.
833 #
834 $ENV{TZ} = 'GMT0';
835
836 print "Running tests from ${testsdir}\n";
837 print "with ${TCPDUMP}, version:\n";
838 system ("${TCPDUMP} --version") == 0 or die "ERROR: '$TCPDUMP --version' failed to run\n";
839
840 init_tmpdir 'tcpdump_TESTrun';
841 exit test_and_report @ready_to_run;