summaryrefslogtreecommitdiffstats
path: root/ldpd/ldpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldpd/ldpd.c')
-rw-r--r--ldpd/ldpd.c1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
new file mode 100644
index 000000000..4e1449198
--- /dev/null
+++ b/ldpd/ldpd.c
@@ -0,0 +1,1227 @@
+/* $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 <sys/types.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+
+static void main_sig_handler(int, short, void *);
+static __dead void usage(void);
+static __dead void ldpd_shutdown(void);
+static pid_t start_child(enum ldpd_process, char *, int, int, int);
+static void main_dispatch_ldpe(int, short, void *);
+static void main_dispatch_lde(int, short, void *);
+static int main_imsg_compose_both(enum imsg_type, void *,
+ uint16_t);
+static int main_imsg_send_ipc_sockets(struct imsgbuf *,
+ struct imsgbuf *);
+static void main_imsg_send_net_sockets(int);
+static void main_imsg_send_net_socket(int, enum socket_type);
+static int main_imsg_send_config(struct ldpd_conf *);
+static int ldp_reload(void);
+static void merge_global(struct ldpd_conf *, struct ldpd_conf *);
+static void merge_af(int, struct ldpd_af_conf *,
+ struct ldpd_af_conf *);
+static void merge_ifaces(struct ldpd_conf *, struct ldpd_conf *);
+static void merge_iface_af(struct iface_af *, struct iface_af *);
+static void merge_tnbrs(struct ldpd_conf *, struct ldpd_conf *);
+static void merge_nbrps(struct ldpd_conf *, struct ldpd_conf *);
+static void merge_l2vpns(struct ldpd_conf *, struct ldpd_conf *);
+static void merge_l2vpn(struct ldpd_conf *, struct l2vpn *,
+ struct l2vpn *);
+
+struct ldpd_global global;
+struct ldpd_conf *ldpd_conf;
+
+static char *conffile;
+static struct imsgev *iev_ldpe;
+static struct imsgev *iev_lde;
+static pid_t ldpe_pid;
+static pid_t lde_pid;
+
+/* ARGSUSED */
+static void
+main_sig_handler(int sig, short event, void *arg)
+{
+ /* signal handler rules don't apply, libevent decouples for us */
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ ldpd_shutdown();
+ /* NOTREACHED */
+ case SIGHUP:
+ if (ldp_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_debug("configuration reloaded");
+ break;
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+static __dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
+ __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct event ev_sigint, ev_sigterm, ev_sighup;
+ char *saved_argv0;
+ int ch;
+ int debug = 0, lflag = 0, eflag = 0;
+ int pipe_parent2ldpe[2];
+ int pipe_parent2lde[2];
+
+ conffile = CONF_FILE;
+ ldpd_process = PROC_MAIN;
+
+ log_init(1); /* log to stderr until daemonized */
+ log_verbose(1);
+
+ saved_argv0 = argv[0];
+ if (saved_argv0 == NULL)
+ saved_argv0 = "ldpd";
+
+ while ((ch = getopt(argc, argv, "dD:f:nvLE")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'D':
+ if (cmdline_symset(optarg) < 0)
+ log_warnx("could not parse macro definition %s",
+ optarg);
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'n':
+ global.cmd_opts |= LDPD_OPT_NOACTION;
+ break;
+ case 'v':
+ if (global.cmd_opts & LDPD_OPT_VERBOSE)
+ global.cmd_opts |= LDPD_OPT_VERBOSE2;
+ global.cmd_opts |= LDPD_OPT_VERBOSE;
+ break;
+ case 'L':
+ lflag = 1;
+ break;
+ case 'E':
+ eflag = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc > 0 || (lflag && eflag))
+ usage();
+
+ if (lflag)
+ lde(debug, global.cmd_opts & LDPD_OPT_VERBOSE);
+ else if (eflag)
+ ldpe(debug, global.cmd_opts & LDPD_OPT_VERBOSE);
+
+ /* fetch interfaces early */
+ kif_init();
+
+ /* parse config file */
+ if ((ldpd_conf = parse_config(conffile)) == NULL ) {
+ kif_clear();
+ exit(1);
+ }
+
+ if (global.cmd_opts & LDPD_OPT_NOACTION) {
+ if (global.cmd_opts & LDPD_OPT_VERBOSE)
+ print_config(ldpd_conf);
+ else
+ fprintf(stderr, "configuration OK\n");
+ kif_clear();
+ exit(0);
+ }
+
+ /* check for root privileges */
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ /* check for ldpd user */
+ if (getpwnam(LDPD_USER) == NULL)
+ errx(1, "unknown user %s", LDPD_USER);
+
+ log_init(debug);
+ log_verbose(global.cmd_opts & (LDPD_OPT_VERBOSE | LDPD_OPT_VERBOSE2));
+
+ if (!debug)
+ daemon(1, 0);
+
+ log_info("startup");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
+ PF_UNSPEC, pipe_parent2ldpe) == -1)
+ fatal("socketpair");
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
+ PF_UNSPEC, pipe_parent2lde) == -1)
+ fatal("socketpair");
+
+ /* start children */
+ lde_pid = start_child(PROC_LDE_ENGINE, saved_argv0,
+ pipe_parent2lde[1], debug, global.cmd_opts & LDPD_OPT_VERBOSE);
+ ldpe_pid = start_child(PROC_LDP_ENGINE, saved_argv0,
+ pipe_parent2ldpe[1], debug, global.cmd_opts & LDPD_OPT_VERBOSE);
+
+ event_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* setup pipes to children */
+ if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL ||
+ (iev_lde = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_ldpe->ibuf, pipe_parent2ldpe[0]);
+ iev_ldpe->handler = main_dispatch_ldpe;
+ imsg_init(&iev_lde->ibuf, pipe_parent2lde[0]);
+ iev_lde->handler = main_dispatch_lde;
+
+ /* setup event handler */
+ iev_ldpe->events = EV_READ;
+ event_set(&iev_ldpe->ev, iev_ldpe->ibuf.fd, iev_ldpe->events,
+ iev_ldpe->handler, iev_ldpe);
+ event_add(&iev_ldpe->ev, NULL);
+
+ iev_lde->events = EV_READ;
+ event_set(&iev_lde->ev, iev_lde->ibuf.fd, iev_lde->events,
+ iev_lde->handler, iev_lde);
+ event_add(&iev_lde->ev, NULL);
+
+ if (main_imsg_send_ipc_sockets(&iev_ldpe->ibuf, &iev_lde->ibuf))
+ fatal("could not establish imsg links");
+ main_imsg_send_config(ldpd_conf);
+
+ /* notify ldpe about existing interfaces and addresses */
+ kif_redistribute(NULL);
+
+ if (kr_init(!(ldpd_conf->flags & F_LDPD_NO_FIB_UPDATE)) == -1)
+ fatalx("kr_init failed");
+
+ if (ldpd_conf->ipv4.flags & F_LDPD_AF_ENABLED)
+ main_imsg_send_net_sockets(AF_INET);
+ if (ldpd_conf->ipv6.flags & F_LDPD_AF_ENABLED)
+ main_imsg_send_net_sockets(AF_INET6);
+
+ /* remove unneded stuff from config */
+ /* ... */
+
+ event_dispatch();
+
+ ldpd_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+static __dead void
+ldpd_shutdown(void)
+{
+ pid_t pid;
+ int status;
+
+ /* close pipes */
+ msgbuf_clear(&iev_ldpe->ibuf.w);
+ close(iev_ldpe->ibuf.fd);
+ msgbuf_clear(&iev_lde->ibuf.w);
+ close(iev_lde->ibuf.fd);
+
+ kr_shutdown();
+ config_clear(ldpd_conf);
+
+ log_debug("waiting for children to terminate");
+ do {
+ pid = wait(&status);
+ if (pid == -1) {
+ if (errno != EINTR && errno != ECHILD)
+ fatal("wait");
+ } else if (WIFSIGNALED(status))
+ log_warnx("%s terminated; signal %d",
+ (pid == lde_pid) ? "label decision engine" :
+ "ldp engine", WTERMSIG(status));
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ free(iev_ldpe);
+ free(iev_lde);
+
+ log_info("terminating");
+ exit(0);
+}
+
+static pid_t
+start_child(enum ldpd_process p, char *argv0, int fd, int debug, int verbose)
+{
+ char *argv[5];
+ int argc = 0;
+ pid_t pid;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ case 0:
+ break;
+ default:
+ close(fd);
+ return (pid);
+ }
+
+ if (dup2(fd, 3) == -1)
+ fatal("cannot setup imsg fd");
+
+ argv[argc++] = argv0;
+ switch (p) {
+ case PROC_MAIN:
+ fatalx("Can not start main process");
+ case PROC_LDE_ENGINE:
+ argv[argc++] = "-L";
+ break;
+ case PROC_LDP_ENGINE:
+ argv[argc++] = "-E";
+ break;
+ }
+ if (debug)
+ argv[argc++] = "-d";
+ if (verbose)
+ argv[argc++] = "-v";
+ argv[argc++] = NULL;
+
+ execvp(argv0, argv);
+ fatal("execvp");
+}
+
+/* imsg handling */
+/* ARGSUSED */
+static void
+main_dispatch_ldpe(int fd, short event, void *bula)
+{
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ int af;
+ ssize_t n;
+ int shut = 0, verbose;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0)
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_REQUEST_SOCKETS:
+ af = imsg.hdr.pid;
+ main_imsg_send_net_sockets(af);
+ break;
+ case IMSG_CTL_RELOAD:
+ if (ldp_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_debug("configuration reloaded");
+ break;
+ case IMSG_CTL_FIB_COUPLE:
+ kr_fib_couple();
+ break;
+ case IMSG_CTL_FIB_DECOUPLE:
+ kr_fib_decouple();
+ break;
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_KROUTE_ADDR:
+ kr_show_route(&imsg);
+ break;
+ case IMSG_CTL_IFINFO:
+ if (imsg.hdr.len == IMSG_HEADER_SIZE)
+ kr_ifinfo(NULL, imsg.hdr.pid);
+ else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ)
+ kr_ifinfo(imsg.data, imsg.hdr.pid);
+ else
+ log_warnx("IFINFO request with wrong len");
+ break;
+ case IMSG_CTL_LOG_VERBOSE:
+ /* already checked by ldpe */
+ memcpy(&verbose, imsg.data, sizeof(verbose));
+ log_verbose(verbose);
+ 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 handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+/* ARGSUSED */
+static void
+main_dispatch_lde(int fd, short event, void *bula)
+{
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ ssize_t n;
+ int shut = 0;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0)
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_KLABEL_CHANGE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct kroute))
+ fatalx("invalid size of IMSG_KLABEL_CHANGE");
+ if (kr_change(imsg.data))
+ log_warnx("%s: error changing route", __func__);
+ break;
+ case IMSG_KLABEL_DELETE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct kroute))
+ fatalx("invalid size of IMSG_KLABEL_DELETE");
+ if (kr_delete(imsg.data))
+ log_warnx("%s: error deleting route", __func__);
+ break;
+ case IMSG_KPWLABEL_CHANGE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct kpw))
+ fatalx("invalid size of IMSG_KPWLABEL_CHANGE");
+ if (kmpw_set(imsg.data))
+ log_warnx("%s: error changing pseudowire",
+ __func__);
+ break;
+ case IMSG_KPWLABEL_DELETE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct kpw))
+ fatalx("invalid size of IMSG_KPWLABEL_DELETE");
+ if (kmpw_unset(imsg.data))
+ log_warnx("%s: error unsetting pseudowire",
+ __func__);
+ 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 handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+main_imsg_compose_ldpe(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ if (iev_ldpe == NULL)
+ return;
+ imsg_compose_event(iev_ldpe, type, 0, pid, -1, data, datalen);
+}
+
+void
+main_imsg_compose_lde(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ imsg_compose_event(iev_lde, type, 0, pid, -1, data, datalen);
+}
+
+static int
+main_imsg_compose_both(enum imsg_type type, void *buf, uint16_t len)
+{
+ if (imsg_compose_event(iev_ldpe, type, 0, 0, -1, buf, len) == -1)
+ return (-1);
+ if (imsg_compose_event(iev_lde, type, 0, 0, -1, buf, len) == -1)
+ return (-1);
+ return (0);
+}
+
+void
+imsg_event_add(struct imsgev *iev)
+{
+ iev->events = EV_READ;
+ if (iev->ibuf.w.queued)
+ iev->events |= EV_WRITE;
+
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+ event_add(&iev->ev, NULL);
+}
+
+int
+imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
+ pid_t pid, int fd, void *data, uint16_t datalen)
+{
+ int ret;
+
+ if ((ret = imsg_compose(&iev->ibuf, type, peerid,
+ pid, fd, data, datalen)) != -1)
+ imsg_event_add(iev);
+ return (ret);
+}
+
+void
+evbuf_enqueue(struct evbuf *eb, struct ibuf *buf)
+{
+ ibuf_close(&eb->wbuf, buf);
+ evbuf_event_add(eb);
+}
+
+void
+evbuf_event_add(struct evbuf *eb)
+{
+ if (eb->wbuf.queued)
+ event_add(&eb->ev, NULL);
+}
+
+void
+evbuf_init(struct evbuf *eb, int fd, void (*handler)(int, short, void *),
+ void *arg)
+{
+ msgbuf_init(&eb->wbuf);
+ eb->wbuf.fd = fd;
+ event_set(&eb->ev, eb->wbuf.fd, EV_WRITE, handler, arg);
+}
+
+void
+evbuf_clear(struct evbuf *eb)
+{
+ event_del(&eb->ev);
+ msgbuf_clear(&eb->wbuf);
+ eb->wbuf.fd = -1;
+}
+
+static int
+main_imsg_send_ipc_sockets(struct imsgbuf *ldpe_buf, struct imsgbuf *lde_buf)
+{
+ int pipe_ldpe2lde[2];
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ PF_UNSPEC, pipe_ldpe2lde) == -1)
+ return (-1);
+
+ if (imsg_compose(ldpe_buf, IMSG_SOCKET_IPC, 0, 0, pipe_ldpe2lde[0],
+ NULL, 0) == -1)
+ return (-1);
+ if (imsg_compose(lde_buf, IMSG_SOCKET_IPC, 0, 0, pipe_ldpe2lde[1],
+ NULL, 0) == -1)
+ return (-1);
+
+ return (0);
+}
+
+static void
+main_imsg_send_net_sockets(int af)
+{
+ main_imsg_send_net_socket(af, LDP_SOCKET_DISC);
+ main_imsg_send_net_socket(af, LDP_SOCKET_EDISC);
+ main_imsg_send_net_socket(af, LDP_SOCKET_SESSION);
+ imsg_compose_event(iev_ldpe, IMSG_SETUP_SOCKETS, af, 0, -1, NULL, 0);
+}
+
+static void
+main_imsg_send_net_socket(int af, enum socket_type type)
+{
+ int fd;
+
+ fd = ldp_create_socket(af, type);
+ if (fd == -1) {
+ log_warnx("%s: failed to create %s socket for address-family "
+ "%s", __func__, socket_name(type), af_name(af));
+ return;
+ }
+
+ imsg_compose_event(iev_ldpe, IMSG_SOCKET_NET, af, 0, fd, &type,
+ sizeof(type));
+}
+
+struct ldpd_af_conf *
+ldp_af_conf_get(struct ldpd_conf *xconf, int af)
+{
+ switch (af) {
+ case AF_INET:
+ return (&xconf->ipv4);
+ case AF_INET6:
+ return (&xconf->ipv6);
+ default:
+ fatalx("ldp_af_conf_get: unknown af");
+ }
+}
+
+struct ldpd_af_global *
+ldp_af_global_get(struct ldpd_global *xglobal, int af)
+{
+ switch (af) {
+ case AF_INET:
+ return (&xglobal->ipv4);
+ case AF_INET6:
+ return (&xglobal->ipv6);
+ default:
+ fatalx("ldp_af_global_get: unknown af");
+ }
+}
+
+int
+ldp_is_dual_stack(struct ldpd_conf *xconf)
+{
+ return ((xconf->ipv4.flags & F_LDPD_AF_ENABLED) &&
+ (xconf->ipv6.flags & F_LDPD_AF_ENABLED));
+}
+
+static int
+main_imsg_send_config(struct ldpd_conf *xconf)
+{
+ struct iface *iface;
+ struct tnbr *tnbr;
+ struct nbr_params *nbrp;
+ struct l2vpn *l2vpn;
+ struct l2vpn_if *lif;
+ struct l2vpn_pw *pw;
+
+ if (main_imsg_compose_both(IMSG_RECONF_CONF, xconf,
+ sizeof(*xconf)) == -1)
+ return (-1);
+
+ LIST_FOREACH(iface, &xconf->iface_list, entry) {
+ if (main_imsg_compose_both(IMSG_RECONF_IFACE, iface,
+ sizeof(*iface)) == -1)
+ return (-1);
+ }
+
+ LIST_FOREACH(tnbr, &xconf->tnbr_list, entry) {
+ if (main_imsg_compose_both(IMSG_RECONF_TNBR, tnbr,
+ sizeof(*tnbr)) == -1)
+ return (-1);
+ }
+
+ LIST_FOREACH(nbrp, &xconf->nbrp_list, entry) {
+ if (main_imsg_compose_both(IMSG_RECONF_NBRP, nbrp,
+ sizeof(*nbrp)) == -1)
+ return (-1);
+ }
+
+ LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) {
+ if (main_imsg_compose_both(IMSG_RECONF_L2VPN, l2vpn,
+ sizeof(*l2vpn)) == -1)
+ return (-1);
+
+ LIST_FOREACH(lif, &l2vpn->if_list, entry) {
+ if (main_imsg_compose_both(IMSG_RECONF_L2VPN_IF, lif,
+ sizeof(*lif)) == -1)
+ return (-1);
+ }
+ LIST_FOREACH(pw, &l2vpn->pw_list, entry) {
+ if (main_imsg_compose_both(IMSG_RECONF_L2VPN_PW, pw,
+ sizeof(*pw)) == -1)
+ return (-1);
+ }
+ }
+
+ if (main_imsg_compose_both(IMSG_RECONF_END, NULL, 0) == -1)
+ return (-1);
+
+ return (0);
+}
+
+static int
+ldp_reload(void)
+{
+ struct ldpd_conf *xconf;
+
+ if ((xconf = parse_config(conffile)) == NULL)
+ return (-1);
+
+ if (main_imsg_send_config(xconf) == -1)
+ return (-1);
+
+ merge_config(ldpd_conf, xconf);
+
+ return (0);
+}
+
+void
+merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ merge_global(conf, xconf);
+ merge_af(AF_INET, &conf->ipv4, &xconf->ipv4);
+ merge_af(AF_INET6, &conf->ipv6, &xconf->ipv6);
+ merge_ifaces(conf, xconf);
+ merge_tnbrs(conf, xconf);
+ merge_nbrps(conf, xconf);
+ merge_l2vpns(conf, xconf);
+ free(xconf);
+}
+
+static void
+merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ /* change of router-id requires resetting all neighborships */
+ if (conf->rtr_id.s_addr != xconf->rtr_id.s_addr) {
+ if (ldpd_process == PROC_LDP_ENGINE) {
+ ldpe_reset_nbrs(AF_INET);
+ ldpe_reset_nbrs(AF_INET6);
+ if (conf->rtr_id.s_addr == INADDR_ANY ||
+ xconf->rtr_id.s_addr == INADDR_ANY) {
+ if_update_all(AF_UNSPEC);
+ tnbr_update_all(AF_UNSPEC);
+ }
+ }
+ conf->rtr_id = xconf->rtr_id;
+ }
+
+ if (conf->trans_pref != xconf->trans_pref) {
+ if (ldpd_process == PROC_LDP_ENGINE)
+ ldpe_reset_ds_nbrs();
+ conf->trans_pref = xconf->trans_pref;
+ }
+
+ if ((conf->flags & F_LDPD_DS_CISCO_INTEROP) !=
+ (xconf->flags & F_LDPD_DS_CISCO_INTEROP)) {
+ if (ldpd_process == PROC_LDP_ENGINE)
+ ldpe_reset_ds_nbrs();
+ }
+
+ conf->flags = xconf->flags;
+}
+
+static void
+merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa)
+{
+ int egress_label_changed = 0;
+ int update_sockets = 0;
+
+ if (af_conf->keepalive != xa->keepalive) {
+ af_conf->keepalive = xa->keepalive;
+ if (ldpd_process == PROC_LDP_ENGINE)
+ ldpe_stop_init_backoff(af);
+ }
+ af_conf->thello_holdtime = xa->thello_holdtime;
+ af_conf->thello_interval = xa->thello_interval;
+
+ /* update flags */
+ if (ldpd_process == PROC_LDP_ENGINE &&
+ (af_conf->flags & F_LDPD_AF_THELLO_ACCEPT) &&
+ !(xa->flags & F_LDPD_AF_THELLO_ACCEPT))
+ ldpe_remove_dynamic_tnbrs(af);
+
+ if ((af_conf->flags & F_LDPD_AF_NO_GTSM) !=
+ (xa->flags & F_LDPD_AF_NO_GTSM)) {
+ if (af == AF_INET6)
+ /* need to set/unset IPV6_MINHOPCOUNT */
+ update_sockets = 1;
+ else if (ldpd_process == PROC_LDP_ENGINE)
+ /* for LDPv4 just resetting the neighbors is enough */
+ ldpe_reset_nbrs(af);
+ }
+
+ if ((af_conf->flags & F_LDPD_AF_EXPNULL) !=
+ (xa->flags & F_LDPD_AF_EXPNULL))
+ egress_label_changed = 1;
+
+ af_conf->flags = xa->flags;
+
+ if (egress_label_changed) {
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ lde_change_egress_label(af, af_conf->flags &
+ F_LDPD_AF_EXPNULL);
+ break;
+ case PROC_MAIN:
+ kr_change_egress_label(af, af_conf->flags &
+ F_LDPD_AF_EXPNULL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ldp_addrcmp(af, &af_conf->trans_addr, &xa->trans_addr)) {
+ af_conf->trans_addr = xa->trans_addr;
+ update_sockets = 1;
+ }
+
+ if (ldpd_process == PROC_MAIN && update_sockets)
+ imsg_compose_event(iev_ldpe, IMSG_CLOSE_SOCKETS, af, 0, -1,
+ NULL, 0);
+}
+
+static void
+merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ struct iface *iface, *itmp, *xi;
+
+ LIST_FOREACH_SAFE(iface, &conf->iface_list, entry, itmp) {
+ /* find deleted interfaces */
+ if ((xi = if_lookup(xconf, iface->ifindex)) == NULL) {
+ LIST_REMOVE(iface, entry);
+ if (ldpd_process == PROC_LDP_ENGINE)
+ if_exit(iface);
+ free(iface);
+ }
+ }
+ LIST_FOREACH_SAFE(xi, &xconf->iface_list, entry, itmp) {
+ /* find new interfaces */
+ if ((iface = if_lookup(conf, xi->ifindex)) == NULL) {
+ LIST_REMOVE(xi, entry);
+ LIST_INSERT_HEAD(&conf->iface_list, xi, entry);
+
+ /* resend addresses to activate new interfaces */
+ if (ldpd_process == PROC_MAIN)
+ kif_redistribute(xi->name);
+ continue;
+ }
+
+ /* update existing interfaces */
+ merge_iface_af(&iface->ipv4, &xi->ipv4);
+ merge_iface_af(&iface->ipv6, &xi->ipv6);
+ LIST_REMOVE(xi, entry);
+ free(xi);
+ }
+}
+
+static void
+merge_iface_af(struct iface_af *ia, struct iface_af *xi)
+{
+ if (ia->enabled != xi->enabled) {
+ ia->enabled = xi->enabled;
+ if (ldpd_process == PROC_LDP_ENGINE)
+ if_update(ia->iface, ia->af);
+ }
+ ia->hello_holdtime = xi->hello_holdtime;
+ ia->hello_interval = xi->hello_interval;
+}
+
+static void
+merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ struct tnbr *tnbr, *ttmp, *xt;
+
+ LIST_FOREACH_SAFE(tnbr, &conf->tnbr_list, entry, ttmp) {
+ if (!(tnbr->flags & F_TNBR_CONFIGURED))
+ continue;
+
+ /* find deleted tnbrs */
+ if ((xt = tnbr_find(xconf, tnbr->af, &tnbr->addr)) == NULL) {
+ if (ldpd_process == PROC_LDP_ENGINE) {
+ tnbr->flags &= ~F_TNBR_CONFIGURED;
+ tnbr_check(tnbr);
+ } else {
+ LIST_REMOVE(tnbr, entry);
+ free(tnbr);
+ }
+ }
+ }
+ LIST_FOREACH_SAFE(xt, &xconf->tnbr_list, entry, ttmp) {
+ /* find new tnbrs */
+ if ((tnbr = tnbr_find(conf, xt->af, &xt->addr)) == NULL) {
+ LIST_REMOVE(xt, entry);
+ LIST_INSERT_HEAD(&conf->tnbr_list, xt, entry);
+
+ if (ldpd_process == PROC_LDP_ENGINE)
+ tnbr_update(xt);
+ continue;
+ }
+
+ /* update existing tnbrs */
+ if (!(tnbr->flags & F_TNBR_CONFIGURED))
+ tnbr->flags |= F_TNBR_CONFIGURED;
+ tnbr->hello_holdtime = xt->hello_holdtime;
+ tnbr->hello_interval = xt->hello_interval;
+ LIST_REMOVE(xt, entry);
+ free(xt);
+ }
+}
+
+static void
+merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ struct nbr_params *nbrp, *ntmp, *xn;
+ struct nbr *nbr;
+ int nbrp_changed;
+
+ LIST_FOREACH_SAFE(nbrp, &conf->nbrp_list, entry, ntmp) {
+ /* find deleted nbrps */
+ if ((xn = nbr_params_find(xconf, nbrp->lsr_id)) == NULL) {
+ if (ldpd_process == PROC_LDP_ENGINE) {
+ nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr);
+ if (nbr) {
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ pfkey_remove(nbr);
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ }
+ LIST_REMOVE(nbrp, entry);
+ free(nbrp);
+ }
+ }
+ LIST_FOREACH_SAFE(xn, &xconf->nbrp_list, entry, ntmp) {
+ /* find new nbrps */
+ if ((nbrp = nbr_params_find(conf, xn->lsr_id)) == NULL) {
+ LIST_REMOVE(xn, entry);
+ LIST_INSERT_HEAD(&conf->nbrp_list, xn, entry);
+
+ if (ldpd_process == PROC_LDP_ENGINE) {
+ nbr = nbr_find_ldpid(xn->lsr_id.s_addr);
+ if (nbr) {
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ if (pfkey_establish(nbr, xn) == -1)
+ fatalx("pfkey setup failed");
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ }
+ continue;
+ }
+
+ /* update existing nbrps */
+ if (nbrp->flags != xn->flags ||
+ nbrp->keepalive != xn->keepalive ||
+ nbrp->gtsm_enabled != xn->gtsm_enabled ||
+ nbrp->gtsm_hops != xn->gtsm_hops ||
+ nbrp->auth.method != xn->auth.method ||
+ strcmp(nbrp->auth.md5key, xn->auth.md5key) != 0)
+ nbrp_changed = 1;
+ else
+ nbrp_changed = 0;
+
+ nbrp->keepalive = xn->keepalive;
+ nbrp->gtsm_enabled = xn->gtsm_enabled;
+ nbrp->gtsm_hops = xn->gtsm_hops;
+ nbrp->auth.method = xn->auth.method;
+ strlcpy(nbrp->auth.md5key, xn->auth.md5key,
+ sizeof(nbrp->auth.md5key));
+ nbrp->auth.md5key_len = xn->auth.md5key_len;
+ nbrp->flags = xn->flags;
+
+ if (ldpd_process == PROC_LDP_ENGINE) {
+ nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr);
+ if (nbr && nbrp_changed) {
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ pfkey_remove(nbr);
+ if (pfkey_establish(nbr, nbrp) == -1)
+ fatalx("pfkey setup failed");
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ }
+ LIST_REMOVE(xn, entry);
+ free(xn);
+ }
+}
+
+static void
+merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ struct l2vpn *l2vpn, *ltmp, *xl;
+
+ LIST_FOREACH_SAFE(l2vpn, &conf->l2vpn_list, entry, ltmp) {
+ /* find deleted l2vpns */
+ if ((xl = l2vpn_find(xconf, l2vpn->name)) == NULL) {
+ LIST_REMOVE(l2vpn, entry);
+
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ l2vpn_exit(l2vpn);
+ break;
+ case PROC_LDP_ENGINE:
+ ldpe_l2vpn_exit(l2vpn);
+ break;
+ case PROC_MAIN:
+ break;
+ }
+ l2vpn_del(l2vpn);
+ }
+ }
+ LIST_FOREACH_SAFE(xl, &xconf->l2vpn_list, entry, ltmp) {
+ /* find new l2vpns */
+ if ((l2vpn = l2vpn_find(conf, xl->name)) == NULL) {
+ LIST_REMOVE(xl, entry);
+ LIST_INSERT_HEAD(&conf->l2vpn_list, xl, entry);
+
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ l2vpn_init(xl);
+ break;
+ case PROC_LDP_ENGINE:
+ ldpe_l2vpn_init(xl);
+ break;
+ case PROC_MAIN:
+ break;
+ }
+ continue;
+ }
+
+ /* update existing l2vpns */
+ merge_l2vpn(conf, l2vpn, xl);
+ LIST_REMOVE(xl, entry);
+ free(xl);
+ }
+}
+
+static void
+merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl)
+{
+ struct l2vpn_if *lif, *ftmp, *xf;
+ struct l2vpn_pw *pw, *ptmp, *xp;
+ struct nbr *nbr;
+ int reset_nbr, reinstall_pwfec, reinstall_tnbr;
+ int previous_pw_type, previous_mtu;
+
+ previous_pw_type = l2vpn->pw_type;
+ previous_mtu = l2vpn->mtu;
+
+ /* merge intefaces */
+ LIST_FOREACH_SAFE(lif, &l2vpn->if_list, entry, ftmp) {
+ /* find deleted interfaces */
+ if ((xf = l2vpn_if_find(xl, lif->ifindex)) == NULL) {
+ LIST_REMOVE(lif, entry);
+ free(lif);
+ }
+ }
+ LIST_FOREACH_SAFE(xf, &xl->if_list, entry, ftmp) {
+ /* find new interfaces */
+ if ((lif = l2vpn_if_find(l2vpn, xf->ifindex)) == NULL) {
+ LIST_REMOVE(xf, entry);
+ LIST_INSERT_HEAD(&l2vpn->if_list, xf, entry);
+ xf->l2vpn = l2vpn;
+ continue;
+ }
+
+ LIST_REMOVE(xf, entry);
+ free(xf);
+ }
+
+ /* merge pseudowires */
+ LIST_FOREACH_SAFE(pw, &l2vpn->pw_list, entry, ptmp) {
+ /* find deleted pseudowires */
+ if ((xp = l2vpn_pw_find(xl, pw->ifindex)) == NULL) {
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ l2vpn_pw_exit(pw);
+ break;
+ case PROC_LDP_ENGINE:
+ ldpe_l2vpn_pw_exit(pw);
+ break;
+ case PROC_MAIN:
+ break;
+ }
+
+ LIST_REMOVE(pw, entry);
+ free(pw);
+ }
+ }
+ LIST_FOREACH_SAFE(xp, &xl->pw_list, entry, ptmp) {
+ /* find new pseudowires */
+ if ((pw = l2vpn_pw_find(l2vpn, xp->ifindex)) == NULL) {
+ LIST_REMOVE(xp, entry);
+ LIST_INSERT_HEAD(&l2vpn->pw_list, xp, entry);
+ xp->l2vpn = l2vpn;
+
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ l2vpn_pw_init(xp);
+ break;
+ case PROC_LDP_ENGINE:
+ ldpe_l2vpn_pw_init(xp);
+ break;
+ case PROC_MAIN:
+ break;
+ }
+ continue;
+ }
+
+ /* update existing pseudowire */
+ if (pw->af != xp->af ||
+ ldp_addrcmp(pw->af, &pw->addr, &xp->addr))
+ reinstall_tnbr = 1;
+ else
+ reinstall_tnbr = 0;
+
+ /* changes that require a session restart */
+ if ((pw->flags & (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF)) !=
+ (xp->flags & (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF)))
+ reset_nbr = 1;
+ else
+ reset_nbr = 0;
+
+ if (l2vpn->pw_type != xl->pw_type || l2vpn->mtu != xl->mtu ||
+ pw->pwid != xp->pwid || reinstall_tnbr || reset_nbr ||
+ pw->lsr_id.s_addr != xp->lsr_id.s_addr)
+ reinstall_pwfec = 1;
+ else
+ reinstall_pwfec = 0;
+
+ if (ldpd_process == PROC_LDP_ENGINE) {
+ if (reinstall_tnbr)
+ ldpe_l2vpn_pw_exit(pw);
+ if (reset_nbr) {
+ nbr = nbr_find_ldpid(pw->lsr_id.s_addr);
+ if (nbr && nbr->state == NBR_STA_OPER)
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ }
+ }
+ if (ldpd_process == PROC_LDE_ENGINE &&
+ !reset_nbr && reinstall_pwfec)
+ l2vpn_pw_exit(pw);
+ pw->lsr_id = xp->lsr_id;
+ pw->af = xp->af;
+ pw->addr = xp->addr;
+ pw->pwid = xp->pwid;
+ strlcpy(pw->ifname, xp->ifname, sizeof(pw->ifname));
+ pw->ifindex = xp->ifindex;
+ if (xp->flags & F_PW_CWORD_CONF)
+ pw->flags |= F_PW_CWORD_CONF;
+ else
+ pw->flags &= ~F_PW_CWORD_CONF;
+ if (xp->flags & F_PW_STATUSTLV_CONF)
+ pw->flags |= F_PW_STATUSTLV_CONF;
+ else
+ pw->flags &= ~F_PW_STATUSTLV_CONF;
+ if (ldpd_process == PROC_LDP_ENGINE && reinstall_tnbr)
+ ldpe_l2vpn_pw_init(pw);
+ if (ldpd_process == PROC_LDE_ENGINE &&
+ !reset_nbr && reinstall_pwfec) {
+ l2vpn->pw_type = xl->pw_type;
+ l2vpn->mtu = xl->mtu;
+ l2vpn_pw_init(pw);
+ l2vpn->pw_type = previous_pw_type;
+ l2vpn->mtu = previous_mtu;
+ }
+
+ LIST_REMOVE(xp, entry);
+ free(xp);
+ }
+
+ l2vpn->pw_type = xl->pw_type;
+ l2vpn->mtu = xl->mtu;
+ strlcpy(l2vpn->br_ifname, xl->br_ifname, sizeof(l2vpn->br_ifname));
+ l2vpn->br_ifindex = xl->br_ifindex;
+}
+
+struct ldpd_conf *
+config_new_empty(void)
+{
+ struct ldpd_conf *xconf;
+
+ xconf = calloc(1, sizeof(*xconf));
+ if (xconf == NULL)
+ fatal(NULL);
+
+ LIST_INIT(&xconf->iface_list);
+ LIST_INIT(&xconf->tnbr_list);
+ LIST_INIT(&xconf->nbrp_list);
+ LIST_INIT(&xconf->l2vpn_list);
+
+ return (xconf);
+}
+
+void
+config_clear(struct ldpd_conf *conf)
+{
+ struct ldpd_conf *xconf;
+
+ /*
+ * Merge current config with an empty config, this will deactivate
+ * and deallocate all the interfaces, pseudowires and so on. Before
+ * merging, copy the router-id and other variables to avoid some
+ * unnecessary operations, like trying to reset the neighborships.
+ */
+ xconf = config_new_empty();
+ xconf->ipv4 = conf->ipv4;
+ xconf->ipv6 = conf->ipv6;
+ xconf->rtr_id = conf->rtr_id;
+ xconf->trans_pref = conf->trans_pref;
+ xconf->flags = conf->flags;
+ merge_config(conf, xconf);
+ free(conf);
+}