]> git.proxmox.com Git - mirror_frr.git/commitdiff
ospfd: api: add reachable router notifications
authorChristian Hopps <chopps@labn.net>
Wed, 1 Jun 2022 19:25:35 +0000 (15:25 -0400)
committerChristian Hopps <chopps@labn.net>
Thu, 2 Jun 2022 20:37:16 +0000 (16:37 -0400)
Reachable router information is used by OSPF opaque clients in order
to determine if the router advertising the opaque LSA data is
reachable (i.e., 2-way conectivity check).

Signed-off-by: Christian Hopps <chopps@labn.net>
ospfd/ospf_api.c
ospfd/ospf_api.h
ospfd/ospf_apiserver.c
ospfd/ospf_apiserver.h
ospfd/ospf_spf.c

index 81de8827548b75fbd674cc6cf36d4c226e5332bb..99bc6c0b03132687c2e0472c75247e07b0c3e38d 100644 (file)
@@ -177,6 +177,10 @@ const char *ospf_api_typename(int msgtype)
                {
                        MSG_NSM_CHANGE, "NSM change",
                },
+               {
+                       MSG_REACHABLE_CHANGE,
+                       "Reachable change",
+               },
        };
 
        int i, n = array_size(NameTab);
@@ -651,4 +655,31 @@ struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
        return msg_new(msgtype, nmsg, seqnum, len);
 }
 
+struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
+                                    struct in_addr *add, uint16_t nremove,
+                                    struct in_addr *remove)
+{
+       uint8_t buf[OSPF_API_MAX_MSG_SIZE];
+       struct msg_reachable_change *nmsg = (void *)buf;
+       const uint insz = sizeof(*nmsg->router_ids);
+       const uint nmax = (sizeof(buf) - sizeof(*nmsg)) / insz;
+       uint len;
+
+       if (nadd > nmax)
+               nadd = nmax;
+       if (nremove > (nmax - nadd))
+               nremove = (nmax - nadd);
+
+       if (nadd)
+               memcpy(nmsg->router_ids, add, nadd * insz);
+       if (nremove)
+               memcpy(&nmsg->router_ids[nadd], remove, nremove * insz);
+
+       nmsg->nadd = htons(nadd);
+       nmsg->nremove = htons(nremove);
+       len = sizeof(*nmsg) + insz * (nadd + nremove);
+
+       return msg_new(MSG_REACHABLE_CHANGE, nmsg, seqnum, len);
+}
+
 #endif /* SUPPORT_OSPF_API */
index c20284aed5d808ad3296acdec474dd3636443874..7ff39dc123c14cd6ab2e5ddde0dc937881f8065d 100644 (file)
@@ -26,6 +26,9 @@
 #ifndef _OSPF_API_H
 #define _OSPF_API_H
 
+#include <zebra.h>
+#include "ospf_lsa.h"
+
 #define OSPF_API_VERSION           1
 
 /* MTYPE definition is not reflected to "memory.h". */
@@ -112,6 +115,7 @@ extern void msg_fifo_free(struct msg_fifo *fifo);
 #define MSG_SYNC_LSDB             4
 #define MSG_ORIGINATE_REQUEST     5
 #define MSG_DELETE_REQUEST        6
+#define MSG_SYNC_REACHABLE        7
 
 /* Messages from OSPF daemon. */
 #define MSG_REPLY                10
@@ -122,6 +126,7 @@ extern void msg_fifo_free(struct msg_fifo *fifo);
 #define MSG_DEL_IF               15
 #define MSG_ISM_CHANGE           16
 #define MSG_NSM_CHANGE           17
+#define MSG_REACHABLE_CHANGE     18
 
 struct msg_register_opaque_type {
        uint8_t lsatype;
@@ -247,6 +252,12 @@ struct msg_nsm_change {
        uint8_t pad[3];
 };
 
+struct msg_reachable_change {
+       uint16_t nadd;
+       uint16_t nremove;
+       struct in_addr router_ids[]; /* add followed by remove */
+};
+
 /* We make use of a union to define a structure that covers all
    possible API messages. This allows us to find out how much memory
    needs to be reserved for the largest API message. */
@@ -265,6 +276,7 @@ struct apimsg {
                struct msg_ism_change ism_change;
                struct msg_nsm_change nsm_change;
                struct msg_lsa_change_notify lsa_change_notify;
+               struct msg_reachable_change reachable_change;
        } u;
 };
 
@@ -320,6 +332,10 @@ extern struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
                                             uint8_t is_self_originated,
                                             struct lsa_header *data);
 
+extern struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
+                                           struct in_addr *add,
+                                           uint16_t nremove,
+                                           struct in_addr *remove);
 /* string printing functions */
 extern const char *ospf_api_errname(int errcode);
 extern const char *ospf_api_typename(int msgtype);
index d6c1e28d414799d1272f5141062a8439cb63c705..259ba2a3f1274f87c699717720518cd497a4c49b 100644 (file)
@@ -726,6 +726,7 @@ static int ospf_apiserver_send_msg(struct ospf_apiserver *apiserv,
        case MSG_DEL_IF:
        case MSG_ISM_CHANGE:
        case MSG_NSM_CHANGE:
+       case MSG_REACHABLE_CHANGE:
                fifo = apiserv->out_async_fifo;
                fd = apiserv->fd_async;
                event = OSPF_APISERVER_ASYNC_WRITE;
@@ -799,6 +800,9 @@ int ospf_apiserver_handle_msg(struct ospf_apiserver *apiserv, struct msg *msg)
        case MSG_DELETE_REQUEST:
                rc = ospf_apiserver_handle_delete_request(apiserv, msg);
                break;
+       case MSG_SYNC_REACHABLE:
+               rc = ospf_apiserver_handle_sync_reachable(apiserv, msg);
+               break;
        default:
                zlog_warn("ospf_apiserver_handle_msg: Unknown message type: %d",
                          msg->hdr.msgtype);
@@ -1343,6 +1347,59 @@ int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv,
        return rc;
 }
 
+/* -----------------------------------------------------------
+ * Followings are functions for Reachability synchronization.
+ * -----------------------------------------------------------
+ */
+
+int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv,
+                                        struct msg *msg)
+{
+       struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+       struct route_table *rt = ospf->all_rtrs;
+       uint32_t seqnum = msg_get_seq(msg);
+       struct in_addr *a, *abuf;
+       struct msg_reachable_change *areach;
+       struct msg *amsg;
+       uint mcount, count;
+       int _rc, rc = 0;
+
+       if (!rt)
+               goto out;
+
+       /* send all adds based on current reachable routers */
+       a = abuf = XCALLOC(MTYPE_OSPF_APISERVER,
+                          sizeof(struct in_addr) * rt->count);
+       for (struct route_node *rn = route_top(rt); rn; rn = route_next(rn))
+               if (listhead((struct list *)rn->info))
+                       *a++ = rn->p.u.prefix4;
+
+       assert((a - abuf) <= (long)rt->count);
+       count = (a - abuf);
+
+       a = abuf;
+       while (count && !rc) {
+               amsg = new_msg_reachable_change(seqnum, count, a, 0, NULL);
+               areach = (struct msg_reachable_change *)STREAM_DATA(amsg->s);
+               mcount = ntohs(areach->nadd) + ntohs(areach->nremove);
+               assert(mcount <= count);
+               a = a + mcount;
+               count -= mcount;
+               rc = ospf_apiserver_send_msg(apiserv, amsg);
+               msg_free(amsg);
+       }
+       XFREE(MTYPE_OSPF_APISERVER, abuf);
+
+out:
+       zlog_info("ospf_apiserver_handle_sync_reachable: rc %d", rc);
+       /* Send a reply back to client with return code */
+       _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
+       zlog_info("ospf_apiserver_handle_sync_reachable: _rc %d", _rc);
+       rc = rc ? rc : _rc;
+       apiserv->reachable_sync = !rc;
+       return rc;
+}
+
 
 /* -----------------------------------------------------------
  * Following are functions to originate or update LSA
@@ -2471,4 +2528,115 @@ int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa)
        return apiserver_notify_clients_lsa(MSG_LSA_DELETE_NOTIFY, lsa);
 }
 
+/* -------------------------------------------------------------
+ * Reachable functions
+ * -------------------------------------------------------------
+ */
+
+static inline int cmp_route_nodes(struct route_node *orn,
+                                 struct route_node *nrn)
+{
+       if (!orn)
+               return 1;
+       else if (!nrn)
+               return -1;
+       else if (orn->p.u.prefix4.s_addr < nrn->p.u.prefix4.s_addr)
+               return -1;
+       else if (orn->p.u.prefix4.s_addr > nrn->p.u.prefix4.s_addr)
+               return 1;
+       else
+               return 0;
+}
+
+void ospf_apiserver_notify_reachable(struct route_table *ort,
+                                    struct route_table *nrt)
+{
+       struct msg *msg;
+       struct msg_reachable_change *areach;
+       struct route_node *orn, *nrn;
+       const uint insz = sizeof(struct in_addr);
+       struct in_addr *abuf = NULL, *dbuf = NULL;
+       struct in_addr *a = NULL, *d = NULL;
+       uint nadd, nremove;
+       int cmp;
+
+       if (!ort && !nrt) {
+               if (IS_DEBUG_OSPF_CLIENT_API)
+                       zlog_debug("%s: no routing tables", __func__);
+               return;
+       }
+       if (nrt && nrt->count)
+               a = abuf = XCALLOC(MTYPE_OSPF_APISERVER, insz * nrt->count);
+       if (ort && ort->count)
+               d = dbuf = XCALLOC(MTYPE_OSPF_APISERVER, insz * ort->count);
+
+       /* walk both tables */
+       orn = ort ? route_top(ort) : NULL;
+       nrn = nrt ? route_top(nrt) : NULL;
+       while (orn || nrn) {
+               if (orn && !listhead((struct list *)orn->info)) {
+                       orn = route_next(orn);
+                       continue;
+               }
+               if (nrn && !listhead((struct list *)nrn->info)) {
+                       nrn = route_next(nrn);
+                       continue;
+               }
+               cmp = cmp_route_nodes(orn, nrn);
+               if (!cmp) {
+                       /* if old == new advance old and new */
+                       if (IS_DEBUG_OSPF_CLIENT_API)
+                               zlog_debug("keeping router id: %pI4",
+                                          &orn->p.u.prefix4);
+                       orn = route_next(orn);
+                       nrn = route_next(nrn);
+               } else if (cmp < 0) {
+                       assert(d != NULL); /* Silence SA warning */
+
+                       /* if old < new, delete old, advance old */
+                       *d++ = orn->p.u.prefix4;
+                       if (IS_DEBUG_OSPF_CLIENT_API)
+                               zlog_debug("removing router id: %pI4",
+                                          &orn->p.u.prefix4);
+                       orn = route_next(orn);
+               } else {
+                       assert(a != NULL); /* Silence SA warning */
+
+                       /* if new < old, add new, advance new */
+                       *a++ = nrn->p.u.prefix4;
+                       if (IS_DEBUG_OSPF_CLIENT_API)
+                               zlog_debug("adding router id: %pI4",
+                                          &nrn->p.u.prefix4);
+                       nrn = route_next(nrn);
+               }
+       }
+
+       nadd = abuf ? (a - abuf) : 0;
+       nremove = dbuf ? (d - dbuf) : 0;
+       a = abuf;
+       d = dbuf;
+
+       while (nadd + nremove) {
+               msg = new_msg_reachable_change(0, nadd, a, nremove, d);
+               areach = (struct msg_reachable_change *)STREAM_DATA(msg->s);
+
+               a += ntohs(areach->nadd);
+               nadd = nadd - ntohs(areach->nadd);
+
+               d += ntohs(areach->nremove);
+               nremove = nremove - ntohs(areach->nremove);
+
+               if (IS_DEBUG_OSPF_CLIENT_API)
+                       zlog_debug("%s: adding %d removing %d", __func__,
+                                  ntohs(areach->nadd), ntohs(areach->nremove));
+               ospf_apiserver_clients_notify_all(msg);
+               msg_free(msg);
+       }
+       if (abuf)
+               XFREE(MTYPE_OSPF_APISERVER, abuf);
+       if (dbuf)
+               XFREE(MTYPE_OSPF_APISERVER, dbuf);
+}
+
+
 #endif /* SUPPORT_OSPF_API */
index b4d8bb2f52ff589585c0534442a2ecd252985994..8a756e9c3c8a12864642337f925591b7c1c0d9eb 100644 (file)
 #ifndef _OSPF_APISERVER_H
 #define _OSPF_APISERVER_H
 
+#include <zebra.h>
+#include "ospf_api.h"
+#include "ospf_lsdb.h"
+
 /* MTYPE definition is not reflected to "memory.h". */
 #define MTYPE_OSPF_APISERVER MTYPE_TMP
 #define MTYPE_OSPF_APISERVER_MSGFILTER MTYPE_TMP
@@ -52,6 +56,9 @@ struct ospf_apiserver {
        /* Temporary storage for LSA instances to be refreshed. */
        struct ospf_lsdb reserve;
 
+       /* Sync reachable routers */
+       bool reachable_sync;
+
        /* filter for LSA update/delete notifies */
        struct lsa_filter_type *filter;
 
@@ -144,8 +151,11 @@ extern int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv,
                                                struct msg *msg);
 extern int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv,
                                           struct msg *msg);
+extern int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv,
+                                               struct msg *msg);
 
-
+extern void ospf_apiserver_notify_reachable(struct route_table *ort,
+                                           struct route_table *nrt);
 /* -----------------------------------------------------------
  * Following are functions for LSA origination/deletion
  * -----------------------------------------------------------
index 1974a42f5232ce81511a4113e76f35a1e22fcf33..44549b980cacc92227bd9f6fa2fb16f5fc719d0f 100644 (file)
@@ -48,6 +48,7 @@
 #include "ospfd/ospf_sr.h"
 #include "ospfd/ospf_ti_lfa.h"
 #include "ospfd/ospf_errors.h"
+#include "ospfd/ospf_apiserver.h"
 
 /* Variables to ensure a SPF scheduled log message is printed only once */
 
@@ -1891,6 +1892,7 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
        /* Update all routers routing table */
        ospf->oall_rtrs = ospf->all_rtrs;
        ospf->all_rtrs = all_rtrs;
+       ospf_apiserver_notify_reachable(ospf->oall_rtrs, ospf->all_rtrs);
 
        /* Free old ABR/ASBR routing table */
        if (ospf->old_rtrs)