]> The Tcpdump Group git mirrors - tcpdump/blob - tests/TESTrun
d2dfba851ec3299fb7f639e62a2f52cdb41ee52c
[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
562 sub decode_exit_status {
563 my $r = shift;
564 my $status;
565 my $coredump = false;
566 if ($^O eq 'msys') {
567 #
568 # On Windows, the return value of system is the lower 8
569 # bits of the exit status of the process, shifted left
570 # 8 bits.
571 #
572 # If the process crashed, rather than exiting, the
573 # exit status will be one of the EXCEPTION_ values
574 # listed in the documentation for the GetExceptionCode()
575 # macro.
576 #
577 # Those are defined as STATUS_ values, which should have
578 # 0xC in the topmost 4 bits (being fatal error
579 # statuses); some of them have a value that fits in
580 # the lower 8 bits. We could, I guess, assume that
581 # any value that 1) isn't returned by tcpdump and 2)
582 # corresponds to the lower 8 bits of a STATUS_ value
583 # used as an EXCEPTION_ value indicates that tcpdump
584 # exited with that exception.
585 #
586 # However, as we're running tcpdump with system, which
587 # runs the command through cmd.exe, and as cmd.exe
588 # doesn't map the command's exit code to its own exit
589 # code in any straightforward manner, we can't get
590 # that information in any case, so there's no point
591 # in trying to interpret it in that fashion.
592 #
593 $status = $r >> 8;
594 } else {
595 #
596 # On UN*Xes, the return status is a POSIX as filled in
597 # by wait() or waitpid().
598 #
599 # POSIX offers some calls for analyzing it, such as
600 # WIFSIGNALED() to test whether it indicates that the
601 # process was terminated by a signal, WTERMSIG() to
602 # get the signal number from it, WIFEXITED() to test
603 # whether it indicates that the process exited normally,
604 # and WEXITSTATUS() to get the exit status from it.
605 #
606 # POSIX doesn't standardize core dumps, so the POSIX
607 # calls can't test whether a core dump occurred.
608 # However, all the UN*Xes we are likely to encounter
609 # follow Research UNIX in this regard, with the exit
610 # status containing either 0 or a signal number in
611 # the lower 7 bits, with 0 meaning "exited rather
612 # than being terminated by a signal", the "core dumped"
613 # flag in the 0x80 bit, and, if the signal number is
614 # 0, the exit status in the next 8 bits up.
615 #
616 # This should be cleaned up to use the POSIX calls
617 # from the Perl library - and to define an additional
618 # WCOREDUMP() call to test the "core dumped" bit and
619 # use that.
620 #
621 # But note also that, as we're running tcpdump with
622 # system, which runs the command through a shell, if
623 # tcpdump crashes, we'll only know that if the shell
624 # maps the signal indication and uses that as its
625 # exit status.
626 #
627 # The good news is that the Bourne shell, and compatible
628 # shells, have traditionally done that. If the process
629 # for which the shell reports the exit status terminates
630 # with a signal, it adds 128 to the signal number and
631 # returns that as its exit status. (This is why the
632 # "this is now working right" behavior described in a
633 # comment below is occurring.)
634 #
635 # As tcpdump itself never returns with an exit status
636 # >= 128, we can try checking for an exit status with
637 # the 0x80 bit set and, if we have one, get the signal
638 # number from the lower 7 bits of the exit status. We
639 # can't get the "core dumped" indication from the
640 # shell's exit status; all we can do is check whether
641 # there's a core file.
642 #
643 $coredump = $r & 127 if ($r & 128);
644 # This works as intended only if the caller has removed any
645 # pre-existing core dumps before running the command.
646 $coredump = 'present' if (! $coredump && -f 'core');
647 $status = WEXITSTATUS ($r) if WIFEXITED ($r);
648 }
649 return ($status, $coredump);
650 }
651
652 sub run_decode_test {
653 my $test = shift;
654 my $input = $testsdir . '/' . $test->{input};
655 my $output = $testsdir . '/' . $test->{output};
656
657 # we used to do this as a nice pipeline, but the problem is that $r fails to
658 # to be set properly if the tcpdump core dumps.
659 #
660 # Furthermore, on Windows, fc can't read the standard input, so we
661 # can't do it as a pipeline in any case.
662
663 unlink 'core';
664 my $cmdline = sprintf (
665 '%s -# -n -r "%s" %s >"%s" 2>"%s"',
666 $TCPDUMP,
667 $input,
668 $test->{options},
669 mytmpfile ($filename_stdout),
670 mytmpfile ($filename_stderr)
671 );
672 my $r;
673 my $T;
674 if (! $print_passed) {
675 $r = system $cmdline;
676 } else {
677 my $t0 = Time::HiRes::time;
678 $r = system $cmdline;
679 $T = Time::HiRes::time - $t0;
680 }
681
682 return result_failed ('failed to run tcpdump', $!) if $r == -1;
683
684 if ($r != 0) {
685 #
686 # Something other than "failed to start".
687 # Something other than "tcpdump opened the file, read it, and
688 # dissected all the packets". What happened?
689 #
690 my ($status, $coredump) = decode_exit_status $r;
691 return result_failed (
692 ($coredump || $status) ?
693 sprintf ('exit code 0x%08x (dump: %d, code: %d)', $r, $coredump, $status) :
694 sprintf ('exit code 0x%08x', $r),
695 file_get_contents mytmpfile $filename_stderr
696 );
697 }
698
699 #
700 # $r == 0
701 # Compare tcpdump's output with what we think it should be.
702 #
703 my $diffstat;
704 $cmdline = sprintf (
705 'diff %s "%s" "%s" >"%s" 2>&1',
706 $diff_flags,
707 $output,
708 mytmpfile ($filename_stdout),
709 mytmpfile ($filename_diags)
710 );
711 $diffstat = WEXITSTATUS (system $cmdline);
712 return result_failed (
713 "diff exited with $diffstat",
714 file_get_contents mytmpfile $filename_diags
715 ) if $diffstat;
716
717 # Anything other than the "reading from" line on stderr fails the test.
718 my $failed = false;
719 my $filename = mytmpfile $filename_stderr;
720 open (ERRORRAW, '<', $filename) || die "ERROR: failed opening ${filename}: $!\n";
721 while (<ERRORRAW>) {
722 next if /^reading from file /o;
723 $failed = true;
724 last;
725 }
726 close (ERRORRAW) || die "ERROR: failed closing '$filename'";;
727 return result_failed (
728 'stderr present',
729 file_get_contents mytmpfile $filename_stderr
730 ) if $failed;
731
732 return result_passed $T;
733 }
734
735 sub request_test {
736 my $testconfig = shift;
737
738 return {
739 label => $testconfig->{name},
740 func => \&run_skip_test,
741 skip => $testconfig->{skip},
742 } if defined $testconfig->{skip} && $testconfig->{skip} ne '';
743
744 my $options = defined ($testconfig->{args}) ? $testconfig->{args} : '';
745 if (index ($options, 'SPECIAL_t') != -1) {
746 # Hack to keep specific time options for tcp-handshake-micro-t, etc.
747 # -t, -tt, etc.
748 $options =~ s/ SPECIAL_t//o;
749 } else {
750 # No specific time option, use -tttt
751 $options .= ' -tttt';
752 }
753
754 return {
755 label => $testconfig->{name},
756 func => \&run_decode_test,
757 input => $testconfig->{input},
758 options => $options,
759 output => $testconfig->{output},
760 };
761 }
762
763 my $fn_testlist = "${testsdir}/TESTLIST";
764 open (TESTLIST, '<', $fn_testlist) || die "ERROR: failed opening ${fn_testlist}: $!\n";
765 while (<TESTLIST>) {
766 next if /^\#/o || /^$/o;
767 my ($name, $input, $output, @options) = split;
768 push @decode_tests, {
769 name => $name,
770 input => $input,
771 output => $output,
772 args => join (' ', @options)
773 };
774 }
775 close (TESTLIST) || die "ERROR failed closing '$fn_testlist'";
776
777 my @ready_to_run;
778 for (@decode_tests) {
779 next if defined ($only_one) && $only_one ne $_->{name};
780 push @ready_to_run, request_test $_
781 }
782
783 if (! scalar @ready_to_run) {
784 die "ERROR: Unknown test case '${only_one}'" if defined $only_one;
785 die 'Internal error: no tests defined to run!'
786 }
787 if ($only_list) {
788 print $_->{label} . "\n" foreach @ready_to_run;
789 exit EX_OK;
790 }
791
792 $diff_flags = get_diff_flags;
793
794 #
795 # Force UTC, so time stamps are printed in a standard time zone, and
796 # tests don't have to be run in the time zone in which the output
797 # file was generated.
798 #
799 $ENV{TZ} = 'GMT0';
800
801 print "Running tests from ${testsdir}\n";
802 print "with ${TCPDUMP}, version:\n";
803 system ("${TCPDUMP} --version") == 0 or die "ERROR: '$TCPDUMP --version' failed to run\n";
804
805 init_tmpdir 'tcpdump_TESTrun';
806 exit test_and_report @ready_to_run;