git: a800622765b1 - stable/13 - netlink: fix interface dump.

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Mon, 20 Feb 2023 14:34:07 UTC
The branch stable/13 has been updated by melifaro:

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

commit a800622765b1e7cab79fa857e9a671e6c0c1ccec
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-02-16 13:17:58 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-02-20 14:33:28 +0000

    netlink: fix interface dump.
    
    The current code missed interface addition when reallocating
     temporary buffer.
    Tweak the code to perform the reallocation first and add
     interface afterwards unconditionally.
    
    Reported by:    Marek Zarychta <zarychtam@plan-b.pwste.edu.pl>
    MFC after:      3 days
    
    (cherry picked from commit 86fd0bdba540132ae298457e160b651f61d1db6b)
---
 sys/netlink/route/iface.c            | 33 ++++++++++---------
 tests/sys/netlink/test_rtnl_iface.py | 61 ++++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+), 17 deletions(-)

diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c
index 9cd7e6e80f3c..0eafacff4775 100644
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -449,24 +449,23 @@ rtnl_handle_getlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n
         CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 		wa.count++;
 		if (match_iface(&attrs, ifp)) {
-			if (offset < base_count) {
-				if (!if_try_ref(ifp))
-					continue;
-				match_array[offset++] = ifp;
-				continue;
-			}
-			/* Too many matches, need to reallocate */
-			struct ifnet **new_array;
-			int sz = base_count * sizeof(void *);
-			base_count *= 2;
-			new_array = malloc(sz * 2, M_TEMP, M_NOWAIT);
-			if (new_array == NULL) {
-				error = ENOMEM;
-				break;
+			if (offset >= base_count) {
+				/* Too many matches, need to reallocate */
+				struct ifnet **new_array;
+				int sz = base_count * sizeof(void *);
+				base_count *= 2;
+				new_array = malloc(sz * 2, M_TEMP, M_NOWAIT);
+				if (new_array == NULL) {
+					error = ENOMEM;
+					break;
+				}
+				memcpy(new_array, match_array, sz);
+				free(match_array, M_TEMP);
+				match_array = new_array;
 			}
-			memcpy(new_array, match_array, sz);
-			free(match_array, M_TEMP);
-			match_array = new_array;
+
+			if (if_try_ref(ifp))
+				match_array[offset++] = ifp;
                 }
         }
 	NET_EPOCH_EXIT(et);
diff --git a/tests/sys/netlink/test_rtnl_iface.py b/tests/sys/netlink/test_rtnl_iface.py
index ecc932c6213e..cbe5aed8b8ae 100644
--- a/tests/sys/netlink/test_rtnl_iface.py
+++ b/tests/sys/netlink/test_rtnl_iface.py
@@ -242,6 +242,67 @@ class TestRtNlIface(NetlinkTestTemplate, SingleVnetTestTemplate):
         assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
         assert rx_msg.error_code == errno.ENODEV
 
+    @pytest.mark.require_user("root")
+    def test_dump_ifaces_many(self):
+        """Tests if interface dummp is not missing interfaces"""
+
+        ifmap = {}
+        for ifname in (self.vnet.iface_alias_map["if1"].name, "lo0"):
+            ifindex = socket.if_nametoindex(ifname)
+            ifmap[ifindex] = ifname
+
+        for i in range(40):
+            ifname = "lo{}".format(i + 1)
+            flags = NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+            msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+            msg.nl_hdr.nlmsg_flags = (
+                flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+            )
+            msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, ifname))
+            msg.add_nla(
+                NlAttrNested(
+                    IflattrType.IFLA_LINKINFO,
+                    [
+                        NlAttrStrn(IflinkInfo.IFLA_INFO_KIND, "lo"),
+                    ],
+                )
+            )
+
+            rx_msg = self.get_reply(msg)
+            assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+            nla_list, _ = rx_msg.parse_attrs(bytes(rx_msg.cookie)[4:], rtnl_ifla_attrs)
+            nla_map = {n.nla_type: n for n in nla_list}
+            assert nla_map[IflattrType.IFLA_IFNAME.value].text == ifname
+            ifindex = nla_map[IflattrType.IFLA_NEW_IFINDEX.value].u32
+            assert ifindex > 0
+            assert ifindex not in ifmap
+            ifmap[ifindex] = ifname
+
+            # Dump all interfaces and check if the output matches ifmap
+            kernel_ifmap = {}
+            msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_GETLINK.value)
+            msg.nl_hdr.nlmsg_flags = (
+                NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+            )
+            self.write_message(msg)
+            while True:
+                rx_msg = self.read_message()
+                if msg.nl_hdr.nlmsg_seq != rx_msg.nl_hdr.nlmsg_seq:
+                    raise ValueError(
+                        "unexpected seq {}".format(rx_msg.nl_hdr.nlmsg_seq)
+                    )
+                if rx_msg.is_type(NlMsgType.NLMSG_ERROR):
+                    raise ValueError("unexpected message {}".format(rx_msg))
+                if rx_msg.is_type(NlMsgType.NLMSG_DONE):
+                    break
+                if not rx_msg.is_type(NlRtMsgType.RTM_NEWLINK):
+                    raise ValueError("unexpected message {}".format(rx_msg))
+
+                ifindex = rx_msg.base_hdr.ifi_index
+                assert ifindex == rx_msg.base_hdr.ifi_index
+                kernel_ifmap[ifindex] = rx_msg.get_nla(IflattrType.IFLA_IFNAME).text
+            assert kernel_ifmap == ifmap
+
     #
     # *
     # * {len=76, type=RTM_NEWLINK, flags=NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, seq=1662892737, pid=0},