From a6b07429a4b0e4959a33ad2e17de6da5017dc438 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 24 Apr 2018 16:35:00 +0200 Subject: [PATCH] bgpd: handle bgp pbr hash list destroy upon BGP destroy Upon BGP destroy, the hash list related to PBR are removed. The pbr_match entries, as well as the contained pbr_match_entries entries. Then the pbr_action entries. The order is important, since the former are referencing pbr_action. So the references must be removed, prior to remove pbr action. Also, the zebra associated contexts are removed. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_pbr.h | 4 ++- bgpd/bgp_zebra.c | 5 ++- bgpd/bgpd.c | 2 +- 4 files changed, 93 insertions(+), 3 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 88e929f047..04d6314fd7 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -309,6 +309,48 @@ static int bgp_pbr_build_and_validate_entry(struct prefix *p, return 0; } +static void bgp_pbr_match_entry_free(void *arg) +{ + struct bgp_pbr_match_entry *bpme; + + bpme = (struct bgp_pbr_match_entry *)arg; + + if (bpme->installed) { + bgp_send_pbr_ipset_entry_match(bpme, false); + bpme->installed = false; + bpme->backpointer = NULL; + } + XFREE(MTYPE_PBR_MATCH_ENTRY, bpme); +} + +static void bgp_pbr_match_free(void *arg) +{ + struct bgp_pbr_match *bpm; + + bpm = (struct bgp_pbr_match *)arg; + + hash_clean(bpm->entry_hash, bgp_pbr_match_entry_free); + + if (hashcount(bpm->entry_hash) == 0) { + /* delete iptable entry first */ + /* then delete ipset match */ + if (bpm->installed) { + if (bpm->installed_in_iptable) { + bgp_send_pbr_iptable(bpm->action, + bpm, false); + bpm->installed_in_iptable = false; + bpm->action->refcnt--; + } + bgp_send_pbr_ipset_match(bpm, false); + bpm->installed = false; + bpm->action = NULL; + } + } + hash_free(bpm->entry_hash); + + XFREE(MTYPE_PBR_MATCH, bpm); +} + static void *bgp_pbr_match_alloc_intern(void *arg) { struct bgp_pbr_match *bpm, *new; @@ -321,6 +363,24 @@ static void *bgp_pbr_match_alloc_intern(void *arg) return new; } +static void bgp_pbr_action_free(void *arg) +{ + struct bgp_pbr_action *bpa; + + bpa = (struct bgp_pbr_action *)arg; + + if (bpa->refcnt == 0) { + if (bpa->installed && bpa->table_id != 0) { + bgp_send_pbr_rule_action(bpa, false); + bgp_zebra_announce_default(bpa->bgp, &(bpa->nh), + AFI_IP, + bpa->table_id, + false); + } + } + XFREE(MTYPE_PBR_ACTION, bpa); +} + static void *bgp_pbr_action_alloc_intern(void *arg) { struct bgp_pbr_action *bpa, *new; @@ -515,6 +575,20 @@ struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id, return bpmiu.bpm_found; } +void bgp_pbr_cleanup(struct bgp *bgp) +{ + if (bgp->pbr_match_hash) { + hash_clean(bgp->pbr_match_hash, bgp_pbr_match_free); + hash_free(bgp->pbr_match_hash); + bgp->pbr_match_hash = NULL; + } + if (bgp->pbr_action_hash) { + hash_clean(bgp->pbr_action_hash, bgp_pbr_action_free); + hash_free(bgp->pbr_action_hash); + bgp->pbr_action_hash = NULL; + } +} + void bgp_pbr_init(struct bgp *bgp) { bgp->pbr_match_hash = @@ -685,6 +759,7 @@ static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa, bgp_send_pbr_iptable(bpm->action, bpm, false); bpm->installed_in_iptable = false; + bpm->action->refcnt--; } bgp_send_pbr_ipset_match(bpm, false); bpm->installed = false; @@ -695,6 +770,15 @@ static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa, * note that drop does not need to call send_pbr_action */ } + if (bpa->refcnt == 0) { + if (bpa->installed && bpa->table_id != 0) { + bgp_send_pbr_rule_action(bpa, false); + bgp_zebra_announce_default(bpa->bgp, &(bpa->nh), + AFI_IP, + bpa->table_id, + false); + } + } } struct bgp_pbr_match_entry_remain { @@ -821,6 +905,7 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, bpa->table_id = bpa->fwmark; bpa->installed = false; } + bpa->bgp = bgp; bpa->unique = ++bgp_pbr_action_counter_unique; /* 0 value is forbidden */ bpa->install_in_progress = false; diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index d82f125ec3..5129ada37b 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -215,7 +215,8 @@ struct bgp_pbr_action { bool installed; bool install_in_progress; - + uint32_t refcnt; + struct bgp *bgp; }; extern struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id, @@ -230,6 +231,7 @@ extern struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup( extern struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id, uint32_t unique); +extern void bgp_pbr_cleanup(struct bgp *bgp); extern void bgp_pbr_init(struct bgp *bgp); extern uint32_t bgp_pbr_action_hash_key(void *arg); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index a138f7cb2d..93a509c219 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2112,6 +2112,7 @@ static int iptable_notify_owner(int command, struct zclient *zclient, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received IPTABLE_INSTALLED", __PRETTY_FUNCTION__); + bgpm->action->refcnt++; break; case ZAPI_IPTABLE_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) @@ -2580,8 +2581,10 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, bgp_encode_pbr_iptable_match(s, pba, pbm); stream_putw_at(s, 0, stream_get_endp(s)); - if (!zclient_send_message(zclient) && install) + if (!zclient_send_message(zclient) && install) { pbm->install_iptable_in_progress = true; + pba->refcnt++; + } } /* inject in table a default route to: diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 3ed82ccd1e..a331fad5d4 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3403,7 +3403,7 @@ void bgp_free(struct bgp *bgp) bf_release_index(bm->rd_idspace, bgp->vrf_rd_id); bgp_evpn_cleanup(bgp); - + bgp_pbr_cleanup(bgp); if (bgp->name) XFREE(MTYPE_BGP, bgp->name); if (bgp->name_pretty) -- 2.39.5