diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c index 795a41491c..d390e70ad0 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 327cb32434..5aa14ed067 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 5ed0ed4520..69338b8bad 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 #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: diff --git a/ldpd/lde.h b/ldpd/lde.h index 21769ffe07..28468931ec 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 d89cbb308f..68b721e213 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 a53854fa56..ea86c2dc03 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 d6da45c862..83e93ebbbc 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 f8a94b4e2a..beb625d8a2 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 f3f8b85102..6a5a0750bd 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 #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 0000000000..697ec08af8 --- /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 + +#include "ldpd.h" +#include "lde.h" +#include "ldpe.h" +#include "log.h" +#include "ldp_debug.h" +#include "rlfa.h" + +#include + +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 0000000000..fe67917e8a --- /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 2058d2596a..d89d18341d 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 2af448a20c..910a4dbae5 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.