]> git.puffer.fish Git - mirror/frr.git/commitdiff
ldpd: add support for RLFA clients
authorRenato Westphal <renato@opensourcerouting.org>
Sun, 6 Dec 2020 00:45:52 +0000 (21:45 -0300)
committerRenato Westphal <renato@opensourcerouting.org>
Sat, 9 Jan 2021 01:22:11 +0000 (22:22 -0300)
Add an API that allows IGP client daemons to register/unregister
RLFAs with ldpd.

IGP daemons need to be able to query the LDP labels needed by RLFAs
and monitor label updates that might affect those RLFAs. This is
similar to the NHT mechanism used by bgpd to resolve and monitor
recursive nexthops.

This API is based on the following ZAPI opaque messages:
* LDP_RLFA_REGISTER: used by IGP daemons to register an RLFA with ldpd.
* LDP_RLFA_UNREGISTER_ALL: used by IGP daemons to unregister all of
  their RLFAs with ldpd.
* LDP_RLFA_LABELS: used by ldpd to send RLFA labels to the registered
  clients.

For each RLFA, ldpd needs to return the following labels:
* Outer label(s): the labels advertised by the adjacent routers to
  reach the PQ node;
* Inner label: the label advertised by the PQ node to reach the RLFA
  destination.

For the inner label, ldpd automatically establishes a targeted
neighborship with the PQ node if one doesn't already exist. For that
to work, the PQ node needs to be configured to accept targeted hello
messages. If that doesn't happen, ldpd doesn't send a response to
the IGP client daemon which in turn won't be able to activate the
previously computed RLFA.

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
13 files changed:
ldpd/adjacency.c
ldpd/hello.c
ldpd/lde.c
ldpd/lde.h
ldpd/lde_lib.c
ldpd/ldp_zebra.c
ldpd/ldpd.c
ldpd/ldpd.h
ldpd/ldpe.c
ldpd/rlfa.c [new file with mode: 0644]
ldpd/rlfa.h [new file with mode: 0644]
ldpd/subdir.am
lib/zclient.h

index 795a41491cd4fe9ade5f0093f46695104e6d4538..d390e70ad0d0cd8b9d794e683557173d41633444 100644 (file)
@@ -183,7 +183,8 @@ adj_itimer(struct thread *thread)
 
        if (adj->source.type == HELLO_TARGETED) {
                if (!(adj->source.target->flags & F_TNBR_CONFIGURED) &&
-                   adj->source.target->pw_count == 0) {
+                   adj->source.target->pw_count == 0 &&
+                   adj->source.target->rlfa_count == 0) {
                        /* remove dynamic targeted neighbor */
                        tnbr_del(leconf, adj->source.target);
                        return (0);
@@ -259,7 +260,7 @@ struct tnbr *
 tnbr_check(struct ldpd_conf *xconf, struct tnbr *tnbr)
 {
        if (!(tnbr->flags & (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC)) &&
-           tnbr->pw_count == 0) {
+           tnbr->pw_count == 0 && tnbr->rlfa_count == 0) {
                tnbr_del(xconf, tnbr);
                return (NULL);
        }
index 327cb32434afb0578a7999babf341ba3ed7d92a9..5aa14ed067f9b0d1a0e8009e59651767f5265ade 100644 (file)
@@ -67,7 +67,8 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr)
                af = tnbr->af;
                holdtime = tnbr_get_hello_holdtime(tnbr);
                flags = F_HELLO_TARGETED;
-               if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count)
+               if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count
+                   || tnbr->rlfa_count)
                        flags |= F_HELLO_REQ_TARG;
                fd = (ldp_af_global_get(&global, af))->ldp_edisc_socket;
 
index 5ed0ed4520dc668d0660b732d3396b847e02c1e7..69338b8bad6a407d93ce19b428c0bdc1dc3de010 100644 (file)
@@ -27,6 +27,7 @@
 #include "log.h"
 #include "lde.h"
 #include "ldp_debug.h"
+#include "rlfa.h"
 
 #include <lib/log.h>
 #include "memory.h"
@@ -444,6 +445,10 @@ lde_dispatch_parent(struct thread *thread)
        int                      shut = 0;
        struct fec               fec;
        struct ldp_access       *laccess;
+       struct ldp_rlfa_node     *rnode, *rntmp;
+       struct ldp_rlfa_client   *rclient;
+       struct zapi_rlfa_request *rlfa_req;
+       struct zapi_rlfa_igp     *rlfa_igp;
 
        iev->ev_read = NULL;
 
@@ -650,6 +655,42 @@ lde_dispatch_parent(struct thread *thread)
                        lde_check_filter_af(AF_INET6, &ldeconf->ipv6,
                                laccess->name);
                        break;
+               case IMSG_RLFA_REG:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(struct zapi_rlfa_request)) {
+                               log_warnx("%s: wrong imsg len", __func__);
+                               break;
+                       }
+                       rlfa_req = imsg.data;
+                       rnode = rlfa_node_find(&rlfa_req->destination,
+                                              rlfa_req->pq_address);
+                       if (!rnode)
+                               rnode = rlfa_node_new(&rlfa_req->destination,
+                                                     rlfa_req->pq_address);
+                       rclient = rlfa_client_find(rnode, &rlfa_req->igp);
+                       if (rclient)
+                               /* RLFA already registered - do nothing */
+                               break;
+                       rclient = rlfa_client_new(rnode, &rlfa_req->igp);
+                       lde_rlfa_check(rclient);
+                       break;
+               case IMSG_RLFA_UNREG_ALL:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(struct zapi_rlfa_igp)) {
+                               log_warnx("%s: wrong imsg len", __func__);
+                               break;
+                       }
+                       rlfa_igp = imsg.data;
+
+                       RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head,
+                                        &rlfa_node_tree, rntmp) {
+                               rclient = rlfa_client_find(rnode, rlfa_igp);
+                               if (!rclient)
+                                       continue;
+
+                               rlfa_client_del(rclient);
+                       }
+                       break;
                default:
                        log_debug("%s: unexpected imsg %d", __func__,
                            imsg.hdr.type);
@@ -875,6 +916,48 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh)
        }
 }
 
+void
+lde_fec2prefix(const struct fec *fec, struct prefix *prefix)
+{
+       memset(prefix, 0, sizeof(*prefix));
+       switch (fec->type) {
+       case FEC_TYPE_IPV4:
+               prefix->family = AF_INET;
+               prefix->u.prefix4 = fec->u.ipv4.prefix;
+               prefix->prefixlen = fec->u.ipv4.prefixlen;
+               break;
+       case FEC_TYPE_IPV6:
+               prefix->family = AF_INET6;
+               prefix->u.prefix6 = fec->u.ipv6.prefix;
+               prefix->prefixlen = fec->u.ipv6.prefixlen;
+               break;
+       default:
+               prefix->family = AF_UNSPEC;
+               break;
+       }
+}
+
+void
+lde_prefix2fec(const struct prefix *prefix, struct fec *fec)
+{
+       memset(fec, 0, sizeof(*fec));
+       switch (prefix->family) {
+       case AF_INET:
+               fec->type = FEC_TYPE_IPV4;
+               fec->u.ipv4.prefix = prefix->u.prefix4;
+               fec->u.ipv4.prefixlen = prefix->prefixlen;
+               break;
+       case AF_INET6:
+               fec->type = FEC_TYPE_IPV6;
+               fec->u.ipv6.prefix = prefix->u.prefix6;
+               fec->u.ipv6.prefixlen = prefix->prefixlen;
+               break;
+       default:
+               fatalx("lde_prefix2fec: unknown af");
+               break;
+       }
+}
+
 void
 lde_fec2map(struct fec *fec, struct map *map)
 {
@@ -1388,6 +1471,9 @@ lde_nbr_del(struct lde_nbr *ln)
        RB_FOREACH(f, fec_tree, &ft) {
                fn = (struct fec_node *)f;
 
+               /* Update RLFA clients. */
+               lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
+
                LIST_FOREACH(fnh, &fn->nexthops, entry) {
                        switch (f->type) {
                        case FEC_TYPE_IPV4:
index 21769ffe0755c853b7a71014307a1997bbf69ab1..28468931ec625d9129e8a82f01728bcc10ad9dd7 100644 (file)
@@ -158,6 +158,8 @@ uint32_t     lde_update_label(struct fec_node *);
 void            lde_free_label(uint32_t label);
 void            lde_send_change_klabel(struct fec_node *, struct fec_nh *);
 void            lde_send_delete_klabel(struct fec_node *, struct fec_nh *);
+void            lde_fec2prefix(const struct fec *fec, struct prefix *prefix);
+void            lde_prefix2fec(const struct prefix *prefix, struct fec *fec);
 void            lde_fec2map(struct fec *, struct map *);
 void            lde_map2fec(struct map *, struct in_addr, struct fec *);
 void            lde_send_labelmapping(struct lde_nbr *, struct fec_node *,
index d89cbb308fa96d5f8cc6177cc3cabfb7275b37dc..68b721e2133e449ffbf9fee976e3e15508066450 100644 (file)
@@ -23,6 +23,7 @@
 #include "ldpe.h"
 #include "lde.h"
 #include "log.h"
+#include "rlfa.h"
 
 #include "mpls.h"
 
@@ -609,6 +610,10 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
                        break;
                }
        }
+
+       /* Update RLFA clients. */
+       lde_rlfa_update_clients(&fec, ln, map->label);
+
        /* LMp.13 & LMp.16: Record the mapping from this peer */
        if (me == NULL)
                me = lde_map_add(ln, fn, 0);
@@ -866,6 +871,9 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)
                fnh->remote_label = NO_LABEL;
        }
 
+       /* Update RLFA clients. */
+       lde_rlfa_update_clients(&fec, ln, MPLS_INVALID_LABEL);
+
        /* LWd.2: send label release */
        lde_send_labelrelease(ln, fn, NULL, map->label);
 
@@ -948,6 +956,9 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
                        fnh->remote_label = NO_LABEL;
                }
 
+               /* Update RLFA clients. */
+               lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
+
                /* LWd.3: check previously received label mapping */
                if (me && (map->label == NO_LABEL ||
                    map->label == me->map.label))
index a53854fa56d4b304d7dc8e5d682bcc889d5b8ed8..ea86c2dc039c40fbc474718066d7689e6dc7b8c8 100644 (file)
@@ -114,12 +114,16 @@ static void
 ldp_zebra_opaque_register(void)
 {
        zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
+       zclient_register_opaque(zclient, LDP_RLFA_REGISTER);
+       zclient_register_opaque(zclient, LDP_RLFA_UNREGISTER_ALL);
 }
 
 static void
 ldp_zebra_opaque_unregister(void)
 {
        zclient_unregister_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
+       zclient_unregister_opaque(zclient, LDP_RLFA_REGISTER);
+       zclient_unregister_opaque(zclient, LDP_RLFA_UNREGISTER_ALL);
 }
 
 int
@@ -147,12 +151,29 @@ ldp_sync_zebra_send_announce(void)
                return 0;
 }
 
+int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *rlfa_labels)
+{
+       int ret;
+
+       ret = zclient_send_opaque(zclient, LDP_RLFA_LABELS,
+                                 (const uint8_t *)rlfa_labels,
+                                 sizeof(*rlfa_labels));
+       if (ret == ZCLIENT_SEND_FAILURE) {
+               log_warn("failed to send RLFA labels to IGP");
+               return -1;
+       }
+
+       return 0;
+}
+
 static int
 ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
 {
        struct stream *s;
        struct zapi_opaque_msg info;
        struct ldp_igp_sync_if_state_req state_req;
+       struct zapi_rlfa_igp igp;
+       struct zapi_rlfa_request rlfa;
 
         s = zclient->ibuf;
 
@@ -165,6 +186,14 @@ ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
                main_imsg_compose_ldpe(IMSG_LDP_SYNC_IF_STATE_REQUEST, 0, &state_req,
                            sizeof(state_req));
                break;
+       case LDP_RLFA_REGISTER:
+               STREAM_GET(&rlfa, s, sizeof(rlfa));
+               main_imsg_compose_both(IMSG_RLFA_REG, &rlfa, sizeof(rlfa));
+               break;
+       case LDP_RLFA_UNREGISTER_ALL:
+               STREAM_GET(&igp, s, sizeof(igp));
+               main_imsg_compose_both(IMSG_RLFA_UNREG_ALL, &igp, sizeof(igp));
+               break;
        default:
                break;
        }
index d6da45c8624ab9b71ae7ea2a819b1353fc2ad717..83e93ebbbc52715295bfd53700088f0a28d44d36 100644 (file)
@@ -625,6 +625,7 @@ main_dispatch_lde(struct thread *thread)
        struct imsg      imsg;
        ssize_t          n;
        int              shut = 0;
+       struct zapi_rlfa_response *rlfa_labels;
 
        iev->ev_read = NULL;
 
@@ -691,6 +692,15 @@ main_dispatch_lde(struct thread *thread)
                                fatalx("IMSG_ACL_CHECK imsg with wrong len");
                        ldp_acl_reply(iev, (struct acl_check *)imsg.data);
                        break;
+               case IMSG_RLFA_LABELS:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(struct zapi_rlfa_response)) {
+                               log_warnx("%s: wrong imsg len", __func__);
+                               break;
+                       }
+                       rlfa_labels = imsg.data;
+                       ldp_zebra_send_rlfa_labels(rlfa_labels);
+                       break;
                default:
                        log_debug("%s: error handling imsg %d", __func__,
                            imsg.hdr.type);
index f8a94b4e2a50d08d651a6210966ab0109149d8f7..beb625d8a26bf08133c8ea573e8d9479afb1b7ec 100644 (file)
@@ -157,7 +157,10 @@ enum imsg_type {
        IMSG_FILTER_UPDATE,
        IMSG_NBR_SHUTDOWN,
        IMSG_LDP_SYNC_IF_STATE_REQUEST,
-       IMSG_LDP_SYNC_IF_STATE_UPDATE
+       IMSG_LDP_SYNC_IF_STATE_UPDATE,
+       IMSG_RLFA_REG,
+       IMSG_RLFA_UNREG_ALL,
+       IMSG_RLFA_LABELS,
 };
 
 struct ldpd_init {
@@ -373,6 +376,7 @@ struct tnbr {
        union ldpd_addr          addr;
        int                      state;
        uint16_t                 pw_count;
+       uint32_t                 rlfa_count;
        uint8_t                  flags;
        QOBJ_FIELDS
 };
@@ -875,6 +879,8 @@ extern char                  ctl_sock_path[MAXPATHLEN];
 void            ldp_zebra_init(struct thread_master *);
 void            ldp_zebra_destroy(void);
 int             ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *);
+int             ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *
+                   rlfa_labels);
 
 /* compatibility */
 #ifndef __OpenBSD__
index f3f8b8510257bad73365ad83c2dec713c35ba3ca..6a5a0750bd007f2cc281ea33c4ca80f44b064ae2 100644 (file)
@@ -27,6 +27,7 @@
 #include "control.h"
 #include "log.h"
 #include "ldp_debug.h"
+#include "rlfa.h"
 
 #include <lib/log.h>
 #include "memory.h"
@@ -298,7 +299,11 @@ ldpe_dispatch_main(struct thread *thread)
        int                      n, shut = 0;
        struct ldp_access       *laccess;
        struct ldp_igp_sync_if_state_req *ldp_sync_if_state_req;
-       
+       struct ldp_rlfa_node     *rnode, *rntmp;
+       struct ldp_rlfa_client   *rclient;
+       struct zapi_rlfa_request *rlfa_req;
+       struct zapi_rlfa_igp     *rlfa_igp;
+
        iev->ev_read = NULL;
 
        if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
@@ -569,6 +574,44 @@ ldpe_dispatch_main(struct thread *thread)
                        ldp_sync_if_state_req = imsg.data;
                        ldp_sync_fsm_state_req(ldp_sync_if_state_req);
                        break;
+               case IMSG_RLFA_REG:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(struct zapi_rlfa_request)) {
+                               log_warnx("%s: wrong imsg len", __func__);
+                               break;
+                       }
+                       rlfa_req = imsg.data;
+
+                       rnode = rlfa_node_find(&rlfa_req->destination,
+                                              rlfa_req->pq_address);
+                       if (!rnode)
+                               rnode = rlfa_node_new(&rlfa_req->destination,
+                                                     rlfa_req->pq_address);
+                       rclient = rlfa_client_find(rnode, &rlfa_req->igp);
+                       if (rclient)
+                               /* RLFA already registered - do nothing */
+                               break;
+                       rclient = rlfa_client_new(rnode, &rlfa_req->igp);
+                       ldpe_rlfa_init(rclient);
+                       break;
+               case IMSG_RLFA_UNREG_ALL:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(struct zapi_rlfa_igp)) {
+                               log_warnx("%s: wrong imsg len", __func__);
+                               break;
+                       }
+                       rlfa_igp = imsg.data;
+
+                       RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head,
+                                        &rlfa_node_tree, rntmp) {
+                               rclient = rlfa_client_find(rnode, rlfa_igp);
+                               if (!rclient)
+                                       continue;
+
+                               ldpe_rlfa_exit(rclient);
+                               rlfa_client_del(rclient);
+                       }
+                       break;
                default:
                        log_debug("ldpe_dispatch_main: error handling imsg %d",
                            imsg.hdr.type);
diff --git a/ldpd/rlfa.c b/ldpd/rlfa.c
new file mode 100644 (file)
index 0000000..697ec08
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2020  NetDEF, Inc.
+ *                     Renato Westphal
+ *
+ * This program is free software; you can redistribute it 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "lde.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.h"
+#include "rlfa.h"
+
+#include <lib/log.h>
+
+struct ldp_rlfa_node_head rlfa_node_tree;
+
+static int ldp_rlfa_client_compare(const struct ldp_rlfa_client *a,
+                                  const struct ldp_rlfa_client *b)
+{
+       if (a->igp.vrf_id < b->igp.vrf_id)
+               return -1;
+       if (a->igp.vrf_id > b->igp.vrf_id)
+               return 1;
+
+       if (a->igp.protocol < b->igp.protocol)
+               return -1;
+       if (a->igp.protocol > b->igp.protocol)
+               return 1;
+
+       if (a->igp.isis.spf.tree_id < b->igp.isis.spf.tree_id)
+               return -1;
+       if (a->igp.isis.spf.tree_id > b->igp.isis.spf.tree_id)
+               return 1;
+
+       if (a->igp.isis.spf.level < b->igp.isis.spf.level)
+               return -1;
+       if (a->igp.isis.spf.level > b->igp.isis.spf.level)
+               return 1;
+
+       return 0;
+}
+RB_GENERATE(ldp_rlfa_client_head, ldp_rlfa_client, entry,
+           ldp_rlfa_client_compare)
+
+static int ldp_rlfa_node_compare(const struct ldp_rlfa_node *a,
+                                const struct ldp_rlfa_node *b)
+{
+       if (ntohl(a->pq_address.s_addr) < ntohl(b->pq_address.s_addr))
+               return -1;
+       if (ntohl(a->pq_address.s_addr) > ntohl(b->pq_address.s_addr))
+               return 1;
+
+       return prefix_cmp(&a->destination, &b->destination);
+}
+RB_GENERATE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare)
+
+struct ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode,
+                                       struct zapi_rlfa_igp *igp)
+{
+       struct ldp_rlfa_client *rclient;
+
+       if ((rclient = calloc(1, sizeof(*rclient))) == NULL)
+               fatal(__func__);
+
+       rclient->igp = *igp;
+       rclient->node = rnode;
+       RB_INSERT(ldp_rlfa_client_head, &rnode->clients, rclient);
+
+       return rclient;
+}
+
+void rlfa_client_del(struct ldp_rlfa_client *rclient)
+{
+       struct ldp_rlfa_node *rnode = rclient->node;
+
+       RB_REMOVE(ldp_rlfa_client_head, &rnode->clients, rclient);
+       free(rclient);
+
+       /* Delete RLFA node if it's empty. */
+       if (RB_EMPTY(ldp_rlfa_client_head, &rnode->clients))
+               rlfa_node_del(rnode);
+}
+
+struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode,
+                                        struct zapi_rlfa_igp *igp)
+{
+       struct ldp_rlfa_client rclient;
+
+       rclient.igp = *igp;
+       return RB_FIND(ldp_rlfa_client_head, &rnode->clients, &rclient);
+}
+
+struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination,
+                                   struct in_addr pq_address)
+{
+       struct ldp_rlfa_node *rnode;
+
+       if ((rnode = calloc(1, sizeof(*rnode))) == NULL)
+               fatal(__func__);
+
+       rnode->destination = *destination;
+       rnode->pq_address = pq_address;
+       rnode->pq_label = MPLS_INVALID_LABEL;
+       RB_INIT(ldp_rlfa_client_head, &rnode->clients);
+       RB_INSERT(ldp_rlfa_node_head, &rlfa_node_tree, rnode);
+
+       return rnode;
+}
+
+void rlfa_node_del(struct ldp_rlfa_node *rnode)
+{
+       /* Delete RLFA clients. */
+       while (!RB_EMPTY(ldp_rlfa_client_head, &rnode->clients)) {
+               struct ldp_rlfa_client *rclient;
+
+               rclient = RB_ROOT(ldp_rlfa_client_head, &rnode->clients);
+               rlfa_client_del(rclient);
+       }
+
+       RB_REMOVE(ldp_rlfa_node_head, &rlfa_node_tree, rnode);
+       free(rnode);
+}
+
+struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination,
+                                    struct in_addr pq_address)
+{
+       struct ldp_rlfa_node rnode = {};
+
+       rnode.destination = *destination;
+       rnode.pq_address = pq_address;
+       return RB_FIND(ldp_rlfa_node_head, &rlfa_node_tree, &rnode);
+}
+
+void lde_rlfa_client_send(struct ldp_rlfa_client *rclient)
+{
+       struct ldp_rlfa_node            *rnode = rclient->node;
+       struct zapi_rlfa_response        rlfa_labels = {};
+       struct fec                       fec;
+       struct fec_node                 *fn;
+       struct fec_nh                   *fnh;
+       int                              i = 0;
+
+       /* Fill in inner label (allocated by PQ node). */
+       rlfa_labels.igp = rclient->igp;
+       rlfa_labels.destination = rnode->destination;
+       rlfa_labels.pq_label = rnode->pq_label;
+
+       /* Fill in outer label(s) (allocated by the nexthop routers). */
+       fec.type = FEC_TYPE_IPV4;
+       fec.u.ipv4.prefix = rnode->pq_address;
+       fec.u.ipv4.prefixlen = IPV4_MAX_BITLEN;
+       fn = (struct fec_node *)fec_find(&ft, &fec);
+       if (!fn)
+               return;
+       LIST_FOREACH(fnh, &fn->nexthops, entry) {
+               if (fnh->remote_label == NO_LABEL)
+                       continue;
+
+               rlfa_labels.nexthops[i].family = fnh->af;
+               switch (fnh->af) {
+               case AF_INET:
+                       rlfa_labels.nexthops[i].gate.ipv4 = fnh->nexthop.v4;
+                       break;
+               case AF_INET6:
+                       rlfa_labels.nexthops[i].gate.ipv6 = fnh->nexthop.v6;
+                       break;
+               default:
+                       continue;
+               }
+               rlfa_labels.nexthops[i].label = fnh->remote_label;
+               i++;
+       }
+       rlfa_labels.nexthop_num = i;
+
+       lde_imsg_compose_parent(IMSG_RLFA_LABELS, 0, &rlfa_labels,
+                               sizeof(rlfa_labels));
+}
+
+void lde_rlfa_label_update(const struct fec *fec)
+{
+       struct ldp_rlfa_node *rnode;
+
+       if (fec->type != FEC_TYPE_IPV4
+           || fec->u.ipv4.prefixlen != IPV4_MAX_BITLEN)
+               return;
+
+       /*
+        * TODO: use an rb-tree lookup to restrict the iteration to the RLFAs
+        * that were effectivelly affected by the label update.
+        */
+       RB_FOREACH (rnode, ldp_rlfa_node_head, &rlfa_node_tree) {
+               struct ldp_rlfa_client *rclient;
+
+               if (!IPV4_ADDR_SAME(&rnode->pq_address, &fec->u.ipv4.prefix))
+                       continue;
+
+               RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients)
+                       lde_rlfa_client_send(rclient);
+       }
+}
+
+void lde_rlfa_check(struct ldp_rlfa_client *rclient)
+{
+       struct lde_nbr *ln;
+       struct lde_map *me;
+       struct fec fec;
+       union ldpd_addr pq_address = {};
+
+       pq_address.v4 = rclient->node->pq_address;
+       ln = lde_nbr_find_by_addr(AF_INET, &pq_address);
+       if (!ln)
+               return;
+
+       lde_prefix2fec(&rclient->node->destination, &fec);
+       me = (struct lde_map *)fec_find(&ln->recv_map, &fec);
+       if (!me)
+               return;
+
+       rclient->node->pq_label = me->map.label;
+       lde_rlfa_client_send(rclient);
+}
+
+/*
+ * Check if there's any registered RLFA client for this prefix/neighbor (PQ
+ * node) and notify about the updated label.
+ */
+void lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln,
+                            uint32_t label)
+{
+       struct prefix            rlfa_dest;
+       struct ldp_rlfa_node    *rnode;
+
+       lde_fec2prefix(fec, &rlfa_dest);
+       rnode = rlfa_node_find(&rlfa_dest, ln->id);
+       if (rnode) {
+               struct ldp_rlfa_client *rclient;
+
+               rnode->pq_label = label;
+               RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients)
+                       lde_rlfa_client_send(rclient);
+       } else
+               lde_rlfa_label_update(fec);
+}
+
+void ldpe_rlfa_init(struct ldp_rlfa_client *rclient)
+{
+       struct tnbr *tnbr;
+       union ldpd_addr pq_address = {};
+
+       pq_address.v4 = rclient->node->pq_address;
+       tnbr = tnbr_find(leconf, AF_INET, &pq_address);
+       if (tnbr == NULL) {
+               tnbr = tnbr_new(AF_INET, &pq_address);
+               tnbr_update(tnbr);
+               RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr);
+       }
+
+       tnbr->rlfa_count++;
+}
+
+void ldpe_rlfa_exit(struct ldp_rlfa_client *rclient)
+{
+       struct tnbr *tnbr;
+       union ldpd_addr pq_address = {};
+
+       pq_address.v4 = rclient->node->pq_address;
+       tnbr = tnbr_find(leconf, AF_INET, &pq_address);
+       if (tnbr) {
+               tnbr->rlfa_count--;
+               tnbr_check(leconf, tnbr);
+       }
+}
diff --git a/ldpd/rlfa.h b/ldpd/rlfa.h
new file mode 100644 (file)
index 0000000..fe67917
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020  NetDEF, Inc.
+ *                     Renato Westphal
+ *
+ * This program is free software; you can redistribute it 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _LDPD_RLFA_H_
+#define _LDPD_RLFA_H_
+
+#include "openbsd-tree.h"
+#include "zclient.h"
+
+struct ldp_rlfa_client {
+       RB_ENTRY(ldp_rlfa_client) entry;
+
+       /* IGP instance data. */
+       struct zapi_rlfa_igp igp;
+
+       /* Backpointer to RLFA node. */
+       struct ldp_rlfa_node *node;
+};
+RB_HEAD(ldp_rlfa_client_head, ldp_rlfa_client);
+RB_PROTOTYPE(ldp_rlfa_client_head, ldp_rlfa_client, entry,
+            ldp_rlfa_client_compare);
+
+struct ldp_rlfa_node {
+       RB_ENTRY(ldp_rlfa_node) entry;
+
+       /* Destination prefix. */
+       struct prefix destination;
+
+       /* PQ node address. */
+       struct in_addr pq_address;
+
+       /* RLFA clients. */
+       struct ldp_rlfa_client_head clients;
+
+       /* Label allocated by the PQ node to the RLFA destination. */
+       mpls_label_t pq_label;
+};
+RB_HEAD(ldp_rlfa_node_head, ldp_rlfa_node);
+RB_PROTOTYPE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare);
+
+extern struct ldp_rlfa_node_head rlfa_node_tree;
+
+/* prototypes */
+struct          ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode,
+                   struct zapi_rlfa_igp *igp);
+void            rlfa_client_del(struct ldp_rlfa_client *rclient);
+struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode,
+                   struct zapi_rlfa_igp *igp);
+struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination,
+                   struct in_addr pq_address);
+void rlfa_node_del(struct ldp_rlfa_node *rnode);
+struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination,
+                   struct in_addr pq_address);
+void            lde_rlfa_check(struct ldp_rlfa_client *rclient);
+void            lde_rlfa_client_send(struct ldp_rlfa_client *rclient);
+void            lde_rlfa_label_update(const struct fec *fec);
+void            lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln,
+                   uint32_t label);
+void            ldpe_rlfa_init(struct ldp_rlfa_client *rclient);
+void            ldpe_rlfa_exit(struct ldp_rlfa_client *rclient);
+
+#endif /* _LDPD_RLFA_H_ */
index 2058d2596a6d7e992eca01817bf4bb86948bd2dc..d89d18341d0a315c67bf9e11cef4887970a44478 100644 (file)
@@ -36,6 +36,7 @@ ldpd_libldp_a_SOURCES = \
        ldpd/notification.c \
        ldpd/packet.c \
        ldpd/pfkey.c \
+       ldpd/rlfa.c \
        ldpd/socket.c \
        ldpd/util.c \
        # end
@@ -53,6 +54,7 @@ noinst_HEADERS += \
        ldpd/ldpd.h \
        ldpd/ldpe.h \
        ldpd/log.h \
+       ldpd/rlfa.h \
        # end
 
 ldpd_ldpd_SOURCES = ldpd/ldpd.c
index 2af448a20c9943e94fde993142ff5a47fd7b03ee..910a4dbae5ad53c2412d4bb768e304209e4ec6a7 100644 (file)
@@ -634,6 +634,52 @@ struct zapi_pw_status {
        uint32_t status;
 };
 
+/* IGP instance data associated to a RLFA. */
+struct zapi_rlfa_igp {
+       vrf_id_t vrf_id;
+       int protocol;
+       union {
+               struct {
+                       char area_tag[32];
+                       struct {
+                               int tree_id;
+                               int level;
+                               unsigned int run_id;
+                       } spf;
+               } isis;
+       };
+};
+
+/* IGP -> LDP RLFA (un)registration message. */
+struct zapi_rlfa_request {
+       /* IGP instance data. */
+       struct zapi_rlfa_igp igp;
+
+       /* Destination prefix. */
+       struct prefix destination;
+
+       /* PQ node address. */
+       struct in_addr pq_address;
+};
+
+/* LDP -> IGP RLFA label update. */
+struct zapi_rlfa_response {
+       /* IGP instance data. */
+       struct zapi_rlfa_igp igp;
+
+       /* Destination prefix. */
+       struct prefix destination;
+
+       /* Resolved LDP labels. */
+       mpls_label_t pq_label;
+       uint16_t nexthop_num;
+       struct {
+               int family;
+               union g_addr gate;
+               mpls_label_t label;
+       } nexthops[MULTIPATH_NUM];
+};
+
 enum zapi_route_notify_owner {
        ZAPI_ROUTE_FAIL_INSTALL,
        ZAPI_ROUTE_BETTER_ADMIN_WON,
@@ -1091,6 +1137,12 @@ enum zapi_opaque_registry {
        LDP_IGP_SYNC_IF_STATE_UPDATE = 4,
        /* Announce that LDP is up  */
        LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5,
+       /* Register RLFA with LDP */
+       LDP_RLFA_REGISTER = 7,
+       /* Unregister all RLFAs with LDP */
+       LDP_RLFA_UNREGISTER_ALL = 8,
+       /* Announce LDP labels associated to a previously registered RLFA */
+       LDP_RLFA_LABELS = 9,
 };
 
 /* Send the hello message.