git: 92d8e0f13657 - stable/13 - netlink: fix compatibility with older netlink applications.

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Mon, 23 Jan 2023 22:12:23 UTC
The branch stable/13 has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=92d8e0f136576d8a57e374fa4e460fc8903b68b6

commit 92d8e0f136576d8a57e374fa4e460fc8903b68b6
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-01-16 12:14:35 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-01-23 22:09:05 +0000

    netlink: fix compatibility with older netlink applications.
    
    Some apps try to provide only the non-zero part of the required message
     header instead of the 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.
    
    Reported by:    Goran Mekić <meka@tilda.center>
    
    (cherry picked from commit 228c632ab3f6245df4a08d8692d49c8e12aacc27)
---
 sys/netlink/netlink_message_parser.h  | 16 +++++++++++++---
 tests/sys/netlink/test_rtnl_ifaddr.py | 20 ++++++++++++++++++++
 2 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/sys/netlink/netlink_message_parser.h b/sys/netlink/netlink_message_parser.h
index 96fd1c7337b7..3f64c1967f09 100644
--- a/sys/netlink/netlink_message_parser.h
+++ b/sys/netlink/netlink_message_parser.h
@@ -213,9 +213,19 @@ nl_parse_header(void *hdr, int len, const struct nlhdr_parser *parser,
 	int error;
 
 	if (__predict_false(len < parser->nl_hdr_off)) {
-		nlmsg_report_err_msg(npt, "header too short: expected %d, got %d",
-		    parser->nl_hdr_off, len);
-		return (EINVAL);
+		if (npt->strict) {
+			nlmsg_report_err_msg(npt, "header too short: expected %d, got %d",
+			    parser->nl_hdr_off, len);
+			return (EINVAL);
+		}
+
+		/* Compat with older applications: pretend there's a full header */
+		void *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;
 	}
 
 	if (npt->strict && parser->sp != NULL && !parser->sp(hdr, npt))
diff --git a/tests/sys/netlink/test_rtnl_ifaddr.py b/tests/sys/netlink/test_rtnl_ifaddr.py
index bd1c55e268e7..ec349fcd6fde 100644
--- a/tests/sys/netlink/test_rtnl_ifaddr.py
+++ b/tests/sys/netlink/test_rtnl_ifaddr.py
@@ -8,6 +8,7 @@ from atf_python.sys.net.netlink import NetlinkTestTemplate
 from atf_python.sys.net.netlink import NlConst
 from atf_python.sys.net.netlink import NlHelper
 from atf_python.sys.net.netlink import NlmBaseFlags
+from atf_python.sys.net.netlink import Nlmsghdr
 from atf_python.sys.net.netlink import NlMsgType
 from atf_python.sys.net.netlink import NlRtMsgType
 from atf_python.sys.net.netlink import Nlsock
@@ -68,6 +69,25 @@ class TestRtNlIfaddr(NetlinkTestTemplate, SingleVnetTestTemplate):
         assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET6]) == 2
         assert len(ret) == 3
 
+    def test_46_filter_family_compat(self):
+        """Tests that family filtering works with the stripped header"""
+
+        hdr = Nlmsghdr(
+                nlmsg_len=17,
+                nlmsg_type=NlRtMsgType.RTM_GETADDR.value,
+                nlmsg_flags=NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value,
+                nlmsg_seq=self.helper.get_seq()
+                )
+        data = bytes(hdr) + struct.pack("@B", socket.AF_INET)
+        self.nlsock.write_data(data)
+
+        ret = []
+        for rx_msg in self.read_msg_list(hdr.nlmsg_seq, NlRtMsgType.RTM_NEWADDR):
+            ifname = socket.if_indextoname(rx_msg.base_hdr.ifa_index)
+            family = rx_msg.base_hdr.ifa_family
+            ret.append((ifname, family, rx_msg))
+        assert len(ret) == 2
+
     def filter_iface_family(self, family, num_items):
         """Tests that listing outputs IPv4 for the specific interface"""
         epair_ifname = self.vnet.iface_alias_map["if1"].name