From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id A4A0E1FF14C for ; Fri, 15 May 2026 21:24:01 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 69F131637A; Fri, 15 May 2026 21:23:51 +0200 (CEST) From: Gabriel Goller To: pve-devel@lists.proxmox.com Subject: [PATCH frr v2 2/3] frr: backport the "Memory leak problems." (#21844) upstream PR Date: Fri, 15 May 2026 14:03:46 +0200 Message-ID: <20260515120351.395649-3-g.goller@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260515120351.395649-1-g.goller@proxmox.com> References: <20260515120351.395649-1-g.goller@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1778846629017 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.097 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LOTSOFHASH 0.25 Emails with lots of hash-like gibberish SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record X-MailFrom: g.goller@proxmox.com X-Mailman-Rule-Hits: max-size X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; news-moderation; no-subject; digests; suspicious-header Message-ID-Hash: GWQR2L5ASAAILZLHAR5NECLN2IFPQOTH X-Message-ID-Hash: GWQR2L5ASAAILZLHAR5NECLN2IFPQOTH X-Mailman-Approved-At: Fri, 15 May 2026 21:23:37 +0200 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: The RT2 route leaking PR looks to have some race-condition-related problems, namely a crash at bgp router shutdown and one case in which the rt2 route is not leaked (see the last comment at #20005 [1]). I was not able to reproduce both of these issues, but they seem to be fixed in the upstream CI with #21844 [2], which solves multiple memory-related issues in multiple dameons. In order to be on the safe side, pull these in. The backporting was a bit of a struggle, because frr-10.6.1::master now also contains the new BGP-LS address-family (again, after it was reverted out of 10.5 :)) which changes a lot of core bgp structures. [1]: https://github.com/FRRouting/frr/pull/20005 [2]: https://github.com/FRRouting/frr/pull/21844 Signed-off-by: Gabriel Goller --- debian/patches/series | 20 + ...nup-complaint-about-unneeded-destroy.patch | 77 ++ ...p-termination-cleanup-of-memory-leak.patch | 47 + .../0008-lib-ferr-memory-leaked.patch | 48 + ...fpm_listener-has-signal-handlers-now.patch | 55 ++ ...g-file-isisd.log-from-all-isis-tests.patch | 883 ++++++++++++++++++ ...-tests-Tell-fpm_listener-to-shutdown.patch | 52 ++ ...sd-Clean-up-and-memory-leaks-in-isis.patch | 324 +++++++ ...gpd-Cleanup-memory-leaks-on-shutdown.patch | 608 ++++++++++++ ...ipd-Cleanup-memory-leaks-on-shutdown.patch | 58 ++ ...-termination-cleanup-of-memory-leaks.patch | 126 +++ ...eanup-nhrpd-termination-memory-leaks.patch | 105 +++ ...rpd-memory-leaks-on-shutdown-cleanup.patch | 80 ++ .../0018-ospfd-memory-leaks-on-shutdown.patch | 38 + ...ry-leaks-in-shutdown-in-affinitymaps.patch | 32 + ...bra-cleanup-memory-leaks-on-shutdown.patch | 175 ++++ ...-ldpd-Fixup-memory-leaks-on-shutdown.patch | 207 ++++ .../0022-Cleanup-snmp-memory-leaks.patch | 121 +++ ...cleanup-of-leaked-memory-on-shutdown.patch | 41 + ...opotest-fail-if-a-memory-leak-is-det.patch | 126 +++ ...hash_clean_and_free-remove-hash_free.patch | 480 ++++++++++ 21 files changed, 3703 insertions(+) create mode 100644 debian/patches/upstream/0006-pim6d-cleanup-complaint-about-unneeded-destroy.patch create mode 100644 debian/patches/upstream/0007-lib-nexthop-group-termination-cleanup-of-memory-leak.patch create mode 100644 debian/patches/upstream/0008-lib-ferr-memory-leaked.patch create mode 100644 debian/patches/upstream/0009-zebra-fpm_listener-has-signal-handlers-now.patch create mode 100644 debian/patches/upstream/0010-tests-Remove-log-file-isisd.log-from-all-isis-tests.patch create mode 100644 debian/patches/upstream/0011-tests-Tell-fpm_listener-to-shutdown.patch create mode 100644 debian/patches/upstream/0012-isisd-Clean-up-and-memory-leaks-in-isis.patch create mode 100644 debian/patches/upstream/0013-bgpd-Cleanup-memory-leaks-on-shutdown.patch create mode 100644 debian/patches/upstream/0014-ripd-Cleanup-memory-leaks-on-shutdown.patch create mode 100644 debian/patches/upstream/0015-pbrd-termination-cleanup-of-memory-leaks.patch create mode 100644 debian/patches/upstream/0016-nhrpd-Cleanup-nhrpd-termination-memory-leaks.patch create mode 100644 debian/patches/upstream/0017-sharpd-memory-leaks-on-shutdown-cleanup.patch create mode 100644 debian/patches/upstream/0018-ospfd-memory-leaks-on-shutdown.patch create mode 100644 debian/patches/upstream/0019-lib-Cleanup-memory-leaks-in-shutdown-in-affinitymaps.patch create mode 100644 debian/patches/upstream/0020-zebra-cleanup-memory-leaks-on-shutdown.patch create mode 100644 debian/patches/upstream/0021-ldpd-Fixup-memory-leaks-on-shutdown.patch create mode 100644 debian/patches/upstream/0022-Cleanup-snmp-memory-leaks.patch create mode 100644 debian/patches/upstream/0023-pimd-cleanup-of-leaked-memory-on-shutdown.patch create mode 100644 debian/patches/upstream/0024-tests-make-the-topotest-fail-if-a-memory-leak-is-det.patch create mode 100644 debian/patches/upstream/0025-Use-hash_clean_and_free-remove-hash_free.patch diff --git a/debian/patches/series b/debian/patches/series index 89587e528948..a6af3bcdc3c4 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -3,6 +3,26 @@ upstream/0002-bgpd-export-local-rt2-mac-ip-entries-to-unicast.patch upstream/0003-bgpd-do-not-add-local-vtep-as-remote.patch upstream/0004-topotests-add-bgp_evpn_rt2_local_leak.patch upstream/0005-bgpd-fix-valgrind-memory-leaks-on-daemon-shutdown.patch +upstream/0006-pim6d-cleanup-complaint-about-unneeded-destroy.patch +upstream/0007-lib-nexthop-group-termination-cleanup-of-memory-leak.patch +upstream/0008-lib-ferr-memory-leaked.patch +upstream/0009-zebra-fpm_listener-has-signal-handlers-now.patch +upstream/0010-tests-Remove-log-file-isisd.log-from-all-isis-tests.patch +upstream/0011-tests-Tell-fpm_listener-to-shutdown.patch +upstream/0012-isisd-Clean-up-and-memory-leaks-in-isis.patch +upstream/0013-bgpd-Cleanup-memory-leaks-on-shutdown.patch +upstream/0014-ripd-Cleanup-memory-leaks-on-shutdown.patch +upstream/0015-pbrd-termination-cleanup-of-memory-leaks.patch +upstream/0016-nhrpd-Cleanup-nhrpd-termination-memory-leaks.patch +upstream/0017-sharpd-memory-leaks-on-shutdown-cleanup.patch +upstream/0018-ospfd-memory-leaks-on-shutdown.patch +upstream/0019-lib-Cleanup-memory-leaks-in-shutdown-in-affinitymaps.patch +upstream/0020-zebra-cleanup-memory-leaks-on-shutdown.patch +upstream/0021-ldpd-Fixup-memory-leaks-on-shutdown.patch +upstream/0022-Cleanup-snmp-memory-leaks.patch +upstream/0023-pimd-cleanup-of-leaked-memory-on-shutdown.patch +upstream/0024-tests-make-the-topotest-fail-if-a-memory-leak-is-det.patch +upstream/0025-Use-hash_clean_and_free-remove-hash_free.patch pve/0001-enable-bgp-bfd-daemons.patch pve/0002-bgpd-add-an-option-for-RT-auto-derivation-to-force-A.patch pve/0003-tests-add-bgp-evpn-autort-test.patch diff --git a/debian/patches/upstream/0006-pim6d-cleanup-complaint-about-unneeded-destroy.patch b/debian/patches/upstream/0006-pim6d-cleanup-complaint-about-unneeded-destroy.patch new file mode 100644 index 000000000000..a9415984caec --- /dev/null +++ b/debian/patches/upstream/0006-pim6d-cleanup-complaint-about-unneeded-destroy.patch @@ -0,0 +1,77 @@ +From b08277dc7820b3621e4ecf360f75acb8334c3e19 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 16:41:26 -0400 +Subject: [PATCH 02/21] pim6d: cleanup complaint about unneeded destroy + +Make the complaint about a unneeded destroy in nb code go away. + +Signed-off-by: Donald Sharp +--- + pimd/pim_nb.c | 6 ++++++ + pimd/pim_nb.h | 1 + + pimd/pim_nb_config.c | 20 ++++++++++++++++++++ + 3 files changed, 27 insertions(+) + +diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c +index 4770536d654f..d76c97623b6a 100644 +--- a/pimd/pim_nb.c ++++ b/pimd/pim_nb.c +@@ -370,6 +370,12 @@ const struct frr_yang_module_info frr_pim_info = { + } + }, + { ++ .xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/override-interval", ++ .cbs = { ++ .modify = lib_interface_pim_override_interval_modify, ++ } ++ }, ++ { + .xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/bfd", + .cbs = { + .create = lib_interface_pim_address_family_bfd_create, +diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h +index 287d5ff7cf0c..20e86af769f4 100644 +--- a/pimd/pim_nb.h ++++ b/pimd/pim_nb.h +@@ -124,6 +124,7 @@ int lib_interface_pim_address_family_nbr_plist_destroy(struct nb_cb_destroy_args + int lib_interface_pim_assert_interval_modify(struct nb_cb_modify_args *args); + int lib_interface_pim_assert_override_interval_modify(struct nb_cb_modify_args *args); + int lib_interface_pim_assert_override_interval_destroy(struct nb_cb_destroy_args *args); ++int lib_interface_pim_override_interval_modify(struct nb_cb_modify_args *args); + int lib_interface_pim_address_family_create(struct nb_cb_create_args *args); + int lib_interface_pim_address_family_destroy(struct nb_cb_destroy_args *args); + int lib_interface_pim_address_family_pim_enable_modify( +diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c +index 46417be87e29..ceeaa0802ad2 100644 +--- a/pimd/pim_nb_config.c ++++ b/pimd/pim_nb_config.c +@@ -2505,6 +2505,26 @@ int lib_interface_pim_assert_override_interval_destroy(struct nb_cb_destroy_args + return NB_OK; + } + ++int lib_interface_pim_override_interval_modify(struct nb_cb_modify_args *args) ++{ ++ struct interface *ifp; ++ struct pim_interface *pim_ifp; ++ ++ switch (args->event) { ++ case NB_EV_VALIDATE: ++ case NB_EV_ABORT: ++ case NB_EV_PREPARE: ++ break; ++ case NB_EV_APPLY: ++ ifp = nb_running_get_entry(args->dnode, NULL, true); ++ pim_ifp = ifp->info; ++ pim_ifp->pim_override_interval_msec = yang_dnode_get_uint16(args->dnode, NULL); ++ break; ++ } ++ ++ return NB_OK; ++} ++ + /* + * XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/bfd + */ +-- +2.47.3 + diff --git a/debian/patches/upstream/0007-lib-nexthop-group-termination-cleanup-of-memory-leak.patch b/debian/patches/upstream/0007-lib-nexthop-group-termination-cleanup-of-memory-leak.patch new file mode 100644 index 000000000000..8bd2b2edfb39 --- /dev/null +++ b/debian/patches/upstream/0007-lib-nexthop-group-termination-cleanup-of-memory-leak.patch @@ -0,0 +1,47 @@ +From e543bae815e264ee75bbe9dd7b53a6c9ff1965d5 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 14:11:23 -0400 +Subject: [PATCH 03/21] lib: nexthop-group termination cleanup of memory leaks + +Nexthop groups were leaking memory on shutdown. Add the +ability to cleanup memory leaks from a using daemon. + +Signed-off-by: Donald Sharp +--- + lib/nexthop_group.c | 10 ++++++++++ + lib/nexthop_group.h | 1 + + 2 files changed, 11 insertions(+) + +diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c +index 7ee12079aaa6..5c6a02ae1bcc 100644 +--- a/lib/nexthop_group.c ++++ b/lib/nexthop_group.c +@@ -1397,3 +1397,13 @@ void nexthop_group_init(void (*new)(const char *name), + if (delete) + nhg_hooks.delete = delete; + } ++ ++void nexthop_group_terminate(void) ++{ ++ struct nexthop_group_cmd *nhgc; ++ ++ while ((nhgc = RB_ROOT(nhgc_entry_head, &nhgc_entries))) ++ nhgc_delete(nhgc); ++ ++ memset(&nhg_hooks, 0, sizeof(nhg_hooks)); ++} +diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h +index e12e341be88d..f96e5780063f 100644 +--- a/lib/nexthop_group.h ++++ b/lib/nexthop_group.h +@@ -120,6 +120,7 @@ void nexthop_group_init( + void (*del_nexthop)(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop), + void (*destroy)(const char *name)); ++void nexthop_group_terminate(void); + + void nexthop_group_enable_vrf(struct vrf *vrf); + void nexthop_group_disable_vrf(struct vrf *vrf); +-- +2.47.3 + diff --git a/debian/patches/upstream/0008-lib-ferr-memory-leaked.patch b/debian/patches/upstream/0008-lib-ferr-memory-leaked.patch new file mode 100644 index 000000000000..ae03b2f54faf --- /dev/null +++ b/debian/patches/upstream/0008-lib-ferr-memory-leaked.patch @@ -0,0 +1,48 @@ +From 80b48927045505951641df6eeccd357b7b814611 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Sat, 2 May 2026 17:46:17 -0400 +Subject: [PATCH 04/21] lib: ferr memory leaked + +Usage of the frr pthread specific error messages were not being +cleaned up. Make it so. + +Signed-off-by: Donald Sharp +--- + lib/ferr.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/lib/ferr.c b/lib/ferr.c +index 33bcb075fa5f..5cfc011fac34 100644 +--- a/lib/ferr.c ++++ b/lib/ferr.c +@@ -179,9 +179,27 @@ void log_ref_init(void) + + void log_ref_fini(void) + { ++ struct ferr *error; ++ + frr_with_mutex (&refs_mtx) { + hash_clean_and_free(&refs, NULL); + } ++ ++ /* ++ * Worker threads that called any ferr_*() macro have their per-thread ++ * struct ferr freed by ferr_free() through pthread_key_create()'s ++ * destructor when they exit (frr_pthread_finish() joins them earlier ++ * in frr_fini()). The main thread, however, is *itself* the thread ++ * driving shutdown and does not exit through pthread teardown until ++ * after log_memstats() has already dumped active allocations. Free ++ * its slot explicitly here so the lazily-allocated struct ferr (~4512 ++ * bytes) is not reported as an "error information" leak. ++ */ ++ error = pthread_getspecific(errkey); ++ if (error) { ++ pthread_setspecific(errkey, NULL); ++ ferr_free(error); ++ } + } + + void log_ref_vty_init(void) +-- +2.47.3 + diff --git a/debian/patches/upstream/0009-zebra-fpm_listener-has-signal-handlers-now.patch b/debian/patches/upstream/0009-zebra-fpm_listener-has-signal-handlers-now.patch new file mode 100644 index 000000000000..b6be07f97893 --- /dev/null +++ b/debian/patches/upstream/0009-zebra-fpm_listener-has-signal-handlers-now.patch @@ -0,0 +1,55 @@ +From 9c445535ae1712e4bf0f153c06a92145c3309d70 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Sun, 3 May 2026 12:22:33 -0400 +Subject: [PATCH 05/21] zebra: fpm_listener has signal handlers now + +Add signal handlers for fpm_listener.c. This will allow +it to be shutdown cleanly, such that gcoverage can work. + +Signed-off-by: Donald Sharp +--- + zebra/fpm_listener.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/zebra/fpm_listener.c b/zebra/fpm_listener.c +index 491062001e7f..098421a9fb33 100644 +--- a/zebra/fpm_listener.c ++++ b/zebra/fpm_listener.c +@@ -1120,6 +1120,11 @@ static void fpm_serve(void) + } + } + ++static void sigterm_handler(int signum) ++{ ++ exit(0); ++} ++ + /* Signal handler for SIGUSR1 */ + static void sigusr1_handler(int signum) + { +@@ -1214,6 +1219,22 @@ int main(int argc, char **argv) + exit(1); + } + ++ memset(&sa, 0, sizeof(sa)); ++ sa.sa_handler = sigterm_handler; ++ sigemptyset(&sa.sa_mask); ++ if (sigaction(SIGTERM, &sa, NULL) < 0) { ++ fprintf(stderr, "Failed to set up SIGTERM handler: %s\n", strerror(errno)); ++ exit(1); ++ } ++ if (sigaction(SIGINT, &sa, NULL) < 0) { ++ fprintf(stderr, "Failed to set up SIGINT handler: %s\n", strerror(errno)); ++ exit(1); ++ } ++ if (sigaction(SIGHUP, &sa, NULL) < 0) { ++ fprintf(stderr, "Failed to set up SIGHUP handler: %s\n", strerror(errno)); ++ exit(1); ++ } ++ + while ((r = getopt(argc, argv, "rfdvo:z:")) != -1) { + switch (r) { + case 'r': +-- +2.47.3 + diff --git a/debian/patches/upstream/0010-tests-Remove-log-file-isisd.log-from-all-isis-tests.patch b/debian/patches/upstream/0010-tests-Remove-log-file-isisd.log-from-all-isis-tests.patch new file mode 100644 index 000000000000..e9ceee277599 --- /dev/null +++ b/debian/patches/upstream/0010-tests-Remove-log-file-isisd.log-from-all-isis-tests.patch @@ -0,0 +1,883 @@ +From 08f033dc1813d8ab237c8716d1520052fa23438a Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 12:52:10 -0400 +Subject: [PATCH 06/21] tests: Remove `log file isisd.log` from all isis tests + +This is not necessary, remove. + +Signed-off-by: Donald Sharp +--- + tests/topotests/all_protocol_startup/r1/isisd.conf | 1 - + tests/topotests/isis_lfa_topo1/rt1/isisd.conf | 1 - + tests/topotests/isis_lfa_topo1/rt2/isisd.conf | 1 - + tests/topotests/isis_lfa_topo1/rt3/isisd.conf | 1 - + tests/topotests/isis_lfa_topo1/rt4/isisd.conf | 1 - + tests/topotests/isis_lfa_topo1/rt5/isisd.conf | 1 - + tests/topotests/isis_lfa_topo1/rt6/isisd.conf | 1 - + tests/topotests/isis_lfa_topo1/rt7/isisd.conf | 1 - + tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf | 1 - + tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf | 1 - + tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf | 1 - + tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf | 1 - + tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf | 1 - + tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf | 1 - + tests/topotests/isis_rlfa_topo1/rt1/isisd.conf | 1 - + tests/topotests/isis_rlfa_topo1/rt2/isisd.conf | 1 - + tests/topotests/isis_rlfa_topo1/rt3/isisd.conf | 1 - + tests/topotests/isis_rlfa_topo1/rt4/isisd.conf | 1 - + tests/topotests/isis_rlfa_topo1/rt5/isisd.conf | 1 - + tests/topotests/isis_rlfa_topo1/rt6/isisd.conf | 1 - + tests/topotests/isis_rlfa_topo1/rt7/isisd.conf | 1 - + tests/topotests/isis_rlfa_topo1/rt8/isisd.conf | 1 - + tests/topotests/isis_snmp/r1/isisd.conf | 1 - + tests/topotests/isis_snmp/r2/isisd.conf | 1 - + tests/topotests/isis_snmp/r3/isisd.conf | 1 - + tests/topotests/isis_snmp/r4/isisd.conf | 1 - + tests/topotests/isis_snmp/r5/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf | 1 - + tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf | 1 - + tests/topotests/isis_sr_te_topo1/rt1/isisd.conf | 1 - + tests/topotests/isis_sr_te_topo1/rt2/isisd.conf | 1 - + tests/topotests/isis_sr_te_topo1/rt3/isisd.conf | 1 - + tests/topotests/isis_sr_te_topo1/rt4/isisd.conf | 1 - + tests/topotests/isis_sr_te_topo1/rt5/isisd.conf | 1 - + tests/topotests/isis_sr_te_topo1/rt6/isisd.conf | 1 - + tests/topotests/isis_sr_topo1/rt1/isisd.conf | 1 - + tests/topotests/isis_sr_topo1/rt2/isisd.conf | 1 - + tests/topotests/isis_sr_topo1/rt3/isisd.conf | 1 - + tests/topotests/isis_sr_topo1/rt4/isisd.conf | 1 - + tests/topotests/isis_sr_topo1/rt5/isisd.conf | 1 - + tests/topotests/isis_sr_topo1/rt6/isisd.conf | 1 - + tests/topotests/isis_srv6_topo1/rt1/isisd.conf | 1 - + tests/topotests/isis_srv6_topo1/rt2/isisd.conf | 1 - + tests/topotests/isis_srv6_topo1/rt3/isisd.conf | 1 - + tests/topotests/isis_srv6_topo1/rt4/isisd.conf | 1 - + tests/topotests/isis_srv6_topo1/rt5/isisd.conf | 1 - + tests/topotests/isis_srv6_topo1/rt6/isisd.conf | 1 - + tests/topotests/isis_tilfa_topo1/rt1/isisd.conf | 1 - + tests/topotests/isis_tilfa_topo1/rt2/isisd.conf | 1 - + tests/topotests/isis_tilfa_topo1/rt3/isisd.conf | 1 - + tests/topotests/isis_tilfa_topo1/rt4/isisd.conf | 1 - + tests/topotests/isis_tilfa_topo1/rt5/isisd.conf | 1 - + tests/topotests/isis_tilfa_topo1/rt6/isisd.conf | 1 - + tests/topotests/ldp_snmp/r1/isisd.conf | 1 - + tests/topotests/ldp_snmp/r2/isisd.conf | 1 - + tests/topotests/ldp_snmp/r3/isisd.conf | 1 - + tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf | 1 - + tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf | 1 - + tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf | 1 - + tests/topotests/srv6_sid_manager/rt1/isisd.conf | 1 - + tests/topotests/srv6_sid_manager/rt2/isisd.conf | 1 - + tests/topotests/srv6_sid_manager/rt3/isisd.conf | 1 - + tests/topotests/srv6_sid_manager/rt4/isisd.conf | 1 - + tests/topotests/srv6_sid_manager/rt5/isisd.conf | 1 - + tests/topotests/srv6_sid_manager/rt6/isisd.conf | 1 - + 76 files changed, 76 deletions(-) + +diff --git a/tests/topotests/all_protocol_startup/r1/isisd.conf b/tests/topotests/all_protocol_startup/r1/isisd.conf +index 8ceded894fc7..b94b6fac8d0a 100644 +--- a/tests/topotests/all_protocol_startup/r1/isisd.conf ++++ b/tests/topotests/all_protocol_startup/r1/isisd.conf +@@ -1,4 +1,3 @@ +-log file isisd.log + ! + ! debug isis events + ! +diff --git a/tests/topotests/isis_lfa_topo1/rt1/isisd.conf b/tests/topotests/isis_lfa_topo1/rt1/isisd.conf +index fc81df02cb8c..db580017386d 100644 +--- a/tests/topotests/isis_lfa_topo1/rt1/isisd.conf ++++ b/tests/topotests/isis_lfa_topo1/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lfa_topo1/rt2/isisd.conf b/tests/topotests/isis_lfa_topo1/rt2/isisd.conf +index 6981692add18..6a75d11bc5cb 100644 +--- a/tests/topotests/isis_lfa_topo1/rt2/isisd.conf ++++ b/tests/topotests/isis_lfa_topo1/rt2/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt2 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lfa_topo1/rt3/isisd.conf b/tests/topotests/isis_lfa_topo1/rt3/isisd.conf +index e3ddb0984b5f..175646390f29 100644 +--- a/tests/topotests/isis_lfa_topo1/rt3/isisd.conf ++++ b/tests/topotests/isis_lfa_topo1/rt3/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt3 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lfa_topo1/rt4/isisd.conf b/tests/topotests/isis_lfa_topo1/rt4/isisd.conf +index 4db5c8ed057d..cd1311340ed4 100644 +--- a/tests/topotests/isis_lfa_topo1/rt4/isisd.conf ++++ b/tests/topotests/isis_lfa_topo1/rt4/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt4 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lfa_topo1/rt5/isisd.conf b/tests/topotests/isis_lfa_topo1/rt5/isisd.conf +index 1206a4e51f82..c028cc0280e5 100644 +--- a/tests/topotests/isis_lfa_topo1/rt5/isisd.conf ++++ b/tests/topotests/isis_lfa_topo1/rt5/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt5 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lfa_topo1/rt6/isisd.conf b/tests/topotests/isis_lfa_topo1/rt6/isisd.conf +index 2ba9e4991960..6a40f52d6f4e 100644 +--- a/tests/topotests/isis_lfa_topo1/rt6/isisd.conf ++++ b/tests/topotests/isis_lfa_topo1/rt6/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lfa_topo1/rt7/isisd.conf b/tests/topotests/isis_lfa_topo1/rt7/isisd.conf +index 060be2bf6d08..421ed25b5154 100644 +--- a/tests/topotests/isis_lfa_topo1/rt7/isisd.conf ++++ b/tests/topotests/isis_lfa_topo1/rt7/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf +index 51bf7e60a5d5..777899812e32 100644 +--- a/tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf ++++ b/tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf +index 96c9f33dae4e..6bdb7612ca0b 100644 +--- a/tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf ++++ b/tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt2 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf +index c3354f2e68c3..14cb6eaabc1f 100644 +--- a/tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf ++++ b/tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt3 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf +index f1627e75473b..c1f5cdca820b 100644 +--- a/tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf ++++ b/tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt4 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf +index 41241590d14c..0c4b6b04ac80 100644 +--- a/tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf ++++ b/tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt5 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf +index b66f40e9ee64..553e94974351 100644 +--- a/tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf ++++ b/tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_rlfa_topo1/rt1/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt1/isisd.conf +index c96d0b137475..a7ec2c450005 100644 +--- a/tests/topotests/isis_rlfa_topo1/rt1/isisd.conf ++++ b/tests/topotests/isis_rlfa_topo1/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_rlfa_topo1/rt2/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt2/isisd.conf +index 27203375c150..baf2d46a3775 100644 +--- a/tests/topotests/isis_rlfa_topo1/rt2/isisd.conf ++++ b/tests/topotests/isis_rlfa_topo1/rt2/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt2 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_rlfa_topo1/rt3/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt3/isisd.conf +index 1139d9df41ad..4ee5fffa8696 100644 +--- a/tests/topotests/isis_rlfa_topo1/rt3/isisd.conf ++++ b/tests/topotests/isis_rlfa_topo1/rt3/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt3 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_rlfa_topo1/rt4/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt4/isisd.conf +index 980bd5d9f01a..af29a62b479c 100644 +--- a/tests/topotests/isis_rlfa_topo1/rt4/isisd.conf ++++ b/tests/topotests/isis_rlfa_topo1/rt4/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt4 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_rlfa_topo1/rt5/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt5/isisd.conf +index 82ba9cb56e51..5b7ce0c99a01 100644 +--- a/tests/topotests/isis_rlfa_topo1/rt5/isisd.conf ++++ b/tests/topotests/isis_rlfa_topo1/rt5/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt5 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_rlfa_topo1/rt6/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt6/isisd.conf +index ea859ffa3be8..c3c66374bcf2 100644 +--- a/tests/topotests/isis_rlfa_topo1/rt6/isisd.conf ++++ b/tests/topotests/isis_rlfa_topo1/rt6/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_rlfa_topo1/rt7/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt7/isisd.conf +index 5acfa95a1315..4f8ad87708fc 100644 +--- a/tests/topotests/isis_rlfa_topo1/rt7/isisd.conf ++++ b/tests/topotests/isis_rlfa_topo1/rt7/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt7 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_rlfa_topo1/rt8/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt8/isisd.conf +index 237784280a97..0e870a927dc0 100644 +--- a/tests/topotests/isis_rlfa_topo1/rt8/isisd.conf ++++ b/tests/topotests/isis_rlfa_topo1/rt8/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt8 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_snmp/r1/isisd.conf b/tests/topotests/isis_snmp/r1/isisd.conf +index 492834122585..559273e15602 100644 +--- a/tests/topotests/isis_snmp/r1/isisd.conf ++++ b/tests/topotests/isis_snmp/r1/isisd.conf +@@ -1,5 +1,4 @@ + hostname r1 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/isis_snmp/r2/isisd.conf b/tests/topotests/isis_snmp/r2/isisd.conf +index 9bb8a8d5f917..d735e5b85527 100644 +--- a/tests/topotests/isis_snmp/r2/isisd.conf ++++ b/tests/topotests/isis_snmp/r2/isisd.conf +@@ -1,5 +1,4 @@ + hostname r2 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/isis_snmp/r3/isisd.conf b/tests/topotests/isis_snmp/r3/isisd.conf +index 4daec791b3d6..4002a56abf9e 100644 +--- a/tests/topotests/isis_snmp/r3/isisd.conf ++++ b/tests/topotests/isis_snmp/r3/isisd.conf +@@ -1,5 +1,4 @@ + hostname r3 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/isis_snmp/r4/isisd.conf b/tests/topotests/isis_snmp/r4/isisd.conf +index 32b07b3cb4eb..004389a1d851 100644 +--- a/tests/topotests/isis_snmp/r4/isisd.conf ++++ b/tests/topotests/isis_snmp/r4/isisd.conf +@@ -1,5 +1,4 @@ + hostname r4 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/isis_snmp/r5/isisd.conf b/tests/topotests/isis_snmp/r5/isisd.conf +index fe3ca0f3aa20..abd4bb27819b 100644 +--- a/tests/topotests/isis_snmp/r5/isisd.conf ++++ b/tests/topotests/isis_snmp/r5/isisd.conf +@@ -1,5 +1,4 @@ + hostname r5 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf +index 090e89628f8e..9ea4e481ec90 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + !debug northbound + !debug isis events +diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf +index 3901ccaefe7e..9ee25c20043b 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt2 +-log file isisd.log + ! + !debug northbound + !debug isis events +diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf +index f7a52bc79d1d..21ef5519a17e 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt3 +-log file isisd.log + ! + !debug northbound + !debug isis events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf +index c102b1b22b79..e5391fd475b2 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt0 +-log file isisd.log + ! + !debug isis events + !debug isis spf-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf +index a7933888cc71..b638d4bed17d 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + !debug isis events + !debug isis spf-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf +index 312a6df2284a..0618bf135884 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt2 +-log file isisd.log + ! + !debug isis events + !debug isis route-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf +index c287e8a5da6c..3918a577efac 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt3 +-log file isisd.log + ! + !debug isis events + !debug isis route-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf +index c040ce200b21..0a3ee536652e 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt4 +-log file isisd.log + ! + !debug isis events + !debug isis spf-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf +index 6cf87d44fb5f..377369514201 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt5 +-log file isisd.log + ! + !debug isis events + !debug isis spf-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf +index 87a259696d9a..23121ec62001 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt6 +-log file isisd.log + ! + !debug isis events + !debug isis spf-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf +index 6645542db9b3..e10354debc24 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt7 +-log file isisd.log + ! + !debug isis events + !debug isis spf-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf +index 04e925e4820e..a6b7796a12c5 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt8 +-log file isisd.log + ! + !debug isis events + !debug isis spf-events +diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf +index dabb9986dffe..ce38b6ce52a8 100644 +--- a/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf ++++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt9 +-log file isisd.log + ! + !debug isis events + !debug isis spf-events +diff --git a/tests/topotests/isis_sr_te_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt1/isisd.conf +index 3a94af7e0d78..93b7e7a6afe4 100644 +--- a/tests/topotests/isis_sr_te_topo1/rt1/isisd.conf ++++ b/tests/topotests/isis_sr_te_topo1/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_te_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt2/isisd.conf +index 553e72cb2df2..b1539a8ee018 100644 +--- a/tests/topotests/isis_sr_te_topo1/rt2/isisd.conf ++++ b/tests/topotests/isis_sr_te_topo1/rt2/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt2 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_te_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt3/isisd.conf +index 8d89612b5cb4..f6295b76cdca 100644 +--- a/tests/topotests/isis_sr_te_topo1/rt3/isisd.conf ++++ b/tests/topotests/isis_sr_te_topo1/rt3/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt3 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_te_topo1/rt4/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt4/isisd.conf +index e5f72a77133e..002e838a64e7 100644 +--- a/tests/topotests/isis_sr_te_topo1/rt4/isisd.conf ++++ b/tests/topotests/isis_sr_te_topo1/rt4/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt4 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_te_topo1/rt5/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt5/isisd.conf +index 1a66d18ee1f6..58a451ca60ab 100644 +--- a/tests/topotests/isis_sr_te_topo1/rt5/isisd.conf ++++ b/tests/topotests/isis_sr_te_topo1/rt5/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt5 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_te_topo1/rt6/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt6/isisd.conf +index bfa988305be5..a8083667dad3 100644 +--- a/tests/topotests/isis_sr_te_topo1/rt6/isisd.conf ++++ b/tests/topotests/isis_sr_te_topo1/rt6/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_topo1/rt1/isisd.conf +index ef8d4275c2a3..49262809c02f 100644 +--- a/tests/topotests/isis_sr_topo1/rt1/isisd.conf ++++ b/tests/topotests/isis_sr_topo1/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_topo1/rt2/isisd.conf +index e65ec81a3c52..d67edd6e32f7 100644 +--- a/tests/topotests/isis_sr_topo1/rt2/isisd.conf ++++ b/tests/topotests/isis_sr_topo1/rt2/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt2 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_topo1/rt3/isisd.conf +index e7b8d942ccca..36fbbd3a1352 100644 +--- a/tests/topotests/isis_sr_topo1/rt3/isisd.conf ++++ b/tests/topotests/isis_sr_topo1/rt3/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt3 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_topo1/rt4/isisd.conf b/tests/topotests/isis_sr_topo1/rt4/isisd.conf +index 92ea1ea3d9d3..6451c4a29b52 100644 +--- a/tests/topotests/isis_sr_topo1/rt4/isisd.conf ++++ b/tests/topotests/isis_sr_topo1/rt4/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt4 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_topo1/rt5/isisd.conf b/tests/topotests/isis_sr_topo1/rt5/isisd.conf +index de604d71f4c9..0db2bcef6eb8 100644 +--- a/tests/topotests/isis_sr_topo1/rt5/isisd.conf ++++ b/tests/topotests/isis_sr_topo1/rt5/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt5 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_sr_topo1/rt6/isisd.conf b/tests/topotests/isis_sr_topo1/rt6/isisd.conf +index b96a2c6e1e1c..af1d8ec4f333 100644 +--- a/tests/topotests/isis_sr_topo1/rt6/isisd.conf ++++ b/tests/topotests/isis_sr_topo1/rt6/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_srv6_topo1/rt1/isisd.conf b/tests/topotests/isis_srv6_topo1/rt1/isisd.conf +index 29e1a3117165..0f7af9633f2c 100644 +--- a/tests/topotests/isis_srv6_topo1/rt1/isisd.conf ++++ b/tests/topotests/isis_srv6_topo1/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_srv6_topo1/rt2/isisd.conf b/tests/topotests/isis_srv6_topo1/rt2/isisd.conf +index b095f049101f..a8fd364bf769 100644 +--- a/tests/topotests/isis_srv6_topo1/rt2/isisd.conf ++++ b/tests/topotests/isis_srv6_topo1/rt2/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt2 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_srv6_topo1/rt3/isisd.conf b/tests/topotests/isis_srv6_topo1/rt3/isisd.conf +index e237db2f4940..31c904fba7b9 100644 +--- a/tests/topotests/isis_srv6_topo1/rt3/isisd.conf ++++ b/tests/topotests/isis_srv6_topo1/rt3/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt3 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_srv6_topo1/rt4/isisd.conf b/tests/topotests/isis_srv6_topo1/rt4/isisd.conf +index b4c92146a1f6..0df3fe1b99b8 100644 +--- a/tests/topotests/isis_srv6_topo1/rt4/isisd.conf ++++ b/tests/topotests/isis_srv6_topo1/rt4/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt4 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_srv6_topo1/rt5/isisd.conf b/tests/topotests/isis_srv6_topo1/rt5/isisd.conf +index 26f895dd82ea..ffb4993cd523 100644 +--- a/tests/topotests/isis_srv6_topo1/rt5/isisd.conf ++++ b/tests/topotests/isis_srv6_topo1/rt5/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt5 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_srv6_topo1/rt6/isisd.conf b/tests/topotests/isis_srv6_topo1/rt6/isisd.conf +index f8816db43aac..99cec5bca0cf 100644 +--- a/tests/topotests/isis_srv6_topo1/rt6/isisd.conf ++++ b/tests/topotests/isis_srv6_topo1/rt6/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf +index 1218ed350f8e..c573a6a47699 100644 +--- a/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf ++++ b/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf +index aed69713e183..d14135dc24df 100644 +--- a/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf ++++ b/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt2 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf +index d2f01e459514..8b4e1c035d1a 100644 +--- a/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf ++++ b/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt3 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf +index c1117b5d1bd8..8bf7c842f902 100644 +--- a/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf ++++ b/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt4 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf +index 3678ff9074df..9366d56f440e 100644 +--- a/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf ++++ b/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt5 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf +index 9d3b464726d4..b139393fa2b0 100644 +--- a/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf ++++ b/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/ldp_snmp/r1/isisd.conf b/tests/topotests/ldp_snmp/r1/isisd.conf +index d1abb497a6ce..ee23d9a308d9 100644 +--- a/tests/topotests/ldp_snmp/r1/isisd.conf ++++ b/tests/topotests/ldp_snmp/r1/isisd.conf +@@ -1,5 +1,4 @@ + hostname r1 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/ldp_snmp/r2/isisd.conf b/tests/topotests/ldp_snmp/r2/isisd.conf +index 213b65ee4cc7..ec79aaa71d20 100644 +--- a/tests/topotests/ldp_snmp/r2/isisd.conf ++++ b/tests/topotests/ldp_snmp/r2/isisd.conf +@@ -1,5 +1,4 @@ + hostname r2 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/ldp_snmp/r3/isisd.conf b/tests/topotests/ldp_snmp/r3/isisd.conf +index 956d58239bf1..48ca62aab15c 100644 +--- a/tests/topotests/ldp_snmp/r3/isisd.conf ++++ b/tests/topotests/ldp_snmp/r3/isisd.conf +@@ -1,5 +1,4 @@ + hostname r3 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf b/tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf +index d1abb497a6ce..ee23d9a308d9 100644 +--- a/tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf ++++ b/tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf +@@ -1,5 +1,4 @@ + hostname r1 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf b/tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf +index 213b65ee4cc7..ec79aaa71d20 100644 +--- a/tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf ++++ b/tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf +@@ -1,5 +1,4 @@ + hostname r2 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf b/tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf +index 956d58239bf1..48ca62aab15c 100644 +--- a/tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf ++++ b/tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf +@@ -1,5 +1,4 @@ + hostname r3 +-log file isisd.log + ! debug isis adj-packets + ! debug isis events + ! debug isis update-packets +diff --git a/tests/topotests/srv6_sid_manager/rt1/isisd.conf b/tests/topotests/srv6_sid_manager/rt1/isisd.conf +index 29e1a3117165..0f7af9633f2c 100644 +--- a/tests/topotests/srv6_sid_manager/rt1/isisd.conf ++++ b/tests/topotests/srv6_sid_manager/rt1/isisd.conf +@@ -1,6 +1,5 @@ + password 1 + hostname rt1 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/srv6_sid_manager/rt2/isisd.conf b/tests/topotests/srv6_sid_manager/rt2/isisd.conf +index b095f049101f..a8fd364bf769 100644 +--- a/tests/topotests/srv6_sid_manager/rt2/isisd.conf ++++ b/tests/topotests/srv6_sid_manager/rt2/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt2 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/srv6_sid_manager/rt3/isisd.conf b/tests/topotests/srv6_sid_manager/rt3/isisd.conf +index e237db2f4940..31c904fba7b9 100644 +--- a/tests/topotests/srv6_sid_manager/rt3/isisd.conf ++++ b/tests/topotests/srv6_sid_manager/rt3/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt3 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/srv6_sid_manager/rt4/isisd.conf b/tests/topotests/srv6_sid_manager/rt4/isisd.conf +index b4c92146a1f6..0df3fe1b99b8 100644 +--- a/tests/topotests/srv6_sid_manager/rt4/isisd.conf ++++ b/tests/topotests/srv6_sid_manager/rt4/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt4 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/srv6_sid_manager/rt5/isisd.conf b/tests/topotests/srv6_sid_manager/rt5/isisd.conf +index 26f895dd82ea..ffb4993cd523 100644 +--- a/tests/topotests/srv6_sid_manager/rt5/isisd.conf ++++ b/tests/topotests/srv6_sid_manager/rt5/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt5 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +diff --git a/tests/topotests/srv6_sid_manager/rt6/isisd.conf b/tests/topotests/srv6_sid_manager/rt6/isisd.conf +index f8816db43aac..99cec5bca0cf 100644 +--- a/tests/topotests/srv6_sid_manager/rt6/isisd.conf ++++ b/tests/topotests/srv6_sid_manager/rt6/isisd.conf +@@ -1,5 +1,4 @@ + hostname rt6 +-log file isisd.log + ! + ! debug isis events + ! debug isis route-events +-- +2.47.3 + diff --git a/debian/patches/upstream/0011-tests-Tell-fpm_listener-to-shutdown.patch b/debian/patches/upstream/0011-tests-Tell-fpm_listener-to-shutdown.patch new file mode 100644 index 000000000000..d0a0b9acddca --- /dev/null +++ b/debian/patches/upstream/0011-tests-Tell-fpm_listener-to-shutdown.patch @@ -0,0 +1,52 @@ +From c6c1ceea720527b68350eefb854aebfbeb49f695 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Sun, 3 May 2026 12:23:50 -0400 +Subject: [PATCH 07/21] tests: Tell fpm_listener to shutdown + +Allow gcoverage to work with fpm_listener + +Currently there is no way to tell fpm_listener to shutdown, +so let's allow it to listen to signals and cleanly shutdown. + +Signed-off-by: Donald Sharp +--- + tests/topotests/lib/topotest.py | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py +index 52a85eef8536..1989b7c94b35 100644 +--- a/tests/topotests/lib/topotest.py ++++ b/tests/topotests/lib/topotest.py +@@ -1623,6 +1623,29 @@ class Router(Node): + return ret + + def stopRouter(self, assertOnError=True): ++ # fpm_listener writes its PID file to the gear log directory (next to ++ # its data dump), not to /var/run/frr/, so listDaemons() can't see it. ++ # Send it SIGTERM first and give it a brief moment to run its atexit ++ # handlers (in particular gcov flush under --enable-gcov) before the ++ # namespace teardown SIGKILLs it. This must happen before listDaemons() ++ # below sends SIGTERM to the FRR daemons because once zebra exits the ++ # FPM client side closes the socket and we want fpm_listener's last ++ # accept loop iteration to be reflected in coverage. ++ fpm_pidfile = "{}/{}/fpm_listener.pid".format(self.logdir, self.name) ++ try: ++ with open(fpm_pidfile) as f: ++ fpm_pid = int(f.read().strip()) ++ try: ++ os.kill(fpm_pid, signal.SIGTERM) ++ logger.debug( ++ "%s: sent SIGTERM to fpm_listener pid %d", self.name, fpm_pid ++ ) ++ except OSError: ++ pass ++ time.sleep(0.1) ++ except (FileNotFoundError, ValueError): ++ pass ++ + # Stop Running FRR Daemons + running = self.listDaemons() + if not running: +-- +2.47.3 + diff --git a/debian/patches/upstream/0012-isisd-Clean-up-and-memory-leaks-in-isis.patch b/debian/patches/upstream/0012-isisd-Clean-up-and-memory-leaks-in-isis.patch new file mode 100644 index 000000000000..fcdf28a2f08b --- /dev/null +++ b/debian/patches/upstream/0012-isisd-Clean-up-and-memory-leaks-in-isis.patch @@ -0,0 +1,324 @@ +From 27141f85fbd8d6e9bec4e74e86eac241ce7b4a4b Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 12:26:01 -0400 +Subject: [PATCH 08/21] isisd: Clean up and memory leaks in isis + +a) isisd is using a MTYPE_TMP in a bunch of different places, +add better descriptors and break it up a bit. + +b) Cleanup of sbuf usage such that memory is not leaked on shutdown. + +Signed-off-by: Donald Sharp +--- + isisd/isis_circuit.c | 2 +- + isisd/isis_main.c | 2 ++ + isisd/isis_misc.c | 13 +++++++---- + isisd/isis_nb_config.c | 13 +++++++---- + isisd/isis_tlvs.c | 50 +++++++++++++++++++++++++++++----------- + isisd/isis_tlvs.h | 1 + + isisd/isis_vty_fabricd.c | 6 +++-- + isisd/isisd.c | 1 + + isisd/isisd.h | 1 + + 9 files changed, 62 insertions(+), 27 deletions(-) + +diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c +index 6bcb7190c764..cc6a699981c0 100644 +--- a/isisd/isis_circuit.c ++++ b/isisd/isis_circuit.c +@@ -211,7 +211,7 @@ void isis_circuit_del(struct isis_circuit *circuit) + circuit->ext = NULL; + } + +- XFREE(MTYPE_TMP, circuit->bfd_config.profile); ++ XFREE(MTYPE_ISIS_BFD_PROFILE, circuit->bfd_config.profile); + XFREE(MTYPE_ISIS_CIRCUIT, circuit->tag); + + /* and lastly the circuit itself */ +diff --git a/isisd/isis_main.c b/isisd/isis_main.c +index 24b40de912e8..7acfb3b2327d 100644 +--- a/isisd/isis_main.c ++++ b/isisd/isis_main.c +@@ -46,6 +46,7 @@ + #include "isisd/isis_bfd.h" + #include "isisd/isis_lsp.h" + #include "isisd/isis_mt.h" ++#include "isisd/isis_tlvs.h" + #include "isisd/fabricd.h" + #include "isisd/isis_nb.h" + #include "isisd/isis_ldp_sync.h" +@@ -122,6 +123,7 @@ static FRR_NORETURN void terminate(int i) + isis_affinity_map_terminate(); + #endif + isis_master_terminate(); ++ isis_tlvs_terminate(); + + frr_fini(); + exit(i); +diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c +index 088b8016cc45..66b0b5cf91e5 100644 +--- a/isisd/isis_misc.c ++++ b/isisd/isis_misc.c +@@ -31,7 +31,10 @@ + #include "isisd/isis_adjacency.h" + #include "isisd/isis_dynhn.h" + +-/* staticly assigned vars for printing purposes */ ++DEFINE_MTYPE_STATIC(ISISD, ISIS_TMP_LOG_MULTILINE, "ISIS log multiline temporary"); ++DEFINE_MTYPE_STATIC(ISISD, ISIS_TMP_VTY_MULTILINE, "ISIS vty multiline temporary"); ++ ++/* statically assigned vars for printing purposes */ + static char sys_hostname[ISO_SYSID_STRLEN]; + struct in_addr new_prefix; + /* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */ +@@ -408,7 +411,7 @@ void log_multiline(int priority, const char *prefix, const char *format, ...) + char *p; + + va_start(ap, format); +- p = vasnprintfrr(MTYPE_TMP, shortbuf, sizeof(shortbuf), format, ap); ++ p = vasnprintfrr(MTYPE_ISIS_TMP_LOG_MULTILINE, shortbuf, sizeof(shortbuf), format, ap); + va_end(ap); + + if (!p) +@@ -421,7 +424,7 @@ void log_multiline(int priority, const char *prefix, const char *format, ...) + } + + if (p != shortbuf) +- XFREE(MTYPE_TMP, p); ++ XFREE(MTYPE_ISIS_TMP_LOG_MULTILINE, p); + } + + void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) +@@ -431,7 +434,7 @@ void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) + char *p; + + va_start(ap, format); +- p = vasnprintfrr(MTYPE_TMP, shortbuf, sizeof(shortbuf), format, ap); ++ p = vasnprintfrr(MTYPE_ISIS_TMP_VTY_MULTILINE, shortbuf, sizeof(shortbuf), format, ap); + va_end(ap); + + if (!p) +@@ -444,7 +447,7 @@ void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) + } + + if (p != shortbuf) +- XFREE(MTYPE_TMP, p); ++ XFREE(MTYPE_ISIS_TMP_VTY_MULTILINE, p); + } + + void vty_out_timestr(struct vty *vty, time_t uptime) +diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c +index a6ce9f674278..8b11ba56e1ab 100644 +--- a/isisd/isis_nb_config.c ++++ b/isisd/isis_nb_config.c +@@ -45,6 +45,8 @@ + #include "isisd/isis_flex_algo.h" + #include "isisd/isis_zebra.h" + ++DEFINE_MTYPE_STATIC(ISISD, ISIS_TMP_SPF_BACKOFF_BUF_NB, "ISIS NB SPF backoff temporary buffer"); ++ + #define AFFINITY_INCLUDE_ANY 0 + #define AFFINITY_INCLUDE_ALL 1 + #define AFFINITY_EXCLUDE_ANY 2 +@@ -632,7 +634,7 @@ void ietf_backoff_delay_apply_finish(struct nb_cb_apply_finish_args *args) + long timetolearn = yang_dnode_get_uint16(args->dnode, "time-to-learn"); + struct isis_area *area = nb_running_get_entry(args->dnode, NULL, true); + size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx"); +- char *buf = XCALLOC(MTYPE_TMP, bufsiz); ++ char *buf = XCALLOC(MTYPE_ISIS_TMP_SPF_BACKOFF_BUF_NB, bufsiz); + + snprintf(buf, bufsiz, "IS-IS %s L1", area->area_tag); + spf_backoff_free(area->spf_delay_ietf[0]); +@@ -644,7 +646,7 @@ void ietf_backoff_delay_apply_finish(struct nb_cb_apply_finish_args *args) + area->spf_delay_ietf[1] = spf_backoff_new(master, buf, init_delay, short_delay, long_delay, + holddown, timetolearn); + +- XFREE(MTYPE_TMP, buf); ++ XFREE(MTYPE_ISIS_TMP_SPF_BACKOFF_BUF_NB, buf); + } + + int isis_instance_spf_ietf_backoff_delay_create(struct nb_cb_create_args *args) +@@ -3633,8 +3635,9 @@ int lib_interface_isis_bfd_monitoring_profile_modify(struct nb_cb_modify_args *a + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); +- XFREE(MTYPE_TMP, circuit->bfd_config.profile); +- circuit->bfd_config.profile = XSTRDUP(MTYPE_TMP, yang_dnode_get_string(args->dnode, NULL)); ++ XFREE(MTYPE_ISIS_BFD_PROFILE, circuit->bfd_config.profile); ++ circuit->bfd_config.profile = XSTRDUP(MTYPE_ISIS_BFD_PROFILE, ++ yang_dnode_get_string(args->dnode, NULL)); + + return NB_OK; + } +@@ -3647,7 +3650,7 @@ int lib_interface_isis_bfd_monitoring_profile_destroy(struct nb_cb_destroy_args + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); +- XFREE(MTYPE_TMP, circuit->bfd_config.profile); ++ XFREE(MTYPE_ISIS_BFD_PROFILE, circuit->bfd_config.profile); + + return NB_OK; + } +diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c +index b0b51ea9e75f..303b9ae6106e 100644 +--- a/isisd/isis_tlvs.c ++++ b/isisd/isis_tlvs.c +@@ -58,6 +58,11 @@ typedef void (*format_item_func)(uint16_t mtid, struct isis_item *i, struct sbuf + struct json_object *json, int indent); + typedef struct isis_item *(*copy_item_func)(struct isis_item *i); + ++static struct sbuf format_tlvs_buf; ++static bool format_tlvs_buf_inited; ++static struct sbuf unpack_tlvs_logbuf; ++static bool unpack_tlvs_logbuf_inited; ++ + struct tlv_ops { + const char *name; + unpack_tlv_func unpack; +@@ -6303,14 +6308,14 @@ const char *isis_format_tlvs(struct isis_tlvs *tlvs, struct json_object *json) + format_tlvs(tlvs, NULL, json, 0); + return NULL; + } else { +- static struct sbuf buf; +- +- if (!sbuf_buf(&buf)) +- sbuf_init(&buf, NULL, 0); ++ if (!format_tlvs_buf_inited) { ++ sbuf_init(&format_tlvs_buf, NULL, 0); ++ format_tlvs_buf_inited = true; ++ } + +- sbuf_reset(&buf); +- format_tlvs(tlvs, &buf, NULL, 0); +- return sbuf_buf(&buf); ++ sbuf_reset(&format_tlvs_buf); ++ format_tlvs(tlvs, &format_tlvs_buf, NULL, 0); ++ return sbuf_buf(&format_tlvs_buf); + } + } + +@@ -6664,31 +6669,48 @@ static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, struct s + int isis_unpack_tlvs(size_t avail_len, struct stream *stream, struct isis_tlvs **dest, + const char **log) + { +- static struct sbuf logbuf; + int indent = 0; + int rv; + struct isis_tlvs *result; + +- if (!sbuf_buf(&logbuf)) +- sbuf_init(&logbuf, NULL, 0); ++ if (!unpack_tlvs_logbuf_inited) { ++ sbuf_init(&unpack_tlvs_logbuf, NULL, 0); ++ unpack_tlvs_logbuf_inited = true; ++ } + +- sbuf_reset(&logbuf); ++ sbuf_reset(&unpack_tlvs_logbuf); + if (avail_len > STREAM_READABLE(stream)) { +- sbuf_push(&logbuf, indent, ++ sbuf_push(&unpack_tlvs_logbuf, indent, + "Stream doesn't contain sufficient data. Claimed %zu, available %zu\n", + avail_len, STREAM_READABLE(stream)); + return 1; + } + + result = isis_alloc_tlvs(); +- rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result, indent, NULL); ++ rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &unpack_tlvs_logbuf, result, indent, ++ NULL); + +- *log = sbuf_buf(&logbuf); ++ *log = sbuf_buf(&unpack_tlvs_logbuf); + *dest = result; + + return rv; + } + ++void isis_tlvs_terminate(void) ++{ ++ if (format_tlvs_buf_inited) { ++ sbuf_free(&format_tlvs_buf); ++ memset(&format_tlvs_buf, 0, sizeof(format_tlvs_buf)); ++ format_tlvs_buf_inited = false; ++ } ++ ++ if (unpack_tlvs_logbuf_inited) { ++ sbuf_free(&unpack_tlvs_logbuf); ++ memset(&unpack_tlvs_logbuf, 0, sizeof(unpack_tlvs_logbuf)); ++ unpack_tlvs_logbuf_inited = false; ++ } ++} ++ + #define TLV_OPS(_name_, _desc_) \ + static const struct tlv_ops tlv_##_name_##_ops = { \ + .name = _desc_, \ +diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h +index 889d7369b2e6..b952e4f28890 100644 +--- a/isisd/isis_tlvs.h ++++ b/isisd/isis_tlvs.h +@@ -757,6 +757,7 @@ struct isis_subsubtlvs *isis_alloc_subsubtlvs(enum isis_tlv_context context); + int isis_unpack_tlvs(size_t avail_len, struct stream *stream, struct isis_tlvs **dest, + const char **error_log); + const char *isis_format_tlvs(struct isis_tlvs *tlvs, struct json_object *json); ++void isis_tlvs_terminate(void); + struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs); + struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); + +diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c +index 8560e898cba4..b99b677df79b 100644 +--- a/isisd/isis_vty_fabricd.c ++++ b/isisd/isis_vty_fabricd.c +@@ -22,6 +22,8 @@ + #include "lib/spf_backoff.h" + #include "isisd/isis_mt.h" + ++DEFINE_MTYPE_STATIC(ISISD, ISIS_TMP_SPF_BACKOFF_BUF_VTY, "ISIS VTY SPF backoff temporary buffer"); ++ + static struct isis_circuit *isis_circuit_lookup(struct vty *vty) + { + struct interface *ifp = VTY_GET_CONTEXT(interface); +@@ -696,7 +698,7 @@ DEFUN (spf_delay_ietf, + long timetolearn = atol(argv[10]->arg); + + size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx"); +- char *buf = XCALLOC(MTYPE_TMP, bufsiz); ++ char *buf = XCALLOC(MTYPE_ISIS_TMP_SPF_BACKOFF_BUF_VTY, bufsiz); + + snprintf(buf, bufsiz, "IS-IS %s L1", area->area_tag); + spf_backoff_free(area->spf_delay_ietf[0]); +@@ -710,7 +712,7 @@ DEFUN (spf_delay_ietf, + spf_backoff_new(master, buf, init_delay, short_delay, + long_delay, holddown, timetolearn); + +- XFREE(MTYPE_TMP, buf); ++ XFREE(MTYPE_ISIS_TMP_SPF_BACKOFF_BUF_VTY, buf); + return CMD_SUCCESS; + } + +diff --git a/isisd/isisd.c b/isisd/isisd.c +index e4986bc56f86..6aae50ba5985 100644 +--- a/isisd/isisd.c ++++ b/isisd/isisd.c +@@ -80,6 +80,7 @@ DEFINE_MTYPE_STATIC(ISISD, ISIS_AREA, "ISIS area"); + DEFINE_MTYPE(ISISD, ISIS_AREA_ADDR, "ISIS area address"); + DEFINE_MTYPE(ISISD, ISIS_ACL_NAME, "ISIS access-list name"); + DEFINE_MTYPE(ISISD, ISIS_PLIST_NAME, "ISIS prefix-list name"); ++DEFINE_MTYPE(ISISD, ISIS_BFD_PROFILE, "ISIS BFD profile"); + + DEFINE_QOBJ_TYPE(isis_area); + +diff --git a/isisd/isisd.h b/isisd/isisd.h +index 62363151585c..2721e50fbed7 100644 +--- a/isisd/isisd.h ++++ b/isisd/isisd.h +@@ -286,6 +286,7 @@ DECLARE_DLIST(isis_area_list, struct isis_area, area_list_item); + DECLARE_MTYPE(ISIS_ACL_NAME); /* isis_area->spf_prefix_prioritites */ + DECLARE_MTYPE(ISIS_AREA_ADDR); /* isis_area->area_addrs */ + DECLARE_MTYPE(ISIS_PLIST_NAME); ++DECLARE_MTYPE(ISIS_BFD_PROFILE); /* isis_circuit->bfd_config.profile */ + + DECLARE_HOOK(isis_area_overload_bit_update, (struct isis_area * area), (area)); + +-- +2.47.3 + diff --git a/debian/patches/upstream/0013-bgpd-Cleanup-memory-leaks-on-shutdown.patch b/debian/patches/upstream/0013-bgpd-Cleanup-memory-leaks-on-shutdown.patch new file mode 100644 index 000000000000..9fbb4dfc29a3 --- /dev/null +++ b/debian/patches/upstream/0013-bgpd-Cleanup-memory-leaks-on-shutdown.patch @@ -0,0 +1,608 @@ +From 1bf2f572adb0908e6a002d99e74ba20e4d3e1248 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 12:17:06 -0400 +Subject: [PATCH 09/21] bgpd: Cleanup memory leaks on shutdown + +a) tx addpath id's not being cleaned up properly, fix. +b) evpn memory leaks on shutdown, fix. +c) route_nodes not being properly released on shutdown, fix. +d) rpki memory leaks on shutdown, fix. +e) srv6 memory leaks on shutdown, fix. +f) hidden bgp instances memory leaks on recreate, fix. + +Signed-off-by: Donald Sharp +--- + bgpd/bgp_addpath.c | 32 ++++++++++++++++ + bgpd/bgp_addpath.h | 2 + + bgpd/bgp_evpn.c | 44 +++++++++++++++++++++ + bgpd/bgp_evpn.h | 1 + + bgpd/bgp_labelpool.c | 91 ++++++++++++++++++++++++++++++++++++++++++-- + bgpd/bgp_labelpool.h | 1 + + bgpd/bgp_main.c | 19 +++++++++ + bgpd/bgp_route.c | 30 ++++++++++++++- + bgpd/bgp_route.h | 1 + + bgpd/bgp_rpki.c | 2 + + bgpd/bgp_srv6.c | 10 +++++ + bgpd/bgp_table.c | 24 ++++++++++++ + bgpd/bgp_updgrp.c | 13 ++++++- + bgpd/bgpd.c | 57 ++++++++++++++++++++++----- + 14 files changed, 312 insertions(+), 15 deletions(-) + +diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c +index cb3a33180cbf..1b361e651dd8 100644 +--- a/bgpd/bgp_addpath.c ++++ b/bgpd/bgp_addpath.c +@@ -84,6 +84,38 @@ void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d) + } + } + ++/* ++ * Destroy any tx addpath id_allocators still attached to the BGP instance. ++ * ++ * Allocators are normally created by bgp_addpath_populate_type() when the ++ * peer count for an (afi, safi, type) combination transitions from zero to ++ * non-zero, and destroyed by the matching bgp_addpath_flush_type() when the ++ * count drops back to zero. Some configurations keep that count >= 1 even ++ * after every peer has been deleted (e.g. an L3VNI VRF advertising ++ * "ipv4/ipv6 unicast gateway-ip" keeps the BGP_ADDPATH_ALL count at 1 via ++ * advertise_type5_routes_multipath()), so bgp_addpath_flush_type() never ++ * fires. Call this from bgp_free() to release those leftover allocators. ++ * ++ * It is safe to skip the per-path id drain here because every bgp_path_info ++ * has already been freed by bgp_path_info_free_with_caller() -> ++ * bgp_addpath_free_info_data() by the time bgp_free() runs. ++ */ ++void bgp_addpath_finish_bgp_data(struct bgp_addpath_bgp_data *d) ++{ ++ safi_t safi; ++ afi_t afi; ++ int i; ++ ++ FOREACH_AFI_SAFI (afi, safi) { ++ for (i = 0; i < BGP_ADDPATH_MAX; i++) { ++ if (d->id_allocators[afi][safi][i]) { ++ idalloc_destroy(d->id_allocators[afi][safi][i]); ++ d->id_allocators[afi][safi][i] = NULL; ++ } ++ } ++ } ++} ++ + /* + * Free up resources associated with BGP route info structures. + */ +diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h +index c136671ea455..c7b1bff23800 100644 +--- a/bgpd/bgp_addpath.h ++++ b/bgpd/bgp_addpath.h +@@ -25,6 +25,8 @@ struct bgp_paths_limit_capability { + + void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d); + ++void bgp_addpath_finish_bgp_data(struct bgp_addpath_bgp_data *d); ++ + bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi, + safi_t safi); + +diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c +index 5d1122e34554..4f8ee466bb74 100644 +--- a/bgpd/bgp_evpn.c ++++ b/bgpd/bgp_evpn.c +@@ -5420,6 +5420,40 @@ static void free_vni_entry(struct hash_bucket *bucket, struct bgp *bgp) + bgp_evpn_free(bgp, vpn); + } + ++/* ++ * Iterator helper: drain per-VNI route tables (mac_table/ip_table) without ++ * freeing the bgpevpn struct itself. Used during early cleanup so that the ++ * imported per-VNI bgp_path_info entries (which reference parent path_info ++ * and dest objects in the global EVPN RIB) are reaped before the global ++ * EVPN RIB is finalized by bgp_cleanup_routes(). ++ */ ++static void drain_vni_routes(struct hash_bucket *bucket, struct bgp *bgp) ++{ ++ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; ++ ++ delete_all_vni_routes(bgp, vpn); ++} ++ ++/* ++ * Drain the per-VNI route tables for every VNI owned by this bgp instance. ++ * ++ * Per-VNI imported paths (in vpn->mac_table and vpn->ip_table) hold ++ * extra->vrfleak->parent references back to BPI/dest objects in the global ++ * EVPN RIB. bgp_cleanup_routes() later finalizes those tables via ++ * bgp_table_finish() which forcibly zeroes the dest lock counts and frees the ++ * dest nodes, leaving dangling pointers behind. Reap the per-VNI paths now ++ * so that their bgp_path_info_extra_free() can safely unlock parent dests ++ * while those parents are still alive; the subsequent bgp_evpn_cleanup() then ++ * walks empty per-VNI tables and is a no-op for routes. ++ */ ++void bgp_evpn_cleanup_per_vni_routes(struct bgp *bgp) ++{ ++ if (!bgp->vnihash) ++ return; ++ ++ hash_iterate(bgp->vnihash, (void (*)(struct hash_bucket *, void *))drain_vni_routes, bgp); ++} ++ + /* + * Derive AUTO import RT for BGP VRF - L3VNI + */ +@@ -6104,6 +6138,16 @@ void bgp_evpn_handle_deferred_bestpath_for_vnis(struct bgp *bgp, uint16_t cnt) + { + struct vni_gr_walk ctx; + ++ /* ++ * GR deferred path selection for SAFI_EVPN runs for any bgp instance ++ * with peers participating in graceful restart, but vnihash is only ++ * allocated by bgp_evpn_init() on instances that actually configure ++ * "address-family l2vpn evpn". Skip non-EVPN instances cleanly ++ * instead of crashing in hash_iterate(). ++ */ ++ if (!bgp->vnihash) ++ return; ++ + ctx.bgp = bgp; + ctx.cnt = cnt; + +diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h +index 38ec840b7ffd..f5d5b26137cb 100644 +--- a/bgpd/bgp_evpn.h ++++ b/bgpd/bgp_evpn.h +@@ -179,6 +179,7 @@ extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, + ifindex_t svi_ifindex); + extern void bgp_evpn_flood_control_change(struct bgp *bgp); + extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); ++extern void bgp_evpn_cleanup_per_vni_routes(struct bgp *bgp); + extern void bgp_evpn_cleanup(struct bgp *bgp); + extern void bgp_evpn_init(struct bgp *bgp); + extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); +diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c +index 52cd676f4876..0fee625d8c5c 100644 +--- a/bgpd/bgp_labelpool.c ++++ b/bgpd/bgp_labelpool.c +@@ -124,6 +124,14 @@ static wq_item_status lp_cbq_docallback(struct work_queue *wq, void *data) + return WQ_SUCCESS; + } + ++ /* ++ * Drained item: bgp_lp_release_pending_lu_locks() has already ++ * dropped the dest reference and zeroed labelid as a marker. ++ * Don't run the callback - it would deref a stale or NULL labelid. ++ */ ++ if (!lcbq->labelid) ++ return WQ_SUCCESS; ++ + if (!bgp) { + /* + * If we can't find the BGP instance, we should still release +@@ -201,7 +209,13 @@ void bgp_lp_init(struct event_loop *master, struct labelpool *pool) + /* check if a label callback was for a BGP LU node, and if so, unlock it */ + static void check_bgp_lu_cb_unlock(struct lp_lcb *lcb) + { +- if (lcb->type == LP_TYPE_BGP_LU) ++ /* ++ * NULL labelid is the marker left by bgp_lp_release_pending_lu_locks() ++ * for entries whose dest reference has already been dropped early ++ * (before bgp_delete() force-frees the underlying dests at shutdown). ++ * Skip those so we don't double-unlock or read freed memory. ++ */ ++ if (lcb->type == LP_TYPE_BGP_LU && lcb->labelid) + bgp_dest_unlock_node(lcb->labelid); + } + +@@ -212,10 +226,68 @@ static void check_bgp_lu_cb_lock(struct lp_lcb *lcb) + bgp_dest_lock_node(lcb->labelid); + } + ++/* ++ * Release any bgp_dest locks held by pending BGP-LU label requests. ++ * ++ * Pending LU requests live in the labelpool's slow-path FIFO ++ * (lp->requests) and on its callback workqueue (lp->callback_q); each one ++ * bumps the underlying bgp_dest's lock count via check_bgp_lu_cb_lock() ++ * so the dest stays alive until the request is satisfied or cancelled. ++ * ++ * At daemon shutdown bgp_exit() runs bgp_delete() for every bgp instance ++ * before bgp_lp_finish(). bgp_delete() / bgp_free() calls ++ * bgp_table_finish() which calls route_table_finish() which calls ++ * bgp_node_destroy() for every node, force-freeing the dest regardless ++ * of its lock count. Anything still held by the labelpool then becomes ++ * a dangling pointer; the unlock attempts in bgp_lp_finish() are a ++ * use-after-free, observable as an occasional ++ * assert(node->lock > 0) in route_unlock_node(). ++ * ++ * This function must be called BEFORE bgp_delete() so the labelpool ++ * drops its dest references while the dests are still valid. Each ++ * neutralized entry is marked by zeroing its labelid; both the ++ * subsequent bgp_lp_finish() walk and lp_cbq_docallback() (in case the ++ * workqueue ever does run after this) treat NULL labelid as a no-op. ++ * ++ * The labelpool itself is left fully functional so bgp_delete() and its ++ * callees (e.g. bgp_label_per_nexthop_free() -> bgp_lp_release()) keep ++ * working. The earlier "just move bgp_lp_finish() up" patch ++ * (PR #15583) failed precisely because it made bgp_delete()-time ++ * label releases dereference an already-freed labelpool. ++ */ ++void bgp_lp_release_pending_lu_locks(void) ++{ ++ struct lp_fifo *lf; ++ struct work_queue_item *item; ++ struct lp_cbq_item *q; ++ ++ if (!lp) ++ return; ++ ++ frr_each (lp_fifo, &lp->requests, lf) { ++ if (lf->lcb.type == LP_TYPE_BGP_LU && lf->lcb.labelid) { ++ bgp_dest_unlock_node(lf->lcb.labelid); ++ lf->lcb.labelid = NULL; ++ } ++ } ++ ++ if (!lp->callback_q) ++ return; ++ ++ STAILQ_FOREACH (item, &lp->callback_q->items, wq) { ++ q = item->data; ++ if (q && q->type == LP_TYPE_BGP_LU && q->labelid) { ++ bgp_dest_unlock_node(q->labelid); ++ q->labelid = NULL; ++ } ++ } ++} ++ + void bgp_lp_finish(void) + { + struct lp_fifo *lf; + struct work_queue_item *item, *titem; ++ struct lp_cbq_item *q; + struct listnode *node; + struct lp_chunk *chunk; + +@@ -247,11 +319,22 @@ void bgp_lp_finish(void) + * to remove an element from the queue after it has been run, resulting + * in a double unlock. Hence we need to iterate over our queues and + * lists and manually perform the unlocking (ugh) ++ * ++ * NB: callback workqueue items are struct lp_cbq_item, NOT struct ++ * lp_lcb - the two have different layouts so a cast is wrong. ++ * Read the type/labelid fields directly off lp_cbq_item. Entries ++ * already drained by bgp_lp_release_pending_lu_locks() carry a NULL ++ * labelid and are skipped. + */ +- STAILQ_FOREACH_SAFE (item, &lp->callback_q->items, wq, titem) +- check_bgp_lu_cb_unlock(item->data); ++ if (lp->callback_q) { ++ STAILQ_FOREACH_SAFE (item, &lp->callback_q->items, wq, titem) { ++ q = item->data; ++ if (q && q->type == LP_TYPE_BGP_LU && q->labelid) ++ bgp_dest_unlock_node(q->labelid); ++ } + +- work_queue_free_and_null(&lp->callback_q); ++ work_queue_free_and_null(&lp->callback_q); ++ } + + lp = NULL; + } +diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h +index 809d5c305cca..9a2d86b23e8e 100644 +--- a/bgpd/bgp_labelpool.h ++++ b/bgpd/bgp_labelpool.h +@@ -34,6 +34,7 @@ struct labelpool { + }; + + extern void bgp_lp_init(struct event_loop *master, struct labelpool *pool); ++extern void bgp_lp_release_pending_lu_locks(void); + extern void bgp_lp_finish(void); + extern void bgp_lp_get(int type, void *labelid, vrf_id_t vrf_id, + int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)); +diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c +index ff8493e776a6..3f3ea0150800 100644 +--- a/bgpd/bgp_main.c ++++ b/bgpd/bgp_main.c +@@ -179,6 +179,25 @@ static FRR_NORETURN void bgp_exit(int status) + bgp_default = bgp_get_default(); + bgp_evpn = bgp_get_evpn(); + ++ /* ++ * Drop any bgp_dest references the labelpool is still holding for ++ * pending BGP-LU label requests BEFORE bgp_delete() runs. ++ * ++ * bgp_delete() -> bgp_free() -> bgp_table_finish() force-frees every ++ * dest in the instance's tables regardless of lock count; if the ++ * labelpool's slow-path FIFO or callback workqueue still pin any of ++ * those dests, the unlock attempts later in bgp_lp_finish() become ++ * a use-after-free (occasional assert(node->lock > 0) crash in ++ * route_unlock_node()). ++ * ++ * This only releases the dest locks - the labelpool itself stays ++ * functional so bgp_delete()-time callers (e.g. ++ * bgp_label_per_nexthop_free() -> bgp_lp_release()) keep working. ++ * The remainder of the labelpool is torn down by bgp_lp_finish() ++ * below. ++ */ ++ bgp_lp_release_pending_lu_locks(); ++ + /* reverse bgp_master_init */ + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + if (bgp_default == bgp || bgp_evpn == bgp) +diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c +index 778b1ce214c8..ddbd24d9aafb 100644 +--- a/bgpd/bgp_route.c ++++ b/bgpd/bgp_route.c +@@ -7087,11 +7087,39 @@ static void bgp_clear_node_queue_init(struct peer *peer) + peer->clear_node_queue->spec.data = peer; + } + ++bool bgp_clear_node_queue_drain(struct peer *peer) ++{ ++ struct work_queue *wq; ++ struct work_queue_item *item; ++ bool need_peer_unlock = false; ++ ++ if (!peer || !peer->clear_node_queue) ++ return false; ++ ++ wq = peer->clear_node_queue; ++ need_peer_unlock = work_queue_is_scheduled(wq) || !work_queue_empty(wq); ++ ++ /* ++ * During daemon termination we need the clear-route work to run ++ * synchronously before queue teardown, otherwise we only drop refs. ++ * ++ * This is intentionally done this way because we are trying to cleanup ++ * memory on shutdown and these items need to be handled no matter what. ++ */ ++ if (bm->terminating && wq->spec.workfunc) { ++ STAILQ_FOREACH (item, &wq->items, wq) ++ wq->spec.workfunc(wq, item->data); ++ } ++ ++ work_queue_free_and_null(&peer->clear_node_queue); ++ return need_peer_unlock; ++} ++ + static void bgp_clear_route_table(struct peer *peer, afi_t afi, safi_t safi, + struct bgp_table *table) + { + struct bgp_dest *dest; +- int force = peer->bgp->process_queue ? 0 : 1; ++ int force = (!peer->bgp->process_queue || bm->terminating) ? 1 : 0; + + if (!table) + table = peer->bgp->rib[afi][safi]; +diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h +index 8c26de1b45b7..d93e89e98584 100644 +--- a/bgpd/bgp_route.h ++++ b/bgpd/bgp_route.h +@@ -786,6 +786,7 @@ extern void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp, + extern bool bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi); + extern void bgp_clear_route(struct peer *peer, afi_t afi, safi_t safi); + extern void bgp_clear_route_all(struct peer *peer); ++extern bool bgp_clear_node_queue_drain(struct peer *peer); + /* Clear routes for a batch of peers */ + void bgp_clear_route_batch(struct bgp_clearing_info *cinfo); + +diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c +index 1f4541088334..2a884a071a5d 100644 +--- a/bgpd/bgp_rpki.c ++++ b/bgpd/bgp_rpki.c +@@ -878,6 +878,8 @@ static int bgp_rpki_fini(void) + XFREE(MTYPE_BGP_RPKI_CACHE, rpki_vrf); + } + ++ list_delete(&rpki_vrf_list); ++ + return 0; + } + +diff --git a/bgpd/bgp_srv6.c b/bgpd/bgp_srv6.c +index 5b3b2e4d90bb..c16c3d57fd9b 100644 +--- a/bgpd/bgp_srv6.c ++++ b/bgpd/bgp_srv6.c +@@ -262,6 +262,16 @@ void bgp_srv6_unicast_register_route(struct bgp *bgp, afi_t afi, struct bgp_dest + if (dest->srv6_unicast && sid_same(bgp->srv6_unicast[afi].sid, &dest->srv6_unicast->sid)) + return; + ++ /* ++ * If a previous SID was installed on this dest (e.g. the operator ++ * reconfigured the unicast SID without first walking/withdrawing the ++ * RIB), free the old descriptor before allocating a new one. Without ++ * this the previous XCALLOC is leaked across "no sid export ..." + ++ * re-add sequences that change the SID value. ++ */ ++ if (dest->srv6_unicast) ++ bgp_srv6_unicast_unregister_route(dest); ++ + locator = bgp->srv6_unicast[afi].sid_locator; + dest->srv6_unicast = XCALLOC(MTYPE_BGP_SRV6_L3SERVICE, + sizeof(struct bgp_attr_srv6_l3service)); +diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c +index 17b417895f17..34847609df7b 100644 +--- a/bgpd/bgp_table.c ++++ b/bgpd/bgp_table.c +@@ -18,6 +18,7 @@ + #include "bgp_addpath.h" + #include "bgp_trace.h" + #include "bgp_mpath.h" ++#include "bgp_srv6.h" + + void bgp_table_lock(struct bgp_table *rt) + { +@@ -99,6 +100,17 @@ inline struct bgp_dest *bgp_dest_unlock_node(struct bgp_dest *dest) + if (dest->mpath) + bgp_path_info_mpath_free(&dest->mpath); + ++ /* ++ * Release any SRv6 unicast prefix-sid descriptor still ++ * attached to this dest before the dest itself is freed. ++ * Normally bgp_cleanup_table() reaps these at instance ++ * teardown, but a dest can also be freed mid-run when its ++ * last path is reaped (e.g. peer flap, route withdrawal), ++ * which bypasses the cleanup walk and leaks the descriptor. ++ */ ++ if (dest->srv6_unicast) ++ bgp_srv6_unicast_unregister_route(dest); ++ + XFREE(MTYPE_BGP_NODE, dest); + dest = NULL; + rn->info = NULL; +@@ -129,6 +141,18 @@ static void bgp_node_destroy(route_table_delegate_t *delegate, + if (dest->mpath) + bgp_path_info_mpath_free(&dest->mpath); + ++ /* ++ * As in bgp_dest_unlock_node_debug(), make sure any ++ * lingering SRv6 unicast descriptor is released before ++ * the dest itself goes away. This path is hit when a ++ * route_table is force-finished (e.g. via ++ * bgp_table_finish() during instance teardown) while ++ * one or more dests still carry their dest->srv6_unicast ++ * pointer. ++ */ ++ if (dest->srv6_unicast) ++ bgp_srv6_unicast_unregister_route(dest); ++ + XFREE(MTYPE_BGP_NODE, dest); + node->info = NULL; + } +diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c +index f92e19ad69da..e523528c86c4 100644 +--- a/bgpd/bgp_updgrp.c ++++ b/bgpd/bgp_updgrp.c +@@ -1940,10 +1940,21 @@ void update_bgp_group_init(struct bgp *bgp) + { + int afid; + +- AF_FOREACH (afid) ++ /* ++ * Be idempotent: when a hidden bgp instance is revived (see ++ * bgp_lookup_by_as_name_type() -> bgp_create() with hidden=true), ++ * bgp_create() runs again on the same struct bgp. The connectionhash ++ * and per-afi import_vrf list are already guarded with explicit NULL ++ * checks; without the same check here we would re-allocate the per-afi ++ * update_groups hash tables and leak the previously allocated set. ++ */ ++ AF_FOREACH (afid) { ++ if (bgp->update_groups[afid]) ++ continue; + bgp->update_groups[afid] = + hash_create(updgrp_hash_key_make, updgrp_hash_cmp, + "BGP Update Group Hash"); ++ } + } + + void update_bgp_group_free(struct bgp *bgp) +diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c +index ef8ebea216d6..e92fe29af871 100644 +--- a/bgpd/bgpd.c ++++ b/bgpd/bgpd.c +@@ -2796,6 +2796,7 @@ int peer_delete(struct peer *peer) + struct bgp_filter *filter; + struct listnode *pn; + int accept_peer; ++ bool clear_queue_lock_held = false; + + assert(peer->connection->status != Deleted); + +@@ -2873,6 +2874,13 @@ int peer_delete(struct peer *peer) + bgp_stop(peer->connection); + UNSET_FLAG(peer->flags, PEER_FLAG_DELETE); + ++ /* ++ * If shutdown interrupts clear-node workers, drain the queue now and ++ * account for the deferred bgp_clear_route() peer lock explicitly. ++ */ ++ if (bm->terminating) ++ clear_queue_lock_held = bgp_clear_node_queue_drain(peer); ++ + if (peer->doppelganger) { + peer->doppelganger->doppelganger = NULL; + peer->doppelganger = NULL; +@@ -2946,7 +2954,9 @@ int peer_delete(struct peer *peer) + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version); + +- peer_unlock(peer); /* initial reference */ ++ if (clear_queue_lock_held) ++ peer_unlock(peer); /* bgp_clear_route, completion callback equivalent */ ++ peer_unlock(peer); /* initial reference */ + + return 0; + } +@@ -3636,11 +3646,13 @@ static struct bgp *bgp_create(as_t *as, const char *name, + + peer_init: + bgp->peer->cmp = (int (*)(void *, void *))peer_cmp; +- bgp->connectionhash = hash_create(connection_hash_key_make, connection_hash_same, +- "BGP Peer Hash"); +- bgp->connectionhash->max_size = BGP_PEER_MAX_HASH_SIZE; ++ if (!bgp->connectionhash) { ++ bgp->connectionhash = hash_create(connection_hash_key_make, connection_hash_same, ++ "BGP Peer Hash"); ++ bgp->connectionhash->max_size = BGP_PEER_MAX_HASH_SIZE; ++ } + +- if (!hidden) ++ if (!bgp->group) + bgp->group = list_new(); + bgp->group->cmp = (int (*)(void *, void *))peer_group_cmp; + +@@ -4416,13 +4428,39 @@ int bgp_delete(struct bgp *bgp) + } + } + ++ if (bm->bgp_evpn == bgp) { ++ /* ++ * Reap all paths from per-VNI route tables BEFORE the global ++ * EVPN RIB is finalized by bgp_cleanup_routes() below. ++ * ++ * Per-VNI imported BPIs hold extra->vrfleak->parent references ++ * back into the global EVPN RIB. bgp_cleanup_routes() calls ++ * bgp_table_finish() on the per-RD inner tables which forcibly ++ * zeroes node->lock and frees the dest objects. If the per-VNI ++ * paths are reaped after that, bgp_path_info_extra_free() ++ * tries to bgp_dest_unlock_node() those already-freed dests, ++ * tripping the "node->lock > 0" assert. ++ */ ++ bgp_evpn_cleanup_per_vni_routes(bgp); ++ } ++ + bgp_cleanup_routes(bgp); + +- if (bm->terminating) ++ if (bm->bgp_evpn == bgp) { + /* +- * Release EVPN VNI bgp_lock references so the +- * subsequent bgp_unlock() can drive refcount to +- * zero and trigger bgp_free(). ++ * Release EVPN VNI bgp_lock references so the subsequent ++ * bgp_unlock() can drive refcount to zero and trigger ++ * bgp_free(). ++ * ++ * This must run on every teardown of the EVPN-owner instance ++ * (not only during daemon termination). Each L2VNI holds a ++ * bgp_lock on the owner via bgpevpn_link_to_l3vni(); without ++ * releasing those locks here, "no router bgp" of the EVPN ++ * owner leaks the instance, its peer_self, and any local ++ * EVPN paths still held in the per-VNI tables. The per-VNI ++ * route tables were already drained above; free_vni_entry() ++ * here re-runs delete_all_vni_routes() which is a no-op (the ++ * tables are empty) and then frees the bgpevpn structs. + */ + bgp_evpn_cleanup(bgp); + } +@@ -4565,6 +4603,7 @@ void bgp_free(struct bgp *bgp) + + bgp_evpn_cleanup(bgp); + bgp_pbr_cleanup(bgp); ++ bgp_addpath_finish_bgp_data(&bgp->tx_addpath); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + enum vpn_policy_direction dir; +-- +2.47.3 + diff --git a/debian/patches/upstream/0014-ripd-Cleanup-memory-leaks-on-shutdown.patch b/debian/patches/upstream/0014-ripd-Cleanup-memory-leaks-on-shutdown.patch new file mode 100644 index 000000000000..67807a0a6a41 --- /dev/null +++ b/debian/patches/upstream/0014-ripd-Cleanup-memory-leaks-on-shutdown.patch @@ -0,0 +1,58 @@ +From b235bb1ae2a4ad049d93560ce1e1f72e65313b49 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 13:54:28 -0400 +Subject: [PATCH 10/21] ripd: Cleanup memory leaks on shutdown + +a) access_list memory leaks +b) snmp memory leaks + +Signed-off-by: Donald Sharp +--- + ripd/rip_main.c | 1 + + ripd/rip_snmp.c | 12 ++++++++++++ + 2 files changed, 13 insertions(+) + +diff --git a/ripd/rip_main.c b/ripd/rip_main.c +index f7b011b747e3..a179919c9779 100644 +--- a/ripd/rip_main.c ++++ b/ripd/rip_main.c +@@ -94,6 +94,7 @@ static FRR_NORETURN void sigint(void) + rip_zclient_stop(); + + route_map_finish(); ++ access_list_reset(); + prefix_list_reset(); + + keychain_terminate(); +diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c +index 118b08e527e9..8f8edd0247eb 100644 +--- a/ripd/rip_snmp.c ++++ b/ripd/rip_snmp.c +@@ -562,12 +562,24 @@ static int rip_snmp_init(struct event_loop *mstr) + return 0; + } + ++static int rip_snmp_terminate(void) ++{ ++ if (rip_ifaddr_table) { ++ route_table_finish(rip_ifaddr_table); ++ rip_ifaddr_table = NULL; ++ } ++ ++ smux_terminate(); ++ return 0; ++} ++ + static int rip_snmp_module_init(void) + { + hook_register(rip_ifaddr_add, rip_snmp_ifaddr_add); + hook_register(rip_ifaddr_del, rip_snmp_ifaddr_del); + + hook_register(frr_late_init, rip_snmp_init); ++ hook_register(frr_fini, rip_snmp_terminate); + return 0; + } + +-- +2.47.3 + diff --git a/debian/patches/upstream/0015-pbrd-termination-cleanup-of-memory-leaks.patch b/debian/patches/upstream/0015-pbrd-termination-cleanup-of-memory-leaks.patch new file mode 100644 index 000000000000..0862da9df6a7 --- /dev/null +++ b/debian/patches/upstream/0015-pbrd-termination-cleanup-of-memory-leaks.patch @@ -0,0 +1,126 @@ +From c2e0c6c9b8d5d4e8c127f9a5cf0930eed93c607c Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 14:11:37 -0400 +Subject: [PATCH 11/21] pbrd: termination cleanup of memory leaks + +a) pbr maps were being leaked, stop +b) nexthop-groups were being leaked, stop +c) access list's were being leaked, stop + +Signed-off-by: Donald Sharp +--- + pbrd/pbr_main.c | 4 ++++ + pbrd/pbr_map.c | 14 ++++++++++++++ + pbrd/pbr_map.h | 1 + + pbrd/pbr_nht.c | 28 ++++++++++++++++++++++++++++ + pbrd/pbr_nht.h | 1 + + 5 files changed, 48 insertions(+) + +diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c +index 7724a795d7f0..0a4aa34d501f 100644 +--- a/pbrd/pbr_main.c ++++ b/pbrd/pbr_main.c +@@ -69,7 +69,11 @@ static FRR_NORETURN void sigint(void) + { + zlog_notice("Terminating on signal"); + ++ pbr_map_terminate(); + pbr_vrf_terminate(); ++ nexthop_group_terminate(); ++ pbr_nht_terminate(); ++ access_list_reset(); + + pbr_zebra_destroy(); + +diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c +index 713345a13016..0bd1fb3ba3a1 100644 +--- a/pbrd/pbr_map.c ++++ b/pbrd/pbr_map.c +@@ -957,3 +957,17 @@ void pbr_map_init(void) + + pbr_map_sequence_unique = 1; + } ++ ++void pbr_map_terminate(void) ++{ ++ struct pbr_map *pbrm; ++ ++ while ((pbrm = RB_ROOT(pbr_map_entry_head, &pbr_maps))) { ++ struct pbr_map_sequence *pbrms; ++ ++ pbrms = listnode_head(pbrm->seqnumbers); ++ assert(pbrms); ++ ++ pbr_map_delete(pbrms); ++ } ++} +diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h +index 9fb674bd6eb8..976e0e65ffff 100644 +--- a/pbrd/pbr_map.h ++++ b/pbrd/pbr_map.h +@@ -246,6 +246,7 @@ extern void pbr_map_schedule_policy_from_nhg(const char *nh_group, + bool installed); + + extern void pbr_map_install(struct pbr_map *pbrm); ++extern void pbr_map_terminate(void); + + extern void pbr_map_policy_install(const char *name); + extern void pbr_map_policy_delete(struct pbr_map *pbrm, +diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c +index 5f87c0c1fa61..76fecc3f8866 100644 +--- a/pbrd/pbr_nht.c ++++ b/pbrd/pbr_nht.c +@@ -173,6 +173,13 @@ static void pbr_nhgc_delete(struct pbr_nexthop_group_cache *p) + XFREE(MTYPE_PBR_NHG, p); + } + ++static void pbr_nhrc_delete(void *arg) ++{ ++ struct nhrc *nhrc = arg; ++ ++ XFREE(MTYPE_PBR_NHG, nhrc); ++} ++ + static void *pbr_nhgc_alloc(void *p) + { + struct pbr_nexthop_group_cache *new; +@@ -1486,3 +1493,24 @@ void pbr_nht_init(void) + /* First unallocated table is lowest in range on init */ + pbr_next_unallocated_table_id = PBR_NHT_DEFAULT_LOW_TABLEID; + } ++ ++void pbr_nht_terminate(void) ++{ ++ if (pbr_nhg_hash) { ++ hash_clean(pbr_nhg_hash, (void (*)(void *))pbr_nhgc_delete); ++ hash_free(pbr_nhg_hash); ++ pbr_nhg_hash = NULL; ++ } ++ ++ if (pbr_nhrc_hash) { ++ hash_clean(pbr_nhrc_hash, pbr_nhrc_delete); ++ hash_free(pbr_nhrc_hash); ++ pbr_nhrc_hash = NULL; ++ } ++ ++ if (pbr_nhg_allocated_id_hash) { ++ hash_clean(pbr_nhg_allocated_id_hash, NULL); ++ hash_free(pbr_nhg_allocated_id_hash); ++ pbr_nhg_allocated_id_hash = NULL; ++ } ++} +diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h +index a8c26413798b..92dc4060bbc7 100644 +--- a/pbrd/pbr_nht.h ++++ b/pbrd/pbr_nht.h +@@ -139,6 +139,7 @@ extern void pbr_nht_nexthop_update(struct zapi_route *nhr); + extern void pbr_nht_nexthop_interface_update(struct interface *ifp); + + extern void pbr_nht_init(void); ++extern void pbr_nht_terminate(void); + + extern void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf); + extern void pbr_nht_interface_update(struct interface *ifp); +-- +2.47.3 + diff --git a/debian/patches/upstream/0016-nhrpd-Cleanup-nhrpd-termination-memory-leaks.patch b/debian/patches/upstream/0016-nhrpd-Cleanup-nhrpd-termination-memory-leaks.patch new file mode 100644 index 000000000000..b91050d58ac6 --- /dev/null +++ b/debian/patches/upstream/0016-nhrpd-Cleanup-nhrpd-termination-memory-leaks.patch @@ -0,0 +1,105 @@ +From 5bec9de50fe664bcbc857e16f6e656c328968c03 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 14:18:31 -0400 +Subject: [PATCH 12/21] nhrpd: Cleanup nhrpd termination memory leaks + +a) interface gre memory leaks, stop +b) packet request id memory leaks, stop +c) event request id memory leaks, stop + +Signed-off-by: Donald Sharp +--- + nhrpd/nhrp_interface.c | 10 ++++++++++ + nhrpd/nhrp_main.c | 3 +++ + nhrpd/nhrpd.h | 2 ++ + nhrpd/reqid.c | 9 +++++++++ + 4 files changed, 24 insertions(+) + +diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c +index 947ef77b9410..a70ce536fbea 100644 +--- a/nhrpd/nhrp_interface.c ++++ b/nhrpd/nhrp_interface.c +@@ -52,6 +52,11 @@ static void *nhrp_interface_gre_alloc(void *data) + return a; + } + ++static void nhrp_interface_gre_free(void *data) ++{ ++ XFREE(MTYPE_NHRP_IF_GRE, data); ++} ++ + struct nhrp_gre_info *nhrp_gre_info_alloc(struct nhrp_gre_info *p) + { + struct nhrp_gre_info *a; +@@ -115,6 +120,11 @@ void nhrp_interface_init(void) + "NHRP GRE list Hash"); + } + ++void nhrp_interface_terminate(void) ++{ ++ hash_clean_and_free(&nhrp_gre_list, nhrp_interface_gre_free); ++} ++ + void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi) + { + struct nhrp_interface *nifp = ifp->info; +diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c +index 3c33db316701..480ab6b9f0ac 100644 +--- a/nhrpd/nhrp_main.c ++++ b/nhrpd/nhrp_main.c +@@ -88,12 +88,15 @@ static FRR_NORETURN void nhrp_request_stop(void) + nhrp_zebra_terminate(); + vici_terminate(); + evmgr_terminate(); ++ nhrp_interface_terminate(); + vrf_terminate(); + nhrp_vc_terminate(); + + debugf(NHRP_DEBUG_COMMON, "Done."); + + resolver_terminate(); ++ nhrp_reqid_terminate(&nhrp_packet_reqid); ++ nhrp_reqid_terminate(&nhrp_event_reqid); + frr_fini(); + + exit(0); +diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h +index 5d59a60e766e..97be65cd087b 100644 +--- a/nhrpd/nhrpd.h ++++ b/nhrpd/nhrpd.h +@@ -364,6 +364,7 @@ extern struct zebra_privs_t nhrpd_privs; + int sock_open_unix(const char *path); + + void nhrp_interface_init(void); ++void nhrp_interface_terminate(void); + void nhrp_interface_update(struct interface *ifp); + void nhrp_interface_update_arp(struct interface *ifp, bool arp_enable); + void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi); +@@ -516,6 +517,7 @@ uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *p, struct nhrp_reqid *r, + void (*cb)(struct nhrp_reqid *, void *)); + void nhrp_reqid_free(struct nhrp_reqid_pool *p, struct nhrp_reqid *r); + struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *, uint32_t reqid); ++void nhrp_reqid_terminate(struct nhrp_reqid_pool *p); + + int nhrp_packet_init(void); + +diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c +index c2f5d24b69b8..d6d094919cad 100644 +--- a/nhrpd/reqid.c ++++ b/nhrpd/reqid.c +@@ -52,3 +52,12 @@ struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *p, uint32_t reqid) + key.request_id = reqid; + return hash_lookup(p->reqid_hash, &key); + } ++ ++void nhrp_reqid_terminate(struct nhrp_reqid_pool *p) ++{ ++ if (!p) ++ return; ++ ++ hash_clean_and_free(&p->reqid_hash, NULL); ++ p->next_request_id = 0; ++} +-- +2.47.3 + diff --git a/debian/patches/upstream/0017-sharpd-memory-leaks-on-shutdown-cleanup.patch b/debian/patches/upstream/0017-sharpd-memory-leaks-on-shutdown-cleanup.patch new file mode 100644 index 000000000000..580793fda534 --- /dev/null +++ b/debian/patches/upstream/0017-sharpd-memory-leaks-on-shutdown-cleanup.patch @@ -0,0 +1,80 @@ +From 506287772549bbd5ff59a4e74cbb32ed98a5b36a Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 14:30:58 -0400 +Subject: [PATCH 13/21] sharpd: memory leaks on shutdown cleanup + +a) srv6 memory leaks on shutdown, stop +b) nexthop-group memory leaks on shutdown, stop + +Signed-off-by: Donald Sharp +--- + sharpd/sharp_main.c | 14 ++++++++++++++ + sharpd/sharp_nht.c | 5 +++++ + sharpd/sharp_nht.h | 1 + + 3 files changed, 20 insertions(+) + +diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c +index 76aa923e09c8..7f39f4a8ab64 100644 +--- a/sharpd/sharp_main.c ++++ b/sharpd/sharp_main.c +@@ -27,6 +27,7 @@ + #include "distribute.h" + #include "libfrr.h" + #include "routemap.h" ++#include "nexthop.h" + #include "nexthop_group.h" + #include "link_state.h" + +@@ -74,6 +75,18 @@ static void sharp_srv6_locators_list_delete(void *item) + + static void sharp_global_destroy(void) + { ++ /* ++ * Route templates can keep SRv6 encap/seg6local payloads attached to ++ * sg.r.nhop across command invocations. Ensure they are released at exit. ++ */ ++ nexthop_del_srv6_seg6local(&sg.r.nhop); ++ nexthop_del_srv6_seg6(&sg.r.nhop); ++ nexthop_del_srv6_seg6local(&sg.r.backup_nhop); ++ nexthop_del_srv6_seg6(&sg.r.backup_nhop); ++ ++ /* Free imported Traffic Engineering LSDB (nodes/edges/subnets + attrs). */ ++ ls_ted_del_all(&sg.ted); ++ + list_delete(&sg.nhs); + + sg.srv6_locators->del = sharp_srv6_locators_list_delete; +@@ -94,6 +107,7 @@ static FRR_NORETURN void sigint(void) + { + zlog_notice("Terminating on signal"); + ++ sharp_nhgroup_terminate(); + vrf_terminate(); + sharp_zebra_terminate(); + +diff --git a/sharpd/sharp_nht.c b/sharpd/sharp_nht.c +index 6d64fcfb259b..b42a1fba7f19 100644 +--- a/sharpd/sharp_nht.c ++++ b/sharpd/sharp_nht.c +@@ -229,3 +229,8 @@ void sharp_nhgroup_init(void) + sharp_nhgroup_del_nexthop_cb, + sharp_nhgroup_delete_cb); + } ++ ++void sharp_nhgroup_terminate(void) ++{ ++ nexthop_group_terminate(); ++} +diff --git a/sharpd/sharp_nht.h b/sharpd/sharp_nht.h +index b27952ac511c..233b0c5f77fa 100644 +--- a/sharpd/sharp_nht.h ++++ b/sharpd/sharp_nht.h +@@ -27,4 +27,5 @@ extern void sharp_nhgroup_id_set_installed(uint32_t id, bool installed); + extern bool sharp_nhgroup_id_is_installed(uint32_t id); + + extern void sharp_nhgroup_init(void); ++extern void sharp_nhgroup_terminate(void); + #endif +-- +2.47.3 + diff --git a/debian/patches/upstream/0018-ospfd-memory-leaks-on-shutdown.patch b/debian/patches/upstream/0018-ospfd-memory-leaks-on-shutdown.patch new file mode 100644 index 000000000000..1cee15a00517 --- /dev/null +++ b/debian/patches/upstream/0018-ospfd-memory-leaks-on-shutdown.patch @@ -0,0 +1,38 @@ +From c45c8876301933ede0948775bc34a2cb911c33d5 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 14:33:23 -0400 +Subject: [PATCH 14/21] ospfd: memory leaks on shutdown + +Cleanup ted memory leaks on shutdown. + +Signed-off-by: Donald Sharp +--- + ospfd/ospf_te.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c +index 22abb58b02e5..a5c40f5d547d 100644 +--- a/ospfd/ospf_te.c ++++ b/ospfd/ospf_te.c +@@ -159,6 +159,9 @@ int ospf_mpls_te_init(void) + + void ospf_mpls_te_term(void) + { ++ /* Release imported/exported TE link-state database on daemon exit. */ ++ ls_ted_del_all(&OspfMplsTE.ted); ++ + list_delete(&OspfMplsTE.iflist); + + ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA, +@@ -177,6 +180,8 @@ void ospf_mpls_te_term(void) + + void ospf_mpls_te_finish(void) + { ++ ls_ted_del_all(&OspfMplsTE.ted); ++ + OspfMplsTE.enabled = false; + OspfMplsTE.inter_as = Off; + OspfMplsTE.export = false; +-- +2.47.3 + diff --git a/debian/patches/upstream/0019-lib-Cleanup-memory-leaks-in-shutdown-in-affinitymaps.patch b/debian/patches/upstream/0019-lib-Cleanup-memory-leaks-in-shutdown-in-affinitymaps.patch new file mode 100644 index 000000000000..b82688c63a5c --- /dev/null +++ b/debian/patches/upstream/0019-lib-Cleanup-memory-leaks-in-shutdown-in-affinitymaps.patch @@ -0,0 +1,32 @@ +From 4372bf1cca88569b95c1cab1fc8627dc2f4d2ff6 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 15:09:11 -0400 +Subject: [PATCH 15/21] lib: Cleanup memory leaks in shutdown in affinitymaps + +Signed-off-by: Donald Sharp +--- + lib/affinitymap.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/lib/affinitymap.c b/lib/affinitymap.c +index e8659ccabb3b..cd7029e36c87 100644 +--- a/lib/affinitymap.c ++++ b/lib/affinitymap.c +@@ -115,9 +115,10 @@ void affinity_map_set_update_hook(void (*func)(const char *affmap_name, + + void affinity_map_terminate(void) + { +- struct affinity_map *map; +- struct listnode *node, *nnode; ++ if (!affinity_map_master.maps) ++ return; + +- for (ALL_LIST_ELEMENTS(affinity_map_master.maps, node, nnode, map)) +- affinity_map_free(map); ++ affinity_map_master.maps->del = (void (*)(void *))affinity_map_free; ++ list_delete(&affinity_map_master.maps); ++ affinity_map_master.update_hook = NULL; + } +-- +2.47.3 + diff --git a/debian/patches/upstream/0020-zebra-cleanup-memory-leaks-on-shutdown.patch b/debian/patches/upstream/0020-zebra-cleanup-memory-leaks-on-shutdown.patch new file mode 100644 index 000000000000..5b45d20e35c0 --- /dev/null +++ b/debian/patches/upstream/0020-zebra-cleanup-memory-leaks-on-shutdown.patch @@ -0,0 +1,175 @@ +From ce85fb6a6ba5f81a2782d87d1ac6c60ee29d575d Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 15:09:52 -0400 +Subject: [PATCH 16/21] zebra: cleanup memory leaks on shutdown + +a) vxlan memory leaks on shutdown, fix. +b) evpn-mh memory leaks on shutdown, fix. +c) mpls-fec memory leaks on shutdown, fix. +d) route-map memory leaks on shutdown, fix. + +Signed-off-by: Donald Sharp +--- + zebra/interface.c | 16 ++++++++++++++++ + zebra/zebra_evpn_mh.c | 23 +++++++++++++++++------ + zebra/zebra_mpls.c | 15 +++++++++++++++ + zebra/zebra_routemap.c | 22 ++++++++++++++++++++++ + 4 files changed, 70 insertions(+), 6 deletions(-) + +diff --git a/zebra/interface.c b/zebra/interface.c +index 23324a677314..de71b0c31a81 100644 +--- a/zebra/interface.c ++++ b/zebra/interface.c +@@ -32,6 +32,7 @@ + #include "zebra/rt_netlink.h" + #include "zebra/if_netlink.h" + #include "zebra/zebra_vxlan.h" ++#include "zebra/zebra_vxlan_if.h" + #include "zebra/zebra_errors.h" + #include "zebra/zebra_evpn_mh.h" + #include "zebra/zebra_trace.h" +@@ -228,6 +229,21 @@ static int if_zebra_delete_hook(struct interface *ifp) + list_delete(&bond->mbr_zifs); + + zebra_l2_bridge_if_cleanup(ifp); ++ /* ++ * Destroy any per-VXLAN-IF SVD VNI table that is still hanging ++ * around. In the runtime delete path this is already done via ++ * zebra_l2_vxlanif_del() before if_delete_update() fires the ++ * if_del hook, so this is a no-op there. At zebra shutdown, ++ * however, dplane delete events do not arrive, so we need to ++ * release the VNI table here or the L2 VNI entries leak. ++ * ++ * Must guard on IS_ZEBRA_VXLAN_IF_SVD: for per-VNI VXLAN ++ * interfaces vni_info->vni_table aliases the embedded ++ * zebra_vxlan_vni struct via a union, so reading it as a hash ++ * pointer would dereference garbage. ++ */ ++ if (IS_ZEBRA_IF_VXLAN(ifp) && IS_ZEBRA_VXLAN_IF_SVD(zebra_if)) ++ zebra_vxlan_if_vni_table_destroy(zebra_if); + zebra_evpn_if_cleanup(zebra_if); + zebra_evpn_mac_ifp_del(ifp); + +diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c +index 2c95342ac587..de602f529024 100644 +--- a/zebra/zebra_evpn_mh.c ++++ b/zebra/zebra_evpn_mh.c +@@ -826,7 +826,7 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id, + void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, + struct zebra_if *vxlan_zif) + { +- struct interface *br_if; ++ ifindex_t bridge_ifindex; + struct zebra_evpn_access_bd *acc_bd; + uint8_t tmp_cnt; + +@@ -836,11 +836,22 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, + if (!vni_id) + return; + +- br_if = vxlan_zif->brslave_info.br_if; +- if (!br_if) ++ /* ++ * Use the stored bridge_ifindex rather than dereferencing ++ * brslave_info.br_if. At zebra shutdown, vrf_terminate_single() -> ++ * if_terminate() walks the per-VRF interface tree by name and frees ++ * interfaces one by one; if the bridge is freed before the VxLAN ++ * interface, the back pointer becomes a dangling reference. The ++ * bridge_ifindex field is never invalidated this way, and ++ * zebra_evpn_acc_vl_find_index() looks up the access-bd directly by ++ * (vid, ifindex), avoiding the heap-use-after-free in ++ * zebra_evpn_acc_vl_find()'s br_if->ifindex deref. ++ */ ++ bridge_ifindex = vxlan_zif->brslave_info.bridge_ifindex; ++ if (bridge_ifindex == IFINDEX_INTERNAL) + return; + +- acc_bd = zebra_evpn_acc_vl_find(vid, br_if); ++ acc_bd = zebra_evpn_acc_vl_find_index(vid, bridge_ifindex); + if (!acc_bd) + return; + if (acc_bd->vni_refcnt > 1) { +@@ -857,8 +868,8 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) +- zlog_debug("access vlan %d bridge %s vni %u deref", acc_bd->vid, +- br_if->name, vni_id); ++ zlog_debug("access vlan %d bridge ifindex %u vni %u deref", acc_bd->vid, ++ bridge_ifindex, vni_id); + + if (acc_bd->zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, acc_bd->zevpn); +diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c +index 3325532ca920..c6bb8e619d10 100644 +--- a/zebra/zebra_mpls.c ++++ b/zebra/zebra_mpls.c +@@ -4055,6 +4055,19 @@ static void lsp_table_free(void *p) + XFREE(MTYPE_LSP, lsp); + } + ++static void zebra_mpls_fec_node_cleanup(struct route_table *table, struct route_node *node) ++{ ++ struct zebra_fec *fec; ++ ++ if (!node->info) ++ return; ++ ++ fec = node->info; ++ list_delete(&fec->client_list); ++ XFREE(MTYPE_FEC, fec); ++ node->info = NULL; ++} ++ + /* + * Called upon process exiting, need to delete LSP forwarding + * entries from the kernel. +@@ -4089,6 +4102,8 @@ void zebra_mpls_init_tables(struct zebra_vrf *zvrf) + zvrf->lsp_table = hash_create_size(8, label_hash, label_cmp, buffer); + zvrf->fec_table[AFI_IP] = route_table_init(); + zvrf->fec_table[AFI_IP6] = route_table_init(); ++ zvrf->fec_table[AFI_IP]->cleanup = zebra_mpls_fec_node_cleanup; ++ zvrf->fec_table[AFI_IP6]->cleanup = zebra_mpls_fec_node_cleanup; + zvrf->mpls_flags = 0; + zvrf->mpls_srgb.start_label = MPLS_DEFAULT_MIN_SRGB_LABEL; + zvrf->mpls_srgb.end_label = MPLS_DEFAULT_MAX_SRGB_LABEL; +diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c +index cdc05fe2ab1f..93e067f8308f 100644 +--- a/zebra/zebra_routemap.c ++++ b/zebra/zebra_routemap.c +@@ -1196,10 +1196,32 @@ void zebra_route_map_set_delay_timer(uint32_t value) + + void zebra_routemap_finish(void) + { ++ afi_t afi; ++ safi_t safi; ++ uint32_t table; ++ + /* Set zebra_rmap_update_timer to 0 so that it wont schedule again */ + zebra_rmap_update_timer = 0; + /* Thread off if any scheduled already */ + event_cancel(&zebra_t_rmap_update); ++ ++ /* ++ * Release any per-import-table route-map name strings that were ++ * never explicitly removed via "no ip import-table ...". The import ++ * table machinery only frees the string when the import is torn down ++ * at runtime, so on a clean SIGTERM shutdown the configured names ++ * leak. ++ */ ++ for (afi = AFI_IP; afi < AFI_MAX; afi++) { ++ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { ++ for (table = 0; table < ZEBRA_KERNEL_TABLE_MAX; table++) { ++ if (zebra_import_table_routemap[afi][safi][table]) ++ XFREE(MTYPE_ROUTE_MAP_NAME, ++ zebra_import_table_routemap[afi][safi][table]); ++ } ++ } ++ } ++ + route_map_finish(); + } + +-- +2.47.3 + diff --git a/debian/patches/upstream/0021-ldpd-Fixup-memory-leaks-on-shutdown.patch b/debian/patches/upstream/0021-ldpd-Fixup-memory-leaks-on-shutdown.patch new file mode 100644 index 000000000000..000a9129cd3f --- /dev/null +++ b/debian/patches/upstream/0021-ldpd-Fixup-memory-leaks-on-shutdown.patch @@ -0,0 +1,207 @@ +From 364545ba9db021b924c047006b584eff47bc3290 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 16:03:37 -0400 +Subject: [PATCH 17/21] ldpd: Fixup memory leaks on shutdown + +a) zclient memory not cleaned up on shutdown, fix. +b) libfrr memory not cleaned up on shutdown, fix. +c) lde label list not cleaned up on shutdown, fix. +d) snmp memory not cleaned up on shutdown, fix. +e) accept memory not cleaned up on shutdown, fix. + +Signed-off-by: Donald Sharp +--- + ldpd/lde.c | 40 ++++++++++++++++++++++++++++++++++++---- + ldpd/ldp_snmp.c | 8 ++++++++ + ldpd/ldpd.c | 17 +++++++++++++++++ + ldpd/ldpe.c | 15 ++++++++++++--- + 4 files changed, 73 insertions(+), 7 deletions(-) + +diff --git a/ldpd/lde.c b/ldpd/lde.c +index 83bc806e49a2..6dc48b17a239 100644 +--- a/ldpd/lde.c ++++ b/ldpd/lde.c +@@ -48,7 +48,9 @@ static int lde_address_add(struct lde_nbr *, struct lde_addr *); + static int lde_address_del(struct lde_nbr *, struct lde_addr *); + static void lde_address_list_free(struct lde_nbr *); + static void zclient_sync_init(void); ++static void zclient_sync_cleanup(void); + static void lde_label_list_init(void); ++static void lde_label_list_cleanup(void); + static int lde_get_label_chunk(void); + static void on_get_label_chunk_response(uint32_t start, uint32_t end); + static uint32_t lde_get_next_label(void); +@@ -194,6 +196,8 @@ static FRR_NORETURN void lde_shutdown(void) + lde_gc_stop_timer(); + lde_nbr_clear(); + fec_tree_clear(); ++ lde_label_list_cleanup(); ++ zclient_sync_cleanup(); + + config_clear(ldeconf); + +@@ -203,7 +207,8 @@ static FRR_NORETURN void lde_shutdown(void) + + log_info("label decision engine exiting"); + +- zlog_fini(); ++ frr_early_fini(); ++ frr_fini(); + exit(0); + } + +@@ -2219,14 +2224,22 @@ static void zclient_sync_init(void) + retry: + + /* Discard failed zclient object */ +- zclient_stop(zclient_sync); +- zclient_free(zclient_sync); +- zclient_sync = NULL; ++ zclient_sync_cleanup(); + + /* Retry using a timer */ + event_add_timer(master, zclient_sync_retry, NULL, 1, NULL); + } + ++static void zclient_sync_cleanup(void) ++{ ++ if (!zclient_sync) ++ return; ++ ++ zclient_stop(zclient_sync); ++ zclient_free(zclient_sync); ++ zclient_sync = NULL; ++} ++ + static void + lde_del_label_chunk(void *val) + { +@@ -2278,6 +2291,25 @@ lde_label_list_init(void) + } + } + ++static void ++lde_label_list_cleanup(void) ++{ ++ struct listnode *node, *nnode; ++ struct label_chunk *label_chunk; ++ ++ if (!label_chunk_list) ++ return; ++ ++ for (ALL_LIST_ELEMENTS(label_chunk_list, node, nnode, label_chunk)) { ++ if (zclient_sync && ++ lde_release_label_chunk(label_chunk->start, label_chunk->end) != 0) ++ log_warnx("%s: Error releasing label chunk!", __func__); ++ } ++ ++ current_label_chunk = NULL; ++ list_delete(&label_chunk_list); ++} ++ + static void + on_get_label_chunk_response(uint32_t start, uint32_t end) + { +diff --git a/ldpd/ldp_snmp.c b/ldpd/ldp_snmp.c +index 2e7933f95c94..c289080195cc 100644 +--- a/ldpd/ldp_snmp.c ++++ b/ldpd/ldp_snmp.c +@@ -1173,6 +1173,12 @@ static int ldp_snmp_nbr_state_change(struct nbr * nbr, int old_state) + return 0; + } + ++static int ldp_snmp_terminate(void) ++{ ++ smux_terminate(); ++ return 0; ++} ++ + static int ldp_snmp_init(struct event_loop *tm) + { + hook_register(agentx_enabled, ldp_snmp_agentx_enabled); +@@ -1208,6 +1214,8 @@ static int ldp_snmp_register_mib(struct event_loop *tm) + + static int ldp_snmp_module_init(void) + { ++ hook_register(frr_fini, ldp_snmp_terminate); ++ + if (ldpd_process == PROC_MAIN) + hook_register(frr_late_init, ldp_snmp_init); + else +diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c +index 8675edf1ddf8..0d1698cdd288 100644 +--- a/ldpd/ldpd.c ++++ b/ldpd/ldpd.c +@@ -2026,6 +2026,7 @@ void + config_clear(struct ldpd_conf *conf) + { + struct ldpd_conf *xconf; ++ struct tnbr *tnbr; + + /* + * Merge current config with an empty config, this will deactivate +@@ -2041,5 +2042,21 @@ config_clear(struct ldpd_conf *conf) + xconf->flags = conf->flags; + merge_config(conf, xconf); + free(xconf); ++ ++ /* ++ * merge_tnbrs() only walks tnbrs that have F_TNBR_CONFIGURED set; ++ * dynamic targeted neighbours learned from received hello packets ++ * (F_TNBR_DYNAMIC, recv_hello() in hello.c) and rlfa tnbrs created ++ * by ldpe_rlfa_init() in rlfa.c are skipped, so they remain in the ++ * tree and would leak when conf is freed below. Drain the tree ++ * explicitly before letting the container go. ++ */ ++ while (!RB_EMPTY(tnbr_head, &conf->tnbr_tree)) { ++ tnbr = RB_ROOT(tnbr_head, &conf->tnbr_tree); ++ event_cancel(&tnbr->hello_timer); ++ RB_REMOVE(tnbr_head, &conf->tnbr_tree, tnbr); ++ free(tnbr); ++ } ++ + free(conf); + } +diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c +index f88d566b4188..b5e0f2dea946 100644 +--- a/ldpd/ldpe.c ++++ b/ldpd/ldpe.c +@@ -152,6 +152,16 @@ ldpe_init(struct ldpd_init *init) + zprivs_preinit(&ldpe_privs); + zprivs_init(&ldpe_privs); + ++ /* ++ * Initialise the accept_queue before any caller of accept_add() so we ++ * don't LIST_INIT-clobber entries that have already been registered ++ * (the control socket registers one in control_listen() below). When ++ * the orphaned entry is later left on a dangling list head, accept_del() ++ * at shutdown can't find the fd and the struct accept_ev allocation ++ * leaks. ++ */ ++ accept_init(); ++ + /* listen on ldpd control socket */ + strlcpy(ctl_sock_path, init->ctl_sock_path, sizeof(ctl_sock_path)); + if (control_init(ctl_sock_path) == -1) +@@ -177,8 +187,6 @@ ldpe_init(struct ldpd_init *init) + + if ((pkt_ptr = calloc(1, IBUF_READ_SIZE)) == NULL) + fatal(__func__); +- +- accept_init(); + } + + static FRR_NORETURN void ldpe_shutdown(void) +@@ -231,7 +239,8 @@ static FRR_NORETURN void ldpe_shutdown(void) + + log_info("ldp engine exiting"); + +- zlog_fini(); ++ frr_early_fini(); ++ frr_fini(); + + exit(0); + } +-- +2.47.3 + diff --git a/debian/patches/upstream/0022-Cleanup-snmp-memory-leaks.patch b/debian/patches/upstream/0022-Cleanup-snmp-memory-leaks.patch new file mode 100644 index 000000000000..d11209c96cc7 --- /dev/null +++ b/debian/patches/upstream/0022-Cleanup-snmp-memory-leaks.patch @@ -0,0 +1,121 @@ +From f34c158e097ca662b8aab6f074c31fa419d9a15e Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Fri, 1 May 2026 16:40:34 -0400 +Subject: [PATCH 18/21] *: Cleanup snmp memory leaks + +bgpd, ospf6d, ospfd and zebra were not cleaning up snmp smux +data structures no shutdown. Do so. + +Signed-off-by: Donald Sharp +--- + bgpd/bgp_snmp.c | 7 +++++++ + ospf6d/ospf6_snmp.c | 7 +++++++ + ospfd/ospf_snmp.c | 17 +++++++++++++++++ + zebra/zebra_snmp.c | 7 +++++++ + 4 files changed, 38 insertions(+) + +diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c +index eff7c5e0f653..03b5798770e6 100644 +--- a/bgpd/bgp_snmp.c ++++ b/bgpd/bgp_snmp.c +@@ -144,11 +144,18 @@ static int bgp_snmp_init(struct event_loop *tm) + return 0; + } + ++static int bgp_snmp_terminate(void) ++{ ++ smux_terminate(); ++ return 0; ++} ++ + static int bgp_snmp_module_init(void) + { + hook_register(peer_status_changed, bgpTrapEstablished); + hook_register(peer_backward_transition, bgpTrapBackwardTransition); + hook_register(frr_late_init, bgp_snmp_init); ++ hook_register(frr_fini, bgp_snmp_terminate); + hook_register(bgp_snmp_traps_config_write, + bgp_cli_snmp_traps_config_write); + return 0; +diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c +index f00d1dc125ca..7ac701973a98 100644 +--- a/ospf6d/ospf6_snmp.c ++++ b/ospf6d/ospf6_snmp.c +@@ -1429,11 +1429,18 @@ static int ospf6_snmp_init(struct event_loop *mstr) + return 0; + } + ++static int ospf6_snmp_terminate(void) ++{ ++ smux_terminate(); ++ return 0; ++} ++ + static int ospf6_snmp_module_init(void) + { + hook_register(ospf6_interface_change, ospf6TrapIfStateChange); + hook_register(ospf6_neighbor_change, ospf6TrapNbrStateChange); + hook_register(frr_late_init, ospf6_snmp_init); ++ hook_register(frr_fini, ospf6_snmp_terminate); + return 0; + } + +diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c +index 83cd45788da1..084b02ff4f48 100644 +--- a/ospfd/ospf_snmp.c ++++ b/ospfd/ospf_snmp.c +@@ -2533,6 +2533,22 @@ static int ospf_snmp_init(struct event_loop *tm) + return 0; + } + ++static int ospf_snmp_terminate(void) ++{ ++ if (ospf_snmp_iflist) { ++ ospf_snmp_iflist->del = (void (*)(void *))ospf_snmp_if_free; ++ list_delete(&ospf_snmp_iflist); ++ } ++ ++ if (ospf_snmp_vl_table) { ++ route_table_finish(ospf_snmp_vl_table); ++ ospf_snmp_vl_table = NULL; ++ } ++ ++ smux_terminate(); ++ return 0; ++} ++ + static int ospf_snmp_module_init(void) + { + hook_register(ospf_if_update, ospf_snmp_if_update); +@@ -2543,6 +2559,7 @@ static int ospf_snmp_module_init(void) + hook_register(ospf_nsm_change, ospf_snmp_nsm_change); + + hook_register(frr_late_init, ospf_snmp_init); ++ hook_register(frr_fini, ospf_snmp_terminate); + return 0; + } + +diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c +index 06a9c0debec1..1fd1f3820823 100644 +--- a/zebra/zebra_snmp.c ++++ b/zebra/zebra_snmp.c +@@ -546,9 +546,16 @@ static int zebra_snmp_init(struct event_loop *tm) + return 0; + } + ++static int zebra_snmp_terminate(void) ++{ ++ smux_terminate(); ++ return 0; ++} ++ + static int zebra_snmp_module_init(void) + { + hook_register(frr_late_init, zebra_snmp_init); ++ hook_register(frr_fini, zebra_snmp_terminate); + return 0; + } + +-- +2.47.3 + diff --git a/debian/patches/upstream/0023-pimd-cleanup-of-leaked-memory-on-shutdown.patch b/debian/patches/upstream/0023-pimd-cleanup-of-leaked-memory-on-shutdown.patch new file mode 100644 index 000000000000..f20acc964ca9 --- /dev/null +++ b/debian/patches/upstream/0023-pimd-cleanup-of-leaked-memory-on-shutdown.patch @@ -0,0 +1,41 @@ +From 7fb6badbe9170bddee2ba03058eb50181fed505b Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Sat, 2 May 2026 14:50:20 -0400 +Subject: [PATCH 19/21] pimd: cleanup of leaked memory on shutdown + +a) route-map memory was not being cleaned up +b) Some mld memory was not being cleaned up + +Signed-off-by: Donald Sharp +--- + pimd/pim6_main.c | 1 + + pimd/pim6_mld.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/pimd/pim6_main.c b/pimd/pim6_main.c +index 1d94f9d27eb6..2cf095f5c439 100644 +--- a/pimd/pim6_main.c ++++ b/pimd/pim6_main.c +@@ -197,6 +197,7 @@ static void pim6_terminate(void) + + prefix_list_reset(); + access_list_reset(); ++ pim_route_map_terminate(); + + zclient = pim_zebra_zclient_get(); + if (zclient) { +diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c +index 5e4f18af8aac..ce8fc1cd3ad4 100644 +--- a/pimd/pim6_mld.c ++++ b/pimd/pim6_mld.c +@@ -2381,6 +2381,7 @@ void gm_ifp_teardown(struct interface *ifp) + + gm_group_delete(gm_ifp); + ++ gm_gsq_pends_fini(gm_ifp->gsq_pends); + gm_grp_pends_fini(gm_ifp->grp_pends); + gm_packet_expires_fini(gm_ifp->expires); + gm_subscribers_fini(gm_ifp->subscribers); +-- +2.47.3 + diff --git a/debian/patches/upstream/0024-tests-make-the-topotest-fail-if-a-memory-leak-is-det.patch b/debian/patches/upstream/0024-tests-make-the-topotest-fail-if-a-memory-leak-is-det.patch new file mode 100644 index 000000000000..92b3f8a2caa8 --- /dev/null +++ b/debian/patches/upstream/0024-tests-make-the-topotest-fail-if-a-memory-leak-is-det.patch @@ -0,0 +1,126 @@ +From d2f1cd9158eb960b4e1d120e2992aa41199066f7 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Sat, 2 May 2026 15:16:55 -0400 +Subject: [PATCH 20/21] tests: make the topotest fail if a memory leak is + detected + +Currently the topotests output a bunch of memory leaks detected +on shutdown, modify the code such that the memory leaks are detected +and cause the test to fail. + +Signed-off-by: Donald Sharp +--- + tests/topotests/lib/topogen.py | 21 ++++++++++++++++----- + tests/topotests/lib/topotest.py | 22 +++++++++++++++------- + 2 files changed, 31 insertions(+), 12 deletions(-) + +diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py +index fff9d492072c..33be0d292dc2 100644 +--- a/tests/topotests/lib/topogen.py ++++ b/tests/topotests/lib/topogen.py +@@ -487,14 +487,17 @@ class Topogen(object): + errors = "" + for gear in self.gears.values(): + errors += gear.stop() +- if len(errors) > 0: +- logger.error( +- "Errors found post shutdown - details follow: {}".format(errors) +- ) + ++ # Always tear down the underlying munet topology before reporting any ++ # gear-level errors back to pytest. gear.stop() only kills the FRR ++ # daemons inside each router; self.net.stop() (Mininet.delete()) is ++ # what actually reaps the per-gear mutini.py namespace processes, ++ # removes the bridge(s), and unmounts the per-router tmpfs/cgroup ++ # binds. If we assert before this runs (e.g. on a memory-leak ++ # report from gear.stop()), those mutini.py PID-1 processes - and ++ # the namespaces / mounts they anchor - are leaked across runs. + try: + self.net.stop() +- + except OSError as error: + # OSError exception is raised when mininet tries to stop switch + # though switch is stopped once but mininet tries to stop same +@@ -503,6 +506,14 @@ class Topogen(object): + logger.info(error) + logger.info("Exception ignored: switch is already stopped") + ++ if len(errors) > 0: ++ logger.error( ++ "Errors found post shutdown - details follow: {}".format(errors) ++ ) ++ assert False, "Errors found post shutdown - details follow:\n{}".format( ++ errors ++ ) ++ + def get_exabgp_cmd(self): + if not self.exabgp_cmd: + self.exabgp_cmd = get_exabgp_cmd(self.net) +diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py +index 1989b7c94b35..e619771ca41a 100644 +--- a/tests/topotests/lib/topotest.py ++++ b/tests/topotests/lib/topotest.py +@@ -1914,7 +1914,6 @@ class Router(Node): + self.run_in_window("vtysh", title="vt-%s" % self.name) + + if self.unified_config: +- + # Check that none of the datastores are locked before proceeding + def check_datastores_unlocked(): + """Check that all datastores are unlocked""" +@@ -1993,7 +1992,8 @@ class Router(Node): + # Get global bundle data + if not self.path_exists("/etc/frr/support_bundle_commands.conf"): + logger.info( +- "No support bundle commands.conf found in %s namespace, copying them over", self.name ++ "No support bundle commands.conf found in %s namespace, copying them over", ++ self.name, + ) + # Copy global value if was covered by namespace mount + bundle_data = "" +@@ -2001,7 +2001,9 @@ class Router(Node): + with open("/etc/frr/support_bundle_commands.conf", "r") as rf: + bundle_data = rf.read() + else: +- logger.warning("No support bundle commands.conf found, please install them on this system") ++ logger.warning( ++ "No support bundle commands.conf found, please install them on this system" ++ ) + self.cmd_raises( + "cat > /etc/frr/support_bundle_commands.conf", + stdin=bundle_data, +@@ -2677,11 +2679,11 @@ class Router(Node): + dname, + ) + reportMade = True +- return reportMade ++ return reportMade, traces + + def checkRouterCores(self, reportLeaks=True, reportOnce=False): + if reportOnce and not self.reportCores: +- return ++ return "" + reportMade = False + traces = "" + for daemon in self.daemons: +@@ -2691,9 +2693,15 @@ class Router(Node): + and len(self.daemon_instances[daemon]) > 0 + ): + for inst in self.daemon_instances[daemon]: +- self.check_daemon(daemon, reportLeaks, traces, inst) ++ daemon_reported, traces = self.check_daemon( ++ daemon, reportLeaks, traces, inst ++ ) ++ reportMade = reportMade or daemon_reported + else: +- self.check_daemon(daemon, reportLeaks, traces) ++ daemon_reported, traces = self.check_daemon( ++ daemon, reportLeaks, traces ++ ) ++ reportMade = reportMade or daemon_reported + + if reportMade: + self.reportCores = False +-- +2.47.3 + diff --git a/debian/patches/upstream/0025-Use-hash_clean_and_free-remove-hash_free.patch b/debian/patches/upstream/0025-Use-hash_clean_and_free-remove-hash_free.patch new file mode 100644 index 000000000000..a5b62e0a1fd7 --- /dev/null +++ b/debian/patches/upstream/0025-Use-hash_clean_and_free-remove-hash_free.patch @@ -0,0 +1,480 @@ +From 0716102a0a7f755b33f2bab064fa4167570eb9a6 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Wed, 6 May 2026 10:54:28 -0400 +Subject: [PATCH 21/21] *: Use hash_clean_and_free, remove hash_free + +Ran across a single case of a hash bucket not being freed, +on shutdown. Let's convert everything to use hash_clean_and_free +and remove usage of hash_free outside of the .c file. + +Signed-off-by: Donald Sharp +--- + bfdd/bfd.c | 6 +++--- + bgpd/bgp_clist.c | 6 +++--- + bgpd/bgp_evpn.c | 3 +-- + bgpd/bgp_updgrp.c | 8 ++------ + bgpd/bgpd.c | 5 +---- + isisd/fabricd.c | 2 +- + isisd/isis_spf_private.h | 3 +-- + lib/hash.c | 34 +++++++++++++++++----------------- + lib/hash.h | 11 ----------- + lib/routemap.c | 13 +++++-------- + mgmtd/mgmt_fe_adapter.c | 2 +- + mgmtd/mgmt_txn.c | 3 +-- + nhrpd/nhrp_cache.c | 4 ++-- + nhrpd/nhrp_vc.c | 2 +- + pbrd/pbr_nht.c | 11 ++++------- + pimd/pim_igmp.c | 2 +- + pimd/pim_nht.c | 2 +- + zebra/kernel_netlink.c | 3 +-- + zebra/zebra_evpn.c | 6 ++---- + zebra/zebra_evpn_mh.c | 6 +++--- + zebra/zebra_vxlan.c | 13 +++++-------- + 21 files changed, 56 insertions(+), 89 deletions(-) + +diff --git a/bfdd/bfd.c b/bfdd/bfd.c +index 003052d843f1..36b2f70c1364 100644 +--- a/bfdd/bfd.c ++++ b/bfdd/bfd.c +@@ -2214,9 +2214,9 @@ void bfd_shutdown(void) + assert(sbfd_rflt_hash->count == 0); + + /* Now free the hashes themselves. */ +- hash_free(bfd_id_hash); +- hash_free(bfd_key_hash); +- hash_free(sbfd_rflt_hash); ++ hash_clean_and_free(&bfd_id_hash, NULL); ++ hash_clean_and_free(&bfd_key_hash, NULL); ++ hash_clean_and_free(&sbfd_rflt_hash, NULL); + + destroy_bfd_perm_vrfs_data(); + +diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c +index 9de32e85db5f..62244935f71c 100644 +--- a/bgpd/bgp_clist.c ++++ b/bgpd/bgp_clist.c +@@ -1456,21 +1456,21 @@ void community_list_terminate(struct community_list_handler *ch) + community_list_delete(cm, list); + while ((list = cm->str.head) != NULL) + community_list_delete(cm, list); +- hash_free(cm->hash); ++ hash_clean_and_free(&cm->hash, NULL); + + cm = &ch->lcommunity_list; + while ((list = cm->num.head) != NULL) + community_list_delete(cm, list); + while ((list = cm->str.head) != NULL) + community_list_delete(cm, list); +- hash_free(cm->hash); ++ hash_clean_and_free(&cm->hash, NULL); + + cm = &ch->extcommunity_list; + while ((list = cm->num.head) != NULL) + community_list_delete(cm, list); + while ((list = cm->str.head) != NULL) + community_list_delete(cm, list); +- hash_free(cm->hash); ++ hash_clean_and_free(&cm->hash, NULL); + + XFREE(MTYPE_COMMUNITY_LIST_HANDLER, ch); + } +diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c +index 4f8ee466bb74..f04242455527 100644 +--- a/bgpd/bgp_evpn.c ++++ b/bgpd/bgp_evpn.c +@@ -7999,8 +7999,7 @@ static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn) + (void (*)(struct hash_bucket *, void *))bgp_evpn_remote_ip_hash_free, + vpn); + +- hash_free(vpn->remote_ip_hash); +- vpn->remote_ip_hash = NULL; ++ hash_clean_and_free(&vpn->remote_ip_hash, NULL); + } + + /* Add a remote MAC/IP route to hash table */ +diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c +index e523528c86c4..03d4b6fe3148 100644 +--- a/bgpd/bgp_updgrp.c ++++ b/bgpd/bgp_updgrp.c +@@ -1961,12 +1961,8 @@ void update_bgp_group_free(struct bgp *bgp) + { + int afid; + +- AF_FOREACH (afid) { +- if (bgp->update_groups[afid]) { +- hash_free(bgp->update_groups[afid]); +- bgp->update_groups[afid] = NULL; +- } +- } ++ AF_FOREACH (afid) ++ hash_clean_and_free(&bgp->update_groups[afid], NULL); + } + + void update_group_show(struct bgp *bgp, afi_t afi, safi_t safi, struct vty *vty, +diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c +index e92fe29af871..dfbd8d00dca6 100644 +--- a/bgpd/bgpd.c ++++ b/bgpd/bgpd.c +@@ -4563,10 +4563,7 @@ void bgp_free(struct bgp *bgp) + list_delete(&bgp->group); + list_delete(&bgp->peer); + +- if (bgp->connectionhash) { +- hash_free(bgp->connectionhash); +- bgp->connectionhash = NULL; +- } ++ hash_clean_and_free(&bgp->connectionhash, NULL); + + FOREACH_AFI_SAFI (afi, safi) { + /* Special handling for 2-level routing tables. */ +diff --git a/isisd/fabricd.c b/isisd/fabricd.c +index 059ec0d278eb..62624e5789fb 100644 +--- a/isisd/fabricd.c ++++ b/isisd/fabricd.c +@@ -233,7 +233,7 @@ void fabricd_finish(struct fabricd *f) + isis_spftree_del(f->spftree); + neighbor_lists_clear(f); + skiplist_free(f->neighbors); +- hash_free(f->neighbors_neighbors); ++ hash_clean_and_free(&f->neighbors_neighbors, NULL); + } + + static void fabricd_initial_sync_timeout(struct event *event) +diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h +index fd09346c9d59..1d5ac953842a 100644 +--- a/isisd/isis_spf_private.h ++++ b/isisd/isis_spf_private.h +@@ -191,8 +191,7 @@ __attribute__((__unused__)) static void isis_vertex_queue_free(struct isis_verte + { + isis_vertex_queue_clear(queue); + +- hash_free(queue->hash); +- queue->hash = NULL; ++ hash_clean_and_free(&queue->hash, NULL); + + if (queue->insert_counter) { + skiplist_free(queue->l.slist); +diff --git a/lib/hash.c b/lib/hash.c +index edbfeec4645e..6ecbf03ab3f0 100644 +--- a/lib/hash.c ++++ b/lib/hash.c +@@ -297,6 +297,23 @@ void hash_clean(struct hash *hash, void (*free_func)(void *)) + hash->stats.empty = hash->size; + } + ++static void hash_free(struct hash *hash) ++{ ++ frr_with_mutex (&_hashes_mtx) { ++ if (_hashes) { ++ listnode_delete(_hashes, hash); ++ if (_hashes->count == 0) { ++ list_delete(&_hashes); ++ } ++ } ++ } ++ ++ XFREE(MTYPE_HASH, hash->name); ++ ++ XFREE(MTYPE_HASH_INDEX, hash->index); ++ XFREE(MTYPE_HASH, hash); ++} ++ + void hash_clean_and_free(struct hash **hash, void (*free_func)(void *)) + { + if (!*hash) +@@ -322,23 +339,6 @@ struct list *hash_to_list(struct hash *hash) + return list; + } + +-void hash_free(struct hash *hash) +-{ +- frr_with_mutex (&_hashes_mtx) { +- if (_hashes) { +- listnode_delete(_hashes, hash); +- if (_hashes->count == 0) { +- list_delete(&_hashes); +- } +- } +- } +- +- XFREE(MTYPE_HASH, hash->name); +- +- XFREE(MTYPE_HASH_INDEX, hash->index); +- XFREE(MTYPE_HASH, hash); +-} +- + + /* CLI commands ------------------------------------------------------------ */ + +diff --git a/lib/hash.h b/lib/hash.h +index f9584ae109fa..bee91bcc7f27 100644 +--- a/lib/hash.h ++++ b/lib/hash.h +@@ -302,17 +302,6 @@ extern void hash_clean(struct hash *hash, void (*free_func)(void *data)); + */ + extern void hash_clean_and_free(struct hash **hash, void (*free_func)(void *data)); + +-/* +- * Delete a hash table. +- * +- * This function assumes the table is empty. Call hash_clean to delete the +- * hashtable contents if necessary. +- * +- * hash +- * hash table to delete +- */ +-extern void hash_free(struct hash *hash); +- + /* + * Converts a hash table to an unsorted linked list. + * Does not modify the hash table in any way. +diff --git a/lib/routemap.c b/lib/routemap.c +index 67600b1f89d8..d7eefd2317c2 100644 +--- a/lib/routemap.c ++++ b/lib/routemap.c +@@ -2850,7 +2850,7 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) + } + if (!dep->dep_rmap_hash->count) { + dep = hash_release(dep->this_hash, (void *)dep->dep_name); +- hash_free(dep->dep_rmap_hash); ++ hash_clean_and_free(&dep->dep_rmap_hash, NULL); + XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); + XFREE(MTYPE_ROUTE_MAP_DEP, dep); + } +@@ -3002,7 +3002,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, + + if (!dep->dep_rmap_hash->count) { + dep = hash_release(dephash, dname); +- hash_free(dep->dep_rmap_hash); ++ hash_clean_and_free(&dep->dep_rmap_hash, NULL); + XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); + XFREE(MTYPE_ROUTE_MAP_DEP, dep); + } +@@ -3338,13 +3338,10 @@ void route_map_finish(void) + route_map_delete(map); + } + +- for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) { +- hash_free(route_map_dep_hash[i]); +- route_map_dep_hash[i] = NULL; +- } ++ for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) ++ hash_clean_and_free(&route_map_dep_hash[i], NULL); + +- hash_free(route_map_master_hash); +- route_map_master_hash = NULL; ++ hash_clean_and_free(&route_map_master_hash, NULL); + } + + /* Increment the use_count counter while attaching the route map */ +diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c +index 891c28b5aa1b..6c43f01cd1c6 100644 +--- a/mgmtd/mgmt_fe_adapter.c ++++ b/mgmtd/mgmt_fe_adapter.c +@@ -2065,5 +2065,5 @@ void mgmt_fe_adapter_destroy(void) + + mgmt_fe_free_ns_strings(&mgmt_fe_ns_strings); + +- hash_free(mgmt_fe_sessions); ++ hash_clean_and_free(&mgmt_fe_sessions, NULL); + } +diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c +index 1d0756d4a782..554fd38cfc8b 100644 +--- a/mgmtd/mgmt_txn.c ++++ b/mgmtd/mgmt_txn.c +@@ -863,6 +863,5 @@ void mgmt_txn_destroy(void) + TXN_DECREF(txn); + } + +- if (txn_id_tab) +- hash_free(txn_id_tab); ++ hash_clean_and_free(&txn_id_tab, NULL); + } +diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c +index 13b05da5db58..dd71d3361ac1 100644 +--- a/nhrpd/nhrp_cache.c ++++ b/nhrpd/nhrp_cache.c +@@ -164,13 +164,13 @@ void nhrp_cache_interface_del(struct interface *ifp) + + if (nifp->cache_hash) { + hash_iterate(nifp->cache_hash, do_nhrp_cache_free, NULL); +- hash_free(nifp->cache_hash); ++ hash_clean_and_free(&nifp->cache_hash, NULL); + } + + if (nifp->cache_config_hash) { + hash_iterate(nifp->cache_config_hash, do_nhrp_cache_config_free, + NULL); +- hash_free(nifp->cache_config_hash); ++ hash_clean_and_free(&nifp->cache_config_hash, NULL); + } + } + +diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c +index 6f3346c95c74..ac6d1398f84e 100644 +--- a/nhrpd/nhrp_vc.c ++++ b/nhrpd/nhrp_vc.c +@@ -214,5 +214,5 @@ void nhrp_vc_terminate(void) + { + nhrp_vc_reset(); + hash_clean(nhrp_vc_hash, nhrp_vc_free); +- hash_free(nhrp_vc_hash); ++ hash_clean_and_free(&nhrp_vc_hash, NULL); + } +diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c +index 76fecc3f8866..da452f6040b3 100644 +--- a/pbrd/pbr_nht.c ++++ b/pbrd/pbr_nht.c +@@ -169,7 +169,7 @@ static bool pbr_nh_hash_equal(const void *arg1, const void *arg2) + static void pbr_nhgc_delete(struct pbr_nexthop_group_cache *p) + { + hash_iterate(p->nhh, pbr_nh_delete_iterate, NULL); +- hash_free(p->nhh); ++ hash_clean_and_free(&p->nhh, NULL); + XFREE(MTYPE_PBR_NHG, p); + } + +@@ -1498,19 +1498,16 @@ void pbr_nht_terminate(void) + { + if (pbr_nhg_hash) { + hash_clean(pbr_nhg_hash, (void (*)(void *))pbr_nhgc_delete); +- hash_free(pbr_nhg_hash); +- pbr_nhg_hash = NULL; ++ hash_clean_and_free(&pbr_nhg_hash, NULL); + } + + if (pbr_nhrc_hash) { + hash_clean(pbr_nhrc_hash, pbr_nhrc_delete); +- hash_free(pbr_nhrc_hash); +- pbr_nhrc_hash = NULL; ++ hash_clean_and_free(&pbr_nhrc_hash, NULL); + } + + if (pbr_nhg_allocated_id_hash) { + hash_clean(pbr_nhg_allocated_id_hash, NULL); +- hash_free(pbr_nhg_allocated_id_hash); +- pbr_nhg_allocated_id_hash = NULL; ++ hash_clean_and_free(&pbr_nhg_allocated_id_hash, NULL); + } + } +diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c +index a9565e692e30..ba128a7daf47 100644 +--- a/pimd/pim_igmp.c ++++ b/pimd/pim_igmp.c +@@ -1200,7 +1200,7 @@ void pim_igmp_if_fini(struct pim_interface *pim_ifp) + assert(!listcount(pim_ifp->gm_group_list)); + + list_delete(&pim_ifp->gm_group_list); +- hash_free(pim_ifp->gm_group_hash); ++ hash_clean_and_free(&pim_ifp->gm_group_hash, NULL); + + list_delete(&pim_ifp->gm_socket_list); + } +diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c +index 3a1527185f1e..2671fe43288d 100644 +--- a/pimd/pim_nht.c ++++ b/pimd/pim_nht.c +@@ -652,7 +652,7 @@ static void pim_nht_drop_maybe(struct pim_instance *pim, struct pim_nexthop_cach + + list_delete(&pnc->rp_list); + +- hash_free(pnc->upstream_hash); ++ hash_clean_and_free(&pnc->upstream_hash, NULL); + hash_release(pim->nht_hash, pnc); + + if (pnc->urib.nexthop) +diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c +index ea1337d3c143..fec0b017880b 100644 +--- a/zebra/kernel_netlink.c ++++ b/zebra/kernel_netlink.c +@@ -1863,8 +1863,7 @@ void kernel_router_terminate(void) + + pthread_mutex_destroy(&nlsock_mutex); + +- hash_free(nlsock_hash); +- nlsock_hash = NULL; ++ hash_clean_and_free(&nlsock_hash, NULL); + } + + #endif /* HAVE_NETLINK */ +diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c +index 258e818ec5d2..963fde4d262e 100644 +--- a/zebra/zebra_evpn.c ++++ b/zebra/zebra_evpn.c +@@ -1075,12 +1075,10 @@ int zebra_evpn_del(struct zebra_evpn *zevpn) + zevpn->svi_if = NULL; + + /* Free the neighbor hash table. */ +- hash_free(zevpn->neigh_table); +- zevpn->neigh_table = NULL; ++ hash_clean_and_free(&zevpn->neigh_table, NULL); + + /* Free the MAC hash table. */ +- hash_free(zevpn->mac_table); +- zevpn->mac_table = NULL; ++ hash_clean_and_free(&zevpn->mac_table, NULL); + + /* Remove references to the zevpn in the MH databases */ + if (zevpn->vxlan_if) +diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c +index de602f529024..0b174d803f34 100644 +--- a/zebra/zebra_evpn_mh.c ++++ b/zebra/zebra_evpn_mh.c +@@ -3972,9 +3972,9 @@ void zebra_evpn_mh_terminate(void) + + hash_iterate(zmh_info->evpn_vlan_table, + zebra_evpn_acc_vl_cleanup_all, NULL); +- hash_free(zmh_info->evpn_vlan_table); +- hash_free(zmh_info->nhg_table); +- hash_free(zmh_info->nh_ip_table); ++ hash_clean_and_free(&zmh_info->evpn_vlan_table, NULL); ++ hash_clean_and_free(&zmh_info->nhg_table, NULL); ++ hash_clean_and_free(&zmh_info->nh_ip_table, NULL); + bf_free(zmh_info->nh_id_bitmap); + + XFREE(MTYPE_ZMH_INFO, zrouter.mh_info); +diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c +index 26769979e874..ecf1eea0db56 100644 +--- a/zebra/zebra_vxlan.c ++++ b/zebra/zebra_vxlan.c +@@ -2075,12 +2075,10 @@ static int zl3vni_del(struct zebra_l3vni *zl3vni) + zl3vni->l2vnis = NULL; + + /* Free the rmac table */ +- hash_free(zl3vni->rmac_table); +- zl3vni->rmac_table = NULL; ++ hash_clean_and_free(&zl3vni->rmac_table, NULL); + + /* Free the nh table */ +- hash_free(zl3vni->nh_table); +- zl3vni->nh_table = NULL; ++ hash_clean_and_free(&zl3vni->nh_table, NULL); + + /* Free the VNI hash entry and allocated memory. */ + tmp_zl3vni = hash_release(zrouter.l3vni_table, zl3vni); +@@ -6031,11 +6029,10 @@ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) + if (!zvrf) + return; + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf); +- hash_free(zvrf->evpn_table); ++ hash_clean_and_free(&zvrf->evpn_table, NULL); + if (zvrf->vxlan_sg_table) { + zebra_vxlan_cleanup_sg_table(zvrf); +- hash_free(zvrf->vxlan_sg_table); +- zvrf->vxlan_sg_table = NULL; ++ hash_clean_and_free(&zvrf->vxlan_sg_table, NULL); + } + } + +@@ -6059,7 +6056,7 @@ void zebra_vxlan_terminate(void) + /* free l3vni table */ + void zebra_vxlan_disable(void) + { +- hash_free(zrouter.l3vni_table); ++ hash_clean_and_free(&zrouter.l3vni_table, NULL); + zebra_evpn_mh_terminate(); + } + +-- +2.47.3 + -- 2.47.3