summaryrefslogtreecommitdiffstats
path: root/ldpd/lde.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /ldpd/lde.c
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ldpd/lde.c')
-rw-r--r--ldpd/lde.c2500
1 files changed, 2500 insertions, 0 deletions
diff --git a/ldpd/lde.c b/ldpd/lde.c
new file mode 100644
index 0000000..c7e915d
--- /dev/null
+++ b/ldpd/lde.c
@@ -0,0 +1,2500 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldp.h"
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "lde.h"
+#include "ldp_debug.h"
+#include "rlfa.h"
+
+#include <lib/log.h>
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "mpls.h"
+#include <lib/linklist.h>
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "libfrr.h"
+
+static void lde_shutdown(void);
+static void lde_dispatch_imsg(struct event *thread);
+static void lde_dispatch_parent(struct event *thread);
+static __inline int lde_nbr_compare(const struct lde_nbr *,
+ const struct lde_nbr *);
+static struct lde_nbr *lde_nbr_new(uint32_t, struct lde_nbr *);
+static void lde_nbr_del(struct lde_nbr *);
+static struct lde_nbr *lde_nbr_find(uint32_t);
+static void lde_nbr_clear(void);
+static void lde_nbr_addr_update(struct lde_nbr *,
+ struct lde_addr *, int);
+static __inline int lde_map_compare(const struct lde_map *,
+ const struct lde_map *);
+static void lde_map_free(void *);
+static int lde_address_add(struct lde_nbr *, struct lde_addr *);
+static int lde_address_del(struct lde_nbr *, struct lde_addr *);
+static void lde_address_list_free(struct lde_nbr *);
+static void zclient_sync_init(void);
+static void lde_label_list_init(void);
+static int lde_get_label_chunk(void);
+static void on_get_label_chunk_response(uint32_t start, uint32_t end);
+static uint32_t lde_get_next_label(void);
+static bool lde_fec_connected(const struct fec_node *);
+static bool lde_fec_outside_mpls_network(const struct fec_node *);
+static void lde_check_filter_af(int, struct ldpd_af_conf *,
+ const char *);
+
+RB_GENERATE(nbr_tree, lde_nbr, entry, lde_nbr_compare)
+RB_GENERATE(lde_map_head, lde_map, entry, lde_map_compare)
+
+struct ldpd_conf *ldeconf;
+struct nbr_tree lde_nbrs = RB_INITIALIZER(&lde_nbrs);
+
+static struct imsgev *iev_ldpe;
+static struct imsgev iev_main_sync_data;
+static struct imsgev *iev_main, *iev_main_sync;
+
+/* lde privileges */
+static zebra_capabilities_t _caps_p [] =
+{
+ ZCAP_NET_ADMIN
+};
+
+static struct zebra_privs_t lde_privs =
+{
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0
+};
+
+/* List of chunks of labels externally assigned by Zebra */
+static struct list *label_chunk_list;
+static struct listnode *current_label_chunk;
+
+/* Synchronous zclient to request labels */
+struct zclient *zclient_sync;
+
+/* SIGINT / SIGTERM handler. */
+static void
+sigint(void)
+{
+ lde_shutdown();
+}
+
+static struct frr_signal_t lde_signals[] =
+{
+ {
+ .signal = SIGHUP,
+ /* ignore */
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+/* label decision engine */
+void
+lde(void)
+{
+#ifdef HAVE_SETPROCTITLE
+ setproctitle("label decision engine");
+#endif
+ ldpd_process = PROC_LDE_ENGINE;
+ log_procname = log_procnames[PROC_LDE_ENGINE];
+
+ master = frr_init();
+ /* no frr_config_fork() here, allow frr_pthread to create threads */
+ frr_is_after_fork = true;
+
+ /* setup signal handler */
+ signal_init(master, array_size(lde_signals), lde_signals);
+
+ /* setup pipes and event handlers to the parent process */
+ if ((iev_main = calloc(1, sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC);
+ iev_main->handler_read = lde_dispatch_parent;
+ event_add_read(master, iev_main->handler_read, iev_main,
+ iev_main->ibuf.fd, &iev_main->ev_read);
+ iev_main->handler_write = ldp_write_handler;
+
+ memset(&iev_main_sync_data, 0, sizeof(iev_main_sync_data));
+ iev_main_sync = &iev_main_sync_data;
+ imsg_init(&iev_main_sync->ibuf, LDPD_FD_SYNC);
+
+ /* create base configuration */
+ ldeconf = config_new_empty();
+
+ struct event thread;
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+
+ /* NOTREACHED */
+ return;
+}
+
+void
+lde_init(struct ldpd_init *init)
+{
+ /* drop privileges */
+ lde_privs.user = init->user;
+ lde_privs.group = init->group;
+ zprivs_preinit(&lde_privs);
+ zprivs_init(&lde_privs);
+
+ /* start the LIB garbage collector */
+ lde_gc_start_timer();
+
+ /* Init synchronous zclient and label list */
+ frr_zclient_addr(&zclient_addr, &zclient_addr_len,
+ init->zclient_serv_path);
+ zclient_sync_init();
+}
+
+static void
+lde_shutdown(void)
+{
+ /* close pipes */
+ if (iev_ldpe) {
+ msgbuf_clear(&iev_ldpe->ibuf.w);
+ close(iev_ldpe->ibuf.fd);
+ iev_ldpe->ibuf.fd = -1;
+ }
+ msgbuf_clear(&iev_main->ibuf.w);
+ close(iev_main->ibuf.fd);
+ iev_main->ibuf.fd = -1;
+ msgbuf_clear(&iev_main_sync->ibuf.w);
+ close(iev_main_sync->ibuf.fd);
+ iev_main_sync->ibuf.fd = -1;
+
+ lde_gc_stop_timer();
+ lde_nbr_clear();
+ fec_tree_clear();
+
+ config_clear(ldeconf);
+
+ if (iev_ldpe)
+ free(iev_ldpe);
+ free(iev_main);
+
+ log_info("label decision engine exiting");
+
+ zlog_fini();
+ exit(0);
+}
+
+/* imesg */
+int
+lde_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ if (iev_main->ibuf.fd == -1)
+ return (0);
+ return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen));
+}
+
+void
+lde_imsg_compose_parent_sync(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ if (iev_main_sync->ibuf.fd == -1)
+ return;
+ imsg_compose_event(iev_main_sync, type, 0, pid, -1, data, datalen);
+ imsg_flush(&iev_main_sync->ibuf);
+}
+
+int
+lde_imsg_compose_ldpe(int type, uint32_t peerid, pid_t pid, void *data,
+ uint16_t datalen)
+{
+ if (iev_ldpe->ibuf.fd == -1)
+ return (0);
+ return (imsg_compose_event(iev_ldpe, type, peerid, pid,
+ -1, data, datalen));
+}
+
+/* ARGSUSED */
+static void lde_dispatch_imsg(struct event *thread)
+{
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ struct lde_nbr *ln;
+ struct map *map;
+ struct lde_addr *lde_addr;
+ struct notify_msg *nm;
+ ssize_t n;
+ int shut = 0;
+
+ iev->ev_read = NULL;
+
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("lde_dispatch_imsg: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_LABEL_MAPPING_FULL:
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ fec_snap(ln);
+ break;
+ case IMSG_LABEL_MAPPING:
+ case IMSG_LABEL_REQUEST:
+ case IMSG_LABEL_RELEASE:
+ case IMSG_LABEL_WITHDRAW:
+ case IMSG_LABEL_ABORT:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct map))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ map = imsg.data;
+
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_LABEL_MAPPING:
+ lde_check_mapping(map, ln, 1);
+ break;
+ case IMSG_LABEL_REQUEST:
+ lde_check_request(map, ln);
+ break;
+ case IMSG_LABEL_RELEASE:
+ lde_check_release(map, ln);
+ break;
+ case IMSG_LABEL_WITHDRAW:
+ lde_check_withdraw(map, ln);
+ break;
+ case IMSG_LABEL_ABORT:
+ /* not necessary */
+ break;
+ }
+ break;
+ case IMSG_ADDRESS_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_addr))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ lde_addr = imsg.data;
+
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ if (lde_address_add(ln, lde_addr) < 0) {
+ log_debug("%s: cannot add address %s, it already exists", __func__,
+ log_addr(lde_addr->af, &lde_addr->addr));
+ }
+ break;
+ case IMSG_ADDRESS_DEL:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_addr))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ lde_addr = imsg.data;
+
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ if (lde_address_del(ln, lde_addr) < 0) {
+ log_debug("%s: cannot delete address %s, it does not exist", __func__,
+ log_addr(lde_addr->af, &lde_addr->addr));
+ }
+ break;
+ case IMSG_NOTIFICATION:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct notify_msg))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ nm = imsg.data;
+
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ switch (nm->status_code) {
+ case S_PW_STATUS:
+ l2vpn_recv_pw_status(ln, nm);
+ break;
+ case S_ENDOFLIB:
+ /*
+ * Do nothing for now. Should be useful in
+ * the future when we implement LDP-IGP
+ * Synchronization (RFC 5443) and Graceful
+ * Restart (RFC 3478).
+ */
+ default:
+ break;
+ }
+ break;
+ case IMSG_NEIGHBOR_UP:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_nbr))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+
+ if (lde_nbr_find(imsg.hdr.peerid))
+ fatalx("lde_dispatch_imsg: neighbor already exists");
+ lde_nbr_new(imsg.hdr.peerid, imsg.data);
+ break;
+ case IMSG_NEIGHBOR_DOWN:
+ lde_nbr_del(lde_nbr_find(imsg.hdr.peerid));
+ break;
+ case IMSG_CTL_SHOW_LIB:
+ rt_dump(imsg.hdr.pid);
+
+ lde_imsg_compose_ldpe(IMSG_CTL_END, 0,
+ imsg.hdr.pid, NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ l2vpn_pw_ctl(imsg.hdr.pid);
+
+ lde_imsg_compose_ldpe(IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ l2vpn_binding_ctl(imsg.hdr.pid);
+
+ lde_imsg_compose_ldpe(IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0);
+ break;
+ default:
+ log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handlers and exit */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ lde_shutdown();
+ }
+}
+
+/* ARGSUSED */
+static void lde_dispatch_parent(struct event *thread)
+{
+ static struct ldpd_conf *nconf;
+ struct iface *iface, *niface;
+ struct tnbr *ntnbr;
+ struct nbr_params *nnbrp;
+ static struct l2vpn *l2vpn, *nl2vpn;
+ struct l2vpn_if *lif, *nlif;
+ struct l2vpn_pw *pw, *npw;
+ struct imsg imsg;
+ struct kif *kif;
+ struct kroute *kr;
+ int fd;
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ ssize_t n;
+ 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;
+
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("lde_dispatch_parent: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_IFSTATUS:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kif))
+ fatalx("IFSTATUS imsg with wrong len");
+ kif = imsg.data;
+
+ iface = if_lookup_name(ldeconf, kif->ifname);
+ if (iface) {
+ if_update_info(iface, kif);
+
+ /* if up see if any labels need to be updated */
+ if (kif->operative)
+ lde_route_update(iface, AF_UNSPEC);
+ break;
+ }
+
+ RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree) {
+ lif = l2vpn_if_find(l2vpn, kif->ifname);
+ if (lif) {
+ l2vpn_if_update_info(lif, kif);
+ break;
+ }
+ pw = l2vpn_pw_find(l2vpn, kif->ifname);
+ if (pw) {
+ l2vpn_pw_update_info(pw, kif);
+ break;
+ }
+ }
+ break;
+ case IMSG_PW_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct zapi_pw_status))
+ fatalx("PW_UPDATE imsg with wrong len");
+
+ if (l2vpn_pw_status_update(imsg.data) != 0)
+ log_warnx("%s: error updating PW status", __func__);
+ break;
+ case IMSG_NETWORK_ADD:
+ case IMSG_NETWORK_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kroute)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ kr = imsg.data;
+
+ switch (kr->af) {
+ case AF_INET:
+ fec.type = FEC_TYPE_IPV4;
+ fec.u.ipv4.prefix = kr->prefix.v4;
+ fec.u.ipv4.prefixlen = kr->prefixlen;
+ break;
+ case AF_INET6:
+ fec.type = FEC_TYPE_IPV6;
+ fec.u.ipv6.prefix = kr->prefix.v6;
+ fec.u.ipv6.prefixlen = kr->prefixlen;
+ break;
+ default:
+ fatalx("lde_dispatch_parent: unknown af");
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_NETWORK_ADD:
+ lde_kernel_insert(&fec, kr->af, &kr->nexthop,
+ kr->ifindex, kr->route_type, kr->route_instance,
+ CHECK_FLAG(kr->flags, F_CONNECTED), NULL);
+ break;
+ case IMSG_NETWORK_UPDATE:
+ lde_kernel_update(&fec);
+ break;
+ }
+ break;
+ case IMSG_SOCKET_IPC:
+ if (iev_ldpe) {
+ log_warnx("%s: received unexpected imsg fd to ldpe", __func__);
+ break;
+ }
+ if ((fd = imsg.fd) == -1) {
+ log_warnx("%s: expected to receive imsg fd to ldpe but didn't receive any", __func__);
+ break;
+ }
+
+ if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_ldpe->ibuf, fd);
+ iev_ldpe->handler_read = lde_dispatch_imsg;
+ event_add_read(master, iev_ldpe->handler_read, iev_ldpe,
+ iev_ldpe->ibuf.fd, &iev_ldpe->ev_read);
+ iev_ldpe->handler_write = ldp_write_handler;
+ iev_ldpe->ev_write = NULL;
+ break;
+ case IMSG_INIT:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ldpd_init))
+ fatalx("INIT imsg with wrong len");
+
+ memcpy(&init, imsg.data, sizeof(init));
+ lde_init(&init);
+ break;
+ case IMSG_AGENTX_ENABLED:
+ ldp_agentx_enabled();
+ break;
+ case IMSG_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct ldpd_conf));
+
+ RB_INIT(iface_head, &nconf->iface_tree);
+ RB_INIT(tnbr_head, &nconf->tnbr_tree);
+ RB_INIT(nbrp_head, &nconf->nbrp_tree);
+ RB_INIT(l2vpn_head, &nconf->l2vpn_tree);
+ break;
+ case IMSG_RECONF_IFACE:
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ RB_INSERT(iface_head, &nconf->iface_tree, niface);
+ break;
+ case IMSG_RECONF_TNBR:
+ if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL)
+ fatal(NULL);
+ memcpy(ntnbr, imsg.data, sizeof(struct tnbr));
+
+ RB_INSERT(tnbr_head, &nconf->tnbr_tree, ntnbr);
+ break;
+ case IMSG_RECONF_NBRP:
+ if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL)
+ fatal(NULL);
+ memcpy(nnbrp, imsg.data, sizeof(struct nbr_params));
+
+ RB_INSERT(nbrp_head, &nconf->nbrp_tree, nnbrp);
+ break;
+ case IMSG_RECONF_L2VPN:
+ if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL)
+ fatal(NULL);
+ memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn));
+
+ RB_INIT(l2vpn_if_head, &nl2vpn->if_tree);
+ RB_INIT(l2vpn_pw_head, &nl2vpn->pw_tree);
+ RB_INIT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree);
+
+ RB_INSERT(l2vpn_head, &nconf->l2vpn_tree, nl2vpn);
+ break;
+ case IMSG_RECONF_L2VPN_IF:
+ if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL)
+ fatal(NULL);
+ memcpy(nlif, imsg.data, sizeof(struct l2vpn_if));
+
+ RB_INSERT(l2vpn_if_head, &nl2vpn->if_tree, nlif);
+ break;
+ case IMSG_RECONF_L2VPN_PW:
+ if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL)
+ fatal(NULL);
+ memcpy(npw, imsg.data, sizeof(struct l2vpn_pw));
+
+ RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_tree, npw);
+ break;
+ case IMSG_RECONF_L2VPN_IPW:
+ if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL)
+ fatal(NULL);
+ memcpy(npw, imsg.data, sizeof(struct l2vpn_pw));
+
+ RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree, npw);
+ break;
+ case IMSG_RECONF_END:
+ merge_config(ldeconf, nconf);
+ ldp_clear_config(nconf);
+ nconf = NULL;
+ break;
+ case IMSG_DEBUG_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(ldp_debug)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug));
+ break;
+ case IMSG_FILTER_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ldp_access)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ laccess = imsg.data;
+ lde_check_filter_af(AF_INET, &ldeconf->ipv4,
+ laccess->name);
+ 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);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handlers and exit */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ lde_shutdown();
+ }
+}
+
+int
+lde_acl_check(char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen)
+{
+ return ldp_acl_request(iev_main_sync, acl_name, af, addr, prefixlen);
+}
+
+static bool lde_fec_connected(const struct fec_node *fn)
+{
+ struct fec_nh *fnh;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry)
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED))
+ return true;
+
+ return false;
+}
+
+static bool lde_fec_outside_mpls_network(const struct fec_node *fn)
+{
+ struct fec_nh *fnh;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry)
+ if (!CHECK_FLAG(fnh->flags, F_FEC_NH_NO_LDP))
+ return false;
+
+ return true;
+}
+
+uint32_t
+lde_update_label(struct fec_node *fn)
+{
+
+ /* should we allocate a label for this fec? */
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (CHECK_FLAG(ldeconf->ipv4.flags, F_LDPD_AF_ALLOCHOSTONLY)
+ && fn->fec.u.ipv4.prefixlen != IPV4_MAX_BITLEN)
+ return (NO_LABEL);
+
+ if (lde_acl_check(ldeconf->ipv4.acl_label_allocate_for,
+ AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
+ fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
+ return (NO_LABEL);
+ break;
+ case FEC_TYPE_IPV6:
+ if (CHECK_FLAG(ldeconf->ipv6.flags, F_LDPD_AF_ALLOCHOSTONLY)
+ && fn->fec.u.ipv6.prefixlen != IPV6_MAX_BITLEN)
+ return (NO_LABEL);
+
+ if (lde_acl_check(ldeconf->ipv6.acl_label_allocate_for,
+ AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
+ fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
+ return (NO_LABEL);
+ break;
+ case FEC_TYPE_PWID:
+ break;
+ }
+
+ /*
+ * If connected interface act as egress for fec.
+ * If LDP is not configured on an interface but there
+ * are other NHs with interfaces configured with LDP
+ * then don't act as an egress for the fec, otherwise
+ * act as an egress for the fec
+ */
+ if (lde_fec_connected(fn) || lde_fec_outside_mpls_network(fn)) {
+ /* choose implicit or explicit-null depending on configuration */
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!CHECK_FLAG(ldeconf->ipv4.flags, F_LDPD_AF_EXPNULL))
+ return (MPLS_LABEL_IMPLICIT_NULL);
+
+ if (lde_acl_check(ldeconf->ipv4.acl_label_expnull_for,
+ AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
+ fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
+ return (MPLS_LABEL_IMPLICIT_NULL);
+ return MPLS_LABEL_IPV4_EXPLICIT_NULL;
+ case FEC_TYPE_IPV6:
+ if (!CHECK_FLAG(ldeconf->ipv6.flags, F_LDPD_AF_EXPNULL))
+ return (MPLS_LABEL_IMPLICIT_NULL);
+
+ if (lde_acl_check(ldeconf->ipv6.acl_label_expnull_for,
+ AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
+ fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
+ return (MPLS_LABEL_IMPLICIT_NULL);
+ return MPLS_LABEL_IPV6_EXPLICIT_NULL;
+ case FEC_TYPE_PWID:
+ break;
+ }
+ }
+
+ /* preserve current label if there's no need to update it */
+ if (fn->local_label != NO_LABEL &&
+ fn->local_label > MPLS_LABEL_RESERVED_MAX)
+ return (fn->local_label);
+
+ return (lde_get_next_label());
+}
+
+void
+lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh)
+{
+ struct kroute kr;
+ struct zapi_pw zpw;
+ struct l2vpn_pw *pw;
+
+ /*
+ * Ordered Control: don't program label into HW until a
+ * labelmap msg has been received from upstream router
+ */
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_DEFER))
+ return;
+
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ memset(&kr, 0, sizeof(kr));
+ kr.af = AF_INET;
+ kr.prefix.v4 = fn->fec.u.ipv4.prefix;
+ kr.prefixlen = fn->fec.u.ipv4.prefixlen;
+ kr.nexthop.v4 = fnh->nexthop.v4;
+ kr.ifindex = fnh->ifindex;
+ kr.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.route_type = fnh->route_type;
+ kr.route_instance = fnh->route_instance;
+ lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr));
+ break;
+ case FEC_TYPE_IPV6:
+ memset(&kr, 0, sizeof(kr));
+ kr.af = AF_INET6;
+ kr.prefix.v6 = fn->fec.u.ipv6.prefix;
+ kr.prefixlen = fn->fec.u.ipv6.prefixlen;
+ kr.nexthop.v6 = fnh->nexthop.v6;
+ kr.ifindex = fnh->ifindex;
+ kr.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.route_type = fnh->route_type;
+ kr.route_instance = fnh->route_instance;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr));
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (!pw || fn->local_label == NO_LABEL ||
+ fnh->remote_label == NO_LABEL)
+ return;
+
+ pw->enabled = true;
+ pw2zpw(pw, &zpw);
+ zpw.local_label = fn->local_label;
+ zpw.remote_label = fnh->remote_label;
+ lde_imsg_compose_parent(IMSG_KPW_SET, 0, &zpw, sizeof(zpw));
+ break;
+ }
+}
+
+void
+lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh)
+{
+ struct kroute kr;
+ struct zapi_pw zpw;
+ struct l2vpn_pw *pw;
+
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ memset(&kr, 0, sizeof(kr));
+ kr.af = AF_INET;
+ kr.prefix.v4 = fn->fec.u.ipv4.prefix;
+ kr.prefixlen = fn->fec.u.ipv4.prefixlen;
+ kr.nexthop.v4 = fnh->nexthop.v4;
+ kr.ifindex = fnh->ifindex;
+ kr.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.route_type = fnh->route_type;
+ kr.route_instance = fnh->route_instance;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr));
+ break;
+ case FEC_TYPE_IPV6:
+ memset(&kr, 0, sizeof(kr));
+ kr.af = AF_INET6;
+ kr.prefix.v6 = fn->fec.u.ipv6.prefix;
+ kr.prefixlen = fn->fec.u.ipv6.prefixlen;
+ kr.nexthop.v6 = fnh->nexthop.v6;
+ kr.ifindex = fnh->ifindex;
+ kr.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.route_type = fnh->route_type;
+ kr.route_instance = fnh->route_instance;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr));
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (!pw)
+ return;
+
+ pw->enabled = false;
+ pw2zpw(pw, &zpw);
+ zpw.local_label = fn->local_label;
+ zpw.remote_label = fnh->remote_label;
+ lde_imsg_compose_parent(IMSG_KPW_UNSET, 0, &zpw, sizeof(zpw));
+ break;
+ }
+}
+
+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;
+ case FEC_TYPE_PWID:
+ 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));
+
+ switch (fec->type) {
+ case FEC_TYPE_IPV4:
+ map->type = MAP_TYPE_PREFIX;
+ map->fec.prefix.af = AF_INET;
+ map->fec.prefix.prefix.v4 = fec->u.ipv4.prefix;
+ map->fec.prefix.prefixlen = fec->u.ipv4.prefixlen;
+ break;
+ case FEC_TYPE_IPV6:
+ map->type = MAP_TYPE_PREFIX;
+ map->fec.prefix.af = AF_INET6;
+ map->fec.prefix.prefix.v6 = fec->u.ipv6.prefix;
+ map->fec.prefix.prefixlen = fec->u.ipv6.prefixlen;
+ break;
+ case FEC_TYPE_PWID:
+ map->type = MAP_TYPE_PWID;
+ map->fec.pwid.type = fec->u.pwid.type;
+ map->fec.pwid.group_id = 0;
+ SET_FLAG(map->flags, F_MAP_PW_ID);
+ map->fec.pwid.pwid = fec->u.pwid.pwid;
+ break;
+ }
+}
+
+void
+lde_map2fec(struct map *map, struct in_addr lsr_id, struct fec *fec)
+{
+ memset(fec, 0, sizeof(*fec));
+
+ switch (map->type) {
+ case MAP_TYPE_PREFIX:
+ switch (map->fec.prefix.af) {
+ case AF_INET:
+ fec->type = FEC_TYPE_IPV4;
+ fec->u.ipv4.prefix = map->fec.prefix.prefix.v4;
+ fec->u.ipv4.prefixlen = map->fec.prefix.prefixlen;
+ break;
+ case AF_INET6:
+ fec->type = FEC_TYPE_IPV6;
+ fec->u.ipv6.prefix = map->fec.prefix.prefix.v6;
+ fec->u.ipv6.prefixlen = map->fec.prefix.prefixlen;
+ break;
+ default:
+ fatalx("lde_map2fec: unknown af");
+ break;
+ }
+ break;
+ case MAP_TYPE_PWID:
+ fec->type = FEC_TYPE_PWID;
+ fec->u.pwid.type = map->fec.pwid.type;
+ fec->u.pwid.pwid = map->fec.pwid.pwid;
+ fec->u.pwid.lsr_id = lsr_id;
+ break;
+ }
+}
+
+void
+lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single)
+{
+ struct lde_wdraw *lw;
+ struct lde_map *me;
+ struct lde_req *lre;
+ struct map map;
+ struct l2vpn_pw *pw;
+ struct fec_nh *fnh;
+ bool allow = false;
+
+ /*
+ * Ordered Control: do not send a labelmap msg until
+ * a labelmap message is received from downstream router
+ * and don't send labelmap back to downstream router
+ */
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_DEFER))
+ continue;
+
+ if (lde_address_find(ln, fnh->af, &fnh->nexthop))
+ return;
+ allow = true;
+ break;
+ }
+ if (!allow)
+ return;
+ }
+
+ /*
+ * We shouldn't send a new label mapping if we have a pending
+ * label release to receive. In this case, schedule to send a
+ * label mapping as soon as a label release is received.
+ */
+ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
+ if (lw) {
+ if (!fec_find(&ln->sent_map_pending, &fn->fec)) {
+ debug_evt("%s: FEC %s: scheduling to send label mapping later (waiting for pending label release)",
+ __func__, log_fec(&fn->fec));
+ lde_map_pending_add(ln, fn);
+ }
+ return;
+ }
+
+ /*
+ * This function skips SL.1 - 3 and SL.9 - 14 because the label
+ * allocation is done way earlier (because of the merging nature of
+ * ldpd).
+ */
+
+ lde_fec2map(&fn->fec, &map);
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!ln->v4_enabled)
+ return;
+
+ if (lde_acl_check(ldeconf->ipv4.acl_label_advertise_to,
+ AF_INET, (union ldpd_addr *)&ln->id, 32) != FILTER_PERMIT)
+ return;
+
+ if (lde_acl_check(ldeconf->ipv4.acl_label_advertise_for,
+ AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
+ fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (!ln->v6_enabled)
+ return;
+
+ if (lde_acl_check(ldeconf->ipv6.acl_label_advertise_to,
+ AF_INET, (union ldpd_addr *)&ln->id, 32) != FILTER_PERMIT)
+ return;
+
+ if (lde_acl_check(ldeconf->ipv6.acl_label_advertise_for,
+ AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
+ fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
+ /* not the remote end of the pseudowire */
+ return;
+
+ SET_FLAG(map.flags, F_MAP_PW_IFMTU);
+ map.fec.pwid.ifmtu = pw->l2vpn->mtu;
+
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD))
+ SET_FLAG(map.flags, F_MAP_PW_CWORD);
+
+ if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV)) {
+ SET_FLAG(map.flags, F_MAP_PW_STATUS);
+ map.pw_status = pw->local_status;
+ }
+ break;
+ }
+ map.label = fn->local_label;
+
+ /* SL.6: is there a pending request for this mapping? */
+ lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec);
+ if (lre) {
+ /* set label request msg id in the mapping response. */
+ map.requestid = lre->msg_id;
+ map.flags = F_MAP_REQ_ID;
+
+ /* SL.7: delete record of pending request */
+ lde_req_del(ln, lre, 0);
+ }
+
+ /* SL.4: send label mapping */
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD, ln->peerid, 0,
+ &map, sizeof(map));
+ if (single)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0,
+ NULL, 0);
+
+ /* SL.5: record sent label mapping */
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (me == NULL)
+ me = lde_map_add(ln, fn, 1);
+ me->map = map;
+}
+
+void
+lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn,
+ struct map *wcard, struct status_tlv *st)
+{
+ struct lde_wdraw *lw;
+ struct map map;
+ struct fec *f;
+ struct l2vpn_pw *pw;
+
+ if (fn) {
+ lde_fec2map(&fn->fec, &map);
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!ln->v4_enabled)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (!ln->v6_enabled)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
+ /* not the remote end of the pseudowire */
+ return;
+
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD))
+ SET_FLAG(map.flags, F_MAP_PW_CWORD);
+ break;
+ }
+ map.label = fn->local_label;
+ } else
+ memcpy(&map, wcard, sizeof(map));
+
+ if (st) {
+ map.st.status_code = st->status_code;
+ map.st.msg_id = st->msg_id;
+ map.st.msg_type = st->msg_type;
+ SET_FLAG(map.flags, F_MAP_STATUS);
+ }
+
+ /* SWd.1: send label withdraw. */
+ lde_imsg_compose_ldpe(IMSG_WITHDRAW_ADD, ln->peerid, 0,
+ &map, sizeof(map));
+ lde_imsg_compose_ldpe(IMSG_WITHDRAW_ADD_END, ln->peerid, 0, NULL, 0);
+
+ /* SWd.2: record label withdraw. */
+ if (fn) {
+ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
+ if (lw == NULL)
+ lw = lde_wdraw_add(ln, fn);
+ lw->label = map.label;
+ } else {
+ struct lde_map *me;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (lde_wildcard_apply(wcard, &fn->fec, me) == 0)
+ continue;
+
+ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
+ if (lw == NULL)
+ lw = lde_wdraw_add(ln, fn);
+
+ lw->label = map.label;
+ }
+ }
+}
+
+void
+lde_send_labelwithdraw_wcard(struct lde_nbr *ln, uint32_t label)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_WILDCARD;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelwithdraw_twcard_prefix(struct lde_nbr *ln, uint16_t af,
+ uint32_t label)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_TYPED_WCARD;
+ wcard.fec.twcard.type = MAP_TYPE_PREFIX;
+ wcard.fec.twcard.u.prefix_af = af;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelwithdraw_twcard_pwid(struct lde_nbr *ln, uint16_t pw_type,
+ uint32_t label)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_TYPED_WCARD;
+ wcard.fec.twcard.type = MAP_TYPE_PWID;
+ wcard.fec.twcard.u.pw_type = pw_type;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *ln, uint16_t pw_type,
+ uint32_t group_id)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_PWID;
+ wcard.fec.pwid.type = pw_type;
+ wcard.fec.pwid.group_id = group_id;
+ /* we can not append a Label TLV when using PWid group wildcards. */
+ wcard.label = NO_LABEL;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn,
+ struct map *wcard, uint32_t label)
+{
+ struct map map;
+ struct l2vpn_pw *pw;
+
+ if (fn) {
+ lde_fec2map(&fn->fec, &map);
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!ln->v4_enabled)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (!ln->v6_enabled)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
+ /* not the remote end of the pseudowire */
+ return;
+
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD))
+ SET_FLAG(map.flags, F_MAP_PW_CWORD);
+ break;
+ }
+ } else
+ memcpy(&map, wcard, sizeof(map));
+ map.label = label;
+
+ lde_imsg_compose_ldpe(IMSG_RELEASE_ADD, ln->peerid, 0,
+ &map, sizeof(map));
+ lde_imsg_compose_ldpe(IMSG_RELEASE_ADD_END, ln->peerid, 0, NULL, 0);
+}
+
+void
+lde_send_labelrequest(struct lde_nbr *ln, struct fec_node *fn,
+ struct map *wcard, int single)
+{
+ struct map map;
+ struct fec *f;
+ struct lde_req *lre;
+
+ if (fn) {
+ lde_fec2map(&fn->fec, &map);
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!ln->v4_enabled)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (!ln->v6_enabled)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ fatalx("lde_send_labelrequest: unknown af");
+ }
+ } else
+ memcpy(&map, wcard, sizeof(map));
+
+ map.label = NO_LABEL;
+
+ if (fn) {
+ /* SLR1.1: has label request for FEC been previously sent
+ * and still outstanding just return,
+ */
+ lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec);
+ if (lre == NULL) {
+ /* SLRq.3: send label request */
+ lde_imsg_compose_ldpe(IMSG_REQUEST_ADD, ln->peerid, 0,
+ &map, sizeof(map));
+ if (single)
+ lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END,
+ ln->peerid, 0, NULL, 0);
+
+ /* SLRq.4: record sent request */
+ lde_req_add(ln, &fn->fec, 1);
+ }
+ } else {
+ /* if Wilcard just send label request */
+ /* SLRq.3: send label request */
+ lde_imsg_compose_ldpe(IMSG_REQUEST_ADD,
+ ln->peerid, 0, &map, sizeof(map));
+ if (single)
+ lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END, ln->peerid, 0, NULL, 0);
+
+ /* SLRq.4: record sent request */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec);
+ if (lde_wildcard_apply(wcard, &fn->fec, NULL) == 0)
+ continue;
+ if (lre == NULL)
+ lde_req_add(ln, f, 1);
+ }
+ }
+}
+
+void
+lde_send_labelrequest_wcard(struct lde_nbr *ln, uint16_t af)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_TYPED_WCARD;
+ wcard.fec.twcard.type = MAP_TYPE_PREFIX;
+ wcard.fec.twcard.u.prefix_af = af;
+ lde_send_labelrequest(ln, NULL, &wcard, 1);
+}
+
+void
+lde_send_notification(struct lde_nbr *ln, uint32_t status_code, uint32_t msg_id,
+ uint16_t msg_type)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = status_code;
+ /* 'msg_id' and 'msg_type' should be in network byte order */
+ nm.msg_id = msg_id;
+ nm.msg_type = msg_type;
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
+ &nm, sizeof(nm));
+}
+
+void
+lde_send_notification_eol_prefix(struct lde_nbr *ln, int af)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_ENDOFLIB;
+ nm.fec.type = MAP_TYPE_TYPED_WCARD;
+ nm.fec.fec.twcard.type = MAP_TYPE_PREFIX;
+ nm.fec.fec.twcard.u.prefix_af = af;
+ SET_FLAG(nm.flags, F_NOTIF_FEC);
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
+ &nm, sizeof(nm));
+}
+
+void
+lde_send_notification_eol_pwid(struct lde_nbr *ln, uint16_t pw_type)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_ENDOFLIB;
+ nm.fec.type = MAP_TYPE_TYPED_WCARD;
+ nm.fec.fec.twcard.type = MAP_TYPE_PWID;
+ nm.fec.fec.twcard.u.pw_type = pw_type;
+ SET_FLAG(nm.flags, F_NOTIF_FEC);
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
+ &nm, sizeof(nm));
+}
+
+static __inline int
+lde_nbr_compare(const struct lde_nbr *a, const struct lde_nbr *b)
+{
+ return (a->peerid - b->peerid);
+}
+
+static struct lde_nbr *
+lde_nbr_new(uint32_t peerid, struct lde_nbr *new)
+{
+ struct lde_nbr *ln;
+
+ if ((ln = calloc(1, sizeof(*ln))) == NULL)
+ fatal(__func__);
+
+ ln->id = new->id;
+ ln->v4_enabled = new->v4_enabled;
+ ln->v6_enabled = new->v6_enabled;
+ ln->flags = new->flags;
+ ln->peerid = peerid;
+ fec_init(&ln->recv_map);
+ fec_init(&ln->sent_map);
+ fec_init(&ln->sent_map_pending);
+ fec_init(&ln->recv_req);
+ fec_init(&ln->sent_req);
+ fec_init(&ln->sent_wdraw);
+
+ TAILQ_INIT(&ln->addr_list);
+
+ if (RB_INSERT(nbr_tree, &lde_nbrs, ln) != NULL)
+ fatalx("lde_nbr_new: RB_INSERT failed");
+
+ return (ln);
+}
+
+static void
+lde_nbr_del(struct lde_nbr *ln)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct l2vpn_pw *pw;
+ struct lde_nbr *lnbr;
+
+ if (ln == NULL)
+ return;
+
+ /* uninstall received mappings */
+ 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:
+ case FEC_TYPE_IPV6:
+ if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
+ continue;
+
+ /*
+ * Ordered Control: must mark any non-connected
+ * NH to wait until we receive a labelmap msg
+ * before installing in kernel and sending to
+ * peer, must do this as NHs are not removed
+ * when lsps go down. Also send label withdraw
+ * to other neighbors for all fecs from neighbor
+ * going down
+ */
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
+ SET_FLAG(fnh->flags, F_FEC_NH_DEFER);
+
+ RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) {
+ if (ln->peerid == lnbr->peerid)
+ continue;
+ lde_send_labelwithdraw(lnbr, fn, NULL, NULL);
+ }
+ }
+ break;
+ case FEC_TYPE_PWID:
+ if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr)
+ continue;
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw) {
+ pw->reason = F_PW_NO_REMOTE_LABEL;
+ l2vpn_pw_reset(pw);
+ }
+ break;
+ default:
+ break;
+ }
+
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ }
+ }
+
+ lde_address_list_free(ln);
+
+ fec_clear(&ln->recv_map, lde_map_free);
+ fec_clear(&ln->sent_map, lde_map_free);
+ fec_clear(&ln->sent_map_pending, free);
+ fec_clear(&ln->recv_req, free);
+ fec_clear(&ln->sent_req, free);
+ fec_clear(&ln->sent_wdraw, free);
+
+ RB_REMOVE(nbr_tree, &lde_nbrs, ln);
+
+ free(ln);
+}
+
+static struct lde_nbr *
+lde_nbr_find(uint32_t peerid)
+{
+ struct lde_nbr ln;
+
+ ln.peerid = peerid;
+
+ return (RB_FIND(nbr_tree, &lde_nbrs, &ln));
+}
+
+struct lde_nbr *
+lde_nbr_find_by_lsrid(struct in_addr addr)
+{
+ struct lde_nbr *ln;
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ if (ln->id.s_addr == addr.s_addr)
+ return (ln);
+
+ return (NULL);
+}
+
+struct lde_nbr *
+lde_nbr_find_by_addr(int af, union ldpd_addr *addr)
+{
+ struct lde_nbr *ln;
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ if (lde_address_find(ln, af, addr) != NULL)
+ return (ln);
+
+ return (NULL);
+}
+
+static void
+lde_nbr_clear(void)
+{
+ struct lde_nbr *ln;
+
+ while (!RB_EMPTY(nbr_tree, &lde_nbrs)) {
+ ln = RB_ROOT(nbr_tree, &lde_nbrs);
+
+ lde_nbr_del(ln);
+ }
+}
+
+static void
+lde_nbr_addr_update(struct lde_nbr *ln, struct lde_addr *lde_addr, int removed)
+{
+ struct fec *fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_map *me;
+
+ RB_FOREACH(fec, fec_tree, &ln->recv_map) {
+ switch (fec->type) {
+ case FEC_TYPE_IPV4:
+ if (lde_addr->af != AF_INET)
+ continue;
+ break;
+ case FEC_TYPE_IPV6:
+ if (lde_addr->af != AF_INET6)
+ continue;
+ break;
+ case FEC_TYPE_PWID:
+ continue;
+ }
+
+ fn = (struct fec_node *)fec_find(&ft, fec);
+ if (fn == NULL)
+ /* shouldn't happen */
+ continue;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ if (ldp_addrcmp(fnh->af, &fnh->nexthop, &lde_addr->addr))
+ continue;
+
+ if (removed) {
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ } else {
+ me = (struct lde_map *)fec;
+ fnh->remote_label = me->map.label;
+ lde_send_change_klabel(fn, fnh);
+ }
+ break;
+ }
+ }
+}
+
+void
+lde_allow_broken_lsp_update(int new_config)
+{
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct fec *f;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ /* allow-broken-lsp config is changing so
+ * we need to reprogram labeled routes to
+ * have proper top-level label
+ */
+ if (!(new_config & F_LDPD_ALLOW_BROKEN_LSP))
+ lde_send_delete_klabel(fn, fnh);
+
+ if (fn->local_label != NO_LABEL)
+ lde_send_change_klabel(fn, fnh);
+ }
+ }
+}
+
+static __inline int
+lde_map_compare(const struct lde_map *a, const struct lde_map *b)
+{
+ return (ldp_addrcmp(AF_INET, (union ldpd_addr *)&a->nexthop->id,
+ (union ldpd_addr *)&b->nexthop->id));
+}
+
+struct lde_map *
+lde_map_add(struct lde_nbr *ln, struct fec_node *fn, int sent)
+{
+ struct lde_map *me;
+
+ me = calloc(1, sizeof(*me));
+ if (me == NULL)
+ fatal(__func__);
+
+ me->fec = fn->fec;
+ me->nexthop = ln;
+
+ if (sent) {
+ RB_INSERT(lde_map_head, &fn->upstream, me);
+ me->head = &fn->upstream;
+ if (fec_insert(&ln->sent_map, &me->fec))
+ log_warnx("failed to add %s to sent map", log_fec(&me->fec));
+ /* XXX on failure more cleanup is needed */
+ } else {
+ RB_INSERT(lde_map_head, &fn->downstream, me);
+ me->head = &fn->downstream;
+ if (fec_insert(&ln->recv_map, &me->fec))
+ log_warnx("failed to add %s to recv map", log_fec(&me->fec));
+ }
+
+ return (me);
+}
+
+void
+lde_map_del(struct lde_nbr *ln, struct lde_map *me, int sent)
+{
+ if (sent)
+ fec_remove(&ln->sent_map, &me->fec);
+ else
+ fec_remove(&ln->recv_map, &me->fec);
+
+ lde_map_free(me);
+}
+
+static void
+lde_map_free(void *ptr)
+{
+ struct lde_map *map = ptr;
+
+ RB_REMOVE(lde_map_head, map->head, map);
+ free(map);
+}
+
+struct fec *
+lde_map_pending_add(struct lde_nbr *ln, struct fec_node *fn)
+{
+ struct fec *map;
+
+ map = calloc(1, sizeof(*map));
+ if (map == NULL)
+ fatal(__func__);
+
+ *map = fn->fec;
+ if (fec_insert(&ln->sent_map_pending, map))
+ log_warnx("failed to add %s to sent map (pending)", log_fec(map));
+
+ return (map);
+}
+
+void
+lde_map_pending_del(struct lde_nbr *ln, struct fec *map)
+{
+ fec_remove(&ln->sent_map_pending, map);
+ free(map);
+}
+
+struct lde_req *
+lde_req_add(struct lde_nbr *ln, struct fec *fec, int sent)
+{
+ struct fec_tree *t;
+ struct lde_req *lre;
+
+ t = sent ? &ln->sent_req : &ln->recv_req;
+
+ lre = calloc(1, sizeof(*lre));
+ if (lre != NULL) {
+ lre->fec = *fec;
+
+ if (fec_insert(t, &lre->fec)) {
+ log_warnx("failed to add %s to %s req",
+ log_fec(&lre->fec), sent ? "sent" : "recv");
+ free(lre);
+ return (NULL);
+ }
+ }
+
+ return (lre);
+}
+
+void
+lde_req_del(struct lde_nbr *ln, struct lde_req *lre, int sent)
+{
+ if (sent)
+ fec_remove(&ln->sent_req, &lre->fec);
+ else
+ fec_remove(&ln->recv_req, &lre->fec);
+
+ free(lre);
+}
+
+struct lde_wdraw *
+lde_wdraw_add(struct lde_nbr *ln, struct fec_node *fn)
+{
+ struct lde_wdraw *lw;
+
+ lw = calloc(1, sizeof(*lw));
+ if (lw == NULL)
+ fatal(__func__);
+
+ lw->fec = fn->fec;
+
+ if (fec_insert(&ln->sent_wdraw, &lw->fec))
+ log_warnx("failed to add %s to sent wdraw", log_fec(&lw->fec));
+
+ return (lw);
+}
+
+void
+lde_wdraw_del(struct lde_nbr *ln, struct lde_wdraw *lw)
+{
+ fec_remove(&ln->sent_wdraw, &lw->fec);
+ free(lw);
+}
+
+void
+lde_change_egress_label(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+
+ /* explicitly withdraw all null labels */
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IMPLICIT_NULL);
+ if (ln->v4_enabled)
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV4_EXPLICIT_NULL);
+ if (ln->v6_enabled)
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV6_EXPLICIT_NULL);
+ }
+
+ /* update label of connected routes */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (fn->local_label > MPLS_LABEL_RESERVED_MAX)
+ continue;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ fatalx("lde_change_egress_label: unknown af");
+ }
+
+ fn->local_label = lde_update_label(fn);
+ if (fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0);
+}
+
+void
+lde_change_allocate_filter(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ uint32_t new_label;
+
+ /* reallocate labels for fecs that match this filter */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ fatalx("lde_change_allocate_filter: unknown af");
+ }
+
+ /*
+ * If the local label has changed to NO_LABEL, send a label
+ * withdraw to all peers.
+ * If the local label has changed and it's different from
+ * NO_LABEL, send a label mapping to all peers advertising
+ * the new label.
+ * If the local label hasn't changed, do nothing
+ */
+ new_label = lde_update_label(fn);
+ if (fn->local_label != new_label) {
+ if (new_label == NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+
+ fn->local_label = new_label;
+ if (fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0,
+ NULL, 0);
+}
+
+void
+lde_change_advertise_filter(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ char *acl_to_filter;
+ char *acl_for_filter;
+ union ldpd_addr *prefix;
+ uint8_t plen;
+ struct lde_map *me;
+
+ /* advertise label for fecs to neighbors if matches advertise filters */
+ switch (af) {
+ case AF_INET:
+ acl_to_filter = ldeconf->ipv4.acl_label_advertise_to;
+ acl_for_filter = ldeconf->ipv4.acl_label_advertise_for;
+ break;
+ case AF_INET6:
+ acl_to_filter = ldeconf->ipv6.acl_label_advertise_to;
+ acl_for_filter = ldeconf->ipv6.acl_label_advertise_for;
+ break;
+ default:
+ fatalx("lde_change_advertise_filter: unknown af");
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
+ if (lde_acl_check(acl_to_filter, af, (union ldpd_addr *)&ln->id,
+ IPV4_MAX_BITLEN) != FILTER_PERMIT)
+ lde_send_labelwithdraw_wcard(ln, NO_LABEL);
+ else {
+ /* This neighbor is allowed in to_filter, so
+ * send labels if fec also matches for_filter
+ */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix;
+ plen = fn->fec.u.ipv4.prefixlen;
+ break;
+ case FEC_TYPE_IPV6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix;
+ plen = fn->fec.u.ipv6.prefixlen;
+ break;
+ default:
+ continue;
+ }
+
+ if (lde_acl_check(acl_for_filter, af,
+ prefix, plen) != FILTER_PERMIT) {
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (me)
+ /* fec filtered withdraw */
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+ } else
+ /* fec allowed send map */
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0);
+ }
+ }
+}
+
+
+void
+lde_change_accept_filter(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ char *acl_for_filter;
+ char *acl_from_filter;
+ union ldpd_addr *prefix;
+ uint8_t plen;
+ struct lde_map *me;
+ enum fec_type type;
+
+ /* accept labels from neighbors specified in the from_filter and for
+ * fecs defined in the for_filter
+ */
+ switch (af) {
+ case AF_INET:
+ acl_for_filter = ldeconf->ipv4.acl_label_accept_for;
+ acl_from_filter = ldeconf->ipv4.acl_label_accept_from;
+ type = FEC_TYPE_IPV4;
+ break;
+ case AF_INET6:
+ acl_for_filter = ldeconf->ipv6.acl_label_accept_for;
+ acl_from_filter = ldeconf->ipv6.acl_label_accept_from;
+ type = FEC_TYPE_IPV6;
+ break;
+ default:
+ fatalx("lde_change_accept_filter: unknown af");
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
+ if (lde_acl_check(acl_from_filter, AF_INET, (union ldpd_addr *)
+ &ln->id, IPV4_MAX_BITLEN) != FILTER_PERMIT) {
+ /* This neighbor is now filtered so remove fecs from
+ * recv list
+ */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (fn->fec.type == type) {
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+ if (me)
+ lde_map_del(ln, me, 0);
+ }
+ }
+ } else if (CHECK_FLAG(ln->flags, F_NBR_CAP_TWCARD)) {
+ /* This neighbor is allowed and supports type
+ * wildcard so send a labelrequest
+ * to get any new labels from neighbor
+ * and make sure any fecs we currently have
+ * match for_filter.
+ */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix;
+ plen = fn->fec.u.ipv4.prefixlen;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix;
+ plen = fn->fec.u.ipv6.prefixlen;
+ break;
+ default:
+ continue;
+ }
+ if (lde_acl_check(acl_for_filter, af,
+ prefix, plen) != FILTER_PERMIT) {
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+ if (me)
+ lde_map_del(ln, me, 0);
+ }
+ }
+ lde_send_labelrequest_wcard(ln, af);
+ } else
+ /* Type Wildcard is not supported so restart session */
+ lde_imsg_compose_ldpe(IMSG_NBR_SHUTDOWN, ln->peerid, 0, NULL, 0);
+ }
+}
+
+void
+lde_change_expnull_for_filter(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ char *acl_name;
+ uint32_t exp_label;
+ union ldpd_addr *prefix;
+ uint8_t plen;
+
+ /* Configure explicit-null advertisement for all fecs in this filter */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ acl_name = ldeconf->ipv4.acl_label_expnull_for;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix;
+ plen = fn->fec.u.ipv4.prefixlen;
+ exp_label = MPLS_LABEL_IPV4_EXPLICIT_NULL;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ acl_name = ldeconf->ipv6.acl_label_expnull_for;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix;
+ plen = fn->fec.u.ipv6.prefixlen;
+ exp_label = MPLS_LABEL_IPV6_EXPLICIT_NULL;
+ break;
+ default:
+ fatalx("lde_change_expnull_for_filter: unknown af");
+ }
+
+ if (lde_acl_check(acl_name, af, prefix, plen) == FILTER_PERMIT) {
+ /* for this fec change any imp-null to exp-null */
+ if (fn->local_label == MPLS_LABEL_IMPLICIT_NULL) {
+ fn->local_label= lde_update_label(fn);
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ } else {
+ /* for this fec change any exp-null back to imp-null */
+ if (fn->local_label == exp_label) {
+ fn->local_label = lde_update_label(fn);
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ }
+ }
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0,
+ NULL, 0);
+}
+
+static int
+lde_address_add(struct lde_nbr *ln, struct lde_addr *lde_addr)
+{
+ struct lde_addr *new;
+
+ if (lde_address_find(ln, lde_addr->af, &lde_addr->addr) != NULL)
+ return (-1);
+
+ if ((new = calloc(1, sizeof(*new))) == NULL)
+ fatal(__func__);
+
+ new->af = lde_addr->af;
+ new->addr = lde_addr->addr;
+ TAILQ_INSERT_TAIL(&ln->addr_list, new, entry);
+
+ /* reevaluate the previously received mappings from this neighbor */
+ lde_nbr_addr_update(ln, lde_addr, 0);
+
+ return (0);
+}
+
+static int
+lde_address_del(struct lde_nbr *ln, struct lde_addr *lde_addr)
+{
+ lde_addr = lde_address_find(ln, lde_addr->af, &lde_addr->addr);
+ if (lde_addr == NULL)
+ return (-1);
+
+ /* reevaluate the previously received mappings from this neighbor */
+ lde_nbr_addr_update(ln, lde_addr, 1);
+
+ TAILQ_REMOVE(&ln->addr_list, lde_addr, entry);
+ free(lde_addr);
+
+ return (0);
+}
+
+struct lde_addr *
+lde_address_find(struct lde_nbr *ln, int af, union ldpd_addr *addr)
+{
+ struct lde_addr *lde_addr;
+
+ TAILQ_FOREACH(lde_addr, &ln->addr_list, entry)
+ if (lde_addr->af == af &&
+ ldp_addrcmp(af, &lde_addr->addr, addr) == 0)
+ return (lde_addr);
+
+ return (NULL);
+}
+
+static void
+lde_address_list_free(struct lde_nbr *ln)
+{
+ struct lde_addr *lde_addr;
+
+ while ((lde_addr = TAILQ_POP_FIRST(&ln->addr_list, entry)) != NULL)
+ free(lde_addr);
+}
+
+/*
+ * Event callback used to retry the label-manager sync zapi session.
+ */
+static void zclient_sync_retry(struct event *thread)
+{
+ zclient_sync_init();
+}
+
+/*
+ * Initialize and open a synchronous zapi session. This is used by label chunk
+ * management code, which acquires and releases blocks of labels from the
+ * zebra label-manager module.
+ */
+static void zclient_sync_init(void)
+{
+ struct zclient_options options = zclient_options_default;
+
+ options.synchronous = true;
+
+ /* Initialize special zclient for synchronous message exchanges. */
+ zclient_sync = zclient_new(master, &options, NULL, 0);
+ zclient_sync->sock = -1;
+ zclient_sync->redist_default = ZEBRA_ROUTE_LDP;
+ zclient_sync->session_id = 1; /* Distinguish from main session */
+ zclient_sync->privs = &lde_privs;
+
+ if (zclient_socket_connect(zclient_sync) < 0) {
+ log_warnx("Error connecting synchronous zclient!");
+ goto retry;
+ }
+ /* make socket non-blocking */
+ sock_set_nonblock(zclient_sync->sock);
+
+ /* Send hello to notify zebra this is a synchronous client */
+ if (zclient_send_hello(zclient_sync) == ZCLIENT_SEND_FAILURE) {
+ log_warnx("Error sending hello for synchronous zclient!");
+ goto retry;
+ }
+
+ /* Connect to label manager */
+ if (lm_label_manager_connect(zclient_sync, 0) != 0) {
+ log_warnx("Error connecting to label manager!");
+ goto retry;
+ }
+
+ /* Finish label-manager init once the LM session is running */
+ lde_label_list_init();
+
+ return;
+
+retry:
+
+ /* Discard failed zclient object */
+ zclient_stop(zclient_sync);
+ zclient_free(zclient_sync);
+ zclient_sync = NULL;
+
+ /* Retry using a timer */
+ event_add_timer(master, zclient_sync_retry, NULL, 1, NULL);
+}
+
+static void
+lde_del_label_chunk(void *val)
+{
+ free(val);
+}
+
+static int
+lde_release_label_chunk(uint32_t start, uint32_t end)
+{
+ int ret;
+
+ ret = lm_release_label_chunk(zclient_sync, start, end);
+ if (ret < 0) {
+ log_warnx("Error releasing label chunk!");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+lde_get_label_chunk(void)
+{
+ int ret;
+ uint32_t start, end;
+
+ debug_labels("getting label chunk (size %u)", CHUNK_SIZE);
+ ret = lm_get_label_chunk(zclient_sync, 0, MPLS_LABEL_BASE_ANY,
+ CHUNK_SIZE, &start, &end);
+ if (ret < 0) {
+ log_warnx("Error getting label chunk!");
+ return -1;
+ }
+
+ on_get_label_chunk_response(start, end);
+
+ return (0);
+}
+
+static void
+lde_label_list_init(void)
+{
+ label_chunk_list = list_new();
+ label_chunk_list->del = lde_del_label_chunk;
+
+ /* get first chunk */
+ while (lde_get_label_chunk () != 0) {
+ log_warnx("Error getting first label chunk!");
+ sleep(1);
+ }
+}
+
+static void
+on_get_label_chunk_response(uint32_t start, uint32_t end)
+{
+ struct label_chunk *new_label_chunk;
+
+ debug_labels("label chunk assign: %u - %u", start, end);
+
+ new_label_chunk = calloc(1, sizeof(struct label_chunk));
+ if (!new_label_chunk) {
+ log_warn("Error trying to allocate label chunk %u - %u", start, end);
+ return;
+ }
+
+ new_label_chunk->start = start;
+ new_label_chunk->end = end;
+ new_label_chunk->used_mask = 0;
+
+ listnode_add(label_chunk_list, (void *)new_label_chunk);
+
+ /* let's update current if needed */
+ if (!current_label_chunk)
+ current_label_chunk = listtail(label_chunk_list);
+}
+
+void
+lde_free_label(uint32_t label)
+{
+ struct listnode *node;
+ struct label_chunk *label_chunk;
+ uint64_t pos;
+
+ for (ALL_LIST_ELEMENTS_RO(label_chunk_list, node, label_chunk)) {
+ if (label <= label_chunk->end && label >= label_chunk->start) {
+ pos = 1ULL << (label - label_chunk->start);
+ UNSET_FLAG(label_chunk->used_mask, pos);
+ /* if nobody is using this chunk and it's not current_label_chunk, then free it */
+ if (!label_chunk->used_mask && (current_label_chunk != node)) {
+ if (lde_release_label_chunk(label_chunk->start, label_chunk->end) != 0)
+ log_warnx("%s: Error releasing label chunk!", __func__);
+ else {
+ listnode_delete(label_chunk_list, label_chunk);
+ lde_del_label_chunk(label_chunk);
+ }
+ }
+ break;
+ }
+ }
+
+ return;
+}
+
+static uint32_t
+lde_get_next_label(void)
+{
+ struct label_chunk *label_chunk;
+ uint32_t i, size;
+ uint64_t pos;
+ uint32_t label = NO_LABEL;
+
+ while (current_label_chunk) {
+ label_chunk = listgetdata(current_label_chunk);
+ if (!label_chunk)
+ goto end;
+
+ /* try to get next free label in currently used label chunk */
+ size = label_chunk->end - label_chunk->start + 1;
+ for (i = 0, pos = 1; i < size; i++, pos <<= 1) {
+ if (!(pos & label_chunk->used_mask)) {
+ SET_FLAG(label_chunk->used_mask, pos);
+ label = label_chunk->start + i;
+ goto end;
+ }
+ }
+ current_label_chunk = listnextnode(current_label_chunk);
+ }
+
+end:
+ /* we moved till the last chunk, or were not able to find a label,
+ so let's ask for another one */
+ if (!current_label_chunk ||
+ current_label_chunk == listtail(label_chunk_list) ||
+ label == NO_LABEL) {
+ if (lde_get_label_chunk() != 0)
+ log_warn("%s: Error getting label chunk!", __func__);
+
+ }
+
+ return (label);
+}
+
+static void
+lde_check_filter_af(int af, struct ldpd_af_conf *af_conf,
+ const char *filter_name)
+{
+ if (strcmp(af_conf->acl_label_allocate_for, filter_name) == 0)
+ lde_change_allocate_filter(af);
+
+ if ((strcmp(af_conf->acl_label_advertise_to, filter_name) == 0)
+ || (strcmp(af_conf->acl_label_advertise_for, filter_name) == 0))
+ lde_change_advertise_filter(af);
+
+ if ((strcmp(af_conf->acl_label_accept_for, filter_name) == 0)
+ || (strcmp(af_conf->acl_label_accept_from, filter_name) == 0))
+ lde_change_accept_filter(af);
+
+ if (strcmp(af_conf->acl_label_expnull_for, filter_name) == 0)
+ lde_change_expnull_for_filter(af);
+}
+
+void lde_route_update(struct iface *iface, int af)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_nbr *ln;
+
+ /* update label of non-connected routes */
+ log_debug("update labels for interface %s", iface->name);
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (IS_MPLS_UNRESERVED_LABEL(fn->local_label))
+ continue;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ /* unspecified so process both address families */
+ break;
+ }
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ /*
+ * If connected leave existing label. If LDP
+ * configured on interface or a static route
+ * may need new label. If no LDP configured
+ * treat fec as a connected route
+ */
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED))
+ break;
+
+ if (fnh->ifindex != iface->ifindex)
+ continue;
+
+ UNSET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ if (IS_MPLS_RESERVED_LABEL(fn->local_label)) {
+ fn->local_label = NO_LABEL;
+ fn->local_label = lde_update_label(fn);
+ if (fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(
+ ln, fn, 0);
+ }
+ break;
+ }
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid,
+ 0, NULL, 0);
+}
+
+void lde_route_update_release(struct iface *iface, int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+
+ /* update label of interfaces no longer running LDP */
+ log_debug("release all labels for interface %s af %s", iface->name,
+ af == AF_INET ? "ipv4" : "ipv6");
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ fatalx("lde_route_update_release: unknown af");
+ }
+
+ if (fn->local_label == NO_LABEL)
+ continue;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ /*
+ * If connected leave existing label. If LDP
+ * removed from interface may need new label
+ * and would be treated as a connected route
+ */
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED))
+ break;
+
+ if (fnh->ifindex != iface->ifindex)
+ continue;
+
+ SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+ lde_free_label(fn->local_label);
+ fn->local_label = NO_LABEL;
+ fn->local_label = lde_update_label(fn);
+ if (fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ break;
+ }
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid,
+ 0, NULL, 0);
+}
+
+void lde_route_update_release_all(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+
+ /* remove labels from all interfaces as LDP is no longer running for
+ * this address family
+ */
+ log_debug("release all labels for address family %s",
+ af == AF_INET ? "ipv4" : "ipv6");
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ fatalx("lde_route_update_release: unknown af");
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ lde_send_delete_klabel(fn, fnh);
+ }
+ }
+}