. "$BST_LIB" # BST-TIMEOUT: 600 # mstpd 0.1.0+git (meta-oe recipes-extended/networking/mstpd, SRCREV 181c453fc1) # -- the userspace IEEE 802.1Q MSTP / RSTP daemon and its mstpctl client. What # ships is /usr/sbin/mstpd, /usr/sbin/mstpctl, the /usr/sbin/bridge-stp kernel # helper meta-oe substitutes for upstream's, the /usr/sbin/mstp_restart symlink # onto it, /etc/bridge-stp.conf, and two man pages. # # THE SUBJECT IS EVERYTHING THAT RUNS BEFORE A BRIDGE IS NEEDED, because that is # the half this kernel can reach. mstpd is a daemon for a bridge device and this # kernel has none, so the split below is not a convenience -- it is where the # program itself divides: # # * mstpctl's OPTION LAYER runs to completion. main() parses argv with # getopt_long and handles -h/--help, -V/--version, -f/--format, -b/--batch, # -s/--stdin and every malformed spelling of them BEFORE it calls # ctl_client_init() (ctl_main.c, the `if(ctl_client_init())' four statements # after the getopt loop). Everything in that layer is asserted here by value. # * mstpd's STARTUP runs as far as its EPOLL instance. main() does # sanity_check(), signal_init(), driver_mstp_init(), init_epoll(), # ctl_socket_init() and only THEN packet_sock_init() (main.c:178-184). # The first three succeed on this image and are asserted; init_epoll() is # the wall, and the two behind it are asserted as UNREACHED rather than as # passed. This is one step earlier than the port's own gap would put it -- # the BPDU socket never gets to be asked for. # * mstpctl's COMMAND LAYER is unreachable, and not for the reason one would # guess. It is not that the commands fail against an absent bridge: it is # that ctl_client_init() runs FIRST, so `mstpctl bogus-command' and # `mstpctl showbridge br0' produce the same "can't setup control connection" # and the same exit 1. Per-command argument validation # (command_lookup_and_validate) sits behind that connect and cannot be # reached at all without a live daemon. That is asserted as the positive # fact it is, and the per-bridge queries are SKIPPED against a probe rather # than guessed at. # # TWO CONFIG GAPS STAND BETWEEN THIS PORT AND A RUNNING STATE MACHINE, and both # are the platform's rather than this port's. Each is scored as ONE red, so that # the gap is carried rather than silently skipped, and each is written so that OK # is GREEN -- it flips by itself the day the option lands, with no edit here: # # * mstpd/bridge-device -> brintos/linux#109. wasm32_defconfig sets no # CONFIG_BRIDGE (it is not even in that file's own # "DELIBERATELY still OFF" list), so # rtnl_link_ops_get("bridge") finds nothing and # __rtnl_newlink() answers EOPNOTSUPP with the extack # "Unknown device type"; `# CONFIG_MODULES is not # set' compiles out the request_module() fallback. # * mstpd/start-stops-at-epoll -> brintos/linux#53. epoll_create(2) returns # ENOSYS: no epoll entry is wired in the wasm32 # syscall table, although CONFIG_EPOLL=y and # fs/eventpoll.o is in vmlinux. This is scored as a # PASS, not a red -- the red it would duplicate is # mstpd/packet-socket below, and what this pins is # WHERE the daemon stops, by TST() expression and # source line. # * mstpd/packet-socket -> brintos/linux#81. BPDUs are 802.2/LLC frames, so # packet_sock_init() asks for socket(PF_PACKET, # SOCK_RAW, htons(ETH_P_802_2)) (packet.c:166) and # this kernel is built `# CONFIG_PACKET is not set'. # The same wall 88-lldpd.test carries at # lldpd/ports-initialised. # # NOTHING IS BACKGROUNDED, NOTHING IS KILLED, AND NOTHING IS LEFT RUNNING. Every # invocation below returns on its own. mstpd -d IS THE ONE COMMAND THAT COULD # BLOCK -- on a kernel that HAS AF_PACKET it would reach epoll_main_loop() and # never return -- and timeout(1) did not fire on this target before the # brintos/linux#77 fix (76-daemonize.test, measured), so it is GUARDED BY THE # PROBE rather than by the bound: it is only run where /proc/net/packet has # already established that the socket cannot open, and it is skipped, loudly, # where that is no longer true. The rb() bound (bst_run) around every # invocation is kept as a second line. # # THE DAEMONIZING FORM (`mstpd' with no -d) IS DELIBERATELY NOT RUN. It fopen()s # /var/run/mstpd.pid and calls daemon(3) before it reaches the packet socket, so # on this image it would leave a pidfile naming a child that then dies invisibly # -- and an exiting task does not reparent its children here (brintos/linux#35). # The foreground form exercises the identical startup sequence and returns. # # CALIBRATED AGAINST A NATIVE x86_64 BUILD of the same upstream commit with the # same shipped configure and the same out-of-tree build directory, so every # string and exit status below was read off a real run before it ever ran here. # That build directory matters: it is why PACKAGE_VERSION is "0.1.0-" with an # EMPTY build id. configure.ac computes PACKAGE_BUILD as `git log --pretty=%h -n # 1' and autotools runs configure in ${B}, which under bitbake is NOT inside ${S} # -- the do_configure log records `fatal: not a git repository'. The trailing # hyphen is therefore upstream's format with an empty field, not a truncation, # and it is asserted as such below so that a future recipe that DOES thread the # revision through shows up here rather than passing unnoticed. require /usr/sbin/mstpd mstpd/present MSTPD=/usr/sbin/mstpd MSTPCTL=/usr/sbin/mstpctl BRIDGESTP=/usr/sbin/bridge-stp VER='0.1.0-' W=${TMPDIR:-/tmp}/bst-mstpd.$$ rm -rf "$W"; mkdir -p "$W" || { bst_notok mstpd/workdir "cannot create $W"; exit 0; } # rb -- run bounded, merging stdout+stderr; sets $out and $rc. # Both streams are read together throughout: mstpctl writes its own diagnostics # to stderr while the ERROR macro underneath them prints to stdout (log.h's # NO_DAEMON branch), so a case that read only one would see half the evidence. rb() { 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() { case "$2" in *"$1"*) return 0 ;; *) return 1 ;; esac; } yn() { if "$@"; then echo yes; else echo no; fi; } # --- inventory: what the package put on the image ----------------------------- check_eq mstpd/inventory-mstpd "yes" "$([ -x "$MSTPD" ] && echo yes)" check_eq mstpd/inventory-mstpctl "yes" "$([ -x "$MSTPCTL" ] && echo yes)" check_eq mstpd/inventory-bridgestp "yes" "$([ -x "$BRIDGESTP" ] && echo yes)" # The bare names a user types must be these binaries; `command -v' reads root's # real PATH, so this also catches an image where /usr/sbin is not on it. check_eq mstpd/on-path-mstpd "$MSTPD" "$(command -v mstpd 2>/dev/null)" check_eq mstpd/on-path-mstpctl "$MSTPCTL" "$(command -v mstpctl 2>/dev/null)" # mstp_restart is a symlink onto bridge-stp, and the two live in DIFFERENT # binary packages -- the link is in `mstpd', its target in `mstpd-mstpd'. That # split is what makes mstpd Depends: mstpd-mstpd, so a dangling link here is the # visible form of an image that took only one of the pair. The link is RELATIVE # (`bridge-stp', a sibling in the same directory), so it is resolved rather than # read: what the packaging guarantees is the target, not the spelling. check_eq mstpd/mstp-restart-link "$BRIDGESTP" \ "$(cd /usr/sbin 2>/dev/null && readlink -f mstp_restart 2>/dev/null)" check_eq mstpd/mstp-restart-resolves "yes" "$([ -x /usr/sbin/mstp_restart ] && echo yes)" check_eq mstpd/bridge-stp-conf "yes" "$([ -f /etc/bridge-stp.conf ] && echo yes)" check_eq mstpd/man-mstpctl "yes" "$([ -f /usr/share/man/man8/mstpctl.8 ] && echo yes)" check_eq mstpd/man-interfaces "yes" "$([ -f /usr/share/man/man5/mstpctl-utils-interfaces.5 ] && echo yes)" # The recipe inherits systemd and installs mstpd.service, and this distro sets no # "systemd" in DISTRO_FEATURES -- so systemd.bbclass strips ${systemd_unitdir} at # the end of do_install and NO unit ships. Asserted as the positive fact it is: # an image that suddenly grew one would mean the distro had gained systemd, which # is a change nobody should learn about from a boot failure. check_eq mstpd/no-systemd-unit "absent" \ "$([ -e /lib/systemd/system/mstpd.service ] && echo present || echo absent)" # The libexecdir half of upstream (mstpctl-utils' ifupdown.sh, mstp_config_bridge # and the python3 ifquery) is removed by the recipe's do_install:append. Pinned # because a recipe change that stopped removing it would put a /usr/bin/python3 # shebang on an image that has no python3. check_eq mstpd/no-mstpctl-utils "absent" \ "$([ -d /usr/lib/mstpctl-utils ] && echo present || echo absent)" # --- version, asserted by value for both binaries ----------------------------- rb 20 "$MSTPCTL" -V; mv_short=$out; mv_short_rc=$rc rb 20 "$MSTPCTL" --version; mv_long=$out rb 20 "$MSTPD" -V; md_ver=$out; md_ver_rc=$rc check_eq mstpd/version-mstpctl "$VER" "$mv_short" check_eq mstpd/version-mstpctl-rc "0" "$mv_short_rc" check_eq mstpd/version-long-form "$VER" "$mv_long" check_eq mstpd/version-mstpd "$VER" "$md_ver" check_eq mstpd/version-mstpd-rc "0" "$md_ver_rc" # The two binaries are compiled from one config.h, so a divergence would mean a # mixed install rather than a version bump. check_eq mstpd/version-agrees "$mv_short" "$md_ver" # The empty build id -- see the header. `0.1.0-' with nothing after the hyphen. check_eq mstpd/version-build-id-empty "" "${md_ver#0.1.0-}" # --- usage text, asserted by value -------------------------------------------- rb 20 "$MSTPCTL" --help; help_out=$out; help_rc=$rc check_eq mstpd/help-rc "0" "$help_rc" check_eq mstpd/help-usage-line "yes" "$(yn has 'Usage: mstpctl [options] [commands]' "$help_out")" check_eq mstpd/help-format-option "yes" \ "$(yn has '-f | --format Select output format (json, plain)' "$help_out")" check_eq mstpd/help-batch-option "yes" \ "$(yn has '-b | --batch Process file with mstpctl commands' "$help_out")" # command_helpall() prints one two-line stanza per command: a `-:' # line at column 0 and the ` ' line under it. The command table # has 39 entries and the printer suppresses exactly one of them, so both counts # are 38. Counting is what makes this stronger than a substring: a help text that # lost half its commands would still contain the usage line. printf '%s\n' "$help_out" > "$W/help.txt" check_eq mstpd/help-command-stanzas "38" "$(grep -c '^-' "$W/help.txt")" check_eq mstpd/help-command-names "38" "$(grep -c '^ [a-z]' "$W/help.txt")" # The one suppressed entry, by name: `if(strcmp("setportdonttxmt", ...))' in # command_helpall(). It is still a valid command -- only undocumented. check_eq mstpd/help-hides-donttxmt "0" "$(grep -c 'setportdonttxmt' "$W/help.txt")" # Four commands sampled by name and by their argument spelling, so that a # reordered or re-worded table is visible rather than merely a shorter one. check_eq mstpd/help-lists-addbridge "yes" "$(yn has ' addbridge [ ...]' "$help_out")" check_eq mstpd/help-lists-showbridge "yes" "$(yn has ' showbridge [ ... [param]]' "$help_out")" check_eq mstpd/help-lists-setmaxage "yes" "$(yn has ' setmaxage ' "$help_out")" check_eq mstpd/help-lists-settreeport "yes" "$(yn has ' settreeportcost ' "$help_out")" # -h and --help are the same case in the getopt table, so they must produce # byte-identical output. Comparing the whole of both runs is the assertion. rb 20 "$MSTPCTL" -h; check_eq mstpd/help-short-form "$help_out" "$out" # mstpd HAS NO USAGE TEXT AT ALL. Its getopt string is "Vdsv:" with no 'h' and no # default help branch -- `-h' falls to `default: return -1'. Recorded as the # positive fact it is, because "mstpd --help printed nothing useful" is otherwise # indistinguishable from a broken binary. rb 20 "$MSTPD" -h; md_h=$out; md_h_rc=$rc check_eq mstpd/mstpd-no-usage-rc "255" "$md_h_rc" check_eq mstpd/mstpd-no-usage-text "no" "$(yn has 'Usage' "$md_h")" check_eq mstpd/mstpd-getopt-diagnostic "yes" "$(yn has "invalid option -- 'h'" "$md_h")" # --- argument validation, asserted for real ----------------------------------- # Every case here is reached BEFORE ctl_client_init(), which is what makes it # assertable on an image with no daemon. # No arguments at all: `if((argc == optind) && !batch_file) goto help' -- the # usage text goes to stdout and the status is 1, not 0. rb 20 "$MSTPCTL"; check_eq mstpd/argv-none-rc "1" "$rc" check_eq mstpd/argv-none-prints-usage "yes" "$(yn has 'Usage: mstpctl' "$out")" # An unknown short option: getopt_long prints its own line, then the `default:' # arm prints ours, then help() runs and the status is 1. rb 20 "$MSTPCTL" -Z; check_eq mstpd/argv-bad-option-rc "1" "$rc" check_eq mstpd/argv-bad-option-getopt "yes" "$(yn has "invalid option -- 'Z'" "$out")" check_eq mstpd/argv-bad-option-own "yes" "$(yn has "Unknown option '?'" "$out")" # -f takes exactly "json" or "plain"; anything else is named back to the user. rb 20 "$MSTPCTL" -f bogus; check_eq mstpd/argv-bad-format-rc "1" "$rc" check_eq mstpd/argv-bad-format-msg "yes" "$(yn has "Invalid format 'bogus'" "$out")" # ...and the two valid spellings are NOT rejected. `-f json' with no command # still falls through to the `argc == optind' help path, so what distinguishes # accept from reject is the absence of the diagnostic, not the status. rb 20 "$MSTPCTL" -f json; check_eq mstpd/argv-format-json-accepted "no" "$(yn has 'Invalid format' "$out")" rb 20 "$MSTPCTL" -f plain; check_eq mstpd/argv-format-plain-accepted "no" "$(yn has 'Invalid format' "$out")" # -b names a file that must open. The path is checked by fopen(3) inside the # getopt loop, so this is real validation and not a later failure. rb 20 "$MSTPCTL" -b "$W/there-is-no-such-file" check_eq mstpd/argv-missing-batch-rc "1" "$rc" check_eq mstpd/argv-missing-batch-msg "yes" "$(yn has "Could not open file '$W/there-is-no-such-file'" "$out")" # -s and -b are mutually exclusive, and the refusal fires from whichever arrives # second. The file exists here, so the ONLY reason to refuse is the combination. : > "$W/batch.txt" rb 20 "$MSTPCTL" -s -b "$W/batch.txt" check_eq mstpd/argv-stdin-plus-batch-rc "1" "$rc" check_eq mstpd/argv-stdin-plus-batch-msg "yes" "$(yn has 'Cannot mix stdin & batch file' "$out")" rb 20 "$MSTPCTL" -b "$W/batch.txt" -s check_eq mstpd/argv-batch-plus-stdin-msg "yes" "$(yn has 'Cannot mix stdin & batch file' "$out")" # mstpd's own -v takes an integer 0..LOG_LEVEL_MAX (100); strtoul must consume # the WHOLE argument. Both halves of that check are exercised: a non-numeric # argument and an in-range-looking number that is over the ceiling. rb 20 "$MSTPD" -v abc; check_eq mstpd/argv-loglevel-nan-rc "1" "$rc" check_eq mstpd/argv-loglevel-nan-msg "yes" "$(yn has 'Invalid loglevel abc' "$out")" rb 20 "$MSTPD" -v 999; check_eq mstpd/argv-loglevel-range-rc "1" "$rc" check_eq mstpd/argv-loglevel-range-msg "yes" "$(yn has 'Invalid loglevel 999' "$out")" # ...and a legal level is not refused. -v 1 with no -d still daemonizes, so the # accepted case is taken on -V, which returns from the same getopt loop. rb 20 "$MSTPD" -v 2 -V; check_eq mstpd/argv-loglevel-accepted "$VER" "$out" # mstpd's unknown-option arm returns -1 from main(), which is exit status 255 -- # a different contract from mstpctl's 1, and worth pinning as such. rb 20 "$MSTPD" -Z; check_eq mstpd/argv-mstpd-bad-option-rc "255" "$rc" # --- the kernel helper: /usr/sbin/bridge-stp ---------------------------------- # meta-oe replaces upstream's 200-line bridge-stp.in with an 18-line POSIX-sh # script, and that script IS the documented contract: mstpctl(8) says user-space # STP is enabled only "while ensuring that /sbin/bridge-stp kernel helper script # will return success (0) for this operation". Its argument handling therefore # runs on this image for real, with no daemon and no bridge involved. rb 20 "$BRIDGESTP"; check_eq mstpd/helper-no-args-rc "1" "$rc" check_eq mstpd/helper-no-args-msg "yes" "$(yn has 'Missing args: bridge-stp ' "$out")" rb 20 "$BRIDGESTP" br0; check_eq mstpd/helper-one-arg-rc "1" "$rc" check_eq mstpd/helper-one-arg-msg "yes" "$(yn has 'Missing args:' "$out")" rb 20 "$BRIDGESTP" br0 bogus; check_eq mstpd/helper-bad-op-rc "1" "$rc" check_eq mstpd/helper-bad-op-msg "yes" "$(yn has 'Invalid operation: bogus' "$out")" # NOTE FOR WHOEVER TURNS CONFIG_BRIDGE ON: the kernel execs the helper at the # hardcoded path /sbin/bridge-stp, and this port installs it to ${sbindir}, # which is /usr/sbin here because the distro is not usrmerged. Upstream's # configure.ac forces exec_prefix='' for exactly this reason and Yocto's # --exec-prefix=/usr overrides it. Pinned as a fact rather than worked around, # because on this kernel there is no bridge to call it. check_eq mstpd/helper-install-path "usr-sbin-only" \ "$([ -x /sbin/bridge-stp ] && echo both || echo usr-sbin-only)" # --- probes: the two config gaps, established at runtime ---------------------- # AF_PACKET. CONFIG_PACKET is what creates /proc/net/packet (packet_net_ops), so # its absence is a direct reading of the option rather than an inference. # 79-libnet.test measures the syscall itself with a compiled probe. packet_proc=$([ -e /proc/net/packet ] && echo present || echo absent) check_eq mstpd/probe-no-af-packet "absent" "$packet_proc" # The bridge link type. The active probe is the assertion: ask rtnetlink to # create one and read what it says. Nothing is created when it fails, and the # cleanup below covers the day it succeeds. if command -v ip >/dev/null 2>&1; then rb 30 ip link add name bstbr0 type bridge; br_out=$out; br_rc=$rc br_exists=$([ -d /sys/class/net/bstbr0 ] && echo yes || echo no) [ "$br_exists" = yes ] && ip link del bstbr0 >/dev/null 2>&1 check_eq mstpd/probe-bridge-add-refused "yes" "$([ "$br_rc" -ne 0 ] && echo yes || echo no)" check_eq mstpd/probe-bridge-not-created "no" "$br_exists" # Either spelling is accepted: iproute2 prints the kernel's extack when it # parses one and falls back to strerror(EOPNOTSUPP) when it does not. check_eq mstpd/probe-bridge-diagnostic "yes" \ "$(if has 'Unknown device type' "$br_out" || has 'not supported' "$br_out"; then echo yes; else echo no; fi)" else bst_skip mstpd/probe-bridge-add-refused "iproute2 ip is not on this image" bst_skip mstpd/probe-bridge-not-created "iproute2 ip is not on this image" bst_skip mstpd/probe-bridge-diagnostic "iproute2 ip is not on this image" br_exists=no fi # The passive half, independent of iproute2: a bridge device advertises itself # through /sys/class/net//bridge/, and no interface on this image has one. brdirs=0 for _d in /sys/class/net/*; do [ -d "$_d/bridge" ] && brdirs=$((brdirs + 1)); done check_eq mstpd/probe-no-bridge-sysfs "0" "$brdirs" # --- mstpd startup: the steps that work, and the one that does not ------------ # Guarded by the probe rather than by the bound -- see the header. On a kernel # with AF_PACKET this WOULD reach epoll_main_loop() and never return. The guard # stays on the packet probe deliberately: it is the LAST of the two walls, so it # is still true where the epoll one (brintos/linux#53) has been lifted, and the # skip arm below then asks for this block to be reworked against a real bound. if [ "$packet_proc" = absent ]; then rb 60 "$MSTPD" -d; start_out=$out; start_rc=$rc # It got as far as printing its version banner, which means the binary loaded # and its logging path works. check_eq mstpd/start-banner "yes" "$(yn has "Version - $VER" "$start_out")" # sanity_check() verifies eight sizeof() invariants over the packed on-wire # MSTP structures -- an 8-byte bridge identifier and a 51-byte MST # configuration identifier. This is the one genuinely ARCH-SENSITIVE assertion # in the suite: it is what would catch wasm32 laying out # __attribute__((packed)) differently from every arm upstream has run on. check_eq mstpd/start-sanity-checks "yes" "$(yn has 'Sanity checks succeeded' "$start_out")" # The wall, by name and by line. TST() names the failing expression and its # source location, so this pins WHICH of main()'s seven initialisers stopped # it -- and it is init_epoll() (main.c:182), NOT the packet socket two lines # later: epoll_create(2) is ENOSYS on this kernel because no epoll entry is # wired in the wasm32 syscall table, although CONFIG_EPOLL=y and # fs/eventpoll.o is in vmlinux -- brintos/linux#53 # (https://brintos.io/brintos/linux/issues/53). check_eq mstpd/start-stops-at-epoll "yes" "$(yn has 'epoll_create failed' "$start_out")" check_eq mstpd/start-tst-names-the-step "yes" "$(yn has 'verifying init_epoll() == 0' "$start_out")" # The two initialisers BEHIND that wall, asserted as unreached rather than as # passed. ctl_socket_init() binds the abstract AF_UNIX datagram socket # ".mstp_server" and packet_sock_init() asks for the BPDU socket; both run # after init_epoll(), so neither diagnostic can appear while #53 stands. Each # flips to a real assertion of that step the day epoll is wired. check_eq mstpd/start-ctl-socket-not-reached "no" \ "$(yn has "Couldn't bind socket" "$start_out")" check_eq mstpd/start-packet-sock-not-reached "no" \ "$(yn has 'packet_sock_init: socket failed' "$start_out")" check_eq mstpd/start-exit-status "255" "$start_rc" else for _c in start-banner start-sanity-checks start-stops-at-epoll \ start-tst-names-the-step start-ctl-socket-not-reached \ start-packet-sock-not-reached start-exit-status; do bst_skip "mstpd/$_c" \ "/proc/net/packet now exists, so mstpd -d would enter epoll_main_loop() and timeout(1) cannot be relied on to end it (it never fired before the brintos/linux#77 fix; brintos/linux#21) -- rework this block against a real bound before enabling it" done fi # --- mstpctl with no daemon: the exit contract, asserted for real ------------- # ctl_client_init() runs before command_lookup_and_validate(), so EVERY command # ends the same way. The three cases below are deliberately different shapes -- # a query, a mutation, and a name that is not a command at all -- and the point # is that they are indistinguishable. rb 20 "$MSTPCTL" showbridge; sb_out=$out check_eq mstpd/noctl-showbridge-rc "1" "$rc" check_eq mstpd/noctl-showbridge-msg "yes" "$(yn has "can't setup control connection" "$sb_out")" check_eq mstpd/noctl-connect-diagnostic "yes" "$(yn has "Couldn't connect to server" "$sb_out")" rb 20 "$MSTPCTL" addbridge br0; check_eq mstpd/noctl-addbridge-rc "1" "$rc" check_eq mstpd/noctl-addbridge-msg "yes" "$(yn has "can't setup control connection" "$out")" rb 20 "$MSTPCTL" no-such-command; nsc_out=$out check_eq mstpd/noctl-unknown-command-rc "1" "$rc" # The unreachability itself, asserted as a positive fact: the "Unknown command" # diagnostic exists in ctl_main.c and CANNOT be produced on an image with no # daemon, because the connect refuses first. A build that ever printed it here # would mean the two had been reordered. check_eq mstpd/noctl-validation-unreachable "no" "$(yn has 'Unknown command' "$nsc_out")" check_eq mstpd/noctl-unknown-same-as-valid "$sb_out" "$nsc_out" # The documented helper contract, at its own level: mstpctl(8) requires # /sbin/bridge-stp to "return success (0)" before the kernel will hand STP to # userspace. With no daemon it returns mstpctl's 1, so the kernel would refuse # -- which is the correct answer on a kernel that has no bridge either. rb 30 "$BRIDGESTP" br0 start check_eq mstpd/helper-start-propagates-rc "1" "$rc" check_eq mstpd/helper-start-reaches-mstpctl "yes" "$(yn has "can't setup control connection" "$out")" # --- the two config gaps, one attributed red each ----------------------------- # Written so that OK is GREEN. Neither needs an edit here when the option lands. if [ "$br_exists" = yes ]; then bst_ok mstpd/bridge-device else bst_notok mstpd/bridge-device \ "no bridge device type on this kernel: wasm32_defconfig sets no CONFIG_BRIDGE and '# CONFIG_MODULES is not set', so __rtnl_newlink() answers EOPNOTSUPP 'Unknown device type' -- brintos/linux#109 (https://brintos.io/brintos/linux/issues/109). mstpctl's 39 commands all take a as argv[1]" fi if [ "$packet_proc" = present ]; then bst_ok mstpd/packet-socket else bst_notok mstpd/packet-socket \ "mstpd cannot open its BPDU socket: socket(PF_PACKET, SOCK_RAW, ETH_P_802_2) is EAFNOSUPPORT on this kernel ('# CONFIG_PACKET is not set') -- brintos/linux#81 (https://brintos.io/brintos/linux/issues/81); same wall as lldpd/ports-initialised" fi # --- the state machine and the per-bridge queries: skipped, against the probe -- # These need a live daemon attached to a real bridge. Both preconditions are # absent and both were ESTABLISHED above rather than assumed -- that is what # makes these skips rather than silence. Each names what it would assert, so # that turning them on is a matter of deleting the guard. if [ "$br_exists" = yes ] && [ "$packet_proc" = present ]; then bst_notok mstpd/runtime-block-not-written \ "this kernel now has BOTH a bridge device type and AF_PACKET, so the MSTP state machine and the per-bridge queries are reachable and this block must be written -- see brintos/linux#109 and #81" else _why="no bridge device type (brintos/linux#109) and no AF_PACKET (brintos/linux#81) on this kernel; established by mstpd/probe-no-bridge-sysfs and mstpd/probe-no-af-packet" bst_skip mstpd/mstp-state-machine \ "would run mstpd, addbridge a real bridge and watch the CIST reach Designated/Forwarding -- $_why" bst_skip mstpd/showbridge-fields \ "would assert showbridge's bridge-id, designated-root, path-cost and protocol-version fields by value -- $_why" bst_skip mstpd/showmstconfid \ "would assert the MST ConfigId digest against the 802.1Q default and after setmstconfid -- $_why" bst_skip mstpd/set-bridge-params \ "would assert setmaxage/setfdelay/sethello round-trip through showbridge, including their documented range refusals -- $_why" bst_skip mstpd/showportdetail \ "would assert per-port role and state for a bridge port -- $_why" bst_skip mstpd/batch-command-validation \ "would assert mstpctl -b's per-line argc validation ('Incorrect number of arguments', 'Error on line N'), which sits behind ctl_client_init() and needs a live daemon -- $_why" fi rm -rf "$W"