From c3fa5405b05d6daa02f9e28e9eadd308e4de954b Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Fri, 18 Sep 2026 11:05:06 -0300 Subject: [PATCH] test(blackboxtest): read the boot file from the BOOTP header only bootfile returned option 67 when the reply carried one and the file header otherwise. xCAT names the loader in the header, and the firmware it serves does not read option 67 (dhcp-wire.md). A backend that moved the loader to option 67 and left the header empty passed every loader assertion. bootfile now reads the header only, and each "no boot file" check also asserts that option 67 is absent. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-test/blackboxtest/README.md | 15 ++++++++------- .../blackboxtest/conf/dhcp/loader-absent.conf | 1 + xCAT-test/blackboxtest/conf/dhcp/localboot.conf | 3 +++ .../blackboxtest/conf/dhcp/netboot-methods.conf | 1 + .../blackboxtest/conf/dhcp/pxe-arch-matrix.conf | 2 ++ .../blackboxtest/src/blackboxtest_lib/dhcpopts.py | 8 ++++---- xCAT-test/blackboxtest/tests/test_assertions.py | 9 ++++++--- 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/xCAT-test/blackboxtest/README.md b/xCAT-test/blackboxtest/README.md index 09da69ece..2756a2c8d 100644 --- a/xCAT-test/blackboxtest/README.md +++ b/xCAT-test/blackboxtest/README.md @@ -185,16 +185,17 @@ a flag letter (`S` `O` `E` `N`); `E` sends the name in wire format. Reply fields: the BOOTP header (`msgtype` `xid` `yiaddr` `siaddr` `ciaddr` `giaddr` `chaddr` `file` `sname` `secs` `flags` `src_ip` `src_mac`), the aliases `address` `mac` `next_server`, any option as `54`, `option:54`, -`opt54` or `server_id`, and three fields a client reads from two places: +`opt54` or `server_id`, and three derived fields: -- `bootfile`: option 67 when sent, else the `file` header. ISC fills the - header, dnsmasq answers in option 67, and firmware reads either. +- `bootfile`: the loader, from the BOOTP `file` header only. xCAT names the + loader there, and the firmware it serves does not read option 67. A "no boot + file" check also asserts `67 absent`. - `dns_name`: option 81 when sent, else option 12. Kea answers option 81 in option 81; ISC with `ignore client-updates` sends option 12 only. - `fqdn_flags`: option 81's flags as letters, absent without option 81. -Assert on those rather than on `file` or one option, unless the one place is -the point. `msgtype` is `BOOTREPLY` for a reply with no option 53. +Assert on `dns_name` rather than on option 12 or 81, unless one option is the +point. `msgtype` is `BOOTREPLY` for a reply with no option 53. The client MAC is a random locally administered address, so the host's own network stack never takes a reply as its own. The tool answers ARP only for an @@ -265,8 +266,8 @@ not ok 3 - pxe-bios-x86/bios-discover: bootfile == pxelinux.0 --- expected: 'pxelinux.0' received: 'xcat/xnba.kpxe' - reply: OFFER from 10.99.0.1 (9a:3d:4f:3d:59:1b) xid=0xc45c6376 yiaddr=10.99.0.175 siaddr=10.99.0.1 file='' - options: {1 (subnet_mask)=255.255.255.0, 54 (server_id)=10.99.0.1, 67 (bootfile_name)=xcat/xnba.kpxe} + reply: OFFER from 10.99.0.1 (9a:3d:4f:3d:59:1b) xid=0xc45c6376 yiaddr=10.99.0.175 siaddr=10.99.0.1 file='xcat/xnba.kpxe' + options: {1 (subnet_mask)=255.255.255.0, 54 (server_id)=10.99.0.1} sent: DISCOVER mac=02:da:16:d0:80:dd xid=0xc45c6376 client_arch=0x0000 attempt: 1 of 3 ... diff --git a/xCAT-test/blackboxtest/conf/dhcp/loader-absent.conf b/xCAT-test/blackboxtest/conf/dhcp/loader-absent.conf index 030535cd2..d364c11db 100644 --- a/xCAT-test/blackboxtest/conf/dhcp/loader-absent.conf +++ b/xCAT-test/blackboxtest/conf/dhcp/loader-absent.conf @@ -32,6 +32,7 @@ assert = msgtype == OFFER yiaddr in %(pool)s bootfile absent + 67 absent [scenario present-loader-is-still-named] description = S-12: An architecture whose loader is still there is unaffected diff --git a/xCAT-test/blackboxtest/conf/dhcp/localboot.conf b/xCAT-test/blackboxtest/conf/dhcp/localboot.conf index fadf55403..33565b7ee 100644 --- a/xCAT-test/blackboxtest/conf/dhcp/localboot.conf +++ b/xCAT-test/blackboxtest/conf/dhcp/localboot.conf @@ -43,6 +43,7 @@ assert = yiaddr == %(booted_ip)s bootfile != %(booted_script)s bootfile absent + 67 absent [scenario booted-pxe-node-is-not-sent-a-loader] description = S-31: An installed node provisioned with netboot=pxe is not handed pxelinux either @@ -59,6 +60,7 @@ assert = msgtype == OFFER yiaddr == %(booted_pxe_ip)s bootfile absent + 67 absent [scenario booted-node-is-not-sent-a-script-on-the-second-stage] description = S-31: And is not sent one when the loader asks again as xNBA @@ -78,3 +80,4 @@ assert = yiaddr == %(booted_ip)s bootfile != %(booted_script)s bootfile absent + 67 absent diff --git a/xCAT-test/blackboxtest/conf/dhcp/netboot-methods.conf b/xCAT-test/blackboxtest/conf/dhcp/netboot-methods.conf index c889b0534..150f8e99c 100644 --- a/xCAT-test/blackboxtest/conf/dhcp/netboot-methods.conf +++ b/xCAT-test/blackboxtest/conf/dhcp/netboot-methods.conf @@ -190,6 +190,7 @@ assert = msgtype == OFFER option:209 == %(petitboot_conf)s bootfile absent + 67 absent [scenario noip-interface-is-denied] description = S-07: An interface marked *NOIP* is not answered at all diff --git a/xCAT-test/blackboxtest/conf/dhcp/pxe-arch-matrix.conf b/xCAT-test/blackboxtest/conf/dhcp/pxe-arch-matrix.conf index f4a9fdd95..107f2306e 100644 --- a/xCAT-test/blackboxtest/conf/dhcp/pxe-arch-matrix.conf +++ b/xCAT-test/blackboxtest/conf/dhcp/pxe-arch-matrix.conf @@ -174,6 +174,7 @@ assert = msgtype == OFFER option:209 == %(opal_conf)s bootfile absent + 67 absent [scenario s390x-qemu] description = S-16: A QEMU s390x client is given a conf-file and no loader @@ -185,6 +186,7 @@ assert = msgtype == OFFER option:209 == %(s390x_conf)s bootfile absent + 67 absent [scenario vendor-class-etherboot] description = S-18: A client that identifies itself by vendor class alone still boots diff --git a/xCAT-test/blackboxtest/src/blackboxtest_lib/dhcpopts.py b/xCAT-test/blackboxtest/src/blackboxtest_lib/dhcpopts.py index 9dfde3ed9..0875bfc80 100644 --- a/xCAT-test/blackboxtest/src/blackboxtest_lib/dhcpopts.py +++ b/xCAT-test/blackboxtest/src/blackboxtest_lib/dhcpopts.py @@ -439,12 +439,12 @@ class DhcpReply(Reply): self.raw_options = raw_options def bootfile(self): - """What a client boots: option 67 when sent, else the `file` header. + """The loader a node boots: the BOOTP `file` header. - ISC fills the header, dnsmasq answers in option 67, and firmware - reads whichever arrived. + The firmware xCAT serves reads the header. A loader named only in + option 67 does not boot it. """ - return self.options.get(67) or self.fields.get("file", "") + return self.fields.get("file", "") def dns_name(self): """The node's name: option 81 when sent, else option 12. diff --git a/xCAT-test/blackboxtest/tests/test_assertions.py b/xCAT-test/blackboxtest/tests/test_assertions.py index 9f0fbb726..246b5af6e 100644 --- a/xCAT-test/blackboxtest/tests/test_assertions.py +++ b/xCAT-test/blackboxtest/tests/test_assertions.py @@ -114,11 +114,14 @@ class DhcpFields(unittest.TestCase): for target in ("54", "option:54", "server_id", "opt54"): self.assertTrue(check("%s == 10.0.0.1" % target, reply).ok, target) - def test_the_boot_file_prefers_option_67(self): + def test_the_boot_file_is_the_header_and_never_option_67(self): + # xCAT names the loader in the BOOTP header; its firmware does not + # read option 67. reply = offer({67: "grub2.aarch64"}) + self.assertTrue(check("bootfile absent", reply).ok) reply.fields["file"] = "pxelinux.0" - self.assertTrue(check("bootfile == grub2.aarch64", reply).ok) - self.assertTrue(check("file == pxelinux.0", reply).ok) + self.assertTrue(check("bootfile == pxelinux.0", reply).ok) + self.assertTrue(check("67 == grub2.aarch64", reply).ok) def test_the_dns_name_prefers_option_81_and_falls_back_to_12(self): self.assertTrue(check("dns_name == n1", offer({12: "n1"})).ok)