// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 NetDEF, Inc. * Renato Westphal */ #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); } }