. "$BST_LIB" # BST-TIMEOUT: 300 # babeld 1.13.1 -- Juliusz Chroboczek's implementation of the Babel routing # protocol (RFC 8966), from meta-networking recipes-protocols/babeld. It is the # FIRST ROUTING DAEMON on this image: everything networking that came before it # is a client, a server or a diagnostic, and nothing until now has computed a # route or asked the kernel to install one. Test-plan entry 529. # # --------------------------------------------------------------------------- # THE PROTOCOL'S TRANSPORT IS IPv6, AND THIS KERNEL HAS NONE. That single fact # is the shape of the whole suite. # # Babel runs over UDP to the link-local multicast group ff02::1:6, port 6696 -- # RFC 8966 section 3.1, and babeld hardcodes exactly that in main() before it # reads argv. It carries IPv4 routes happily, but the packets that carry them are # IPv6 packets; there is no IPv4 transport in the protocol and none in this # implementation. arch/wasm32/configs/wasm32_defconfig line 265 reads # `# CONFIG_IPV6 is not set`, annotated "no carrier yet; halves the coroutinized # net surface" -- so babeld cannot open the one socket it exists to use. # # IT NEVER GETS AS FAR AS THAT SOCKET, AND THE SUITE SAYS SO RATHER THAN # ASSUMING THE OBVIOUS STOP. kernel_setup() reads four sysctls before # kernel_setup_socket() is ever called, and the first of them is # /proc/sys/net/ipv6/conf/all/forwarding (kernel_netlink.c:91) -- a file the # kernel does not create when CONFIG_IPV6 is off. So the run ends at # "Couldn't read sysctl: No such file or directory" / "kernel_setup failed.", # one step earlier than the socket, and a case that had asserted # "kernel_setup_socket failed." would have reported a working port as broken. # babeld/stops-at-the-ipv6-gap accepts either wall by name, because both are the # same fact and a future kernel may move the stop between them. # # Everything the ticket asks to be ASSERTED FOR REAL happens BEFORE that socket, # and that is not luck -- it is main()'s order. getopt runs first, then -C # statements, then the configuration files, then reopen_logfile(), and only then # kernel_setup() and kernel_setup_socket(). So configuration-file parsing, the # usage and version text, argument validation and the documented exit code for an # unreadable configuration file or an unknown interface name are all reachable on # this image, exactly, offline, with no interface and no peer. They are asserted # below as ordinary greens, not as approximations of a daemon that could not run. # # Everything the ticket asks to be PROBED AT RUNTIME -- route computation and the # kernel routing-table writes -- is behind that socket, and is skipped only where # the probe below confirms the absence rather than assumes it. The probe is # /proc/net/if_inet6, which the kernel creates if and only if CONFIG_IPV6=y, and # it is corroborated by babeld's own diagnostic further down: two independent # readings of one fact, so a skip here can never be a suite that gave up. # # THE GAP ITSELF IS SCORED AS AN ATTRIBUTED RED, not hidden behind the skips it # causes. babeld is present and working; what stops it is the platform, and that # is the finding this port exists to surface. # # --------------------------------------------------------------------------- # WHAT THE BINARY PRINTS, AND WHERE. babeld writes EVERYTHING to stderr -- the # version, the usage, every diagnostic -- and never to stdout unless a debug # level asks it to. Every capture below therefore folds 2>&1, and one case # asserts that split directly so a future release that moved -V to stdout would # be reported rather than silently absorbed. BD=$(command -v babeld 2>/dev/null || true) if [ -z "$BD" ]; then for p in /bin/babeld /usr/bin/babeld /sbin/babeld /usr/sbin/babeld; do [ -x "$p" ] && { BD=$p; break; } done fi require "${BD:-babeld}" babeld/present D=${TMPDIR:-/tmp}/bst-babeld.$$ rm -rf "$D"; mkdir -p "$D" || { bst_notok babeld/workdir "cannot create $D"; exit 0; } WANT_VERSION="babeld-1.13.1" # db : run bounded, fold stderr into stdout, keep both the # text and the status. Nothing on this image can be killed once it blocks # (brintos/linux#21), so every invocation of the daemon is bounded even where it # is expected to exit at once. db() { bst_run "$@"; } # lib/bst.sh: a bound that FIRES # (timeout(1) did not, before the # brintos/linux#77 fix) and a file capture # no wedged child can hold open. # has ... : true iff EVERY needle is a substring. has() { _h=$1; shift for _n in "$@"; do case "$_h" in *"$_n"*) ;; *) return 1 ;; esac; done return 0 } # firstline firstline() { printf '%s\n' "$1" | head -n 1; } # ---------------------------------------------------------------- inventory -- check_eq babeld/inventory "yes" "$([ -x "$BD" ] && echo yes)" # /bin, not /usr/bin, and that is the recipe's arithmetic rather than a choice: # meta-networking installs with PREFIX=${root_prefix}, root_prefix is # ${base_prefix} whenever DISTRO_FEATURES lacks usrmerge, and this distro sets # DISTRO_FEATURES without it. A red here means the distro grew usrmerge, which is # worth knowing for every other package that splits the same way. check_eq babeld/install-path "/bin/babeld" "$BD" # The bare name a user types must reach this binary; `command -v` reads root's # actual PATH, so this also catches an image on which /bin lost its place. check_eq babeld/on-path "$BD" "$(command -v babeld 2>/dev/null)" # The man page is this port's own addition: upstream's `install` target ships # babeld.8 and `install.minimal` -- the one meta-networking calls -- does not, so # the stock package documents nothing. Every "documented" claim this suite makes # is read from that page, so its absence would leave the assertions below # citing a file the image does not have. MAN8=/usr/share/man/man8/babeld.8 check babeld/manpage-shipped test -f "$MAN8" if [ -f "$MAN8" ]; then case "$(head -n 5 "$MAN8" 2>/dev/null)" in *".TH BABELD"*) bst_ok babeld/manpage-is-roff ;; *) bst_notok babeld/manpage-is-roff "want a roff .TH BABELD header, got '$(_bst_1line "$(head -n 1 "$MAN8" 2>/dev/null)")'" ;; esac else bst_notok babeld/manpage-is-roff "no man page to read (see babeld/manpage-shipped)" fi # ------------------------------------------------------------------ version -- # -V is answered inside the getopt loop, ahead of every config file and every # socket, so a red in this block separates "the binary is broken" from every # case below it. db 30 "$BD" -V check_eq babeld/version-rc "0" "$rc" # Exact, not a prefix. The string is this port's doing and the reason is worth # stating where it is asserted: upstream's generate-version.sh derives # BABELD_VERSION from `git describe --dirty` of the build directory, so the stock # recipe's answer depends on whether bitbake's fetcher carried tags and on # whether any patch left the tree dirty -- three legitimate builds of one SRCREV # print three different strings. The bbappend writes version.h from PV instead, # so this equality is a statement about the source and not about the fetcher. check_eq babeld/version-string "$WANT_VERSION" "$out" # stdout is empty and stderr carries it. Asserted rather than assumed, because # every other capture in this file folds 2>&1 and would keep passing if the two # streams swapped. # BST-TIMEOUT-OK: babeld -V and `ip -6 route show', one-shot; the daemon converted. check_eq babeld/version-on-stderr "" "$(timeout 30 "$BD" -V 2>/dev/null)" # -------------------------------------------------------- usage and argv ---- # An unknown option must be REFUSED, not ignored, and getopt runs before any # config file or socket so this stays offline. db 30 "$BD" -Z check_eq babeld/usage-rc "1" "$rc" if has "$out" "Syntax: babeld"; then bst_ok babeld/usage-syntax-line else bst_notok babeld/usage-syntax-line "want the documented 'Syntax: babeld' usage, rc=$rc -- $(_bst_1line "$out")" fi # The usage text's first line is BABELD_VERSION -- the same string -V printed -- # so a binary that identified itself two different ways would be caught here. # # READ FROM A VALUE ERROR, NOT FROM THE UNKNOWN OPTION ABOVE. getopt prints its # own `invalid option` diagnostic to stderr before returning '?', so the capture # from -Z begins with a line the C library wrote and not with babeld's. `-p 0` is # a well-formed option with an out-of-range value: it reaches the same usage label # with nothing printed in front of it. db 20 "$BD" -p 0 usage_out=$out check_eq babeld/usage-leads-with-version "$WANT_VERSION" "$(firstline "$usage_out")" # The two options this suite's configuration cases drive must be the ones the # usage advertises; a release that renamed either would otherwise leave the # cases below testing options the binary no longer documents. if has "$usage_out" "[-c file]" "[-C statement]"; then bst_ok babeld/usage-lists-config-options else bst_notok babeld/usage-lists-config-options "the usage does not advertise both -c and -C -- $(_bst_1line "$usage_out")" fi # Argument validation, one bound per case. Each of these is rejected inside the # getopt loop by an explicit range test in babeld.c, so each is a separate # statement about a separate check rather than four spellings of one. badopt() { _id=$1; shift db 20 "$BD" "$@" if [ "$rc" -eq 124 ]; then bst_notok "$_id" "hung on an argument that must be refused -- $(_bst_1line "$out")" elif [ "$rc" -ne 1 ]; then bst_notok "$_id" "want the documented exit 1, got rc=$rc -- $(_bst_1line "$out")" elif has "$out" "Syntax: babeld"; then bst_ok "$_id" else bst_notok "$_id" "rejected (rc=1) but printed no usage -- $(_bst_1line "$out")" fi } badopt babeld/rejects-port-zero -p 0 badopt babeld/rejects-port-overflow -p 65536 badopt babeld/rejects-port-nonnumeric -p six-six-nine-six badopt babeld/rejects-hello-zero -h 0 badopt babeld/rejects-table-overflow -t 70000 badopt babeld/rejects-debug-nonnumeric -d loud badopt babeld/rejects-malformed-group -m not:an:address # The multicast checks are two distinct rules and babeld says so in two distinct # ways -- a non-multicast group is refused with its own message, while a # multicast group that is merely not link-local is a WARNING the run survives. # Asserting them together would let either one cover for the other's absence. db 20 "$BD" -m 2001:db8::1 if [ "$rc" -eq 1 ] && has "$out" "is not a multicast address" "Syntax: babeld"; then bst_ok babeld/rejects-non-multicast-group else bst_notok babeld/rejects-non-multicast-group "want the documented refusal and exit 1, rc=$rc -- $(_bst_1line "$out")" fi # ff05::1:6 is site-local: multicast, so accepted, but not the link-local scope # the protocol is defined on. babeld warns and carries on -- and then dies at the # IPv6 socket like every other run here, so only the warning is scored. db 20 "$BD" -I '' -m ff05::1:6 lo if has "$out" "is not a link-local multicast address"; then bst_ok babeld/warns-non-link-local-group else bst_notok babeld/warns-non-link-local-group "want the documented warning for a non-link-local group, rc=$rc -- $(_bst_1line "$out")" fi # THE DEFAULT PIDFILE SITS IN FRONT OF THE KERNEL STAGE, so every run below that # is about kernel setup passes -I '' to switch it off. babeld's pidfile defaults # to /var/run/babeld.pid (babeld.c:74) and is taken with O_CREAT|O_EXCL BEFORE # kernel_setup(), so on an image whose /var/run is read-only at runtime -- or one # carrying a stale pidfile -- every one of those cases would stop at # `creat(/var/run/babeld.pid)` and red as though the kernel had refused # something. That is our own wall and not the subject; 88-dhcpcd.test records the # same shape from the other side, at _PATH_VARRUN. main() skips the pidfile # entirely when its path is empty (`pidfile[0] != '\0'`), so -I '' is babeld's own # switch rather than a workaround, and the default path gets one case of its own # below instead of contaminating twenty. # ------------------------------------------- the configuration file, parsed -- # THE TICKET'S DOCUMENTED EXIT CODE, half one: an unreadable configuration file. # parse_config_from_file() returns -1 with *line_return = 0 when fopen fails, and # main() prints the message below and exit(1)s. The line number is part of the # assertion because 0 is what distinguishes "could not be opened" from "opened # and did not parse", which is the other half and is asserted next. MISSING="$D/absent.conf" rm -f "$MISSING" db 20 "$BD" -c "$MISSING" check_eq babeld/conf-missing-rc "1" "$rc" if has "$out" "Couldn't parse configuration from file $MISSING (error at line 0)."; then bst_ok babeld/conf-missing-diagnostic else bst_notok babeld/conf-missing-diagnostic "want the documented 'error at line 0' refusal for an unopenable file, rc=$rc -- $(_bst_1line "$out")" fi # Half two: a file that opens and does not parse, reported with the line the bad # statement is on. That number is asserted as a DIFFERENCE between two fixtures # carrying the SAME bad statement three lines apart, not as a constant: the # difference is what says the parser counts, and it stays exact without this # suite having to encode where babeld's counter starts. A parser that always said # 1, or always said 0, fails it; so does one that counts blank lines twice. printf 'no-such-directive 1\n' > "$D/syntax1.conf" cat > "$D/syntax.conf" < "$D/valid.conf" < "$D/comments.conf" db 20 "$BD" -c "$D/comments.conf" if has "$out" "Couldn't parse configuration from file"; then bst_notok babeld/conf-comments-only "a comments-and-blanks file was refused -- $(_bst_1line "$out")" else bst_ok babeld/conf-comments-only fi # Two config files on one command line are read in order; both must be parsed. # The second is the bad one, so its line number -- not the first file's length -- # must be what comes back. db 20 "$BD" -c "$D/valid.conf" -c "$D/syntax.conf" if has "$out" "$D/syntax.conf (error at line "; then bst_ok babeld/conf-second-file-read else bst_notok babeld/conf-second-file-read "the second -c file was not the one reported, rc=$rc -- $(_bst_1line "$out")" fi # The port ships NO /etc/babeld.conf, deliberately: main() reads that path # automatically when no -c is given, so a file placed there would silently # reconfigure every run on the image, including the cases above. Its absence is # what makes "no -c" mean "no configuration". check babeld/ships-no-default-conf test ! -e /etc/babeld.conf # ------------------------------------- the same parser, from the command line -- # -C takes one statement, and parse_config_from_string() must return # CONFIG_ACTION_DONE or main() refuses with its own message and exit(1). Each of # these is a documented statement of a different kind. cstmt_ok() { _id=$1; _stmt=$2 db 20 "$BD" -I '' -C "$_stmt" lo if has "$out" "Couldn't parse configuration from command line."; then bst_notok "$_id" "the documented statement '$_stmt' was refused -- $(_bst_1line "$out")" else bst_ok "$_id" fi } cstmt_ok babeld/cstmt-global "protocol-port 6696" cstmt_ok babeld/cstmt-default "default rxcost 96" cstmt_ok babeld/cstmt-interface "interface lo type wired" cstmt_ok babeld/cstmt-filter "redistribute local deny" # The complement, and it is scored on both the status and the message: an # unparseable statement returns -1 from parse_config_from_string(), which is not # CONFIG_ACTION_DONE, so main() refuses. db 20 "$BD" -C "no-such-directive 1" if [ "$rc" -eq 1 ] && has "$out" "Couldn't parse configuration from command line."; then bst_ok babeld/cstmt-refuses-unknown else bst_notok babeld/cstmt-refuses-unknown "want the documented refusal and exit 1, rc=$rc -- $(_bst_1line "$out")" fi # THE TICKET'S DOCUMENTED EXIT CODE, and the one case here that is about an # INTERFACE NAME rather than about syntax. `flush interface ` is # well-formed whatever the name is, so it parses; flush_interface() then returns # 0 for a name that is not among the configured interfaces and the statement # comes back CONFIG_ACTION_NO. main() compares against CONFIG_ACTION_DONE, so an # unknown interface name is refused with the same message and the same exit 1 as # a syntax error -- which is the documented behaviour and the reason the pair # above matters: without cstmt-* passing, this red would read as "-C always # fails" instead of "this name is not known". db 20 "$BD" -C "flush interface bstnosuch0" if [ "$rc" -eq 1 ] && has "$out" "Couldn't parse configuration from command line."; then bst_ok babeld/unknown-interface-refused else bst_notok babeld/unknown-interface-refused "want the documented exit 1 for an unknown interface name, rc=$rc -- $(_bst_1line "$out")" fi # -L redirects stderr to a file before kernel setup, so the diagnostic that ends # every run on this image lands there instead of on the terminal. This is the # only case that observes babeld writing to the filesystem at all. rm -f "$D/babeld.log" db 30 "$BD" -I '' -L "$D/babeld.log" lo check babeld/logfile-created test -s "$D/babeld.log" if [ -s "$D/babeld.log" ] && has "$(cat "$D/babeld.log" 2>/dev/null)" "kernel_setup"; then bst_ok babeld/logfile-carries-diagnostic elif [ -s "$D/babeld.log" ] && has "$(cat "$D/babeld.log" 2>/dev/null)" "my-id"; then # On a kernel that HAS IPv6 there is no kernel_setup failure to log; what lands # there instead is the router-id babeld computed. Either one says the redirect # took, which is what this case is about. bst_ok babeld/logfile-carries-diagnostic else bst_notok babeld/logfile-carries-diagnostic "the -L logfile did not receive the run's own output -- $(_bst_1line "$(cat "$D/babeld.log" 2>/dev/null)")" fi # ------------------------------------------- what the platform withholds ---- # The probe, read from the kernel rather than inferred from a failed command. # /proc/net/if_inet6 is created by the IPv6 module's proc registration and exists # if and only if CONFIG_IPV6=y -- on a kernel that has IPv6 it is present even # with no address configured, since loopback's ::1 is always listed. IPV6=0 [ -e /proc/net/if_inet6 ] && IPV6=1 if [ "$IPV6" -eq 1 ]; then bst_ok babeld/ipv6-transport else bst_notok babeld/ipv6-transport "brintos/linux: the kernel is built '# CONFIG_IPV6 is not set' (arch/wasm32/configs/wasm32_defconfig, annotated 'no carrier yet; halves the coroutinized net surface'), so /proc/net/if_inet6 does not exist and socket(PF_INET6) cannot succeed -- Babel's only transport is UDP to ff02::1:6, so no Babel packet can be sent or received on this image" fi # The same fact from a third angle, and the one that actually stops babeld first. # kernel_setup() reads four sysctls before it opens anything, and the FIRST is # /proc/sys/net/ipv6/conf/all/forwarding (kernel_netlink.c:91) -- a path the # kernel does not create when CONFIG_IPV6 is off. So the two probes must agree: # either both the interface table and the forwarding knob are there, or neither # is. A disagreement would mean IPv6 is half-configured, which is a stranger # state than either and worth a red of its own. FWD=/proc/sys/net/ipv6/conf/all/forwarding if [ "$IPV6" -eq 1 ] && [ -e "$FWD" ]; then bst_ok babeld/ipv6-probes-agree elif [ "$IPV6" -eq 0 ] && [ ! -e "$FWD" ]; then bst_ok babeld/ipv6-probes-agree else bst_notok babeld/ipv6-probes-agree "the two IPv6 probes disagree: /proc/net/if_inet6 $([ "$IPV6" -eq 1 ] && echo exists || echo 'does not exist') while $FWD $([ -e "$FWD" ] && echo does || echo 'does not')" fi # babeld's own reading, and the case that tells the two halves of kernel setup # apart. kernel_setup() opens the rtnetlink socket FIRST -- AF_NETLINK, which # this kernel has, since CONFIG_NET brings the rtnetlink core in unconditionally # -- and only then reads the sysctls; kernel_setup_socket() comes after both and # is where the IPv6 protocol socket would be opened. Scoring them together would # read a missing netlink as missing IPv6, and the two have entirely different # consequences for every port that comes after this one. netlink_socket() is the # only one of the three that perror()s its own name, which is what makes them # separable from the outside. db 30 "$BD" -I '' lo if [ "$rc" -eq 124 ]; then bst_notok babeld/netlink-usable "hung before reporting any kernel step -- $(_bst_1line "$out")" bst_notok babeld/stops-at-the-ipv6-gap "not reached: the run hung (see babeld/netlink-usable)" elif has "$out" "netlink_socket("; then bst_notok babeld/netlink-usable "the rtnetlink socket could not be opened, which is AF_NETLINK rather than IPv6 -- $(_bst_1line "$out")" bst_notok babeld/stops-at-the-ipv6-gap "not reached: babeld stopped at rtnetlink, before anything IPv6 (see babeld/netlink-usable)" else bst_ok babeld/netlink-usable # Where the run ends, and whether that place is the one IPv6's absence # predicts. It is scored GREEN when it is: babeld behaving exactly as its # source says it must, on the kernel it was given, is the correct outcome # here, and the gap itself is already carried -- once -- by # babeld/ipv6-transport above. if [ "$IPV6" -eq 0 ]; then if [ "$rc" -ne 1 ]; then bst_notok babeld/stops-at-the-ipv6-gap "want the documented exit 1 on a kernel with no IPv6, got rc=$rc -- $(_bst_1line "$out")" elif has "$out" "Couldn't read sysctl" && has "$out" "kernel_setup failed."; then # Today's shape: kernel_setup() never reaches the socket, because the # first of its four sysctls is an IPv6 one that does not exist here. bst_ok babeld/stops-at-the-ipv6-gap elif has "$out" "kernel_setup_socket failed."; then # The other IPv6 wall, for a kernel that kept the sysctls and dropped the # address family. Equally correct, and named so a future kernel does not # red this case for moving the stop by one step. bst_ok babeld/stops-at-the-ipv6-gap elif has "$out" "Couldn't write sysctl"; then bst_notok babeld/stops-at-the-ipv6-gap "stopped writing a sysctl rather than reading one, which is a privilege fact about the caller and not about IPv6 -- $(_bst_1line "$out")" else bst_notok babeld/stops-at-the-ipv6-gap "exited 1 somewhere this case cannot name -- $(_bst_1line "$out")" fi else # A kernel that HAS IPv6: babeld must get past both halves of kernel setup # and stay up until the bound, rather than stop anywhere. if [ "$rc" -eq 124 ] || has "$out" "my-id"; then bst_ok babeld/stops-at-the-ipv6-gap else bst_notok babeld/stops-at-the-ipv6-gap "IPv6 is present, so babeld should have reached its main loop; it exited rc=$rc instead -- $(_bst_1line "$out")" fi fi fi # The default pidfile path, asserted once and kept away from every other case. # It is /var/run/babeld.pid (babeld.c:74) and it is taken with O_CREAT|O_EXCL # before kernel setup, so on this image a run without -I '' either names that # path in a creat() failure or passes through it silently -- both of which say # the documented default is in force, and neither of which says anything about # babeld. What would NOT say it is a run that named some other path. db 30 "$BD" lo if has "$out" "/var/run/babeld.pid"; then bst_ok babeld/default-pidfile-path elif has "$out" "kernel_setup" || has "$out" "my-id"; then bst_ok babeld/default-pidfile-path else bst_notok babeld/default-pidfile-path "a run with no -I neither used nor named the documented default pidfile, rc=$rc -- $(_bst_1line "$out")" fi # ------------------------------------------------ route computation and FIB -- # The ticket's runtime half. It is SKIPPED only where the probe above confirmed # the absence of the transport -- not because the daemon is awkward to drive, and # not on a guess. Written out in full so that the day CONFIG_IPV6 lands it runs # with no edit here. # # What it does when it runs: babeld is started on every non-loopback interface # with a bounded lifetime, its own state file and its own read-only local socket. # `dump` over that socket is babeld's own view of what it has computed -- the # interfaces it brought up, its router-id, its self xroutes and any route it # learned -- and `ip -6 route show proto babel` is the kernel's view of what # babeld asked it to install. The two are scored separately because they fail # for different reasons: the first is route computation, the second is the FIB # write, and a daemon that computes correctly and cannot install is a different # defect from one that never computes. # # THIS IMAGE HAS ONE VIRTIO INTERFACE AND NO SECOND BABEL SPEAKER BEHIND IT, so # even with IPv6 restored the learned-route cases would have nothing to learn. # That is stated where it will be read rather than assumed: the run below still # asserts what one speaker alone can produce -- the interface coming up, a # router-id, and its own redistributed local routes -- and says so in the red if # the neighbour half is what is missing. if [ "$IPV6" -ne 1 ]; then bst_skip babeld/route-computation "not attempted: the probe confirmed IPv6 is absent (/proc/net/if_inet6 does not exist -- see babeld/ipv6-transport), so babeld cannot open the socket the protocol runs on and no route can be computed" bst_skip babeld/fib-write "not attempted: the probe confirmed IPv6 is absent (see babeld/ipv6-transport), so babeld never reaches the point of installing a route" else IFS_LIST=$(ls /sys/class/net 2>/dev/null | grep -v '^lo$' | tr '\n' ' ') if [ -z "$IFS_LIST" ]; then bst_notok babeld/route-computation "no non-loopback interface exists on this image, so babeld has nothing to run on" bst_notok babeld/fib-write "not attempted: no non-loopback interface (see babeld/route-computation)" else rm -f "$D/babeld.state" "$D/run.log" SOCK="$D/babeld.sock" bst_bound 60 "$BD" -I '' -S "$D/babeld.state" -L "$D/run.log" -g "$SOCK" $IFS_LIST & bpid=$! i=0 while [ $i -lt 30 ]; do [ -S "$SOCK" ] && break kill -0 "$bpid" 2>/dev/null || break i=$((i+1)); sleep 1 done dump="" if [ -S "$SOCK" ] && command -v socat >/dev/null 2>&1; then dump=$(printf 'dump\nquit\n' | bst_bound 20 socat - "UNIX-CONNECT:$SOCK" 2>&1) fi log=$(cat "$D/run.log" 2>/dev/null) if [ -n "$dump" ] && has "$dump" "add interface"; then bst_ok babeld/route-computation elif [ -z "$dump" ] && has "$log" "my-id"; then # No socat, or the socket refused: fall back to babeld's own log, which # reports the router-id it computed before anything else. bst_ok babeld/route-computation elif has "$log" "kernel_setup"; then bst_notok babeld/route-computation "babeld could not set the kernel up even with IPv6 present -- $(_bst_1line "$log")" else bst_notok babeld/route-computation "babeld started but reported no interface and no router-id; with one interface and no second Babel speaker this is the expected shape of a run that has nothing to converge with -- dump='$(_bst_1line "$dump")' log='$(_bst_1line "$log")'" fi if ! command -v ip >/dev/null 2>&1; then bst_notok babeld/fib-write "iproute2 ip is not on this image, so the kernel's own view of the routing table cannot be read" else routes=$(timeout 20 ip -6 route show proto babel 2>&1) if [ -n "$routes" ]; then bst_ok babeld/fib-write else bst_notok babeld/fib-write "no route carries proto babel: with one interface and no peer babeld has no route to install, so this is the absent neighbour rather than a failed write -- $(_bst_1line "$routes")" fi fi kill -TERM "$bpid" 2>/dev/null wait "$bpid" 2>/dev/null fi fi # The marker every case above is counted against. Its absence is the only signal # that the suite died mid-file and the remaining cases were lost silently -- the # runner counts what it saw and would otherwise report a complete green. bst_ok babeld/suite-complete rm -rf "$D"