. "$BST_LIB" # BST-TIMEOUT: 300 # 74 cases on the expected path, ~80 execs, one of which is a # # daemon start under `timeout 20`. No loops and no corpus. # # Declared at roughly 5x the aoetools measurement next door # # (55 cases / 46 s on the same guest), which is the ratio # # 99-sg3-utils.test uses. Nothing here waits on a network: # # every transport call is refused at socket(2) or # # setsockopt(2) and returns immediately, and the one daemon # # start aborts before it opens its IPC socket. # smcroute -- a static multicast routing daemon on a kernel with no multicast # routing table. # # WHAT THIS MACHINE CAN AND CANNOT TEST. SMCRoute exists to drive the mrouted # API: smcrouted opens a raw IGMP socket, claims the API with # setsockopt(MRT_INIT), and installs (S,G) entries in the kernel's multicast # forwarding cache. arch/wasm32/configs/wasm32_defconfig brings the net stack up # LOOPBACK ONLY -- CONFIG_NET=y, CONFIG_INET=y, CONFIG_UNIX=y, with # "# CONFIG_IPV6 is not set" and "# CONFIG_PACKET is not set" annotated beside # them as deliberate v0.1 omissions. CONFIG_IP_MROUTE is not named there at all, # which is exactly the point: it defaults to n in the kernel's own Kconfig and # nothing turns it on. So no route this package installs is reachable. # # WHAT IS THEREFORE ASSERTED FOR REAL, and it is most of the two programs: # the .conf parser (every directive, every diagnostic, run under `smcrouted -F`, # which parses and returns without touching the kernel at all), the client/daemon # argument split (two front-ends with two disjoint option sets and a client that # validates its own arguments before it ever opens the IPC socket), the usage and # version text both programs print, and the sysexits(3) status the daemon returns # when it cannot get a multicast routing socket. Those are arithmetic on argv and # strings; they are the same on every architecture, so a wasm32 build that got # them wrong has no environmental excuse available. # # WHAT IS PROBED RATHER THAN ASSUMED. The kernel gap itself, read at runtime from # ${LIBEXEC}/smcroute-tests/smcroute-probe. Every branch below is written against # the live answer, so this suite turns its skips back into assertions on their own # evidence the day the kernel grows the subsystem -- not on this comment. # # AND THE CONTROLS ARE THE POINT OF THE PROBE. "MRT_INIT was refused" is a # statement about the kernel only while this process can still open ordinary # sockets. The probe therefore opens AF_INET stream, AF_INET datagram and AF_UNIX # stream in the same run, and every multicast-routing skip below is gated on all # three succeeding. If they stop succeeding this suite REDS rather than quietly # skipping, which is the failure mode a suite full of environmental skips is # otherwise built to have. # # THE EXIT-STATUS CASE IS THE ONE THIS PORT CARRIES A SOURCE PATCH FOR. # smcrouted.8 documents status 76, EX_PROTOCOL, for "Kernel does not seem to # support multicast routing" -- this image, exactly -- and the shipped 2.5.6 # binary cannot produce it: start_server() counts one slot per address family # while mroute_init() folds both into one boolean, so the count never reaches # zero and the daemon instead runs forever with no routing API and no diagnostic # status. 0001-smcrouted-count-both-routing-APIs.patch makes the count honest. # The two cases at the end are what hold that fix in place. bst_ok smcroute/suite-canary # first line: absorbs brintos/linux#96, which drops it require smcrouted smcroute/present work=${TMPDIR:-/tmp}/bst-smcroute.$$ if ! mkdir -p "$work" 2>/dev/null; then bst_notok smcroute/workdir "cannot create $work -- the .conf fixtures below have nowhere to live" exit 0 fi bst_ok smcroute/workdir # --- the three programs upstream installs into ${sbindir} --------------------- # # smcrouted and smcroutectl are automake sbin_PROGRAMS; `smcroute` is a # dist_sbin_SCRIPTS shell wrapper installed by a different rule, so it can go # missing on its own without either binary noticing. for t in smcrouted smcroutectl smcroute; do if command -v "$t" >/dev/null 2>&1; then bst_ok "smcroute/present/$t" else bst_notok "smcroute/present/$t" "not installed" fi done # --- smcrouted: usage and version -------------------------------------------- check_rc smcroute/daemon/help-rc 0 smcrouted -h check_out smcroute/daemon/help-usage "[-hnNsv]" smcrouted -h check_out smcroute/daemon/help-verify-opt "Check configuration file syntax" smcrouted -h check_out smcroute/daemon/help-foreground "Run daemon in foreground" smcrouted -h check_rc smcroute/daemon/version-rc 0 smcrouted -v check_out smcroute/daemon/version-string "SMCRoute v2.5.6" smcrouted -v check_out smcroute/daemon/version-bugaddr "Bug report address: https://github.com/troglobit/smcroute/issues" smcrouted -v check_out smcroute/daemon/version-homepage "Project homepage: https://troglobit.com/smcroute.html" smcrouted -v # EX_USAGE. `-Z` is in neither getopt string, so getopt's default arm is what # answers -- the same arm every malformed invocation lands in. check_rc smcroute/daemon/unknown-option 64 smcrouted -Z # atoi("-1") < 0 is the daemon's only numeric argument rejection that does not # need the kernel: it is a range check on argv, decided before anything opens. check_rc smcroute/daemon/bad-table-id 64 smcrouted -t -1 # --- the client/daemon argument split ---------------------------------------- # # Two front-ends over one source tree, with DISJOINT option sets, and this is # where that is asserted rather than assumed. The daemon has no -a/-r/-j/-l: # adding and removing routes is the client's job, spoken over the IPC socket, and # a daemon that quietly accepted them would be a daemon that had grown a second # way to mutate the kernel. Symmetrically the client has no -f/-F/-l: it never # reads a .conf file and never opens a routing socket. # # Each of these is the getopt default arm, so the assertion is "this option is # not in this program's string" -- which is the split, stated in the only place # the programs actually enforce it. check_rc smcroute/split/daemon-has-no-add 64 smcrouted -a check_rc smcroute/split/daemon-has-no-remove 64 smcrouted -r check_rc smcroute/split/daemon-has-no-join 64 smcrouted -j check_rc smcroute/split/daemon-has-no-batch 64 smcrouted -b check_rc smcroute/split/client-has-no-conf 1 smcroutectl -f /dev/null check_rc smcroute/split/client-has-no-verify 1 smcroutectl -F /dev/null check_rc smcroute/split/client-has-no-loglvl 1 smcroutectl -l info # --- smcroutectl: usage and version ------------------------------------------ check_rc smcroute/client/help-rc 0 smcroutectl -h check_out smcroute/client/help-commands "Commands:" smcroutectl -h check_out smcroute/client/help-arguments "INBOUND" smcroutectl -h check_out smcroute/client/help-show "show routes" smcroutectl -h check_rc smcroute/client/version-rc 0 smcroutectl -v check_out smcroute/client/version-string "SMCRoute v2.5.6" smcroutectl -v check_rc smcroute/client/unknown-option 1 smcroutectl -Z # --- the client validates its own arguments BEFORE it opens the socket -------- # # `add` needs three, `remove`/`join`/`leave` two. parse() counts them against # args[].min_args and returns 1 with "Not enough arguments" without constructing # an IPC message, so these cases are true whether or not a daemon is running -- # which is what makes them assertions rather than observations of this image. for c in add remove join leave; do out=$(smcroutectl "$c" eth0 2>&1); rc=$? if bst_execfail "$out"; then bst_unrun "smcroute/client/short-args-$c" "$out" elif [ "$rc" -eq 1 ]; then case "$out" in *"Not enough arguments to command $c"*) bst_ok "smcroute/client/short-args-$c" ;; *) bst_notok "smcroute/client/short-args-$c" \ "rc=1 but not the argument-count diagnostic: $(_bst_1line "$out")" ;; esac else bst_notok "smcroute/client/short-args-$c" "rc=$rc want=1 -- $(_bst_1line "$out")" fi done # `help ` is the other half of the same parser: it resolves the command, # sees help was asked for, and prints that command's own example instead of # reaching the socket. check_rc smcroute/client/help-topic-rc 0 smcroutectl help add check_out smcroute/client/help-topic-eg "smcroutectl add eth0 192.168.2.42 225.1.2.3 eth1 eth2" smcroutectl help add # --- the client with no daemon ----------------------------------------------- # # Nothing is listening on this image and nothing in this suite starts a listener # (the daemon case below exits before ipc_init()), so ipc_connect() fails ENOENT # and every command reports it the same way. rc is 1 -- NOT a sysexits code: # smcroutectl's failures are plain 1, which is itself part of the split. The # diagnostic is ipc_connect()'s "Cannot find IPC socket " followed by # ipc_command()'s ENOENT arm; both were read off a native build of this same # source during the port, and either one satisfies the case. for c in "show" "kill" "flush"; do out=$(bst_bound 20 smcroutectl $c 2>&1); rc=$? if bst_execfail "$out"; then bst_unrun "smcroute/client/no-daemon-$c" "$out" elif [ "$rc" -eq 1 ]; then case "$out" in *"Cannot find IPC socket"*|*"Daemon may be running with another"*|*"Failed connecting to daemon"*|*"Daemon not running"*) bst_ok "smcroute/client/no-daemon-$c" ;; *) bst_notok "smcroute/client/no-daemon-$c" \ "rc=1 but no connect diagnostic: $(_bst_1line "$out")" ;; esac else bst_notok "smcroute/client/no-daemon-$c" "rc=$rc want=1 -- $(_bst_1line "$out")" fi done # --- the compatibility wrapper ----------------------------------------------- # # /usr/sbin/smcroute is a #!/bin/sh script, so its exit status is the shell's # rather than the package's -- its -h arm ends in a top-level `return`, which # busybox ash accepts and bash diagnoses. The TEXT is the package's either way, # so that is what is asserted; the dispatch arm below is what proves the wrapper # actually reaches smcroutectl rather than merely echoing. out=$(smcroute -h 2>/dev/null) case "$out" in *"compatibility wrapper script for SMCRoute"*) bst_ok smcroute/wrapper/help-text ;; *) bst_notok smcroute/wrapper/help-text "no wrapper help text: $(_bst_1line "$out")" ;; esac out=$(smcroute -Q 2>/dev/null) case "$out" in *"Unknown command or option to the SMCRoute compatiblity wrapper"*) bst_ok smcroute/wrapper/unknown-text ;; *) bst_notok smcroute/wrapper/unknown-text "no unknown-command text: $(_bst_1line "$out")" ;; esac check_out smcroute/wrapper/version-dispatch "SMCRoute v2.5.6" smcroute -v # --- the probe --------------------------------------------------------------- P=/usr/libexec/smcroute-tests/smcroute-probe if [ ! -x "$P" ]; then bst_notok smcroute/probe/present "no probe at $P -- smcroute-tests is not installed, so nothing below can be attributed" exit 0 fi bst_ok smcroute/probe/present PO=$("$P" 2>&1); prc=$? if bst_execfail "$PO"; then bst_unrun smcroute/probe/runs "$PO" exit 0 fi case "$PO" in *"probe end"*) bst_ok smcroute/probe/runs ;; *) bst_notok smcroute/probe/runs "rc=$prc, no 'probe end' -- the probe did not finish, so its values below are partial: $(_bst_1line "$PO")" exit 0 ;; esac # field -- the value of on the probe's # line. Reads from a variable, never a redirect: a top-level stdin # redirect on the virtiofs root makes dup2(2) return 513 and ash exits, ending # the suite silently (brintos/linux#73). field() { printf '%s\n' "$PO" | sed -n "s/^$1 / /p" \ | sed -n "s/.*[ ]$2=\\([^ ]*\\).*/\\1/p" | sed -n 1p } # state -- the third word of a ` present|absent` line. state() { printf '%s\n' "$PO" | sed -n "s/^$1 [^ ]* \\([a-z]*\\)$/\\1/p" | sed -n 1p } inet_s=$(field inet-stream rc) inet_d=$(field inet-dgram rc) unix_s=$(field unix-stream rc) raw4=$(field raw-igmp rc) mrt4=$(field mrt-init rc) mrt4_err=$(printf '%s\n' "$PO" | sed -n 's/^mrt-init .*strerror=//p' | sed -n 1p) raw6=$(field raw-icmpv6 rc) inet6=$(field inet6-dgram rc) gifa=$(field getifaddrs rc) ifall=$(field ifcensus all) IF=$(field iface-first name) MIF=$(field iface-mcast name) ifmcast=$(field ifcensus mcast) mrvif=$(state proc-ip-mr-vif) mrcache=$(state proc-ip-mr-cache) # The controls, asserted as cases of their own. Every skip further down cites # them, so they must be visible rather than implied. check_eq smcroute/probe/control-inet-stream 0 "$inet_s" check_eq smcroute/probe/control-inet-dgram 0 "$inet_d" check_eq smcroute/probe/control-unix-stream 0 "$unix_s" controls_ok=0 [ "$inet_s" = 0 ] && [ "$inet_d" = 0 ] && [ "$unix_s" = 0 ] && controls_ok=1 # --- what the kernel actually answers ---------------------------------------- # # Reported as cases rather than as a comment, because the day one of them flips # is the day the skips below have to stop being skips. mrt-init is the whole # question: rc=0 means this kernel has a multicast routing table. if [ "$controls_ok" != 1 ]; then bst_notok smcroute/kernel/mroute-reading \ "the probe could not open ordinary sockets (inet-stream=$inet_s inet-dgram=$inet_d unix-stream=$unix_s), so nothing it says about multicast routing can be attributed to the kernel" elif [ "$mrt4" = 0 ]; then bst_ok smcroute/kernel/mroute-reading bst_notok smcroute/kernel/mroute-absent \ "MRT_INIT succeeded -- this kernel HAS IPv4 multicast routing. That is not a defect, it is the milestone this suite was written against; the skips below are now assertions and BRINTOS-1531's test-plan entry needs re-taking" elif [ "$raw4" != 0 ]; then bst_ok smcroute/kernel/mroute-reading bst_ok smcroute/kernel/mroute-absent # refused one step earlier, at socket(2) else bst_ok smcroute/kernel/mroute-reading bst_ok smcroute/kernel/mroute-absent fi # /proc/net/ip_mr_vif and ip_mr_cache are created by the kernel's own # CONFIG_IP_MROUTE code, so they are a second, independent reading of the same # fact -- and they must AGREE with MRT_INIT. A kernel that answered MRT_INIT # while publishing neither file, or the reverse, is doing something nobody has # modelled, and that is a red rather than a skip. # # The agreement is only assertable when MRT_INIT was actually ASKED. If the raw # IGMP socket never opened, mrt-init carries the not-attempted sentinel and there # is nothing for /proc to agree with -- so the two files are then held to # agreeing with EACH OTHER instead, which is still a real assertion (the same # kernel code creates both) and is not a claim the suite cannot support. if [ "$raw4" = 0 ]; then if [ "$mrt4" = 0 ]; then want_proc=present; else want_proc=absent; fi check_eq smcroute/kernel/proc-ip-mr-vif-agrees "$want_proc" "$mrvif" check_eq smcroute/kernel/proc-ip-mr-cache-agrees "$want_proc" "$mrcache" else check_eq smcroute/kernel/proc-ip-mr-files-agree "$mrvif" "$mrcache" fi # --- .conf parsing, the environment-independent half ------------------------- # # `smcrouted -F FILE` is a parse-and-return mode: main() runs iface_init(), # conf_read(), iface_exit() and returns, touching no socket and no kernel table. # It answers EX_OK (0) for a clean file and EX_CONFIG (78) for one with any # diagnostic in it, and under -F smclog() prints to stdout, so the diagnostic # text is assertable too. # # These eight need no interface to exist: every one of them is decided either # before the interface list is consulted or by the absence of a name from it. vrfy() { # vrfy -- conf text on stdin via $vrfy_text _vid=$1; _vwant=$2; _vneedle=$3 printf '%s\n' "$vrfy_text" > "$work/t.conf" _vout=$(bst_bound 20 smcrouted -F "$work/t.conf" 2>&1); _vrc=$? if bst_execfail "$_vout"; then bst_unrun "$_vid" "$_vout" return fi case "$_vout" in *"$_vneedle"*) _vhit=1 ;; *) _vhit=0 ;; esac if [ "$_vrc" -eq "$_vwant" ] && [ "$_vhit" -eq 1 ]; then bst_ok "$_vid" else bst_notok "$_vid" "rc=$_vrc want=$_vwant want:'$_vneedle' got:'$(_bst_1line "$_vout")'" fi } if [ "$gifa" != 0 ]; then bst_notok smcroute/conf/iface-list \ "getifaddrs(3) failed in the probe (rc=$gifa), and smcrouted -F calls it too -- the parser cases below would be measuring that failure, not the parser" else bst_ok smcroute/conf/iface-list : > "$work/empty.conf" check_rc smcroute/conf/empty-file 0 bst_bound 20 smcrouted -F "$work/empty.conf" vrfy_text='# a comment # an indented comment ' vrfy smcroute/conf/comments-only 0 "Verifying configuration file" # A file that is not there is EX_CONFIG under -F and merely a NOTICE without # it -- the daemon starts anyway and waits for a client. -F is the strict # reading, and this is the case that pins the difference. check_rc smcroute/conf/missing-file 78 bst_bound 20 smcrouted -F "$work/no-such-file.conf" vrfy_text='frobnicate from lo group 225.1.2.3' vrfy smcroute/conf/unknown-command 78 "Unknown command frobnicate" vrfy_text='phyint' vrfy smcroute/conf/phyint-missing-name 78 "phyint missing interface pattern" vrfy_text='phyint bstnosuchif0 enable' vrfy smcroute/conf/phyint-unknown-iface 78 "does not exist" vrfy_text='mroute from bstnosuchif0 source 192.168.1.42 group 225.1.2.3 to bstnosuchif1' vrfy smcroute/conf/mroute-unknown-iif 78 "matches no valid phyint" vrfy_text='mgroup from bstnosuchif0 group 225.1.2.3' vrfy smcroute/conf/mgroup-unknown-iif 78 "matches no valid phyint" fi # --- .conf parsing, the half that needs one interface to exist --------------- # # conf_parse() rejects any directive naming an interface that is not in the # getifaddrs(3) list BEFORE it parses that directive's addresses, so the address # and prefix-length parser -- the arithmetic this package's correctness actually # turns on -- is only reachable through a name that IS in the list. The probe # supplies one, or says there is none; it is not typed here, because which # interfaces a brintOS guest brings up is not this suite's to assert. if [ "$gifa" != 0 ]; then bst_skip smcroute/conf/address-parser \ "getifaddrs(3) failed in the probe (rc=$gifa), so there is no interface name to reach the address parser through" elif [ -z "$IF" ] || [ "$IF" = "-" ]; then bst_skip smcroute/conf/address-parser \ "the probe found no interfaces at all (ifcensus all=$ifall), so every directive is rejected on its interface name before its addresses are read" else bst_ok smcroute/conf/address-parser vrfy_text="phyint $IF enable mroute from $IF source 192.168.1.42 group 225.1.2.3 to $IF" vrfy smcroute/conf/valid-mroute 0 "Verifying configuration file" vrfy_text="phyint $IF enable mgroup from $IF group 225.1.2.3" vrfy smcroute/conf/valid-mgroup 0 "Verifying configuration file" # 10.1.2.3 is a perfectly good address and not a multicast one. This is the # is_multicast() arm, and it is the single assertion that most directly says # "the address parser was compiled correctly for this arm". vrfy_text="phyint $IF enable mroute from $IF source 192.168.1.42 group 10.1.2.3 to $IF" vrfy smcroute/conf/bad-group 78 "Invalid multicast group: 10.1.2.3" vrfy_text="phyint $IF enable mroute from $IF group 225.1.2.3/40 to $IF" vrfy smcroute/conf/bad-group-preflen 78 "Invalid multicast group prefix length" vrfy_text="phyint $IF enable mroute from $IF source 999.1.2.3 group 225.1.2.3 to $IF" vrfy smcroute/conf/bad-source 78 "Invalid source address: 999.1.2.3" vrfy_text="phyint $IF enable mgroup from $IF group 10.1.2.3" vrfy smcroute/conf/mgroup-bad-group 78 "Invalid multicast group" vrfy_text="phyint $IF enable ttl-threshold 300" vrfy smcroute/conf/ttl-out-of-range 78 "out of range (1-255)" vrfy_text="phyint $IF enable mroute from $IF group 225.1.2.3 to bstnosuchif1" vrfy smcroute/conf/unknown-oif 78 "outbound bstnosuchif1 is not a known phyint" # include is a glob(3) expansion, and an included file's own diagnostics are # logged rather than counted -- conf_parse() recurses and only warns. So the # assertion is that the include is FOLLOWED and the outer file still parses, # which is what the directive promises. printf '%s\n' "phyint $IF enable" > "$work/inc-a.conf" vrfy_text="include $work/inc-*.conf" vrfy smcroute/conf/include-glob 0 "Verifying configuration file" fi # --- the documented exit status ---------------------------------------------- # # THE case this port carries a source patch for. smcrouted.8 documents 76 # EX_PROTOCOL for "Kernel does not seem to support multicast routing", and stock # 2.5.6 cannot return it: `api = 2` counts one slot per address family while # mroute_init() collapses both into one boolean and the caller decrements once, # so the `!api` arm is dead code and the daemon instead enters its server loop # with no routing API at all -- indistinguishable, to anything supervising it, # from a daemon that is working. The patch makes mroute_init() attempt both # families and return how many failed. # # Run in the foreground (-n, which also routes the log to stderr via LOG_PERROR) # with -N so no VIFs are attempted, and with every path -- .conf, PID, IPC socket # -- pointed into this suite's own directory, so a failure to exit cannot leave a # listener behind for the client cases above to find on a later run. Under # `timeout 20`, because the failure mode being guarded against is precisely "does # not exit". if [ "$controls_ok" != 1 ]; then bst_notok smcroute/daemon/no-mroute-exit \ "the probe could not open ordinary sockets (inet-stream=$inet_s inet-dgram=$inet_d unix-stream=$unix_s), so the daemon's refusal below could not be attributed to the kernel" elif [ "$mrt4" = 0 ]; then bst_skip smcroute/daemon/no-mroute-exit \ "MRT_INIT succeeded (this kernel has multicast routing), so the daemon would start and serve rather than refuse -- there is no refusal to assert" bst_skip smcroute/daemon/no-mroute-message \ "MRT_INIT succeeded, so the daemon prints no such diagnostic" else : > "$work/d.conf" out=$(bst_bound 20 smcrouted -n -N -f "$work/d.conf" -P "$work/d.pid" \ -u "$work/d.sock" -i bstsmcroute 2>&1); rc=$? if bst_execfail "$out"; then bst_unrun smcroute/daemon/no-mroute-exit "$out" bst_unrun smcroute/daemon/no-mroute-message "$out" else if [ "$rc" -eq 76 ]; then bst_ok smcroute/daemon/no-mroute-exit elif [ "$rc" -eq 124 ]; then bst_notok smcroute/daemon/no-mroute-exit \ "the daemon did not exit within 20 s -- it entered its server loop with no routing API, which is the stock 2.5.6 behaviour the api-accounting patch removes" else bst_notok smcroute/daemon/no-mroute-exit \ "rc=$rc want=76 (EX_PROTOCOL, smcrouted.8) with MRT_INIT refused ($mrt4_err) -- $(_bst_1line "$out")" fi case "$out" in *"Kernel does not support multicast routing."*) bst_ok smcroute/daemon/no-mroute-message ;; *) bst_notok smcroute/daemon/no-mroute-message \ "no 'Kernel does not support multicast routing.' on stderr -- $(_bst_1line "$out")" ;; esac fi # It must not have got far enough to publish an IPC socket: ipc_init() runs # after the abort point, and a socket here would mean the daemon served. if [ -e "$work/d.sock" ]; then bst_notok smcroute/daemon/no-ipc-socket "the daemon created $work/d.sock, so it reached ipc_init() -- it did not abort where it reports it did" else bst_ok smcroute/daemon/no-ipc-socket fi fi # --- installing and removing routes ------------------------------------------ # # The package's actual purpose, and the one thing this image cannot do. Skipped # on the probe's evidence and on the controls, never on a comment: the day # MRT_INIT succeeds these two become live assertions with no edit to this file. if [ "$controls_ok" != 1 ]; then bst_notok smcroute/route/install \ "the probe could not open ordinary sockets, so a route failure here would say nothing about multicast routing" bst_notok smcroute/route/remove \ "the probe could not open ordinary sockets, so a route failure here would say nothing about multicast routing" elif [ "$mrt4" != 0 ]; then bst_skip smcroute/route/install \ "no multicast routing table on this kernel: raw-igmp rc=$raw4, MRT_INIT rc=$mrt4 ($mrt4_err), /proc/net/ip_mr_vif $mrvif, with all three control sockets open" bst_skip smcroute/route/remove \ "no multicast routing table on this kernel: MRT_INIT rc=$mrt4 ($mrt4_err)" elif [ -z "$MIF" ] || [ "$MIF" = "-" ]; then # NOT the same name as the parser cases use. mroute4_enable() creates a VIF # only for an IFF_MULTICAST interface, and on Linux `lo` carries no such flag # -- so an image whose only interface is loopback can parse an mroute and # still have nowhere to install one. That is a fact about the image, not a # defect, and it is the probe's census that says so. bst_skip smcroute/route/install \ "MRT_INIT works but no interface is both IFF_MULTICAST and IFF_UP (ifcensus all=$ifall mcast=$ifmcast), so no VIF can be created to route through" bst_skip smcroute/route/remove \ "MRT_INIT works but no interface is both IFF_MULTICAST and IFF_UP (ifcensus all=$ifall mcast=$ifmcast)" else # Reached only on a kernel that has grown the subsystem. The daemon owns the # table, so the route goes in through it: start it against this suite's own # socket, add through the client, remove, then kill. No -N here -- the VIFs # are the point. : > "$work/r.conf" bst_bound 60 smcrouted -n -f "$work/r.conf" -P "$work/r.pid" -u "$work/r.sock" \ -i bstsmcroute -D 30 >"$work/r.log" 2>&1 & _dpid=$! _i=0 while [ $_i -lt 30 ] && [ ! -e "$work/r.sock" ]; do _i=$((_i + 1)); sleep 1; done if [ ! -e "$work/r.sock" ]; then bst_notok smcroute/route/install "the daemon published no IPC socket within 30 s on a kernel whose MRT_INIT succeeded -- $(_bst_1line "$(cat "$work/r.log" 2>/dev/null)")" bst_notok smcroute/route/remove "no daemon to remove through" else check_rc smcroute/route/install 0 bst_bound 20 smcroutectl -u "$work/r.sock" \ add "$MIF" 192.168.1.42 225.1.2.3 "$MIF" check_rc smcroute/route/remove 0 bst_bound 20 smcroutectl -u "$work/r.sock" \ remove "$MIF" 192.168.1.42 225.1.2.3 fi bst_bound 20 smcroutectl -u "$work/r.sock" kill >/dev/null 2>&1 wait $_dpid 2>/dev/null fi rm -rf "$work" 2>/dev/null