. "$BST_LIB" # BST-TIMEOUT: 300 # vlan 1.9 (meta-networking recipes-connectivity/vlan) -- vconfig, the 802.1Q # VLAN interface tool. Test-plan entry 565. # # WHAT vconfig ACTUALLY IS, read off vconfig.c rather than off its man page. # One file of C, no library. main() validates argv, warns if it cannot open # /proc/net/vlan/config, opens socket(AF_INET, SOCK_STREAM, 0) purely as an # ioctl handle, and then issues exactly one ioctl -- SIOCSIFVLAN -- with a # struct vlan_ioctl_args whose `cmd` field selects add / rem / set_flag / # set_egress_map / set_ingress_map / set_name_type. There is no second code # path. So this port's whole runtime surface is: does this kernel answer # SIOCSIFVLAN, and what does the tool do with the answer. # # --------------------------------------------------------------------------- # THE TICKET'S CRITERION CANNOT BE MET ON THIS IMAGE, AND WHY IS THE FINDING. # It names "add a VLAN interface on top of the virtio NIC, confirm it appears # with the expected parent and id in `ip link`, pass traffic over it on # loopback, then remove it". Two independent platform gaps stand in front of # that, both read off the kernel this image is built with rather than guessed: # # * NO 802.1Q IN THE KERNEL AT ALL. arch/wasm32/configs/wasm32_defconfig # carries no CONFIG_VLAN_8021Q line and the distro's config overlay adds no # net options at all, so olddefconfig leaves it at its `n` default; the same # defconfig is `# CONFIG_MODULES is not set`, so there is no 8021q module to # load either. net/socket.c:1303 makes the consequence exact rather than # inferred: SIOCSIFVLAN presets err = -ENOPKG, calls request_module("8021q") # -- a no-op with no module loader -- and only overwrites err if # vlan_ioctl_hook is non-NULL, which only net/8021q/vlan.c can make it. # Every vconfig subcommand on this image therefore returns ENOPKG (65, # "Package not installed"), and that errno is what the cases below assert. # It is a precise fingerprint: ENOPKG comes from nowhere else in this path, # so a case matching it has identified the gap rather than merely failed. # * NO SECOND INTERFACE TO PUT A VLAN ON. The same defconfig block that turns # on CONFIG_NET/CONFIG_INET/CONFIG_UNIX says the net stack is LOOPBACK ONLY # -- no virtio NIC, no veth, no dummy. 88-dhcpcd.test measured the veth half # of that from the other side (`ip link add ... type veth` -> no such # driver). So the ticket's "virtio NIC" does not exist here, and lo is the # only parent a VLAN could be asked for. # # Both are scored as PRESENT-AND-BLOCKED REDS naming the gap, never skips. The # tool is installed and works; the platform withholds the subsystem it drives. # Surfacing that is what the ticket asked for, and a skip would delete exactly # the signal worth having. # # WHAT IS ASSERTED POSITIVELY, so the port is not scored only on refusals. Six # cases, all of them reachable BECAUSE vconfig validates before it ioctls: # # * the alternatives link -- the deb installs /sbin/vconfig.vlan and # update-alternatives makes /sbin/vconfig at rootfs assembly, so the name # users type is a rootfs-time artifact and is asserted rather than assumed; # * the argc guard, the 15-character if_name limit and the name-type table, # all of which sit ABOVE the socket() call in main() and therefore exercise # argv handling and stdio on this arm with the kernel gap out of the way; # * the unknown-command arm, which reaches the bottom of the dispatch switch # having opened the socket -- proof the handle itself is fine; # * vconfig's OWN diagnosis of the gap. Before any ioctl it tries to open # /proc/net/vlan/config and, failing, prints "Maybe you need to load the # 8021q module". That is the tool independently reporting this exact # finding in its own voice, and it is a green: correct behaviour, not a # defect. # # AND ONE INDEPENDENT CONTROL. `ip link add link lo name lo.20 type vlan id 20` # does not use SIOCSIFVLAN at all -- it asks rtnetlink for a link of kind # "vlan", which only 8021q registers. Two unrelated interfaces failing for the # same missing CONFIG is what turns "vconfig is broken" into "this kernel has # no VLAN support", and it costs one case. # # BRINTOS-WORKAROUND: brintos/linux#103 revisit -- every red below is the # absent CONFIG_VLAN_8021Q. When that issue closes, the add / rem / # set_name_type / link-visible / rtnetlink / proc cases flip green on their own # against a rebuilt kernel; that flip is the signal to retire the attributions # here, measured against a run rather than against the issue's state. require vconfig vlan/present VC=vconfig GAP="brintos/linux#103 (https://brintos.io/brintos/linux/issues/103): this kernel carries no CONFIG_VLAN_8021Q and is # CONFIG_MODULES is not set, so net/socket.c leaves SIOCSIFVLAN at its preset -ENOPKG" # r : run, set `out` (stdout+stderr) and `rc`. Nothing here can block # -- vconfig issues one ioctl and returns -- so no watchdog is needed, and # timeout(1) would bound nothing on this arm anyway (brintos/linux#21). r() { out=$("$@" 2>&1); rc=$?; } # has : true iff is a substring. has() { case "$1" in *"$2"*) return 0 ;; *) return 1 ;; esac; } # enopkg : true iff carries strerror(ENOPKG). The tool prints the # strerror string and not the number, so this is the whole of the fingerprint. enopkg() { has "$1" "Package not installed"; } # --- the artifact ------------------------------------------------------------- # What the package installs is /sbin/vconfig.vlan; the name in the header of # every man page and every howto is /sbin/vconfig, and that one is made by # update-alternatives at rootfs assembly. A port whose binary shipped and whose # alternatives link did not would leave every documented invocation failing, so # the link is asserted on its own rather than folded into the cases that use it. if [ -x /sbin/vconfig.vlan ] && [ -e /sbin/vconfig ]; then bst_ok vlan/alternatives-link elif [ -e /sbin/vconfig ]; then bst_notok vlan/alternatives-link "/sbin/vconfig exists but /sbin/vconfig.vlan -- the file the vlan deb actually installs -- does not, so the link is not this package's" else bst_notok vlan/alternatives-link "/sbin/vconfig is absent: the vlan deb installs /sbin/vconfig.vlan and update-alternatives did not make the link at rootfs assembly"; fi # --- everything above the socket() call --------------------------------------- # main() rejects argc outside 3..5 before it touches anything, printing the # count it got and then the usage table. This is the whole of the binary that is # reachable with no kernel subsystem at all, and separating it from the ENOPKG # cases below is the only way a reader can tell "the tool is fine, the kernel is # not" from "the tool never worked". r "$VC" if bst_execfail "$out"; then bst_unrun vlan/argc-guard "$out" elif [ "$rc" -eq 1 ] && has "$out" "Expecting argc to be 3-5" && has "$out" "Usage: add"; then bst_ok vlan/argc-guard else bst_notok vlan/argc-guard "vconfig with no arguments did not print its argc diagnostic and usage table -- want rc=1 -- rc=$rc out='$(_bst_1line "$out")'"; fi # if_name is copied into a 16-byte device1[] with strcpy, so main() length-checks # it first and exits 1. 16 characters is one over the limit. r "$VC" add abcdefghijklmnop 10 if bst_execfail "$out"; then bst_unrun vlan/reject-long-ifname "$out" elif [ "$rc" -eq 1 ] && has "$out" "15 characters or less" && ! enopkg "$out"; then bst_ok vlan/reject-long-ifname else bst_notok vlan/reject-long-ifname "a 16-character interface name was not rejected before the ioctl -- want rc=1 -- rc=$rc out='$(_bst_1line "$out")'"; fi # set_name_type's argument is matched against a four-entry table, and anything # else exits 1 with the usage table -- again above the socket. r "$VC" set_name_type NOT_A_NAME_TYPE if bst_execfail "$out"; then bst_unrun vlan/reject-bad-name-type "$out" elif [ "$rc" -eq 1 ] && has "$out" "Invalid name type" && ! enopkg "$out"; then bst_ok vlan/reject-bad-name-type else bst_notok vlan/reject-bad-name-type "an unknown name-type was not rejected before the ioctl -- want rc=1 -- rc=$rc out='$(_bst_1line "$out")'"; fi # An unrecognised subcommand falls through the whole dispatch switch and exits 5 # -- having already opened the AF_INET socket. So this case additionally proves # the ioctl handle itself is fine on this arm, which is what makes the ENOPKG # below attributable to the subsystem rather than to the socket. r "$VC" no-such-command lo 10 if bst_execfail "$out"; then bst_unrun vlan/unknown-cmd "$out" elif [ "$rc" -eq 5 ] && has "$out" "Unknown command" && ! enopkg "$out"; then bst_ok vlan/unknown-cmd else bst_notok vlan/unknown-cmd "an unknown subcommand did not reach the dispatch default -- want rc=5 -- rc=$rc out='$(_bst_1line "$out")'"; fi # vconfig's own diagnosis, printed before any ioctl on every invocation here: # it opens /proc/net/vlan/config, fails, and says so. A green -- the tool is # behaving correctly -- and simultaneously this suite's cheapest independent # statement of the finding, in the package's own words. r "$VC" no-such-command lo 10 if bst_execfail "$out"; then bst_unrun vlan/self-diagnosis "$out" elif has "$out" "you need to load the 8021q module"; then bst_ok vlan/self-diagnosis elif has "$out" "Could not open /proc/net/vlan/config"; then bst_ok vlan/self-diagnosis else bst_notok vlan/self-diagnosis "vconfig did not warn about /proc/net/vlan/config, which it opens on every run and which cannot exist without 8021q -- rc=$rc out='$(_bst_1line "$out")'"; fi # --- the criterion, on the only interface this image has ---------------------- # THE TICKET'S STEP 1, run for real. lo stands in for the virtio NIC the ticket # names because there is no other interface (see the header). Present-and- # blocked: what comes back is the kernel's ENOPKG, at vconfig's own exit 3. r "$VC" add lo 10 if bst_execfail "$out"; then bst_unrun vlan/add "$out" elif [ "$rc" -eq 0 ] && ! enopkg "$out"; then bst_ok vlan/add elif enopkg "$out"; then bst_notok vlan/add "the ticket's criterion, blocked in the KERNEL and not in the port: 'vconfig add lo 10' reached ioctl(SIOCSIFVLAN) and got ENOPKG at exit $rc (vconfig exits 3 there) -- $GAP -- out='$(_bst_1line "$out")'" else bst_notok vlan/add "'vconfig add lo 10' failed with something other than ENOPKG, which is NOT the known platform gap and wants reading -- rc=$rc out='$(_bst_1line "$out")'"; fi # THE TICKET'S STEP 2 -- the interface must appear in `ip link` with the # expected parent and id. It cannot, because step 1 created nothing. The case is # kept and scored rather than dropped, so the criterion is answered in full and # flips green by itself the day the kernel gains 8021q. if command -v ip >/dev/null 2>&1; then r ip -o link show lo.10 if [ "$rc" -eq 0 ] && has "$out" "lo.10"; then bst_ok vlan/link-visible else bst_notok vlan/link-visible "lo.10 is absent from 'ip link' because 'vconfig add' created nothing (see vlan/add) -- $GAP"; fi else bst_notok vlan/link-visible "iproute2 ip is not on this image, so the ticket's 'ip link' confirmation could not be attempted at all"; fi # THE TICKET'S STEP 4 -- remove it again. Same cause, vconfig's exit 4. r "$VC" rem lo.10 if bst_execfail "$out"; then bst_unrun vlan/rem "$out" elif [ "$rc" -eq 0 ] && ! enopkg "$out"; then bst_ok vlan/rem elif enopkg "$out"; then bst_notok vlan/rem "'vconfig rem lo.10' reached ioctl(SIOCSIFVLAN) and got ENOPKG at exit $rc (vconfig exits 4 there), the same gap that stopped the add -- $GAP -- out='$(_bst_1line "$out")'" else bst_notok vlan/rem "'vconfig rem lo.10' failed with something other than ENOPKG -- rc=$rc out='$(_bst_1line "$out")'"; fi # set_name_type takes a different `cmd` in the ioctl argument AND names no # interface at all -- if_request.device1 stays empty. So it isolates the refusal # from anything to do with lo being the wrong kind of parent: same ENOPKG, at # vconfig's exit 8. That is the point of spending a case on it. r "$VC" set_name_type DEV_PLUS_VID_NO_PAD if bst_execfail "$out"; then bst_unrun vlan/set-name-type "$out" elif [ "$rc" -eq 0 ] && ! enopkg "$out"; then bst_ok vlan/set-name-type elif enopkg "$out"; then bst_notok vlan/set-name-type "'vconfig set_name_type' names no interface and still gets ENOPKG at exit $rc (vconfig exits 8 there), so the refusal is the missing subsystem and not a property of the parent device -- $GAP -- out='$(_bst_1line "$out")'" else bst_notok vlan/set-name-type "'vconfig set_name_type' failed with something other than ENOPKG -- rc=$rc out='$(_bst_1line "$out")'"; fi # --- the independent control -------------------------------------------------- # iproute2 reaches the same subsystem over rtnetlink, not over SIOCSIFVLAN. If # THIS fails too, for want of a "vlan" link kind, the finding is the kernel's # and not vconfig's -- which is the whole reason this case is here and why it is # worth more than a second vconfig invocation would be. if command -v ip >/dev/null 2>&1; then r ip link add link lo name lo.20 type vlan id 20 if [ "$rc" -eq 0 ]; then ip link del lo.20 >/dev/null 2>&1 bst_notok vlan/rtnetlink-control "rtnetlink CREATED a vlan link while vconfig's ioctl reports ENOPKG -- the two disagree, so the attribution above is wrong and this wants reading" else bst_notok vlan/rtnetlink-control "the control agrees with vconfig: iproute2's rtnetlink path finds no 'vlan' link kind either, so no VLAN support is registered on any interface by any mechanism -- $GAP -- rc=$rc out='$(_bst_1line "$out")'"; fi else bst_notok vlan/rtnetlink-control "iproute2 ip is not on this image, so the independent control on the ENOPKG attribution could not be run"; fi # /proc/net/vlan is created by 8021q's proc registration and by nothing else, so # its absence is a third reading of the same fact -- taken passively, invoking # nothing, which is why it is worth having beside the ioctl and the rtnetlink # control. if [ -d /proc/net/vlan ]; then bst_notok vlan/proc-absent "/proc/net/vlan EXISTS while SIOCSIFVLAN returns ENOPKG -- 8021q registers both together, so this contradicts the attribution above and wants reading" else bst_notok vlan/proc-absent "/proc/net/vlan is absent, and only 8021q creates it -- the passive confirmation that no VLAN support is in this kernel -- $GAP"; fi