[Bug 296838] pf: cannot reload ruleset in securelevel 2
[email protected] Tue, 21 Jul 2026 13:10:01 +0000
| Newsgroups | gmane.os.freebsd.devel.pf4freebsd |
|---|---|
| Message-ID | <[email protected]/bugzilla/> |
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D296838 --- Comment #8 from Christian Ullrich <[email protected]> --- (In reply to crest from comment #5) > - securelevel_ge(nlp_get_cred(nlp), cmd->cmd_securelevel)) { > + securelevel_gt(nlp_get_cred(nlp), cmd->cmd_securelevel)) { > NLP_LOG(LOG_DEBUG, nlp, "family %s: cmd %d securelevel_gt() failed", Note that the error message even now mentions the presumably correct functi= on. I think we can be confident that _gt() really was meant. However, ISTM there is a major bug in the entire idea. Commit 47c12f20bf58b introduced the feature of associating a securelevel with a netlink command = (as well as the check above), and this new field is documented like this: --- a/sys/netlink/netlink_ctl.h +++ b/sys/netlink/netlink_ctl.h @@ -89,6 +89,9 @@ struct genl_cmd { uint32_t cmd_flags; uint32_t cmd_priv; uint32_t cmd_num; + + /* Disallow this call from this level up (inclusive). */ + uint32_t cmd_securelevel; }; This requires comparing _ge(). However, the same commit then sets that leve= l to either 2 or 3 for every single netlink command pf has. Before the change it was possible to get the ruleset, state table, table contents, etc. even in securelevel 3; now _every_ interaction with pf is forbidden at that level. The levels added to the netlink commands are clear= ly meant as "allowed up to here" rather than "forbidden from here on". I think the author first decided to introduce genl_cmd.cmd_securelevel as t= he comment says (forbidden at this level and above), then became confused when updating the pf netlink calls: Most of the status queries would either have= to be not forbidden at all, or starting _above_ securelevel 3, and I find it really difficult to think of "securelevel 4" when you are used to the featu= re. They were also persuaded to special-case cmd_securelevel=3D=3D0 in D56390. = This was necessary to prevent a difference in behavior between securelevel -1 and 0.= The code does "if securelevel > 0 then compare _ge()" to maintain the "cmd_securelevel is the lowest prohibited level but 0 means always allowed" logic. It would have been better, IMHO, to define cmd_securelevel as the highest permitted level and always compare _gt(), as suggested here and apparently intended at least for pf (based on the error message) but contradicted by t= he comment to the new cmd_securelevel field. It would also make much more sens= e in the context of the genl_cmd struct itself because, seeing a restriction def= ined for a command, I'd expect it to describe when that command works, not when = it doesn't. Cleaning this up, when it may be used elsewhere, is going to be so much fun= for whoever gets to do it. --=20 You are receiving this mail because: You are the assignee for the bug.=