# BST-TIMEOUT: 300 # ~40 tcpdump execs; an exec is the expensive operation here. # # tcpdump 4.99.4 (meta-openembedded/meta-networking, recipes-support/tcpdump) # -- the packet analyser, read side. # # WHAT THIS SUITE ESTABLISHES. tcpdump has two halves and only one of them is # reachable here. Capture mode needs an interface and a peer; -r mode needs # neither -- it reads a saved pcap and prints the decoded packets to stdout, # which is a pure function of the file and the flags. The filter compiler is # likewise reachable without a device: `-r -d ` compiles against # the SAVEFILE'S link type and opens nothing. Capture mode is asserted only for # its refusal, at the bottom: it must report an interface that does not exist # and exit, rather than block. # # THE FIXTURE IS BUILT IN THE GUEST by the awk below, not shipped in data/. # Five Ethernet frames pinned as hex -- IPv4/TCP SYN, IPv4/UDP, ARP request, # IPv4/ICMP echo request and an IPv4/UDP DNS query -- wrapped in a 24-byte # little-endian microsecond-magic global header (DLT_EN10MB, snaplen 65535) and # fixed timestamps. Pinning the FRAME BYTES rather than generating them is what # makes every literal below a statement about tcpdump: the packet is a constant, # so a difference in the decode is a difference in the decoder. The checksums in # those bytes are correct, which is what lets -v assert `cksum ... (correct)` # and -vv assert `[udp sum ok]` -- an arithmetic case tcpdump runs itself. # # Every pinned value comes from a NATIVE x86-64 tcpdump 4.99.4 against libpcap # 1.10.4 driven by this same shell, and it must agree: the savefile format is # fixed, the frames are IEEE 802.3 wire format, the decode is integer arithmetic # and string formatting, and both architectures are little-endian. A difference # is a real defect and not an expected variation. # # TZ IS PINNED because the timestamp is rendered through localtime(3). UTC0 is a # POSIX TZ string, parsed by glibc without any tzdata file, so the times below # are fixed on an image that ships no zoneinfo. # # THE ADDRESSES ARE TEST-NET-1 (192.0.2.0/24, RFC 5737), which has no PTR. That # is what makes the DEFAULT-verbosity cases assertable: tcpdump without -n # resolves addresses, the resolution fails on both architectures, and the # addresses print numerically -- while the PORTS still resolve, out of # /etc/services, which netbase puts on this image (07-netbase). The # default/-n/-nn triple is therefore about name resolution and nothing else: # 80 <-> http and 53 <-> domain move, the addresses do not. # # THE OPENSSL LINE IS ASSERTED PRESENT, and that is this port's one recipe-side # fact. tcpdump's default PACKAGECONFIG turns on --with-crypto for the -M/-E ESP # decryption path. meta-brintos' tcpdump_%.bbappend used to drop it, from a time # when openssl was blocked here (the coro pass refused bn_mod_exp_mont_fixed_top); # openssl 3.5.7 has built and shipped on the glibc arm since BRINTOS-295, and # this image installs it, so the override went and the stock PACKAGECONFIG # resolves. The third --version line is what says the crypto arm linked -- and it # names the openssl the image ships, so it is also the case that goes red if the # two ever drift apart. . "$BST_LIB" # tcpdump_4.99.4.bb builds the one program into ${bindir} -- /usr/bin/tcpdump is # the whole of the runtime package. Its sibling tcpslice, out of the same # meta-networking directory, lands in ${sbindir} instead, which is why # 60-tcpslice.test extends PATH and this suite does not have to. TZ=UTC0 export TZ require tcpdump tcpdump/present # The fixture generator and the byte readers are not the subject. Their absence # is an image regression rather than "tcpdump is not installed", so it is a red # here and not a skip -- a skip would retire the whole port's coverage silently. for _t in awk od dd wc cmp; do command -v "$_t" >/dev/null 2>&1 || { bst_notok tcpdump/harness-$_t "$_t is not on the image; the suite cannot build or read a fixture" exit 0 } done bst_ok tcpdump/harness-present TMP=${TMPDIR:-/tmp} WORK=$TMP/bst-tcpdump.$$ mkdir -p "$WORK" 2>/dev/null if [ ! -d "$WORK" ]; then bst_notok tcpdump/workdir "cannot create $WORK -- the fixtures need somewhere writable" exit 0 fi bst_ok tcpdump/workdir # cd so tcpdump echoes bare filenames in its diagnostics, which is what the # error cases below compare against. cd "$WORK" || { bst_notok tcpdump/workdir-cd "cannot cd $WORK"; exit 0; } # --- the frames -------------------------------------------------------------- # dst 66:77:88:99:aa:bb, src 00:11:22:33:44:55 throughout. # 1 IPv4/TCP 192.0.2.1:1234 > 192.0.2.2:80, SYN, seq 0x11223344, win 8192 # 2 IPv4/UDP 192.0.2.1:5000 > 192.0.2.2:9999, payload "abcdefgh" # 3 ARP who-has 192.0.2.2 tell 192.0.2.1 # 4 IPv4/ICMP 192.0.2.1 > 192.0.2.2, echo request id 0x0abc seq 1 # 5 IPv4/UDP 192.0.2.1:40000 > 192.0.2.3:53, DNS query A? example.com F1=66778899aabb001122334455080045000028111140004006a5bbc0000201c000020204d20050112233440000000050022000c2560000 F2=66778899aabb00112233445508004500002422224000401194a3c0000201c00002021388270f0010af9d6162636465666768 F3=66778899aabb00112233445508060001080006040001001122334455c0000201000000000000c0000202 F4=66778899aabb00112233445508004500002433334000400183a2c0000201c000020208001c6e0abc00013132333435363738 F5=66778899aabb001122334455080045000039444440004011726bc0000201c00002039c4000350025c2914d2a01000001000000000000076578616d706c6503636f6d0000010001 # --- fixture generator ------------------------------------------------------- # mkcap .. ... # No exponentiation and no printf %c: `d = d*256` and \0ddd octal escapes # through printf %b are the forms busybox awk and busybox printf both implement. mkcap() { _o=$1; shift _e=$(awk -v spec="$*" ' function b(x) { return sprintf("\\0%03o", x % 256) } function le32(v, i, s, d) { s=""; d=1; for (i=0;i<4;i++) { s = s b(int(v/d)); d = d*256 } return s } function le16(v, i, s, d) { s=""; d=1; for (i=0;i<2;i++) { s = s b(int(v/d)); d = d*256 } return s } function hx(c) { return index("0123456789abcdef", c) - 1 } function esc(h, i, s) { s = "" for (i = 1; i <= length(h); i += 2) s = s b(hx(substr(h,i,1)) * 16 + hx(substr(h,i+1,1))) return s } BEGIN { out = le32(2712847316) le16(2) le16(4) le32(0) le32(0) le32(65535) le32(1) n = split(spec, a, " ") for (k = 1; k <= n; k++) { split(a[k], p, ".") nb = length(p[3]) / 2 out = out le32(p[1]) le32(p[2]) le32(nb) le32(nb) esc(p[3]) } printf "%s", out }') || return 1 printf '%b' "$_e" > "$_o" } # --- readers ----------------------------------------------------------------- # nth : line n of a captured output. nth() { printf '%s\n' "$2" | awk -v n="$1" 'NR==n{print; exit}'; } # nlines : line count. Empty text is 0 lines, not 1. nlines() { [ -n "$1" ] || { echo 0; return; }; printf '%s\n' "$1" | wc -l | tr -d ' '; } # bpf : the -d disassembly with runs of blank space squeezed to one # space, so the tabs between `jt` and `jf` do not have to be typed below. bpf() { tcpdump -r capA.pcap -d "$@" 2>/dev/null | tr -s ' \t' ' '; } # hexof : the hex bytes out of a -x/-X dump, offsets and ASCII gutter # dropped, joined. A -X line's gutter is separated by two spaces; -x has none. hexof() { printf '%s\n' "$1" | awk ' /^[ \t]*0x[0-9a-f]+:/ { sub(/^[ \t]*0x[0-9a-f]+:[ \t]*/, "") sub(/ .*$/, "") gsub(/ /, "") printf "%s", $0 }' } SPEC="1700000000.100.$F1 1700000001.200.$F2 1700000002.300.$F3 1700000003.400.$F4 1700000004.500.$F5" mkcap capA.pcap $SPEC # --- null controls on the fixture itself ------------------------------------- # Everything below reads tcpdump against these bytes, so a generator that # emitted the wrong thing would show up as a tcpdump defect. These cases are # what separate the two. check_eq tcpdump/fixture-size 371 "$(wc -c < capA.pcap | tr -d ' ')" check_eq tcpdump/fixture-magic d4c3b2a1 "$(dd if=capA.pcap bs=1 count=4 2>/dev/null | od -An -tx1 | tr -d ' \n')" check_eq tcpdump/fixture-snaplen ffff0000 "$(dd if=capA.pcap bs=1 skip=16 count=4 2>/dev/null | od -An -tx1 | tr -d ' \n')" check_eq tcpdump/fixture-frame1-head 66778899 \ "$(dd if=capA.pcap bs=1 skip=40 count=4 2>/dev/null | od -An -tx1 | tr -d ' \n')" # --- the program ------------------------------------------------------------- # The runtime package ships exactly one file and this is it. check tcpdump/installed-in-bindir test -x /usr/bin/tcpdump check_eq tcpdump/resolves-to-bindir /usr/bin/tcpdump "$(command -v tcpdump)" v=$(tcpdump --version 2>&1); vrc=$? if bst_execfail "$v"; then bst_unrun tcpdump/version "$v" else check_eq tcpdump/version-rc 0 "$vrc" check_eq tcpdump/version "tcpdump version 4.99.4" "$(nth 1 "$v")" # tcpdump prints the libpcap it was linked against; this is the port's # evidence that the library dependency resolved rather than being stubbed. case "$(nth 2 "$v")" in "libpcap version 1.10.4"*) bst_ok tcpdump/libpcap-linked ;; *) bst_notok tcpdump/libpcap-linked "libpcap line is: $(_bst_1line "$(nth 2 "$v")")" ;; esac # The stock PACKAGECONFIG ?= "openssl" resolves here, so tcpdump prints a third # line naming the libcrypto it linked. The version is pinned to the openssl # this image installs: a third line naming a DIFFERENT one would mean tcpdump # linked something other than the image's own. case "$(nth 3 "$v")" in "OpenSSL 3.5.7"*) bst_ok tcpdump/openssl-linked ;; "") bst_notok tcpdump/openssl-linked "--version has no OpenSSL line; the crypto arm did not link" ;; *) bst_notok tcpdump/openssl-linked "third --version line is: $(_bst_1line "$(nth 3 "$v")")" ;; esac fi # --- default verbosity ------------------------------------------------------- # The banner ("reading from file ...") goes to stderr, so stdout is the decode # and nothing else. Ports resolve out of /etc/services here: http and domain. # This is the ONE run that resolves, so it is the one run that can hang: a # resolver with nowhere to ask is expected to fail immediately, and bst_probe is # what makes "it did not" a red here instead of a suite timeout that takes every # case after it down as well. bst_probe tcpdump/default-returns "$WORK/default.out" 30 tcpdump -r capA.pcap # bst_probe above already asserted the exit status, so there is no rc case here # -- one that read a variable this shell set to 0 itself could not fail. d=$(cat "$WORK/default.out" 2>/dev/null) if bst_execfail "$d"; then bst_unrun tcpdump/default "$d" else check_eq tcpdump/default-lines 5 "$(nlines "$d")" check_eq tcpdump/default-tcp '22:13:20.000100 IP 192.0.2.1.1234 > 192.0.2.2.http: Flags [S], seq 287454020, win 8192, length 0' "$(nth 1 "$d")" check_eq tcpdump/default-udp '22:13:21.000200 IP 192.0.2.1.5000 > 192.0.2.2.9999: UDP, length 8' "$(nth 2 "$d")" check_eq tcpdump/default-arp '22:13:22.000300 ARP, Request who-has 192.0.2.2 tell 192.0.2.1, length 28' "$(nth 3 "$d")" check_eq tcpdump/default-icmp '22:13:23.000400 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 2748, seq 1, length 16' "$(nth 4 "$d")" check_eq tcpdump/default-dns '22:13:24.000500 IP 192.0.2.1.40000 > 192.0.2.3.domain: 19754+ A? example.com. (29)' "$(nth 5 "$d")" fi # The banner is on stderr, and it names the link type the savefile declares -- # which is how the DLT reached the printer at all. berr=$(tcpdump -r capA.pcap 2>&1 >/dev/null) check_eq tcpdump/banner 'reading from file capA.pcap, link-type EN10MB (Ethernet), snapshot length 65535' "$(nth 1 "$berr")" # --- -n: numeric addresses --------------------------------------------------- n=$(tcpdump -n -r capA.pcap 2>/dev/null) check_eq tcpdump/n-lines 5 "$(nlines "$n")" check_eq tcpdump/n-tcp '22:13:20.000100 IP 192.0.2.1.1234 > 192.0.2.2.80: Flags [S], seq 287454020, win 8192, length 0' "$(nth 1 "$n")" check_eq tcpdump/n-dns '22:13:24.000500 IP 192.0.2.1.40000 > 192.0.2.3.53: 19754+ A? example.com. (29)' "$(nth 5 "$n")" # The addresses were already numeric under default verbosity (TEST-NET-1 has no # PTR), so -n moves the PORTS and nothing else. Asserting the pair is what says # the port lookup happened rather than the whole line being numeric by accident. check_eq tcpdump/n-arp-unchanged "$(nth 3 "$d")" "$(nth 3 "$n")" # --- -nn: numeric ports too -------------------------------------------------- # -n already suppresses the /etc/services lookup for these ports, so -nn adds # nothing here and the two outputs must be identical, byte for byte. nn=$(tcpdump -nn -r capA.pcap 2>/dev/null) check_eq tcpdump/nn-equals-n "$n" "$nn" check_eq tcpdump/nn-dns-port '22:13:24.000500 IP 192.0.2.1.40000 > 192.0.2.3.53: 19754+ A? example.com. (29)' "$(nth 5 "$nn")" # --- -q: quiet --------------------------------------------------------------- # -q drops the protocol detail: the TCP line loses its flags, seq and window, # and the DNS payload is not decoded at all -- it is reported as the UDP # datagram it rides in. q=$(tcpdump -q -r capA.pcap 2>/dev/null) check_eq tcpdump/q-lines 5 "$(nlines "$q")" check_eq tcpdump/q-tcp '22:13:20.000100 IP 192.0.2.1.1234 > 192.0.2.2.http: tcp 0' "$(nth 1 "$q")" check_eq tcpdump/q-dns '22:13:24.000500 IP 192.0.2.1.40000 > 192.0.2.3.domain: UDP, length 29' "$(nth 5 "$q")" # --- -e: the link-layer header ---------------------------------------------- e=$(tcpdump -e -n -r capA.pcap 2>/dev/null) check_eq tcpdump/e-lines 5 "$(nlines "$e")" check_eq tcpdump/e-tcp '22:13:20.000100 00:11:22:33:44:55 > 66:77:88:99:aa:bb, ethertype IPv4 (0x0800), length 54: 192.0.2.1.1234 > 192.0.2.2.80: Flags [S], seq 287454020, win 8192, length 0' "$(nth 1 "$e")" check_eq tcpdump/e-arp '22:13:22.000300 00:11:22:33:44:55 > 66:77:88:99:aa:bb, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.2 tell 192.0.2.1, length 28' "$(nth 3 "$e")" # The frame lengths in those lines are the fixture's own, which is the check # that the caplen reached the link-layer printer intact. check_eq tcpdump/e-dns-length 71 "$(printf '%s\n' "$(nth 5 "$e")" | sed -n 's/.*length \([0-9]*\):.*/\1/p')" # --- -v: the IP header ------------------------------------------------------- # Two lines per IP packet, one for ARP: 4*2 + 1 = 9. v1=$(tcpdump -v -n -r capA.pcap 2>/dev/null) check_eq tcpdump/v-lines 9 "$(nlines "$v1")" check_eq tcpdump/v-ip-header '22:13:20.000100 IP (tos 0x0, ttl 64, id 4369, offset 0, flags [DF], proto TCP (6), length 40)' "$(nth 1 "$v1")" # THE CHECKSUM CASE. tcpdump recomputes the TCP checksum over the pseudo-header # and says so; the fixture's checksum is correct, so anything but `(correct)` # is tcpdump's arithmetic and not the packet. check_eq tcpdump/v-tcp-cksum ' 192.0.2.1.1234 > 192.0.2.2.80: Flags [S], cksum 0xc256 (correct), seq 287454020, win 8192, length 0' "$(nth 2 "$v1")" check_eq tcpdump/v-arp '22:13:22.000300 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.0.2.2 tell 192.0.2.1, length 28' "$(nth 5 "$v1")" check_eq tcpdump/v-icmp-header '22:13:23.000400 IP (tos 0x0, ttl 64, id 13107, offset 0, flags [DF], proto ICMP (1), length 36)' "$(nth 6 "$v1")" # -v does NOT verify the UDP checksum; that is -vv's job, and the pair below is # what separates the two levels. case "$(nth 4 "$v1")" in *"udp sum ok"*) bst_notok tcpdump/v-no-udp-sum "-v reported the UDP checksum, which is -vv's level" ;; *) bst_ok tcpdump/v-no-udp-sum ;; esac # --- -vv: the transport checksums ------------------------------------------- v2=$(tcpdump -vv -n -r capA.pcap 2>/dev/null) check_eq tcpdump/vv-lines 9 "$(nlines "$v2")" check_eq tcpdump/vv-udp-sum ' 192.0.2.1.5000 > 192.0.2.2.9999: [udp sum ok] UDP, length 8' "$(nth 4 "$v2")" check_eq tcpdump/vv-dns-sum ' 192.0.2.1.40000 > 192.0.2.3.53: [udp sum ok] 19754+ A? example.com. (29)' "$(nth 9 "$v2")" # ...and the IP-header line is unchanged between the two levels, so the delta # above is the transport checksum and not a wholesale reformat. check_eq tcpdump/vv-ip-header-same "$(nth 1 "$v1")" "$(nth 1 "$v2")" # --- -x / -X / -A: the payload dumps ----------------------------------------- # -x dumps the IP datagram, not the frame: the 14-byte Ethernet header is gone # and what is left is the fixture's own bytes from offset 14 on. IP1=45000028111140004006a5bbc0000201c000020204d20050112233440000000050022000c2560000 x=$(tcpdump -x -n -c 1 -r capA.pcap 2>/dev/null) check_eq tcpdump/x-first-line "$(nth 1 "$n")" "$(nth 1 "$x")" check_eq tcpdump/x-hex "$IP1" "$(hexof "$x")" # -X is -x plus an ASCII gutter. The hex column must be the same bytes, which # is what says the gutter was added rather than the dump re-derived. bigX=$(tcpdump -X -n -c 1 -r capA.pcap 2>/dev/null) check_eq tcpdump/X-hex "$IP1" "$(hexof "$bigX")" # The gutter itself, on the packet that carries printable payload. X2=$(tcpdump -X -n -c 2 -r capA.pcap 2>/dev/null) case "$X2" in *abcd*efgh*) bst_ok tcpdump/X-ascii-gutter ;; *) bst_notok tcpdump/X-ascii-gutter "the UDP payload 'abcdefgh' is not in the ASCII gutter" ;; esac # -A is the gutter alone, one line per packet, non-printables as dots. a=$(tcpdump -A -n -c 2 -r capA.pcap 2>/dev/null) check_eq tcpdump/A-lines 4 "$(nlines "$a")" check_eq tcpdump/A-udp 'E..$""@.@.............'"'"'.....abcdefgh' "$(nth 4 "$a")" # --- -c: stop after N ------------------------------------------------------- c2=$(tcpdump -c 2 -n -r capA.pcap 2>/dev/null); crc=$? check_eq tcpdump/c-rc 0 "$crc" check_eq tcpdump/c-lines 2 "$(nlines "$c2")" check_eq tcpdump/c-first "$(nth 1 "$n")" "$(nth 1 "$c2")" check_eq tcpdump/c-second "$(nth 2 "$n")" "$(nth 2 "$c2")" # A count larger than the file is not an error and does not pad. c9=$(tcpdump -c 9 -n -r capA.pcap 2>/dev/null) check_eq tcpdump/c-over-count "$n" "$c9" # --- -w: write a savefile --------------------------------------------------- # With no filter, -w copies every packet. Byte-identical output is the strongest # single statement about the reader and the writer together. tcpdump -w out.pcap -r capA.pcap 2>/dev/null check_eq tcpdump/w-rc 0 "$?" check tcpdump/w-byte-exact cmp -s out.pcap capA.pcap # NULL CONTROL for the comparator: it must be able to say no. Without this, # "identical" is equally consistent with a cmp that always agrees. cp capA.pcap flip.pcap printf '%b' '\0377' | dd of=flip.pcap bs=1 seek=60 count=1 conv=notrunc 2>/dev/null if cmp -s flip.pcap capA.pcap; then bst_notok tcpdump/cmp-control-finds-diff "cmp called a one-byte-flipped copy identical" else bst_ok tcpdump/cmp-control-finds-diff fi # ...and the written file reads back through -r to the same decode. check_eq tcpdump/w-reread "$n" "$(tcpdump -n -r out.pcap 2>/dev/null)" # --- the filter, applied on read -------------------------------------------- # Two of the five packets are UDP. This is the filter running against the # savefile, which is the same BPF program -d prints below. u=$(tcpdump -n -r capA.pcap udp 2>/dev/null) check_eq tcpdump/filter-udp-lines 2 "$(nlines "$u")" check_eq tcpdump/filter-udp-first "$(nth 2 "$n")" "$(nth 1 "$u")" check_eq tcpdump/filter-udp-second "$(nth 5 "$n")" "$(nth 2 "$u")" check_eq tcpdump/filter-arp-lines 1 "$(nlines "$(tcpdump -n -r capA.pcap arp 2>/dev/null)")" check_eq tcpdump/filter-none-lines 0 "$(nlines "$(tcpdump -n -r capA.pcap 'port 12345' 2>/dev/null)")" # --- -d: the compiled filter, with no device -------------------------------- # `-r -d ` compiles against the savefile's link type, so no # interface is opened and nothing here needs a device. `ret #65535` is the # savefile's own snaplen reaching the accept branch. bt=$(bpf tcp) check_eq tcpdump/d-tcp-lines 12 "$(nlines "$bt")" check_eq tcpdump/d-tcp-load '(000) ldh [12]' "$(nth 1 "$bt")" check_eq tcpdump/d-tcp-proto '(003) jeq #0x6 jt 10 jf 11' "$(nth 4 "$bt")" check_eq tcpdump/d-tcp-accept '(010) ret #65535' "$(nth 11 "$bt")" check_eq tcpdump/d-tcp-reject '(011) ret #0' "$(nth 12 "$bt")" bh=$(bpf host 192.0.2.1) check_eq tcpdump/d-host-lines 14 "$(nlines "$bh")" # The address is matched as a 32-bit word at the IPv4 source and destination # offsets, and again at the ARP sender/target offsets -- the four `jeq` on # 0xc0000201 are what "host" means. check_eq tcpdump/d-host-src '(003) jeq #0xc0000201 jt 12 jf 4' "$(nth 4 "$bh")" check_eq tcpdump/d-host-dst '(005) jeq #0xc0000201 jt 12 jf 13' "$(nth 6 "$bh")" check_eq tcpdump/d-host-arp '(007) jeq #0x8035 jt 8 jf 13' "$(nth 8 "$bh")" bp=$(bpf port 80) check_eq tcpdump/d-port-lines 24 "$(nlines "$bp")" # The IPv4 arm loads the transport ports through the variable IHL, which is the # one part of "port" that is not a fixed offset. check_eq tcpdump/d-port-frag '(016) jset #0x1fff jt 23 jf 17' "$(nth 17 "$bp")" check_eq tcpdump/d-port-ihl '(017) ldxb 4*([14]&0xf)' "$(nth 18 "$bp")" check_eq tcpdump/d-port-src '(019) jeq #0x50 jt 22 jf 20' "$(nth 20 "$bp")" bc=$(bpf tcp and port 80 and host 192.0.2.1) check_eq tcpdump/d-compound-lines 17 "$(nlines "$bc")" # The compound program is not the concatenation of the three: the IPv6 arms of # `tcp` and `port` are gone, because `tcp and ...` proved the ethertype first. check_eq tcpdump/d-compound-ethertype '(001) jeq #0x800 jt 2 jf 16' "$(nth 2 "$bc")" check_eq tcpdump/d-compound-host '(012) jeq #0xc0000201 jt 15 jf 13' "$(nth 13 "$bc")" check_eq tcpdump/d-compound-accept '(015) ret #65535' "$(nth 16 "$bc")" # An invalid filter is a compile error, reported and non-zero -- not an empty # result, which is what a compiler that swallowed the expression would give. bad=$(tcpdump -n -r capA.pcap 'tcp porrt 80' 2>&1); brc=$? check_eq tcpdump/bad-filter-rc 1 "$brc" case "$bad" in *"can't parse filter expression: syntax error"*) bst_ok tcpdump/bad-filter-message ;; *) bst_notok tcpdump/bad-filter-message "$(_bst_1line "$bad")" ;; esac # --- inputs the reader must refuse ------------------------------------------ # A tool that answered "no packets, no error" to these would pass every case # above and be useless. e1=$(tcpdump -n -r nosuch.pcap 2>&1); check_eq tcpdump/absent-input-rc 1 "$?" case "$e1" in *"nosuch.pcap: No such file or directory"*) bst_ok tcpdump/absent-input-message ;; *) bst_notok tcpdump/absent-input-message "$(_bst_1line "$e1")" ;; esac # A bad magic number is refused at the header, before any packet is printed. printf '%b' '\0336\0255\0276\0357' > badmagic.pcap dd if=capA.pcap bs=1 skip=4 2>/dev/null >> badmagic.pcap check_eq tcpdump/badmagic-size 371 "$(wc -c < badmagic.pcap | tr -d ' ')" bm=$(tcpdump -n -r badmagic.pcap 2>&1); check_eq tcpdump/badmagic-rc 1 "$?" check_eq tcpdump/badmagic-no-packets 0 "$(nlines "$(tcpdump -n -r badmagic.pcap 2>/dev/null)")" case "$bm" in *"unknown file format"*) bst_ok tcpdump/badmagic-message ;; *) bst_notok tcpdump/badmagic-message "$(_bst_1line "$bm")" ;; esac # A file cut mid-record is the harder case: the first four packets are whole and # must be PRINTED, and only then is the short read reported. "Printed four, then # failed" and "failed outright" are different answers and only the line count # tells them apart. dd if=capA.pcap of=trunc.pcap bs=1 count=336 2>/dev/null check_eq tcpdump/trunc-size 336 "$(wc -c < trunc.pcap | tr -d ' ')" tout=$(tcpdump -n -r trunc.pcap 2>/dev/null) terr=$(tcpdump -n -r trunc.pcap 2>&1 >/dev/null); trc=$? check_eq tcpdump/trunc-rc 1 "$trc" check_eq tcpdump/trunc-lines 4 "$(nlines "$tout")" check_eq tcpdump/trunc-last "$(nth 4 "$n")" "$(nth 4 "$tout")" case "$terr" in *"truncated dump file; tried to read 71 captured bytes, only got 36"*) bst_ok tcpdump/trunc-message ;; *) bst_notok tcpdump/trunc-message "$(_bst_1line "$terr")" ;; esac # --- capture mode, asserted only for its refusal ---------------------------- # The one thing capture mode owes a machine with no interfaces: report the # device it was given and exit, rather than block. -c 1 bounds it so a blocking # implementation is a suite timeout rather than a hang with no evidence, and the # exact wording is NOT pinned -- it differs with why the open failed (no such # device, no permission), and all of those are the same correct answer here. iout=$(tcpdump -n -i nosuchif0 -c 1 2>&1); irc=$? check tcpdump/capture-refusal-rc test "$irc" -ne 0 case "$iout" in *nosuchif0*) bst_ok tcpdump/capture-refusal-names-device ;; *) bst_notok tcpdump/capture-refusal-names-device "$(_bst_1line "$iout")" ;; esac case "$iout" in *"22:13:"*) bst_notok tcpdump/capture-printed-nothing "capture mode printed a decode with no interface" ;; *) bst_ok tcpdump/capture-printed-nothing ;; esac cd "$TMP" 2>/dev/null rm -rf "$WORK"