# BST-TIMEOUT: 300 # MEASURED: see the ==BST-SUITE== line on any run. The # # scoring forks almost nothing -- every case below is a # # shell string compare against values awk lifted from ONE # # probe run -- so the cost is the single exec of the probe. # # 300 is the bound the ticket gates and the nightly pass. . "$BST_LIB" # libnetfilter_conntrack 1.0.9 -- the userspace library for the kernel's # connection-tracking state table, i.e. the API for reading, creating and # deleting conntrack entries over the NFNL_SUBSYS_CTNETLINK message family. # What ships under --disable-shared is /usr/lib/libnetfilter_conntrack.a, ten # headers under /usr/include/libnetfilter_conntrack/ and # libnetfilter_conntrack.pc. There is no CLI and no daemon; the tarball's # utils/ and examples/ are check_PROGRAMS that `make` does not build and `make # install` discards, and each takes its arguments on argv and talks straight to # a netlink socket, so none can self-drive. On an image with no compiler that # leaves nothing that ships able to drive a line of this library -- hence the # probe, built in the recipe against the archive that ships. # # --------------------------------------------------------------------------- # THIS LIBRARY OWNS A SOCKET, AND THAT DECIDES THE SHAPE OF THIS FILE. # # It is the opposite case from the sibling port libnetfilter_cthelper (suite # 79-libnetfilter-cthelper, BRINTOS-964), which owns no descriptor at all and # is therefore fully measurable on a kernel with no netfilter. Here the library # is two layers, and only one of them runs on this image: # # * A FRAMING layer over libmnl -- nfct_nlmsg_build/parse, the attribute # set/get families, the group API, nfct_snprintf, nfct_cmp/nfct_copy, the # dump filter, the connlabel bitmask and the expectation object. None of it # touches a descriptor. It is 132 of the 138 probe cases below and every # one of them runs here. # * A TRANSPORT layer -- mnl_socket_open(NETLINK_NETFILTER) for the mnl-style # API and nfct_open()/nfct_query() over libnfnetlink for the older one. # Both begin at socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER). That is # the S group, six cases, and it is expected red here for a named reason. # # --------------------------------------------------------------------------- # WHY THE ROUND TRIP IS DRIVEN OVER libmnl AND NOT nfct_query(). It is not a # preference. nfct_query()/nfct_catch() CANNOT BE LINKED on this arm: they # reach libnfnetlink's nfnl_step(), whose VLA # `struct nfattr *tb[ssh->cb[type].attr_count]` (src/libnfnetlink.c:1347) has # its address escape to the indirect subsystem callback across a suspend, and # the coroutinize pass REFUSES that shape and FATALs the whole link -- # brintos/hwjs-cc#15 (https://brintos.io/brintos/hwjs-cc/issues/15), the same # residual DS6 case that blocks oniguruma. It is a LINK-time refusal, not a # runtime failure, so a probe calling nfct_query() would not exist to run and # there is no case here that could report it. Isolated to one statement: adding # a single nfct_query() call to a program that otherwise links clean is the # entire difference, reproduced twice. # # So the round trip is composed with nfct_nlmsg_build() and driven over # mnl_socket_*, exactly as upstream's own examples/nfct-mnl-{create,get,del}.c # drive it -- the modern transport for the same messages, and the one every # example in the tarball uses. nfct_open()/nfct_close() still link and are # still driven (S05), because the refusal is in the callback path rather than # in opening the handle. The symbol census above still asserts nfct_query is # IN the archive; what this arm cannot do is link a program that calls it. # # --------------------------------------------------------------------------- # THE ORACLE, AND WHY IT IS STRONGER ON THIS PORT THAN ON THE SIBLING. # # Every composed buffer is scored byte for byte against hex pinned from a # native x86-64 build of this same 1.0.9 tarball against libmnl 1.0.5 and # libnfnetlink 1.0.2. Netlink's TLV encoding is a wire format -- 4-byte # alignment, a 4-byte struct nlattr of {u16 len, u16 type}, struct nlmsghdr 16 # bytes and struct nfgenmsg 4 on both a 32- and a 64-bit build -- and this # library puts its scalars on the wire in NETWORK byte order (htonl/htons), so # the two architectures MUST agree exactly and a disagreement is a real defect, # never an expected difference. Set-then-get through the library's own parser # would prove only that its writer and its reader agree: a port that placed # nla_len at the wrong offset would round-trip through its own mistake and # report every field correct. The Z group is scored first because a # disagreement there explains every hex mismatch after it. # # The oracle here also reaches the TRANSPORT layer, which the cthelper one # could not. Run under `unshare -rn`, the same probe binary on the same source # returns S01-S06 all green -- socket, bind, a completed dump of an empty table, # and a create/get/destroy round trip in which the GET actually finds the entry # the CREATE made (found=1) before the DELETE removes it. So the S values # pinned below are MEASURED against a kernel that has conntrack, not merely # asserted as the criterion a configured kernel ought to meet. (ctnetlink gates # on ns_capable(net->user_ns, CAP_NET_ADMIN), so a user+net namespace is enough; # cthelper's equivalent gates on plain capable() and refused its control.) # A third arm is worth knowing about when reading a red here: run natively but # WITHOUT the namespace, the same binary answers S01/S02 ok, S03 dump-failed # EPERM, S06 create-failed EPERM. Permission and absence are therefore # distinguishable in this probe's own output, and the errno in each detail line # is what says which one this image hit. # # --------------------------------------------------------------------------- # THE EXPECTED REDS: S01-S06, AND THEY ARE HELD BY TWO GAPS, NOT ONE. # # S01/S02 open and bind NETLINK_NETFILTER. On this image that answers # EPROTONOSUPPORT: wasm32_defconfig sets CONFIG_NET=y, CONFIG_INET=y, # CONFIG_UNIX=y and NO CONFIG_NETFILTER of any kind, so # net/netfilter/nfnetlink.c is not built and netlink protocol 12 is never # registered. That is brintos/linux#86 # (https://brintos.io/brintos/linux/issues/86), filed under the sibling port # BRINTOS-963. # # S03-S06 NEED MORE THAN #86, AND THIS IS THE PART THAT IS EASY TO GET WRONG. # Protocol 12 alone is not enough for any of them: the ctnetlink subsystem is # registered by CONFIG_NF_CT_NETLINK on top of CONFIG_NF_CONNTRACK, and #86's # proposed diff turns on only CONFIG_NETFILTER + CONFIG_NETFILTER_NETLINK -- it # says so itself, that neither pulls in "any hook, table or conntrack". So the # day #86 lands, S01 and S02 go green and S03-S06 stay red, and that is NOT # this port regressing: it is the second, separately-tracked gap surfacing, # which is exactly what the ticket asked this suite to establish. The two are # scored as one group but attributed by which one the detail line's errno # names. # # They are reported failing and NOT skipped so they flip green by themselves # the day the options land -- that is the regression signal this suite is # bought for. # # C01/C02 ARE THE CONTROL AND MUST STAY GREEN. They open NETLINK_ROUTE -- # protocol 0, registered unconditionally under CONFIG_NET. C green with S red # says "netlink protocol 12 is not registered", a kernel .config fact. # BOTH red would say "AF_NETLINK is unavailable", a different fault in a # different place that would make the attribution above wrong. Without the # control the two are indistinguishable from this file's output, so C01/C02 are # scored strictly and carry no attribution: they are not held by the gap, they # are what gives the gap its meaning. # # --------------------------------------------------------------------------- # THREE UPSTREAM BEHAVIOURS ARE PINNED RATHER THAN FIXED. # # B25/B26 and P18 -- ATTR_ID never reaches the wire. nfct_nlmsg_build drops it # (B25/B26 are byte-for-byte B23/B24, with the id set) and a parsed object # therefore reads it back as 0, even though the object holds it and # nfct_snprintf prints it in the XML. The kernel owns conntrack ids; upstream # is right not to let a consumer propose one. Pinned so a future version that # starts emitting it is a visible change of subject. # # B01-B06 -- the original tuple's CTA_IP_V4_SRC and CTA_IP_V4_DST are emitted # whether or not they were set, as 0.0.0.0. That is why B01, B03 and B05 are # all 56 bytes while B02, B04 and B06 differ: the LENGTHS are equal and the # BYTES are not, and scoring both is what makes the difference legible. # # pkgconfig-requires -- upstream's .pc carries `Requires: libnfnetlink` and # does NOT name libmnl, even though nfct_nlmsg_build/parse cannot resolve a # single mnl_* symbol without it. On a --disable-shared distro a consumer # built from `pkg-config --libs libnetfilter_conntrack` alone therefore fails # to link. It is pinned because it is a fact a consumer has to know, and # because it is why the recipe's own probe link line names libmnl explicitly. # # All three are third-party behaviour on a package we are porting, not brintOS # defects and not something this port changes. Per the project's # no-public-upstream-reports rule nothing is filed for them; they are pinned # here and written up in the test plan. # # --------------------------------------------------------------------------- # ONE VALUE THIS SUITE DELIBERATELY DOES NOT SCORE. nfexp_maxsize() is # sizeof(struct nf_expect), which carries pointers, so a 32-bit target and the # 64-bit oracle disagree by construction. Pinning it would buy a red that says # "wasm32 is 32-bit" and nothing about this port, so the probe does not emit it. R=${BST_LIBNETFILTER_CONNTRACK_ROOT:-} PROBE=${BST_LIBNETFILTER_CONNTRACK_PROBE:-$R/usr/libexec/libnetfilter-conntrack-tests/libnetfilter-conntrack-probe} ARCHIVE=$R/usr/lib/libnetfilter_conntrack.a PCFILE=$R/usr/lib/pkgconfig/libnetfilter_conntrack.pc INC=$R/usr/include/libnetfilter_conntrack # Asserted against the .pc rather than assumed: a recipe bumped without # regenerating the pinned bytes below must go red on ONE named case instead of # silently changing the subject of the other hundred. WANT_VERSION=1.0.9 if [ ! -f "$ARCHIVE" ]; then bst_skip libnetfilter-conntrack/present "libnetfilter-conntrack not installed on this image" exit 0 fi bst_ok libnetfilter-conntrack/present check libnetfilter-conntrack/staticlib test -s "$ARCHIVE" check libnetfilter-conntrack/pkgconfig test -s "$PCFILE" check libnetfilter-conntrack/hdr-main test -s "$INC/libnetfilter_conntrack.h" # The two uapi copies the package ships itself. They are pkginclude_HEADERS and # they matter here more than on a normal distro: this image installs NO kernel # headers at all, so these are the only definition of the ctnetlink attribute # numbers anything on target could compile against. check libnetfilter-conntrack/hdr-uapi-ctnl test -s "$INC/linux_nfnetlink_conntrack.h" check libnetfilter-conntrack/hdr-uapi-common test -s "$INC/linux_nf_conntrack_common.h" check libnetfilter-conntrack/hdr-tcp test -s "$INC/libnetfilter_conntrack_tcp.h" # Shape, not absence: this arm converts to shared libraries per recipe # (BRINTOS-1740), so a .so here is a conversion, not a policy breach. bst_lib_shape libnetfilter-conntrack/no-shared "$R/usr/lib/libnetfilter_conntrack.so" "$R/usr/lib/libnetfilter_conntrack.so.3" pcver=$(sed -n 's/^Version: *//p' "$PCFILE" 2>/dev/null | head -1) check_eq libnetfilter-conntrack/pkgconfig-version "$WANT_VERSION" "$pcver" check_out libnetfilter-conntrack/pkgconfig-libs "-lnetfilter_conntrack" cat "$PCFILE" # See the header comment: Requires names libnfnetlink and not libmnl, which is # why the recipe's probe link line names libmnl explicitly. pcreq=$(sed -n 's/^Requires: *//p' "$PCFILE" 2>/dev/null | head -1) check_eq libnetfilter-conntrack/pkgconfig-requires "libnfnetlink" "$pcreq" # --------------------------------------------------------------------------- # The symbol census. This library exports far more than fits, so this is a # deliberate SAMPLE rather than the exhaustive list the cthelper suite can # afford -- one name from each subsystem the probe drives, so a red says which # half of the archive did not ship rather than that one arbitrary name went # missing. # grep -a: the archive is binary and some greps refuse it without it. if echo x | grep -aq x 2>/dev/null; then _grepq="grep -aq"; else _grepq="grep -q"; fi for sym in nfct_new nfct_destroy nfct_clone \ nfct_set_attr nfct_set_attr_u8 nfct_set_attr_u16 nfct_set_attr_u32 \ nfct_get_attr nfct_get_attr_u8 nfct_get_attr_u32 \ nfct_attr_is_set nfct_attr_unset \ nfct_set_attr_grp nfct_get_attr_grp nfct_attr_grp_is_set \ nfct_setobjopt nfct_getobjopt \ nfct_snprintf nfct_snprintf_labels \ nfct_cmp nfct_copy nfct_copy_attr \ nfct_nlmsg_build nfct_nlmsg_parse nfct_nlmsg_build_filter \ nfct_payload_parse \ nfct_filter_create nfct_filter_dump_create \ nfct_bitmask_new nfct_bitmask_set_bit nfct_bitmask_equal \ nfct_labelmap_new \ nfct_open nfct_close nfct_fd nfct_query nfct_send nfct_catch \ nfct_callback_register \ nfexp_new nfexp_destroy nfexp_set_attr nfexp_snprintf \ nfexp_nlmsg_build nfexp_nlmsg_parse; do if $_grepq "$sym" "$ARCHIVE" 2>/dev/null; then bst_ok "libnetfilter-conntrack/sym-$sym" else bst_notok "libnetfilter-conntrack/sym-$sym" "$sym absent from $ARCHIVE" fi done # --------------------------------------------------------------------------- # The probe. Both it and the archive are this ticket's deliverable, so a skip # on either is a broken port and not a legitimate absence -- but an exec that # the shell could not stage at all is platform noise, and bst_unrun is what # tells those two apart. if [ ! -x "$PROBE" ]; then bst_skip libnetfilter-conntrack/probe-runs "$PROBE absent from the image" exit 0 fi tmp=${TMPDIR:-/tmp} pout=$tmp/bst-libnetfilter-conntrack-out.$$ "$PROBE" > "$pout" 2>&1 prc=$? if [ "$prc" -eq 0 ]; then bst_ok libnetfilter-conntrack/probe-runs elif bst_execfail "$(head -c 200 "$pout" 2>/dev/null)"; then bst_unrun libnetfilter-conntrack/probe-runs "$(head -c 200 "$pout" 2>/dev/null)" rm -f "$pout" exit 0 else bst_notok libnetfilter-conntrack/probe-runs "probe exited $prc: $(_bst_1line "$(head -c 300 "$pout")")" fi # A probe killed part-way still emits usable lines, so the run is scored # either way -- but PROBE-END is what separates "these values are the whole # story" from "the rest is missing", and want() reports an absent value as its # own red rather than as a mismatch. if grep -q '^PROBE-END$' "$pout" 2>/dev/null; then bst_ok libnetfilter-conntrack/probe-complete else bst_notok libnetfilter-conntrack/probe-complete "probe output has no PROBE-END terminator (last line: $(tail -1 "$pout" 2>/dev/null))" fi # One awk pass lifts every case line into a V_ shell variable, so the # hundred-odd comparisons below fork nothing. First occurrence wins. eval "$(awk ' function q(v) { gsub(/'"'"'/, "'"'"'\\'"'"''"'"'", v); return "'"'"'" v "'"'"'" } $1 ~ /^[ZHBMPRGKUNFXECS][0-9][0-9]$/ && seen[$1]++ == 0 { id = $1; v = $0; sub(/^[^ ]+ ?/, "", v); print "V_" id "=" q(v) } ' "$pout")" # want : the strict comparison. An absent # value is its own message -- "the probe stopped before here" and "the probe # answered differently" are different findings and a bare mismatch would hide # the first behind an empty got. want() { eval "_got=\${V_$1-}" if [ -z "$_got" ]; then bst_notok "libnetfilter-conntrack/$2" "no value on target (probe stopped before $1); want='$3'" elif [ "$_got" = "$3" ]; then bst_ok "libnetfilter-conntrack/$2" else bst_notok "libnetfilter-conntrack/$2" "want:'$3' got:'$_got'" fi } NF_PROTO=${V_S01-} NF_HELD="held by S01 netfilter-socket ($NF_PROTO): wasm32_defconfig sets no CONFIG_NETFILTER, so netlink protocol 12 is unregistered (brintos/linux#86)" # The second gap. Once #86 lands, S01/S02 go green and this is what still holds # S03-S06 -- so the attribution follows the evidence rather than needing this # file edited on the day. CT_HELD="held by the conntrack table itself: protocol 12 alone is not ctnetlink, which needs CONFIG_NF_CONNTRACK + CONFIG_NF_CT_NETLINK -- neither of which brintos/linux#86's proposed diff turns on" # want_s : same comparison, but a red while # the transport is unavailable carries its cause, and names WHICH of the two # gaps the evidence points at. Green is scored green by the same test as # want(), so these flip by themselves the day the options land rather than # needing this file edited. want_s() { eval "_got=\${V_$1-}" if [ "$_got" = "$3" ]; then bst_ok "libnetfilter-conntrack/$2" elif [ "$NF_PROTO" != "ok" ]; then bst_notok "libnetfilter-conntrack/$2" "want:'$3' got:'$_got' -- $NF_HELD" elif [ -z "$_got" ]; then bst_notok "libnetfilter-conntrack/$2" "no value on target (probe stopped before $1); want='$3'" else bst_notok "libnetfilter-conntrack/$2" "want:'$3' got:'$_got' -- $CT_HELD" fi } # --- Z: the wire-format constants ----------------------------------------- # Scored first: a disagreement here explains every hex mismatch after it. # ATTR_* is the LIBRARY's bitset index and CTA_* is the number that goes on # the wire -- two different spaces, and confusing them is the easiest way to # write a wrong consumer, so both are pinned next to each other. The repeats # in lib-attr-aliases are the documented aliases (ATTR_IPV4_SRC == # ATTR_ORIG_IPV4_SRC and friends), which is the assertion, not a typo. want Z01 sizeof-nlmsghdr 16 want Z02 sizeof-nfgenmsg 4 want Z03 sizeof-nlattr 4 want Z04 mnl-alignto 4 want Z05 mnl-nlmsg-hdrlen 16 want Z06 mnl-attr-hdrlen 4 want Z07 subsys-ctnetlink 1 want Z08 subsys-ctnetlink-exp 2 want Z09 nfnetlink-v0 0 want Z10 lib-subsys-aliases '1 2' want Z11 uapi-msg-types '0 1 2 3' want Z12 uapi-cta-numbers '1 2 3 4 5 7 8 12' want Z13 uapi-cta-tuple-numbers '1 2 3 18' want Z14 lib-attr-aliases '0 0 15 15 17 17' want Z15 lib-attr-numbers '19 24 25 32 55 61' want Z16 lib-attr-max 75 want Z17 status-bits '1 2 4 8' want Z18 query-enum '0 1 2 3 4 5' want Z19 output-enum '0 0 1 2' want Z20 cmp-flags '0 1 2 64' # --- H: the message header ------------------------------------------------ # 16 bytes of nlmsghdr + the 4-byte nfgenmsg extra header, composed the way # upstream's own examples compose it. hdr-fields/hdr-nfgenmsg decompose what # hdr-bytes pins, so a red in the hex is readable without a hex editor. want H01 hdr-len 20 want H02 hdr-bytes 1400000000010506443322110000000002000000 want H03 hdr-fields 'type=0x0100 subsys=1 cmd=0 flags=0x0605 seq=0x11223344' want H04 hdr-nfgenmsg 'family=2 version=0 res_id=0' # --- B: nfct_nlmsg_build, one attribute at a time -------------------------- # Incremental rather than only the full message: a single wrong encoding # reddens exactly its own pair and everything after it, and the FIRST red # names the attribute that broke. # # Two of these pairs look like duplicates and are not, both deliberately: # * l3proto/orig-src/orig-dst are all 56 bytes because the tuple nest emits # CTA_IP_V4_SRC and CTA_IP_V4_DST whether or not they were set. The # lengths are equal; the BYTES are not, and that is what the pair scores. # * build-id is byte-for-byte build-zone because ATTR_ID never reaches the # wire -- the kernel owns conntrack ids and the builder drops ours. want B01 build-l3proto-len 56 want B02 build-l3proto-bytes 38000000000105064433221100000000020000002400018014000180080001000000000008000200000000000c0002800500010000000000 want B03 build-orig-src-len 56 want B04 build-orig-src-bytes 38000000000105064433221100000000020000002400018014000180080001000101010108000200000000000c0002800500010000000000 want B05 build-orig-dst-len 56 want B06 build-orig-dst-bytes 38000000000105064433221100000000020000002400018014000180080001000101010108000200020202020c0002800500010000000000 want B07 build-l4proto-len 72 want B08 build-l4proto-bytes 48000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c000280050001000600000006000200000000000600030000000000 want B09 build-ports-len 72 want B10 build-ports-bytes 48000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a0000 want B11 build-reply-len 124 want B12 build-reply-bytes 7c000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a00000600030000140000 want B13 build-tcp-state-len 140 want B14 build-tcp-state-bytes 8c000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a00000600030000140000100004800c0001800500010001000000 want B15 build-timeout-len 148 want B16 build-timeout-bytes 94000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a000006000300001400000800070000000064100004800c0001800500010001000000 want B17 build-mark-len 156 want B18 build-mark-bytes 9c000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a000006000300001400000800070000000064080008000000cafe100004800c0001800500010001000000 want B19 build-status-len 164 want B20 build-status-bytes a4000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a0000060003000014000008000300000000080800070000000064080008000000cafe100004800c0001800500010001000000 want B21 build-helper-len 176 want B22 build-helper-bytes b0000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a0000060003000014000008000300000000080800070000000064080008000000cafe100004800c00018005000100010000000c0005800800010066747000 want B23 build-zone-len 184 want B24 build-zone-bytes b8000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a0000060003000014000008000300000000080800070000000064080008000000cafe100004800c00018005000100010000000c00058008000100667470000600120000070000 want B25 build-id-len 184 want B26 build-id-bytes b8000000000105064433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a0000060003000014000008000300000000080800070000000064080008000000cafe100004800c00018005000100010000000c00058008000100667470000600120000070000 # --- M: the one mandatory attribute --------------------------------------- # nfct_nlmsg_build refuses a conntrack with no ATTR_ORIG_L3PROTO and composes # anything else it is given. Pinned in both directions, including the case # that carries metadata but no tuple: l3proto is still required there. want M01 mandatory-no-l3proto '-1 EINVAL' want M02 mandatory-l3proto-only 0 want M03 mandatory-meta-without-l3proto '-1 EINVAL' # --- P: nfct_nlmsg_parse --------------------------------------------------- # The ticket's "assert every field survives", read back through the public # getters. The numbers are asserted as numbers, not as the macros that name # them: 2 is AF_INET, 6 is IPPROTO_TCP, 1 is TCP_CONNTRACK_SYN_SENT and 8 is # IPS_CONFIRMED. The reply half is the derived tuple, so parse-repl-src being # the original's DST is the assertion that nfct_setobjopt inverted correctly. # parse-id-not-on-wire is the pinned upstream drop, not a lost field. want P01 parse-rc 0 want P02 parse-orig-l3 2 want P03 parse-orig-src 1.1.1.1 want P04 parse-orig-dst 2.2.2.2 want P05 parse-orig-l4 6 want P06 parse-orig-sport 20 want P07 parse-orig-dport 10 want P08 parse-repl-src 2.2.2.2 want P09 parse-repl-dst 1.1.1.1 want P10 parse-repl-sport 10 want P11 parse-repl-dport 20 want P12 parse-tcp-state 1 want P13 parse-timeout 100 want P14 parse-mark 0x0000cafe want P15 parse-status 8 want P16 parse-helper ftp want P17 parse-zone 7 want P18 parse-id-not-on-wire 0x00000000 # --- R: the round trip proved by re-serialisation -------------------------- # Reading fields back (P) proves the parser agrees with the builder; requiring # the PARSED object to re-compose the identical bytes proves nothing was # silently dropped in between. reserialise-nfct-cmp asks the library the same # question the bytes answer, and the two are pinned side by side on purpose: # if they ever disagree, one of nfct_cmp and nfct_nlmsg_build is wrong and the # pair says which. want R01 reserialise-equal 'len=184 origlen=184 equal=yes' want R02 reserialise-bytes b8000000000101004433221100000000020000003400018014000180080001000101010108000200020202021c0002800500010006000000060002000014000006000300000a00003400028014000180080001000202020208000200010101011c000280050001000600000006000200000a0000060003000014000008000300000000080800070000000064080008000000cafe100004800c00018005000100010000000c00058008000100667470000600120000070000 want R03 reserialise-nfct-cmp 1 # --- G: the group setter/getter API --------------------------------------- # nfct_set_attr_grp / nfct_get_attr_grp write and read several related # attributes as one struct -- a separate code path from the per-attribute # family, and a port that got the struct layout wrong would pass every P case # and fail here. grp-set-then-get-* set through the group API and read back # through the per-attribute one, because the API invites mixing them and a # consumer that does must not see stale values. want G01 grp-orig-ipv4-set 1 want G02 grp-orig-port-set 1 want G03 grp-counters-unset 0 want G04 grp-get-ipv4 'src=1.1.1.1 dst=2.2.2.2' want G05 grp-get-port 'sport=20 dport=10' want G06 grp-set-then-get-src 10.0.0.1 want G07 grp-set-then-get-dst 10.0.0.2 want G08 grp-unset-rc 0 want G09 grp-unset-clears 0 # --- K: nfct_cmp and nfct_copy -------------------------------------------- # The flags select which halves participate, so these cases differ by ONE # attribute at a time and each names the flag it is about; a comparator that # ignored its flags would pass cmp-identical and fail the rest. want K01 cmp-identical 1 want K02 cmp-orig-differs 0 want K03 cmp-repl-same 1 want K04 cmp-all-sees-mark 0 want K05 cmp-orig-ignores-mark 1 want K06 copy-orig-equal 1 want K07 copy-orig-omits-meta 0 want K08 copy-meta-mark 0x0000cafe want K09 copy-meta-timeout 100 want K10 copy-attr-one '1 7' want K11 copy-attr-only-one 0 # --- U: is_set / unset, and the effect on the wire ------------------------ # unset-shortens-wire is the point of the group: removing an attribute has to # shorten the composed message by its 4-byte header plus its 4-byte payload, # or the "unset" only cleared a bit the builder does not read. want U01 is-set-mark 1 want U02 is-set-secmark 0 want U03 unset-mark-rc 0 want U04 unset-mark-clears 0 want U05 unset-shortens-wire 'full=184 after=176 delta=8' want U06 unset-absent-noop 0 want U07 unset-absent-wire-unmoved 176 # --- N: nfct_snprintf, both output types ---------------------------------- # Newlines and tabs are escaped by the probe so each case is one line. # NFCT_OF_TIME and NFCT_OF_TIMESTAMP are never passed: both read the clock and # no pinned value could survive them. Truncation returns what snprintf WOULD # have written, so the rc stays at the untruncated length while the buffer # holds nine characters and a NUL. want N01 snprintf-rc 148 want N02 snprintf-default 'tcp 6 100 SYN_SENT src=1.1.1.1 dst=2.2.2.2 sport=20 dport=10 [UNREPLIED] src=2.2.2.2 dst=1.1.1.1 sport=10 dport=20 mark=51966 zone=7 helper=ftp' want N03 snprintf-rc-layer3 159 want N04 snprintf-layer3 'ipv4 2 tcp 6 100 SYN_SENT src=1.1.1.1 dst=2.2.2.2 sport=20 dport=10 [UNREPLIED] src=2.2.2.2 dst=1.1.1.1 sport=10 dport=20 mark=51966 zone=7 helper=ftp' want N05 snprintf-rc-xml 568 want N06 snprintf-xml '1.1.1.12.2.2.220102.2.2.21.1.1.11020SYN_SENT100519667305419896ftp' want N07 snprintf-truncate-rc 148 want N08 snprintf-truncate-out 'tcp ' want N09 snprintf-rc-event 158 want N10 snprintf-event ' [NEW] tcp 6 100 SYN_SENT src=1.1.1.1 dst=2.2.2.2 sport=20 dport=10 [UNREPLIED] src=2.2.2.2 dst=1.1.1.1 sport=10 dport=20 mark=51966 zone=7 helper=ftp' # --- F: nfct_nlmsg_build_filter ------------------------------------------- # The dump-filter object is a second builder with its own attribute space, # reached by NFCT_Q_DUMP_FILTER. It composes without a socket, so it is # measurable here even though the query that would carry it is not. want F01 filter-empty-len 20 want F02 filter-empty-bytes 1400000001010103443322110000000002000000 want F03 filter-mark-l3-len 36 want F04 filter-mark-l3-bytes 2400000001010103443322110000000002000000080008000000cafe08001500ffffffff # --- X: the connlabel bitmask --------------------------------------------- # Pure bit manipulation with no kernel and no configuration file behind it. # nfct_labelmap_new() is deliberately not driven: it reads # /etc/xtables/connlabel.conf, which this image does not ship, so it would # measure the image's file layout rather than this port. want X01 bitmask-maxbit 127 want X02 bitmask-initially-clear 0 want X03 bitmask-set '1 1' want X04 bitmask-neighbour-clear 0 want X05 bitmask-out-of-range 0 want X06 bitmask-clone-equal 1 want X07 bitmask-diverges 0 want X08 bitmask-clone-bits '0 1' want X09 bitmask-clear '0 0' # --- E: the expectation half ---------------------------------------------- # nfexp_* is a second object with its own attribute space built on top of # conntrack objects. It shares the transport with the conntrack half # (subsystem NFNL_SUBSYS_CTNETLINK_EXP) but its framing is independent code. want E01 exp-timeout 100 want E02 exp-class 1 want E03 exp-helper ftp want E04 exp-is-set '1 0' want E05 exp-snprintf-rc 180 want E06 exp-snprintf '100 proto=6 src=1.1.1.1 dst=2.2.2.2 sport=20 dport=10 mask-src=1.1.1.1 mask-dst=2.2.2.2 sport=20 dport=10 master-src=1.1.1.1 master-dst=2.2.2.2 sport=20 dport=10 class=1 helper=ftp' # --- C: the control, scored strictly -------------------------------------- # NETLINK_ROUTE is protocol 0, registered unconditionally under CONFIG_NET. # These carry no attribution: they are not held by the gap, they are what # gives the gap its meaning. want C01 rtnetlink-socket ok want C02 rtnetlink-bind ok # --- S: the transport layer, expected red on this image ------------------- # The ticket's pass criterion: open an NFNL_SUBSYS_CTNETLINK handle, dump the # table (an empty table is a pass -- ctnetlink-dump-entries 0 is the expected # value, not a shortfall), then create, query and destroy an entry. # # All six values below are MEASURED, from the same probe binary run under # `unshare -rn` on a kernel that has conntrack. They are what this port does # when the kernel provides the subsystem. ctnetlink-roundtrip carries found=1 # rather than a bare rc: the GET between the CREATE and the DELETE actually # retrieves the entry, so the case asserts the conntrack existed and not # merely that three messages were acknowledged. # # nfct-open is where the OTHER transport stops being exercisable on this arm: # opening the libnfnetlink handle links, driving it does not (hwjs-cc#15, see # the header). So a green here means the handle opened, and there is no case # after it because the code that would follow cannot be linked at all. # # ctnetlink-dump-entries is pinned at 0 because this image cannot track a # connection. If it ever reads non-zero, that is a finding about the kernel # having grown conntrack rather than a regression of this port, and the five # cases around it say which. want_s S01 netfilter-socket ok want_s S02 netfilter-bind ok want_s S03 ctnetlink-dump ok want_s S04 ctnetlink-dump-entries 0 want_s S05 nfct-open ok want_s S06 ctnetlink-roundtrip 'create=0 get=0 found=1 destroy=0' rm -f "$pout"