summaryrefslogtreecommitdiffstats
path: root/ldpd/ldpe.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ldpd/ldpe.c1074
1 files changed, 1074 insertions, 0 deletions
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
new file mode 100644
index 0000000..1b5c0e9
--- /dev/null
+++ b/ldpd/ldpe.c
@@ -0,0 +1,1074 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2008 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "control.h"
+#include "log.h"
+#include "ldp_debug.h"
+#include "rlfa.h"
+
+#include <lib/log.h>
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "libfrr.h"
+
+static void ldpe_shutdown(void);
+static void ldpe_dispatch_main(struct thread *thread);
+static void ldpe_dispatch_lde(struct thread *thread);
+#ifdef __OpenBSD__
+static void ldpe_dispatch_pfkey(struct thread *thread);
+#endif
+static void ldpe_setup_sockets(int, int, int, int);
+static void ldpe_close_sockets(int);
+static void ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t ifidx);
+static void ldpe_check_filter_af(int, struct ldpd_af_conf *, const char *);
+
+struct ldpd_conf *leconf;
+#ifdef __OpenBSD__
+struct ldpd_sysdep sysdep;
+#endif
+
+static struct imsgev iev_main_data;
+static struct imsgev *iev_main, *iev_main_sync;
+static struct imsgev *iev_lde;
+#ifdef __OpenBSD__
+static struct thread *pfkey_ev;
+#endif
+
+/* ldpe privileges */
+static zebra_capabilities_t _caps_p [] =
+{
+ ZCAP_BIND,
+ ZCAP_NET_ADMIN
+};
+
+struct zebra_privs_t ldpe_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
+};
+
+/* SIGINT / SIGTERM handler. */
+static void
+sigint(void)
+{
+ ldpe_shutdown();
+}
+
+static struct frr_signal_t ldpe_signals[] =
+{
+ {
+ .signal = SIGHUP,
+ /* ignore */
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+char *pkt_ptr; /* packet buffer */
+
+/* label distribution protocol engine */
+void
+ldpe(void)
+{
+#ifdef HAVE_SETPROCTITLE
+ setproctitle("ldp engine");
+#endif
+ ldpd_process = PROC_LDP_ENGINE;
+ log_procname = log_procnames[ldpd_process];
+
+ 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(ldpe_signals), ldpe_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 = ldpe_dispatch_main;
+ thread_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_data, 0, sizeof(iev_main_data));
+ iev_main_sync = &iev_main_data;
+ imsg_init(&iev_main_sync->ibuf, LDPD_FD_SYNC);
+
+ /* create base configuration */
+ leconf = config_new_empty();
+
+ struct thread thread;
+ while (thread_fetch(master, &thread))
+ thread_call(&thread);
+
+ /* NOTREACHED */
+ return;
+}
+
+void
+ldpe_init(struct ldpd_init *init)
+{
+#ifdef __OpenBSD__
+ /* This socket must be open before dropping privileges. */
+ global.pfkeysock = pfkey_init();
+ if (sysdep.no_pfkey == 0) {
+ thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock,
+ &pfkey_ev);
+ }
+#endif
+
+ /* drop privileges */
+ ldpe_privs.user = init->user;
+ ldpe_privs.group = init->group;
+ zprivs_preinit(&ldpe_privs);
+ zprivs_init(&ldpe_privs);
+
+ /* listen on ldpd control socket */
+ strlcpy(ctl_sock_path, init->ctl_sock_path, sizeof(ctl_sock_path));
+ if (control_init(ctl_sock_path) == -1)
+ fatalx("control socket setup failed");
+ TAILQ_INIT(&ctl_conns);
+ control_listen();
+
+ LIST_INIT(&global.addr_list);
+ RB_INIT(global_adj_head, &global.adj_tree);
+ TAILQ_INIT(&global.pending_conns);
+ if (inet_pton(AF_INET, AllRouters_v4, &global.mcast_addr_v4) != 1)
+ fatal("inet_pton");
+ if (inet_pton(AF_INET6, AllRouters_v6, &global.mcast_addr_v6) != 1)
+ fatal("inet_pton");
+
+ /* mark sockets as closed */
+ global.ipv4.ldp_disc_socket = -1;
+ global.ipv4.ldp_edisc_socket = -1;
+ global.ipv4.ldp_session_socket = -1;
+ global.ipv6.ldp_disc_socket = -1;
+ global.ipv6.ldp_edisc_socket = -1;
+ global.ipv6.ldp_session_socket = -1;
+
+ if ((pkt_ptr = calloc(1, IBUF_READ_SIZE)) == NULL)
+ fatal(__func__);
+
+ accept_init();
+}
+
+static void
+ldpe_shutdown(void)
+{
+ struct if_addr *if_addr;
+ struct adj *adj;
+
+ /* close pipes */
+ if (iev_lde) {
+ msgbuf_clear(&iev_lde->ibuf.w);
+ close(iev_lde->ibuf.fd);
+ iev_lde->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;
+
+ control_cleanup(ctl_sock_path);
+ config_clear(leconf);
+
+#ifdef __OpenBSD__
+ if (sysdep.no_pfkey == 0) {
+ THREAD_OFF(pfkey_ev);
+ close(global.pfkeysock);
+ }
+#endif
+ ldpe_close_sockets(AF_INET);
+ ldpe_close_sockets(AF_INET6);
+
+ /* remove addresses from global list */
+ while ((if_addr = LIST_FIRST(&global.addr_list)) != NULL) {
+ LIST_REMOVE(if_addr, entry);
+ assert(if_addr != LIST_FIRST(&global.addr_list));
+ free(if_addr);
+ }
+ while (!RB_EMPTY(global_adj_head, &global.adj_tree)) {
+ adj = RB_ROOT(global_adj_head, &global.adj_tree);
+
+ adj_del(adj, S_SHUTDOWN);
+ }
+
+ /* clean up */
+ if (iev_lde)
+ free(iev_lde);
+ free(iev_main);
+ free(pkt_ptr);
+
+ log_info("ldp engine exiting");
+
+ zlog_fini();
+
+ exit(0);
+}
+
+/* imesg */
+int
+ldpe_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
+ldpe_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
+ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data,
+ uint16_t datalen)
+{
+ if (iev_lde->ibuf.fd == -1)
+ return (0);
+ return (imsg_compose_event(iev_lde, type, peerid, pid, -1,
+ data, datalen));
+}
+
+/* ARGSUSED */
+static void ldpe_dispatch_main(struct thread *thread)
+{
+ static struct ldpd_conf *nconf;
+ struct 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;
+ int fd;
+ struct imsgev *iev = THREAD_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct iface *iface = NULL;
+ struct kif *kif;
+ int af;
+ enum socket_type *socket_type;
+ static int disc_socket = -1;
+ static int edisc_socket = -1;
+ static int session_socket = -1;
+ struct nbr *nbr;
+#ifdef __OpenBSD__
+ struct nbr_params *nbrp;
+#endif
+ 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)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("ldpe_dispatch_main: 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(leconf, kif->ifname);
+ if (iface) {
+ if_update_info(iface, kif);
+ ldp_if_update(iface, AF_UNSPEC);
+ break;
+ }
+
+ RB_FOREACH(l2vpn, l2vpn_head, &leconf->l2vpn_tree) {
+ lif = l2vpn_if_find(l2vpn, kif->ifname);
+ if (lif) {
+ l2vpn_if_update_info(lif, kif);
+ l2vpn_if_update(lif);
+ break;
+ }
+ pw = l2vpn_pw_find(l2vpn, kif->ifname);
+ if (pw) {
+ l2vpn_pw_update_info(pw, kif);
+ break;
+ }
+ }
+ break;
+ case IMSG_NEWADDR:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kaddr))
+ fatalx("NEWADDR imsg with wrong len");
+
+ if_addr_add(imsg.data);
+ break;
+ case IMSG_DELADDR:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kaddr))
+ fatalx("DELADDR imsg with wrong len");
+
+ if_addr_del(imsg.data);
+ break;
+ case IMSG_SOCKET_IPC:
+ if (iev_lde) {
+ log_warnx("%s: received unexpected imsg fd to lde", __func__);
+ break;
+ }
+ if ((fd = imsg.fd) == -1) {
+ log_warnx("%s: expected to receive imsg fd to lde but didn't receive any", __func__);
+ break;
+ }
+
+ if ((iev_lde = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_lde->ibuf, fd);
+ iev_lde->handler_read = ldpe_dispatch_lde;
+ thread_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd,
+ &iev_lde->ev_read);
+ iev_lde->handler_write = ldp_write_handler;
+ iev_lde->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));
+ ldpe_init(&init);
+ break;
+ case IMSG_AGENTX_ENABLED:
+ ldp_agentx_enabled();
+ break;
+ case IMSG_CLOSE_SOCKETS:
+ af = imsg.hdr.peerid;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->af != af)
+ continue;
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+#ifdef __OpenBSD__
+ pfkey_remove(nbr);
+#endif
+ nbr->auth.method = AUTH_NONE;
+ }
+ ldpe_close_sockets(af);
+ if_update_all(af);
+ tnbr_update_all(af);
+
+ disc_socket = -1;
+ edisc_socket = -1;
+ session_socket = -1;
+ if ((ldp_af_conf_get(leconf, af))->flags &
+ F_LDPD_AF_ENABLED)
+ ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS,
+ af, NULL, 0);
+ break;
+ case IMSG_SOCKET_NET:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(enum socket_type))
+ fatalx("SOCKET_NET imsg with wrong len");
+ socket_type = imsg.data;
+
+ switch (*socket_type) {
+ case LDP_SOCKET_DISC:
+ disc_socket = imsg.fd;
+ break;
+ case LDP_SOCKET_EDISC:
+ edisc_socket = imsg.fd;
+ break;
+ case LDP_SOCKET_SESSION:
+ session_socket = imsg.fd;
+ break;
+ }
+ break;
+ case IMSG_SETUP_SOCKETS:
+ af = imsg.hdr.peerid;
+ if (disc_socket == -1 || edisc_socket == -1 ||
+ session_socket == -1) {
+ if (disc_socket != -1)
+ close(disc_socket);
+ if (edisc_socket != -1)
+ close(edisc_socket);
+ if (session_socket != -1)
+ close(session_socket);
+ break;
+ }
+
+ ldpe_setup_sockets(af, disc_socket, edisc_socket,
+ session_socket);
+ if_update_all(af);
+ tnbr_update_all(af);
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->af != af)
+ continue;
+ nbr->laddr = (ldp_af_conf_get(leconf,
+ af))->trans_addr;
+#ifdef __OpenBSD__
+ nbrp = nbr_params_find(leconf, nbr->id);
+ if (nbrp) {
+ nbr->auth.method = nbrp->auth.method;
+ if (pfkey_establish(nbr, nbrp) == -1)
+ fatalx("pfkey setup failed");
+ }
+#endif
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ break;
+ case IMSG_RTRID_UPDATE:
+ memcpy(&global.rtr_id, imsg.data,
+ sizeof(global.rtr_id));
+ if (leconf->rtr_id.s_addr == INADDR_ANY) {
+ ldpe_reset_nbrs(AF_UNSPEC);
+ }
+ if_update_all(AF_UNSPEC);
+ tnbr_update_all(AF_UNSPEC);
+ 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(leconf, nconf);
+ ldp_clear_config(nconf);
+ nconf = NULL;
+ global.conf_seqnum++;
+ break;
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ 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;
+ ldpe_check_filter_af(AF_INET, &leconf->ipv4,
+ laccess->name);
+ ldpe_check_filter_af(AF_INET6, &leconf->ipv6,
+ laccess->name);
+ break;
+ case IMSG_LDP_SYNC_IF_STATE_REQUEST:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ldp_igp_sync_if_state_req)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ 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("%s: error handling 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 */
+ THREAD_OFF(iev->ev_read);
+ THREAD_OFF(iev->ev_write);
+ ldpe_shutdown();
+ }
+}
+
+/* ARGSUSED */
+static void ldpe_dispatch_lde(struct thread *thread)
+{
+ struct imsgev *iev = THREAD_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ struct map *map;
+ struct notify_msg *nm;
+ struct nbr *nbr;
+ int n, 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("ldpe_dispatch_lde: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_MAPPING_ADD:
+ case IMSG_RELEASE_ADD:
+ case IMSG_REQUEST_ADD:
+ case IMSG_WITHDRAW_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct map))
+ fatalx("invalid size of map request");
+ map = imsg.data;
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+ if (nbr->state != NBR_STA_OPER)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_MAPPING_ADD:
+ mapping_list_add(&nbr->mapping_list, map);
+ break;
+ case IMSG_RELEASE_ADD:
+ mapping_list_add(&nbr->release_list, map);
+ break;
+ case IMSG_REQUEST_ADD:
+ mapping_list_add(&nbr->request_list, map);
+ break;
+ case IMSG_WITHDRAW_ADD:
+ mapping_list_add(&nbr->withdraw_list, map);
+ break;
+ }
+ break;
+ case IMSG_MAPPING_ADD_END:
+ case IMSG_RELEASE_ADD_END:
+ case IMSG_REQUEST_ADD_END:
+ case IMSG_WITHDRAW_ADD_END:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+ if (nbr->state != NBR_STA_OPER)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_MAPPING_ADD_END:
+ send_labelmessage(nbr, MSG_TYPE_LABELMAPPING,
+ &nbr->mapping_list);
+ break;
+ case IMSG_RELEASE_ADD_END:
+ send_labelmessage(nbr, MSG_TYPE_LABELRELEASE,
+ &nbr->release_list);
+ break;
+ case IMSG_REQUEST_ADD_END:
+ send_labelmessage(nbr, MSG_TYPE_LABELREQUEST,
+ &nbr->request_list);
+ break;
+ case IMSG_WITHDRAW_ADD_END:
+ send_labelmessage(nbr, MSG_TYPE_LABELWITHDRAW,
+ &nbr->withdraw_list);
+ break;
+ }
+ break;
+ case IMSG_NOTIFICATION_SEND:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct notify_msg))
+ fatalx("invalid size of OE request");
+ nm = imsg.data;
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find neighbor", __func__);
+ break;
+ }
+ if (nbr->state != NBR_STA_OPER)
+ break;
+
+ send_notification_full(nbr->tcp, nm);
+ break;
+ case IMSG_CTL_END:
+ case IMSG_CTL_SHOW_LIB_BEGIN:
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ case IMSG_CTL_SHOW_LIB_SENT:
+ case IMSG_CTL_SHOW_LIB_END:
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ control_imsg_relay(&imsg);
+ break;
+ case IMSG_NBR_SHUTDOWN:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find neighbor", __func__);
+ break;
+ }
+ if (nbr->state != NBR_STA_OPER)
+ break;
+ session_shutdown(nbr,S_SHUTDOWN,0,0);
+ break;
+ default:
+ log_debug("%s: error handling 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 */
+ THREAD_OFF(iev->ev_read);
+ THREAD_OFF(iev->ev_write);
+ ldpe_shutdown();
+ }
+}
+
+#ifdef __OpenBSD__
+/* ARGSUSED */
+static void ldpe_dispatch_pfkey(struct thread *thread)
+{
+ int fd = THREAD_FD(thread);
+
+ thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock,
+ &pfkey_ev);
+
+ if (pfkey_read(fd, NULL) == -1)
+ fatal("pfkey_read failed, exiting...");
+}
+#endif /* __OpenBSD__ */
+
+static void
+ldpe_setup_sockets(int af, int disc_socket, int edisc_socket,
+ int session_socket)
+{
+ struct ldpd_af_global *af_global;
+
+ af_global = ldp_af_global_get(&global, af);
+
+ /* discovery socket */
+ af_global->ldp_disc_socket = disc_socket;
+ thread_add_read(master, disc_recv_packet, &af_global->disc_ev, af_global->ldp_disc_socket,
+ &af_global->disc_ev);
+
+ /* extended discovery socket */
+ af_global->ldp_edisc_socket = edisc_socket;
+ thread_add_read(master, disc_recv_packet, &af_global->edisc_ev, af_global->ldp_edisc_socket,
+ &af_global->edisc_ev);
+
+ /* session socket */
+ af_global->ldp_session_socket = session_socket;
+ accept_add(af_global->ldp_session_socket, session_accept, NULL);
+}
+
+static void
+ldpe_close_sockets(int af)
+{
+ struct ldpd_af_global *af_global;
+
+ af_global = ldp_af_global_get(&global, af);
+
+ /* discovery socket */
+ THREAD_OFF(af_global->disc_ev);
+ if (af_global->ldp_disc_socket != -1) {
+ close(af_global->ldp_disc_socket);
+ af_global->ldp_disc_socket = -1;
+ }
+
+ /* extended discovery socket */
+ THREAD_OFF(af_global->edisc_ev);
+ if (af_global->ldp_edisc_socket != -1) {
+ close(af_global->ldp_edisc_socket);
+ af_global->ldp_edisc_socket = -1;
+ }
+
+ /* session socket */
+ if (af_global->ldp_session_socket != -1) {
+ accept_del(af_global->ldp_session_socket);
+ close(af_global->ldp_session_socket);
+ af_global->ldp_session_socket = -1;
+ }
+}
+
+int
+ldpe_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);
+}
+
+void
+ldpe_reset_nbrs(int af)
+{
+ struct nbr *nbr;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (af == AF_UNSPEC || nbr->af == af)
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ }
+}
+
+void
+ldpe_reset_ds_nbrs(void)
+{
+ struct nbr *nbr;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->ds_tlv)
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ }
+}
+
+void
+ldpe_remove_dynamic_tnbrs(int af)
+{
+ struct tnbr *tnbr, *safe;
+
+ RB_FOREACH_SAFE(tnbr, tnbr_head, &leconf->tnbr_tree, safe) {
+ if (tnbr->af != af)
+ continue;
+
+ tnbr->flags &= ~F_TNBR_DYNAMIC;
+ tnbr_check(leconf, tnbr);
+ }
+}
+
+void
+ldpe_stop_init_backoff(int af)
+{
+ struct nbr *nbr;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->af == af && nbr_pending_idtimer(nbr)) {
+ nbr_stop_idtimer(nbr);
+ nbr_establish_connection(nbr);
+ }
+ }
+}
+
+static void
+ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t idx)
+{
+ struct iface *iface;
+ struct iface_af *ia;
+ struct ctl_iface *ictl;
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree) {
+ if (idx == 0 || idx == iface->ifindex) {
+ ia = iface_af_get(iface, af);
+ if (!ia->enabled)
+ continue;
+
+ ictl = if_to_ctl(ia);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_INTERFACE,
+ 0, 0, -1, ictl, sizeof(struct ctl_iface));
+ }
+ }
+}
+
+void
+ldpe_iface_ctl(struct ctl_conn *c, ifindex_t idx)
+{
+ ldpe_iface_af_ctl(c, AF_INET, idx);
+ ldpe_iface_af_ctl(c, AF_INET6, idx);
+}
+
+void
+ldpe_adj_ctl(struct ctl_conn *c)
+{
+ struct adj *adj;
+ struct ctl_adj *actl;
+
+ RB_FOREACH(adj, global_adj_head, &global.adj_tree) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0,
+ -1, actl, sizeof(struct ctl_adj));
+ }
+
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
+
+void
+ldpe_adj_detail_ctl(struct ctl_conn *c)
+{
+ struct iface *iface;
+ struct tnbr *tnbr;
+ struct adj *adj;
+ struct ctl_adj *actl;
+ struct ctl_disc_if ictl;
+ struct ctl_disc_tnbr tctl;
+
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, -1, NULL, 0);
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree) {
+ memset(&ictl, 0, sizeof(ictl));
+ ictl.active_v4 = (iface->ipv4.state == IF_STA_ACTIVE);
+ ictl.active_v6 = (iface->ipv6.state == IF_STA_ACTIVE);
+
+ if (!ictl.active_v4 && !ictl.active_v6)
+ continue;
+
+ strlcpy(ictl.name, iface->name, sizeof(ictl.name));
+ if (RB_EMPTY(ia_adj_head, &iface->ipv4.adj_tree) &&
+ RB_EMPTY(ia_adj_head, &iface->ipv6.adj_tree))
+ ictl.no_adj = 1;
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_IFACE, 0, 0,
+ -1, &ictl, sizeof(ictl));
+
+ RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ,
+ 0, 0, -1, actl, sizeof(struct ctl_adj));
+ }
+ RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ,
+ 0, 0, -1, actl, sizeof(struct ctl_adj));
+ }
+ }
+
+ RB_FOREACH(tnbr, tnbr_head, &leconf->tnbr_tree) {
+ memset(&tctl, 0, sizeof(tctl));
+ tctl.af = tnbr->af;
+ tctl.addr = tnbr->addr;
+ if (tnbr->adj == NULL)
+ tctl.no_adj = 1;
+
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_TNBR, 0, 0,
+ -1, &tctl, sizeof(tctl));
+
+ if (tnbr->adj == NULL)
+ continue;
+
+ actl = adj_to_ctl(tnbr->adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, 0, 0,
+ -1, actl, sizeof(struct ctl_adj));
+ }
+
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
+
+void
+ldpe_nbr_ctl(struct ctl_conn *c)
+{
+ struct adj *adj;
+ struct ctl_adj *actl;
+ struct nbr *nbr;
+ struct ctl_nbr *nctl;
+
+ RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) {
+ if (nbr->state == NBR_STA_PRESENT)
+ continue;
+
+ nctl = nbr_to_ctl(nbr);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 0, -1, nctl,
+ sizeof(struct ctl_nbr));
+
+ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_DISC,
+ 0, 0, -1, actl, sizeof(struct ctl_adj));
+ }
+
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_END, 0, 0, -1,
+ NULL, 0);
+ }
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
+
+void
+ldpe_ldp_sync_ctl(struct ctl_conn *c)
+{
+ struct iface *iface;
+ struct ctl_ldp_sync *ictl;
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree) {
+ ictl = ldp_sync_to_ctl(iface);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_LDP_SYNC,
+ 0, 0, -1, ictl, sizeof(struct ctl_ldp_sync));
+ }
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
+
+void
+mapping_list_add(struct mapping_head *mh, struct map *map)
+{
+ struct mapping_entry *me;
+
+ me = calloc(1, sizeof(*me));
+ if (me == NULL)
+ fatal(__func__);
+ me->map = *map;
+
+ TAILQ_INSERT_TAIL(mh, me, entry);
+}
+
+void
+mapping_list_clr(struct mapping_head *mh)
+{
+ struct mapping_entry *me;
+
+ while ((me = TAILQ_FIRST(mh)) != NULL) {
+ TAILQ_REMOVE(mh, me, entry);
+ assert(me != TAILQ_FIRST(mh));
+ free(me);
+ }
+}
+
+void
+ldpe_check_filter_af(int af, struct ldpd_af_conf *af_conf,
+ const char *filter_name)
+{
+ if (strcmp(af_conf->acl_thello_accept_from, filter_name) == 0)
+ ldpe_remove_dynamic_tnbrs(af);
+}
+
+void
+ldpe_set_config_change_time(void)
+{
+ /* SNMP update time when ever there is a config change */
+ leconf->config_change_time = time(NULL);
+}