. "$BST_LIB" require ulogd ulogd2/present # BST-TIMEOUT: 300 # ulogd2 2.0.8+git at 79aa980f (meta-networking recipes-filter/ulogd2, plus this # layer's ulogd2_%.bbappend and its static-plugin patch) -- the netfilter # userspace logging daemon: a configurable stack of input, filter and output # plugins driven from ulogd.conf. # # --------------------------------------------------------------------------- # WHAT THIS SUITE IS ACTUALLY FOR: THE PLUGINS HAVE TO BE THERE. # # Upstream dlopen()s every plugin from a path ulogd.conf names. This arm has no # dynamic loader, so a stock build starts, fails every `plugin' line, and runs an # empty stack -- and a test that only checks "it started" passes against exactly # that. The bbappend's patch links the plugins into the daemon instead. Groups B, # C and D are what tell the two builds apart: they assert the plugin NAMES the # daemon resolves, and a build with an empty stack answers almost none of them. # Scored as a guard rather than merely observed to pass -- run against a build # whose plugins cannot be loaded, these 46 cases drop to 13 green. # # Every expectation below was scored against a native x86-64 build of the same # 79aa980f tree, built twice: once stock (shared modules, dlopen) and once # --disable-shared --enable-static with this patch. Against the reference config # the two arms emit the same 173 log lines in the same order, differing only in # the file:line stamp the logger prints -- so the text asserted here is the # shared build's own and not a wasm-specific dialect. # # group A identity 3 cases # group B each built-in plugin resolves 25 cases # group C the whole set, in one run 3 cases # group D the reference stack runs 6 cases # group E the netlink input 1 case # group F config diagnostics 7 cases # canary 1 case # --------- # 46 cases, all of them offline # # --------------------------------------------------------------------------- # NOTHING HERE IS RED, AND THAT IS A STATEMENT ABOUT THE PORT'S SHAPE RATHER # THAN A CLEAN BILL FOR THE KERNEL. # # This kernel is CONFIG_NET=y with no CONFIG_NETFILTER (brintos/linux#86), which # is why nfnetlink protocol 12 and the NFLOG/conntrack groups are unreachable -- # the same gap iptables, ebtables, arptables, ipset and the libnetfilter_* ports # are held by. It does not surface as a red here for two reasons, both worth # stating so that a fully green run is not mistaken for full coverage: # # * The plugins that would bind an NFLOG or conntrack group (NFLOG, NFCT, # NFACCT) are NOT BUILT. They need libnetfilter_log, libnetfilter_conntrack # and libnetfilter_acct, none of which is ported yet -- each has its own # ticket. There is no case to run. # * The one netlink input that IS built, ULOG, fails identically on a modern # x86-64 kernel: ipt_ULOG was removed from Linux in 3.17 and netlink # protocol NETLINK_NFLOG(5) is unregistered there too. Group E asserts that # failure because it is the oracle's own behaviour, not because this arm is # special. # # So ulogd's packet path is dead on this image and stays dead until #86 lands # AND the three libnetfilter_* ports do. What this suite covers is the whole of # what the daemon does before it touches a socket: read the config, resolve the # stack, wire the keys, start the outputs, and diagnose a broken config. # # --------------------------------------------------------------------------- # The first result line a suite emits is lost to the console reader # (linux-in-the-browser#138, ours BRINTOS-1149/BRINTOS-1181): the runner counts # passes as total-minus-failures, so whatever sits at position 1 is never # actually adjudicated. This case therefore asserts nothing. Remove it when 1149 # is fixed; the suite total drops by one when it goes. bst_ok ulogd2/suite-canary tmp=${TMPDIR:-/tmp}/bst-ulogd2.$$ mkdir -p "$tmp" 2>/dev/null # --------------------------------------------------------------------------- # group A -- identity. -V and -h return before any config is read. check_out ulogd2/version 'ulogd Version 2.0.8' ulogd -V check_out ulogd2/copyright 'Harald Welte ' ulogd -V check_out ulogd2/usage '-c --configfile' ulogd -h # --------------------------------------------------------------------------- # group B -- every plugin this build carries resolves by the name the config # would use. `ulogd -i ` loads one plugin and prints its info; upstream # always exits 0 from info mode, so the assertion is on the printed name. # # The MODULE PATHS BELOW DO NOT EXIST on this image and that is deliberate: the # bbappend deletes ${libdir}/ulogd because every one of those archives is # already inside the daemon. A config still names plugins the upstream way, and # the patch resolves the name out of the path rather than opening it -- so these # cases also prove that a stock ulogd.conf needs no rewriting here. for spec in \ inppkt:ULOG inppkt:UNIXSOCK \ raw2packet:BASE \ filter:HWHDR filter:IFINDEX filter:IP2BIN filter:IP2HBIN filter:IP2STR \ filter:MARK filter:PRINTFLOW filter:PRINTPKT filter:PWSNIFF \ output:GPRINT output:GRAPHITE output:IPFIX output:JSON output:LOGEMU \ output:NACCT output:OPRINT output:PCAP output:SQLITE3 output:SYSLOG \ output:XML do cls=${spec%%:*}; nm=${spec#*:} check_out "ulogd2/plugin-$nm" "Name: $nm" \ ulogd -i "/usr/lib/ulogd/ulogd_${cls}_${nm}.so" done # The control that makes the 23 above mean something: a plugin this build does # NOT carry resolves to nothing. NFLOG needs libnetfilter_log, which is not # ported (its own ticket), so PACKAGECONFIG leaves it out -- and info mode then # prints nothing at all, exactly as upstream does for a module it cannot open. check_eq ulogd2/plugin-nflog-absent "" \ "$(ulogd -i /usr/lib/ulogd/ulogd_inppkt_NFLOG.so 2>/dev/null)" # ...and a path that names no plugin at all is refused in the daemon's own voice. cat > "$tmp/nosuch.conf" <&1); rc=$? case "$out" in *"load_plugin: '/usr/lib/ulogd/ulogd_output_NOSUCHPLUGIN.so'"*) hit=1 ;; *) hit=0 ;; esac check_eq ulogd2/plugin-unknown-path "1 1" "$hit $rc" # --------------------------------------------------------------------------- # group C -- the whole built-in set, registered in one run. A config that names # a stack but no `plugin' lines takes upstream's load-everything path, which on # this build registers every plugin linked into the daemon. One run, and the # answer is the complete inventory rather than 23 separate claims. # # The stack is headed by ULOG so the run TERMINATES BY ITSELF: the input cannot # bind (group E) and the daemon exits, which keeps this case synchronous. Every # plugin has already been registered by then -- the fallback runs before the # stack is built. cat > "$tmp/all.conf" <&1) got=$(printf '%s\n' "$allout" | sed -n "s/.*registering plugin \`\\([A-Z0-9]*\\)'.*/\\1/p" | sort | tr '\n' ' ') want="BASE GPRINT GRAPHITE HWHDR IFINDEX IP2BIN IP2HBIN IP2STR IPFIX JSON LOGEMU MARK NACCT OPRINT PCAP PRINTFLOW PRINTPKT PWSNIFF SQLITE3 SYSLOG ULOG UNIXSOCK XML " check_eq ulogd2/builtin-set "$want" "$got" case "$allout" in *'registering all built-in plugins'*) a=1 ;; *) a=0 ;; esac check_eq ulogd2/builtin-load-all 1 "$a" # 23 built in, and the count is asserted separately from the names so that a # suite reading "the set matched" cannot also be reading "the set was empty". check_eq ulogd2/builtin-count 23 \ "$(printf '%s\n' "$allout" | sed -n "s/.*registering plugin \`[A-Z0-9]*'.*/x/p" | wc -l | tr -d ' ')" # --------------------------------------------------------------------------- # group D -- the reference stack. Five plugins named the upstream way, resolved # in the order the config lists them, wired together, started, and left running # until it is signalled. This is the port's pass criterion. # # The daemon must NOT be bounded with timeout(1): GNU timeout waits for its # child in the sigsuspend idiom linux-in-the-browser#161 parks on, so it hangs # alongside the process it was meant to bound (measured under 16-zsh.test). # BRINTOS-WORKAROUND: linux-in-the-browser#161 remove -- the hand-rolled bound # below replaces timeout(1) only for that reason; restore it when #161 closes. # # Readiness is POLLED rather than slept for. ulogd is a 20 MB whole-program wasm # binary and takes appreciably longer to reach its first log line here than on a # native host; a fixed sleep long enough to be safe would also be long enough to # matter against the suite's own bound, and one that is too short reports an # empty log as a broken daemon (measured: at 5 s the log is still empty, at 15 s # it is complete). cat > "$tmp/ref.conf" < "$tmp/ref.log" 2>&1 & refpid=$! n=0 while [ $n -lt 90 ]; do grep -q 'pluginstance stack' "$tmp/ref.log" 2>/dev/null && break sleep 2; n=$((n+2)) done sleep 5 if kill -0 "$refpid" 2>/dev/null; then alive=1; else alive=0; fi kill -TERM "$refpid" 2>/dev/null sleep 5 kill -9 "$refpid" 2>/dev/null # the names, in the order the config named them -- not a set this time order=$(sed -n "s/.*registering plugin \`\\([A-Z0-9]*\\)'.*/\\1/p" "$tmp/ref.log" | tr '\n' ' ') check_eq ulogd2/ref-register-order "UNIXSOCK BASE IP2STR PRINTPKT LOGEMU " "$order" check_out ulogd2/ref-stack \ "building new pluginstance stack: 'unix1:UNIXSOCK,base1:BASE,ip2str1:IP2STR,print1:PRINTPKT,emu1:LOGEMU'" \ cat "$tmp/ref.log" # a `plugin' line was honoured, so upstream's load-everything fallback must NOT # have fired -- the distinction the handle bookkeeping in the patch preserves. # Both spellings are rejected: this build's and the shared build's. case "$(cat "$tmp/ref.log" 2>/dev/null)" in *'registering all built-in plugins'*) fellback=1 ;; *'loading all plugins at'*) fellback=1 ;; *) fellback=0 ;; esac check_eq ulogd2/ref-no-fallback 0 "$fellback" # the stack came up: it was still running once it had said so, and it said so on # the way out. An empty stack reaches neither -- it dies at once with "not even # a single working plugin stack". check_eq ulogd2/ref-stays-up 1 "$alive" check_out ulogd2/ref-clean-exit 'Terminal signal received, exiting' cat "$tmp/ref.log" # LOGEMU's start() opened the file the config gave it, so an output plugin did # not merely register -- it ran. check ulogd2/ref-output-opened test -f "$tmp/ref.emu" # --------------------------------------------------------------------------- # group E -- the netlink input. ULOG binds NETLINK_NFLOG(5) in its start(), and # nothing registers protocol 5 here: this kernel has no CONFIG_NETFILTER at all # (brintos/linux#86). The native oracle answers exactly the same way -- ipt_ULOG # was removed from Linux in 3.17 -- so this is asserted rather than attributed. # What it proves is that the failure happens where it should: AFTER the whole # stack has been resolved and wired, in the input plugin's own start(). The run # reused here is group C's, which is headed by ULOG for this reason. case "$allout" in *"Can't create ULOG handle"*) a=1 ;; *) a=0 ;; esac case "$allout" in *"error starting \`ulog1'"*) b=1 ;; *) b=0 ;; esac case "$allout" in *'not even a single working plugin stack'*) c=1 ;; *) c=0 ;; esac check_eq ulogd2/ulog-netlink-unavailable "1 1 1" "$a $b $c" # --------------------------------------------------------------------------- # group F -- config diagnostics. Each message and exit status below is the # native oracle's, byte for byte. # a key that may not repeat cat > "$tmp/e_mult.conf" <&1); rc=$? case "$out" in *'option "logfile" occurred more than once'*) a=1 ;; *) a=0 ;; esac case "$out" in *'unable to parse config file'*) b=1 ;; *) b=0 ;; esac check_eq ulogd2/err-duplicate-key "1 1 1" "$a $b $rc" # a stack naming a plugin nothing registered cat > "$tmp/e_stack.conf" <&1); rc=$? case "$out" in *"can't find requested plugin NOSUCHPLUGIN"*) a=1 ;; *) a=0 ;; esac check_eq ulogd2/err-unknown-stack-plugin "1 1" "$a $rc" # an output plugin where a source plugin has to be. The [emu1] section has to be # present for this case to reach the check it is about: a plugin instance whose # section is missing fails in configure() first, which is the case below. cat > "$tmp/e_first.conf" <&1); rc=$? case "$out" in *'first plugin in stack has to be source plugin'*) a=1 ;; *) a=0 ;; esac check_eq ulogd2/err-stack-order "1 1" "$a $rc" # ...and the same stack with no [emu1] section at all stops one step earlier. cat > "$tmp/e_section.conf" <&1); rc=$? case "$out" in *'error during configure of plugin LOGEMU'*) a=1 ;; *) a=0 ;; esac check_eq ulogd2/err-missing-section "1 1" "$a $rc" # a config file that is not there check_rc ulogd2/err-no-configfile 1 ulogd -v -c "$tmp/absent.conf" # a config with no [global] section at all: upstream reports it before the log # is open, so the message is lost and only the status survives. Asserted as the # oracle behaves rather than as it ought to. cat > "$tmp/e_nosection.conf" <<'EOF' [notglobal] logfile="/dev/null" EOF check_rc ulogd2/err-no-global-section 1 ulogd -v -c "$tmp/e_nosection.conf" # a string value past the parser's bound, same class long=$(awk 'BEGIN{s="";while(length(s)<300)s=s "x";print s}' "$tmp/e_toolong.conf" </dev/null