include $(TOPDIR)/rules.mk
PKG_NAME:=frr
PKG_VERSION:=7.5
-PKG_RELEASE:=4
-PKG_SOURCE_DATE:=2021-02-05
+PKG_RELEASE:=5
+PKG_SOURCE_DATE:=2021-02-26
PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_DATE).tar.gz
-PKG_SOURCE_VERSION:=20b35e4c3386de798f3b0cb9f2a7e6b04d995485
+PKG_SOURCE_VERSION:=13a8efb4b6e3c92e8b9361c9cb1e78a86b0194cf
PKG_SOURCE_URL:=https://codeload.github.com/FRRouting/frr/tar.gz/$(PKG_SOURCE_VERSION)?
-PKG_HASH:=f3b4a4ce43ad60fcf4b908dc4467cbc9dae277b5829489b3d221913e043dd250
+PKG_HASH:=6e313edff69cd12444b53dbc5593892b280280b7735e620c00189a669f80bdcc
PKG_MAINTAINER:=Lucian Cristian <lucian.cristian@gmail.com>
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_SOURCE_VERSION)
--- /dev/null
+From fef2ed139d140f551cdfcbb21c5a023dea2e02cb Mon Sep 17 00:00:00 2001
+From: Philippe Guibert <philippe.guibert@6wind.com>
+Date: Thu, 26 Mar 2020 17:33:53 +0100
+Subject: [PATCH] nhrpd: cache config may disappear if iface not present at
+ startup
+
+When interface not present at config time, store separately the list of
+config parameters. Then, when interface is ready and an address has been configured, the nbma setting is done. Reversely, when interface disappears,
+there is no need to keep the maps present, then keep only the configuration.
+
+Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
+---
+ nhrpd/nhrp_cache.c | 86 ++++++++++++++++++++++++++++++++++++++++++
+ nhrpd/nhrp_interface.c | 63 ++++++++++++++++++++++++++++++-
+ nhrpd/nhrp_vty.c | 49 ++++++++++++++++--------
+ nhrpd/nhrpd.h | 14 +++++++
+ 4 files changed, 195 insertions(+), 17 deletions(-)
+
+--- a/nhrpd/nhrp_cache.c
++++ b/nhrpd/nhrp_cache.c
+@@ -16,6 +16,7 @@
+ #include "netlink.h"
+
+ DEFINE_MTYPE_STATIC(NHRPD, NHRP_CACHE, "NHRP cache entry")
++DEFINE_MTYPE_STATIC(NHRPD, NHRP_CACHE_CONFIG, "NHRP cache config entry")
+
+ unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
+
+@@ -77,6 +78,68 @@ static void nhrp_cache_free(struct nhrp_
+ XFREE(MTYPE_NHRP_CACHE, c);
+ }
+
++static unsigned int nhrp_cache_config_protocol_key(const void *peer_data)
++{
++ const struct nhrp_cache_config *p = peer_data;
++ return sockunion_hash(&p->remote_addr);
++}
++
++static bool nhrp_cache_config_protocol_cmp(const void *cache_data,
++ const void *key_data)
++{
++ const struct nhrp_cache_config *a = cache_data;
++ const struct nhrp_cache_config *b = key_data;
++
++ if (!sockunion_same(&a->remote_addr, &b->remote_addr))
++ return false;
++ if (a->ifp != b->ifp)
++ return false;
++ return true;
++}
++
++static void *nhrp_cache_config_alloc(void *data)
++{
++ struct nhrp_cache_config *p, *key = data;
++
++ p = XCALLOC(MTYPE_NHRP_CACHE_CONFIG, sizeof(struct nhrp_cache_config));
++
++ *p = (struct nhrp_cache_config){
++ .remote_addr = key->remote_addr,
++ .ifp = key->ifp,
++ };
++ return p;
++}
++
++void nhrp_cache_config_free(struct nhrp_cache_config *c)
++{
++ struct nhrp_interface *nifp = c->ifp->info;
++
++ hash_release(nifp->cache_config_hash, c);
++ XFREE(MTYPE_NHRP_CACHE_CONFIG, c);
++}
++
++struct nhrp_cache_config *nhrp_cache_config_get(struct interface *ifp,
++ union sockunion *remote_addr,
++ int create)
++{
++ struct nhrp_interface *nifp = ifp->info;
++ struct nhrp_cache_config key;
++
++ if (!nifp->cache_config_hash) {
++ nifp->cache_config_hash =
++ hash_create(nhrp_cache_config_protocol_key,
++ nhrp_cache_config_protocol_cmp,
++ "NHRP Config Cache");
++ if (!nifp->cache_config_hash)
++ return NULL;
++ }
++ key.remote_addr = *remote_addr;
++ key.ifp = ifp;
++
++ return hash_get(nifp->cache_config_hash, &key,
++ create ? nhrp_cache_config_alloc : NULL);
++}
++
+ struct nhrp_cache *nhrp_cache_get(struct interface *ifp,
+ union sockunion *remote_addr, int create)
+ {
+@@ -423,12 +486,23 @@ struct nhrp_cache_iterator_ctx {
+ void *ctx;
+ };
+
++struct nhrp_cache_config_iterator_ctx {
++ void (*cb)(struct nhrp_cache_config *, void *);
++ void *ctx;
++};
++
+ static void nhrp_cache_iterator(struct hash_bucket *b, void *ctx)
+ {
+ struct nhrp_cache_iterator_ctx *ic = ctx;
+ ic->cb(b->data, ic->ctx);
+ }
+
++static void nhrp_cache_config_iterator(struct hash_bucket *b, void *ctx)
++{
++ struct nhrp_cache_config_iterator_ctx *ic = ctx;
++ ic->cb(b->data, ic->ctx);
++}
++
+ void nhrp_cache_foreach(struct interface *ifp,
+ void (*cb)(struct nhrp_cache *, void *), void *ctx)
+ {
+@@ -441,6 +515,18 @@ void nhrp_cache_foreach(struct interface
+ hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic);
+ }
+
++void nhrp_cache_config_foreach(struct interface *ifp,
++ void (*cb)(struct nhrp_cache_config *, void *), void *ctx)
++{
++ struct nhrp_interface *nifp = ifp->info;
++ struct nhrp_cache_config_iterator_ctx ic = {
++ .cb = cb, .ctx = ctx,
++ };
++
++ if (nifp->cache_config_hash)
++ hash_iterate(nifp->cache_config_hash, nhrp_cache_config_iterator, &ic);
++}
++
+ void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n,
+ notifier_fn_t fn)
+ {
+--- a/nhrpd/nhrp_interface.c
++++ b/nhrpd/nhrp_interface.c
+@@ -23,6 +23,10 @@
+
+ DEFINE_MTYPE_STATIC(NHRPD, NHRP_IF, "NHRP interface")
+
++static void nhrp_interface_update_cache_config(struct interface *ifp,
++ bool available,
++ uint8_t family);
++
+ static int nhrp_if_new_hook(struct interface *ifp)
+ {
+ struct nhrp_interface *nifp;
+@@ -311,11 +315,68 @@ int nhrp_ifp_destroy(struct interface *i
+ {
+ debugf(NHRP_DEBUG_IF, "if-delete: %s", ifp->name);
+
++ nhrp_interface_update_cache_config(ifp, false, AF_INET);
++ nhrp_interface_update_cache_config(ifp, false, AF_INET6);
+ nhrp_interface_update(ifp);
+
+ return 0;
+ }
+
++struct map_ctx {
++ int family;
++ bool enabled;
++};
++
++static void interface_config_update_nhrp_map(struct nhrp_cache_config *cc, void *data)
++{
++ struct map_ctx *ctx = data;
++ struct interface *ifp = cc->ifp;
++ struct nhrp_cache *c;
++ union sockunion nbma_addr;
++
++ if (sockunion_family(&cc->remote_addr) != ctx->family)
++ return;
++
++ /* gre layer not ready */
++ if (ifp->vrf_id == VRF_UNKNOWN)
++ return;
++
++ c = nhrp_cache_get(ifp, &cc->remote_addr, ctx->enabled ? 1 : 0);
++ if (!c && !ctx->enabled)
++ return;
++ /* suppress */
++ if (!ctx->enabled) {
++ if (c && c->map) {
++ nhrp_cache_update_binding(c, c->cur.type, -1,
++ nhrp_peer_get(ifp, &nbma_addr), 0, NULL);
++ }
++ return;
++ }
++ /* create */
++ c->map = 1;
++ if (cc->type == NHRP_CACHE_LOCAL)
++ nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0,
++ NULL);
++ else {
++ nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0,
++ nhrp_peer_get(ifp, &cc->nbma), 0,
++ NULL);
++ }
++}
++
++static void nhrp_interface_update_cache_config(struct interface *ifp, bool available, uint8_t family)
++{
++ struct map_ctx mapctx;
++
++ mapctx = (struct map_ctx){
++ .family = family,
++ .enabled = available
++ };
++ nhrp_cache_config_foreach(ifp, interface_config_update_nhrp_map,
++ &mapctx);
++
++}
++
+ int nhrp_ifp_up(struct interface *ifp)
+ {
+ debugf(NHRP_DEBUG_IF, "if-up: %s", ifp->name);
+@@ -346,7 +407,7 @@ int nhrp_interface_address_add(ZAPI_CALL
+
+ nhrp_interface_update_address(
+ ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
+-
++ nhrp_interface_update_cache_config(ifc->ifp, true, PREFIX_FAMILY(ifc->address));
+ return 0;
+ }
+
+--- a/nhrpd/nhrp_vty.c
++++ b/nhrpd/nhrp_vty.c
+@@ -494,28 +494,42 @@ DEFUN(if_nhrp_map, if_nhrp_map_cmd,
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ afi_t afi = cmd_to_afi(argv[0]);
+ union sockunion proto_addr, nbma_addr;
++ struct nhrp_cache_config *cc;
+ struct nhrp_cache *c;
++ enum nhrp_cache_type type;
+
+ if (str2sockunion(argv[3]->arg, &proto_addr) < 0
+ || afi2family(afi) != sockunion_family(&proto_addr))
+ return nhrp_vty_return(vty, NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH);
+
++ if (strmatch(argv[4]->text, "local"))
++ type = NHRP_CACHE_LOCAL;
++ else {
++ if (str2sockunion(argv[4]->arg, &nbma_addr) < 0)
++ return nhrp_vty_return(vty, NHRP_ERR_FAIL);
++ type = NHRP_CACHE_STATIC;
++ }
++ cc = nhrp_cache_config_get(ifp, &proto_addr, 1);
++ if (!cc)
++ return nhrp_vty_return(vty, NHRP_ERR_FAIL);
++ cc->nbma = nbma_addr;
++ cc->type = type;
++ /* gre layer not ready */
++ if (ifp->ifindex == IFINDEX_INTERNAL)
++ return CMD_SUCCESS;
++
+ c = nhrp_cache_get(ifp, &proto_addr, 1);
+ if (!c)
+ return nhrp_vty_return(vty, NHRP_ERR_FAIL);
+
+ c->map = 1;
+- if (strmatch(argv[4]->text, "local")) {
++ if (type == NHRP_CACHE_LOCAL)
+ nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0,
+ NULL);
+- } else {
+- if (str2sockunion(argv[4]->arg, &nbma_addr) < 0)
+- return nhrp_vty_return(vty, NHRP_ERR_FAIL);
++ else
+ nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0,
+ nhrp_peer_get(ifp, &nbma_addr), 0,
+ NULL);
+- }
+-
+ return CMD_SUCCESS;
+ }
+
+@@ -533,15 +547,22 @@ DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ afi_t afi = cmd_to_afi(argv[1]);
+ union sockunion proto_addr, nbma_addr;
++ struct nhrp_cache_config *cc;
+ struct nhrp_cache *c;
+
+ if (str2sockunion(argv[4]->arg, &proto_addr) < 0
+ || afi2family(afi) != sockunion_family(&proto_addr))
+ return nhrp_vty_return(vty, NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH);
+
++ cc = nhrp_cache_config_get(ifp, &proto_addr, 0);
++ if (!cc)
++ return nhrp_vty_return(vty, NHRP_ERR_FAIL);
++ nhrp_cache_config_free(cc);
++
+ c = nhrp_cache_get(ifp, &proto_addr, 0);
++ /* silently return */
+ if (!c || !c->map)
+- return nhrp_vty_return(vty, NHRP_ERR_ENTRY_NOT_FOUND);
++ return CMD_SUCCESS;
+
+ nhrp_cache_update_binding(c, c->cur.type, -1,
+ nhrp_peer_get(ifp, &nbma_addr), 0, NULL);
+@@ -997,23 +1018,19 @@ struct write_map_ctx {
+ const char *aficmd;
+ };
+
+-static void interface_config_write_nhrp_map(struct nhrp_cache *c, void *data)
++static void interface_config_write_nhrp_map(struct nhrp_cache_config *c, void *data)
+ {
+ struct write_map_ctx *ctx = data;
+ struct vty *vty = ctx->vty;
+ char buf[2][SU_ADDRSTRLEN];
+
+- if (!c->map)
+- return;
+ if (sockunion_family(&c->remote_addr) != ctx->family)
+ return;
+
+ vty_out(vty, " %s nhrp map %s %s\n", ctx->aficmd,
+ sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0])),
+- c->cur.type == NHRP_CACHE_LOCAL
+- ? "local"
+- : sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1],
+- sizeof(buf[1])));
++ c->type == NHRP_CACHE_LOCAL
++ ? "local" : sockunion2str(&c->nbma, buf[1], sizeof(buf[1])));
+ }
+
+ static int interface_config_write(struct vty *vty)
+@@ -1076,8 +1093,8 @@ static int interface_config_write(struct
+ .family = afi2family(afi),
+ .aficmd = aficmd,
+ };
+- nhrp_cache_foreach(ifp, interface_config_write_nhrp_map,
+- &mapctx);
++ nhrp_cache_config_foreach(ifp, interface_config_write_nhrp_map,
++ &mapctx);
+
+ list_for_each_entry(nhs, &ad->nhslist_head,
+ nhslist_entry)
+--- a/nhrpd/nhrpd.h
++++ b/nhrpd/nhrpd.h
+@@ -197,6 +197,13 @@ enum nhrp_cache_type {
+ extern const char *const nhrp_cache_type_str[];
+ extern unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
+
++struct nhrp_cache_config {
++ struct interface *ifp;
++ union sockunion remote_addr;
++ enum nhrp_cache_type type;
++ union sockunion nbma;
++};
++
+ struct nhrp_cache {
+ struct interface *ifp;
+ union sockunion remote_addr;
+@@ -280,6 +287,7 @@ struct nhrp_interface {
+ uint32_t grekey;
+
+ struct hash *peer_hash;
++ struct hash *cache_config_hash;
+ struct hash *cache_hash;
+
+ struct notifier_list notifier_list;
+@@ -358,10 +366,16 @@ void nhrp_shortcut_foreach(afi_t afi,
+ void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force);
+ void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted);
+
++void nhrp_cache_config_free(struct nhrp_cache_config *c);
++struct nhrp_cache_config *nhrp_cache_config_get(struct interface *ifp,
++ union sockunion *remote_addr,
++ int create);
+ struct nhrp_cache *nhrp_cache_get(struct interface *ifp,
+ union sockunion *remote_addr, int create);
+ void nhrp_cache_foreach(struct interface *ifp,
+ void (*cb)(struct nhrp_cache *, void *), void *ctx);
++void nhrp_cache_config_foreach(struct interface *ifp,
++ void (*cb)(struct nhrp_cache_config *, void *), void *ctx);
+ void nhrp_cache_set_used(struct nhrp_cache *, int);
+ int nhrp_cache_update_binding(struct nhrp_cache *, enum nhrp_cache_type type,
+ int holding_time, struct nhrp_peer *p,
--- /dev/null
+From e5773617afba7408c76ec2683814ce076c72c79d Mon Sep 17 00:00:00 2001
+From: Mark Stapp <mjs@voltanet.io>
+Date: Tue, 8 Dec 2020 09:10:10 -0500
+Subject: [PATCH] nhrpd: fix SA warning in nhrp_interface
+
+Clear SA warning from recent nhrp cache code changes.
+
+Signed-off-by: Mark Stapp <mjs@voltanet.io>
+---
+ nhrpd/nhrp_interface.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+--- a/nhrpd/nhrp_interface.c
++++ b/nhrpd/nhrp_interface.c
+@@ -327,7 +327,8 @@ struct map_ctx {
+ bool enabled;
+ };
+
+-static void interface_config_update_nhrp_map(struct nhrp_cache_config *cc, void *data)
++static void interface_config_update_nhrp_map(struct nhrp_cache_config *cc,
++ void *data)
+ {
+ struct map_ctx *ctx = data;
+ struct interface *ifp = cc->ifp;
+@@ -344,15 +345,20 @@ static void interface_config_update_nhrp
+ c = nhrp_cache_get(ifp, &cc->remote_addr, ctx->enabled ? 1 : 0);
+ if (!c && !ctx->enabled)
+ return;
++
+ /* suppress */
+ if (!ctx->enabled) {
+ if (c && c->map) {
+- nhrp_cache_update_binding(c, c->cur.type, -1,
+- nhrp_peer_get(ifp, &nbma_addr), 0, NULL);
++ nhrp_cache_update_binding(
++ c, c->cur.type, -1,
++ nhrp_peer_get(ifp, &nbma_addr), 0, NULL);
+ }
+ return;
+ }
+- /* create */
++
++ /* Newly created */
++ assert(c != NULL);
++
+ c->map = 1;
+ if (cc->type == NHRP_CACHE_LOCAL)
+ nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0,
--- /dev/null
+From ee72f0a0eb93038ef6dfd01fed9f32e24c5de2a1 Mon Sep 17 00:00:00 2001
+From: Reuben Dowle <reuben.dowle@4rf.com>
+Date: Mon, 7 Dec 2020 16:35:13 +1300
+Subject: [PATCH] nhrpd: Cleanup resources when interface is deleted
+
+Currently when an interface is deleted from configuration, associated
+resources are not freed. This causes memory leaks and crashes.
+
+To reproduce this issue:
+* Connect to a DMVPN hub
+* Outside of frr, delete the underlying GRE interface
+* Use 'no interface xxx' to delete the interface containing nhrp configurations
+
+Signed-off-by: Reuben Dowle <reuben.dowle@4rf.com>
+---
+ nhrpd/nhrp_cache.c | 42 ++++++++++++++++++++++++++++++++++++++++--
+ nhrpd/nhrp_interface.c | 15 +++++++++++++++
+ nhrpd/nhrp_nhs.c | 18 ++++++++++++++++++
+ nhrpd/nhrp_peer.c | 27 +++++++++++++++++++++++++++
+ nhrpd/nhrpd.h | 3 +++
+ 5 files changed, 103 insertions(+), 2 deletions(-)
+ mode change 100644 => 100755 nhrpd/nhrp_cache.c
+ mode change 100644 => 100755 nhrpd/nhrp_interface.c
+ mode change 100644 => 100755 nhrpd/nhrpd.h
+
+--- a/nhrpd/nhrp_cache.c
++++ b/nhrpd/nhrp_cache.c
+@@ -69,12 +69,13 @@ static void nhrp_cache_free(struct nhrp_
+ {
+ struct nhrp_interface *nifp = c->ifp->info;
+
+- zassert(c->cur.type == NHRP_CACHE_INVALID && c->cur.peer == NULL);
+- zassert(c->new.type == NHRP_CACHE_INVALID && c->new.peer == NULL);
++ debugf(NHRP_DEBUG_COMMON, "Deleting cache entry");
+ nhrp_cache_counts[c->cur.type]--;
+ notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
+ zassert(!notifier_active(&c->notifier_list));
+ hash_release(nifp->cache_hash, c);
++ THREAD_OFF(c->t_timeout);
++ THREAD_OFF(c->t_auth);
+ XFREE(MTYPE_NHRP_CACHE, c);
+ }
+
+@@ -140,6 +141,41 @@ struct nhrp_cache_config *nhrp_cache_con
+ create ? nhrp_cache_config_alloc : NULL);
+ }
+
++static void do_nhrp_cache_free(struct hash_bucket *hb,
++ void *arg __attribute__((__unused__)))
++{
++ struct nhrp_cache *c = hb->data;
++
++ nhrp_cache_free(c);
++}
++
++static void do_nhrp_cache_config_free(struct hash_bucket *hb,
++ void *arg __attribute__((__unused__)))
++{
++ struct nhrp_cache_config *cc = hb->data;
++
++ nhrp_cache_config_free(cc);
++}
++
++void nhrp_cache_interface_del(struct interface *ifp)
++{
++ struct nhrp_interface *nifp = ifp->info;
++
++ debugf(NHRP_DEBUG_COMMON, "Cleaning up undeleted cache entries (%lu)",
++ nifp->cache_hash ? nifp->cache_hash->count : 0);
++
++ if (nifp->cache_hash) {
++ hash_iterate(nifp->cache_hash, do_nhrp_cache_free, NULL);
++ hash_free(nifp->cache_hash);
++ }
++
++ if (nifp->cache_config_hash) {
++ hash_iterate(nifp->cache_config_hash, do_nhrp_cache_config_free,
++ NULL);
++ hash_free(nifp->cache_config_hash);
++ }
++}
++
+ struct nhrp_cache *nhrp_cache_get(struct interface *ifp,
+ union sockunion *remote_addr, int create)
+ {
+@@ -164,6 +200,7 @@ struct nhrp_cache *nhrp_cache_get(struct
+ static int nhrp_cache_do_free(struct thread *t)
+ {
+ struct nhrp_cache *c = THREAD_ARG(t);
++
+ c->t_timeout = NULL;
+ nhrp_cache_free(c);
+ return 0;
+@@ -172,6 +209,7 @@ static int nhrp_cache_do_free(struct thr
+ static int nhrp_cache_do_timeout(struct thread *t)
+ {
+ struct nhrp_cache *c = THREAD_ARG(t);
++
+ c->t_timeout = NULL;
+ if (c->cur.type != NHRP_CACHE_INVALID)
+ nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+--- a/nhrpd/nhrp_interface.c
++++ b/nhrpd/nhrp_interface.c
+@@ -49,6 +49,21 @@ static int nhrp_if_new_hook(struct inter
+
+ static int nhrp_if_delete_hook(struct interface *ifp)
+ {
++ struct nhrp_interface *nifp = ifp->info;
++
++ debugf(NHRP_DEBUG_IF, "Deleted interface (%s)", ifp->name);
++
++ nhrp_cache_interface_del(ifp);
++ nhrp_nhs_interface_del(ifp);
++ nhrp_peer_interface_del(ifp);
++
++ if (nifp->ipsec_profile)
++ free(nifp->ipsec_profile);
++ if (nifp->ipsec_fallback_profile)
++ free(nifp->ipsec_fallback_profile);
++ if (nifp->source)
++ free(nifp->source);
++
+ XFREE(MTYPE_NHRP_IF, ifp->info);
+ return 0;
+ }
+--- a/nhrpd/nhrp_nhs.c
++++ b/nhrpd/nhrp_nhs.c
+@@ -378,6 +378,24 @@ int nhrp_nhs_free(struct nhrp_nhs *nhs)
+ return 0;
+ }
+
++void nhrp_nhs_interface_del(struct interface *ifp)
++{
++ struct nhrp_interface *nifp = ifp->info;
++ struct nhrp_nhs *nhs, *tmp;
++ afi_t afi;
++
++ for (afi = 0; afi < AFI_MAX; afi++) {
++ debugf(NHRP_DEBUG_COMMON, "Cleaning up nhs entries (%d)",
++ !list_empty(&nifp->afi[afi].nhslist_head));
++
++ list_for_each_entry_safe(nhs, tmp, &nifp->afi[afi].nhslist_head,
++ nhslist_entry)
++ {
++ nhrp_nhs_free(nhs);
++ }
++ }
++}
++
+ void nhrp_nhs_terminate(void)
+ {
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+--- a/nhrpd/nhrp_peer.c
++++ b/nhrpd/nhrp_peer.c
+@@ -38,11 +38,17 @@ static void nhrp_packet_debug(struct zbu
+
+ static void nhrp_peer_check_delete(struct nhrp_peer *p)
+ {
++ char buf[2][256];
+ struct nhrp_interface *nifp = p->ifp->info;
+
+ if (p->ref || notifier_active(&p->notifier_list))
+ return;
+
++ debugf(NHRP_DEBUG_COMMON, "Deleting peer ref:%d remote:%s local:%s",
++ p->ref,
++ sockunion2str(&p->vc->remote.nbma, buf[0], sizeof(buf[0])),
++ sockunion2str(&p->vc->local.nbma, buf[1], sizeof(buf[1])));
++
+ THREAD_OFF(p->t_fallback);
+ hash_release(nifp->peer_hash, p);
+ nhrp_interface_notify_del(p->ifp, &p->ifp_notifier);
+@@ -185,6 +191,27 @@ static void *nhrp_peer_create(void *data
+ return p;
+ }
+
++static void do_peer_hash_free(struct hash_bucket *hb,
++ void *arg __attribute__((__unused__)))
++{
++ struct nhrp_peer *p = hb->data;
++ nhrp_peer_check_delete(p);
++}
++
++void nhrp_peer_interface_del(struct interface *ifp)
++{
++ struct nhrp_interface *nifp = ifp->info;
++
++ debugf(NHRP_DEBUG_COMMON, "Cleaning up undeleted peer entries (%lu)",
++ nifp->peer_hash ? nifp->peer_hash->count : 0);
++
++ if (nifp->peer_hash) {
++ hash_iterate(nifp->peer_hash, do_peer_hash_free, NULL);
++ assert(nifp->peer_hash->count == 0);
++ hash_free(nifp->peer_hash);
++ }
++}
++
+ struct nhrp_peer *nhrp_peer_get(struct interface *ifp,
+ const union sockunion *remote_nbma)
+ {
+--- a/nhrpd/nhrpd.h
++++ b/nhrpd/nhrpd.h
+@@ -343,6 +343,7 @@ void nhrp_nhs_foreach(struct interface *
+ void (*cb)(struct nhrp_nhs *, struct nhrp_registration *,
+ void *),
+ void *ctx);
++void nhrp_nhs_interface_del(struct interface *ifp);
+
+ void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
+ void nhrp_route_announce(int add, enum nhrp_cache_type type,
+@@ -366,6 +367,7 @@ void nhrp_shortcut_foreach(afi_t afi,
+ void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force);
+ void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted);
+
++void nhrp_cache_interface_del(struct interface *ifp);
+ void nhrp_cache_config_free(struct nhrp_cache_config *c);
+ struct nhrp_cache_config *nhrp_cache_config_get(struct interface *ifp,
+ union sockunion *remote_addr,
+@@ -446,6 +448,7 @@ struct nhrp_reqid *nhrp_reqid_lookup(str
+
+ int nhrp_packet_init(void);
+
++void nhrp_peer_interface_del(struct interface *ifp);
+ struct nhrp_peer *nhrp_peer_get(struct interface *ifp,
+ const union sockunion *remote_nbma);
+ struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p);
--- /dev/null
+From f91ce319d3cdb465df54ff4e091dbd4aed85b24c Mon Sep 17 00:00:00 2001
+From: Mobashshera Rasool <mrasool@vmware.com>
+Date: Wed, 23 Dec 2020 13:20:22 +0000
+Subject: [PATCH] ospfd: Clear ip ospf process and clear ip ospf neighbor
+
+Implement the below 2 CLIs to clear the current data in the process
+and neighbor data structure.
+1. clear ip ospf process
+2. clear ip ospf neighbor
+
+Signed-off-by: Mobashshera Rasool <mrasool@vmware.com>
+---
+ doc/user/ospfd.rst | 17 ++++++++
+ ospfd/ospf_ase.c | 1 +
+ ospfd/ospf_lsa.c | 13 +++++-
+ ospfd/ospf_vty.c | 72 +++++++++++++++++++++++++++++++--
+ ospfd/ospfd.c | 99 ++++++++++++++++++++++++++++++++++++----------
+ ospfd/ospfd.h | 5 +++
+ 6 files changed, 182 insertions(+), 25 deletions(-)
+
+--- a/doc/user/ospfd.rst
++++ b/doc/user/ospfd.rst
+@@ -322,6 +322,23 @@ To start OSPF process you have to specif
+
+ This feature is enabled by default.
+
++.. index:: clear ip ospf [(1-65535)] process
++.. clicmd:: clear ip ospf [(1-65535)] process
++
++ This command can be used to clear the ospf process data structures. This
++ will clear the ospf neighborship as well and it will get re-established.
++ This will clear the LSDB too. This will be helpful when there is a change
++ in router-id and if user wants the router-id change to take effect, user can
++ use this cli instead of restarting the ospfd daemon.
++
++.. index:: clear ip ospf [(1-65535)] neighbor
++.. clicmd:: clear ip ospf [(1-65535)] neighbor
++
++ This command can be used to clear the ospf neighbor data structures. This
++ will clear the ospf neighborship and it will get re-established. This
++ command can be used when the neighbor state get stuck at some state and
++ this can be used to recover it from that state.
++
+ .. _ospf-area:
+
+ Areas
+--- a/ospfd/ospf_ase.c
++++ b/ospfd/ospf_ase.c
+@@ -753,6 +753,7 @@ void ospf_ase_unregister_external_lsa(st
+ lst = rn->info;
+ listnode_delete(lst, lsa);
+ ospf_lsa_unlock(&lsa); /* external_lsas list */
++
+ route_unlock_node(rn);
+ }
+ }
+--- a/ospfd/ospf_lsa.c
++++ b/ospfd/ospf_lsa.c
+@@ -2735,7 +2735,7 @@ int ospf_check_nbr_status(struct ospf *o
+ static int ospf_maxage_lsa_remover(struct thread *thread)
+ {
+ struct ospf *ospf = THREAD_ARG(thread);
+- struct ospf_lsa *lsa;
++ struct ospf_lsa *lsa, *old;
+ struct route_node *rn;
+ int reschedule = 0;
+
+@@ -2797,6 +2797,17 @@ static int ospf_maxage_lsa_remover(struc
+
+ /* Remove from lsdb. */
+ if (lsa->lsdb) {
++ old = ospf_lsdb_lookup(lsa->lsdb, lsa);
++ /* The max age LSA here must be the same
++ * as the LSA in LSDB
++ */
++ if (old != lsa) {
++ flog_err(EC_OSPF_LSA_MISSING,
++ "%s: LSA[Type%d:%s]: LSA not in LSDB",
++ __func__, lsa->data->type,
++ inet_ntoa(lsa->data->id));
++ continue;
++ }
+ ospf_discard_from_db(ospf, lsa->lsdb, lsa);
+ ospf_lsdb_delete(lsa->lsdb, lsa);
+ } else {
+--- a/ospfd/ospf_vty.c
++++ b/ospfd/ospf_vty.c
+@@ -276,7 +276,7 @@ DEFPY (ospf_router_id,
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
+ if (area->full_nbrs) {
+ vty_out(vty,
+- "For this router-id change to take effect, save config and restart ospfd\n");
++ "For this router-id change to take effect, use “clear ip ospf process” command\n");
+ return CMD_SUCCESS;
+ }
+
+@@ -309,7 +309,7 @@ DEFUN_HIDDEN (ospf_router_id_old,
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
+ if (area->full_nbrs) {
+ vty_out(vty,
+- "For this router-id change to take effect, save config and restart ospfd\n");
++ "For this router-id change to take effect, use “clear ip ospf process” command\n");
+ return CMD_SUCCESS;
+ }
+
+@@ -342,7 +342,7 @@ DEFPY (no_ospf_router_id,
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
+ if (area->full_nbrs) {
+ vty_out(vty,
+- "For this router-id change to take effect, save config and restart ospfd\n");
++ "For this router-id change to take effect, use “clear ip ospf process” command\n");
+ return CMD_SUCCESS;
+ }
+
+@@ -9769,6 +9769,70 @@ DEFUN (show_ip_ospf_vrfs,
+
+ return CMD_SUCCESS;
+ }
++DEFPY (clear_ip_ospf_neighbor,
++ clear_ip_ospf_neighbor_cmd,
++ "clear ip ospf [(1-65535)]$instance neighbor [A.B.C.D$nbr_id]",
++ CLEAR_STR
++ IP_STR
++ "OSPF information\n"
++ "Instance ID\n"
++ "Reset OSPF Neighbor\n"
++ "Neighbor ID\n")
++{
++ struct listnode *node;
++ struct ospf *ospf = NULL;
++
++ /* If user does not specify the arguments,
++ * instance = 0 and nbr_id = 0.0.0.0
++ */
++ if (instance != 0) {
++ /* This means clear only the particular ospf process */
++ ospf = ospf_lookup_instance(instance);
++ if (ospf == NULL)
++ return CMD_NOT_MY_INSTANCE;
++ }
++
++ /* Clear all the ospf processes */
++ for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
++ if (!ospf->oi_running)
++ continue;
++
++ ospf_neighbor_reset(ospf, nbr_id, nbr_id_str);
++ }
++
++ return CMD_SUCCESS;
++}
++
++DEFPY (clear_ip_ospf_process,
++ clear_ip_ospf_process_cmd,
++ "clear ip ospf [(1-65535)]$instance process",
++ CLEAR_STR
++ IP_STR
++ "OSPF information\n"
++ "Instance ID\n"
++ "Reset OSPF Process\n")
++{
++ struct listnode *node;
++ struct ospf *ospf = NULL;
++
++ /* Check if instance is not passed as an argument */
++ if (instance != 0) {
++ /* This means clear only the particular ospf process */
++ ospf = ospf_lookup_instance(instance);
++ if (ospf == NULL)
++ return CMD_NOT_MY_INSTANCE;
++ }
++
++ /* Clear all the ospf processes */
++ for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
++ if (!ospf->oi_running)
++ continue;
++
++ ospf_process_reset(ospf);
++ }
++
++ return CMD_SUCCESS;
++}
+
+ static const char *const ospf_abr_type_str[] = {
+ "unknown", "standard", "ibm", "cisco", "shortcut"
+@@ -10806,6 +10870,8 @@ DEFUN (clear_ip_ospf_interface,
+ void ospf_vty_clear_init(void)
+ {
+ install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd);
++ install_element(ENABLE_NODE, &clear_ip_ospf_process_cmd);
++ install_element(ENABLE_NODE, &clear_ip_ospf_neighbor_cmd);
+ }
+
+
+--- a/ospfd/ospfd.c
++++ b/ospfd/ospfd.c
+@@ -84,13 +84,15 @@ static void ospf_finish_final(struct osp
+
+ #define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1
+
+-void ospf_router_id_update(struct ospf *ospf)
++void ospf_process_refresh_data(struct ospf *ospf, bool reset)
+ {
+ struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
+ struct in_addr router_id, router_id_old;
+ struct ospf_interface *oi;
+ struct interface *ifp;
+- struct listnode *node;
++ struct listnode *node, *nnode;
++ struct ospf_area *area;
++ bool rid_change = false;
+
+ if (!ospf->oi_running) {
+ if (IS_DEBUG_OSPF_EVENT)
+@@ -123,8 +125,8 @@ void ospf_router_id_update(struct ospf *
+ zlog_debug("Router-ID[OLD:%s]: Update to %s",
+ inet_ntoa(ospf->router_id), inet_ntoa(router_id));
+
+- if (!IPV4_ADDR_SAME(&router_id_old, &router_id)) {
+-
++ rid_change = !(IPV4_ADDR_SAME(&router_id_old, &router_id));
++ if (rid_change || (reset)) {
+ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
+ /* Some nbrs are identified by router_id, these needs
+ * to be rebuilt. Possible optimization would be to do
+@@ -146,16 +148,8 @@ void ospf_router_id_update(struct ospf *
+ ospf_if_up(oi);
+ }
+
+- /* Flush (inline) all external LSAs based on the OSPF_LSA_SELF
+- * flag */
+- if (ospf->lsdb) {
+- struct route_node *rn;
+- struct ospf_lsa *lsa;
+-
+- LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa)
+- if (IS_LSA_SELF(lsa))
+- ospf_lsa_flush_schedule(ospf, lsa);
+- }
++ /* Flush (inline) all the self originated LSAs */
++ ospf_flush_self_originated_lsas_now(ospf);
+
+ ospf->router_id = router_id;
+ if (IS_DEBUG_OSPF_EVENT)
+@@ -180,24 +174,81 @@ void ospf_router_id_update(struct ospf *
+ LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) {
+ /* AdvRouter and Router ID is the same. */
+ if (IPV4_ADDR_SAME(&lsa->data->adv_router,
+- &ospf->router_id)) {
++ &ospf->router_id) && rid_change) {
+ SET_FLAG(lsa->flags,
+ OSPF_LSA_SELF_CHECKED);
+ SET_FLAG(lsa->flags, OSPF_LSA_SELF);
+ ospf_lsa_flush_schedule(ospf, lsa);
+ }
++ /* The above flush will send immediately
++ * So discard the LSA to originate new
++ */
++ ospf_discard_from_db(ospf, ospf->lsdb, lsa);
+ }
++
++ LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa)
++ ospf_discard_from_db(ospf, ospf->lsdb, lsa);
++
++ ospf_lsdb_delete_all(ospf->lsdb);
+ }
+
++ /* Delete the LSDB */
++ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area))
++ ospf_area_lsdb_discard_delete(area);
++
+ /* update router-lsa's for each area */
+ ospf_router_lsa_update(ospf);
+
+ /* update ospf_interface's */
+- FOR_ALL_INTERFACES (vrf, ifp)
+- ospf_if_update(ospf, ifp);
++ FOR_ALL_INTERFACES (vrf, ifp) {
++ if (reset)
++ ospf_if_reset(ifp);
++ else
++ ospf_if_update(ospf, ifp);
++ }
+
+ ospf_external_lsa_rid_change(ospf);
+ }
++
++ ospf->inst_shutdown = 0;
++}
++
++void ospf_router_id_update(struct ospf *ospf)
++{
++ ospf_process_refresh_data(ospf, false);
++}
++
++void ospf_process_reset(struct ospf *ospf)
++{
++ ospf_process_refresh_data(ospf, true);
++}
++
++void ospf_neighbor_reset(struct ospf *ospf, struct in_addr nbr_id,
++ const char *nbr_str)
++{
++ struct route_node *rn;
++ struct ospf_neighbor *nbr;
++ struct ospf_interface *oi;
++ struct listnode *node;
++
++ /* Clear only a particular nbr with nbr router id as nbr_id */
++ if (nbr_str != NULL) {
++ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
++ nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, &nbr_id);
++ if (nbr)
++ OSPF_NSM_EVENT_EXECUTE(nbr, NSM_KillNbr);
++ }
++ return;
++ }
++
++ /* send Neighbor event KillNbr to all associated neighbors. */
++ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
++ for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
++ nbr = rn->info;
++ if (nbr && (nbr != oi->nbr_self))
++ OSPF_NSM_EVENT_EXECUTE(nbr, NSM_KillNbr);
++ }
++ }
+ }
+
+ /* For OSPF area sort by area id. */
+@@ -839,14 +890,11 @@ static struct ospf_area *ospf_area_new(s
+ return new;
+ }
+
+-static void ospf_area_free(struct ospf_area *area)
++void ospf_area_lsdb_discard_delete(struct ospf_area *area)
+ {
+ struct route_node *rn;
+ struct ospf_lsa *lsa;
+
+- ospf_opaque_type10_lsa_term(area);
+-
+- /* Free LSDBs. */
+ LSDB_LOOP (ROUTER_LSDB(area), rn, lsa)
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+ LSDB_LOOP (NETWORK_LSDB(area), rn, lsa)
+@@ -864,6 +912,15 @@ static void ospf_area_free(struct ospf_a
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+
+ ospf_lsdb_delete_all(area->lsdb);
++}
++
++static void ospf_area_free(struct ospf_area *area)
++{
++ ospf_opaque_type10_lsa_term(area);
++
++ /* Free LSDBs. */
++ ospf_area_lsdb_discard_delete(area);
++
+ ospf_lsdb_free(area->lsdb);
+
+ ospf_lsa_unlock(&area->router_lsa_self);
+--- a/ospfd/ospfd.h
++++ b/ospfd/ospfd.h
+@@ -523,7 +523,11 @@ extern struct ospf *ospf_lookup_by_inst_
+ const char *name);
+ extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id);
+ extern void ospf_finish(struct ospf *);
++extern void ospf_process_refresh_data(struct ospf *ospf, bool reset);
+ extern void ospf_router_id_update(struct ospf *ospf);
++extern void ospf_process_reset(struct ospf *ospf);
++extern void ospf_neighbor_reset(struct ospf *ospf, struct in_addr nbr_id,
++ const char *nbr_str);
+ extern int ospf_network_set(struct ospf *, struct prefix_ipv4 *, struct in_addr,
+ int);
+ extern int ospf_network_unset(struct ospf *, struct prefix_ipv4 *,
+@@ -548,6 +552,7 @@ extern int ospf_area_shortcut_set(struct
+ extern int ospf_area_shortcut_unset(struct ospf *, struct ospf_area *);
+ extern int ospf_timers_refresh_set(struct ospf *, int);
+ extern int ospf_timers_refresh_unset(struct ospf *);
++void ospf_area_lsdb_discard_delete(struct ospf_area *area);
+ extern int ospf_nbr_nbma_set(struct ospf *, struct in_addr);
+ extern int ospf_nbr_nbma_unset(struct ospf *, struct in_addr);
+ extern int ospf_nbr_nbma_priority_set(struct ospf *, struct in_addr, uint8_t);
--- /dev/null
+From 153bdb3d03542530ed1deccbefc716cb4b699a67 Mon Sep 17 00:00:00 2001
+From: Donald Sharp <sharpd@nvidia.com>
+Date: Thu, 28 Jan 2021 14:56:11 -0500
+Subject: [PATCH] ospfd: ospf_nbr_nbma_lookup_next always returns NULL
+
+The calling function of ospf_nbr_nbma_lookup_next calls
+this function and then immediately returns when it
+gets the NULL. Just cleanup a bit more code.
+
+Signed-off-by: Donald Sharp <sharpd@nvidia.com>
+---
+ ospfd/ospf_snmp.c | 23 +----------------------
+ ospfd/ospfd.c | 9 ---------
+ ospfd/ospfd.h | 2 --
+ 3 files changed, 1 insertion(+), 33 deletions(-)
+
+--- a/ospfd/ospf_snmp.c
++++ b/ospfd/ospf_snmp.c
+@@ -1236,7 +1236,6 @@ static struct ospf_nbr_nbma *ospfHostLoo
+ size_t *length,
+ struct in_addr *addr, int exact)
+ {
+- int len;
+ struct ospf_nbr_nbma *nbr_nbma;
+ struct ospf *ospf;
+
+@@ -1258,28 +1257,8 @@ static struct ospf_nbr_nbma *ospfHostLoo
+ nbr_nbma = ospf_nbr_nbma_lookup(ospf, *addr);
+
+ return nbr_nbma;
+- } else {
+- len = *length - v->namelen;
+- if (len > 4)
+- len = 4;
+-
+- oid2in_addr(name + v->namelen, len, addr);
+-
+- nbr_nbma =
+- ospf_nbr_nbma_lookup_next(ospf, addr, len == 0 ? 1 : 0);
+-
+- if (nbr_nbma == NULL)
+- return NULL;
+-
+- oid_copy_addr(name + v->namelen, addr, IN_ADDR_SIZE);
+-
+- /* Set TOS 0. */
+- name[v->namelen + IN_ADDR_SIZE] = 0;
+-
+- *length = v->namelen + IN_ADDR_SIZE + 1;
+-
+- return nbr_nbma;
+ }
++
+ return NULL;
+ }
+
+--- a/ospfd/ospfd.c
++++ b/ospfd/ospfd.c
+@@ -1932,35 +1932,6 @@ struct ospf_nbr_nbma *ospf_nbr_nbma_look
+ return NULL;
+ }
+
+-struct ospf_nbr_nbma *ospf_nbr_nbma_lookup_next(struct ospf *ospf,
+- struct in_addr *addr, int first)
+-{
+-#if 0
+- struct ospf_nbr_nbma *nbr_nbma;
+- struct listnode *node;
+-#endif
+-
+- if (ospf == NULL)
+- return NULL;
+-
+-#if 0
+- for (ALL_LIST_ELEMENTS_RO (ospf->nbr_nbma, node, nbr_nbma))
+- {
+- if (first)
+- {
+- *addr = nbr_nbma->addr;
+- return nbr_nbma;
+- }
+- else if (ntohl (nbr_nbma->addr.s_addr) > ntohl (addr->s_addr))
+- {
+- *addr = nbr_nbma->addr;
+- return nbr_nbma;
+- }
+- }
+-#endif
+- return NULL;
+-}
+-
+ int ospf_nbr_nbma_set(struct ospf *ospf, struct in_addr nbr_addr)
+ {
+ struct ospf_nbr_nbma *nbr_nbma;
+--- a/ospfd/ospfd.h
++++ b/ospfd/ospfd.h
+@@ -567,8 +567,6 @@ extern void ospf_terminate(void);
+ extern void ospf_nbr_nbma_if_update(struct ospf *, struct ospf_interface *);
+ extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup(struct ospf *,
+ struct in_addr);
+-extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup_next(struct ospf *,
+- struct in_addr *, int);
+ extern int ospf_oi_count(struct interface *);
+
+ extern struct ospf_area *ospf_area_get(struct ospf *, struct in_addr);
--- /dev/null
+From 409f98ab443682ec360e3e76954f1c8985b3371d Mon Sep 17 00:00:00 2001
+From: Igor Ryzhov <iryzhov@nfware.com>
+Date: Thu, 28 Jan 2021 02:41:07 +0300
+Subject: [PATCH 1/2] ospfd: don't rely on instance existence in vty
+
+Store instance index at startup and use it when processing vty commands.
+The instance itself may be created and deleted by the user in runtime
+using `[no] router ospf X` command.
+
+Fixes #7908
+
+Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
+---
+ ospfd/ospf_dump.c | 70 ++++++---------
+ ospfd/ospf_main.c | 20 +----
+ ospfd/ospf_vty.c | 220 +++++++++++++++++++++++-----------------------
+ ospfd/ospfd.c | 26 +++---
+ ospfd/ospfd.h | 3 +-
+ 5 files changed, 154 insertions(+), 185 deletions(-)
+
+--- a/ospfd/ospf_dump.c
++++ b/ospfd/ospf_dump.c
+@@ -607,7 +607,7 @@ DEFUN (debug_ospf_packet,
+
+ if (inst) // user passed instance ID
+ {
+- if (!ospf_lookup_instance(strtoul(argv[2]->arg, NULL, 10)))
++ if (inst != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+ }
+
+@@ -683,7 +683,7 @@ DEFUN (no_debug_ospf_packet,
+
+ if (inst) // user passed instance ID
+ {
+- if (!ospf_lookup_instance(strtoul(argv[3]->arg, NULL, 10)))
++ if (inst != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+ }
+
+@@ -754,7 +754,7 @@ DEFUN (debug_ospf_ism,
+
+ if (inst) // user passed instance ID
+ {
+- if (!ospf_lookup_instance(strtoul(argv[2]->arg, NULL, 10)))
++ if (inst != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+ }
+
+@@ -805,7 +805,7 @@ DEFUN (no_debug_ospf_ism,
+
+ if (inst) // user passed instance ID
+ {
+- if (!ospf_lookup_instance(strtoul(argv[3]->arg, NULL, 10)))
++ if (inst != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+ }
+
+@@ -900,8 +900,8 @@ DEFUN (debug_ospf_instance_nsm,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
+- return CMD_SUCCESS;
++ if (instance != ospf_instance)
++ return CMD_NOT_MY_INSTANCE;
+
+ return debug_ospf_nsm_common(vty, 4, argc, argv);
+ }
+@@ -972,7 +972,7 @@ DEFUN (no_debug_ospf_instance_nsm,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+ return no_debug_ospf_nsm_common(vty, 5, argc, argv);
+@@ -1046,7 +1046,7 @@ DEFUN (debug_ospf_instance_lsa,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+ return debug_ospf_lsa_common(vty, 4, argc, argv);
+@@ -1122,7 +1122,7 @@ DEFUN (no_debug_ospf_instance_lsa,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+ return no_debug_ospf_lsa_common(vty, 5, argc, argv);
+@@ -1184,7 +1184,7 @@ DEFUN (debug_ospf_instance_zebra,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+ return debug_ospf_zebra_common(vty, 4, argc, argv);
+@@ -1248,8 +1248,8 @@ DEFUN (no_debug_ospf_instance_zebra,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
+- return CMD_SUCCESS;
++ if (instance != ospf_instance)
++ return CMD_NOT_MY_INSTANCE;
+
+ return no_debug_ospf_zebra_common(vty, 5, argc, argv);
+ }
+@@ -1294,8 +1294,8 @@ DEFUN (debug_ospf_instance_event,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
+- return CMD_SUCCESS;
++ if (instance != ospf_instance)
++ return CMD_NOT_MY_INSTANCE;
+
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_ON(event, EVENT);
+@@ -1316,8 +1316,8 @@ DEFUN (no_debug_ospf_instance_event,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
+- return CMD_SUCCESS;
++ if (instance != ospf_instance)
++ return CMD_NOT_MY_INSTANCE;
+
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_OFF(event, EVENT);
+@@ -1364,8 +1364,8 @@ DEFUN (debug_ospf_instance_nssa,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
+- return CMD_SUCCESS;
++ if (instance != ospf_instance)
++ return CMD_NOT_MY_INSTANCE;
+
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_ON(nssa, NSSA);
+@@ -1386,8 +1386,8 @@ DEFUN (no_debug_ospf_instance_nssa,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if (!ospf_lookup_instance(instance))
+- return CMD_SUCCESS;
++ if (instance != ospf_instance)
++ return CMD_NOT_MY_INSTANCE;
+
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_OFF(nssa, NSSA);
+@@ -1536,12 +1536,12 @@ DEFUN (no_debug_ospf,
+ return CMD_SUCCESS;
+ }
+
+-static int show_debugging_ospf_common(struct vty *vty, struct ospf *ospf)
++static int show_debugging_ospf_common(struct vty *vty)
+ {
+ int i;
+
+- if (ospf->instance)
+- vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance);
++ if (ospf_instance)
++ vty_out(vty, "\nOSPF Instance: %d\n\n", ospf_instance);
+
+ vty_out(vty, "OSPF debugging status:\n");
+
+@@ -1645,13 +1645,7 @@ DEFUN_NOSH (show_debugging_ospf,
+ DEBUG_STR
+ OSPF_STR)
+ {
+- struct ospf *ospf = NULL;
+-
+- ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+- if (ospf == NULL)
+- return CMD_SUCCESS;
+-
+- return show_debugging_ospf_common(vty, ospf);
++ return show_debugging_ospf_common(vty);
+ }
+
+ DEFUN_NOSH (show_debugging_ospf_instance,
+@@ -1663,14 +1657,13 @@ DEFUN_NOSH (show_debugging_ospf_instance
+ "Instance ID\n")
+ {
+ int idx_number = 3;
+- struct ospf *ospf;
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- if ((ospf = ospf_lookup_instance(instance)) == NULL)
+- return CMD_SUCCESS;
++ if (instance != ospf_instance)
++ return CMD_NOT_MY_INSTANCE;
+
+- return show_debugging_ospf_common(vty, ospf);
++ return show_debugging_ospf_common(vty);
+ }
+
+ static int config_write_debug(struct vty *vty);
+@@ -1693,16 +1686,11 @@ static int config_write_debug(struct vty
+ "", " send", " recv", "",
+ " detail", " send detail", " recv detail", " detail"};
+
+- struct ospf *ospf;
+ char str[16];
+ memset(str, 0, 16);
+
+- ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+- if (ospf == NULL)
+- return CMD_SUCCESS;
+-
+- if (ospf->instance)
+- snprintf(str, sizeof(str), " %u", ospf->instance);
++ if (ospf_instance)
++ snprintf(str, sizeof(str), " %u", ospf_instance);
+
+ /* debug ospf ism (status|events|timers). */
+ if (IS_CONF_DEBUG_OSPF(ism, ISM) == OSPF_DEBUG_ISM)
+--- a/ospfd/ospf_main.c
++++ b/ospfd/ospf_main.c
+@@ -145,9 +145,6 @@ FRR_DAEMON_INFO(ospfd, OSPF, .vty_port =
+ /* OSPFd main routine. */
+ int main(int argc, char **argv)
+ {
+- unsigned short instance = 0;
+- bool created = false;
+-
+ #ifdef SUPPORT_OSPF_API
+ /* OSPF apiserver is disabled by default. */
+ ospf_apiserver_enable = 0;
+@@ -168,8 +165,8 @@ int main(int argc, char **argv)
+
+ switch (opt) {
+ case 'n':
+- ospfd_di.instance = instance = atoi(optarg);
+- if (instance < 1)
++ ospfd_di.instance = ospf_instance = atoi(optarg);
++ if (ospf_instance < 1)
+ exit(0);
+ break;
+ case 0:
+@@ -207,7 +204,7 @@ int main(int argc, char **argv)
+
+ /* OSPFd inits. */
+ ospf_if_init();
+- ospf_zebra_init(master, instance);
++ ospf_zebra_init(master, ospf_instance);
+
+ /* OSPF vty inits. */
+ ospf_vty_init();
+@@ -223,17 +220,6 @@ int main(int argc, char **argv)
+ /* OSPF errors init */
+ ospf_error_init();
+
+- /*
+- * Need to initialize the default ospf structure, so the interface mode
+- * commands can be duly processed if they are received before 'router
+- * ospf', when ospfd is restarted
+- */
+- if (instance && !ospf_get_instance(instance, &created)) {
+- flog_err(EC_OSPF_INIT_FAIL, "OSPF instance init failed: %s",
+- strerror(errno));
+- exit(1);
+- }
+-
+ frr_config_fork();
+ frr_run(master);
+
+--- a/ospfd/ospf_vty.c
++++ b/ospfd/ospf_vty.c
+@@ -136,44 +136,37 @@ int ospf_oi_count(struct interface *ifp)
+ all_vrf = strmatch(vrf_name, "all"); \
+ }
+
+-static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty,
+- struct cmd_token *argv[],
+- const int argc, uint32_t enable,
+- unsigned short *instance)
++static int ospf_router_cmd_parse(struct vty *vty, struct cmd_token *argv[],
++ const int argc, unsigned short *instance,
++ const char **vrf_name)
+ {
+- struct ospf *ospf = NULL;
+ int idx_vrf = 0, idx_inst = 0;
+- const char *vrf_name = NULL;
+- bool created = false;
+
+ *instance = 0;
+- if (argv_find(argv, argc, "(1-65535)", &idx_inst))
++ if (argv_find(argv, argc, "(1-65535)", &idx_inst)) {
++ if (ospf_instance == 0) {
++ vty_out(vty,
++ "%% OSPF is not running in instance mode\n");
++ return CMD_WARNING_CONFIG_FAILED;
++ }
++
+ *instance = strtoul(argv[idx_inst]->arg, NULL, 10);
++ }
+
++ *vrf_name = NULL;
+ if (argv_find(argv, argc, "vrf", &idx_vrf)) {
+- vrf_name = argv[idx_vrf + 1]->arg;
+- if (vrf_name == NULL || strmatch(vrf_name, VRF_DEFAULT_NAME))
+- vrf_name = NULL;
+- if (enable) {
+- /* Allocate VRF aware instance */
+- ospf = ospf_get(*instance, vrf_name, &created);
+- } else {
+- ospf = ospf_lookup_by_inst_name(*instance, vrf_name);
+- }
+- } else {
+- if (enable) {
+- ospf = ospf_get(*instance, NULL, &created);
+- } else {
+- ospf = ospf_lookup_instance(*instance);
++ if (ospf_instance != 0) {
++ vty_out(vty,
++ "%% VRF is not supported in instance mode\n");
++ return CMD_WARNING_CONFIG_FAILED;
+ }
+- }
+
+- if (created) {
+- if (DFLT_OSPF_LOG_ADJACENCY_CHANGES)
+- SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES);
++ *vrf_name = argv[idx_vrf + 1]->arg;
++ if (*vrf_name && strmatch(*vrf_name, VRF_DEFAULT_NAME))
++ *vrf_name = NULL;
+ }
+
+- return ospf;
++ return CMD_SUCCESS;
+ }
+
+ static void ospf_show_vrf_name(struct ospf *ospf, struct vty *vty,
+@@ -209,28 +202,35 @@ DEFUN_NOSH (router_ospf,
+ "Instance ID\n"
+ VRF_CMD_HELP_STR)
+ {
+- struct ospf *ospf = NULL;
+- int ret = CMD_SUCCESS;
+- unsigned short instance = 0;
++ unsigned short instance;
++ const char *vrf_name;
++ bool created = false;
++ struct ospf *ospf;
++ int ret;
+
+- ospf = ospf_cmd_lookup_ospf(vty, argv, argc, 1, &instance);
+- if (!ospf)
+- return CMD_WARNING_CONFIG_FAILED;
++ ret = ospf_router_cmd_parse(vty, argv, argc, &instance, &vrf_name);
++ if (ret != CMD_SUCCESS)
++ return ret;
+
+- /* The following logic to set the vty qobj index is in place to be able
+- to ignore the commands which dont belong to this instance. */
+- if (ospf->instance != instance) {
++ if (instance != ospf_instance) {
+ VTY_PUSH_CONTEXT_NULL(OSPF_NODE);
+- ret = CMD_NOT_MY_INSTANCE;
+- } else {
+- if (IS_DEBUG_OSPF_EVENT)
+- zlog_debug(
+- "Config command 'router ospf %d' received, vrf %s id %u oi_running %u",
+- instance, ospf->name ? ospf->name : "NIL",
+- ospf->vrf_id, ospf->oi_running);
+- VTY_PUSH_CONTEXT(OSPF_NODE, ospf);
++ return CMD_NOT_MY_INSTANCE;
+ }
+
++ ospf = ospf_get(instance, vrf_name, &created);
++
++ if (created)
++ if (DFLT_OSPF_LOG_ADJACENCY_CHANGES)
++ SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES);
++
++ if (IS_DEBUG_OSPF_EVENT)
++ zlog_debug(
++ "Config command 'router ospf %d' received, vrf %s id %u oi_running %u",
++ ospf->instance, ospf->name ? ospf->name : "NIL",
++ ospf->vrf_id, ospf->oi_running);
++
++ VTY_PUSH_CONTEXT(OSPF_NODE, ospf);
++
+ return ret;
+ }
+
+@@ -243,19 +243,25 @@ DEFUN (no_router_ospf,
+ "Instance ID\n"
+ VRF_CMD_HELP_STR)
+ {
++ unsigned short instance;
++ const char *vrf_name;
+ struct ospf *ospf;
+- unsigned short instance = 0;
++ int ret;
+
+- ospf = ospf_cmd_lookup_ospf(vty, argv, argc, 0, &instance);
+- if (ospf == NULL) {
+- if (instance)
+- return CMD_NOT_MY_INSTANCE;
+- else
+- return CMD_WARNING;
+- }
+- ospf_finish(ospf);
++ ret = ospf_router_cmd_parse(vty, argv, argc, &instance, &vrf_name);
++ if (ret != CMD_SUCCESS)
++ return ret;
+
+- return CMD_SUCCESS;
++ if (instance != ospf_instance)
++ return CMD_NOT_MY_INSTANCE;
++
++ ospf = ospf_lookup(instance, vrf_name);
++ if (ospf)
++ ospf_finish(ospf);
++ else
++ ret = CMD_WARNING_CONFIG_FAILED;
++
++ return ret;
+ }
+
+
+@@ -3324,11 +3330,11 @@ DEFUN (show_ip_ospf_instance,
+ json_object *json = NULL;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ if (uj)
+@@ -4014,11 +4020,11 @@ DEFUN (show_ip_ospf_instance_interface,
+ json_object *json = NULL;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ if (uj)
+@@ -4407,11 +4413,11 @@ DEFUN (show_ip_ospf_instance_neighbor,
+ int ret = CMD_SUCCESS;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ if (uj)
+@@ -4619,11 +4625,11 @@ DEFUN (show_ip_ospf_instance_neighbor_al
+ int ret = CMD_SUCCESS;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+ if (uj)
+ json = json_object_new_object();
+@@ -4759,11 +4765,11 @@ DEFUN (show_ip_ospf_instance_neighbor_in
+ show_ip_ospf_neighbour_header(vty);
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ if (!uj)
+@@ -5168,11 +5174,11 @@ DEFPY (show_ip_ospf_instance_neighbor_id
+ {
+ struct ospf *ospf;
+
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ return show_ip_ospf_neighbor_id_common(vty, ospf, &router_id, !!json,
+@@ -5341,11 +5347,11 @@ DEFUN (show_ip_ospf_instance_neighbor_de
+ int ret = CMD_SUCCESS;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ if (uj)
+@@ -5536,11 +5542,11 @@ DEFUN (show_ip_ospf_instance_neighbor_de
+ int ret = CMD_SUCCESS;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ if (uj)
+@@ -5668,11 +5674,11 @@ DEFUN (show_ip_ospf_instance_neighbor_in
+ bool uj = use_json(argc, argv);
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ return show_ip_ospf_neighbor_int_detail_common(vty, ospf, idx_ifname,
+@@ -6418,10 +6424,11 @@ DEFUN (show_ip_ospf_instance_database,
+
+ if (argv_find(argv, argc, "(1-65535)", &idx)) {
+ instance = strtoul(argv[idx]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+- if (!ospf->oi_running)
++
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ return (show_ip_ospf_database_common(vty, ospf, idx ? 1 : 0,
+@@ -6482,15 +6489,12 @@ DEFUN (show_ip_ospf_instance_database_ma
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+-
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running) {
+- vty_out(vty, "%% OSPF instance not found\n");
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+- }
+
+ return show_ip_ospf_database_common(vty, ospf, 1, argc, argv, 0);
+ }
+@@ -6576,13 +6580,12 @@ DEFUN (show_ip_ospf_instance_database_ty
+
+ if (argv_find(argv, argc, "(1-65535)", &idx)) {
+ instance = strtoul(argv[idx]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+- if (!ospf->oi_running) {
+- vty_out(vty, "%% OSPF instance not found\n");
++
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+- }
+
+ return (show_ip_ospf_database_type_adv_router_common(
+ vty, ospf, idx ? 1 : 0, argc, argv, use_vrf));
+@@ -8033,7 +8036,7 @@ DEFUN (ip_ospf_area,
+ else
+ ospf = ospf_lookup_instance(instance);
+
+- if (instance && ospf == NULL) {
++ if (instance && instance != ospf_instance) {
+ /*
+ * At this point we know we have received
+ * an instance and there is no ospf instance
+@@ -8158,7 +8161,7 @@ DEFUN (no_ip_ospf_area,
+ else
+ ospf = ospf_lookup_instance(instance);
+
+- if (instance && ospf == NULL)
++ if (instance && instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+ argv_find(argv, argc, "area", &idx);
+@@ -9519,11 +9522,11 @@ DEFUN (show_ip_ospf_instance_border_rout
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ return show_ip_ospf_border_routers_common(vty, ospf, 0);
+@@ -9687,11 +9690,11 @@ DEFUN (show_ip_ospf_instance_route,
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+- if (!ospf->oi_running)
++ ospf = ospf_lookup_instance(instance);
++ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ return show_ip_ospf_route_common(vty, ospf, NULL, 0);
+@@ -9787,8 +9790,7 @@ DEFPY (clear_ip_ospf_neighbor,
+ */
+ if (instance != 0) {
+ /* This means clear only the particular ospf process */
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+ }
+
+@@ -9818,8 +9820,7 @@ DEFPY (clear_ip_ospf_process,
+ /* Check if instance is not passed as an argument */
+ if (instance != 0) {
+ /* This means clear only the particular ospf process */
+- ospf = ospf_lookup_instance(instance);
+- if (ospf == NULL)
++ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+ }
+
+@@ -9860,7 +9861,6 @@ static int config_write_interface_one(st
+ struct route_node *rn = NULL;
+ struct ospf_if_params *params;
+ int write = 0;
+- struct ospf *ospf = vrf->info;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+
+@@ -10039,9 +10039,9 @@ static int config_write_interface_one(st
+
+ /* Area print. */
+ if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) {
+- if (ospf && ospf->instance)
++ if (ospf_instance)
+ vty_out(vty, " ip ospf %d",
+- ospf->instance);
++ ospf_instance);
+ else
+ vty_out(vty, " ip ospf");
+
+--- a/ospfd/ospfd.c
++++ b/ospfd/ospfd.c
+@@ -67,6 +67,8 @@ static struct ospf_master ospf_master;
+ /* OSPF process wide configuration pointer to export. */
+ struct ospf_master *om;
+
++unsigned short ospf_instance;
++
+ extern struct zclient *zclient;
+
+
+@@ -468,36 +470,28 @@ static void ospf_init(struct ospf *ospf)
+ ospf_router_id_update(ospf);
+ }
+
+-struct ospf *ospf_get(unsigned short instance, const char *name, bool *created)
++struct ospf *ospf_lookup(unsigned short instance, const char *name)
+ {
+ struct ospf *ospf;
+
+- /* vrf name provided call inst and name based api
+- * in case of no name pass default ospf instance */
+- if (name)
++ if (ospf_instance) {
++ ospf = ospf_lookup_instance(instance);
++ } else {
+ ospf = ospf_lookup_by_inst_name(instance, name);
+- else
+- ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+-
+- *created = (ospf == NULL);
+- if (ospf == NULL) {
+- ospf = ospf_new(instance, name);
+- ospf_add(ospf);
+-
+- ospf_init(ospf);
+ }
+
+ return ospf;
+ }
+
+-struct ospf *ospf_get_instance(unsigned short instance, bool *created)
++struct ospf *ospf_get(unsigned short instance, const char *name, bool *created)
+ {
+ struct ospf *ospf;
+
+- ospf = ospf_lookup_instance(instance);
++ ospf = ospf_lookup(instance, name);
++
+ *created = (ospf == NULL);
+ if (ospf == NULL) {
+- ospf = ospf_new(instance, NULL /* VRF_DEFAULT*/);
++ ospf = ospf_new(instance, name);
+ ospf_add(ospf);
+
+ ospf_init(ospf);
+--- a/ospfd/ospfd.h
++++ b/ospfd/ospfd.h
+@@ -507,6 +507,7 @@ struct ospf_nbr_nbma {
+
+ /* Extern variables. */
+ extern struct ospf_master *om;
++extern unsigned short ospf_instance;
+ extern const int ospf_redistributed_proto_max;
+ extern struct zclient *zclient;
+ extern struct thread_master *master;
+@@ -516,9 +517,9 @@ extern struct zebra_privs_t ospfd_privs;
+ /* Prototypes. */
+ extern const char *ospf_redist_string(unsigned int route_type);
+ extern struct ospf *ospf_lookup_instance(unsigned short);
++extern struct ospf *ospf_lookup(unsigned short instance, const char *name);
+ extern struct ospf *ospf_get(unsigned short instance, const char *name,
+ bool *created);
+-extern struct ospf *ospf_get_instance(unsigned short, bool *created);
+ extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance,
+ const char *name);
+ extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id);
+--- a/vtysh/vtysh.c
++++ b/vtysh/vtysh.c
+@@ -2487,7 +2487,7 @@ static int show_per_daemon(const char *l
+ int ret = CMD_SUCCESS;
+
+ for (i = 0; i < array_size(vtysh_client); i++)
+- if (vtysh_client[i].fd >= 0) {
++ if (vtysh_client[i].fd >= 0 || vtysh_client[i].next) {
+ vty_out(vty, headline, vtysh_client[i].name);
+ ret = vtysh_client_execute(&vtysh_client[i], line);
+ vty_out(vty, "\n");
--- /dev/null
+From f9ff7bf497894b74fd02d54dc0f0a39981f7cc06 Mon Sep 17 00:00:00 2001
+From: Amol Lad <amol.lad@4rf.com>
+Date: Wed, 17 Feb 2021 13:47:32 +1300
+Subject: [PATCH 1/6] nhrpd: Add support for forwarding multicast packets
+
+Forwarding multicast is a pre-requisite for allowing multicast based routing
+protocols such as OSPF to work with DMVPN
+
+This code relies on externally adding iptables rule. For example:
+iptables -A OUTPUT -d 224.0.0.0/24 -o gre1 -j NFLOG --nflog-group 224
+
+Signed-off-by: Reuben Dowle <reuben.dowle@4rf.com>
+---
+ nhrpd/linux.c | 11 +-
+ nhrpd/nhrp_interface.c | 2 +
+ nhrpd/nhrp_multicast.c | 312 +++++++++++++++++++++++++++++++++++++++++
+ nhrpd/nhrp_peer.c | 3 +-
+ nhrpd/nhrp_vty.c | 63 +++++++++
+ nhrpd/nhrpd.h | 16 +++
+ nhrpd/os.h | 2 +-
+ nhrpd/subdir.am | 1 +
+ 8 files changed, 403 insertions(+), 7 deletions(-)
+ create mode 100644 nhrpd/nhrp_multicast.c
+
+--- a/nhrpd/linux.c
++++ b/nhrpd/linux.c
+@@ -15,6 +15,7 @@
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <string.h>
++#include <errno.h>
+ #include <sys/ioctl.h>
+ #include <sys/socket.h>
+ #include <sys/types.h>
+@@ -42,7 +43,7 @@ int os_socket(void)
+ }
+
+ int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr,
+- size_t addrlen)
++ size_t addrlen, uint16_t protocol)
+ {
+ struct sockaddr_ll lladdr;
+ struct iovec iov = {
+@@ -61,16 +62,16 @@ int os_sendmsg(const uint8_t *buf, size_
+
+ memset(&lladdr, 0, sizeof(lladdr));
+ lladdr.sll_family = AF_PACKET;
+- lladdr.sll_protocol = htons(ETH_P_NHRP);
++ lladdr.sll_protocol = htons(protocol);
+ lladdr.sll_ifindex = ifindex;
+ lladdr.sll_halen = addrlen;
+ memcpy(lladdr.sll_addr, addr, addrlen);
+
+- status = sendmsg(nhrp_socket_fd, &msg, 0);
++ status = sendmsg(os_socket(), &msg, 0);
+ if (status < 0)
+- return -1;
++ return -errno;
+
+- return 0;
++ return status;
+ }
+
+ int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr,
+--- a/nhrpd/nhrp_interface.c
++++ b/nhrpd/nhrp_interface.c
+@@ -42,6 +42,7 @@ static int nhrp_if_new_hook(struct inter
+ struct nhrp_afi_data *ad = &nifp->afi[afi];
+ ad->holdtime = NHRPD_DEFAULT_HOLDTIME;
+ list_init(&ad->nhslist_head);
++ list_init(&ad->mcastlist_head);
+ }
+
+ return 0;
+@@ -55,6 +56,7 @@ static int nhrp_if_delete_hook(struct in
+
+ nhrp_cache_interface_del(ifp);
+ nhrp_nhs_interface_del(ifp);
++ nhrp_multicast_interface_del(ifp);
+ nhrp_peer_interface_del(ifp);
+
+ if (nifp->ipsec_profile)
+--- /dev/null
++++ b/nhrpd/nhrp_multicast.c
+@@ -0,0 +1,312 @@
++/* NHRP Multicast Support
++ * Copyright (c) 2020-2021 4RF Limited
++ *
++ * This file is free software: you may copy, redistribute and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <fcntl.h>
++#include <net/if.h>
++#include <net/ethernet.h>
++#include <netinet/if_ether.h>
++#include <linux/netlink.h>
++#include <linux/neighbour.h>
++#include <linux/netfilter/nfnetlink_log.h>
++#include <linux/if_packet.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++
++#include "thread.h"
++#include "nhrpd.h"
++#include "netlink.h"
++#include "znl.h"
++#include "os.h"
++
++DEFINE_MTYPE_STATIC(NHRPD, NHRP_MULTICAST, "NHRP Multicast")
++
++static int netlink_mcast_nflog_group;
++static int netlink_mcast_log_fd = -1;
++static struct thread *netlink_mcast_log_thread;
++static int nhrp_multicast_ip_count;
++
++struct mcast_ctx {
++ struct interface *ifp;
++ struct zbuf *pkt;
++};
++
++static void nhrp_multicast_send(struct nhrp_peer *p, struct zbuf *zb)
++{
++ char buf[2][256];
++ size_t addrlen;
++ int ret;
++
++ addrlen = sockunion_get_addrlen(&p->vc->remote.nbma);
++ ret = os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
++ sockunion_get_addr(&p->vc->remote.nbma),
++ addrlen, addrlen == 4 ? 0x0800 : 0x86DD);
++
++ debugf(NHRP_DEBUG_COMMON, "Multicast Packet: %s -> %s, ret = %d, size = %zu, addrlen = %zu",
++ sockunion2str(&p->vc->local.nbma, buf[0], sizeof(buf[0])),
++ sockunion2str(&p->vc->remote.nbma, buf[1], sizeof(buf[1])),
++ ret, zbuf_used(zb), addrlen);
++}
++
++static void nhrp_multicast_forward_nbma(union sockunion *nbma_addr, struct interface *ifp, struct zbuf *pkt)
++{
++ struct nhrp_peer *p = nhrp_peer_get(ifp, nbma_addr);
++ if(p && p->online) {
++ /* Send packet */
++ nhrp_multicast_send(p, pkt);
++ }
++ nhrp_peer_unref(p);
++}
++
++static void nhrp_multicast_forward_cache(struct nhrp_cache *c, void *pctx)
++{
++ struct mcast_ctx *ctx = (struct mcast_ctx *)pctx;
++
++ if (c->cur.type == NHRP_CACHE_DYNAMIC && c->cur.peer)
++ nhrp_multicast_forward_nbma(&c->cur.peer->vc->remote.nbma, ctx->ifp, ctx->pkt);
++}
++
++static void nhrp_multicast_forward(struct nhrp_multicast *mcast, void *pctx)
++{
++ struct mcast_ctx *ctx = (struct mcast_ctx *)pctx;
++ struct nhrp_interface *nifp = ctx->ifp->info;
++
++ if (!nifp->enabled)
++ return;
++
++ /* dynamic */
++ if (sockunion_family(&mcast->nbma_addr) == AF_UNSPEC) {
++ nhrp_cache_foreach(ctx->ifp, nhrp_multicast_forward_cache, pctx);
++ return;
++ }
++
++ /* Fixed IP Address */
++ nhrp_multicast_forward_nbma(&mcast->nbma_addr, ctx->ifp, ctx->pkt);
++}
++
++static void netlink_mcast_log_handler(struct nlmsghdr *msg, struct zbuf *zb)
++{
++ struct nfgenmsg *nf;
++ struct rtattr *rta;
++ struct zbuf rtapl, pktpl;
++ struct interface *ifp;
++ uint32_t *out_ndx = NULL;
++ afi_t afi;
++ struct mcast_ctx ctx;
++
++ debugf(NHRP_DEBUG_COMMON,"Inside %s\n", __func__);
++
++ nf = znl_pull(zb, sizeof(*nf));
++ if (!nf)
++ return;
++
++ memset(&pktpl, 0, sizeof(pktpl));
++ while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) {
++ switch (rta->rta_type) {
++ case NFULA_IFINDEX_OUTDEV:
++ out_ndx = znl_pull(&rtapl, sizeof(*out_ndx));
++ break;
++ case NFULA_PAYLOAD:
++ pktpl = rtapl;
++ break;
++ /* NFULA_HWHDR exists and is supposed to contain source
++ * hardware address. However, for ip_gre it seems to be
++ * the nexthop destination address if the packet matches
++ * route. */
++ }
++ }
++
++ if (!out_ndx || !zbuf_used(&pktpl))
++ return;
++
++ ifp = if_lookup_by_index(htonl(*out_ndx), VRF_DEFAULT);
++ if (!ifp)
++ return;
++
++ debugf(NHRP_DEBUG_COMMON,"Outgoing interface = %s\n", ifp->name);
++
++ ctx = (struct mcast_ctx) {
++ .ifp = ifp,
++ .pkt = &pktpl,
++ };
++
++ for (afi = 0; afi < AFI_MAX; afi++) {
++ nhrp_multicast_foreach(ifp, afi, nhrp_multicast_forward, (void *)&ctx);
++ }
++}
++
++static int netlink_mcast_log_recv(struct thread *t)
++{
++ uint8_t buf[65535]; /* Max OSPF Packet size */
++ int fd = THREAD_FD(t);
++ struct zbuf payload, zb;
++ struct nlmsghdr *n;
++
++ netlink_mcast_log_thread = NULL;
++
++ zbuf_init(&zb, buf, sizeof(buf), 0);
++ while (zbuf_recv(&zb, fd) > 0) {
++ while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) {
++ debugf(NHRP_DEBUG_COMMON,
++ "Netlink-mcast-log: Received msg_type %u, msg_flags %u",
++ n->nlmsg_type, n->nlmsg_flags);
++ switch (n->nlmsg_type) {
++ case (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET:
++ netlink_mcast_log_handler(n, &payload);
++ break;
++ }
++ }
++ }
++
++ thread_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd,
++ &netlink_mcast_log_thread);
++
++ return 0;
++}
++
++static void netlink_mcast_log_register(int fd, int group)
++{
++ struct nlmsghdr *n;
++ struct nfgenmsg *nf;
++ struct nfulnl_msg_config_cmd cmd;
++ struct zbuf *zb = zbuf_alloc(512);
++
++ n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG,
++ NLM_F_REQUEST | NLM_F_ACK);
++ nf = znl_push(zb, sizeof(*nf));
++ *nf = (struct nfgenmsg){
++ .nfgen_family = AF_UNSPEC,
++ .version = NFNETLINK_V0,
++ .res_id = htons(group),
++ };
++ cmd.command = NFULNL_CFG_CMD_BIND;
++ znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd));
++ znl_nlmsg_complete(zb, n);
++
++ zbuf_send(zb, fd);
++ zbuf_free(zb);
++}
++
++static void netlink_mcast_set_nflog_group(struct interface *ifp, int nlgroup)
++{
++ if (netlink_mcast_log_fd >= 0) {
++ THREAD_OFF(netlink_mcast_log_thread);
++ close(netlink_mcast_log_fd);
++ netlink_mcast_log_fd = -1;
++ debugf(NHRP_DEBUG_COMMON, "De-register nflog group");
++ }
++ netlink_mcast_nflog_group = nlgroup;
++ if (nlgroup) {
++ netlink_mcast_log_fd = znl_open(NETLINK_NETFILTER, 0);
++ if (netlink_mcast_log_fd < 0)
++ return;
++
++ netlink_mcast_log_register(netlink_mcast_log_fd, nlgroup);
++ thread_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd,
++ &netlink_mcast_log_thread);
++ debugf(NHRP_DEBUG_COMMON, "Register nflog group: %d", netlink_mcast_nflog_group);
++ }
++}
++
++static int nhrp_multicast_free(struct interface *ifp, struct nhrp_multicast *mcast)
++{
++ list_del(&mcast->list_entry);
++ XFREE(MTYPE_NHRP_MULTICAST, mcast);
++ if (--nhrp_multicast_ip_count == 0)
++ netlink_mcast_set_nflog_group(ifp, 0);
++ return 0;
++}
++
++int nhrp_multicast_add(struct interface *ifp, afi_t afi, union sockunion *nbma_addr)
++{
++ struct nhrp_interface *nifp = ifp->info;
++ struct nhrp_multicast *mcast;
++ char buf[SU_ADDRSTRLEN];
++
++ list_for_each_entry(mcast, &nifp->afi[afi].mcastlist_head, list_entry)
++ {
++ if (sockunion_same(&mcast->nbma_addr, nbma_addr))
++ return NHRP_ERR_ENTRY_EXISTS;
++ }
++
++ mcast = XMALLOC(MTYPE_NHRP_MULTICAST, sizeof(struct nhrp_multicast));
++
++ *mcast = (struct nhrp_multicast){
++ .afi = afi,
++ .ifp = ifp,
++ .nbma_addr = *nbma_addr,
++ };
++ list_add_tail(&mcast->list_entry, &nifp->afi[afi].mcastlist_head);
++
++ if (netlink_mcast_log_fd == -1)
++ netlink_mcast_set_nflog_group(ifp, MCAST_NFLOG_GROUP);
++
++ nhrp_multicast_ip_count++;
++
++ sockunion2str(nbma_addr, buf, sizeof(buf));
++ debugf(NHRP_DEBUG_COMMON, "Adding multicast entry (%s) [%d]", buf, nhrp_multicast_ip_count);
++
++ return NHRP_OK;
++}
++
++int nhrp_multicast_del(struct interface *ifp, afi_t afi, union sockunion *nbma_addr)
++{
++ struct nhrp_interface *nifp = ifp->info;
++ struct nhrp_multicast *mcast, *tmp;
++ char buf[SU_ADDRSTRLEN];
++
++ list_for_each_entry_safe(mcast, tmp, &nifp->afi[afi].mcastlist_head,
++ list_entry)
++ {
++ if (!sockunion_same(&mcast->nbma_addr, nbma_addr))
++ continue;
++
++ sockunion2str(nbma_addr, buf, sizeof(buf));
++ debugf(NHRP_DEBUG_COMMON, "Deleting multicast entry (%s) [%d]", buf, nhrp_multicast_ip_count);
++
++ nhrp_multicast_free(ifp, mcast);
++
++ return NHRP_OK;
++ }
++
++ return NHRP_ERR_ENTRY_NOT_FOUND;
++}
++
++void nhrp_multicast_interface_del(struct interface *ifp)
++{
++ struct nhrp_interface *nifp = ifp->info;
++ struct nhrp_multicast *mcast, *tmp;
++ afi_t afi;
++
++ for (afi = 0; afi < AFI_MAX; afi++) {
++ debugf(NHRP_DEBUG_COMMON, "Cleaning up multicast entries (%d, %d)", !list_empty(&nifp->afi[afi].mcastlist_head), nhrp_multicast_ip_count);
++
++ list_for_each_entry_safe(
++ mcast, tmp, &nifp->afi[afi].mcastlist_head,
++ list_entry) {
++ nhrp_multicast_free(ifp, mcast);
++ }
++ }
++}
++
++void nhrp_multicast_foreach(struct interface *ifp, afi_t afi,
++ void (*cb)(struct nhrp_multicast *, void *),
++ void *ctx)
++{
++ struct nhrp_interface *nifp = ifp->info;
++ struct nhrp_multicast *mcast;
++
++ list_for_each_entry(mcast, &nifp->afi[afi].mcastlist_head, list_entry)
++ {
++ cb (mcast, ctx);
++ }
++}
+--- a/nhrpd/nhrp_peer.c
++++ b/nhrpd/nhrp_peer.c
+@@ -337,7 +337,8 @@ void nhrp_peer_send(struct nhrp_peer *p,
+
+ os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
+ sockunion_get_addr(&p->vc->remote.nbma),
+- sockunion_get_addrlen(&p->vc->remote.nbma));
++ sockunion_get_addrlen(&p->vc->remote.nbma),
++ ETH_P_NHRP);
+ zbuf_reset(zb);
+ }
+
+--- a/nhrpd/nhrp_vty.c
++++ b/nhrpd/nhrp_vty.c
+@@ -569,6 +569,53 @@ DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd
+ return CMD_SUCCESS;
+ }
+
++DEFUN(if_nhrp_map_multicast, if_nhrp_map_multicast_cmd,
++ AFI_CMD " nhrp map multicast <A.B.C.D|X:X::X:X|dynamic>",
++ AFI_STR
++ NHRP_STR
++ "Multicast NBMA Configuration\n"
++ "Use this NBMA mapping for multicasts\n"
++ "IPv4 NBMA address\n"
++ "IPv6 NBMA address\n"
++ "Dynamically learn destinations from client registrations on hub\n")
++{
++ VTY_DECLVAR_CONTEXT(interface, ifp);
++ afi_t afi = cmd_to_afi(argv[0]);
++ union sockunion nbma_addr;
++ int ret;
++
++ if (str2sockunion(argv[4]->arg, &nbma_addr) < 0)
++ sockunion_family(&nbma_addr) = AF_UNSPEC;
++
++ ret = nhrp_multicast_add(ifp, afi, &nbma_addr);
++
++ return nhrp_vty_return(vty, ret);
++}
++
++DEFUN(if_no_nhrp_map_multicast, if_no_nhrp_map_multicast_cmd,
++ "no " AFI_CMD " nhrp map multicast <A.B.C.D|X:X::X:X|dynamic>",
++ NO_STR
++ AFI_STR
++ NHRP_STR
++ "Multicast NBMA Configuration\n"
++ "Use this NBMA mapping for multicasts\n"
++ "IPv4 NBMA address\n"
++ "IPv6 NBMA address\n"
++ "Dynamically learn destinations from client registrations on hub\n")
++{
++ VTY_DECLVAR_CONTEXT(interface, ifp);
++ afi_t afi = cmd_to_afi(argv[1]);
++ union sockunion nbma_addr;
++ int ret;
++
++ if (str2sockunion(argv[5]->arg, &nbma_addr) < 0)
++ sockunion_family(&nbma_addr) = AF_UNSPEC;
++
++ ret = nhrp_multicast_del(ifp, afi, &nbma_addr);
++
++ return nhrp_vty_return(vty, ret);
++}
++
+ DEFUN(if_nhrp_nhs, if_nhrp_nhs_cmd,
+ AFI_CMD " nhrp nhs <A.B.C.D|X:X::X:X|dynamic> nbma <A.B.C.D|FQDN>",
+ AFI_STR
+@@ -1040,6 +1087,7 @@ static int interface_config_write(struct
+ struct interface *ifp;
+ struct nhrp_interface *nifp;
+ struct nhrp_nhs *nhs;
++ struct nhrp_multicast *mcast;
+ const char *aficmd;
+ afi_t afi;
+ char buf[SU_ADDRSTRLEN];
+@@ -1109,6 +1157,19 @@ static int interface_config_write(struct
+ sizeof(buf)),
+ nhs->nbma_fqdn);
+ }
++
++ list_for_each_entry(mcast, &ad->mcastlist_head,
++ list_entry)
++ {
++ vty_out(vty, " %s nhrp map multicast %s\n",
++ aficmd,
++ sockunion_family(&mcast->nbma_addr)
++ == AF_UNSPEC
++ ? "dynamic"
++ : sockunion2str(
++ &mcast->nbma_addr, buf,
++ sizeof(buf)));
++ }
+ }
+
+ vty_endframe(vty, "!\n");
+@@ -1163,6 +1224,8 @@ void nhrp_config_init(void)
+ install_element(INTERFACE_NODE, &if_no_nhrp_reg_flags_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_map_cmd);
+ install_element(INTERFACE_NODE, &if_no_nhrp_map_cmd);
++ install_element(INTERFACE_NODE, &if_nhrp_map_multicast_cmd);
++ install_element(INTERFACE_NODE, &if_no_nhrp_map_multicast_cmd);
+ install_element(INTERFACE_NODE, &if_nhrp_nhs_cmd);
+ install_element(INTERFACE_NODE, &if_no_nhrp_nhs_cmd);
+ }
+--- a/nhrpd/nhrpd.h
++++ b/nhrpd/nhrpd.h
+@@ -24,6 +24,7 @@ DECLARE_MGROUP(NHRPD)
+
+ #define NHRP_VTY_PORT 2610
+ #define NHRP_DEFAULT_CONFIG "nhrpd.conf"
++#define MCAST_NFLOG_GROUP 224
+
+ extern struct thread_master *master;
+
+@@ -259,6 +260,13 @@ struct nhrp_nhs {
+ struct list_head reglist_head;
+ };
+
++struct nhrp_multicast {
++ struct interface *ifp;
++ struct list_head list_entry;
++ afi_t afi;
++ union sockunion nbma_addr; /* IP-address */
++};
++
+ struct nhrp_registration {
+ struct list_head reglist_entry;
+ struct thread *t_register;
+@@ -304,6 +312,7 @@ struct nhrp_interface {
+ unsigned short mtu;
+ unsigned int holdtime;
+ struct list_head nhslist_head;
++ struct list_head mcastlist_head;
+ } afi[AFI_MAX];
+ };
+
+@@ -345,6 +354,13 @@ void nhrp_nhs_foreach(struct interface *
+ void *ctx);
+ void nhrp_nhs_interface_del(struct interface *ifp);
+
++int nhrp_multicast_add(struct interface *ifp, afi_t afi, union sockunion *nbma_addr);
++int nhrp_multicast_del(struct interface *ifp, afi_t afi, union sockunion *nbma_addr);
++void nhrp_multicast_interface_del(struct interface *ifp);
++void nhrp_multicast_foreach(struct interface *ifp, afi_t afi,
++ void (*cb)(struct nhrp_multicast *, void *),
++ void *ctx);
++
+ void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
+ void nhrp_route_announce(int add, enum nhrp_cache_type type,
+ const struct prefix *p, struct interface *ifp,
+--- a/nhrpd/os.h
++++ b/nhrpd/os.h
+@@ -1,7 +1,7 @@
+
+ int os_socket(void);
+ int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr,
+- size_t addrlen);
++ size_t addrlen, uint16_t protocol);
+ int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr,
+ size_t *addrlen);
+ int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af);
+--- a/nhrpd/subdir.am
++++ b/nhrpd/subdir.am
+@@ -21,6 +21,7 @@ nhrpd_nhrpd_SOURCES = \
+ nhrpd/nhrp_nhs.c \
+ nhrpd/nhrp_packet.c \
+ nhrpd/nhrp_peer.c \
++ nhrpd/nhrp_multicast.c \
+ nhrpd/nhrp_route.c \
+ nhrpd/nhrp_shortcut.c \
+ nhrpd/nhrp_vc.c \
+--- a/ospfd/ospf_interface.c
++++ b/ospfd/ospf_interface.c
+@@ -534,6 +534,8 @@ static struct ospf_if_params *ospf_new_i
+ oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER);
+ oip->is_v_wait_set = false;
+
++ oip->ptp_dmvpn = 0;
++
+ return oip;
+ }
+
+--- a/ospfd/ospf_interface.h
++++ b/ospfd/ospf_interface.h
+@@ -105,6 +105,9 @@ struct ospf_if_params {
+
+ /* BFD configuration */
+ struct bfd_info *bfd_info;
++
++ /* point-to-point DMVPN configuration */
++ uint8_t ptp_dmvpn;
+ };
+
+ enum { MEMBER_ALLROUTERS = 0,
+@@ -167,6 +170,9 @@ struct ospf_interface {
+ /* OSPF Network Type. */
+ uint8_t type;
+
++ /* point-to-point DMVPN configuration */
++ uint8_t ptp_dmvpn;
++
+ /* State of Interface State Machine. */
+ uint8_t state;
+
+--- a/ospfd/ospf_lsa.c
++++ b/ospfd/ospf_lsa.c
+@@ -469,6 +469,12 @@ static char link_info_set(struct stream
+ }
+
+ /* Describe Point-to-Point link (Section 12.4.1.1). */
++
++/* Note: If the interface is configured as point-to-point dmvpn then the other
++ * end of link is dmvpn hub with point-to-multipoint ospf network type. The
++ * hub then expects this router to populate the stub network and also Link Data
++ * Field set to IP Address and not MIB-II ifIndex
++ */
+ static int lsa_link_ptop_set(struct stream **s, struct ospf_interface *oi)
+ {
+ int links = 0;
+@@ -482,7 +488,8 @@ static int lsa_link_ptop_set(struct stre
+ if ((nbr = ospf_nbr_lookup_ptop(oi)))
+ if (nbr->state == NSM_Full) {
+ if (CHECK_FLAG(oi->connected->flags,
+- ZEBRA_IFA_UNNUMBERED)) {
++ ZEBRA_IFA_UNNUMBERED)
++ && !oi->ptp_dmvpn) {
+ /* For unnumbered point-to-point networks, the
+ Link Data field
+ should specify the interface's MIB-II ifIndex
+@@ -500,7 +507,8 @@ static int lsa_link_ptop_set(struct stre
+ }
+
+ /* no need for a stub link for unnumbered interfaces */
+- if (!CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) {
++ if (oi->ptp_dmvpn
++ || !CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) {
+ /* Regardless of the state of the neighboring router, we must
+ add a Type 3 link (stub network).
+ N.B. Options 1 & 2 share basically the same logic. */
+--- a/ospfd/ospf_vty.c
++++ b/ospfd/ospf_vty.c
+@@ -7560,20 +7560,21 @@ DEFUN_HIDDEN (no_ospf_hello_interval,
+ return no_ip_ospf_hello_interval(self, vty, argc, argv);
+ }
+
+-DEFUN (ip_ospf_network,
+- ip_ospf_network_cmd,
+- "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point>",
+- "IP Information\n"
+- "OSPF interface commands\n"
+- "Network type\n"
+- "Specify OSPF broadcast multi-access network\n"
+- "Specify OSPF NBMA network\n"
+- "Specify OSPF point-to-multipoint network\n"
+- "Specify OSPF point-to-point network\n")
++DEFUN(ip_ospf_network, ip_ospf_network_cmd,
++ "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]>",
++ "IP Information\n"
++ "OSPF interface commands\n"
++ "Network type\n"
++ "Specify OSPF broadcast multi-access network\n"
++ "Specify OSPF NBMA network\n"
++ "Specify OSPF point-to-multipoint network\n"
++ "Specify OSPF point-to-point network\n"
++ "Specify OSPF point-to-point DMVPN network\n")
+ {
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int idx = 0;
+ int old_type = IF_DEF_PARAMS(ifp)->type;
++ uint8_t old_ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
+ struct route_node *rn;
+
+ if (old_type == OSPF_IFTYPE_LOOPBACK) {
+@@ -7582,16 +7583,22 @@ DEFUN (ip_ospf_network,
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
++ IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
++
+ if (argv_find(argv, argc, "broadcast", &idx))
+ IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_BROADCAST;
+ else if (argv_find(argv, argc, "non-broadcast", &idx))
+ IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA;
+ else if (argv_find(argv, argc, "point-to-multipoint", &idx))
+ IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT;
+- else if (argv_find(argv, argc, "point-to-point", &idx))
++ else if (argv_find(argv, argc, "point-to-point", &idx)) {
+ IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT;
++ if (argv_find(argv, argc, "dmvpn", &idx))
++ IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1;
++ }
+
+- if (IF_DEF_PARAMS(ifp)->type == old_type)
++ if (IF_DEF_PARAMS(ifp)->type == old_type
++ && IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn)
+ return CMD_SUCCESS;
+
+ SET_IF_PARAM(IF_DEF_PARAMS(ifp), type);
+@@ -7603,6 +7610,7 @@ DEFUN (ip_ospf_network,
+ continue;
+
+ oi->type = IF_DEF_PARAMS(ifp)->type;
++ oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
+
+ if (oi->state > ISM_Down) {
+ OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
+@@ -7643,6 +7651,7 @@ DEFUN (no_ip_ospf_network,
+ struct route_node *rn;
+
+ IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp);
++ IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
+
+ if (IF_DEF_PARAMS(ifp)->type == old_type)
+ return CMD_SUCCESS;
+@@ -9888,6 +9897,10 @@ static int config_write_interface_one(st
+ vty_out(vty, " ip ospf network %s",
+ ospf_int_type_str
+ [params->type]);
++ if (params->type
++ == OSPF_IFTYPE_POINTOPOINT
++ && params->ptp_dmvpn)
++ vty_out(vty, " dmvpn");
+ if (params != IF_DEF_PARAMS(ifp) && rn)
+ vty_out(vty, " %s",
+ inet_ntoa(
+--- a/ospfd/ospfd.c
++++ b/ospfd/ospfd.c
+@@ -1017,6 +1017,7 @@ static void add_ospf_interface(struct co
+ /* If network type is specified previously,
+ skip network type setting. */
+ oi->type = IF_DEF_PARAMS(co->ifp)->type;
++ oi->ptp_dmvpn = IF_DEF_PARAMS(co->ifp)->ptp_dmvpn;
+
+ /* Add pseudo neighbor. */
+ ospf_nbr_self_reset(oi, oi->ospf->router_id);
+--- a/doc/user/nhrpd.rst
++++ b/doc/user/nhrpd.rst
+@@ -189,6 +189,34 @@ and
+ https://git.alpinelinux.org/user/tteras/strongswan/log/?h=tteras
+ git repositories for the patches.
+
++.. _multicast-functionality:
++
++Multicast Functionality
++=======================
++
++nhrpd can be configured to forward multicast packets, allowing routing
++protocols that use multicast (such as OSPF) to be supported in the DMVPN
++network.
++
++This support requires an NFLOG redirection rule to work:
++
++ .. code-block:: shell
++
++ iptables -I OUTPUT -d 224.0.0.0/24 -o gre1 -j NFLOG --nflog-group 2
++
++.. index:: nhrp multicast-nflog-group (1-65535)
++.. clicmd:: nhrp multicast-nflog-group (1-65535)
++
++ Sets the nflog group that nhrpd will listen on for multicast packets. This
++ value must match the nflog-group value set in the iptables rule.
++
++.. index:: ip nhrp map multicast A.B.C.D|X:X::X:X A.B.C.D|dynamic
++.. clicmd:: ip nhrp map multicast A.B.C.D|X:X::X:X A.B.C.D|dynamic
++
++ Sends multicast packets to the specified NBMA address. If dynamic is
++ specified then destination NBMA address (or addresses) are learnt
++ dynamically.
++
+ .. _nhrp-events:
+
+ NHRP Events
+--- a/doc/user/ospfd.rst
++++ b/doc/user/ospfd.rst
+@@ -687,8 +687,8 @@ Interfaces
+ :clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also
+ specified for the interface.
+
+-.. index:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)
+-.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)
++.. index:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
++.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
+
+ When configuring a point-to-point network on an interface and the interface
+ has a /32 address associated with then OSPF will treat the interface
+@@ -870,6 +870,9 @@ Redistribution
+ .. index:: no router zebra
+ .. clicmd:: no router zebra
+
++ When used in a DMVPN network at a spoke, this OSPF will be configured in
++ point-to-point, but the HUB will be a point-to-multipoint. To make this
++ topology work, specify the optional 'dmvpn' parameter at the spoke.
+
+ .. _showing-ospf-information:
+
--- /dev/null
+From bd9caa8f11d931db21f628ad61be042147861ad4 Mon Sep 17 00:00:00 2001
+From: Mark Stapp <mjs@voltanet.io>
+Date: Fri, 26 Feb 2021 11:16:09 -0500
+Subject: [PATCH 1/3] lib: fix some misc SA warnings
+
+- clippy.c: fix valid memleak
+- defun_lex.l: suppress warnings in generated code
+- northbound_cli.c: suppress warning in eldritch libyang macro
+
+Signed-off-by: Quentin Young <qlyoung@nvidia.com>
+---
+ lib/clippy.c | 4 +++-
+ lib/defun_lex.l | 4 ++++
+ lib/northbound_cli.c | 12 ++++++++++++
+ 3 files changed, 19 insertions(+), 1 deletion(-)
+
+--- a/lib/clippy.c
++++ b/lib/clippy.c
+@@ -51,7 +51,8 @@ int main(int argc, char **argv)
+ #if PY_VERSION_HEX >= 0x03040000 /* 3.4 */
+ Py_SetStandardStreamEncoding("UTF-8", NULL);
+ #endif
+- Py_SetProgramName(wconv(argv[0]));
++ wchar_t *name = wconv(argv[0]);
++ Py_SetProgramName(name);
+ PyImport_AppendInittab("_clippy", command_py_init);
+
+ Py_Initialize();
+@@ -67,6 +68,8 @@ int main(int argc, char **argv)
+ fp = fopen(pyfile, "r");
+ if (!fp) {
+ fprintf(stderr, "%s: %s\n", pyfile, strerror(errno));
++
++ free(name);
+ return 1;
+ }
+ } else {
+@@ -85,6 +88,8 @@ int main(int argc, char **argv)
+ if (PyRun_AnyFile(fp, pyfile)) {
+ if (PyErr_Occurred())
+ PyErr_Print();
++
++ free(name);
+ return 1;
+ }
+ Py_Finalize();
+@@ -93,6 +98,7 @@ int main(int argc, char **argv)
+ for (int i = 1; i < argc; i++)
+ free(wargv[i - 1]);
+ #endif
++ free(name);
+ free(wargv);
+ return 0;
+ }
+--- a/lib/defun_lex.l
++++ b/lib/defun_lex.l
+@@ -80,6 +80,8 @@ static void extendbuf(char **what, const
+ }
+ #define extend(x) extendbuf(&value, x)
+
++#ifndef __clang_analyzer__
++
+ %}
+
+ ID [A-Za-z0-9_]+
+@@ -157,6 +159,8 @@ SPECIAL [(),]
+
+ %%
+
++#endif /* __clang_analyzer__ */
++
+ static int yylex_clr(char **retbuf)
+ {
+ int rv = def_yylex();
+--- a/lib/northbound_cli.c
++++ b/lib/northbound_cli.c
+@@ -595,7 +595,19 @@ void nb_cli_show_dnode_cmds(struct vty *
+ (*nb_node->cbs.cli_show_end)(vty, parent);
+ }
+
++ /*
++ * There is a possible path in this macro that ends up
++ * dereferencing child->parent->parent. We just null checked
++ * child->parent by checking (ly_iter_next_up(child) != NULL)
++ * above.
++ *
++ * I am not sure whether it is possible for the other
++ * conditions within this macro guarding the problem
++ * dereference to be satisfied when child->parent == NULL.
++ */
++#ifndef __clang_analyzer__
+ LY_TREE_DFS_END(root, next, child);
++#endif
+ }
+ }
+
--- a/lib/northbound.h
+++ b/lib/northbound.h
-@@ -562,11 +562,7 @@ struct frr_yang_module_info {
+@@ -569,11 +569,7 @@ struct frr_yang_module_info {
/* Priority - lower priorities are processed first. */
uint32_t priority;