# BST-TIMEOUT: 900 # ~35 cases; 9 upstream check programs + ~20 dnet pipelines # # libdnet 1.18.0 -- "a portable interface to low-level networking routines": # raw IP and Ethernet transmission, the kernel ARP cache, routing table and # firewall ruleset, and a set of pure address / blob / random helpers under all # of it. # # THE EVIDENCE HERE IS OF TWO KINDS, AND THE SECOND EXISTS BECAUSE THE FIRST IS # HALF EMPTY. # # 1. UPSTREAM'S OWN check(1) PROGRAMS, RUN ON TARGET. test/check/ ships nine # programs that `make all` already builds (noinst_PROGRAMS) and `make # install` then discards; the bbappend installs them instead. They are one # case each rather than one lump, so a failure names the subsystem. # # THEY ARE NOT NINE PROGRAMS' WORTH OF ASSERTION. Counted mechanically over # the nine sources, upstream declares 58 tests of which EXACTLY 29 HAVE # EMPTY BODIES -- `START_TEST(x) { } END_TEST`, which check(1) counts and # reports as a pass: # # check_addr 13/13 real check_ip 1/7 real # check_blob 6/6 real check_intf 2/6 real # check_rand 4/4 real check_arp 1/6 real # check_route 1/5 real # check_fw 1/6 real # check_eth 0/5 real # # check_eth is the extreme case: five empty bodies, so it prints # "100%: Checks: 5, Failures: 0" while making no assertion whatsoever. Each # case below therefore pins the program's EXACT declared check count, not # just its verdict -- the day upstream fills a stub in, the count changes and # the pin goes red, and someone re-reads a result that had been free. # # EVERY ONE OF THEM RUNS UNDER CK_FORK=no, AND THAT IS NOT A STYLE CHOICE. # check(1)'s default fork mode arms a per-test timeout with timer_create(2), # which is ENOSYS on wasm32-hwjs (brintos/linux#40, filed by BRINTOS-221), # so every check binary dies at its first test in the default mode. NOFORK # costs the crash isolation and per-test timeouts that are most of check's # value; it is the documented workaround and it is what 76-libcheck.test # established works here. # # 2. THE `dnet` COMMAND-LINE TOOL, DRIVEN AS A FILTER. This is where the real # functional evidence is, and it is evidence the stub half cannot give. # upstream's test/dnet/ builds an sbin program that composes datagrams as a # Unix filter, so a shell can drive the library's packing and checksum paths # end to end with no compiler on the image. The byte strings asserted below # were captured from an x86_64 build of the SAME SRCREV (3dfbe88) configured # the same way, so they are cross-arm agreement, not this arm marking its own # homework. They cover: address parsing (v4, v6, MAC), C-escape decoding, the # ARP/Ethernet/IP/TCP/UDP/ICMP header packers, and -- the part no check_* # program touches at all -- THE IP, TCP, UDP AND ICMP CHECKSUM ROUTINES, # which are pure computation over a byte buffer and exactly where a 32-bit # arm with different integer promotion would go wrong. # # WHAT IS NOT COVERED, DELIBERATELY: actually transmitting a packet. `dnet send` # needs a raw socket and a route to somewhere, and the gate has neither a # network nor a peer. It is stated as a gap in the test-plan entry rather than # papered over with a send-to-nowhere case that would only assert that the send # failed. The kernel-interface probes below report what the platform answers, # and are pinned by cause where the cause is known. . "$BST_LIB" require dnet libdnet/present # libexecdir is /usr/libexec on this distro, but locate the directory rather # than hardcoding it: a layer that moves libexecdir should make these cases fail # loudly, not vanish. Absent is `not ok`, never `skip` -- the programs are the # ticket's acceptance gate, and a missing directory means the package did not # ship, which is a failure and not a legitimate absence. UPSTREAM="check_addr check_blob check_rand check_ip check_intf check_arp check_route check_fw check_eth" TDIR= for d in /usr/libexec/libdnet-tests /usr/lib/libdnet-tests /usr/libexec/libdnet; do [ -d "$d" ] && { TDIR=$d; break; } done hexof() { od -An -v -tx1 | tr -d ' \n'; } # --------------------------------------------------------------------------- # 1. Packaging and the tool itself. # `dnet` with no arguments prints its module list on STDERR and exits 1 -- # main() calls print_usage() then exit(1), so a non-zero status here is upstream # working correctly, not a failure. check_out requires rc=0 and would therefore # report a red for the one behaviour this case exists to confirm; assert the # status and the text together instead. Measured 2026-08-11: both cases failed # exactly this way before the fix. _u=$(dnet 2>&1); _urc=$? usage_case() { if bst_execfail "$_u"; then bst_unrun "$1" "$_u"; return; fi case "$_u" in *"$2"*) if [ "$_urc" -eq 1 ]; then bst_ok "$1" else bst_notok "$1" "usage text present but rc=$_urc, want 1"; fi ;; *) bst_notok "$1" "rc=$_urc, no '$2' in: $(_bst_1line "$_u")" ;; esac } usage_case libdnet/dnet-usage "Payload generation commands" usage_case libdnet/dnet-usage-kernel "Kernel interface commands" # dnet-config is the package's ${bindir} half; --libs is the one subcommand that # cannot answer without the install layout being right. if command -v dnet-config >/dev/null 2>&1; then check_out libdnet/dnet-config-libs "-ldnet" dnet-config --libs else bst_notok libdnet/dnet-config-libs "dnet-config not installed (libdnet package missing from the image?)" fi # --------------------------------------------------------------------------- # 2. Payload generation -- pure library code, deterministic, cross-arm pinned. check_eq libdnet/addr-ip4 "0a000001" "$(dnet addr 10.0.0.1 /dev/null | hexof)" check_eq libdnet/addr-mac "001122334455" "$(dnet addr 00:11:22:33:44:55 /dev/null | hexof)" check_eq libdnet/addr-ip6 "fe800000000000000000000000000001" "$(dnet addr fe80::1 /dev/null | hexof)" # A malformed address must be refused, not silently packed as zeroes. check_rc libdnet/addr-reject 1 dnet addr 999.999.999.999 check_eq libdnet/hex-escape "41424344" "$(dnet hex '\x41\x42\x43\x44' /dev/null | hexof)" check_eq libdnet/rand-len "16" "$(dnet rand 16 /dev/null | wc -c | tr -d ' ')" # arc4 must not return the same 16 bytes twice. A constant stream would satisfy # every length check above and is precisely what a broken seed looks like. _r1=$(dnet rand 16 /dev/null | hexof) _r2=$(dnet rand 16 /dev/null | hexof) if [ -n "$_r1" ] && [ "$_r1" != "$_r2" ]; then bst_ok libdnet/rand-distinct else bst_notok libdnet/rand-distinct "two rand 16 draws identical or empty: '$_r1' '$_r2'" fi # --------------------------------------------------------------------------- # 3. Header packing and checksums -- the functional core, cross-arm pinned # against an x86_64 build of the same SRCREV. check_eq libdnet/arp-pack \ "0001080006040001000d0e0a0d000a0000030000000000000a000004" \ "$(dnet arp op req sha 0:d:e:a:d:0 spa 10.0.0.3 tpa 10.0.0.4 /dev/null | hexof)" check_eq libdnet/eth-pack \ "ffffffffffff000d0e0a0d000806" \ "$(dnet eth type arp src 0:d:e:a:d:0 dst ff:ff:ff:ff:ff:ff /dev/null | hexof)" # The filter model itself: arp's output becomes eth's payload. check_eq libdnet/arp-over-eth \ "ffffffffffff000d0e0a0d0008060001080006040001000d0e0a0d000a0000030000000000000a000004" \ "$(dnet arp op req sha 0:d:e:a:d:0 spa 10.0.0.3 tpa 10.0.0.4 /dev/null | dnet eth type arp src 0:d:e:a:d:0 dst ff:ff:ff:ff:ff:ff 2>/dev/null | hexof)" # UDP over IP. Pins BOTH checksums: IP header 6ab9, UDP 6677 (the UDP checksum # covers the pseudo-header, so it depends on the addresses the IP layer wrote). check_eq libdnet/udp-ip \ "450000200001000040116ab90102030405060708022b029a000c667741424344" \ "$(dnet hex '\x41\x42\x43\x44' /dev/null | dnet udp sport 555 dport 666 2>/dev/null | dnet ip proto udp id 1 ttl 64 src 1.2.3.4 dst 5.6.7.8 2>/dev/null | hexof)" # TCP over IP: flags S, seq 1, win 512; IP checksum 66cc, TCP checksum 94bd. check_eq libdnet/tcp-ip \ "4500002800020000400666cc0a0000010a00000204d2005000000001000000005002020094bd0000" \ "$(printf '' | dnet tcp sport 1234 dport 80 flags S seq 1 ack 0 win 512 2>/dev/null | dnet ip proto tcp id 2 ttl 64 src 10.0.0.1 dst 10.0.0.2 2>/dev/null | hexof)" # ICMP echo over IP, carrying a payload: ICMP checksum b6ab over "monkey". check_eq libdnet/icmp-ip \ "4500001e0003000040016ac901020304050607080800b6ab6d6f6e6b6579" \ "$(printf 'monkey' | dnet icmp type 8 code 0 2>/dev/null | dnet ip proto icmp id 3 ttl 64 src 1.2.3.4 dst 5.6.7.8 2>/dev/null | hexof)" # --------------------------------------------------------------------------- # 4. Upstream's check(1) programs. # # ck : ok iff the program exits 0, reports zero # failures AND zero errors, and declares exactly the number of checks this suite # was written against. The count is what makes a filled-in stub visible. ck() { _n=$1; _want=$2 if [ -z "$TDIR" ]; then bst_notok "libdnet/upstream-$_n" "libdnet-tests dir not installed (looked in /usr/libexec, /usr/lib)" return fi if [ ! -x "$TDIR/$_n" ]; then bst_notok "libdnet/upstream-$_n" "$TDIR/$_n missing or not executable" return fi _o=$(CK_FORK=no "$TDIR/$_n" 2>&1); _rc=$? printf '#diag: %s rc=%s :: %s\n' "$_n" "$_rc" "$(printf '%s' "$_o" | tr '\n' '|')" if bst_execfail "$_o"; then bst_unrun "libdnet/upstream-$_n" "$_o"; return; fi case "$_o" in *"Checks: $_want, Failures: 0, Errors: 0"*) if [ "$_rc" -eq 0 ]; then bst_ok "libdnet/upstream-$_n" else bst_notok "libdnet/upstream-$_n" "clean summary but rc=$_rc"; fi ;; *"Checks: $_want,"*) bst_notok "libdnet/upstream-$_n" "rc=$_rc $(_bst_1line "$_o")" ;; *) bst_notok "libdnet/upstream-$_n" "check count changed or no summary; rc=$_rc $(_bst_1line "$_o")" ;; esac } # The three that are pure computation and must pass anywhere. ck check_addr 13 ck check_blob 6 ck check_rand 4 # The six that reach for a kernel facility. Their verdicts are measured on this # image, not assumed from it -- see the test-plan entry for what each is worth. ck check_ip 7 ck check_intf 6 ck check_arp 6 ck check_route 5 ck check_fw 6 ck check_eth 5 # --------------------------------------------------------------------------- # 5. Kernel-interface probes -- what the platform actually answers. # # These are the cause behind whatever the six above did. Each prints its raw # answer as a #diag line (outside the BST protocol, so parsers ignore it) and # asserts only that the tool reached a definite answer rather than hanging or # dying: a listing, or a named errno from the open call. A case that cannot # tell those two apart would be worthless, so the assertion is deliberately # narrow and the evidence is in the diag. probe() { _id=$1; shift _o=$("$@" &1); _rc=$? printf '#diag: %s rc=%s :: %s\n' "$_id" "$_rc" "$(printf '%s' "$_o" | tr '\n' '|' | cut -c1-300)" if bst_execfail "$_o"; then bst_unrun "libdnet/$_id" "$_o" elif [ -n "$_o" ]; then bst_ok "libdnet/$_id" else bst_notok "libdnet/$_id" "rc=$_rc, no output at all -- neither a listing nor a diagnostic"; fi } probe plat-intf-show dnet intf show probe plat-route-show dnet route show probe plat-arp-show dnet arp show probe plat-fw-show dnet fw show # `dnet send` with no device routes an IP datagram, so it opens a raw socket. # This is the single probe that says whether SOCK_RAW works on this image, and # it is the cause behind check_ip's verdict. It is expected to report an errno # rather than transmit -- the gate has no network. probe plat-ip-open dnet send # The cause behind check_intf, check_arp and check_route, pinned as its own case # so the suite explains its own reds instead of leaving them to the test plan. # # This kernel is built `# CONFIG_IPV6 is not set`, so socket(AF_INET6, ...) # returns EAFNOSUPPORT. libdnet's Linux backends do not survive that: # route_open() treats ANY AF_INET6 failure as fatal, and intf_open() tolerates # only EPROTONOSUPPORT -- which is not what Linux returns for an absent family. # arp_open() reaches the same code through intf_open() under # HAVE_ARPREQ_ARP_DEV. That is an upstream portability defect against an # IPv6-less Linux, not a brintOS gap, and it is why exactly those three are red # while check_ip -- which opens AF_INET/SOCK_RAW and nothing else -- is green. # # The assertion is deliberately the RIGHT way round: ok while the platform still # matches what the test-plan entry says. The day IPv6 arrives this goes red, and # whoever sees it re-reads three reds that should by then have turned green. if [ -e /proc/net/if_inet6 ]; then bst_notok libdnet/plat-ipv6-absent "/proc/net/if_inet6 exists -- this kernel now has IPv6, so re-read the check_intf/check_arp/check_route verdicts and this suite's test-plan entry" else bst_ok libdnet/plat-ipv6-absent fi