bgpd: bgp_nexthop_cache not deleted with peers
* Fix mild leak, bgp_nexthop_caches were not deleted when their peer was.
Not a huge one, but makes valgrinding for other leaks noisier.
Credit to Lou Berger <lberger@labn.net> for doing the hard work of
debugging and pinning down the leak, and supplying an initial fix.
That one didn't quite get the refcounting right, it seemed, hence
this version.
This version also keeps bncs pinned so long as the peer is defined, where
Lou's tried to delete whenever the peer went through bgp_stop. That causes
lots of zebra traffic if down peers go Active->Connect->Active, etc., so
leaving bnc's in place until peer_delete seemed better.
* bgp_nht.c: (bgp_unlink_nexthop_by_peer) similar to bgp_unlink_nexthop, but
by peer.
* bgp_nht.c: (bgp_unlink_nexthop_check) helper to consolidate checking
if a bnc should be deleted.
(bgp_unlink_nexthop_by_peer) ensure the bnc->nht_info peer reference
is removed, and hence allow bncs to be removed by previous.
* bgpd.c: (peer_delete) cleanup the peer's bnc.
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 34b5fd1..591fbf9 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -65,16 +65,9 @@
return (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID));
}
-void
-bgp_unlink_nexthop (struct bgp_info *path)
+static void
+bgp_unlink_nexthop_check (struct bgp_nexthop_cache *bnc)
{
- struct bgp_nexthop_cache *bnc = path->nexthop;
-
- if (!bnc)
- return;
-
- path_nh_map(path, NULL, 0);
-
if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info)
{
if (BGP_DEBUG(nht, NHT))
@@ -86,10 +79,60 @@
unregister_nexthop(bnc);
bnc->node->info = NULL;
bgp_unlock_node(bnc->node);
+ bnc->node = NULL;
bnc_free(bnc);
}
}
+void
+bgp_unlink_nexthop (struct bgp_info *path)
+{
+ struct bgp_nexthop_cache *bnc = path->nexthop;
+
+ if (!bnc)
+ return;
+
+ path_nh_map(path, NULL, 0);
+
+ bgp_unlink_nexthop_check (bnc);
+}
+
+void
+bgp_unlink_nexthop_by_peer (struct peer *peer)
+{
+ struct prefix p;
+ struct bgp_node *rn;
+ struct bgp_nexthop_cache *bnc;
+ afi_t afi = family2afi(peer->su.sa.sa_family);
+
+ if (afi == AFI_IP)
+ {
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = peer->su.sin.sin_addr;
+ }
+ else if (afi == AFI_IP6)
+ {
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ p.u.prefix6 = peer->su.sin6.sin6_addr;
+ }
+ else
+ return;
+
+ rn = bgp_node_get (bgp_nexthop_cache_table[afi], &p);
+
+ if (!rn->info)
+ return;
+
+ bnc = rn->info;
+
+ /* cleanup the peer reference */
+ bnc->nht_info = NULL;
+
+ bgp_unlink_nexthop_check (bnc);
+}
+
int
bgp_find_or_add_nexthop (afi_t afi, struct bgp_info *ri, struct peer *peer,
int connected)
@@ -139,7 +182,7 @@
if (ri)
{
- path_nh_map(ri, bnc, 1);
+ path_nh_map(ri, bnc, 1); /* updates NHT ri list reference */
if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) && bnc->metric)
(bgp_info_extra_get(ri))->igpmetric = bnc->metric;
@@ -147,7 +190,7 @@
ri->extra->igpmetric = 0;
}
else if (peer)
- bnc->nht_info = (void *)peer;
+ bnc->nht_info = (void *)peer; /* NHT peer reference */
return (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID));
}
diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h
index 2bced7f..5086b08 100644
--- a/bgpd/bgp_nht.h
+++ b/bgpd/bgp_nht.h
@@ -54,5 +54,6 @@
* p - path structure.
*/
extern void bgp_unlink_nexthop(struct bgp_info *p);
+void bgp_unlink_nexthop_by_peer (struct peer *);
#endif /* _BGP_NHT_H */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index eacf804..018a599 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1297,7 +1297,10 @@
peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
bgp_stop (peer);
bgp_fsm_change_status (peer, Deleted);
-
+
+ /* Remove from NHT */
+ bgp_unlink_nexthop_by_peer (peer);
+
/* Password configuration */
if (peer->password)
{