summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRenato Westphal <renato@opensourcerouting.org>2020-12-06 01:45:52 +0100
committerRenato Westphal <renato@opensourcerouting.org>2021-01-09 02:22:11 +0100
commit077d336aa7f543525c03de5a4617a2314c7ca984 (patch)
tree50cb5507a3be86de8db95d0691482c5e004c6550
parentldpd: detect when route received from zebra hasn't changed (diff)
downloadfrr-077d336aa7f543525c03de5a4617a2314c7ca984.tar.xz
frr-077d336aa7f543525c03de5a4617a2314c7ca984.zip
ldpd: add support for RLFA clients
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>
-rw-r--r--ldpd/adjacency.c5
-rw-r--r--ldpd/hello.c3
-rw-r--r--ldpd/lde.c86
-rw-r--r--ldpd/lde.h2
-rw-r--r--ldpd/lde_lib.c11
-rw-r--r--ldpd/ldp_zebra.c29
-rw-r--r--ldpd/ldpd.c10
-rw-r--r--ldpd/ldpd.h8
-rw-r--r--ldpd/ldpe.c45
-rw-r--r--ldpd/rlfa.c288
-rw-r--r--ldpd/rlfa.h78
-rw-r--r--ldpd/subdir.am2
-rw-r--r--lib/zclient.h52
13 files changed, 614 insertions, 5 deletions
diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c
index 795a41491..d390e70ad 100644
--- a/ldpd/adjacency.c
+++ b/ldpd/adjacency.c
@@ -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);
}
diff --git a/ldpd/hello.c b/ldpd/hello.c
index 327cb3243..5aa14ed06 100644
--- a/ldpd/hello.c
+++ b/ldpd/hello.c
@@ -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;
diff --git a/ldpd/lde.c b/ldpd/lde.c
index 5ed0ed452..69338b8ba 100644
--- a/ldpd/lde.c
+++ b/ldpd/lde.c
@@ -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);
@@ -876,6 +917,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)
{
memset(map, 0, sizeof(*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:
diff --git a/ldpd/lde.h b/ldpd/lde.h
index 21769ffe0..28468931e 100644
--- a/ldpd/lde.h
+++ b/ldpd/lde.h
@@ -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 *,
diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c
index d89cbb308..68b721e21 100644
--- a/ldpd/lde_lib.c
+++ b/ldpd/lde_lib.c
@@ -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))
diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c
index a53854fa5..ea86c2dc0 100644
--- a/ldpd/ldp_zebra.c
+++ b/ldpd/ldp_zebra.c
@@ -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;
}
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
index d6da45c86..83e93ebbb 100644
--- a/ldpd/ldpd.c
+++ b/ldpd/ldpd.c
@@ -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);
diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h
index f8a94b4e2..beb625d8a 100644
--- a/ldpd/ldpd.h
+++ b/ldpd/ldpd.h
@@ -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__
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
index f3f8b8510..6a5a0750b 100644
--- a/ldpd/ldpe.c
+++ b/ldpd/ldpe.c
@@ -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
index 000000000..697ec08af
--- /dev/null
+++ b/ldpd/rlfa.c
@@ -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
index 000000000..fe67917e8
--- /dev/null
+++ b/ldpd/rlfa.h
@@ -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_ */
diff --git a/ldpd/subdir.am b/ldpd/subdir.am
index 2058d2596..d89d18341 100644
--- a/ldpd/subdir.am
+++ b/ldpd/subdir.am
@@ -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
diff --git a/lib/zclient.h b/lib/zclient.h
index 2af448a20..910a4dbae 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -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.