git: e145afc9eb91 - main - netlink: factor out compatibility code from inlined function
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 20 May 2026 14:29:39 UTC
The branch main has been updated by glebius:
URL: https://cgit.FreeBSD.org/src/commit/?id=e145afc9eb9187be7e7cfdd42043cefc07b6e8d6
commit e145afc9eb9187be7e7cfdd42043cefc07b6e8d6
Author: Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2026-05-20 14:27:39 +0000
Commit: Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2026-05-20 14:27:39 +0000
netlink: factor out compatibility code from inlined function
This is a refactor of 228c632ab3f62. First, move compatibility one level
up, where we yet work with the full header. Second, move this rarely
executed code outside of the inline function. Should be no functional
change.
Reviewed by: pouria, melifaro
Differential Revision: https://reviews.freebsd.org/D56915
---
sys/netlink/netlink_message_parser.c | 32 ++++++++++++++++++++++++++++++++
sys/netlink/netlink_message_parser.h | 29 ++++++++---------------------
2 files changed, 40 insertions(+), 21 deletions(-)
diff --git a/sys/netlink/netlink_message_parser.c b/sys/netlink/netlink_message_parser.c
index 4c41235efaac..54aceb660a98 100644
--- a/sys/netlink/netlink_message_parser.c
+++ b/sys/netlink/netlink_message_parser.c
@@ -50,6 +50,38 @@
#include <netlink/netlink_debug.h>
_DECLARE_DEBUG(LOG_INFO);
+/*
+ * Some applications try to provide only the non-zero part of the required
+ * message header instead of a full one. It happens when fetching routes or
+ * interface addresses, where the first header byte is the family.
+ * This behavior is "illegal" under the "strict" Netlink socket option, however
+ * there are many applications out there doing things in the "old" way.
+ * Support this usecase by copying the provided bytes into the temporary
+ * zero-filled header and running the parser on this header instead.
+ */
+struct nlmsghdr *
+nl_alloc_compat_hdr(struct nlmsghdr *hdr, uint32_t len, struct nl_pstate *npt)
+{
+ struct nlmsghdr *tmp;
+
+ MPASS(hdr->nlmsg_len < sizeof(struct nlmsghdr) + len);
+
+ len += sizeof(struct nlmsghdr);
+ if (npt->strict) {
+ nlmsg_report_err_msg(npt,
+ "header too short: expected %d, got %d",
+ len, hdr->nlmsg_len);
+ return (NULL);
+ }
+ tmp = npt_alloc(npt, len);
+ if (tmp == NULL)
+ return (NULL);
+ memcpy(tmp, hdr, hdr->nlmsg_len);
+ tmp->nlmsg_len = len;
+
+ return (tmp);
+}
+
bool
nlmsg_report_err_msg(struct nl_pstate *npt, const char *fmt, ...)
{
diff --git a/sys/netlink/netlink_message_parser.h b/sys/netlink/netlink_message_parser.h
index 720317ed74f3..8f61091c4a7f 100644
--- a/sys/netlink/netlink_message_parser.h
+++ b/sys/netlink/netlink_message_parser.h
@@ -222,6 +222,9 @@ bool nlmsg_report_err_offset(struct nl_pstate *npt, uint32_t off);
void nlmsg_report_cookie(struct nl_pstate *npt, struct nlattr *nla);
void nlmsg_report_cookie_u32(struct nl_pstate *npt, uint32_t val);
+struct nlmsghdr *nl_alloc_compat_hdr(struct nlmsghdr *hdr, uint32_t len,
+ struct nl_pstate *npt);
+
/*
* Have it inline so compiler can optimize field accesses into
* the list of direct function calls without iteration.
@@ -232,27 +235,7 @@ nl_parse_header(void *hdr, uint32_t len, const struct nlhdr_parser *parser,
{
int error;
- if (__predict_false(len < parser->nl_hdr_off)) {
- void *tmp_hdr;
-
- if (npt->strict) {
- nlmsg_report_err_msg(npt,
- "header too short: expected %d, got %d",
- parser->nl_hdr_off, len);
- return (EINVAL);
- }
-
- /*
- * Compatibility with older applications:
- * pretend there's a full header.
- */
- tmp_hdr = npt_alloc(npt, parser->nl_hdr_off);
- if (tmp_hdr == NULL)
- return (EINVAL);
- memcpy(tmp_hdr, hdr, len);
- hdr = tmp_hdr;
- len = parser->nl_hdr_off;
- }
+ MPASS(len >= parser->nl_hdr_off);
if (npt->strict && parser->sp != NULL && !parser->sp(hdr, npt))
return (EINVAL);
@@ -320,6 +303,10 @@ static inline int
nl_parse_nlmsg(struct nlmsghdr *hdr, const struct nlhdr_parser *parser,
struct nl_pstate *npt, void *target)
{
+ if (__predict_false(hdr->nlmsg_len - sizeof(struct nlmsghdr) <
+ parser->nl_hdr_off) &&
+ ((hdr = nl_alloc_compat_hdr(hdr, parser->nl_hdr_off, npt)) == NULL))
+ return (ENOMEM);
return (nl_parse_header(hdr + 1, hdr->nlmsg_len - sizeof(*hdr), parser,
npt, target));
}