summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ldpd/accept.c135
-rw-r--r--ldpd/address.c297
-rw-r--r--ldpd/adjacency.c346
-rw-r--r--ldpd/control.c308
-rw-r--r--ldpd/control.h38
-rw-r--r--ldpd/hello.c574
-rw-r--r--ldpd/init.c166
-rw-r--r--ldpd/interface.c531
-rw-r--r--ldpd/keepalive.c58
-rw-r--r--ldpd/l2vpn.c510
-rw-r--r--ldpd/labelmapping.c764
-rw-r--r--ldpd/lde.c1335
-rw-r--r--ldpd/lde.h202
-rw-r--r--ldpd/lde_lib.c784
-rw-r--r--ldpd/ldp.h304
-rw-r--r--ldpd/ldpd.c1227
-rw-r--r--ldpd/ldpd.h606
-rw-r--r--ldpd/ldpe.c808
-rw-r--r--ldpd/ldpe.h282
-rw-r--r--ldpd/log.c600
-rw-r--r--ldpd/log.h63
-rw-r--r--ldpd/neighbor.c827
-rw-r--r--ldpd/notification.c239
-rw-r--r--ldpd/packet.c788
-rw-r--r--ldpd/pfkey.c466
-rw-r--r--ldpd/socket.c391
-rw-r--r--ldpd/util.c356
-rw-r--r--lib/imsg-buffer.c309
-rw-r--r--lib/imsg.c302
-rw-r--r--lib/imsg.h112
-rw-r--r--lib/openbsd-queue.h533
-rw-r--r--lib/openbsd-tree.h748
32 files changed, 15009 insertions, 0 deletions
diff --git a/ldpd/accept.c b/ldpd/accept.c
new file mode 100644
index 000000000..bc13ad49e
--- /dev/null
+++ b/ldpd/accept.c
@@ -0,0 +1,135 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2012 Claudio Jeker <claudio@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 <stdlib.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+struct accept_ev {
+ LIST_ENTRY(accept_ev) entry;
+ struct event ev;
+ void (*accept_cb)(int, short, void *);
+ void *arg;
+ int fd;
+};
+
+struct {
+ LIST_HEAD(, accept_ev) queue;
+ struct event evt;
+} accept_queue;
+
+static void accept_arm(void);
+static void accept_unarm(void);
+static void accept_cb(int, short, void *);
+static void accept_timeout(int, short, void *);
+
+void
+accept_init(void)
+{
+ LIST_INIT(&accept_queue.queue);
+ evtimer_set(&accept_queue.evt, accept_timeout, NULL);
+}
+
+int
+accept_add(int fd, void (*cb)(int, short, void *), void *arg)
+{
+ struct accept_ev *av;
+
+ if ((av = calloc(1, sizeof(*av))) == NULL)
+ return (-1);
+ av->fd = fd;
+ av->accept_cb = cb;
+ av->arg = arg;
+ LIST_INSERT_HEAD(&accept_queue.queue, av, entry);
+
+ event_set(&av->ev, av->fd, EV_READ, accept_cb, av);
+ event_add(&av->ev, NULL);
+
+ log_debug("%s: accepting on fd %d", __func__, fd);
+
+ return (0);
+}
+
+void
+accept_del(int fd)
+{
+ struct accept_ev *av;
+
+ LIST_FOREACH(av, &accept_queue.queue, entry)
+ if (av->fd == fd) {
+ log_debug("%s: %d removed from queue", __func__, fd);
+ event_del(&av->ev);
+ LIST_REMOVE(av, entry);
+ free(av);
+ return;
+ }
+}
+
+void
+accept_pause(void)
+{
+ struct timeval evtpause = { 1, 0 };
+
+ log_debug(__func__);
+ accept_unarm();
+ evtimer_add(&accept_queue.evt, &evtpause);
+}
+
+void
+accept_unpause(void)
+{
+ if (evtimer_pending(&accept_queue.evt, NULL)) {
+ log_debug(__func__);
+ evtimer_del(&accept_queue.evt);
+ accept_arm();
+ }
+}
+
+static void
+accept_arm(void)
+{
+ struct accept_ev *av;
+ LIST_FOREACH(av, &accept_queue.queue, entry)
+ event_add(&av->ev, NULL);
+}
+
+static void
+accept_unarm(void)
+{
+ struct accept_ev *av;
+ LIST_FOREACH(av, &accept_queue.queue, entry)
+ event_del(&av->ev);
+}
+
+static void
+accept_cb(int fd, short event, void *arg)
+{
+ struct accept_ev *av = arg;
+ event_add(&av->ev, NULL);
+ av->accept_cb(fd, event, av->arg);
+}
+
+static void
+accept_timeout(int fd, short event, void *bula)
+{
+ log_debug(__func__);
+ accept_arm();
+}
diff --git a/ldpd/address.c b/ldpd/address.c
new file mode 100644
index 000000000..5e95fcc27
--- /dev/null
+++ b/ldpd/address.c
@@ -0,0 +1,297 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@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 <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+
+static void send_address(struct nbr *, int, struct if_addr_head *,
+ unsigned int, int);
+static int gen_address_list_tlv(struct ibuf *, uint16_t, int,
+ struct if_addr_head *, unsigned int);
+static void address_list_add(struct if_addr_head *, struct if_addr *);
+static void address_list_clr(struct if_addr_head *);
+
+static void
+send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list,
+ unsigned int addr_count, int withdraw)
+{
+ struct ibuf *buf;
+ uint16_t msg_type;
+ uint8_t addr_size;
+ struct if_addr *if_addr;
+ uint16_t size;
+ unsigned int tlv_addr_count = 0;
+ int err = 0;
+
+ /* nothing to send */
+ if (LIST_EMPTY(addr_list))
+ return;
+
+ if (!withdraw)
+ msg_type = MSG_TYPE_ADDR;
+ else
+ msg_type = MSG_TYPE_ADDRWITHDRAW;
+
+ switch (af) {
+ case AF_INET:
+ addr_size = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ addr_size = sizeof(struct in6_addr);
+ break;
+ default:
+ fatalx("send_address: unknown af");
+ }
+
+ while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
+ /*
+ * Send as many addresses as possible - respect the session's
+ * negotiated maximum pdu length.
+ */
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE;
+ if (size + addr_count * addr_size <= nbr->max_pdu_len)
+ tlv_addr_count = addr_count;
+ else
+ tlv_addr_count = (nbr->max_pdu_len - size) / addr_size;
+ size += tlv_addr_count * addr_size;
+ addr_count -= tlv_addr_count;
+
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ err |= gen_ldp_hdr(buf, size);
+ size -= LDP_HDR_SIZE;
+ err |= gen_msg_hdr(buf, msg_type, size);
+ size -= LDP_MSG_SIZE;
+ err |= gen_address_list_tlv(buf, size, af, addr_list,
+ tlv_addr_count);
+ if (err) {
+ address_list_clr(addr_list);
+ ibuf_free(buf);
+ return;
+ }
+
+ while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
+ log_debug("msg-out: %s: lsr-id %s, address %s",
+ msg_name(msg_type), inet_ntoa(nbr->id),
+ log_addr(af, &if_addr->addr));
+
+ LIST_REMOVE(if_addr, entry);
+ free(if_addr);
+ if (--tlv_addr_count == 0)
+ break;
+ }
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+ }
+
+ nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+}
+
+void
+send_address_single(struct nbr *nbr, struct if_addr *if_addr, int withdraw)
+{
+ struct if_addr_head addr_list;
+
+ LIST_INIT(&addr_list);
+ address_list_add(&addr_list, if_addr);
+ send_address(nbr, if_addr->af, &addr_list, 1, withdraw);
+}
+
+void
+send_address_all(struct nbr *nbr, int af)
+{
+ struct if_addr_head addr_list;
+ struct if_addr *if_addr;
+ unsigned int addr_count = 0;
+
+ LIST_INIT(&addr_list);
+ LIST_FOREACH(if_addr, &global.addr_list, entry) {
+ if (if_addr->af != af)
+ continue;
+
+ address_list_add(&addr_list, if_addr);
+ addr_count++;
+ }
+
+ send_address(nbr, af, &addr_list, addr_count, 0);
+}
+
+int
+recv_address(struct nbr *nbr, char *buf, uint16_t len)
+{
+ struct ldp_msg msg;
+ uint16_t msg_type;
+ struct address_list_tlv alt;
+ enum imsg_type type;
+ struct lde_addr lde_addr;
+
+ memcpy(&msg, buf, sizeof(msg));
+ buf += LDP_MSG_SIZE;
+ len -= LDP_MSG_SIZE;
+
+ /* Address List TLV */
+ if (len < ADDR_LIST_SIZE) {
+ session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ memcpy(&alt, buf, sizeof(alt));
+ if (ntohs(alt.length) != len - TLV_HDR_SIZE) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ if (ntohs(alt.type) != TLV_TYPE_ADDRLIST) {
+ session_shutdown(nbr, S_UNKNOWN_TLV, msg.id, msg.type);
+ return (-1);
+ }
+ switch (ntohs(alt.family)) {
+ case AF_IPV4:
+ if (!nbr->v4_enabled)
+ /* just ignore the message */
+ return (0);
+ break;
+ case AF_IPV6:
+ if (!nbr->v6_enabled)
+ /* just ignore the message */
+ return (0);
+ break;
+ default:
+ send_notification_nbr(nbr, S_UNSUP_ADDR, msg.id, msg.type);
+ return (-1);
+ }
+ buf += sizeof(alt);
+ len -= sizeof(alt);
+
+ msg_type = ntohs(msg.type);
+ if (msg_type == MSG_TYPE_ADDR)
+ type = IMSG_ADDRESS_ADD;
+ else
+ type = IMSG_ADDRESS_DEL;
+
+ while (len > 0) {
+ switch (ntohs(alt.family)) {
+ case AF_IPV4:
+ if (len < sizeof(struct in_addr)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ memset(&lde_addr, 0, sizeof(lde_addr));
+ lde_addr.af = AF_INET;
+ memcpy(&lde_addr.addr, buf, sizeof(struct in_addr));
+
+ buf += sizeof(struct in_addr);
+ len -= sizeof(struct in_addr);
+ break;
+ case AF_IPV6:
+ if (len < sizeof(struct in6_addr)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ memset(&lde_addr, 0, sizeof(lde_addr));
+ lde_addr.af = AF_INET6;
+ memcpy(&lde_addr.addr, buf, sizeof(struct in6_addr));
+
+ buf += sizeof(struct in6_addr);
+ len -= sizeof(struct in6_addr);
+ break;
+ default:
+ fatalx("recv_address: unknown af");
+ }
+
+ log_debug("msg-in: %s: lsr-id %s, address %s",
+ msg_name(msg_type), inet_ntoa(nbr->id),
+ log_addr(lde_addr.af, &lde_addr.addr));
+
+ ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr,
+ sizeof(lde_addr));
+ }
+
+ return (0);
+}
+
+static int
+gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af,
+ struct if_addr_head *addr_list, unsigned int tlv_addr_count)
+{
+ struct address_list_tlv alt;
+ uint16_t addr_size;
+ struct if_addr *if_addr;
+ int err = 0;
+
+ memset(&alt, 0, sizeof(alt));
+ alt.type = TLV_TYPE_ADDRLIST;
+ alt.length = htons(size - TLV_HDR_SIZE);
+
+ switch (af) {
+ case AF_INET:
+ alt.family = htons(AF_IPV4);
+ addr_size = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ alt.family = htons(AF_IPV6);
+ addr_size = sizeof(struct in6_addr);
+ break;
+ default:
+ fatalx("gen_address_list_tlv: unknown af");
+ }
+
+ err |= ibuf_add(buf, &alt, sizeof(alt));
+ LIST_FOREACH(if_addr, addr_list, entry) {
+ err |= ibuf_add(buf, &if_addr->addr, addr_size);
+ if (--tlv_addr_count == 0)
+ break;
+ }
+
+ return (err);
+}
+
+static void
+address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr)
+{
+ struct if_addr *new;
+
+ new = malloc(sizeof(*new));
+ if (new == NULL)
+ fatal(__func__);
+ *new = *if_addr;
+
+ LIST_INSERT_HEAD(addr_list, new, entry);
+}
+
+static void
+address_list_clr(struct if_addr_head *addr_list)
+{
+ struct if_addr *if_addr;
+
+ while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
+ LIST_REMOVE(if_addr, entry);
+ free(if_addr);
+ }
+}
diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c
new file mode 100644
index 000000000..266717729
--- /dev/null
+++ b/ldpd/adjacency.c
@@ -0,0 +1,346 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@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 <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+static void adj_del_single(struct adj *);
+static void adj_itimer(int, short, void *);
+static void tnbr_del(struct tnbr *);
+static void tnbr_hello_timer(int, short, void *);
+static void tnbr_start_hello_timer(struct tnbr *);
+static void tnbr_stop_hello_timer(struct tnbr *);
+
+struct adj *
+adj_new(struct in_addr lsr_id, struct hello_source *source,
+ union ldpd_addr *addr)
+{
+ struct adj *adj;
+
+ log_debug("%s: lsr-id %s, %s", __func__, inet_ntoa(lsr_id),
+ log_hello_src(source));
+
+ if ((adj = calloc(1, sizeof(*adj))) == NULL)
+ fatal(__func__);
+
+ adj->lsr_id = lsr_id;
+ adj->nbr = NULL;
+ adj->source = *source;
+ adj->trans_addr = *addr;
+
+ evtimer_set(&adj->inactivity_timer, adj_itimer, adj);
+
+ LIST_INSERT_HEAD(&global.adj_list, adj, global_entry);
+
+ switch (source->type) {
+ case HELLO_LINK:
+ LIST_INSERT_HEAD(&source->link.ia->adj_list, adj, ia_entry);
+ break;
+ case HELLO_TARGETED:
+ source->target->adj = adj;
+ break;
+ }
+
+ return (adj);
+}
+
+static void
+adj_del_single(struct adj *adj)
+{
+ log_debug("%s: lsr-id %s, %s (%s)", __func__, inet_ntoa(adj->lsr_id),
+ log_hello_src(&adj->source), af_name(adj_get_af(adj)));
+
+ adj_stop_itimer(adj);
+
+ LIST_REMOVE(adj, global_entry);
+ if (adj->nbr)
+ LIST_REMOVE(adj, nbr_entry);
+ switch (adj->source.type) {
+ case HELLO_LINK:
+ LIST_REMOVE(adj, ia_entry);
+ break;
+ case HELLO_TARGETED:
+ adj->source.target->adj = NULL;
+ break;
+ }
+
+ free(adj);
+}
+
+void
+adj_del(struct adj *adj, uint32_t notif_status)
+{
+ struct nbr *nbr = adj->nbr;
+ struct adj *atmp;
+
+ adj_del_single(adj);
+
+ /*
+ * If the neighbor still exists but none of its remaining
+ * adjacencies (if any) are from the preferred address-family,
+ * then delete it.
+ */
+ if (nbr && nbr_adj_count(nbr, nbr->af) == 0) {
+ LIST_FOREACH_SAFE(adj, &nbr->adj_list, nbr_entry, atmp)
+ adj_del_single(adj);
+ session_shutdown(nbr, notif_status, 0, 0);
+ nbr_del(nbr);
+ }
+}
+
+struct adj *
+adj_find(struct hello_source *source)
+{
+ struct adj *adj;
+
+ LIST_FOREACH(adj, &global.adj_list, global_entry) {
+ if (adj->source.type != source->type)
+ continue;
+
+ switch (source->type) {
+ case HELLO_LINK:
+ if (ldp_addrcmp(source->link.ia->af,
+ &adj->source.link.src_addr,
+ &source->link.src_addr) == 0)
+ return (adj);
+ break;
+ case HELLO_TARGETED:
+ if (adj->source.target == source->target)
+ return (adj);
+ break;
+ }
+ }
+
+ return (NULL);
+}
+
+int
+adj_get_af(struct adj *adj)
+{
+ switch (adj->source.type) {
+ case HELLO_LINK:
+ return (adj->source.link.ia->af);
+ case HELLO_TARGETED:
+ return (adj->source.target->af);
+ default:
+ fatalx("adj_get_af: unknown hello type");
+ }
+}
+
+/* adjacency timers */
+
+/* ARGSUSED */
+static void
+adj_itimer(int fd, short event, void *arg)
+{
+ struct adj *adj = arg;
+
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(adj->lsr_id));
+
+ if (adj->source.type == HELLO_TARGETED) {
+ if (!(adj->source.target->flags & F_TNBR_CONFIGURED) &&
+ adj->source.target->pw_count == 0) {
+ /* remove dynamic targeted neighbor */
+ tnbr_del(adj->source.target);
+ return;
+ }
+ adj->source.target->adj = NULL;
+ }
+
+ adj_del(adj, S_HOLDTIME_EXP);
+}
+
+void
+adj_start_itimer(struct adj *adj)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = adj->holdtime;
+ if (evtimer_add(&adj->inactivity_timer, &tv) == -1)
+ fatal(__func__);
+}
+
+void
+adj_stop_itimer(struct adj *adj)
+{
+ if (evtimer_pending(&adj->inactivity_timer, NULL) &&
+ evtimer_del(&adj->inactivity_timer) == -1)
+ fatal(__func__);
+}
+
+/* targeted neighbors */
+
+struct tnbr *
+tnbr_new(struct ldpd_conf *xconf, int af, union ldpd_addr *addr)
+{
+ struct tnbr *tnbr;
+
+ if ((tnbr = calloc(1, sizeof(*tnbr))) == NULL)
+ fatal(__func__);
+
+ tnbr->af = af;
+ tnbr->addr = *addr;
+ tnbr->state = TNBR_STA_DOWN;
+ tnbr->hello_holdtime = (ldp_af_conf_get(xconf, af))->thello_holdtime;
+ tnbr->hello_interval = (ldp_af_conf_get(xconf, af))->thello_interval;
+
+ return (tnbr);
+}
+
+static void
+tnbr_del(struct tnbr *tnbr)
+{
+ tnbr_stop_hello_timer(tnbr);
+ if (tnbr->adj)
+ adj_del(tnbr->adj, S_SHUTDOWN);
+ LIST_REMOVE(tnbr, entry);
+ free(tnbr);
+}
+
+struct tnbr *
+tnbr_find(struct ldpd_conf *xconf, int af, union ldpd_addr *addr)
+{
+ struct tnbr *tnbr;
+
+ LIST_FOREACH(tnbr, &xconf->tnbr_list, entry)
+ if (af == tnbr->af &&
+ ldp_addrcmp(af, addr, &tnbr->addr) == 0)
+ return (tnbr);
+
+ return (NULL);
+}
+
+struct tnbr *
+tnbr_check(struct tnbr *tnbr)
+{
+ if (!(tnbr->flags & (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC)) &&
+ tnbr->pw_count == 0) {
+ tnbr_del(tnbr);
+ return (NULL);
+ }
+
+ return (tnbr);
+}
+
+void
+tnbr_update(struct tnbr *tnbr)
+{
+ int socket_ok, rtr_id_ok;
+
+ if ((ldp_af_global_get(&global, tnbr->af))->ldp_edisc_socket != -1)
+ socket_ok = 1;
+ else
+ socket_ok = 0;
+
+ if (leconf->rtr_id.s_addr != INADDR_ANY)
+ rtr_id_ok = 1;
+ else
+ rtr_id_ok = 0;
+
+ if (tnbr->state == TNBR_STA_DOWN) {
+ if (!socket_ok || !rtr_id_ok)
+ return;
+
+ tnbr->state = TNBR_STA_ACTIVE;
+ send_hello(HELLO_TARGETED, NULL, tnbr);
+
+ evtimer_set(&tnbr->hello_timer, tnbr_hello_timer, tnbr);
+ tnbr_start_hello_timer(tnbr);
+ } else if (tnbr->state == TNBR_STA_ACTIVE) {
+ if (socket_ok && rtr_id_ok)
+ return;
+
+ tnbr->state = TNBR_STA_DOWN;
+ tnbr_stop_hello_timer(tnbr);
+ }
+}
+
+void
+tnbr_update_all(int af)
+{
+ struct tnbr *tnbr;
+
+ /* update targeted neighbors */
+ LIST_FOREACH(tnbr, &leconf->tnbr_list, entry)
+ if (tnbr->af == af || af == AF_UNSPEC)
+ tnbr_update(tnbr);
+}
+
+/* target neighbors timers */
+
+/* ARGSUSED */
+static void
+tnbr_hello_timer(int fd, short event, void *arg)
+{
+ struct tnbr *tnbr = arg;
+
+ send_hello(HELLO_TARGETED, NULL, tnbr);
+ tnbr_start_hello_timer(tnbr);
+}
+
+static void
+tnbr_start_hello_timer(struct tnbr *tnbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = tnbr->hello_interval;
+ if (evtimer_add(&tnbr->hello_timer, &tv) == -1)
+ fatal(__func__);
+}
+
+static void
+tnbr_stop_hello_timer(struct tnbr *tnbr)
+{
+ if (evtimer_pending(&tnbr->hello_timer, NULL) &&
+ evtimer_del(&tnbr->hello_timer) == -1)
+ fatal(__func__);
+}
+
+struct ctl_adj *
+adj_to_ctl(struct adj *adj)
+{
+ static struct ctl_adj actl;
+
+ actl.af = adj_get_af(adj);
+ actl.id = adj->lsr_id;
+ actl.type = adj->source.type;
+ switch (adj->source.type) {
+ case HELLO_LINK:
+ memcpy(actl.ifname, adj->source.link.ia->iface->name,
+ sizeof(actl.ifname));
+ break;
+ case HELLO_TARGETED:
+ actl.src_addr = adj->source.target->addr;
+ break;
+ }
+ actl.holdtime = adj->holdtime;
+ actl.trans_addr = adj->trans_addr;
+
+ return (&actl);
+}
diff --git a/ldpd/control.c b/ldpd/control.c
new file mode 100644
index 000000000..42322eb08
--- /dev/null
+++ b/ldpd/control.c
@@ -0,0 +1,308 @@
+/* $OpenBSD$ */
+
+/*
+ * 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/stat.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "control.h"
+
+#define CONTROL_BACKLOG 5
+
+static void control_accept(int, short, void *);
+static struct ctl_conn *control_connbyfd(int);
+static struct ctl_conn *control_connbypid(pid_t);
+static void control_close(int);
+static void control_dispatch_imsg(int, short, void *);
+
+struct ctl_conns ctl_conns;
+
+static int control_fd;
+
+int
+control_init(void)
+{
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ 0)) == -1) {
+ log_warn("%s: socket", __func__);
+ return (-1);
+ }
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, LDPD_SOCKET, sizeof(sun.sun_path));
+
+ if (unlink(LDPD_SOCKET) == -1)
+ if (errno != ENOENT) {
+ log_warn("%s: unlink %s", __func__, LDPD_SOCKET);
+ close(fd);
+ return (-1);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("%s: bind: %s", __func__, LDPD_SOCKET);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(LDPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("%s: chmod", __func__);
+ close(fd);
+ (void)unlink(LDPD_SOCKET);
+ return (-1);
+ }
+
+ control_fd = fd;
+
+ return (0);
+}
+
+int
+control_listen(void)
+{
+ if (listen(control_fd, CONTROL_BACKLOG) == -1) {
+ log_warn("%s: listen", __func__);
+ return (-1);
+ }
+
+ return (accept_add(control_fd, control_accept, NULL));
+}
+
+void
+control_cleanup(void)
+{
+ accept_del(control_fd);
+ close(control_fd);
+ unlink(LDPD_SOCKET);
+}
+
+/* ARGSUSED */
+static void
+control_accept(int listenfd, short event, void *bula)
+{
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct ctl_conn *c;
+
+ len = sizeof(sun);
+ if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len,
+ SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) {
+ /*
+ * Pause accept if we are out of file descriptors, or
+ * libevent will haunt us here too.
+ */
+ if (errno == ENFILE || errno == EMFILE)
+ accept_pause();
+ else if (errno != EWOULDBLOCK && errno != EINTR &&
+ errno != ECONNABORTED)
+ log_warn("%s: accept4", __func__);
+ return;
+ }
+
+ if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
+ log_warn(__func__);
+ close(connfd);
+ return;
+ }
+
+ imsg_init(&c->iev.ibuf, connfd);
+ c->iev.handler = control_dispatch_imsg;
+ c->iev.events = EV_READ;
+ event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
+ c->iev.handler, &c->iev);
+ event_add(&c->iev.ev, NULL);
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+static struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+static struct ctl_conn *
+control_connbypid(pid_t pid)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.pid != pid;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+static void
+control_close(int fd)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warnx("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ msgbuf_clear(&c->iev.ibuf.w);
+ TAILQ_REMOVE(&ctl_conns, c, entry);
+
+ event_del(&c->iev.ev);
+ close(c->iev.ibuf.fd);
+ accept_unpause();
+ free(c);
+}
+
+/* ARGSUSED */
+static void
+control_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct ctl_conn *c;
+ struct imsg imsg;
+ ssize_t n;
+ unsigned int ifidx;
+ int verbose;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warnx("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ if (event & EV_READ) {
+ if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
+ n == 0) {
+ control_close(fd);
+ return;
+ }
+ }
+ if (event & EV_WRITE) {
+ if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
+ control_close(fd);
+ return;
+ }
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
+ control_close(fd);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_FIB_COUPLE:
+ case IMSG_CTL_FIB_DECOUPLE:
+ case IMSG_CTL_RELOAD:
+ c->iev.ibuf.pid = imsg.hdr.pid;
+ ldpe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
+ break;
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_KROUTE_ADDR:
+ case IMSG_CTL_IFINFO:
+ c->iev.ibuf.pid = imsg.hdr.pid;
+ ldpe_imsg_compose_parent(imsg.hdr.type,
+ imsg.hdr.pid, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_SHOW_INTERFACE:
+ if (imsg.hdr.len == IMSG_HEADER_SIZE +
+ sizeof(ifidx)) {
+ memcpy(&ifidx, imsg.data, sizeof(ifidx));
+ ldpe_iface_ctl(c, ifidx);
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0,
+ 0, -1, NULL, 0);
+ }
+ break;
+ case IMSG_CTL_SHOW_DISCOVERY:
+ ldpe_adj_ctl(c);
+ break;
+ case IMSG_CTL_SHOW_LIB:
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ c->iev.ibuf.pid = imsg.hdr.pid;
+ ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_SHOW_NBR:
+ ldpe_nbr_ctl(c);
+ break;
+ case IMSG_CTL_CLEAR_NBR:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ctl_nbr))
+ break;
+
+ nbr_clear_ctl(imsg.data);
+ break;
+ case IMSG_CTL_LOG_VERBOSE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(verbose))
+ break;
+
+ /* forward to other processes */
+ ldpe_imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+ ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ 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);
+ }
+
+ imsg_event_add(&c->iev);
+}
+
+int
+control_imsg_relay(struct imsg *imsg)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
+ return (0);
+
+ return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid,
+ -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
+}
diff --git a/ldpd/control.h b/ldpd/control.h
new file mode 100644
index 000000000..fd6e47071
--- /dev/null
+++ b/ldpd/control.h
@@ -0,0 +1,38 @@
+/* $OpenBSD$ */
+
+/*
+ * 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.
+ */
+
+#ifndef _CONTROL_H_
+#define _CONTROL_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+struct ctl_conn {
+ TAILQ_ENTRY(ctl_conn) entry;
+ struct imsgev iev;
+};
+TAILQ_HEAD(ctl_conns, ctl_conn);
+
+extern struct ctl_conns ctl_conns;
+
+int control_init(void);
+int control_listen(void);
+void control_cleanup(void);
+int control_imsg_relay(struct imsg *);
+
+#endif /* _CONTROL_H_ */
diff --git a/ldpd/hello.c b/ldpd/hello.c
new file mode 100644
index 000000000..d0daed347
--- /dev/null
+++ b/ldpd/hello.c
@@ -0,0 +1,574 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@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 <arpa/inet.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+static int gen_hello_prms_tlv(struct ibuf *buf, uint16_t, uint16_t);
+static int gen_opt4_hello_prms_tlv(struct ibuf *, uint16_t, uint32_t);
+static int gen_opt16_hello_prms_tlv(struct ibuf *, uint16_t, uint8_t *);
+static int gen_ds_hello_prms_tlv(struct ibuf *, uint32_t);
+static int tlv_decode_hello_prms(char *, uint16_t, uint16_t *, uint16_t *);
+static int tlv_decode_opt_hello_prms(char *, uint16_t, int *, int,
+ union ldpd_addr *, uint32_t *, uint16_t *);
+
+int
+send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr)
+{
+ int af;
+ union ldpd_addr dst;
+ uint16_t size, holdtime = 0, flags = 0;
+ int fd = 0;
+ struct ibuf *buf;
+ int err = 0;
+
+ switch (type) {
+ case HELLO_LINK:
+ af = ia->af;
+ holdtime = ia->hello_holdtime;
+ flags = 0;
+ fd = (ldp_af_global_get(&global, af))->ldp_disc_socket;
+
+ /* multicast destination address */
+ switch (af) {
+ case AF_INET:
+ if (!(leconf->ipv4.flags & F_LDPD_AF_NO_GTSM))
+ flags |= F_HELLO_GTSM;
+ dst.v4 = global.mcast_addr_v4;
+ break;
+ case AF_INET6:
+ dst.v6 = global.mcast_addr_v6;
+ break;
+ default:
+ fatalx("send_hello: unknown af");
+ }
+ break;
+ case HELLO_TARGETED:
+ af = tnbr->af;
+ holdtime = tnbr->hello_holdtime;
+ flags = F_HELLO_TARGETED;
+ if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count)
+ flags |= F_HELLO_REQ_TARG;
+ fd = (ldp_af_global_get(&global, af))->ldp_edisc_socket;
+
+ /* unicast destination address */
+ dst = tnbr->addr;
+ break;
+ default:
+ fatalx("send_hello: unknown hello type");
+ }
+
+ /* calculate message size */
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + sizeof(struct hello_prms_tlv);
+ switch (af) {
+ case AF_INET:
+ size += sizeof(struct hello_prms_opt4_tlv);
+ break;
+ case AF_INET6:
+ size += sizeof(struct hello_prms_opt16_tlv);
+ break;
+ default:
+ fatalx("send_hello: unknown af");
+ }
+ size += sizeof(struct hello_prms_opt4_tlv);
+ if (ldp_is_dual_stack(leconf))
+ size += sizeof(struct hello_prms_opt4_tlv);
+
+ /* generate message */
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ err |= gen_ldp_hdr(buf, size);
+ size -= LDP_HDR_SIZE;
+ err |= gen_msg_hdr(buf, MSG_TYPE_HELLO, size);
+ err |= gen_hello_prms_tlv(buf, holdtime, flags);
+
+ /*
+ * RFC 7552 - Section 6.1:
+ * "An LSR MUST include only the transport address whose address
+ * family is the same as that of the IP packet carrying the Hello
+ * message".
+ */
+ switch (af) {
+ case AF_INET:
+ err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_IPV4TRANSADDR,
+ leconf->ipv4.trans_addr.v4.s_addr);
+ break;
+ case AF_INET6:
+ err |= gen_opt16_hello_prms_tlv(buf, TLV_TYPE_IPV6TRANSADDR,
+ leconf->ipv6.trans_addr.v6.s6_addr);
+ break;
+ default:
+ fatalx("send_hello: unknown af");
+ }
+
+ err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_CONFIG,
+ htonl(global.conf_seqnum));
+
+ /*
+ * RFC 7552 - Section 6.1.1:
+ * "A Dual-stack LSR (i.e., an LSR supporting Dual-stack LDP for a peer)
+ * MUST include the Dual-Stack capability TLV in all of its LDP Hellos".
+ */
+ if (ldp_is_dual_stack(leconf))
+ err |= gen_ds_hello_prms_tlv(buf, leconf->trans_pref);
+
+ if (err) {
+ ibuf_free(buf);
+ return (-1);
+ }
+
+ send_packet(fd, af, &dst, ia, buf->buf, buf->wpos);
+ ibuf_free(buf);
+
+ return (0);
+}
+
+void
+recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af,
+ union ldpd_addr *src, struct iface *iface, int multicast, char *buf,
+ uint16_t len)
+{
+ struct adj *adj = NULL;
+ struct nbr *nbr, *nbrt;
+ uint16_t holdtime, flags;
+ int tlvs_rcvd;
+ int ds_tlv;
+ union ldpd_addr trans_addr;
+ uint32_t scope_id = 0;
+ uint32_t conf_seqnum;
+ uint16_t trans_pref;
+ int r;
+ struct hello_source source;
+ struct iface_af *ia = NULL;
+ struct tnbr *tnbr = NULL;
+
+ r = tlv_decode_hello_prms(buf, len, &holdtime, &flags);
+ if (r == -1) {
+ log_debug("%s: lsr-id %s: failed to decode params", __func__,
+ inet_ntoa(lsr_id));
+ return;
+ }
+ /* safety checks */
+ if (holdtime != 0 && holdtime < MIN_HOLDTIME) {
+ log_debug("%s: lsr-id %s: invalid hello holdtime (%u)",
+ __func__, inet_ntoa(lsr_id), holdtime);
+ return;
+ }
+ if (multicast && (flags & F_HELLO_TARGETED)) {
+ log_debug("%s: lsr-id %s: multicast targeted hello", __func__,
+ inet_ntoa(lsr_id));
+ return;
+ }
+ if (!multicast && !((flags & F_HELLO_TARGETED))) {
+ log_debug("%s: lsr-id %s: unicast link hello", __func__,
+ inet_ntoa(lsr_id));
+ return;
+ }
+ buf += r;
+ len -= r;
+
+ r = tlv_decode_opt_hello_prms(buf, len, &tlvs_rcvd, af, &trans_addr,
+ &conf_seqnum, &trans_pref);
+ if (r == -1) {
+ log_debug("%s: lsr-id %s: failed to decode optional params",
+ __func__, inet_ntoa(lsr_id));
+ return;
+ }
+ if (r != len) {
+ log_debug("%s: lsr-id %s: unexpected data in message",
+ __func__, inet_ntoa(lsr_id));
+ return;
+ }
+
+ /* implicit transport address */
+ if (!(tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR))
+ trans_addr = *src;
+ if (bad_addr(af, &trans_addr)) {
+ log_debug("%s: lsr-id %s: invalid transport address %s",
+ __func__, inet_ntoa(lsr_id), log_addr(af, &trans_addr));
+ return;
+ }
+ if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&trans_addr.v6)) {
+ /*
+ * RFC 7552 - Section 6.1:
+ * "An LSR MUST use a global unicast IPv6 address in an IPv6
+ * Transport Address optional object of outgoing targeted
+ * Hellos and check for the same in incoming targeted Hellos
+ * (i.e., MUST discard the targeted Hello if it failed the
+ * check)".
+ */
+ if (flags & F_HELLO_TARGETED) {
+ log_debug("%s: lsr-id %s: invalid targeted hello "
+ "transport address %s", __func__, inet_ntoa(lsr_id),
+ log_addr(af, &trans_addr));
+ return;
+ }
+ scope_id = iface->ifindex;
+ }
+
+ memset(&source, 0, sizeof(source));
+ if (flags & F_HELLO_TARGETED) {
+ /*
+ * RFC 7552 - Section 5.2:
+ * "The link-local IPv6 addresses MUST NOT be used as the
+ * targeted LDP Hello packet's source or destination addresses".
+ */
+ if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&src->v6)) {
+ log_debug("%s: lsr-id %s: targeted hello with "
+ "link-local source address", __func__,
+ inet_ntoa(lsr_id));
+ return;
+ }
+
+ tnbr = tnbr_find(leconf, af, src);
+
+ /* remove the dynamic tnbr if the 'R' bit was cleared */
+ if (tnbr && (tnbr->flags & F_TNBR_DYNAMIC) &&
+ !((flags & F_HELLO_REQ_TARG))) {
+ tnbr->flags &= ~F_TNBR_DYNAMIC;
+ tnbr = tnbr_check(tnbr);
+ }
+
+ if (!tnbr) {
+ if (!((flags & F_HELLO_REQ_TARG) &&
+ ((ldp_af_conf_get(leconf, af))->flags &
+ F_LDPD_AF_THELLO_ACCEPT)))
+ return;
+
+ tnbr = tnbr_new(leconf, af, src);
+ tnbr->flags |= F_TNBR_DYNAMIC;
+ tnbr_update(tnbr);
+ LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry);
+ }
+
+ source.type = HELLO_TARGETED;
+ source.target = tnbr;
+ } else {
+ ia = iface_af_get(iface, af);
+ source.type = HELLO_LINK;
+ source.link.ia = ia;
+ source.link.src_addr = *src;
+ }
+
+ adj = adj_find(&source);
+ nbr = nbr_find_ldpid(lsr_id.s_addr);
+
+ /* check dual-stack tlv */
+ ds_tlv = (tlvs_rcvd & F_HELLO_TLV_RCVD_DS) ? 1 : 0;
+ if (ds_tlv && trans_pref != leconf->trans_pref) {
+ /*
+ * RFC 7552 - Section 6.1.1:
+ * "If the Dual-Stack capability TLV is present and the remote
+ * preference does not match the local preference (or does not
+ * get recognized), then the LSR MUST discard the Hello message
+ * and log an error.
+ * If an LDP session was already in place, then the LSR MUST
+ * send a fatal Notification message with status code of
+ * 'Transport Connection Mismatch' and reset the session".
+ */
+ log_debug("%s: lsr-id %s: remote transport preference does not "
+ "match the local preference", __func__, inet_ntoa(lsr_id));
+ if (nbr)
+ session_shutdown(nbr, S_TRANS_MISMTCH, msg->id,
+ msg->type);
+ if (adj)
+ adj_del(adj, S_SHUTDOWN);
+ return;
+ }
+
+ /*
+ * Check for noncompliant dual-stack neighbor according to
+ * RFC 7552 section 6.1.1.
+ */
+ if (nbr && !ds_tlv) {
+ switch (af) {
+ case AF_INET:
+ if (nbr_adj_count(nbr, AF_INET6) > 0) {
+ session_shutdown(nbr, S_DS_NONCMPLNCE,
+ msg->id, msg->type);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (nbr_adj_count(nbr, AF_INET) > 0) {
+ session_shutdown(nbr, S_DS_NONCMPLNCE,
+ msg->id, msg->type);
+ return;
+ }
+ break;
+ default:
+ fatalx("recv_hello: unknown af");
+ }
+ }
+
+ /*
+ * Protections against misconfigured networks and buggy implementations.
+ */
+ if (nbr && nbr->af == af &&
+ (ldp_addrcmp(af, &nbr->raddr, &trans_addr) ||
+ nbr->raddr_scope != scope_id)) {
+ log_warnx("%s: lsr-id %s: hello packet advertising a different "
+ "transport address", __func__, inet_ntoa(lsr_id));
+ if (adj)
+ adj_del(adj, S_SHUTDOWN);
+ return;
+ }
+ if (nbr == NULL) {
+ nbrt = nbr_find_addr(af, &trans_addr);
+ if (nbrt) {
+ log_debug("%s: transport address %s is already being "
+ "used by lsr-id %s", __func__, log_addr(af,
+ &trans_addr), inet_ntoa(nbrt->id));
+ if (adj)
+ adj_del(adj, S_SHUTDOWN);
+ return;
+ }
+ }
+
+ if (adj == NULL) {
+ adj = adj_new(lsr_id, &source, &trans_addr);
+ if (nbr) {
+ adj->nbr = nbr;
+ LIST_INSERT_HEAD(&nbr->adj_list, adj, nbr_entry);
+ }
+ }
+
+ /*
+ * If the hello adjacency's address-family doesn't match the local
+ * preference, then an adjacency is still created but we don't attempt
+ * to start an LDP session.
+ */
+ if (nbr == NULL && (!ds_tlv ||
+ ((trans_pref == DUAL_STACK_LDPOV4 && af == AF_INET) ||
+ (trans_pref == DUAL_STACK_LDPOV6 && af == AF_INET6))))
+ nbr = nbr_new(lsr_id, af, ds_tlv, &trans_addr, scope_id);
+
+ /* dynamic LDPv4 GTSM negotiation as per RFC 6720 */
+ if (nbr) {
+ if (flags & F_HELLO_GTSM)
+ nbr->flags |= F_NBR_GTSM_NEGOTIATED;
+ else
+ nbr->flags &= ~F_NBR_GTSM_NEGOTIATED;
+ }
+
+ /* update neighbor's configuration sequence number */
+ if (nbr && (tlvs_rcvd & F_HELLO_TLV_RCVD_CONF)) {
+ if (conf_seqnum > nbr->conf_seqnum &&
+ nbr_pending_idtimer(nbr))
+ nbr_stop_idtimer(nbr);
+ nbr->conf_seqnum = conf_seqnum;
+ }
+
+ /* always update the holdtime to properly handle runtime changes */
+ switch (source.type) {
+ case HELLO_LINK:
+ if (holdtime == 0)
+ holdtime = LINK_DFLT_HOLDTIME;
+
+ adj->holdtime = min(ia->hello_holdtime, holdtime);
+ break;
+ case HELLO_TARGETED:
+ if (holdtime == 0)
+ holdtime = TARGETED_DFLT_HOLDTIME;
+
+ adj->holdtime = min(tnbr->hello_holdtime, holdtime);
+ }
+ if (adj->holdtime != INFINITE_HOLDTIME)
+ adj_start_itimer(adj);
+ else
+ adj_stop_itimer(adj);
+
+ if (nbr && nbr->state == NBR_STA_PRESENT && !nbr_pending_idtimer(nbr) &&
+ nbr_session_active_role(nbr) && !nbr_pending_connect(nbr))
+ nbr_establish_connection(nbr);
+}
+
+static int
+gen_hello_prms_tlv(struct ibuf *buf, uint16_t holdtime, uint16_t flags)
+{
+ struct hello_prms_tlv parms;
+
+ memset(&parms, 0, sizeof(parms));
+ parms.type = htons(TLV_TYPE_COMMONHELLO);
+ parms.length = htons(sizeof(parms.holdtime) + sizeof(parms.flags));
+ parms.holdtime = htons(holdtime);
+ parms.flags = htons(flags);
+
+ return (ibuf_add(buf, &parms, sizeof(parms)));
+}
+
+static int
+gen_opt4_hello_prms_tlv(struct ibuf *buf, uint16_t type, uint32_t value)
+{
+ struct hello_prms_opt4_tlv parms;
+
+ memset(&parms, 0, sizeof(parms));
+ parms.type = htons(type);
+ parms.length = htons(sizeof(parms.value));
+ parms.value = value;
+
+ return (ibuf_add(buf, &parms, sizeof(parms)));
+}
+
+static int
+gen_opt16_hello_prms_tlv(struct ibuf *buf, uint16_t type, uint8_t *value)
+{
+ struct hello_prms_opt16_tlv parms;
+
+ memset(&parms, 0, sizeof(parms));
+ parms.type = htons(type);
+ parms.length = htons(sizeof(parms.value));
+ memcpy(&parms.value, value, sizeof(parms.value));
+
+ return (ibuf_add(buf, &parms, sizeof(parms)));
+}
+
+static int
+gen_ds_hello_prms_tlv(struct ibuf *buf, uint32_t value)
+{
+ if (leconf->flags & F_LDPD_DS_CISCO_INTEROP)
+ value = htonl(value);
+ else
+ value = htonl(value << 28);
+
+ return (gen_opt4_hello_prms_tlv(buf, TLV_TYPE_DUALSTACK, value));
+}
+
+static int
+tlv_decode_hello_prms(char *buf, uint16_t len, uint16_t *holdtime,
+ uint16_t *flags)
+{
+ struct hello_prms_tlv tlv;
+
+ if (len < sizeof(tlv))
+ return (-1);
+ memcpy(&tlv, buf, sizeof(tlv));
+
+ if (tlv.type != htons(TLV_TYPE_COMMONHELLO))
+ return (-1);
+ if (ntohs(tlv.length) != sizeof(tlv) - TLV_HDR_SIZE)
+ return (-1);
+
+ *holdtime = ntohs(tlv.holdtime);
+ *flags = ntohs(tlv.flags);
+
+ return (sizeof(tlv));
+}
+
+static int
+tlv_decode_opt_hello_prms(char *buf, uint16_t len, int *tlvs_rcvd, int af,
+ union ldpd_addr *addr, uint32_t *conf_number, uint16_t *trans_pref)
+{
+ struct tlv tlv;
+ uint16_t tlv_len;
+ int total = 0;
+
+ *tlvs_rcvd = 0;
+ memset(addr, 0, sizeof(*addr));
+ *conf_number = 0;
+ *trans_pref = 0;
+
+ /*
+ * RFC 7552 - Section 6.1:
+ * "An LSR SHOULD accept the Hello message that contains both IPv4 and
+ * IPv6 Transport Address optional objects but MUST use only the
+ * transport address whose address family is the same as that of the
+ * IP packet carrying the Hello message. An LSR SHOULD accept only
+ * the first Transport Address optional object for a given address
+ * family in the received Hello message and ignore the rest if the
+ * LSR receives more than one Transport Address optional object for a
+ * given address family".
+ */
+ while (len >= sizeof(tlv)) {
+ memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_len = ntohs(tlv.length);
+ if (tlv_len + TLV_HDR_SIZE > len)
+ return (-1);
+ buf += TLV_HDR_SIZE;
+ len -= TLV_HDR_SIZE;
+ total += TLV_HDR_SIZE;
+
+ switch (ntohs(tlv.type)) {
+ case TLV_TYPE_IPV4TRANSADDR:
+ if (tlv_len != sizeof(addr->v4))
+ return (-1);
+ if (af != AF_INET)
+ return (-1);
+ if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR)
+ break;
+ memcpy(&addr->v4, buf, sizeof(addr->v4));
+ *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR;
+ break;
+ case TLV_TYPE_IPV6TRANSADDR:
+ if (tlv_len != sizeof(addr->v6))
+ return (-1);
+ if (af != AF_INET6)
+ return (-1);
+ if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR)
+ break;
+ memcpy(&addr->v6, buf, sizeof(addr->v6));
+ *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR;
+ break;
+ case TLV_TYPE_CONFIG:
+ if (tlv_len != sizeof(uint32_t))
+ return (-1);
+ memcpy(conf_number, buf, sizeof(uint32_t));
+ *tlvs_rcvd |= F_HELLO_TLV_RCVD_CONF;
+ break;
+ case TLV_TYPE_DUALSTACK:
+ if (tlv_len != sizeof(uint32_t))
+ return (-1);
+ /*
+ * RFC 7552 - Section 6.1:
+ * "A Single-stack LSR does not need to use the
+ * Dual-Stack capability in Hello messages and SHOULD
+ * ignore this capability if received".
+ */
+ if (!ldp_is_dual_stack(leconf))
+ break;
+ /* Shame on you, Cisco! */
+ if (leconf->flags & F_LDPD_DS_CISCO_INTEROP) {
+ memcpy(trans_pref, buf + sizeof(uint16_t),
+ sizeof(uint16_t));
+ *trans_pref = ntohs(*trans_pref);
+ } else {
+ memcpy(trans_pref, buf , sizeof(uint16_t));
+ *trans_pref = ntohs(*trans_pref) >> 12;
+ }
+ *tlvs_rcvd |= F_HELLO_TLV_RCVD_DS;
+ break;
+ default:
+ /* if unknown flag set, ignore TLV */
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ return (-1);
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ total += tlv_len;
+ }
+
+ return (total);
+}
diff --git a/ldpd/init.c b/ldpd/init.c
new file mode 100644
index 000000000..eb22bf52a
--- /dev/null
+++ b/ldpd/init.c
@@ -0,0 +1,166 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@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 <arpa/inet.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+static int gen_init_prms_tlv(struct ibuf *, struct nbr *);
+
+void
+send_init(struct nbr *nbr)
+{
+ struct ibuf *buf;
+ uint16_t size;
+ int err = 0;
+
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE;
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ err |= gen_ldp_hdr(buf, size);
+ size -= LDP_HDR_SIZE;
+ err |= gen_msg_hdr(buf, MSG_TYPE_INIT, size);
+ size -= LDP_MSG_SIZE;
+ err |= gen_init_prms_tlv(buf, nbr);
+ if (err) {
+ ibuf_free(buf);
+ return;
+ }
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+}
+
+int
+recv_init(struct nbr *nbr, char *buf, uint16_t len)
+{
+ struct ldp_msg msg;
+ struct sess_prms_tlv sess;
+ uint16_t max_pdu_len;
+
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ memcpy(&msg, buf, sizeof(msg));
+ buf += LDP_MSG_SIZE;
+ len -= LDP_MSG_SIZE;
+
+ if (len < SESS_PRMS_SIZE) {
+ session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ memcpy(&sess, buf, sizeof(sess));
+ if (ntohs(sess.length) != SESS_PRMS_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ if (ntohs(sess.proto_version) != LDP_VERSION) {
+ session_shutdown(nbr, S_BAD_PROTO_VER, msg.id, msg.type);
+ return (-1);
+ }
+ if (ntohs(sess.keepalive_time) < MIN_KEEPALIVE) {
+ session_shutdown(nbr, S_KEEPALIVE_BAD, msg.id, msg.type);
+ return (-1);
+ }
+ if (sess.lsr_id != leconf->rtr_id.s_addr ||
+ ntohs(sess.lspace_id) != 0) {
+ session_shutdown(nbr, S_NO_HELLO, msg.id, msg.type);
+ return (-1);
+ }
+
+ buf += SESS_PRMS_SIZE;
+ len -= SESS_PRMS_SIZE;
+
+ /* Optional Parameters */
+ while (len > 0) {
+ struct tlv tlv;
+ uint16_t tlv_len;
+
+ if (len < sizeof(tlv)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_len = ntohs(tlv.length);
+ if (tlv_len + TLV_HDR_SIZE > len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ buf += TLV_HDR_SIZE;
+ len -= TLV_HDR_SIZE;
+
+ switch (ntohs(tlv.type)) {
+ case TLV_TYPE_ATMSESSIONPAR:
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ case TLV_TYPE_FRSESSION:
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ default:
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ send_notification_nbr(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type);
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ }
+
+ nbr->keepalive = min(nbr_get_keepalive(nbr->af, nbr->id),
+ ntohs(sess.keepalive_time));
+
+ max_pdu_len = ntohs(sess.max_pdu_len);
+ /*
+ * RFC 5036 - Section 3.5.3:
+ * "A value of 255 or less specifies the default maximum length of
+ * 4096 octets".
+ */
+ if (max_pdu_len <= 255)
+ max_pdu_len = LDP_MAX_LEN;
+ nbr->max_pdu_len = min(max_pdu_len, LDP_MAX_LEN);
+
+ nbr_fsm(nbr, NBR_EVT_INIT_RCVD);
+
+ return (0);
+}
+
+static int
+gen_init_prms_tlv(struct ibuf *buf, struct nbr *nbr)
+{
+ struct sess_prms_tlv parms;
+
+ memset(&parms, 0, sizeof(parms));
+ parms.type = htons(TLV_TYPE_COMMONSESSION);
+ parms.length = htons(SESS_PRMS_LEN);
+ parms.proto_version = htons(LDP_VERSION);
+ parms.keepalive_time = htons(nbr_get_keepalive(nbr->af, nbr->id));
+ parms.reserved = 0;
+ parms.pvlim = 0;
+ parms.max_pdu_len = 0;
+ parms.lsr_id = nbr->id.s_addr;
+ parms.lspace_id = 0;
+
+ return (ibuf_add(buf, &parms, SESS_PRMS_SIZE));
+}
diff --git a/ldpd/interface.c b/ldpd/interface.c
new file mode 100644
index 000000000..ff4c8f169
--- /dev/null
+++ b/ldpd/interface.c
@@ -0,0 +1,531 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@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/time.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+static struct if_addr *if_addr_new(struct kaddr *);
+static struct if_addr *if_addr_lookup(struct if_addr_head *, struct kaddr *);
+static int if_start(struct iface *, int);
+static int if_reset(struct iface *, int);
+static void if_update_af(struct iface_af *, int);
+static void if_hello_timer(int, short, void *);
+static void if_start_hello_timer(struct iface_af *);
+static void if_stop_hello_timer(struct iface_af *);
+static int if_join_ipv4_group(struct iface *, struct in_addr *);
+static int if_leave_ipv4_group(struct iface *, struct in_addr *);
+static int if_join_ipv6_group(struct iface *, struct in6_addr *);
+static int if_leave_ipv6_group(struct iface *, struct in6_addr *);
+
+struct iface *
+if_new(struct kif *kif)
+{
+ struct iface *iface;
+
+ if ((iface = calloc(1, sizeof(*iface))) == NULL)
+ fatal("if_new: calloc");
+
+ strlcpy(iface->name, kif->ifname, sizeof(iface->name));
+
+ /* get type */
+ if (kif->flags & IFF_POINTOPOINT)
+ iface->type = IF_TYPE_POINTOPOINT;
+ if (kif->flags & IFF_BROADCAST &&
+ kif->flags & IFF_MULTICAST)
+ iface->type = IF_TYPE_BROADCAST;
+
+ /* get index and flags */
+ LIST_INIT(&iface->addr_list);
+ iface->ifindex = kif->ifindex;
+ iface->flags = kif->flags;
+ iface->linkstate = kif->link_state;
+ iface->if_type = kif->if_type;
+
+ /* ipv4 */
+ iface->ipv4.af = AF_INET;
+ iface->ipv4.iface = iface;
+ iface->ipv4.enabled = 0;
+ iface->ipv4.state = IF_STA_DOWN;
+ LIST_INIT(&iface->ipv4.adj_list);
+
+ /* ipv6 */
+ iface->ipv6.af = AF_INET6;
+ iface->ipv6.iface = iface;
+ iface->ipv6.enabled = 0;
+ iface->ipv6.state = IF_STA_DOWN;
+ LIST_INIT(&iface->ipv6.adj_list);
+
+ return (iface);
+}
+
+struct iface *
+if_lookup(struct ldpd_conf *xconf, unsigned short ifindex)
+{
+ struct iface *iface;
+
+ LIST_FOREACH(iface, &xconf->iface_list, entry)
+ if (iface->ifindex == ifindex)
+ return (iface);
+
+ return (NULL);
+}
+
+void
+if_exit(struct iface *iface)
+{
+ struct if_addr *if_addr;
+
+ log_debug("%s: interface %s", __func__, iface->name);
+
+ if (iface->ipv4.state == IF_STA_ACTIVE)
+ if_reset(iface, AF_INET);
+ if (iface->ipv6.state == IF_STA_ACTIVE)
+ if_reset(iface, AF_INET6);
+
+ while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) {
+ LIST_REMOVE(if_addr, entry);
+ free(if_addr);
+ }
+}
+
+struct iface_af *
+iface_af_get(struct iface *iface, int af)
+{
+ switch (af) {
+ case AF_INET:
+ return (&iface->ipv4);
+ case AF_INET6:
+ return (&iface->ipv6);
+ default:
+ fatalx("iface_af_get: unknown af");
+ }
+}
+
+static struct if_addr *
+if_addr_new(struct kaddr *ka)
+{
+ struct if_addr *if_addr;
+
+ if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL)
+ fatal(__func__);
+
+ if_addr->af = ka->af;
+ if_addr->addr = ka->addr;
+ if_addr->prefixlen = ka->prefixlen;
+ if_addr->dstbrd = ka->dstbrd;
+
+ return (if_addr);
+}
+
+static struct if_addr *
+if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *ka)
+{
+ struct if_addr *if_addr;
+ int af = ka->af;
+
+ LIST_FOREACH(if_addr, addr_list, entry)
+ if (!ldp_addrcmp(af, &if_addr->addr, &ka->addr) &&
+ if_addr->prefixlen == ka->prefixlen &&
+ !ldp_addrcmp(af, &if_addr->dstbrd, &ka->dstbrd))
+ return (if_addr);
+
+ return (NULL);
+}
+
+void
+if_addr_add(struct kaddr *ka)
+{
+ struct iface *iface;
+ struct if_addr *if_addr;
+ struct nbr *nbr;
+
+ if (if_addr_lookup(&global.addr_list, ka) == NULL) {
+ if_addr = if_addr_new(ka);
+
+ LIST_INSERT_HEAD(&global.addr_list, if_addr, entry);
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->state != NBR_STA_OPER)
+ continue;
+ if (if_addr->af == AF_INET && !nbr->v4_enabled)
+ continue;
+ if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
+ continue;
+
+ send_address_single(nbr, if_addr, 0);
+ }
+ }
+
+ iface = if_lookup(leconf, ka->ifindex);
+ if (iface) {
+ if (ka->af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&ka->addr.v6))
+ iface->linklocal = ka->addr.v6;
+
+ if (if_addr_lookup(&iface->addr_list, ka) == NULL) {
+ if_addr = if_addr_new(ka);
+ LIST_INSERT_HEAD(&iface->addr_list, if_addr, entry);
+ if_update(iface, if_addr->af);
+ }
+ }
+}
+
+void
+if_addr_del(struct kaddr *ka)
+{
+ struct iface *iface;
+ struct if_addr *if_addr;
+ struct nbr *nbr;
+
+ iface = if_lookup(leconf, ka->ifindex);
+ if (iface) {
+ if (ka->af == AF_INET6 &&
+ IN6_ARE_ADDR_EQUAL(&iface->linklocal, &ka->addr.v6))
+ memset(&iface->linklocal, 0, sizeof(iface->linklocal));
+
+ if_addr = if_addr_lookup(&iface->addr_list, ka);
+ if (if_addr) {
+ LIST_REMOVE(if_addr, entry);
+ if_update(iface, if_addr->af);
+ free(if_addr);
+ }
+ }
+
+ if_addr = if_addr_lookup(&global.addr_list, ka);
+ if (if_addr) {
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->state != NBR_STA_OPER)
+ continue;
+ if (if_addr->af == AF_INET && !nbr->v4_enabled)
+ continue;
+ if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
+ continue;
+ send_address_single(nbr, if_addr, 1);
+ }
+ LIST_REMOVE(if_addr, entry);
+ free(if_addr);
+ }
+}
+
+static int
+if_start(struct iface *iface, int af)
+{
+ struct iface_af *ia;
+ struct timeval now;
+
+ log_debug("%s: %s address-family %s", __func__, iface->name,
+ af_name(af));
+
+ ia = iface_af_get(iface, af);
+
+ gettimeofday(&now, NULL);
+ ia->uptime = now.tv_sec;
+
+ switch (af) {
+ case AF_INET:
+ if (if_join_ipv4_group(iface, &global.mcast_addr_v4))
+ return (-1);
+ break;
+ case AF_INET6:
+ if (if_join_ipv6_group(iface, &global.mcast_addr_v6))
+ return (-1);
+ break;
+ default:
+ fatalx("if_start: unknown af");
+ }
+
+ send_hello(HELLO_LINK, ia, NULL);
+
+ evtimer_set(&ia->hello_timer, if_hello_timer, ia);
+ if_start_hello_timer(ia);
+ return (0);
+}
+
+static int
+if_reset(struct iface *iface, int af)
+{
+ struct iface_af *ia;
+ struct adj *adj;
+
+ log_debug("%s: %s address-family %s", __func__, iface->name,
+ af_name(af));
+
+ ia = iface_af_get(iface, af);
+ if_stop_hello_timer(ia);
+
+ while ((adj = LIST_FIRST(&ia->adj_list)) != NULL)
+ adj_del(adj, S_SHUTDOWN);
+
+ /* try to cleanup */
+ switch (af) {
+ case AF_INET:
+ if (global.ipv4.ldp_disc_socket != -1)
+ if_leave_ipv4_group(iface, &global.mcast_addr_v4);
+ break;
+ case AF_INET6:
+ if (global.ipv6.ldp_disc_socket != -1)
+ if_leave_ipv6_group(iface, &global.mcast_addr_v6);
+ break;
+ default:
+ fatalx("if_start: unknown af");
+ }
+
+ return (0);
+}
+
+static void
+if_update_af(struct iface_af *ia, int link_ok)
+{
+ int addr_ok = 0, socket_ok, rtr_id_ok;
+ struct if_addr *if_addr;
+
+ switch (ia->af) {
+ case AF_INET:
+ /*
+ * NOTE: for LDPv4, each interface should have at least one
+ * valid IP address otherwise they can not be enabled.
+ */
+ LIST_FOREACH(if_addr, &ia->iface->addr_list, entry) {
+ if (if_addr->af == AF_INET) {
+ addr_ok = 1;
+ break;
+ }
+ }
+ break;
+ case AF_INET6:
+ /* for IPv6 the link-local address is enough. */
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->iface->linklocal))
+ addr_ok = 1;
+ break;
+ default:
+ fatalx("if_update_af: unknown af");
+ }
+
+ if ((ldp_af_global_get(&global, ia->af))->ldp_disc_socket != -1)
+ socket_ok = 1;
+ else
+ socket_ok = 0;
+
+ if (leconf->rtr_id.s_addr != INADDR_ANY)
+ rtr_id_ok = 1;
+ else
+ rtr_id_ok = 0;
+
+ if (ia->state == IF_STA_DOWN) {
+ if (!ia->enabled || !link_ok || !addr_ok || !socket_ok ||
+ !rtr_id_ok)
+ return;
+
+ ia->state = IF_STA_ACTIVE;
+ if_start(ia->iface, ia->af);
+ } else if (ia->state == IF_STA_ACTIVE) {
+ if (ia->enabled && link_ok && addr_ok && socket_ok && rtr_id_ok)
+ return;
+
+ ia->state = IF_STA_DOWN;
+ if_reset(ia->iface, ia->af);
+ }
+}
+
+void
+if_update(struct iface *iface, int af)
+{
+ int link_ok;
+
+ link_ok = (iface->flags & IFF_UP) &&
+ LINK_STATE_IS_UP(iface->linkstate);
+
+ if (af == AF_INET || af == AF_UNSPEC)
+ if_update_af(&iface->ipv4, link_ok);
+ if (af == AF_INET6 || af == AF_UNSPEC)
+ if_update_af(&iface->ipv6, link_ok);
+}
+
+void
+if_update_all(int af)
+{
+ struct iface *iface;
+
+ LIST_FOREACH(iface, &leconf->iface_list, entry)
+ if_update(iface, af);
+}
+
+/* timers */
+/* ARGSUSED */
+static void
+if_hello_timer(int fd, short event, void *arg)
+{
+ struct iface_af *ia = arg;
+
+ send_hello(HELLO_LINK, ia, NULL);
+ if_start_hello_timer(ia);
+}
+
+static void
+if_start_hello_timer(struct iface_af *ia)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = ia->hello_interval;
+ if (evtimer_add(&ia->hello_timer, &tv) == -1)
+ fatal(__func__);
+}
+
+static void
+if_stop_hello_timer(struct iface_af *ia)
+{
+ if (evtimer_pending(&ia->hello_timer, NULL) &&
+ evtimer_del(&ia->hello_timer) == -1)
+ fatal(__func__);
+}
+
+struct ctl_iface *
+if_to_ctl(struct iface_af *ia)
+{
+ static struct ctl_iface ictl;
+ struct timeval now;
+ struct adj *adj;
+
+ ictl.af = ia->af;
+ memcpy(ictl.name, ia->iface->name, sizeof(ictl.name));
+ ictl.ifindex = ia->iface->ifindex;
+ ictl.state = ia->state;
+ ictl.flags = ia->iface->flags;
+ ictl.linkstate = ia->iface->linkstate;
+ ictl.type = ia->iface->type;
+ ictl.if_type = ia->iface->if_type;
+ ictl.hello_holdtime = ia->hello_holdtime;
+ ictl.hello_interval = ia->hello_interval;
+
+ gettimeofday(&now, NULL);
+ if (ia->state != IF_STA_DOWN &&
+ ia->uptime != 0) {
+ ictl.uptime = now.tv_sec - ia->uptime;
+ } else
+ ictl.uptime = 0;
+
+ ictl.adj_cnt = 0;
+ LIST_FOREACH(adj, &ia->adj_list, ia_entry)
+ ictl.adj_cnt++;
+
+ return (&ictl);
+}
+
+/* multicast membership sockopts */
+in_addr_t
+if_get_ipv4_addr(struct iface *iface)
+{
+ struct if_addr *if_addr;
+
+ LIST_FOREACH(if_addr, &iface->addr_list, entry)
+ if (if_addr->af == AF_INET)
+ return (if_addr->addr.v4.s_addr);
+
+ return (INADDR_ANY);
+}
+
+static int
+if_join_ipv4_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+
+ log_debug("%s: interface %s addr %s", __func__, iface->name,
+ inet_ntoa(*addr));
+
+ mreq.imr_multiaddr = *addr;
+ mreq.imr_interface.s_addr = if_get_ipv4_addr(iface);
+
+ if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s",
+ __func__, iface->name, inet_ntoa(*addr));
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+
+ log_debug("%s: interface %s addr %s", __func__, iface->name,
+ inet_ntoa(*addr));
+
+ mreq.imr_multiaddr = *addr;
+ mreq.imr_interface.s_addr = if_get_ipv4_addr(iface);
+
+ if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
+ "address %s", __func__, iface->name, inet_ntoa(*addr));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+if_join_ipv6_group(struct iface *iface, struct in6_addr *addr)
+{
+ struct ipv6_mreq mreq;
+
+ log_debug("%s: interface %s addr %s", __func__, iface->name,
+ log_in6addr(addr));
+
+ mreq.ipv6mr_multiaddr = *addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
+ IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) {
+ log_warn("%s: error IPV6_JOIN_GROUP, interface %s address %s",
+ __func__, iface->name, log_in6addr(addr));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr)
+{
+ struct ipv6_mreq mreq;
+
+ log_debug("%s: interface %s addr %s", __func__, iface->name,
+ log_in6addr(addr));
+
+ mreq.ipv6mr_multiaddr = *addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
+ IPV6_LEAVE_GROUP, (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("%s: error IPV6_LEAVE_GROUP, interface %s address %s",
+ __func__, iface->name, log_in6addr(addr));
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/ldpd/keepalive.c b/ldpd/keepalive.c
new file mode 100644
index 000000000..4cd49d485
--- /dev/null
+++ b/ldpd/keepalive.c
@@ -0,0 +1,58 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@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 <string.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+void
+send_keepalive(struct nbr *nbr)
+{
+ struct ibuf *buf;
+ uint16_t size;
+
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE;
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ gen_ldp_hdr(buf, size);
+ size -= LDP_HDR_SIZE;
+ gen_msg_hdr(buf, MSG_TYPE_KEEPALIVE, size);
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+}
+
+int
+recv_keepalive(struct nbr *nbr, char *buf, uint16_t len)
+{
+ struct ldp_msg msg;
+
+ memcpy(&msg, buf, sizeof(msg));
+ if (len != LDP_MSG_SIZE) {
+ session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ if (nbr->state != NBR_STA_OPER)
+ nbr_fsm(nbr, NBR_EVT_KEEPALIVE_RCVD);
+
+ return (0);
+}
diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c
new file mode 100644
index 000000000..22c98745e
--- /dev/null
+++ b/ldpd/l2vpn.c
@@ -0,0 +1,510 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@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 <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+
+static void l2vpn_pw_fec(struct l2vpn_pw *, struct fec *);
+
+struct l2vpn *
+l2vpn_new(const char *name)
+{
+ struct l2vpn *l2vpn;
+
+ if ((l2vpn = calloc(1, sizeof(*l2vpn))) == NULL)
+ fatal("l2vpn_new: calloc");
+
+ strlcpy(l2vpn->name, name, sizeof(l2vpn->name));
+
+ /* set default values */
+ l2vpn->mtu = DEFAULT_L2VPN_MTU;
+ l2vpn->pw_type = DEFAULT_PW_TYPE;
+
+ LIST_INIT(&l2vpn->if_list);
+ LIST_INIT(&l2vpn->pw_list);
+
+ return (l2vpn);
+}
+
+struct l2vpn *
+l2vpn_find(struct ldpd_conf *xconf, const char *name)
+{
+ struct l2vpn *l2vpn;
+
+ LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry)
+ if (strcmp(l2vpn->name, name) == 0)
+ return (l2vpn);
+
+ return (NULL);
+}
+
+void
+l2vpn_del(struct l2vpn *l2vpn)
+{
+ struct l2vpn_if *lif;
+ struct l2vpn_pw *pw;
+
+ while ((lif = LIST_FIRST(&l2vpn->if_list)) != NULL) {
+ LIST_REMOVE(lif, entry);
+ free(lif);
+ }
+ while ((pw = LIST_FIRST(&l2vpn->pw_list)) != NULL) {
+ LIST_REMOVE(pw, entry);
+ free(pw);
+ }
+
+ free(l2vpn);
+}
+
+void
+l2vpn_init(struct l2vpn *l2vpn)
+{
+ struct l2vpn_pw *pw;
+
+ LIST_FOREACH(pw, &l2vpn->pw_list, entry)
+ l2vpn_pw_init(pw);
+}
+
+void
+l2vpn_exit(struct l2vpn *l2vpn)
+{
+ struct l2vpn_pw *pw;
+
+ LIST_FOREACH(pw, &l2vpn->pw_list, entry)
+ l2vpn_pw_exit(pw);
+}
+
+struct l2vpn_if *
+l2vpn_if_new(struct l2vpn *l2vpn, struct kif *kif)
+{
+ struct l2vpn_if *lif;
+
+ if ((lif = calloc(1, sizeof(*lif))) == NULL)
+ fatal("l2vpn_if_new: calloc");
+
+ lif->l2vpn = l2vpn;
+ strlcpy(lif->ifname, kif->ifname, sizeof(lif->ifname));
+ lif->ifindex = kif->ifindex;
+ lif->flags = kif->flags;
+ lif->link_state = kif->link_state;
+
+ return (lif);
+}
+
+struct l2vpn_if *
+l2vpn_if_find(struct l2vpn *l2vpn, unsigned int ifindex)
+{
+ struct l2vpn_if *lif;
+
+ LIST_FOREACH(lif, &l2vpn->if_list, entry)
+ if (lif->ifindex == ifindex)
+ return (lif);
+
+ return (NULL);
+}
+
+struct l2vpn_pw *
+l2vpn_pw_new(struct l2vpn *l2vpn, struct kif *kif)
+{
+ struct l2vpn_pw *pw;
+
+ if ((pw = calloc(1, sizeof(*pw))) == NULL)
+ fatal("l2vpn_pw_new: calloc");
+
+ pw->l2vpn = l2vpn;
+ strlcpy(pw->ifname, kif->ifname, sizeof(pw->ifname));
+ pw->ifindex = kif->ifindex;
+
+ return (pw);
+}
+
+struct l2vpn_pw *
+l2vpn_pw_find(struct l2vpn *l2vpn, unsigned int ifindex)
+{
+ struct l2vpn_pw *pw;
+
+ LIST_FOREACH(pw, &l2vpn->pw_list, entry)
+ if (pw->ifindex == ifindex)
+ return (pw);
+
+ return (NULL);
+}
+
+void
+l2vpn_pw_init(struct l2vpn_pw *pw)
+{
+ struct fec fec;
+
+ l2vpn_pw_reset(pw);
+
+ l2vpn_pw_fec(pw, &fec);
+ lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0,
+ 0, (void *)pw);
+}
+
+void
+l2vpn_pw_exit(struct l2vpn_pw *pw)
+{
+ struct fec fec;
+
+ l2vpn_pw_fec(pw, &fec);
+ lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0);
+}
+
+static void
+l2vpn_pw_fec(struct l2vpn_pw *pw, struct fec *fec)
+{
+ memset(fec, 0, sizeof(*fec));
+ fec->type = FEC_TYPE_PWID;
+ fec->u.pwid.type = pw->l2vpn->pw_type;
+ fec->u.pwid.pwid = pw->pwid;
+ fec->u.pwid.lsr_id = pw->lsr_id;
+}
+
+void
+l2vpn_pw_reset(struct l2vpn_pw *pw)
+{
+ pw->remote_group = 0;
+ pw->remote_mtu = 0;
+ pw->remote_status = 0;
+
+ if (pw->flags & F_PW_CWORD_CONF)
+ pw->flags |= F_PW_CWORD;
+ else
+ pw->flags &= ~F_PW_CWORD;
+
+ if (pw->flags & F_PW_STATUSTLV_CONF)
+ pw->flags |= F_PW_STATUSTLV;
+ else
+ pw->flags &= ~F_PW_STATUSTLV;
+}
+
+int
+l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh)
+{
+ struct fec fec;
+ struct fec_node *fn;
+
+ /* check for a remote label */
+ if (fnh->remote_label == NO_LABEL)
+ return (0);
+
+ /* MTUs must match */
+ if (pw->l2vpn->mtu != pw->remote_mtu)
+ return (0);
+
+ /* check pw status if applicable */
+ if ((pw->flags & F_PW_STATUSTLV) &&
+ pw->remote_status != PW_FORWARDING)
+ return (0);
+
+ /* check for a working lsp to the nexthop */
+ memset(&fec, 0, sizeof(fec));
+ switch (pw->af) {
+ case AF_INET:
+ fec.type = FEC_TYPE_IPV4;
+ fec.u.ipv4.prefix = pw->addr.v4;
+ fec.u.ipv4.prefixlen = 32;
+ break;
+ case AF_INET6:
+ fec.type = FEC_TYPE_IPV6;
+ fec.u.ipv6.prefix = pw->addr.v6;
+ fec.u.ipv6.prefixlen = 128;
+ break;
+ default:
+ fatalx("l2vpn_pw_ok: unknown af");
+ }
+
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn == NULL || fn->local_label == NO_LABEL)
+ return (0);
+ /*
+ * Need to ensure that there's a label binding for all nexthops.
+ * Otherwise, ECMP for this route could render the pseudowire unusable.
+ */
+ LIST_FOREACH(fnh, &fn->nexthops, entry)
+ if (fnh->remote_label == NO_LABEL)
+ return (0);
+
+ return (1);
+}
+
+int
+l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map)
+{
+ struct l2vpn_pw *pw;
+ struct status_tlv st;
+
+ /* NOTE: thanks martini & friends for all this mess */
+
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL)
+ /*
+ * pseudowire not configured, return and record
+ * the mapping later
+ */
+ return (0);
+
+ /* RFC4447 - Section 6.2: control word negotiation */
+ if (fec_find(&ln->sent_map, &fn->fec)) {
+ if ((map->flags & F_MAP_PW_CWORD) &&
+ !(pw->flags & F_PW_CWORD_CONF)) {
+ /* ignore the received label mapping */
+ return (1);
+ } else if (!(map->flags & F_MAP_PW_CWORD) &&
+ (pw->flags & F_PW_CWORD_CONF)) {
+ /* append a "Wrong C-bit" status code */
+ st.status_code = S_WRONG_CBIT;
+ st.msg_id = map->msg_id;
+ st.msg_type = htons(MSG_TYPE_LABELMAPPING);
+ lde_send_labelwithdraw(ln, fn, NO_LABEL, &st);
+
+ pw->flags &= ~F_PW_CWORD;
+ lde_send_labelmapping(ln, fn, 1);
+ }
+ } else if (map->flags & F_MAP_PW_CWORD) {
+ if (pw->flags & F_PW_CWORD_CONF)
+ pw->flags |= F_PW_CWORD;
+ else
+ /* act as if no label mapping had been received */
+ return (1);
+ } else
+ pw->flags &= ~F_PW_CWORD;
+
+ /* RFC4447 - Section 5.4.3: pseudowire status negotiation */
+ if (fec_find(&ln->recv_map, &fn->fec) == NULL &&
+ !(map->flags & F_MAP_PW_STATUS))
+ pw->flags &= ~F_PW_STATUSTLV;
+
+ return (0);
+}
+
+void
+l2vpn_send_pw_status(uint32_t peerid, uint32_t status, struct fec *fec)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_PW_STATUS;
+ nm.pw_status = status;
+ nm.flags |= F_NOTIF_PW_STATUS;
+ lde_fec2map(fec, &nm.fec);
+ nm.flags |= F_NOTIF_FEC;
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0,
+ &nm, sizeof(nm));
+}
+
+void
+l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
+{
+ struct fec fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct l2vpn_pw *pw;
+
+ /* TODO group wildcard */
+ if (!(nm->fec.flags & F_MAP_PW_ID))
+ return;
+
+ lde_map2fec(&nm->fec, ln->id, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn == NULL)
+ /* unknown fec */
+ return;
+
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL)
+ return;
+
+ fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0);
+ if (fnh == NULL)
+ return;
+
+ /* remote status didn't change */
+ if (pw->remote_status == nm->pw_status)
+ return;
+
+ pw->remote_status = nm->pw_status;
+
+ if (l2vpn_pw_ok(pw, fnh))
+ lde_send_change_klabel(fn, fnh);
+ else
+ lde_send_delete_klabel(fn, fnh);
+}
+
+void
+l2vpn_sync_pws(int af, union ldpd_addr *addr)
+{
+ struct l2vpn *l2vpn;
+ struct l2vpn_pw *pw;
+ struct fec fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+
+ LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) {
+ LIST_FOREACH(pw, &l2vpn->pw_list, entry) {
+ if (af != pw->af || ldp_addrcmp(af, &pw->addr, addr))
+ continue;
+
+ l2vpn_pw_fec(pw, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn == NULL)
+ continue;
+ fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)
+ &pw->lsr_id, 0);
+ if (fnh == NULL)
+ continue;
+
+ if (l2vpn_pw_ok(pw, fnh))
+ lde_send_change_klabel(fn, fnh);
+ else
+ lde_send_delete_klabel(fn, fnh);
+ }
+ }
+}
+
+void
+l2vpn_pw_ctl(pid_t pid)
+{
+ struct l2vpn *l2vpn;
+ struct l2vpn_pw *pw;
+ static struct ctl_pw pwctl;
+
+ LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry)
+ LIST_FOREACH(pw, &l2vpn->pw_list, entry) {
+ memset(&pwctl, 0, sizeof(pwctl));
+ strlcpy(pwctl.ifname, pw->ifname,
+ sizeof(pwctl.ifname));
+ pwctl.pwid = pw->pwid;
+ pwctl.lsr_id = pw->lsr_id;
+ pwctl.status = pw->flags & F_PW_STATUS_UP;
+
+ lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0,
+ pid, &pwctl, sizeof(pwctl));
+ }
+}
+
+void
+l2vpn_binding_ctl(pid_t pid)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct lde_map *me;
+ struct l2vpn_pw *pw;
+ static struct ctl_pw pwctl;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ if (f->type != FEC_TYPE_PWID)
+ continue;
+
+ fn = (struct fec_node *)f;
+ if (fn->local_label == NO_LABEL &&
+ LIST_EMPTY(&fn->downstream))
+ continue;
+
+ memset(&pwctl, 0, sizeof(pwctl));
+ pwctl.type = f->u.pwid.type;
+ pwctl.pwid = f->u.pwid.pwid;
+ pwctl.lsr_id = f->u.pwid.lsr_id;
+
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw) {
+ pwctl.local_label = fn->local_label;
+ pwctl.local_gid = 0;
+ pwctl.local_ifmtu = pw->l2vpn->mtu;
+ } else
+ pwctl.local_label = NO_LABEL;
+
+ LIST_FOREACH(me, &fn->downstream, entry)
+ if (f->u.pwid.lsr_id.s_addr == me->nexthop->id.s_addr)
+ break;
+
+ if (me) {
+ pwctl.remote_label = me->map.label;
+ pwctl.remote_gid = me->map.fec.pwid.group_id;
+ if (me->map.flags & F_MAP_PW_IFMTU)
+ pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu;
+
+ lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING,
+ 0, pid, &pwctl, sizeof(pwctl));
+ } else if (pw) {
+ pwctl.remote_label = NO_LABEL;
+
+ lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING,
+ 0, pid, &pwctl, sizeof(pwctl));
+ }
+ }
+}
+
+/* ldpe */
+
+void
+ldpe_l2vpn_init(struct l2vpn *l2vpn)
+{
+ struct l2vpn_pw *pw;
+
+ LIST_FOREACH(pw, &l2vpn->pw_list, entry)
+ ldpe_l2vpn_pw_init(pw);
+}
+
+void
+ldpe_l2vpn_exit(struct l2vpn *l2vpn)
+{
+ struct l2vpn_pw *pw;
+
+ LIST_FOREACH(pw, &l2vpn->pw_list, entry)
+ ldpe_l2vpn_pw_exit(pw);
+}
+
+void
+ldpe_l2vpn_pw_init(struct l2vpn_pw *pw)
+{
+ struct tnbr *tnbr;
+
+ tnbr = tnbr_find(leconf, pw->af, &pw->addr);
+ if (tnbr == NULL) {
+ tnbr = tnbr_new(leconf, pw->af, &pw->addr);
+ tnbr_update(tnbr);
+ LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry);
+ }
+
+ tnbr->pw_count++;
+}
+
+void
+ldpe_l2vpn_pw_exit(struct l2vpn_pw *pw)
+{
+ struct tnbr *tnbr;
+
+ tnbr = tnbr_find(leconf, pw->af, &pw->addr);
+ if (tnbr) {
+ tnbr->pw_count--;
+ tnbr_check(tnbr);
+ }
+}
diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c
new file mode 100644
index 000000000..88e64071b
--- /dev/null
+++ b/ldpd/labelmapping.c
@@ -0,0 +1,764 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2014, 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@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/socket.h>
+#include <arpa/inet.h>
+#include <netmpls/mpls.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+static void enqueue_pdu(struct nbr *, struct ibuf *, uint16_t);
+static int gen_label_tlv(struct ibuf *, uint32_t);
+static int tlv_decode_label(struct nbr *, struct ldp_msg *, char *,
+ uint16_t, uint32_t *);
+static int gen_reqid_tlv(struct ibuf *, uint32_t);
+
+static void
+enqueue_pdu(struct nbr *nbr, struct ibuf *buf, uint16_t size)
+{
+ struct ldp_hdr *ldp_hdr;
+
+ ldp_hdr = ibuf_seek(buf, 0, sizeof(struct ldp_hdr));
+ ldp_hdr->length = htons(size);
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+}
+
+/* Generic function that handles all Label Message types */
+void
+send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
+{
+ struct ibuf *buf = NULL;
+ struct mapping_entry *me;
+ uint16_t msg_size, size = 0;
+ int first = 1;
+ int err = 0;
+
+ /* nothing to send */
+ if (TAILQ_EMPTY(mh))
+ return;
+
+ while ((me = TAILQ_FIRST(mh)) != NULL) {
+ /* generate pdu */
+ if (first) {
+ if ((buf = ibuf_open(nbr->max_pdu_len +
+ LDP_HDR_DEAD_LEN)) == NULL)
+ fatal(__func__);
+
+ /* real size will be set up later */
+ err |= gen_ldp_hdr(buf, 0);
+
+ size = LDP_HDR_PDU_LEN;
+ first = 0;
+ }
+
+ /* calculate size */
+ msg_size = LDP_MSG_SIZE + TLV_HDR_SIZE;
+ switch (me->map.type) {
+ case MAP_TYPE_WILDCARD:
+ msg_size += FEC_ELM_WCARD_LEN;
+ break;
+ case MAP_TYPE_PREFIX:
+ msg_size += FEC_ELM_PREFIX_MIN_LEN +
+ PREFIX_SIZE(me->map.fec.prefix.prefixlen);
+ break;
+ case MAP_TYPE_PWID:
+ msg_size += FEC_PWID_ELM_MIN_LEN;
+ if (me->map.flags & F_MAP_PW_ID)
+ msg_size += PW_STATUS_TLV_LEN;
+ if (me->map.flags & F_MAP_PW_IFMTU)
+ msg_size += FEC_SUBTLV_IFMTU_SIZE;
+ if (me->map.flags & F_MAP_PW_STATUS)
+ msg_size += PW_STATUS_TLV_SIZE;
+ break;
+ }
+ if (me->map.label != NO_LABEL)
+ msg_size += LABEL_TLV_SIZE;
+ if (me->map.flags & F_MAP_REQ_ID)
+ msg_size += REQID_TLV_SIZE;
+ if (me->map.flags & F_MAP_STATUS)
+ msg_size += STATUS_SIZE;
+
+ /* maximum pdu length exceeded, we need a new ldp pdu */
+ if (size + msg_size > nbr->max_pdu_len) {
+ enqueue_pdu(nbr, buf, size);
+ first = 1;
+ continue;
+ }
+
+ size += msg_size;
+
+ /* append message and tlvs */
+ err |= gen_msg_hdr(buf, type, msg_size);
+ err |= gen_fec_tlv(buf, &me->map);
+ if (me->map.label != NO_LABEL)
+ err |= gen_label_tlv(buf, me->map.label);
+ if (me->map.flags & F_MAP_REQ_ID)
+ err |= gen_reqid_tlv(buf, me->map.requestid);
+ if (me->map.flags & F_MAP_PW_STATUS)
+ err |= gen_pw_status_tlv(buf, me->map.pw_status);
+ if (me->map.flags & F_MAP_STATUS)
+ err |= gen_status_tlv(buf, me->map.st.status_code,
+ me->map.st.msg_id, me->map.st.msg_type);
+ if (err) {
+ ibuf_free(buf);
+ mapping_list_clr(mh);
+ return;
+ }
+
+ log_debug("msg-out: %s: lsr-id %s, fec %s, label %s",
+ msg_name(type), inet_ntoa(nbr->id), log_map(&me->map),
+ log_label(me->map.label));
+
+ TAILQ_REMOVE(mh, me, entry);
+ free(me);
+ }
+
+ enqueue_pdu(nbr, buf, size);
+
+ nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+}
+
+/* Generic function that handles all Label Message types */
+int
+recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
+{
+ struct ldp_msg msg;
+ struct tlv ft;
+ uint32_t label = NO_LABEL, reqid = 0;
+ uint32_t pw_status = 0;
+ uint8_t flags = 0;
+ int feclen, lbllen, tlen;
+ struct mapping_entry *me;
+ struct mapping_head mh;
+ struct map map;
+
+ memcpy(&msg, buf, sizeof(msg));
+ buf += LDP_MSG_SIZE;
+ len -= LDP_MSG_SIZE;
+
+ /* FEC TLV */
+ if (len < sizeof(ft)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ memcpy(&ft, buf, sizeof(ft));
+ if (ntohs(ft.type) != TLV_TYPE_FEC) {
+ send_notification_nbr(nbr, S_MISS_MSG, msg.id, msg.type);
+ return (-1);
+ }
+ feclen = ntohs(ft.length);
+ if (feclen > len - TLV_HDR_SIZE) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ buf += TLV_HDR_SIZE; /* just advance to the end of the fec header */
+ len -= TLV_HDR_SIZE;
+
+ TAILQ_INIT(&mh);
+ do {
+ memset(&map, 0, sizeof(map));
+ map.msg_id = msg.id;
+
+ if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen,
+ &map)) == -1)
+ goto err;
+ if (map.type == MAP_TYPE_PWID &&
+ !(map.flags & F_MAP_PW_ID) &&
+ type != MSG_TYPE_LABELWITHDRAW &&
+ type != MSG_TYPE_LABELRELEASE) {
+ send_notification_nbr(nbr, S_MISS_MSG, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ /*
+ * The Wildcard FEC Element can be used only in the
+ * Label Withdraw and Label Release messages.
+ */
+ if (map.type == MAP_TYPE_WILDCARD) {
+ switch (type) {
+ case MSG_TYPE_LABELMAPPING:
+ case MSG_TYPE_LABELREQUEST:
+ case MSG_TYPE_LABELABORTREQ:
+ session_shutdown(nbr, S_UNKNOWN_FEC, msg.id,
+ msg.type);
+ goto err;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * LDP supports the use of multiple FEC Elements per
+ * FEC for the Label Mapping message only.
+ */
+ if (type != MSG_TYPE_LABELMAPPING &&
+ tlen != feclen) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ goto err;
+ }
+
+ mapping_list_add(&mh, &map);
+
+ buf += tlen;
+ len -= tlen;
+ feclen -= tlen;
+ } while (feclen > 0);
+
+ /* Mandatory Label TLV */
+ if (type == MSG_TYPE_LABELMAPPING) {
+ lbllen = tlv_decode_label(nbr, &msg, buf, len, &label);
+ if (lbllen == -1)
+ goto err;
+
+ buf += lbllen;
+ len -= lbllen;
+ }
+
+ /* Optional Parameters */
+ while (len > 0) {
+ struct tlv tlv;
+ uint16_t tlv_len;
+ uint32_t reqbuf, labelbuf, statusbuf;
+
+ if (len < sizeof(tlv)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ goto err;
+ }
+
+ memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_len = ntohs(tlv.length);
+ if (tlv_len + TLV_HDR_SIZE > len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ goto err;
+ }
+ buf += TLV_HDR_SIZE;
+ len -= TLV_HDR_SIZE;
+
+ switch (ntohs(tlv.type)) {
+ case TLV_TYPE_LABELREQUEST:
+ switch (type) {
+ case MSG_TYPE_LABELMAPPING:
+ case MSG_TYPE_LABELREQUEST:
+ if (tlv_len != REQID_TLV_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN,
+ msg.id, msg.type);
+ goto err;
+ }
+
+ flags |= F_MAP_REQ_ID;
+ memcpy(&reqbuf, buf, sizeof(reqbuf));
+ reqid = ntohl(reqbuf);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ break;
+ case TLV_TYPE_HOPCOUNT:
+ case TLV_TYPE_PATHVECTOR:
+ /* ignore */
+ break;
+ case TLV_TYPE_GENERICLABEL:
+ switch (type) {
+ case MSG_TYPE_LABELWITHDRAW:
+ case MSG_TYPE_LABELRELEASE:
+ if (tlv_len != LABEL_TLV_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN,
+ msg.id, msg.type);
+ goto err;
+ }
+
+ memcpy(&labelbuf, buf, sizeof(labelbuf));
+ label = ntohl(labelbuf);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ break;
+ case TLV_TYPE_ATMLABEL:
+ case TLV_TYPE_FRLABEL:
+ switch (type) {
+ case MSG_TYPE_LABELWITHDRAW:
+ case MSG_TYPE_LABELRELEASE:
+ /* unsupported */
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
+ msg.type);
+ goto err;
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ break;
+ case TLV_TYPE_STATUS:
+ if (tlv_len != STATUS_TLV_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ goto err;
+ }
+ /* ignore */
+ break;
+ case TLV_TYPE_PW_STATUS:
+ switch (type) {
+ case MSG_TYPE_LABELMAPPING:
+ if (tlv_len != PW_STATUS_TLV_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN,
+ msg.id, msg.type);
+ goto err;
+ }
+
+ flags |= F_MAP_PW_STATUS;
+ memcpy(&statusbuf, buf, sizeof(statusbuf));
+ pw_status = ntohl(statusbuf);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ break;
+ default:
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ send_notification_nbr(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type);
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ }
+
+ /* notify lde about the received message. */
+ while ((me = TAILQ_FIRST(&mh)) != NULL) {
+ int imsg_type = IMSG_NONE;
+
+ me->map.flags |= flags;
+ switch (me->map.type) {
+ case MAP_TYPE_PREFIX:
+ switch (me->map.fec.prefix.af) {
+ case AF_INET:
+ if (label == MPLS_LABEL_IPV6NULL) {
+ session_shutdown(nbr, S_BAD_TLV_VAL,
+ msg.id, msg.type);
+ goto err;
+ }
+ if (!nbr->v4_enabled)
+ goto next;
+ break;
+ case AF_INET6:
+ if (label == MPLS_LABEL_IPV4NULL) {
+ session_shutdown(nbr, S_BAD_TLV_VAL,
+ msg.id, msg.type);
+ goto err;
+ }
+ if (!nbr->v6_enabled)
+ goto next;
+ break;
+ default:
+ fatalx("recv_labelmessage: unknown af");
+ }
+ break;
+ case MAP_TYPE_PWID:
+ if (label <= MPLS_LABEL_RESERVED_MAX) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
+ msg.type);
+ goto err;
+ }
+ if (me->map.flags & F_MAP_PW_STATUS)
+ me->map.pw_status = pw_status;
+ break;
+ default:
+ break;
+ }
+ me->map.label = label;
+ if (me->map.flags & F_MAP_REQ_ID)
+ me->map.requestid = reqid;
+
+ log_debug("msg-in: label mapping: lsr-id %s, fec %s, label %s",
+ inet_ntoa(nbr->id), log_map(&me->map),
+ log_label(me->map.label));
+
+ switch (type) {
+ case MSG_TYPE_LABELMAPPING:
+ imsg_type = IMSG_LABEL_MAPPING;
+ break;
+ case MSG_TYPE_LABELREQUEST:
+ imsg_type = IMSG_LABEL_REQUEST;
+ break;
+ case MSG_TYPE_LABELWITHDRAW:
+ imsg_type = IMSG_LABEL_WITHDRAW;
+ break;
+ case MSG_TYPE_LABELRELEASE:
+ imsg_type = IMSG_LABEL_RELEASE;
+ break;
+ case MSG_TYPE_LABELABORTREQ:
+ imsg_type = IMSG_LABEL_ABORT;
+ break;
+ default:
+ break;
+ }
+
+ ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map,
+ sizeof(struct map));
+
+next:
+ TAILQ_REMOVE(&mh, me, entry);
+ free(me);
+ }
+
+ return (0);
+
+err:
+ mapping_list_clr(&mh);
+
+ return (-1);
+}
+
+/* Other TLV related functions */
+static int
+gen_label_tlv(struct ibuf *buf, uint32_t label)
+{
+ struct label_tlv lt;
+
+ lt.type = htons(TLV_TYPE_GENERICLABEL);
+ lt.length = htons(LABEL_TLV_LEN);
+ lt.label = htonl(label);
+
+ return (ibuf_add(buf, &lt, sizeof(lt)));
+}
+
+static int
+tlv_decode_label(struct nbr *nbr, struct ldp_msg *msg, char *buf,
+ uint16_t len, uint32_t *label)
+{
+ struct label_tlv lt;
+
+ if (len < sizeof(lt)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
+ return (-1);
+ }
+ memcpy(&lt, buf, sizeof(lt));
+
+ if (!(ntohs(lt.type) & TLV_TYPE_GENERICLABEL)) {
+ send_notification_nbr(nbr, S_MISS_MSG, msg->id, msg->type);
+ return (-1);
+ }
+
+ switch (htons(lt.type)) {
+ case TLV_TYPE_GENERICLABEL:
+ if (ntohs(lt.length) != sizeof(lt) - TLV_HDR_SIZE) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ *label = ntohl(lt.label);
+ if (*label > MPLS_LABEL_MAX ||
+ (*label <= MPLS_LABEL_RESERVED_MAX &&
+ *label != MPLS_LABEL_IPV4NULL &&
+ *label != MPLS_LABEL_IPV6NULL &&
+ *label != MPLS_LABEL_IMPLNULL)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
+ msg->type);
+ return (-1);
+ }
+ break;
+ case TLV_TYPE_ATMLABEL:
+ case TLV_TYPE_FRLABEL:
+ default:
+ /* unsupported */
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
+ return (-1);
+ }
+
+ return (sizeof(lt));
+}
+
+static int
+gen_reqid_tlv(struct ibuf *buf, uint32_t reqid)
+{
+ struct reqid_tlv rt;
+
+ rt.type = htons(TLV_TYPE_LABELREQUEST);
+ rt.length = htons(REQID_TLV_LEN);
+ rt.reqid = htonl(reqid);
+
+ return (ibuf_add(buf, &rt, sizeof(rt)));
+}
+
+int
+gen_pw_status_tlv(struct ibuf *buf, uint32_t status)
+{
+ struct pw_status_tlv st;
+
+ st.type = htons(TLV_TYPE_PW_STATUS);
+ st.length = htons(PW_STATUS_TLV_LEN);
+ st.value = htonl(status);
+
+ return (ibuf_add(buf, &st, sizeof(st)));
+}
+
+int
+gen_fec_tlv(struct ibuf *buf, struct map *map)
+{
+ struct tlv ft;
+ uint16_t family, len, pw_type, ifmtu;
+ uint8_t pw_len = 0;
+ uint32_t group_id, pwid;
+ int err = 0;
+
+ ft.type = htons(TLV_TYPE_FEC);
+
+ switch (map->type) {
+ case MAP_TYPE_WILDCARD:
+ ft.length = htons(sizeof(uint8_t));
+ err |= ibuf_add(buf, &ft, sizeof(ft));
+ err |= ibuf_add(buf, &map->type, sizeof(map->type));
+ break;
+ case MAP_TYPE_PREFIX:
+ len = PREFIX_SIZE(map->fec.prefix.prefixlen);
+ ft.length = htons(sizeof(map->type) + sizeof(family) +
+ sizeof(map->fec.prefix.prefixlen) + len);
+ err |= ibuf_add(buf, &ft, sizeof(ft));
+ err |= ibuf_add(buf, &map->type, sizeof(map->type));
+ switch (map->fec.prefix.af) {
+ case AF_INET:
+ family = htons(AF_IPV4);
+ break;
+ case AF_INET6:
+ family = htons(AF_IPV6);
+ break;
+ default:
+ fatalx("gen_fec_tlv: unknown af");
+ break;
+ }
+ err |= ibuf_add(buf, &family, sizeof(family));
+ err |= ibuf_add(buf, &map->fec.prefix.prefixlen,
+ sizeof(map->fec.prefix.prefixlen));
+ if (len)
+ err |= ibuf_add(buf, &map->fec.prefix.prefix, len);
+ break;
+ case MAP_TYPE_PWID:
+ if (map->flags & F_MAP_PW_ID)
+ pw_len += PW_STATUS_TLV_LEN;
+ if (map->flags & F_MAP_PW_IFMTU)
+ pw_len += FEC_SUBTLV_IFMTU_SIZE;
+
+ len = FEC_PWID_ELM_MIN_LEN + pw_len;
+
+ ft.length = htons(len);
+ err |= ibuf_add(buf, &ft, sizeof(ft));
+
+ err |= ibuf_add(buf, &map->type, sizeof(uint8_t));
+ pw_type = map->fec.pwid.type;
+ if (map->flags & F_MAP_PW_CWORD)
+ pw_type |= CONTROL_WORD_FLAG;
+ pw_type = htons(pw_type);
+ err |= ibuf_add(buf, &pw_type, sizeof(uint16_t));
+ err |= ibuf_add(buf, &pw_len, sizeof(uint8_t));
+ group_id = htonl(map->fec.pwid.group_id);
+ err |= ibuf_add(buf, &group_id, sizeof(uint32_t));
+ if (map->flags & F_MAP_PW_ID) {
+ pwid = htonl(map->fec.pwid.pwid);
+ err |= ibuf_add(buf, &pwid, sizeof(uint32_t));
+ }
+ if (map->flags & F_MAP_PW_IFMTU) {
+ struct subtlv stlv;
+
+ stlv.type = SUBTLV_IFMTU;
+ stlv.length = FEC_SUBTLV_IFMTU_SIZE;
+ err |= ibuf_add(buf, &stlv, sizeof(uint16_t));
+
+ ifmtu = htons(map->fec.pwid.ifmtu);
+ err |= ibuf_add(buf, &ifmtu, sizeof(uint16_t));
+ }
+ break;
+ default:
+ break;
+ }
+
+ return (err);
+}
+
+int
+tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
+ uint16_t len, struct map *map)
+{
+ uint16_t off = 0;
+ uint8_t pw_len;
+
+ map->type = *buf;
+ off += sizeof(uint8_t);
+
+ switch (map->type) {
+ case MAP_TYPE_WILDCARD:
+ if (len == FEC_ELM_WCARD_LEN)
+ return (off);
+ else {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
+ msg->type);
+ return (-1);
+ }
+ break;
+ case MAP_TYPE_PREFIX:
+ if (len < FEC_ELM_PREFIX_MIN_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ /* Address Family */
+ memcpy(&map->fec.prefix.af, buf + off,
+ sizeof(map->fec.prefix.af));
+ off += sizeof(map->fec.prefix.af);
+ map->fec.prefix.af = ntohs(map->fec.prefix.af);
+ switch (map->fec.prefix.af) {
+ case AF_IPV4:
+ map->fec.prefix.af = AF_INET;
+ break;
+ case AF_IPV6:
+ map->fec.prefix.af = AF_INET6;
+ break;
+ default:
+ send_notification_nbr(nbr, S_UNSUP_ADDR, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ /* Prefix Length */
+ map->fec.prefix.prefixlen = buf[off];
+ off += sizeof(uint8_t);
+ if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ /* Prefix */
+ memset(&map->fec.prefix.prefix, 0,
+ sizeof(map->fec.prefix.prefix));
+ memcpy(&map->fec.prefix.prefix, buf + off,
+ PREFIX_SIZE(map->fec.prefix.prefixlen));
+
+ /* Just in case... */
+ ldp_applymask(map->fec.prefix.af, &map->fec.prefix.prefix,
+ &map->fec.prefix.prefix, map->fec.prefix.prefixlen);
+
+ return (off + PREFIX_SIZE(map->fec.prefix.prefixlen));
+ case MAP_TYPE_PWID:
+ if (len < FEC_PWID_ELM_MIN_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ /* PW type */
+ memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t));
+ map->fec.pwid.type = ntohs(map->fec.pwid.type);
+ if (map->fec.pwid.type & CONTROL_WORD_FLAG) {
+ map->flags |= F_MAP_PW_CWORD;
+ map->fec.pwid.type &= ~CONTROL_WORD_FLAG;
+ }
+ off += sizeof(uint16_t);
+
+ /* PW info Length */
+ pw_len = buf[off];
+ off += sizeof(uint8_t);
+
+ if (len != FEC_PWID_ELM_MIN_LEN + pw_len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ /* Group ID */
+ memcpy(&map->fec.pwid.group_id, buf + off, sizeof(uint32_t));
+ map->fec.pwid.group_id = ntohl(map->fec.pwid.group_id);
+ off += sizeof(uint32_t);
+
+ /* PW ID */
+ if (pw_len == 0)
+ return (off);
+
+ if (pw_len < sizeof(uint32_t)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t));
+ map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid);
+ map->flags |= F_MAP_PW_ID;
+ off += sizeof(uint32_t);
+ pw_len -= sizeof(uint32_t);
+
+ /* Optional Interface Parameter Sub-TLVs */
+ while (pw_len > 0) {
+ struct subtlv stlv;
+
+ if (pw_len < sizeof(stlv)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ memcpy(&stlv, buf + off, sizeof(stlv));
+ if (stlv.length > pw_len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ switch (stlv.type) {
+ case SUBTLV_IFMTU:
+ if (stlv.length != FEC_SUBTLV_IFMTU_SIZE) {
+ session_shutdown(nbr, S_BAD_TLV_LEN,
+ msg->id, msg->type);
+ return (-1);
+ }
+ memcpy(&map->fec.pwid.ifmtu, buf + off +
+ SUBTLV_HDR_SIZE, sizeof(uint16_t));
+ map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu);
+ map->flags |= F_MAP_PW_IFMTU;
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ off += stlv.length;
+ pw_len -= stlv.length;
+ }
+
+ return (off);
+ default:
+ send_notification_nbr(nbr, S_UNKNOWN_FEC, msg->id, msg->type);
+ break;
+ }
+
+ return (-1);
+}
diff --git a/ldpd/lde.c b/ldpd/lde.c
new file mode 100644
index 000000000..8859ed25b
--- /dev/null
+++ b/ldpd/lde.c
@@ -0,0 +1,1335 @@
+/* $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>
+ *
+ * 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/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netmpls/mpls.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "ldp.h"
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "lde.h"
+
+static void lde_sig_handler(int sig, short, void *);
+static __dead void lde_shutdown(void);
+static int lde_imsg_compose_parent(int, pid_t, void *, uint16_t);
+static void lde_dispatch_imsg(int, short, void *);
+static void lde_dispatch_parent(int, short, void *);
+static __inline int lde_nbr_compare(struct lde_nbr *,
+ 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 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 *);
+
+RB_GENERATE(nbr_tree, lde_nbr, entry, lde_nbr_compare)
+
+struct ldpd_conf *ldeconf;
+struct nbr_tree lde_nbrs = RB_INITIALIZER(&lde_nbrs);
+
+static struct imsgev *iev_ldpe;
+static struct imsgev *iev_main;
+
+/* ARGSUSED */
+static void
+lde_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * signal handler rules don't apply, libevent decouples for us
+ */
+
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ lde_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+/* label decision engine */
+void
+lde(int debug, int verbose)
+{
+ struct event ev_sigint, ev_sigterm;
+ struct timeval now;
+ struct passwd *pw;
+
+ ldeconf = config_new_empty();
+
+ log_init(debug);
+ log_verbose(verbose);
+
+ setproctitle("label decision engine");
+ ldpd_process = PROC_LDE_ENGINE;
+
+ if ((pw = getpwnam(LDPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ if (pledge("stdio recvfd", NULL) == -1)
+ fatal("pledge");
+
+ event_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, lde_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, lde_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ /* setup pipe and event handler to the parent process */
+ if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_main->ibuf, 3);
+ iev_main->handler = lde_dispatch_parent;
+ iev_main->events = EV_READ;
+ event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
+ iev_main->handler, iev_main);
+ event_add(&iev_main->ev, NULL);
+
+ /* setup and start the LIB garbage collector */
+ evtimer_set(&gc_timer, lde_gc_timer, NULL);
+ lde_gc_start_timer();
+
+ gettimeofday(&now, NULL);
+ global.uptime = now.tv_sec;
+
+ event_dispatch();
+
+ lde_shutdown();
+}
+
+static __dead void
+lde_shutdown(void)
+{
+ /* close pipes */
+ msgbuf_clear(&iev_ldpe->ibuf.w);
+ close(iev_ldpe->ibuf.fd);
+ msgbuf_clear(&iev_main->ibuf.w);
+ close(iev_main->ibuf.fd);
+
+ lde_gc_stop_timer();
+ lde_nbr_clear();
+ fec_tree_clear();
+
+ config_clear(ldeconf);
+
+ free(iev_ldpe);
+ free(iev_main);
+
+ log_info("label decision engine exiting");
+ exit(0);
+}
+
+/* imesg */
+static int
+lde_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen));
+}
+
+int
+lde_imsg_compose_ldpe(int type, uint32_t peerid, pid_t pid, void *data,
+ uint16_t datalen)
+{
+ return (imsg_compose_event(iev_ldpe, type, peerid, pid,
+ -1, data, datalen));
+}
+
+/* ARGSUSED */
+static void
+lde_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct imsgev *iev = bula;
+ 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, 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) /* 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(map))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ memcpy(&map, imsg.data, sizeof(map));
+
+ 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);
+ break;
+ case IMSG_LABEL_REQUEST:
+ lde_check_request(&map, ln);
+ break;
+ case IMSG_LABEL_RELEASE:
+ if (map.type == MAP_TYPE_WILDCARD)
+ lde_check_release_wcard(&map, ln);
+ else
+ lde_check_release(&map, ln);
+ break;
+ case IMSG_LABEL_WITHDRAW:
+ if (map.type == MAP_TYPE_WILDCARD)
+ lde_check_withdraw_wcard(&map, ln);
+ else
+ 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(lde_addr))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ memcpy(&lde_addr, imsg.data, sizeof(lde_addr));
+
+ 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(lde_addr))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ memcpy(&lde_addr, imsg.data, sizeof(lde_addr));
+
+ 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(nm))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ memcpy(&nm, imsg.data, sizeof(nm));
+
+ 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;
+ 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;
+ case IMSG_CTL_LOG_VERBOSE:
+ /* already checked by ldpe */
+ memcpy(&verbose, imsg.data, sizeof(verbose));
+ log_verbose(verbose);
+ 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 handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+/* ARGSUSED */
+static void
+lde_dispatch_parent(int fd, short event, void *bula)
+{
+ static struct ldpd_conf *nconf;
+ struct iface *niface;
+ struct tnbr *ntnbr;
+ struct nbr_params *nnbrp;
+ static struct l2vpn *nl2vpn;
+ struct l2vpn_if *nlif;
+ struct l2vpn_pw *npw;
+ struct imsg imsg;
+ struct kroute kr;
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ ssize_t n;
+ int shut = 0;
+ struct fec fec;
+
+ 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) /* 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_NETWORK_ADD:
+ case IMSG_NETWORK_DEL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ memcpy(&kr, imsg.data, sizeof(kr));
+
+ 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.priority, kr.flags & F_CONNECTED, NULL);
+ break;
+ case IMSG_NETWORK_DEL:
+ lde_kernel_remove(&fec, kr.af, &kr.nexthop,
+ kr.priority);
+ 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 = lde_dispatch_imsg;
+ 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);
+ break;
+ case IMSG_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct ldpd_conf))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct ldpd_conf));
+
+ LIST_INIT(&nconf->iface_list);
+ LIST_INIT(&nconf->tnbr_list);
+ LIST_INIT(&nconf->nbrp_list);
+ LIST_INIT(&nconf->l2vpn_list);
+ break;
+ case IMSG_RECONF_IFACE:
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ LIST_INIT(&niface->addr_list);
+ LIST_INIT(&niface->ipv4.adj_list);
+ LIST_INIT(&niface->ipv6.adj_list);
+ niface->ipv4.iface = niface;
+ niface->ipv6.iface = niface;
+
+ LIST_INSERT_HEAD(&nconf->iface_list, niface, entry);
+ break;
+ case IMSG_RECONF_TNBR:
+ if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL)
+ fatal(NULL);
+ memcpy(ntnbr, imsg.data, sizeof(struct tnbr));
+
+ LIST_INSERT_HEAD(&nconf->tnbr_list, ntnbr, entry);
+ break;
+ case IMSG_RECONF_NBRP:
+ if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL)
+ fatal(NULL);
+ memcpy(nnbrp, imsg.data, sizeof(struct nbr_params));
+
+ LIST_INSERT_HEAD(&nconf->nbrp_list, nnbrp, entry);
+ break;
+ case IMSG_RECONF_L2VPN:
+ if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL)
+ fatal(NULL);
+ memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn));
+
+ LIST_INIT(&nl2vpn->if_list);
+ LIST_INIT(&nl2vpn->pw_list);
+
+ LIST_INSERT_HEAD(&nconf->l2vpn_list, nl2vpn, entry);
+ break;
+ case IMSG_RECONF_L2VPN_IF:
+ if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL)
+ fatal(NULL);
+ memcpy(nlif, imsg.data, sizeof(struct l2vpn_if));
+
+ nlif->l2vpn = nl2vpn;
+ LIST_INSERT_HEAD(&nl2vpn->if_list, nlif, entry);
+ break;
+ case IMSG_RECONF_L2VPN_PW:
+ if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL)
+ fatal(NULL);
+ memcpy(npw, imsg.data, sizeof(struct l2vpn_pw));
+
+ npw->l2vpn = nl2vpn;
+ LIST_INSERT_HEAD(&nl2vpn->pw_list, npw, entry);
+ break;
+ case IMSG_RECONF_END:
+ merge_config(ldeconf, nconf);
+ nconf = NULL;
+ 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 handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+uint32_t
+lde_assign_label(void)
+{
+ static uint32_t label = MPLS_LABEL_RESERVED_MAX;
+
+ /* XXX some checks needed */
+ label++;
+ return (label);
+}
+
+void
+lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh)
+{
+ struct kroute kr;
+ struct kpw kpw;
+ 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.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.priority = fnh->priority;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr,
+ sizeof(kr));
+
+ if (fn->fec.u.ipv4.prefixlen == 32)
+ l2vpn_sync_pws(AF_INET, (union ldpd_addr *)
+ &fn->fec.u.ipv4.prefix);
+ 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.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.priority = fnh->priority;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr,
+ sizeof(kr));
+
+ if (fn->fec.u.ipv6.prefixlen == 128)
+ l2vpn_sync_pws(AF_INET6, (union ldpd_addr *)
+ &fn->fec.u.ipv6.prefix);
+ break;
+ case FEC_TYPE_PWID:
+ if (fn->local_label == NO_LABEL ||
+ fnh->remote_label == NO_LABEL)
+ return;
+
+ pw = (struct l2vpn_pw *) fn->data;
+ pw->flags |= F_PW_STATUS_UP;
+
+ memset(&kpw, 0, sizeof(kpw));
+ kpw.ifindex = pw->ifindex;
+ kpw.pw_type = fn->fec.u.pwid.type;
+ kpw.af = pw->af;
+ kpw.nexthop = pw->addr;
+ kpw.local_label = fn->local_label;
+ kpw.remote_label = fnh->remote_label;
+ kpw.flags = pw->flags;
+
+ lde_imsg_compose_parent(IMSG_KPWLABEL_CHANGE, 0, &kpw,
+ sizeof(kpw));
+ break;
+ }
+}
+
+void
+lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh)
+{
+ struct kroute kr;
+ struct kpw kpw;
+ 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.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.priority = fnh->priority;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr,
+ sizeof(kr));
+
+ if (fn->fec.u.ipv4.prefixlen == 32)
+ l2vpn_sync_pws(AF_INET, (union ldpd_addr *)
+ &fn->fec.u.ipv4.prefix);
+ 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.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.priority = fnh->priority;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr,
+ sizeof(kr));
+
+ if (fn->fec.u.ipv6.prefixlen == 128)
+ l2vpn_sync_pws(AF_INET6, (union ldpd_addr *)
+ &fn->fec.u.ipv6.prefix);
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (!(pw->flags & F_PW_STATUS_UP))
+ return;
+ pw->flags &= ~F_PW_STATUS_UP;
+
+ memset(&kpw, 0, sizeof(kpw));
+ kpw.ifindex = pw->ifindex;
+ kpw.pw_type = fn->fec.u.pwid.type;
+ kpw.af = pw->af;
+ kpw.nexthop = pw->addr;
+ kpw.local_label = fn->local_label;
+ kpw.remote_label = fnh->remote_label;
+ kpw.flags = pw->flags;
+
+ lde_imsg_compose_parent(IMSG_KPWLABEL_DELETE, 0, &kpw,
+ sizeof(kpw));
+ 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;
+ 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_req *lre;
+ struct lde_map *me;
+ struct map map;
+ struct l2vpn_pw *pw;
+
+ /*
+ * 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;
+ 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;
+
+ map.flags |= F_MAP_PW_IFMTU;
+ map.fec.pwid.ifmtu = pw->l2vpn->mtu;
+ if (pw->flags & F_PW_CWORD)
+ map.flags |= F_MAP_PW_CWORD;
+ if (pw->flags & F_PW_STATUSTLV) {
+ map.flags |= F_MAP_PW_STATUS;
+ /* VPLS are always up */
+ map.pw_status = PW_FORWARDING;
+ }
+ 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, uint32_t label,
+ 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 (pw->flags & F_PW_CWORD)
+ map.flags |= F_MAP_PW_CWORD;
+ break;
+ }
+ map.label = fn->local_label;
+ } else {
+ memset(&map, 0, sizeof(map));
+ map.type = MAP_TYPE_WILDCARD;
+ map.label = label;
+ }
+
+ if (st) {
+ map.st.status_code = st->status_code;
+ map.st.msg_id = st->msg_id;
+ map.st.msg_type = st->msg_type;
+ 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 {
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ 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_all(struct fec_node *fn, uint32_t label)
+{
+ struct lde_nbr *ln;
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, label, NULL);
+}
+
+void
+lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, 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 (pw->flags & F_PW_CWORD)
+ map.flags |= F_MAP_PW_CWORD;
+ break;
+ }
+ } else {
+ memset(&map, 0, sizeof(map));
+ map.type = MAP_TYPE_WILDCARD;
+ }
+ 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_notification(uint32_t peerid, 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, peerid, 0,
+ &nm, sizeof(nm));
+}
+
+static __inline int
+lde_nbr_compare(struct lde_nbr *a, 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->peerid = peerid;
+ fec_init(&ln->recv_map);
+ fec_init(&ln->sent_map);
+ 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;
+
+ if (ln == NULL)
+ return;
+
+ /* uninstall received mappings */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ 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;
+ 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)
+ 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->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 ((ln = RB_ROOT(&lde_nbrs)) != NULL)
+ 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) {
+ fn = (struct fec_node *)fec_find(&ft, fec);
+ 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;
+ default:
+ 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;
+ }
+ }
+}
+
+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) {
+ LIST_INSERT_HEAD(&fn->upstream, me, entry);
+ 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 {
+ LIST_INSERT_HEAD(&fn->downstream, me, entry);
+ 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;
+
+ LIST_REMOVE(map, entry);
+ 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, int was_implicit)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
+ /* explicit withdraw */
+ if (was_implicit)
+ lde_send_labelwithdraw(ln, NULL, MPLS_LABEL_IMPLNULL,
+ NULL);
+ else {
+ if (ln->v4_enabled)
+ lde_send_labelwithdraw(ln, NULL,
+ MPLS_LABEL_IPV4NULL, NULL);
+ if (ln->v6_enabled)
+ lde_send_labelwithdraw(ln, NULL,
+ MPLS_LABEL_IPV6NULL, NULL);
+ }
+
+ /* advertise new label of connected prefixes */
+ 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 = egress_label(fn->fec.type);
+ lde_send_labelmapping(ln, fn, 0);
+ }
+
+ 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_FIRST(&ln->addr_list)) != NULL) {
+ TAILQ_REMOVE(&ln->addr_list, lde_addr, entry);
+ free(lde_addr);
+ }
+}
diff --git a/ldpd/lde.h b/ldpd/lde.h
new file mode 100644
index 000000000..b0f7b2043
--- /dev/null
+++ b/ldpd/lde.h
@@ -0,0 +1,202 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@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.
+ */
+
+#ifndef _LDE_H_
+#define _LDE_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+
+enum fec_type {
+ FEC_TYPE_IPV4,
+ FEC_TYPE_IPV6,
+ FEC_TYPE_PWID
+};
+
+struct fec {
+ RB_ENTRY(fec) entry;
+ enum fec_type type;
+ union {
+ struct {
+ struct in_addr prefix;
+ uint8_t prefixlen;
+ } ipv4;
+ struct {
+ struct in6_addr prefix;
+ uint8_t prefixlen;
+ } ipv6;
+ struct {
+ uint16_t type;
+ uint32_t pwid;
+ struct in_addr lsr_id;
+ } pwid;
+ } u;
+};
+RB_HEAD(fec_tree, fec);
+RB_PROTOTYPE(fec_tree, fec, entry, fec_compare)
+
+/* request entries */
+struct lde_req {
+ struct fec fec;
+ uint32_t msg_id;
+};
+
+/* mapping entries */
+struct lde_map {
+ struct fec fec;
+ LIST_ENTRY(lde_map) entry;
+ struct lde_nbr *nexthop;
+ struct map map;
+};
+
+/* withdraw entries */
+struct lde_wdraw {
+ struct fec fec;
+ uint32_t label;
+};
+
+/* Addresses belonging to neighbor */
+struct lde_addr {
+ TAILQ_ENTRY(lde_addr) entry;
+ int af;
+ union ldpd_addr addr;
+};
+
+/* just the info LDE needs */
+struct lde_nbr {
+ RB_ENTRY(lde_nbr) entry;
+ uint32_t peerid;
+ struct in_addr id;
+ int v4_enabled; /* announce/process v4 msgs */
+ int v6_enabled; /* announce/process v6 msgs */
+ struct fec_tree recv_req;
+ struct fec_tree sent_req;
+ struct fec_tree recv_map;
+ struct fec_tree sent_map;
+ struct fec_tree sent_wdraw;
+ TAILQ_HEAD(, lde_addr) addr_list;
+};
+RB_HEAD(nbr_tree, lde_nbr);
+RB_PROTOTYPE(nbr_tree, lde_nbr, entry, lde_nbr_compare)
+
+struct fec_nh {
+ LIST_ENTRY(fec_nh) entry;
+ int af;
+ union ldpd_addr nexthop;
+ uint32_t remote_label;
+ uint8_t priority;
+};
+
+struct fec_node {
+ struct fec fec;
+
+ LIST_HEAD(, fec_nh) nexthops; /* fib nexthops */
+ LIST_HEAD(, lde_map) downstream; /* recv mappings */
+ LIST_HEAD(, lde_map) upstream; /* sent mappings */
+
+ uint32_t local_label;
+ void *data; /* fec specific data */
+};
+
+#define LDE_GC_INTERVAL 300
+
+extern struct ldpd_conf *ldeconf;
+extern struct fec_tree ft;
+extern struct nbr_tree lde_nbrs;
+extern struct event gc_timer;
+
+/* lde.c */
+void lde(int, int);
+int lde_imsg_compose_ldpe(int, uint32_t, pid_t, void *, uint16_t);
+uint32_t lde_assign_label(void);
+void lde_send_change_klabel(struct fec_node *, struct fec_nh *);
+void lde_send_delete_klabel(struct fec_node *, struct fec_nh *);
+void lde_fec2map(struct fec *, struct map *);
+void lde_map2fec(struct map *, struct in_addr, struct fec *);
+void lde_send_labelmapping(struct lde_nbr *, struct fec_node *,
+ int);
+void lde_send_labelwithdraw(struct lde_nbr *, struct fec_node *,
+ uint32_t, struct status_tlv *);
+void lde_send_labelwithdraw_all(struct fec_node *, uint32_t);
+void lde_send_labelrelease(struct lde_nbr *, struct fec_node *,
+ uint32_t);
+void lde_send_notification(uint32_t, uint32_t, uint32_t, uint16_t);
+struct lde_nbr *lde_nbr_find_by_lsrid(struct in_addr);
+struct lde_nbr *lde_nbr_find_by_addr(int, union ldpd_addr *);
+struct lde_map *lde_map_add(struct lde_nbr *, struct fec_node *, int);
+void lde_map_del(struct lde_nbr *, struct lde_map *, int);
+struct lde_req *lde_req_add(struct lde_nbr *, struct fec *, int);
+void lde_req_del(struct lde_nbr *, struct lde_req *, int);
+struct lde_wdraw *lde_wdraw_add(struct lde_nbr *, struct fec_node *);
+void lde_wdraw_del(struct lde_nbr *, struct lde_wdraw *);
+void lde_change_egress_label(int, int);
+struct lde_addr *lde_address_find(struct lde_nbr *, int,
+ union ldpd_addr *);
+
+/* lde_lib.c */
+void fec_init(struct fec_tree *);
+struct fec *fec_find(struct fec_tree *, struct fec *);
+int fec_insert(struct fec_tree *, struct fec *);
+int fec_remove(struct fec_tree *, struct fec *);
+void fec_clear(struct fec_tree *, void (*)(void *));
+void rt_dump(pid_t);
+void fec_snap(struct lde_nbr *);
+void fec_tree_clear(void);
+struct fec_nh *fec_nh_find(struct fec_node *, int, union ldpd_addr *,
+ uint8_t);
+uint32_t egress_label(enum fec_type);
+void lde_kernel_insert(struct fec *, int, union ldpd_addr *,
+ uint8_t, int, void *);
+void lde_kernel_remove(struct fec *, int, union ldpd_addr *,
+ uint8_t);
+void lde_check_mapping(struct map *, struct lde_nbr *);
+void lde_check_request(struct map *, struct lde_nbr *);
+void lde_check_release(struct map *, struct lde_nbr *);
+void lde_check_release_wcard(struct map *, struct lde_nbr *);
+void lde_check_withdraw(struct map *, struct lde_nbr *);
+void lde_check_withdraw_wcard(struct map *, struct lde_nbr *);
+void lde_gc_timer(int, short, void *);
+void lde_gc_start_timer(void);
+void lde_gc_stop_timer(void);
+
+/* l2vpn.c */
+struct l2vpn *l2vpn_new(const char *);
+struct l2vpn *l2vpn_find(struct ldpd_conf *, const char *);
+void l2vpn_del(struct l2vpn *);
+void l2vpn_init(struct l2vpn *);
+void l2vpn_exit(struct l2vpn *);
+struct l2vpn_if *l2vpn_if_new(struct l2vpn *, struct kif *);
+struct l2vpn_if *l2vpn_if_find(struct l2vpn *, unsigned int);
+struct l2vpn_pw *l2vpn_pw_new(struct l2vpn *, struct kif *);
+struct l2vpn_pw *l2vpn_pw_find(struct l2vpn *, unsigned int);
+void l2vpn_pw_init(struct l2vpn_pw *);
+void l2vpn_pw_exit(struct l2vpn_pw *);
+void l2vpn_pw_reset(struct l2vpn_pw *);
+int l2vpn_pw_ok(struct l2vpn_pw *, struct fec_nh *);
+int l2vpn_pw_negotiate(struct lde_nbr *, struct fec_node *,
+ struct map *);
+void l2vpn_send_pw_status(uint32_t, uint32_t, struct fec *);
+void l2vpn_recv_pw_status(struct lde_nbr *, struct notify_msg *);
+void l2vpn_sync_pws(int, union ldpd_addr *);
+void l2vpn_pw_ctl(pid_t);
+void l2vpn_binding_ctl(pid_t);
+
+#endif /* _LDE_H_ */
diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c
new file mode 100644
index 000000000..d9c1f544f
--- /dev/null
+++ b/ldpd/lde_lib.c
@@ -0,0 +1,784 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@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/socket.h>
+#include <netmpls/mpls.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "ldpd.h"
+#include "lde.h"
+#include "log.h"
+
+static __inline int fec_compare(struct fec *, struct fec *);
+static int lde_nbr_is_nexthop(struct fec_node *,
+ struct lde_nbr *);
+static void fec_free(void *);
+static struct fec_node *fec_add(struct fec *fec);
+static struct fec_nh *fec_nh_add(struct fec_node *, int, union ldpd_addr *,
+ uint8_t priority);
+static void fec_nh_del(struct fec_nh *);
+
+RB_GENERATE(fec_tree, fec, entry, fec_compare)
+
+struct fec_tree ft = RB_INITIALIZER(&ft);
+struct event gc_timer;
+
+/* FEC tree functions */
+void
+fec_init(struct fec_tree *fh)
+{
+ RB_INIT(fh);
+}
+
+static __inline int
+fec_compare(struct fec *a, struct fec *b)
+{
+ if (a->type < b->type)
+ return (-1);
+ if (a->type > b->type)
+ return (1);
+
+ switch (a->type) {
+ case FEC_TYPE_IPV4:
+ if (ntohl(a->u.ipv4.prefix.s_addr) <
+ ntohl(b->u.ipv4.prefix.s_addr))
+ return (-1);
+ if (ntohl(a->u.ipv4.prefix.s_addr) >
+ ntohl(b->u.ipv4.prefix.s_addr))
+ return (1);
+ if (a->u.ipv4.prefixlen < b->u.ipv4.prefixlen)
+ return (-1);
+ if (a->u.ipv4.prefixlen > b->u.ipv4.prefixlen)
+ return (1);
+ return (0);
+ case FEC_TYPE_IPV6:
+ if (memcmp(&a->u.ipv6.prefix, &b->u.ipv6.prefix,
+ sizeof(struct in6_addr)) < 0)
+ return (-1);
+ if (memcmp(&a->u.ipv6.prefix, &b->u.ipv6.prefix,
+ sizeof(struct in6_addr)) > 0)
+ return (1);
+ if (a->u.ipv6.prefixlen < b->u.ipv6.prefixlen)
+ return (-1);
+ if (a->u.ipv6.prefixlen > b->u.ipv6.prefixlen)
+ return (1);
+ return (0);
+ case FEC_TYPE_PWID:
+ if (a->u.pwid.type < b->u.pwid.type)
+ return (-1);
+ if (a->u.pwid.type > b->u.pwid.type)
+ return (1);
+ if (a->u.pwid.pwid < b->u.pwid.pwid)
+ return (-1);
+ if (a->u.pwid.pwid > b->u.pwid.pwid)
+ return (1);
+ if (ntohl(a->u.pwid.lsr_id.s_addr) <
+ ntohl(b->u.pwid.lsr_id.s_addr))
+ return (-1);
+ if (ntohl(a->u.pwid.lsr_id.s_addr) >
+ ntohl(b->u.pwid.lsr_id.s_addr))
+ return (1);
+ return (0);
+ }
+
+ return (-1);
+}
+
+struct fec *
+fec_find(struct fec_tree *fh, struct fec *f)
+{
+ return (RB_FIND(fec_tree, fh, f));
+}
+
+int
+fec_insert(struct fec_tree *fh, struct fec *f)
+{
+ if (RB_INSERT(fec_tree, fh, f) != NULL)
+ return (-1);
+ return (0);
+}
+
+int
+fec_remove(struct fec_tree *fh, struct fec *f)
+{
+ if (RB_REMOVE(fec_tree, fh, f) == NULL) {
+ log_warnx("%s failed for %s", __func__, log_fec(f));
+ return (-1);
+ }
+ return (0);
+}
+
+void
+fec_clear(struct fec_tree *fh, void (*free_cb)(void *))
+{
+ struct fec *f;
+
+ while ((f = RB_ROOT(fh)) != NULL) {
+ fec_remove(fh, f);
+ free_cb(f);
+ }
+}
+
+/* routing table functions */
+static int
+lde_nbr_is_nexthop(struct fec_node *fn, struct lde_nbr *ln)
+{
+ struct fec_nh *fnh;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry)
+ if (lde_address_find(ln, fnh->af, &fnh->nexthop))
+ return (1);
+
+ return (0);
+}
+
+void
+rt_dump(pid_t pid)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct lde_map *me;
+ static struct ctl_rt rtctl;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (fn->local_label == NO_LABEL &&
+ LIST_EMPTY(&fn->downstream))
+ continue;
+
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ rtctl.af = AF_INET;
+ rtctl.prefix.v4 = fn->fec.u.ipv4.prefix;
+ rtctl.prefixlen = fn->fec.u.ipv4.prefixlen;
+ break;
+ case FEC_TYPE_IPV6:
+ rtctl.af = AF_INET6;
+ rtctl.prefix.v6 = fn->fec.u.ipv6.prefix;
+ rtctl.prefixlen = fn->fec.u.ipv6.prefixlen;
+ break;
+ default:
+ continue;
+ }
+
+ rtctl.local_label = fn->local_label;
+ LIST_FOREACH(me, &fn->downstream, entry) {
+ rtctl.in_use = lde_nbr_is_nexthop(fn, me->nexthop);
+ rtctl.nexthop = me->nexthop->id;
+ rtctl.remote_label = me->map.label;
+
+ lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid,
+ &rtctl, sizeof(rtctl));
+ }
+ if (LIST_EMPTY(&fn->downstream)) {
+ rtctl.in_use = 0;
+ rtctl.nexthop.s_addr = INADDR_ANY;
+ rtctl.remote_label = NO_LABEL;
+
+ lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid,
+ &rtctl, sizeof(rtctl));
+ }
+ }
+}
+
+void
+fec_snap(struct lde_nbr *ln)
+{
+ struct fec *f;
+ struct fec_node *fn;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (fn->local_label == NO_LABEL)
+ continue;
+
+ lde_send_labelmapping(ln, fn, 0);
+ }
+
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0);
+}
+
+static void
+fec_free(void *arg)
+{
+ struct fec_node *fn = arg;
+ struct fec_nh *fnh;
+
+ while ((fnh = LIST_FIRST(&fn->nexthops)))
+ fec_nh_del(fnh);
+ if (!LIST_EMPTY(&fn->downstream))
+ log_warnx("%s: fec %s downstream list not empty", __func__,
+ log_fec(&fn->fec));
+ if (!LIST_EMPTY(&fn->upstream))
+ log_warnx("%s: fec %s upstream list not empty", __func__,
+ log_fec(&fn->fec));
+
+ free(fn);
+}
+
+void
+fec_tree_clear(void)
+{
+ fec_clear(&ft, fec_free);
+}
+
+static struct fec_node *
+fec_add(struct fec *fec)
+{
+ struct fec_node *fn;
+
+ fn = calloc(1, sizeof(*fn));
+ if (fn == NULL)
+ fatal(__func__);
+
+ fn->fec = *fec;
+ fn->local_label = NO_LABEL;
+ LIST_INIT(&fn->upstream);
+ LIST_INIT(&fn->downstream);
+ LIST_INIT(&fn->nexthops);
+
+ if (fec_insert(&ft, &fn->fec))
+ log_warnx("failed to add %s to ft tree",
+ log_fec(&fn->fec));
+
+ return (fn);
+}
+
+struct fec_nh *
+fec_nh_find(struct fec_node *fn, int af, union ldpd_addr *nexthop,
+ uint8_t priority)
+{
+ struct fec_nh *fnh;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry)
+ if (fnh->af == af &&
+ ldp_addrcmp(af, &fnh->nexthop, nexthop) == 0 &&
+ fnh->priority == priority)
+ return (fnh);
+
+ return (NULL);
+}
+
+static struct fec_nh *
+fec_nh_add(struct fec_node *fn, int af, union ldpd_addr *nexthop,
+ uint8_t priority)
+{
+ struct fec_nh *fnh;
+
+ fnh = calloc(1, sizeof(*fnh));
+ if (fnh == NULL)
+ fatal(__func__);
+
+ fnh->af = af;
+ fnh->nexthop = *nexthop;
+ fnh->remote_label = NO_LABEL;
+ fnh->priority = priority;
+ LIST_INSERT_HEAD(&fn->nexthops, fnh, entry);
+
+ return (fnh);
+}
+
+static void
+fec_nh_del(struct fec_nh *fnh)
+{
+ LIST_REMOVE(fnh, entry);
+ free(fnh);
+}
+
+uint32_t
+egress_label(enum fec_type fec_type)
+{
+ switch (fec_type) {
+ case FEC_TYPE_IPV4:
+ if (ldeconf->ipv4.flags & F_LDPD_AF_EXPNULL)
+ return (MPLS_LABEL_IPV4NULL);
+ break;
+ case FEC_TYPE_IPV6:
+ if (ldeconf->ipv6.flags & F_LDPD_AF_EXPNULL)
+ return (MPLS_LABEL_IPV6NULL);
+ break;
+ default:
+ fatalx("egress_label: unexpected fec type");
+ }
+
+ return (MPLS_LABEL_IMPLNULL);
+}
+
+void
+lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop,
+ uint8_t priority, int connected, void *data)
+{
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_map *me;
+ struct lde_nbr *ln;
+
+ fn = (struct fec_node *)fec_find(&ft, fec);
+ if (fn == NULL)
+ fn = fec_add(fec);
+ if (fec_nh_find(fn, af, nexthop, priority) != NULL)
+ return;
+
+ log_debug("lde add fec %s nexthop %s",
+ log_fec(&fn->fec), log_addr(af, nexthop));
+
+ if (fn->fec.type == FEC_TYPE_PWID)
+ fn->data = data;
+
+ if (fn->local_label == NO_LABEL) {
+ if (connected)
+ fn->local_label = egress_label(fn->fec.type);
+ else
+ fn->local_label = lde_assign_label();
+
+ /* FEC.1: perform lsr label distribution procedure */
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 1);
+ }
+
+ fnh = fec_nh_add(fn, af, nexthop, priority);
+ lde_send_change_klabel(fn, fnh);
+
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ case FEC_TYPE_IPV6:
+ ln = lde_nbr_find_by_addr(af, &fnh->nexthop);
+ break;
+ case FEC_TYPE_PWID:
+ ln = lde_nbr_find_by_lsrid(fn->fec.u.pwid.lsr_id);
+ break;
+ default:
+ ln = NULL;
+ break;
+ }
+
+ if (ln) {
+ /* FEC.2 */
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+ if (me)
+ /* FEC.5 */
+ lde_check_mapping(&me->map, ln);
+ }
+}
+
+void
+lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop,
+ uint8_t priority)
+{
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+
+ fn = (struct fec_node *)fec_find(&ft, fec);
+ if (fn == NULL)
+ /* route lost */
+ return;
+ fnh = fec_nh_find(fn, af, nexthop, priority);
+ if (fnh == NULL)
+ /* route lost */
+ return;
+
+ log_debug("lde remove fec %s nexthop %s",
+ log_fec(&fn->fec), log_addr(af, nexthop));
+
+ lde_send_delete_klabel(fn, fnh);
+ fec_nh_del(fnh);
+ if (LIST_EMPTY(&fn->nexthops)) {
+ lde_send_labelwithdraw_all(fn, NO_LABEL);
+ fn->local_label = NO_LABEL;
+ if (fn->fec.type == FEC_TYPE_PWID)
+ fn->data = NULL;
+ }
+}
+
+void
+lde_check_mapping(struct map *map, struct lde_nbr *ln)
+{
+ struct fec fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_req *lre;
+ struct lde_map *me;
+ struct l2vpn_pw *pw;
+ int msgsource = 0;
+
+ lde_map2fec(map, ln->id, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn == NULL)
+ fn = fec_add(&fec);
+
+ /* LMp.1: first check if we have a pending request running */
+ lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec);
+ if (lre)
+ /* LMp.2: delete record of outstanding label request */
+ lde_req_del(ln, lre, 1);
+
+ /* RFC 4447 control word and status tlv negotiation */
+ if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map))
+ return;
+
+ /*
+ * LMp.3 - LMp.8: loop detection - unnecessary for frame-mode
+ * mpls networks.
+ */
+
+ /* LMp.9 */
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+ if (me) {
+ /* LMp.10 */
+ if (me->map.label != map->label && lre == NULL) {
+ /* LMp.10a */
+ lde_send_labelrelease(ln, fn, me->map.label);
+
+ /*
+ * Can not use lde_nbr_find_by_addr() because there's
+ * the possibility of multipath.
+ */
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ if (lde_address_find(ln, fnh->af,
+ &fnh->nexthop) == NULL)
+ continue;
+
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ }
+ }
+ }
+
+ /*
+ * LMp.11 - 12: consider multiple nexthops in order to
+ * support multipath
+ */
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ /* LMp.15: install FEC in FIB */
+ switch (fec.type) {
+ case FEC_TYPE_IPV4:
+ case FEC_TYPE_IPV6:
+ if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
+ continue;
+
+ fnh->remote_label = map->label;
+ lde_send_change_klabel(fn, fnh);
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL)
+ continue;
+
+ pw->remote_group = map->fec.pwid.group_id;
+ if (map->flags & F_MAP_PW_IFMTU)
+ pw->remote_mtu = map->fec.pwid.ifmtu;
+ if (map->flags & F_MAP_PW_STATUS)
+ pw->remote_status = map->pw_status;
+ fnh->remote_label = map->label;
+ if (l2vpn_pw_ok(pw, fnh))
+ lde_send_change_klabel(fn, fnh);
+ break;
+ default:
+ break;
+ }
+
+ msgsource = 1;
+ }
+ /* LMp.13 & LMp.16: Record the mapping from this peer */
+ if (me == NULL)
+ me = lde_map_add(ln, fn, 0);
+ me->map = *map;
+
+ if (msgsource == 0)
+ /* LMp.13: just return since we use liberal lbl retention */
+ return;
+
+ /*
+ * LMp.17 - LMp.27 are unnecessary since we don't need to implement
+ * loop detection. LMp.28 - LMp.30 are unnecessary because we are
+ * merging capable.
+ */
+}
+
+void
+lde_check_request(struct map *map, struct lde_nbr *ln)
+{
+ struct fec fec;
+ struct lde_req *lre;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+
+ /* LRq.1: skip loop detection (not necessary) */
+
+ /* LRq.2: is there a next hop for fec? */
+ lde_map2fec(map, ln->id, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn == NULL || LIST_EMPTY(&fn->nexthops)) {
+ /* LRq.5: send No Route notification */
+ lde_send_notification(ln->peerid, S_NO_ROUTE, map->msg_id,
+ htons(MSG_TYPE_LABELREQUEST));
+ return;
+ }
+
+ /* LRq.3: is MsgSource the next hop? */
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ switch (fec.type) {
+ case FEC_TYPE_IPV4:
+ case FEC_TYPE_IPV6:
+ if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
+ continue;
+
+ /* LRq.4: send Loop Detected notification */
+ lde_send_notification(ln->peerid, S_LOOP_DETECTED,
+ map->msg_id, htons(MSG_TYPE_LABELREQUEST));
+ return;
+ default:
+ break;
+ }
+ }
+
+ /* LRq.6: first check if we have a pending request running */
+ lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec);
+ if (lre != NULL)
+ /* LRq.7: duplicate request */
+ return;
+
+ /* LRq.8: record label request */
+ lre = lde_req_add(ln, &fn->fec, 0);
+ if (lre != NULL)
+ lre->msg_id = ntohl(map->msg_id);
+
+ /* LRq.9: perform LSR label distribution */
+ lde_send_labelmapping(ln, fn, 1);
+
+ /*
+ * LRq.10: do nothing (Request Never) since we use liberal
+ * label retention.
+ * LRq.11 - 12 are unnecessary since we are merging capable.
+ */
+}
+
+void
+lde_check_release(struct map *map, struct lde_nbr *ln)
+{
+ struct fec fec;
+ struct fec_node *fn;
+ struct lde_wdraw *lw;
+ struct lde_map *me;
+
+ /* TODO group wildcard */
+ if (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))
+ return;
+
+ lde_map2fec(map, ln->id, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ /* LRl.1: does FEC match a known FEC? */
+ if (fn == NULL)
+ return;
+
+ /* LRl.3: first check if we have a pending withdraw running */
+ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
+ if (lw && (map->label == NO_LABEL ||
+ (lw->label != NO_LABEL && map->label == lw->label))) {
+ /* LRl.4: delete record of outstanding label withdraw */
+ lde_wdraw_del(ln, lw);
+ }
+
+ /* LRl.6: check sent map list and remove it if available */
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (me && (map->label == NO_LABEL || map->label == me->map.label))
+ lde_map_del(ln, me, 1);
+
+ /*
+ * LRl.11 - 13 are unnecessary since we remove the label from
+ * forwarding/switching as soon as the FEC is unreachable.
+ */
+}
+
+void
+lde_check_release_wcard(struct map *map, struct lde_nbr *ln)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct lde_wdraw *lw;
+ struct lde_map *me;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ /* LRl.3: first check if we have a pending withdraw running */
+ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
+ if (lw && (map->label == NO_LABEL ||
+ (lw->label != NO_LABEL && map->label == lw->label))) {
+ /* LRl.4: delete record of outstanding lbl withdraw */
+ lde_wdraw_del(ln, lw);
+ }
+
+ /* LRl.6: check sent map list and remove it if available */
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (me &&
+ (map->label == NO_LABEL || map->label == me->map.label))
+ lde_map_del(ln, me, 1);
+
+ /*
+ * LRl.11 - 13 are unnecessary since we remove the label from
+ * forwarding/switching as soon as the FEC is unreachable.
+ */
+ }
+}
+
+void
+lde_check_withdraw(struct map *map, struct lde_nbr *ln)
+{
+ struct fec fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_map *me;
+ struct l2vpn_pw *pw;
+
+ /* TODO group wildcard */
+ if (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))
+ return;
+
+ lde_map2fec(map, ln->id, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn == NULL)
+ fn = fec_add(&fec);
+
+ /* LWd.1: remove label from forwarding/switching use */
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ switch (fec.type) {
+ case FEC_TYPE_IPV4:
+ case FEC_TYPE_IPV6:
+ if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
+ continue;
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL)
+ continue;
+ break;
+ default:
+ break;
+ }
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ }
+
+ /* LWd.2: send label release */
+ lde_send_labelrelease(ln, fn, map->label);
+
+ /* LWd.3: check previously received label mapping */
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+ if (me && (map->label == NO_LABEL || map->label == me->map.label))
+ /* LWd.4: remove record of previously received lbl mapping */
+ lde_map_del(ln, me, 0);
+}
+
+void
+lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_map *me;
+
+ /* LWd.2: send label release */
+ lde_send_labelrelease(ln, NULL, map->label);
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ /* LWd.1: remove label from forwarding/switching use */
+ 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;
+ break;
+ case FEC_TYPE_PWID:
+ if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr)
+ continue;
+ break;
+ default:
+ break;
+ }
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ }
+
+ /* LWd.3: check previously received label mapping */
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+ if (me && (map->label == NO_LABEL ||
+ map->label == me->map.label))
+ /*
+ * LWd.4: remove record of previously received
+ * label mapping
+ */
+ lde_map_del(ln, me, 0);
+ }
+}
+
+/* gabage collector timer: timer to remove dead entries from the LIB */
+
+/* ARGSUSED */
+void
+lde_gc_timer(int fd, short event, void *arg)
+{
+ struct fec *fec, *safe;
+ struct fec_node *fn;
+ int count = 0;
+
+ RB_FOREACH_SAFE(fec, fec_tree, &ft, safe) {
+ fn = (struct fec_node *) fec;
+
+ if (!LIST_EMPTY(&fn->nexthops) ||
+ !LIST_EMPTY(&fn->downstream) ||
+ !LIST_EMPTY(&fn->upstream))
+ continue;
+
+ fec_remove(&ft, &fn->fec);
+ free(fn);
+ count++;
+ }
+
+ if (count > 0)
+ log_debug("%s: %u entries removed", __func__, count);
+
+ lde_gc_start_timer();
+}
+
+void
+lde_gc_start_timer(void)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = LDE_GC_INTERVAL;
+ if (evtimer_add(&gc_timer, &tv) == -1)
+ fatal(__func__);
+}
+
+void
+lde_gc_stop_timer(void)
+{
+ if (evtimer_pending(&gc_timer, NULL) &&
+ evtimer_del(&gc_timer) == -1)
+ fatal(__func__);
+}
diff --git a/ldpd/ldp.h b/ldpd/ldp.h
new file mode 100644
index 000000000..77034b30a
--- /dev/null
+++ b/ldpd/ldp.h
@@ -0,0 +1,304 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@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.
+ */
+
+/* LDP protocol definitions */
+
+#ifndef _LDP_H_
+#define _LDP_H_
+
+#include <sys/types.h>
+
+/* misc */
+#define LDP_VERSION 1
+#define LDP_PORT 646
+#define LDP_MAX_LEN 4096
+
+/* All Routers on this Subnet group multicast addresses */
+#define AllRouters_v4 "224.0.0.2"
+#define AllRouters_v6 "ff02::2"
+
+#define LINK_DFLT_HOLDTIME 15
+#define TARGETED_DFLT_HOLDTIME 45
+#define MIN_HOLDTIME 3
+#define MAX_HOLDTIME 0xffff
+#define INFINITE_HOLDTIME 0xffff
+
+#define DEFAULT_KEEPALIVE 180
+#define MIN_KEEPALIVE 3
+#define MAX_KEEPALIVE 0xffff
+#define KEEPALIVE_PER_PERIOD 3
+#define INIT_FSM_TIMEOUT 15
+
+#define DEFAULT_HELLO_INTERVAL 5
+#define MIN_HELLO_INTERVAL 1
+#define MAX_HELLO_INTERVAL 0xffff
+
+#define INIT_DELAY_TMR 15
+#define MAX_DELAY_TMR 120
+
+#define MIN_PWID_ID 1
+#define MAX_PWID_ID 0xffffffff
+
+#define DEFAULT_L2VPN_MTU 1500
+#define MIN_L2VPN_MTU 512
+#define MAX_L2VPN_MTU 0xffff
+
+/* LDP message types */
+#define MSG_TYPE_NOTIFICATION 0x0001
+#define MSG_TYPE_HELLO 0x0100
+#define MSG_TYPE_INIT 0x0200
+#define MSG_TYPE_KEEPALIVE 0x0201
+#define MSG_TYPE_ADDR 0x0300
+#define MSG_TYPE_ADDRWITHDRAW 0x0301
+#define MSG_TYPE_LABELMAPPING 0x0400
+#define MSG_TYPE_LABELREQUEST 0x0401
+#define MSG_TYPE_LABELWITHDRAW 0x0402
+#define MSG_TYPE_LABELRELEASE 0x0403
+#define MSG_TYPE_LABELABORTREQ 0x0404
+
+/* LDP TLV types */
+#define TLV_TYPE_FEC 0x0100
+#define TLV_TYPE_ADDRLIST 0x0101
+#define TLV_TYPE_HOPCOUNT 0x0103
+#define TLV_TYPE_PATHVECTOR 0x0104
+#define TLV_TYPE_GENERICLABEL 0x0200
+#define TLV_TYPE_ATMLABEL 0x0201
+#define TLV_TYPE_FRLABEL 0x0202
+#define TLV_TYPE_STATUS 0x0300
+#define TLV_TYPE_EXTSTATUS 0x0301
+#define TLV_TYPE_RETURNEDPDU 0x0302
+#define TLV_TYPE_RETURNEDMSG 0x0303
+#define TLV_TYPE_COMMONHELLO 0x0400
+#define TLV_TYPE_IPV4TRANSADDR 0x0401
+#define TLV_TYPE_CONFIG 0x0402
+#define TLV_TYPE_IPV6TRANSADDR 0x0403
+#define TLV_TYPE_COMMONSESSION 0x0500
+#define TLV_TYPE_ATMSESSIONPAR 0x0501
+#define TLV_TYPE_FRSESSION 0x0502
+#define TLV_TYPE_LABELREQUEST 0x0600
+/* RFC 4447 */
+#define TLV_TYPE_PW_STATUS 0x096A
+#define TLV_TYPE_PW_IF_PARAM 0x096B
+#define TLV_TYPE_PW_GROUP_ID 0x096C
+/* RFC 7552 */
+#define TLV_TYPE_DUALSTACK 0x8701
+
+/* LDP header */
+struct ldp_hdr {
+ uint16_t version;
+ uint16_t length;
+ uint32_t lsr_id;
+ uint16_t lspace_id;
+} __packed;
+
+#define LDP_HDR_SIZE 10 /* actual size of the LDP header */
+#define LDP_HDR_PDU_LEN 6 /* minimum "PDU Length" */
+#define LDP_HDR_DEAD_LEN 4
+
+/* TLV record */
+struct tlv {
+ uint16_t type;
+ uint16_t length;
+};
+#define TLV_HDR_SIZE 4
+
+struct ldp_msg {
+ uint16_t type;
+ uint16_t length;
+ uint32_t id;
+ /* Mandatory Parameters */
+ /* Optional Parameters */
+} __packed;
+
+#define LDP_MSG_SIZE 8 /* minimum size of LDP message */
+#define LDP_MSG_LEN 4 /* minimum "Message Length" */
+#define LDP_MSG_DEAD_LEN 4
+
+#define UNKNOWN_FLAG 0x8000
+#define FORWARD_FLAG 0xc000
+
+struct hello_prms_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint16_t holdtime;
+ uint16_t flags;
+};
+#define F_HELLO_TARGETED 0x8000
+#define F_HELLO_REQ_TARG 0x4000
+#define F_HELLO_GTSM 0x2000
+
+struct hello_prms_opt4_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint32_t value;
+};
+
+struct hello_prms_opt16_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint8_t value[16];
+};
+
+#define DUAL_STACK_LDPOV4 4
+#define DUAL_STACK_LDPOV6 6
+
+#define F_HELLO_TLV_RCVD_ADDR 0x01
+#define F_HELLO_TLV_RCVD_CONF 0x02
+#define F_HELLO_TLV_RCVD_DS 0x04
+
+#define S_SUCCESS 0x00000000
+#define S_BAD_LDP_ID 0x80000001
+#define S_BAD_PROTO_VER 0x80000002
+#define S_BAD_PDU_LEN 0x80000003
+#define S_UNKNOWN_MSG 0x00000004
+#define S_BAD_MSG_LEN 0x80000005
+#define S_UNKNOWN_TLV 0x00000006
+#define S_BAD_TLV_LEN 0x80000007
+#define S_BAD_TLV_VAL 0x80000008
+#define S_HOLDTIME_EXP 0x80000009
+#define S_SHUTDOWN 0x8000000A
+#define S_LOOP_DETECTED 0x0000000B
+#define S_UNKNOWN_FEC 0x0000000C
+#define S_NO_ROUTE 0x0000000D
+#define S_NO_LABEL_RES 0x0000000E
+#define S_AVAILABLE 0x0000000F
+#define S_NO_HELLO 0x80000010
+#define S_PARM_ADV_MODE 0x80000011
+#define S_MAX_PDU_LEN 0x80000012
+#define S_PARM_L_RANGE 0x80000013
+#define S_KEEPALIVE_TMR 0x80000014
+#define S_LAB_REQ_ABRT 0x00000015
+#define S_MISS_MSG 0x00000016
+#define S_UNSUP_ADDR 0x00000017
+#define S_KEEPALIVE_BAD 0x80000018
+#define S_INTERN_ERR 0x80000019
+/* RFC 4447 */
+#define S_ILLEGAL_CBIT 0x00000024
+#define S_WRONG_CBIT 0x00000025
+#define S_INCPT_BITRATE 0x00000026
+#define S_CEP_MISCONF 0x00000027
+#define S_PW_STATUS 0x00000028
+#define S_UNASSIGN_TAI 0x00000029
+#define S_MISCONF_ERR 0x0000002A
+#define S_WITHDRAW_MTHD 0x0000002B
+/* RFC 7552 */
+#define S_TRANS_MISMTCH 0x80000032
+#define S_DS_NONCMPLNCE 0x80000033
+
+struct sess_prms_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint16_t proto_version;
+ uint16_t keepalive_time;
+ uint8_t reserved;
+ uint8_t pvlim;
+ uint16_t max_pdu_len;
+ uint32_t lsr_id;
+ uint16_t lspace_id;
+} __packed;
+
+#define SESS_PRMS_SIZE 18
+#define SESS_PRMS_LEN 14
+
+struct status_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint32_t status_code;
+ uint32_t msg_id;
+ uint16_t msg_type;
+} __packed;
+
+#define STATUS_SIZE 14
+#define STATUS_TLV_LEN 10
+#define STATUS_FATAL 0x80000000
+
+#define AF_IPV4 0x1
+#define AF_IPV6 0x2
+
+struct address_list_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint16_t family;
+ /* address entries */
+} __packed;
+
+#define ADDR_LIST_SIZE 6
+
+#define FEC_ELM_WCARD_LEN 1
+#define FEC_ELM_PREFIX_MIN_LEN 4
+#define FEC_PWID_ELM_MIN_LEN 8
+
+#define MAP_TYPE_WILDCARD 0x01
+#define MAP_TYPE_PREFIX 0x02
+#define MAP_TYPE_PWID 0x80
+#define MAP_TYPE_GENPWID 0x81
+
+#define CONTROL_WORD_FLAG 0x8000
+#define PW_TYPE_ETHERNET_TAGGED 0x0004
+#define PW_TYPE_ETHERNET 0x0005
+#define DEFAULT_PW_TYPE PW_TYPE_ETHERNET
+
+/* RFC 4447 Sub-TLV record */
+struct subtlv {
+ uint8_t type;
+ uint8_t length;
+};
+#define SUBTLV_HDR_SIZE 2
+
+#define SUBTLV_IFMTU 0x01
+#define SUBTLV_VLANID 0x06
+
+#define FEC_SUBTLV_IFMTU_SIZE 4
+#define FEC_SUBTLV_VLANID_SIZE 4
+
+struct label_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint32_t label;
+};
+#define LABEL_TLV_SIZE 8
+#define LABEL_TLV_LEN 4
+
+struct reqid_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint32_t reqid;
+};
+#define REQID_TLV_SIZE 8
+#define REQID_TLV_LEN 4
+
+struct pw_status_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint32_t value;
+};
+#define PW_STATUS_TLV_SIZE 8
+#define PW_STATUS_TLV_LEN 4
+
+#define PW_FORWARDING 0
+#define PW_NOT_FORWARDING (1 << 0)
+#define PW_LOCAL_RX_FAULT (1 << 1)
+#define PW_LOCAL_TX_FAULT (1 << 2)
+#define PW_PSN_RX_FAULT (1 << 3)
+#define PW_PSN_TX_FAULT (1 << 4)
+
+#define NO_LABEL UINT32_MAX
+
+#endif /* !_LDP_H_ */
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);
+}
diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h
new file mode 100644
index 000000000..d32d23c6c
--- /dev/null
+++ b/ldpd/ldpd.h
@@ -0,0 +1,606 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2004 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.
+ */
+
+#ifndef _LDPD_H_
+#define _LDPD_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <event.h>
+#include <imsg.h>
+
+#include "ldp.h"
+
+#define CONF_FILE "/etc/ldpd.conf"
+#define LDPD_SOCKET "/var/run/ldpd.sock"
+#define LDPD_USER "_ldpd"
+
+#define LDPD_OPT_VERBOSE 0x00000001
+#define LDPD_OPT_VERBOSE2 0x00000002
+#define LDPD_OPT_NOACTION 0x00000004
+
+#define TCP_MD5_KEY_LEN 80
+#define L2VPN_NAME_LEN 32
+
+#define RT_BUF_SIZE 16384
+#define MAX_RTSOCK_BUF 128 * 1024
+#define LDP_BACKLOG 128
+
+#define F_LDPD_INSERTED 0x0001
+#define F_CONNECTED 0x0002
+#define F_STATIC 0x0004
+#define F_DYNAMIC 0x0008
+#define F_REJECT 0x0010
+#define F_BLACKHOLE 0x0020
+#define F_REDISTRIBUTED 0x0040
+
+struct evbuf {
+ struct msgbuf wbuf;
+ struct event ev;
+};
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler)(int, short, void *);
+ struct event ev;
+ short events;
+};
+
+enum imsg_type {
+ IMSG_NONE,
+ IMSG_CTL_RELOAD,
+ IMSG_CTL_SHOW_INTERFACE,
+ IMSG_CTL_SHOW_DISCOVERY,
+ IMSG_CTL_SHOW_NBR,
+ IMSG_CTL_SHOW_LIB,
+ IMSG_CTL_SHOW_L2VPN_PW,
+ IMSG_CTL_SHOW_L2VPN_BINDING,
+ IMSG_CTL_CLEAR_NBR,
+ IMSG_CTL_FIB_COUPLE,
+ IMSG_CTL_FIB_DECOUPLE,
+ IMSG_CTL_KROUTE,
+ IMSG_CTL_KROUTE_ADDR,
+ IMSG_CTL_IFINFO,
+ IMSG_CTL_END,
+ IMSG_CTL_LOG_VERBOSE,
+ IMSG_KLABEL_CHANGE,
+ IMSG_KLABEL_DELETE,
+ IMSG_KPWLABEL_CHANGE,
+ IMSG_KPWLABEL_DELETE,
+ IMSG_IFSTATUS,
+ IMSG_NEWADDR,
+ IMSG_DELADDR,
+ IMSG_LABEL_MAPPING,
+ IMSG_LABEL_MAPPING_FULL,
+ IMSG_LABEL_REQUEST,
+ IMSG_LABEL_RELEASE,
+ IMSG_LABEL_WITHDRAW,
+ IMSG_LABEL_ABORT,
+ IMSG_REQUEST_ADD,
+ IMSG_REQUEST_ADD_END,
+ IMSG_MAPPING_ADD,
+ IMSG_MAPPING_ADD_END,
+ IMSG_RELEASE_ADD,
+ IMSG_RELEASE_ADD_END,
+ IMSG_WITHDRAW_ADD,
+ IMSG_WITHDRAW_ADD_END,
+ IMSG_ADDRESS_ADD,
+ IMSG_ADDRESS_DEL,
+ IMSG_NOTIFICATION,
+ IMSG_NOTIFICATION_SEND,
+ IMSG_NEIGHBOR_UP,
+ IMSG_NEIGHBOR_DOWN,
+ IMSG_NETWORK_ADD,
+ IMSG_NETWORK_DEL,
+ IMSG_SOCKET_IPC,
+ IMSG_SOCKET_NET,
+ IMSG_CLOSE_SOCKETS,
+ IMSG_REQUEST_SOCKETS,
+ IMSG_SETUP_SOCKETS,
+ IMSG_RECONF_CONF,
+ IMSG_RECONF_IFACE,
+ IMSG_RECONF_TNBR,
+ IMSG_RECONF_NBRP,
+ IMSG_RECONF_L2VPN,
+ IMSG_RECONF_L2VPN_IF,
+ IMSG_RECONF_L2VPN_PW,
+ IMSG_RECONF_END
+};
+
+union ldpd_addr {
+ struct in_addr v4;
+ struct in6_addr v6;
+};
+
+#define IN6_IS_SCOPE_EMBED(a) \
+ ((IN6_IS_ADDR_LINKLOCAL(a)) || \
+ (IN6_IS_ADDR_MC_LINKLOCAL(a)) || \
+ (IN6_IS_ADDR_MC_INTFACELOCAL(a)))
+
+/* interface states */
+#define IF_STA_DOWN 0x01
+#define IF_STA_ACTIVE 0x02
+
+/* targeted neighbor states */
+#define TNBR_STA_DOWN 0x01
+#define TNBR_STA_ACTIVE 0x02
+
+/* interface types */
+enum iface_type {
+ IF_TYPE_POINTOPOINT,
+ IF_TYPE_BROADCAST
+};
+
+/* neighbor states */
+#define NBR_STA_PRESENT 0x0001
+#define NBR_STA_INITIAL 0x0002
+#define NBR_STA_OPENREC 0x0004
+#define NBR_STA_OPENSENT 0x0008
+#define NBR_STA_OPER 0x0010
+#define NBR_STA_SESSION (NBR_STA_INITIAL | NBR_STA_OPENREC | \
+ NBR_STA_OPENSENT | NBR_STA_OPER)
+
+/* neighbor events */
+enum nbr_event {
+ NBR_EVT_NOTHING,
+ NBR_EVT_MATCH_ADJ,
+ NBR_EVT_CONNECT_UP,
+ NBR_EVT_CLOSE_SESSION,
+ NBR_EVT_INIT_RCVD,
+ NBR_EVT_KEEPALIVE_RCVD,
+ NBR_EVT_PDU_RCVD,
+ NBR_EVT_PDU_SENT,
+ NBR_EVT_INIT_SENT
+};
+
+/* neighbor actions */
+enum nbr_action {
+ NBR_ACT_NOTHING,
+ NBR_ACT_RST_KTIMEOUT,
+ NBR_ACT_SESSION_EST,
+ NBR_ACT_RST_KTIMER,
+ NBR_ACT_CONNECT_SETUP,
+ NBR_ACT_PASSIVE_INIT,
+ NBR_ACT_KEEPALIVE_SEND,
+ NBR_ACT_CLOSE_SESSION
+};
+
+TAILQ_HEAD(mapping_head, mapping_entry);
+
+struct map {
+ uint8_t type;
+ uint32_t msg_id;
+ union {
+ struct {
+ uint16_t af;
+ union ldpd_addr prefix;
+ uint8_t prefixlen;
+ } prefix;
+ struct {
+ uint16_t type;
+ uint32_t pwid;
+ uint32_t group_id;
+ uint16_t ifmtu;
+ } pwid;
+ } fec;
+ struct {
+ uint32_t status_code;
+ uint32_t msg_id;
+ uint16_t msg_type;
+ } st;
+ uint32_t label;
+ uint32_t requestid;
+ uint32_t pw_status;
+ uint8_t flags;
+};
+#define F_MAP_REQ_ID 0x01 /* optional request message id present */
+#define F_MAP_STATUS 0x02 /* status */
+#define F_MAP_PW_CWORD 0x04 /* pseudowire control word */
+#define F_MAP_PW_ID 0x08 /* pseudowire connection id */
+#define F_MAP_PW_IFMTU 0x10 /* pseudowire interface parameter */
+#define F_MAP_PW_STATUS 0x20 /* pseudowire status */
+
+struct notify_msg {
+ uint32_t status_code;
+ uint32_t msg_id; /* network byte order */
+ uint16_t msg_type; /* network byte order */
+ uint32_t pw_status;
+ struct map fec;
+ uint8_t flags;
+};
+#define F_NOTIF_PW_STATUS 0x01 /* pseudowire status tlv present */
+#define F_NOTIF_FEC 0x02 /* fec tlv present */
+
+struct if_addr {
+ LIST_ENTRY(if_addr) entry;
+ int af;
+ union ldpd_addr addr;
+ uint8_t prefixlen;
+ union ldpd_addr dstbrd;
+};
+LIST_HEAD(if_addr_head, if_addr);
+
+struct iface_af {
+ struct iface *iface;
+ int af;
+ int enabled;
+ int state;
+ LIST_HEAD(, adj) adj_list;
+ time_t uptime;
+ struct event hello_timer;
+ uint16_t hello_holdtime;
+ uint16_t hello_interval;
+};
+
+struct iface {
+ LIST_ENTRY(iface) entry;
+ char name[IF_NAMESIZE];
+ unsigned int ifindex;
+ struct if_addr_head addr_list;
+ struct in6_addr linklocal;
+ enum iface_type type;
+ uint8_t if_type;
+ uint16_t flags;
+ uint8_t linkstate;
+ struct iface_af ipv4;
+ struct iface_af ipv6;
+};
+
+/* source of targeted hellos */
+struct tnbr {
+ LIST_ENTRY(tnbr) entry;
+ struct event hello_timer;
+ struct adj *adj;
+ int af;
+ union ldpd_addr addr;
+ int state;
+ uint16_t hello_holdtime;
+ uint16_t hello_interval;
+ uint16_t pw_count;
+ uint8_t flags;
+};
+#define F_TNBR_CONFIGURED 0x01
+#define F_TNBR_DYNAMIC 0x02
+
+enum auth_method {
+ AUTH_NONE,
+ AUTH_MD5SIG
+};
+
+/* neighbor specific parameters */
+struct nbr_params {
+ LIST_ENTRY(nbr_params) entry;
+ struct in_addr lsr_id;
+ uint16_t keepalive;
+ int gtsm_enabled;
+ uint8_t gtsm_hops;
+ struct {
+ enum auth_method method;
+ char md5key[TCP_MD5_KEY_LEN];
+ uint8_t md5key_len;
+ } auth;
+ uint8_t flags;
+};
+#define F_NBRP_KEEPALIVE 0x01
+#define F_NBRP_GTSM 0x02
+#define F_NBRP_GTSM_HOPS 0x04
+
+struct l2vpn_if {
+ LIST_ENTRY(l2vpn_if) entry;
+ struct l2vpn *l2vpn;
+ char ifname[IF_NAMESIZE];
+ unsigned int ifindex;
+ uint16_t flags;
+ uint8_t link_state;
+};
+
+struct l2vpn_pw {
+ LIST_ENTRY(l2vpn_pw) entry;
+ struct l2vpn *l2vpn;
+ struct in_addr lsr_id;
+ int af;
+ union ldpd_addr addr;
+ uint32_t pwid;
+ char ifname[IF_NAMESIZE];
+ unsigned int ifindex;
+ uint32_t remote_group;
+ uint16_t remote_mtu;
+ uint32_t remote_status;
+ uint8_t flags;
+};
+#define F_PW_STATUSTLV_CONF 0x01 /* status tlv configured */
+#define F_PW_STATUSTLV 0x02 /* status tlv negotiated */
+#define F_PW_CWORD_CONF 0x04 /* control word configured */
+#define F_PW_CWORD 0x08 /* control word negotiated */
+#define F_PW_STATUS_UP 0x10 /* pseudowire is operational */
+
+struct l2vpn {
+ LIST_ENTRY(l2vpn) entry;
+ char name[L2VPN_NAME_LEN];
+ int type;
+ int pw_type;
+ int mtu;
+ char br_ifname[IF_NAMESIZE];
+ unsigned int br_ifindex;
+ LIST_HEAD(, l2vpn_if) if_list;
+ LIST_HEAD(, l2vpn_pw) pw_list;
+};
+#define L2VPN_TYPE_VPWS 1
+#define L2VPN_TYPE_VPLS 2
+
+/* ldp_conf */
+enum ldpd_process {
+ PROC_MAIN,
+ PROC_LDP_ENGINE,
+ PROC_LDE_ENGINE
+} ldpd_process;
+
+enum socket_type {
+ LDP_SOCKET_DISC,
+ LDP_SOCKET_EDISC,
+ LDP_SOCKET_SESSION
+};
+
+enum hello_type {
+ HELLO_LINK,
+ HELLO_TARGETED
+};
+
+struct ldpd_af_conf {
+ uint16_t keepalive;
+ uint16_t thello_holdtime;
+ uint16_t thello_interval;
+ union ldpd_addr trans_addr;
+ int flags;
+};
+#define F_LDPD_AF_ENABLED 0x0001
+#define F_LDPD_AF_THELLO_ACCEPT 0x0002
+#define F_LDPD_AF_EXPNULL 0x0004
+#define F_LDPD_AF_NO_GTSM 0x0008
+
+struct ldpd_conf {
+ struct in_addr rtr_id;
+ struct ldpd_af_conf ipv4;
+ struct ldpd_af_conf ipv6;
+ LIST_HEAD(, iface) iface_list;
+ LIST_HEAD(, tnbr) tnbr_list;
+ LIST_HEAD(, nbr_params) nbrp_list;
+ LIST_HEAD(, l2vpn) l2vpn_list;
+ uint16_t trans_pref;
+ int flags;
+};
+#define F_LDPD_NO_FIB_UPDATE 0x0001
+#define F_LDPD_DS_CISCO_INTEROP 0x0002
+
+struct ldpd_af_global {
+ struct event disc_ev;
+ struct event edisc_ev;
+ int ldp_disc_socket;
+ int ldp_edisc_socket;
+ int ldp_session_socket;
+};
+
+struct ldpd_global {
+ int cmd_opts;
+ time_t uptime;
+ struct ldpd_af_global ipv4;
+ struct ldpd_af_global ipv6;
+ uint32_t conf_seqnum;
+ int pfkeysock;
+ struct if_addr_head addr_list;
+ LIST_HEAD(, adj) adj_list;
+ struct in_addr mcast_addr_v4;
+ struct in6_addr mcast_addr_v6;
+ TAILQ_HEAD(, pending_conn) pending_conns;
+};
+
+/* kroute */
+struct kroute {
+ int af;
+ union ldpd_addr prefix;
+ uint8_t prefixlen;
+ union ldpd_addr nexthop;
+ uint32_t local_label;
+ uint32_t remote_label;
+ unsigned short ifindex;
+ uint8_t priority;
+ uint16_t flags;
+};
+
+struct kpw {
+ unsigned short ifindex;
+ int pw_type;
+ int af;
+ union ldpd_addr nexthop;
+ uint32_t local_label;
+ uint32_t remote_label;
+ uint8_t flags;
+};
+
+struct kaddr {
+ unsigned short ifindex;
+ int af;
+ union ldpd_addr addr;
+ uint8_t prefixlen;
+ union ldpd_addr dstbrd;
+};
+
+struct kif {
+ char ifname[IF_NAMESIZE];
+ unsigned short ifindex;
+ int flags;
+ uint8_t link_state;
+ int mtu;
+ uint8_t if_type;
+ uint64_t baudrate;
+};
+
+/* control data structures */
+struct ctl_iface {
+ int af;
+ char name[IF_NAMESIZE];
+ unsigned int ifindex;
+ int state;
+ uint16_t flags;
+ uint8_t linkstate;
+ enum iface_type type;
+ uint8_t if_type;
+ uint16_t hello_holdtime;
+ uint16_t hello_interval;
+ time_t uptime;
+ uint16_t adj_cnt;
+};
+
+struct ctl_adj {
+ int af;
+ struct in_addr id;
+ enum hello_type type;
+ char ifname[IF_NAMESIZE];
+ union ldpd_addr src_addr;
+ uint16_t holdtime;
+ union ldpd_addr trans_addr;
+};
+
+struct ctl_nbr {
+ int af;
+ struct in_addr id;
+ union ldpd_addr laddr;
+ union ldpd_addr raddr;
+ time_t uptime;
+ int nbr_state;
+};
+
+struct ctl_rt {
+ int af;
+ union ldpd_addr prefix;
+ uint8_t prefixlen;
+ struct in_addr nexthop; /* lsr-id */
+ uint32_t local_label;
+ uint32_t remote_label;
+ uint8_t flags;
+ uint8_t in_use;
+};
+
+struct ctl_pw {
+ uint16_t type;
+ char ifname[IF_NAMESIZE];
+ uint32_t pwid;
+ struct in_addr lsr_id;
+ uint32_t local_label;
+ uint32_t local_gid;
+ uint16_t local_ifmtu;
+ uint32_t remote_label;
+ uint32_t remote_gid;
+ uint16_t remote_ifmtu;
+ uint32_t status;
+};
+
+extern struct ldpd_conf *ldpd_conf;
+extern struct ldpd_global global;
+
+/* parse.y */
+struct ldpd_conf *parse_config(char *);
+int cmdline_symset(char *);
+
+/* kroute.c */
+int kif_init(void);
+int kr_init(int);
+void kif_redistribute(const char *);
+int kr_change(struct kroute *);
+int kr_delete(struct kroute *);
+void kr_shutdown(void);
+void kr_fib_couple(void);
+void kr_fib_decouple(void);
+void kr_change_egress_label(int, int);
+void kr_show_route(struct imsg *);
+void kr_ifinfo(char *, pid_t);
+struct kif *kif_findname(char *);
+void kif_clear(void);
+int kmpw_set(struct kpw *);
+int kmpw_unset(struct kpw *);
+
+/* util.c */
+uint8_t mask2prefixlen(in_addr_t);
+uint8_t mask2prefixlen6(struct sockaddr_in6 *);
+in_addr_t prefixlen2mask(uint8_t);
+struct in6_addr *prefixlen2mask6(uint8_t);
+void ldp_applymask(int, union ldpd_addr *,
+ const union ldpd_addr *, int);
+int ldp_addrcmp(int, const union ldpd_addr *,
+ const union ldpd_addr *);
+int ldp_addrisset(int, const union ldpd_addr *);
+int ldp_prefixcmp(int, const union ldpd_addr *,
+ const union ldpd_addr *, uint8_t);
+int bad_addr_v4(struct in_addr);
+int bad_addr_v6(struct in6_addr *);
+int bad_addr(int, union ldpd_addr *);
+void embedscope(struct sockaddr_in6 *);
+void recoverscope(struct sockaddr_in6 *);
+void addscope(struct sockaddr_in6 *, uint32_t);
+void clearscope(struct in6_addr *);
+struct sockaddr *addr2sa(int af, union ldpd_addr *, uint16_t);
+void sa2addr(struct sockaddr *, int *, union ldpd_addr *);
+
+/* ldpd.c */
+void main_imsg_compose_ldpe(int, pid_t, void *, uint16_t);
+void main_imsg_compose_lde(int, pid_t, void *, uint16_t);
+void imsg_event_add(struct imsgev *);
+int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t,
+ int, void *, uint16_t);
+void evbuf_enqueue(struct evbuf *, struct ibuf *);
+void evbuf_event_add(struct evbuf *);
+void evbuf_init(struct evbuf *, int, void (*)(int, short, void *), void *);
+void evbuf_clear(struct evbuf *);
+struct ldpd_af_conf *ldp_af_conf_get(struct ldpd_conf *, int);
+struct ldpd_af_global *ldp_af_global_get(struct ldpd_global *, int);
+int ldp_is_dual_stack(struct ldpd_conf *);
+void merge_config(struct ldpd_conf *, struct ldpd_conf *);
+struct ldpd_conf *config_new_empty(void);
+void config_clear(struct ldpd_conf *);
+
+/* socket.c */
+int ldp_create_socket(int, enum socket_type);
+void sock_set_recvbuf(int);
+int sock_set_reuse(int, int);
+int sock_set_bindany(int, int);
+int sock_set_ipv4_tos(int, int);
+int sock_set_ipv4_recvif(int, int);
+int sock_set_ipv4_minttl(int, int);
+int sock_set_ipv4_ucast_ttl(int fd, int);
+int sock_set_ipv4_mcast_ttl(int, uint8_t);
+int sock_set_ipv4_mcast(struct iface *);
+int sock_set_ipv4_mcast_loop(int);
+int sock_set_ipv6_dscp(int, int);
+int sock_set_ipv6_pktinfo(int, int);
+int sock_set_ipv6_minhopcount(int, int);
+int sock_set_ipv6_ucast_hops(int, int);
+int sock_set_ipv6_mcast_hops(int, int);
+int sock_set_ipv6_mcast(struct iface *);
+int sock_set_ipv6_mcast_loop(int);
+
+/* printconf.c */
+void print_config(struct ldpd_conf *);
+
+#endif /* _LDPD_H_ */
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
new file mode 100644
index 000000000..1b4d2a67e
--- /dev/null
+++ b/ldpd/ldpe.c
@@ -0,0 +1,808 @@
+/* $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 <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "control.h"
+#include "log.h"
+
+static void ldpe_sig_handler(int, short, void *);
+static __dead void ldpe_shutdown(void);
+static void ldpe_dispatch_main(int, short, void *);
+static void ldpe_dispatch_lde(int, short, void *);
+static void ldpe_dispatch_pfkey(int, short, void *);
+static void ldpe_setup_sockets(int, int, int, int);
+static void ldpe_close_sockets(int);
+static void ldpe_iface_af_ctl(struct ctl_conn *, int, unsigned int);
+
+struct ldpd_conf *leconf;
+struct ldpd_sysdep sysdep;
+
+static struct imsgev *iev_main;
+static struct imsgev *iev_lde;
+static struct event pfkey_ev;
+
+/* ARGSUSED */
+static void
+ldpe_sig_handler(int sig, short event, void *bula)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ ldpe_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+/* label distribution protocol engine */
+void
+ldpe(int debug, int verbose)
+{
+ struct passwd *pw;
+ struct event ev_sigint, ev_sigterm;
+
+ leconf = config_new_empty();
+
+ log_init(debug);
+ log_verbose(verbose);
+
+ setproctitle("ldp engine");
+ ldpd_process = PROC_LDP_ENGINE;
+
+ /* create ldpd control socket outside chroot */
+ if (control_init() == -1)
+ fatalx("control socket setup failed");
+
+ LIST_INIT(&global.addr_list);
+ LIST_INIT(&global.adj_list);
+ 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");
+ global.pfkeysock = pfkey_init();
+
+ if ((pw = getpwnam(LDPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ if (pledge("stdio cpath inet mcast recvfd", NULL) == -1)
+ fatal("pledge");
+
+ event_init();
+ accept_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, ldpe_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, ldpe_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ /* setup pipe and event handler to the parent process */
+ if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_main->ibuf, 3);
+ iev_main->handler = ldpe_dispatch_main;
+ iev_main->events = EV_READ;
+ event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
+ iev_main->handler, iev_main);
+ event_add(&iev_main->ev, NULL);
+
+ if (sysdep.no_pfkey == 0) {
+ event_set(&pfkey_ev, global.pfkeysock, EV_READ | EV_PERSIST,
+ ldpe_dispatch_pfkey, NULL);
+ event_add(&pfkey_ev, NULL);
+ }
+
+ /* 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;
+
+ /* listen on ldpd control socket */
+ TAILQ_INIT(&ctl_conns);
+ control_listen();
+
+ if ((pkt_ptr = calloc(1, IBUF_READ_SIZE)) == NULL)
+ fatal(__func__);
+
+ event_dispatch();
+
+ ldpe_shutdown();
+}
+
+static __dead void
+ldpe_shutdown(void)
+{
+ struct if_addr *if_addr;
+ struct adj *adj;
+
+ /* close pipes */
+ msgbuf_write(&iev_lde->ibuf.w);
+ msgbuf_clear(&iev_lde->ibuf.w);
+ close(iev_lde->ibuf.fd);
+ msgbuf_write(&iev_main->ibuf.w);
+ msgbuf_clear(&iev_main->ibuf.w);
+ close(iev_main->ibuf.fd);
+
+ control_cleanup();
+ config_clear(leconf);
+
+ if (sysdep.no_pfkey == 0) {
+ event_del(&pfkey_ev);
+ close(global.pfkeysock);
+ }
+ 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);
+ free(if_addr);
+ }
+ while ((adj = LIST_FIRST(&global.adj_list)) != NULL)
+ adj_del(adj, S_SHUTDOWN);
+
+ /* clean up */
+ free(iev_lde);
+ free(iev_main);
+ free(pkt_ptr);
+
+ log_info("ldp engine exiting");
+ exit(0);
+}
+
+/* imesg */
+int
+ldpe_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen));
+}
+
+int
+ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data,
+ uint16_t datalen)
+{
+ return (imsg_compose_event(iev_lde, type, peerid, pid, -1,
+ data, datalen));
+}
+
+/* ARGSUSED */
+static void
+ldpe_dispatch_main(int fd, short event, void *bula)
+{
+ static struct ldpd_conf *nconf;
+ struct iface *niface;
+ struct tnbr *ntnbr;
+ struct nbr_params *nnbrp;
+ static struct l2vpn *nl2vpn;
+ struct l2vpn_if *nlif;
+ struct l2vpn_pw *npw;
+ struct imsg imsg;
+ struct imsgev *iev = bula;
+ 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;
+ struct nbr_params *nbrp;
+ int n, 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("ldpe_dispatch_main: msgbuf_write");
+ if (n == 0)
+ 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(leconf, kif->ifindex);
+ if (!iface)
+ break;
+
+ iface->flags = kif->flags;
+ iface->linkstate = kif->link_state;
+ if_update(iface, AF_UNSPEC);
+ 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 = ldpe_dispatch_lde;
+ 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);
+ 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);
+ pfkey_remove(nbr);
+ }
+ 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;
+ nbrp = nbr_params_find(leconf, nbr->id);
+ if (nbrp && pfkey_establish(nbr, nbrp) == -1)
+ fatalx("pfkey setup failed");
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ break;
+ case IMSG_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct ldpd_conf))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct ldpd_conf));
+
+ LIST_INIT(&nconf->iface_list);
+ LIST_INIT(&nconf->tnbr_list);
+ LIST_INIT(&nconf->nbrp_list);
+ LIST_INIT(&nconf->l2vpn_list);
+ break;
+ case IMSG_RECONF_IFACE:
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ LIST_INIT(&niface->addr_list);
+ LIST_INIT(&niface->ipv4.adj_list);
+ LIST_INIT(&niface->ipv6.adj_list);
+ niface->ipv4.iface = niface;
+ niface->ipv6.iface = niface;
+
+ LIST_INSERT_HEAD(&nconf->iface_list, niface, entry);
+ break;
+ case IMSG_RECONF_TNBR:
+ if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL)
+ fatal(NULL);
+ memcpy(ntnbr, imsg.data, sizeof(struct tnbr));
+
+ LIST_INSERT_HEAD(&nconf->tnbr_list, ntnbr, entry);
+ break;
+ case IMSG_RECONF_NBRP:
+ if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL)
+ fatal(NULL);
+ memcpy(nnbrp, imsg.data, sizeof(struct nbr_params));
+
+ LIST_INSERT_HEAD(&nconf->nbrp_list, nnbrp, entry);
+ break;
+ case IMSG_RECONF_L2VPN:
+ if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL)
+ fatal(NULL);
+ memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn));
+
+ LIST_INIT(&nl2vpn->if_list);
+ LIST_INIT(&nl2vpn->pw_list);
+
+ LIST_INSERT_HEAD(&nconf->l2vpn_list, nl2vpn, entry);
+ break;
+ case IMSG_RECONF_L2VPN_IF:
+ if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL)
+ fatal(NULL);
+ memcpy(nlif, imsg.data, sizeof(struct l2vpn_if));
+
+ nlif->l2vpn = nl2vpn;
+ LIST_INSERT_HEAD(&nl2vpn->if_list, nlif, entry);
+ break;
+ case IMSG_RECONF_L2VPN_PW:
+ if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL)
+ fatal(NULL);
+ memcpy(npw, imsg.data, sizeof(struct l2vpn_pw));
+
+ npw->l2vpn = nl2vpn;
+ LIST_INSERT_HEAD(&nl2vpn->pw_list, npw, entry);
+ break;
+ case IMSG_RECONF_END:
+ merge_config(leconf, nconf);
+ nconf = NULL;
+ global.conf_seqnum++;
+ break;
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_KROUTE_ADDR:
+ case IMSG_CTL_IFINFO:
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("ldpe_dispatch_main: error handling imsg %d",
+ 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
+ldpe_dispatch_lde(int fd, short event, void *bula)
+{
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ struct map map;
+ struct notify_msg nm;
+ int n, shut = 0;
+ struct nbr *nbr = NULL;
+
+ 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("ldpe_dispatch_lde: msgbuf_write");
+ if (n == 0)
+ 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(map))
+ fatalx("invalid size of map request");
+ memcpy(&map, imsg.data, sizeof(map));
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("ldpe_dispatch_lde: cannot find "
+ "neighbor");
+ 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) {
+ log_debug("ldpe_dispatch_lde: cannot find "
+ "neighbor");
+ 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(nm))
+ fatalx("invalid size of OE request");
+ memcpy(&nm, imsg.data, sizeof(nm));
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("ldpe_dispatch_lde: cannot find "
+ "neighbor");
+ break;
+ }
+ if (nbr->state != NBR_STA_OPER)
+ break;
+
+ send_notification_full(nbr->tcp, &nm);
+ break;
+ case IMSG_CTL_END:
+ case IMSG_CTL_SHOW_LIB:
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("ldpe_dispatch_lde: error handling imsg %d",
+ 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
+ldpe_dispatch_pfkey(int fd, short event, void *bula)
+{
+ if (event & EV_READ) {
+ if (pfkey_read(fd, NULL) == -1) {
+ fatal("pfkey_read failed, exiting...");
+ }
+ }
+}
+
+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;
+ event_set(&af_global->disc_ev, af_global->ldp_disc_socket,
+ EV_READ|EV_PERSIST, disc_recv_packet, NULL);
+ event_add(&af_global->disc_ev, NULL);
+
+ /* extended discovery socket */
+ af_global->ldp_edisc_socket = edisc_socket;
+ event_set(&af_global->edisc_ev, af_global->ldp_edisc_socket,
+ EV_READ|EV_PERSIST, disc_recv_packet, NULL);
+ event_add(&af_global->edisc_ev, NULL);
+
+ /* 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 */
+ if (event_initialized(&af_global->disc_ev))
+ event_del(&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 */
+ if (event_initialized(&af_global->edisc_ev))
+ event_del(&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;
+ }
+}
+
+void
+ldpe_reset_nbrs(int af)
+{
+ struct nbr *nbr;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (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;
+
+ LIST_FOREACH_SAFE(tnbr, &leconf->tnbr_list, entry, safe) {
+ if (tnbr->af != af)
+ continue;
+
+ tnbr->flags &= ~F_TNBR_DYNAMIC;
+ tnbr_check(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, unsigned int idx)
+{
+ struct iface *iface;
+ struct iface_af *ia;
+ struct ctl_iface *ictl;
+
+ LIST_FOREACH(iface, &leconf->iface_list, entry) {
+ 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, unsigned int 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 nbr *nbr;
+ struct adj *adj;
+ struct ctl_adj *actl;
+
+ RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) {
+ LIST_FOREACH(adj, &nbr->adj_list, nbr_entry) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY,
+ 0, 0, -1, actl, sizeof(struct ctl_adj));
+ }
+ }
+ /* show adjacencies not associated with any neighbor */
+ LIST_FOREACH(adj, &global.adj_list, global_entry) {
+ if (adj->nbr != NULL)
+ continue;
+
+ 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_nbr_ctl(struct ctl_conn *c)
+{
+ struct nbr *nbr;
+ struct ctl_nbr *nctl;
+
+ RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) {
+ nctl = nbr_to_ctl(nbr);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 0, -1, nctl,
+ sizeof(struct ctl_nbr));
+ }
+ 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);
+ free(me);
+ }
+}
diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h
new file mode 100644
index 000000000..e640dd67d
--- /dev/null
+++ b/ldpd/ldpe.h
@@ -0,0 +1,282 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@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.
+ */
+
+#ifndef _LDPE_H_
+#define _LDPE_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <net/pfkeyv2.h>
+
+#include "ldpd.h"
+
+#define min(x,y) ((x) <= (y) ? (x) : (y))
+#define max(x,y) ((x) > (y) ? (x) : (y))
+
+struct hello_source {
+ enum hello_type type;
+ struct {
+ struct iface_af *ia;
+ union ldpd_addr src_addr;
+ } link;
+ struct tnbr *target;
+};
+
+struct adj {
+ LIST_ENTRY(adj) global_entry;
+ LIST_ENTRY(adj) nbr_entry;
+ LIST_ENTRY(adj) ia_entry;
+ struct in_addr lsr_id;
+ struct nbr *nbr;
+ int ds_tlv;
+ struct hello_source source;
+ struct event inactivity_timer;
+ uint16_t holdtime;
+ union ldpd_addr trans_addr;
+};
+
+struct tcp_conn {
+ struct nbr *nbr;
+ int fd;
+ struct ibuf_read *rbuf;
+ struct evbuf wbuf;
+ struct event rev;
+};
+
+struct nbr {
+ RB_ENTRY(nbr) id_tree, addr_tree, pid_tree;
+ struct tcp_conn *tcp;
+ LIST_HEAD(, adj) adj_list; /* adjacencies */
+ struct event ev_connect;
+ struct event keepalive_timer;
+ struct event keepalive_timeout;
+ struct event init_timeout;
+ struct event initdelay_timer;
+
+ struct mapping_head mapping_list;
+ struct mapping_head withdraw_list;
+ struct mapping_head request_list;
+ struct mapping_head release_list;
+ struct mapping_head abortreq_list;
+
+ uint32_t peerid; /* unique ID in DB */
+ int af;
+ int ds_tlv;
+ int v4_enabled; /* announce/process v4 msgs */
+ int v6_enabled; /* announce/process v6 msgs */
+ struct in_addr id; /* lsr id */
+ union ldpd_addr laddr; /* local address */
+ union ldpd_addr raddr; /* remote address */
+ uint32_t raddr_scope; /* remote address scope (v6) */
+ time_t uptime;
+ int fd;
+ int state;
+ uint32_t conf_seqnum;
+ int idtimer_cnt;
+ uint16_t keepalive;
+ uint16_t max_pdu_len;
+
+ struct {
+ uint8_t established;
+ uint32_t spi_in;
+ uint32_t spi_out;
+ enum auth_method method;
+ char md5key[TCP_MD5_KEY_LEN];
+ } auth;
+ int flags;
+};
+#define F_NBR_GTSM_NEGOTIATED 0x01
+
+RB_HEAD(nbr_id_head, nbr);
+RB_PROTOTYPE(nbr_id_head, nbr, id_tree, nbr_id_compare)
+RB_HEAD(nbr_addr_head, nbr);
+RB_PROTOTYPE(nbr_addr_head, nbr, addr_tree, nbr_addr_compare)
+RB_HEAD(nbr_pid_head, nbr);
+RB_PROTOTYPE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare)
+
+struct pending_conn {
+ TAILQ_ENTRY(pending_conn) entry;
+ int fd;
+ int af;
+ union ldpd_addr addr;
+ struct event ev_timeout;
+};
+#define PENDING_CONN_TIMEOUT 5
+
+struct mapping_entry {
+ TAILQ_ENTRY(mapping_entry) entry;
+ struct map map;
+};
+
+struct ldpd_sysdep {
+ uint8_t no_pfkey;
+ uint8_t no_md5sig;
+};
+
+extern struct ldpd_conf *leconf;
+extern struct ldpd_sysdep sysdep;
+extern struct nbr_id_head nbrs_by_id;
+extern struct nbr_addr_head nbrs_by_addr;
+extern struct nbr_pid_head nbrs_by_pid;
+
+/* accept.c */
+void accept_init(void);
+int accept_add(int, void (*)(int, short, void *), void *);
+void accept_del(int);
+void accept_pause(void);
+void accept_unpause(void);
+
+/* hello.c */
+int send_hello(enum hello_type, struct iface_af *, struct tnbr *);
+void recv_hello(struct in_addr, struct ldp_msg *, int, union ldpd_addr *,
+ struct iface *, int, char *, uint16_t);
+
+/* init.c */
+void send_init(struct nbr *);
+int recv_init(struct nbr *, char *, uint16_t);
+
+/* keepalive.c */
+void send_keepalive(struct nbr *);
+int recv_keepalive(struct nbr *, char *, uint16_t);
+
+/* notification.c */
+void send_notification_full(struct tcp_conn *, struct notify_msg *);
+void send_notification(uint32_t, struct tcp_conn *, uint32_t,
+ uint16_t);
+void send_notification_nbr(struct nbr *, uint32_t, uint32_t, uint16_t);
+int recv_notification(struct nbr *, char *, uint16_t);
+int gen_status_tlv(struct ibuf *, uint32_t, uint32_t, uint16_t);
+
+/* address.c */
+void send_address_single(struct nbr *, struct if_addr *, int);
+void send_address_all(struct nbr *, int);
+int recv_address(struct nbr *, char *, uint16_t);
+
+/* labelmapping.c */
+#define PREFIX_SIZE(x) (((x) + 7) / 8)
+void send_labelmessage(struct nbr *, uint16_t, struct mapping_head *);
+int recv_labelmessage(struct nbr *, char *, uint16_t, uint16_t);
+int gen_pw_status_tlv(struct ibuf *, uint32_t);
+int gen_fec_tlv(struct ibuf *, struct map *);
+int tlv_decode_fec_elm(struct nbr *, struct ldp_msg *, char *,
+ uint16_t, struct map *);
+
+/* ldpe.c */
+void ldpe(int, int);
+int ldpe_imsg_compose_parent(int, pid_t, void *,
+ uint16_t);
+int ldpe_imsg_compose_lde(int, uint32_t, pid_t, void *,
+ uint16_t);
+void ldpe_reset_nbrs(int);
+void ldpe_reset_ds_nbrs(void);
+void ldpe_remove_dynamic_tnbrs(int);
+void ldpe_stop_init_backoff(int);
+struct ctl_conn;
+void ldpe_iface_ctl(struct ctl_conn *, unsigned int);
+void ldpe_adj_ctl(struct ctl_conn *);
+void ldpe_nbr_ctl(struct ctl_conn *);
+void mapping_list_add(struct mapping_head *, struct map *);
+void mapping_list_clr(struct mapping_head *);
+
+/* interface.c */
+struct iface *if_new(struct kif *);
+void if_exit(struct iface *);
+struct iface *if_lookup(struct ldpd_conf *, unsigned short);
+struct iface_af *iface_af_get(struct iface *, int);
+void if_addr_add(struct kaddr *);
+void if_addr_del(struct kaddr *);
+void if_update(struct iface *, int);
+void if_update_all(int);
+struct ctl_iface *if_to_ctl(struct iface_af *);
+in_addr_t if_get_ipv4_addr(struct iface *);
+
+/* adjacency.c */
+struct adj *adj_new(struct in_addr, struct hello_source *,
+ union ldpd_addr *);
+void adj_del(struct adj *, uint32_t);
+struct adj *adj_find(struct hello_source *);
+int adj_get_af(struct adj *adj);
+void adj_start_itimer(struct adj *);
+void adj_stop_itimer(struct adj *);
+struct tnbr *tnbr_new(struct ldpd_conf *, int, union ldpd_addr *);
+struct tnbr *tnbr_find(struct ldpd_conf *, int, union ldpd_addr *);
+struct tnbr *tnbr_check(struct tnbr *);
+void tnbr_update(struct tnbr *);
+void tnbr_update_all(int);
+struct ctl_adj *adj_to_ctl(struct adj *);
+
+/* neighbor.c */
+int nbr_fsm(struct nbr *, enum nbr_event);
+struct nbr *nbr_new(struct in_addr, int, int, union ldpd_addr *,
+ uint32_t);
+void nbr_del(struct nbr *);
+struct nbr *nbr_find_ldpid(uint32_t);
+struct nbr *nbr_find_addr(int, union ldpd_addr *);
+struct nbr *nbr_find_peerid(uint32_t);
+int nbr_adj_count(struct nbr *, int);
+int nbr_session_active_role(struct nbr *);
+void nbr_stop_ktimer(struct nbr *);
+void nbr_stop_ktimeout(struct nbr *);
+void nbr_stop_itimeout(struct nbr *);
+void nbr_start_idtimer(struct nbr *);
+void nbr_stop_idtimer(struct nbr *);
+int nbr_pending_idtimer(struct nbr *);
+int nbr_pending_connect(struct nbr *);
+int nbr_establish_connection(struct nbr *);
+int nbr_gtsm_enabled(struct nbr *, struct nbr_params *);
+int nbr_gtsm_setup(int, int, struct nbr_params *);
+int nbr_gtsm_check(int, struct nbr *, struct nbr_params *);
+struct nbr_params *nbr_params_new(struct in_addr);
+struct nbr_params *nbr_params_find(struct ldpd_conf *, struct in_addr);
+uint16_t nbr_get_keepalive(int, struct in_addr);
+struct ctl_nbr *nbr_to_ctl(struct nbr *);
+void nbr_clear_ctl(struct ctl_nbr *);
+
+/* packet.c */
+int gen_ldp_hdr(struct ibuf *, uint16_t);
+int gen_msg_hdr(struct ibuf *, uint16_t, uint16_t);
+int send_packet(int, int, union ldpd_addr *,
+ struct iface_af *, void *, size_t);
+void disc_recv_packet(int, short, void *);
+void session_accept(int, short, void *);
+void session_accept_nbr(struct nbr *, int);
+void session_shutdown(struct nbr *, uint32_t, uint32_t,
+ uint32_t);
+void session_close(struct nbr *);
+struct tcp_conn *tcp_new(int, struct nbr *);
+void pending_conn_del(struct pending_conn *);
+struct pending_conn *pending_conn_find(int, union ldpd_addr *);
+
+char *pkt_ptr; /* packet buffer */
+
+/* pfkey.c */
+int pfkey_read(int, struct sadb_msg *);
+int pfkey_establish(struct nbr *, struct nbr_params *);
+int pfkey_remove(struct nbr *);
+int pfkey_init(void);
+
+/* l2vpn.c */
+void ldpe_l2vpn_init(struct l2vpn *);
+void ldpe_l2vpn_exit(struct l2vpn *);
+void ldpe_l2vpn_pw_init(struct l2vpn_pw *);
+void ldpe_l2vpn_pw_exit(struct l2vpn_pw *);
+
+#endif /* _LDPE_H_ */
diff --git a/ldpd/log.c b/ldpd/log.c
new file mode 100644
index 000000000..e14b6e51e
--- /dev/null
+++ b/ldpd/log.c
@@ -0,0 +1,600 @@
+/* $OpenBSD$ */
+
+/*
+ * 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/socket.h>
+#include <arpa/inet.h>
+#include <netmpls/mpls.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <limits.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+
+static const char * const procnames[] = {
+ "parent",
+ "ldpe",
+ "lde"
+};
+
+static void vlog(int, const char *, va_list);
+
+static int debug;
+static int verbose;
+
+void
+log_init(int n_debug)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ tzset();
+}
+
+void
+log_verbose(int v)
+{
+ verbose = v;
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+static void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (verbose & LDPD_OPT_VERBOSE) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ logit(LOG_CRIT, "fatal in %s: %s", procnames[ldpd_process],
+ strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal in %s: %s: %s",
+ procnames[ldpd_process], emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal in %s: %s",
+ procnames[ldpd_process], emsg);
+
+ exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
+
+#define NUM_LOGS 4
+const char *
+log_sockaddr(void *vp)
+{
+ static char buf[NUM_LOGS][NI_MAXHOST];
+ static int round = 0;
+ struct sockaddr *sa = vp;
+
+ round = (round + 1) % NUM_LOGS;
+
+ if (getnameinfo(sa, sa->sa_len, buf[round], NI_MAXHOST, NULL, 0,
+ NI_NUMERICHOST))
+ return ("(unknown)");
+ else
+ return (buf[round]);
+}
+
+const char *
+log_in6addr(const struct in6_addr *addr)
+{
+ struct sockaddr_in6 sa_in6;
+
+ memset(&sa_in6, 0, sizeof(sa_in6));
+ sa_in6.sin6_len = sizeof(sa_in6);
+ sa_in6.sin6_family = AF_INET6;
+ sa_in6.sin6_addr = *addr;
+
+ recoverscope(&sa_in6);
+
+ return (log_sockaddr(&sa_in6));
+}
+
+const char *
+log_in6addr_scope(const struct in6_addr *addr, unsigned int ifindex)
+{
+ struct sockaddr_in6 sa_in6;
+
+ memset(&sa_in6, 0, sizeof(sa_in6));
+ sa_in6.sin6_len = sizeof(sa_in6);
+ sa_in6.sin6_family = AF_INET6;
+ sa_in6.sin6_addr = *addr;
+
+ addscope(&sa_in6, ifindex);
+
+ return (log_sockaddr(&sa_in6));
+}
+
+const char *
+log_addr(int af, const union ldpd_addr *addr)
+{
+ static char buf[NUM_LOGS][INET6_ADDRSTRLEN];
+ static int round = 0;
+
+ switch (af) {
+ case AF_INET:
+ round = (round + 1) % NUM_LOGS;
+ if (inet_ntop(AF_INET, &addr->v4, buf[round],
+ sizeof(buf[round])) == NULL)
+ return ("???");
+ return (buf[round]);
+ case AF_INET6:
+ return (log_in6addr(&addr->v6));
+ default:
+ break;
+ }
+
+ return ("???");
+}
+
+#define TF_BUFS 4
+#define TF_LEN 32
+
+char *
+log_label(uint32_t label)
+{
+ char *buf;
+ static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */
+ static int idx = 0;
+
+ buf = tfbuf[idx++];
+ if (idx == TF_BUFS)
+ idx = 0;
+
+ switch (label) {
+ case NO_LABEL:
+ snprintf(buf, TF_LEN, "-");
+ break;
+ case MPLS_LABEL_IMPLNULL:
+ snprintf(buf, TF_LEN, "imp-null");
+ break;
+ case MPLS_LABEL_IPV4NULL:
+ case MPLS_LABEL_IPV6NULL:
+ snprintf(buf, TF_LEN, "exp-null");
+ break;
+ default:
+ snprintf(buf, TF_LEN, "%u", label);
+ break;
+ }
+
+ return (buf);
+}
+
+char *
+log_hello_src(const struct hello_source *src)
+{
+ static char buf[64];
+
+ switch (src->type) {
+ case HELLO_LINK:
+ snprintf(buf, sizeof(buf), "iface %s",
+ src->link.ia->iface->name);
+ break;
+ case HELLO_TARGETED:
+ snprintf(buf, sizeof(buf), "source %s",
+ log_addr(src->target->af, &src->target->addr));
+ break;
+ }
+
+ return (buf);
+}
+
+const char *
+log_map(const struct map *map)
+{
+ static char buf[64];
+
+ switch (map->type) {
+ case MAP_TYPE_WILDCARD:
+ if (snprintf(buf, sizeof(buf), "wildcard") < 0)
+ return ("???");
+ break;
+ case MAP_TYPE_PREFIX:
+ if (snprintf(buf, sizeof(buf), "%s/%u",
+ log_addr(map->fec.prefix.af, &map->fec.prefix.prefix),
+ map->fec.prefix.prefixlen) == -1)
+ return ("???");
+ break;
+ case MAP_TYPE_PWID:
+ if (snprintf(buf, sizeof(buf), "pwid %u (%s)",
+ map->fec.pwid.pwid,
+ pw_type_name(map->fec.pwid.type)) == -1)
+ return ("???");
+ break;
+ default:
+ return ("???");
+ }
+
+ return (buf);
+}
+
+const char *
+log_fec(const struct fec *fec)
+{
+ static char buf[64];
+ union ldpd_addr addr;
+
+ switch (fec->type) {
+ case FEC_TYPE_IPV4:
+ addr.v4 = fec->u.ipv4.prefix;
+ if (snprintf(buf, sizeof(buf), "ipv4 %s/%u",
+ log_addr(AF_INET, &addr), fec->u.ipv4.prefixlen) == -1)
+ return ("???");
+ break;
+ case FEC_TYPE_IPV6:
+ addr.v6 = fec->u.ipv6.prefix;
+ if (snprintf(buf, sizeof(buf), "ipv6 %s/%u",
+ log_addr(AF_INET6, &addr), fec->u.ipv6.prefixlen) == -1)
+ return ("???");
+ break;
+ case FEC_TYPE_PWID:
+ if (snprintf(buf, sizeof(buf),
+ "pwid %u (%s) - %s",
+ fec->u.pwid.pwid, pw_type_name(fec->u.pwid.type),
+ inet_ntoa(fec->u.pwid.lsr_id)) == -1)
+ return ("???");
+ break;
+ default:
+ return ("???");
+ }
+
+ return (buf);
+}
+
+/* names */
+const char *
+af_name(int af)
+{
+ switch (af) {
+ case AF_INET:
+ return ("ipv4");
+ case AF_INET6:
+ return ("ipv6");
+ case AF_MPLS:
+ return ("mpls");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+socket_name(int type)
+{
+ switch (type) {
+ case LDP_SOCKET_DISC:
+ return ("discovery");
+ case LDP_SOCKET_EDISC:
+ return ("extended discovery");
+ case LDP_SOCKET_SESSION:
+ return ("session");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+nbr_state_name(int state)
+{
+ switch (state) {
+ case NBR_STA_PRESENT:
+ return ("PRESENT");
+ case NBR_STA_INITIAL:
+ return ("INITIALIZED");
+ case NBR_STA_OPENREC:
+ return ("OPENREC");
+ case NBR_STA_OPENSENT:
+ return ("OPENSENT");
+ case NBR_STA_OPER:
+ return ("OPERATIONAL");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+if_state_name(int state)
+{
+ switch (state) {
+ case IF_STA_DOWN:
+ return ("DOWN");
+ case IF_STA_ACTIVE:
+ return ("ACTIVE");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+if_type_name(enum iface_type type)
+{
+ switch (type) {
+ case IF_TYPE_POINTOPOINT:
+ return ("POINTOPOINT");
+ case IF_TYPE_BROADCAST:
+ return ("BROADCAST");
+ }
+ /* NOTREACHED */
+ return ("UNKNOWN");
+}
+
+const char *
+msg_name(uint16_t msg)
+{
+ static char buf[16];
+
+ switch (msg) {
+ case MSG_TYPE_NOTIFICATION:
+ return ("notification");
+ case MSG_TYPE_HELLO:
+ return ("hello");
+ case MSG_TYPE_INIT:
+ return ("initialization");
+ case MSG_TYPE_KEEPALIVE:
+ return ("keepalive");
+ case MSG_TYPE_ADDR:
+ return ("address");
+ case MSG_TYPE_ADDRWITHDRAW:
+ return ("address withdraw");
+ case MSG_TYPE_LABELMAPPING:
+ return ("label mapping");
+ case MSG_TYPE_LABELREQUEST:
+ return ("label request");
+ case MSG_TYPE_LABELWITHDRAW:
+ return ("label withdraw");
+ case MSG_TYPE_LABELRELEASE:
+ return ("label release");
+ case MSG_TYPE_LABELABORTREQ:
+ default:
+ snprintf(buf, sizeof(buf), "[%08x]", msg);
+ return (buf);
+ }
+}
+
+const char *
+status_code_name(uint32_t status)
+{
+ static char buf[16];
+
+ switch (status) {
+ case S_SUCCESS:
+ return ("Success");
+ case S_BAD_LDP_ID:
+ return ("Bad LDP Identifier");
+ case S_BAD_PROTO_VER:
+ return ("Bad Protocol Version");
+ case S_BAD_PDU_LEN:
+ return ("Bad PDU Length");
+ case S_UNKNOWN_MSG:
+ return ("Unknown Message Type");
+ case S_BAD_MSG_LEN:
+ return ("Bad Message Length");
+ case S_UNKNOWN_TLV:
+ return ("Unknown TLV");
+ case S_BAD_TLV_LEN:
+ return ("Bad TLV Length");
+ case S_BAD_TLV_VAL:
+ return ("Malformed TLV Value");
+ case S_HOLDTIME_EXP:
+ return ("Hold Timer Expired");
+ case S_SHUTDOWN:
+ return ("Shutdown");
+ case S_LOOP_DETECTED:
+ return ("Loop Detected");
+ case S_UNKNOWN_FEC:
+ return ("Unknown FEC");
+ case S_NO_ROUTE:
+ return ("No Route");
+ case S_NO_LABEL_RES:
+ return ("No Label Resources");
+ case S_AVAILABLE:
+ return ("Label Resources Available");
+ case S_NO_HELLO:
+ return ("Session Rejected, No Hello");
+ case S_PARM_ADV_MODE:
+ return ("Rejected Advertisement Mode Parameter");
+ case S_MAX_PDU_LEN:
+ return ("Rejected Max PDU Length Parameter");
+ case S_PARM_L_RANGE:
+ return ("Rejected Label Range Parameter");
+ case S_KEEPALIVE_TMR:
+ return ("KeepAlive Timer Expired");
+ case S_LAB_REQ_ABRT:
+ return ("Label Request Aborted");
+ case S_MISS_MSG:
+ return ("Missing Message Parameters");
+ case S_UNSUP_ADDR:
+ return ("Unsupported Address Family");
+ case S_KEEPALIVE_BAD:
+ return ("Bad KeepAlive Time");
+ case S_INTERN_ERR:
+ return ("Internal Error");
+ case S_ILLEGAL_CBIT:
+ return ("Illegal C-Bit");
+ case S_WRONG_CBIT:
+ return ("Wrong C-Bit");
+ case S_INCPT_BITRATE:
+ return ("Incompatible bit-rate");
+ case S_CEP_MISCONF:
+ return ("CEP-TDM mis-configuration");
+ case S_PW_STATUS:
+ return ("PW Status");
+ case S_UNASSIGN_TAI:
+ return ("Unassigned/Unrecognized TAI");
+ case S_MISCONF_ERR:
+ return ("Generic Misconfiguration Error");
+ case S_WITHDRAW_MTHD:
+ return ("Label Withdraw PW Status Method");
+ case S_TRANS_MISMTCH:
+ return ("Transport Connection Mismatch");
+ case S_DS_NONCMPLNCE:
+ return ("Dual-Stack Noncompliance");
+ default:
+ snprintf(buf, sizeof(buf), "[%08x]", status);
+ return (buf);
+ }
+}
+
+const char *
+pw_type_name(uint16_t pw_type)
+{
+ static char buf[64];
+
+ switch (pw_type) {
+ case PW_TYPE_ETHERNET_TAGGED:
+ return ("Eth Tagged");
+ case PW_TYPE_ETHERNET:
+ return ("Ethernet");
+ default:
+ snprintf(buf, sizeof(buf), "[%0x]", pw_type);
+ return (buf);
+ }
+}
+
+static char *msgtypes[] = {
+ "",
+ "RTM_ADD: Add Route",
+ "RTM_DELETE: Delete Route",
+ "RTM_CHANGE: Change Metrics or flags",
+ "RTM_GET: Report Metrics",
+ "RTM_LOSING: Kernel Suspects Partitioning",
+ "RTM_REDIRECT: Told to use different route",
+ "RTM_MISS: Lookup failed on this address",
+ "RTM_LOCK: fix specified metrics",
+ "RTM_OLDADD: caused by SIOCADDRT",
+ "RTM_OLDDEL: caused by SIOCDELRT",
+ "RTM_RESOLVE: Route created by cloning",
+ "RTM_NEWADDR: address being added to iface",
+ "RTM_DELADDR: address being removed from iface",
+ "RTM_IFINFO: iface status change",
+ "RTM_IFANNOUNCE: iface arrival/departure",
+ "RTM_DESYNC: route socket overflow",
+};
+
+void
+log_rtmsg(unsigned char rtm_type)
+{
+ if (!(verbose & LDPD_OPT_VERBOSE2))
+ return;
+
+ if (rtm_type > 0 &&
+ rtm_type < sizeof(msgtypes)/sizeof(msgtypes[0]))
+ log_debug("kernel message: %s", msgtypes[rtm_type]);
+ else
+ log_debug("kernel message: rtm_type %d out of range",
+ rtm_type);
+}
diff --git a/ldpd/log.h b/ldpd/log.h
new file mode 100644
index 000000000..94c463041
--- /dev/null
+++ b/ldpd/log.h
@@ -0,0 +1,63 @@
+/* $OpenBSD$ */
+
+/*
+ * 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.
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <stdarg.h>
+
+struct in6_addr;
+union ldpd_addr;
+struct hello_source;
+struct fec;
+
+void log_init(int);
+void log_verbose(int);
+void logit(int, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void log_warn(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_warnx(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_info(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_debug(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void fatal(const char *) __dead
+ __attribute__((__format__ (printf, 1, 0)));
+void fatalx(const char *) __dead
+ __attribute__((__format__ (printf, 1, 0)));
+const char *log_sockaddr(void *);
+const char *log_in6addr(const struct in6_addr *);
+const char *log_in6addr_scope(const struct in6_addr *, unsigned int);
+const char *log_addr(int, const union ldpd_addr *);
+char *log_label(uint32_t);
+char *log_hello_src(const struct hello_source *);
+const char *log_map(const struct map *);
+const char *log_fec(const struct fec *);
+const char *af_name(int);
+const char *socket_name(int);
+const char *nbr_state_name(int);
+const char *if_state_name(int);
+const char *if_type_name(enum iface_type);
+const char *msg_name(uint16_t);
+const char *status_code_name(uint32_t);
+const char *pw_type_name(uint16_t);
+void log_rtmsg(unsigned char);
+
+#endif /* _LOG_H_ */
diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c
new file mode 100644
index 000000000..d3f83734f
--- /dev/null
+++ b/ldpd/neighbor.c
@@ -0,0 +1,827 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@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/time.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+
+static __inline int nbr_id_compare(struct nbr *, struct nbr *);
+static __inline int nbr_addr_compare(struct nbr *, struct nbr *);
+static __inline int nbr_pid_compare(struct nbr *, struct nbr *);
+static void nbr_update_peerid(struct nbr *);
+static void nbr_ktimer(int, short, void *);
+static void nbr_start_ktimer(struct nbr *);
+static void nbr_ktimeout(int, short, void *);
+static void nbr_start_ktimeout(struct nbr *);
+static void nbr_itimeout(int, short, void *);
+static void nbr_start_itimeout(struct nbr *);
+static void nbr_idtimer(int, short, void *);
+static int nbr_act_session_operational(struct nbr *);
+static void nbr_send_labelmappings(struct nbr *);
+
+RB_GENERATE(nbr_id_head, nbr, id_tree, nbr_id_compare)
+RB_GENERATE(nbr_addr_head, nbr, addr_tree, nbr_addr_compare)
+RB_GENERATE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare)
+
+struct {
+ int state;
+ enum nbr_event event;
+ enum nbr_action action;
+ int new_state;
+} nbr_fsm_tbl[] = {
+ /* current state event that happened action to take resulting state */
+/* Passive Role */
+ {NBR_STA_PRESENT, NBR_EVT_MATCH_ADJ, NBR_ACT_NOTHING, NBR_STA_INITIAL},
+ {NBR_STA_INITIAL, NBR_EVT_INIT_RCVD, NBR_ACT_PASSIVE_INIT, NBR_STA_OPENREC},
+ {NBR_STA_OPENREC, NBR_EVT_KEEPALIVE_RCVD, NBR_ACT_SESSION_EST, NBR_STA_OPER},
+/* Active Role */
+ {NBR_STA_PRESENT, NBR_EVT_CONNECT_UP, NBR_ACT_CONNECT_SETUP, NBR_STA_INITIAL},
+ {NBR_STA_INITIAL, NBR_EVT_INIT_SENT, NBR_ACT_NOTHING, NBR_STA_OPENSENT},
+ {NBR_STA_OPENSENT, NBR_EVT_INIT_RCVD, NBR_ACT_KEEPALIVE_SEND, NBR_STA_OPENREC},
+/* Session Maintenance */
+ {NBR_STA_OPER, NBR_EVT_PDU_RCVD, NBR_ACT_RST_KTIMEOUT, 0},
+ {NBR_STA_SESSION, NBR_EVT_PDU_RCVD, NBR_ACT_NOTHING, 0},
+ {NBR_STA_OPER, NBR_EVT_PDU_SENT, NBR_ACT_RST_KTIMER, 0},
+ {NBR_STA_SESSION, NBR_EVT_PDU_SENT, NBR_ACT_NOTHING, 0},
+/* Session Close */
+ {NBR_STA_PRESENT, NBR_EVT_CLOSE_SESSION, NBR_ACT_NOTHING, 0},
+ {NBR_STA_SESSION, NBR_EVT_CLOSE_SESSION, NBR_ACT_CLOSE_SESSION, NBR_STA_PRESENT},
+ {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0},
+};
+
+const char * const nbr_event_names[] = {
+ "NOTHING",
+ "ADJACENCY MATCHED",
+ "CONNECTION UP",
+ "SESSION CLOSE",
+ "INIT RECEIVED",
+ "KEEPALIVE RECEIVED",
+ "PDU RECEIVED",
+ "PDU SENT",
+ "INIT SENT"
+};
+
+const char * const nbr_action_names[] = {
+ "NOTHING",
+ "RESET KEEPALIVE TIMEOUT",
+ "START NEIGHBOR SESSION",
+ "RESET KEEPALIVE TIMER",
+ "SETUP NEIGHBOR CONNECTION",
+ "SEND INIT AND KEEPALIVE",
+ "SEND KEEPALIVE",
+ "CLOSE SESSION"
+};
+
+struct nbr_id_head nbrs_by_id = RB_INITIALIZER(&nbrs_by_id);
+struct nbr_addr_head nbrs_by_addr = RB_INITIALIZER(&nbrs_by_addr);
+struct nbr_pid_head nbrs_by_pid = RB_INITIALIZER(&nbrs_by_pid);
+
+static __inline int
+nbr_id_compare(struct nbr *a, struct nbr *b)
+{
+ return (ntohl(a->id.s_addr) - ntohl(b->id.s_addr));
+}
+
+static __inline int
+nbr_addr_compare(struct nbr *a, struct nbr *b)
+{
+ if (a->af < b->af)
+ return (-1);
+ if (a->af > b->af)
+ return (1);
+
+ return (ldp_addrcmp(a->af, &a->raddr, &b->raddr));
+}
+
+static __inline int
+nbr_pid_compare(struct nbr *a, struct nbr *b)
+{
+ return (a->peerid - b->peerid);
+}
+
+int
+nbr_fsm(struct nbr *nbr, enum nbr_event event)
+{
+ struct timeval now;
+ int old_state;
+ int new_state = 0;
+ int i;
+
+ old_state = nbr->state;
+ for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
+ if ((nbr_fsm_tbl[i].state & old_state) &&
+ (nbr_fsm_tbl[i].event == event)) {
+ new_state = nbr_fsm_tbl[i].new_state;
+ break;
+ }
+
+ if (nbr_fsm_tbl[i].state == -1) {
+ /* event outside of the defined fsm, ignore it. */
+ log_warnx("%s: lsr-id %s, event %s not expected in "
+ "state %s", __func__, inet_ntoa(nbr->id),
+ nbr_event_names[event], nbr_state_name(old_state));
+ return (0);
+ }
+
+ if (new_state != 0)
+ nbr->state = new_state;
+
+ if (old_state != nbr->state) {
+ log_debug("%s: event %s resulted in action %s and "
+ "changing state for lsr-id %s from %s to %s",
+ __func__, nbr_event_names[event],
+ nbr_action_names[nbr_fsm_tbl[i].action],
+ inet_ntoa(nbr->id), nbr_state_name(old_state),
+ nbr_state_name(nbr->state));
+
+ if (nbr->state == NBR_STA_OPER) {
+ gettimeofday(&now, NULL);
+ nbr->uptime = now.tv_sec;
+ }
+ }
+
+ if (nbr->state == NBR_STA_OPER || nbr->state == NBR_STA_PRESENT)
+ nbr_stop_itimeout(nbr);
+ else
+ nbr_start_itimeout(nbr);
+
+ switch (nbr_fsm_tbl[i].action) {
+ case NBR_ACT_RST_KTIMEOUT:
+ nbr_start_ktimeout(nbr);
+ break;
+ case NBR_ACT_RST_KTIMER:
+ nbr_start_ktimer(nbr);
+ break;
+ case NBR_ACT_SESSION_EST:
+ nbr_act_session_operational(nbr);
+ nbr_start_ktimer(nbr);
+ nbr_start_ktimeout(nbr);
+ if (nbr->v4_enabled)
+ send_address_all(nbr, AF_INET);
+ if (nbr->v6_enabled)
+ send_address_all(nbr, AF_INET6);
+ nbr_send_labelmappings(nbr);
+ break;
+ case NBR_ACT_CONNECT_SETUP:
+ nbr->tcp = tcp_new(nbr->fd, nbr);
+
+ /* trigger next state */
+ send_init(nbr);
+ nbr_fsm(nbr, NBR_EVT_INIT_SENT);
+ break;
+ case NBR_ACT_PASSIVE_INIT:
+ send_init(nbr);
+ send_keepalive(nbr);
+ break;
+ case NBR_ACT_KEEPALIVE_SEND:
+ nbr_start_ktimeout(nbr);
+ send_keepalive(nbr);
+ break;
+ case NBR_ACT_CLOSE_SESSION:
+ ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0,
+ NULL, 0);
+ session_close(nbr);
+ break;
+ case NBR_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ return (0);
+}
+
+struct nbr *
+nbr_new(struct in_addr id, int af, int ds_tlv, union ldpd_addr *addr,
+ uint32_t scope_id)
+{
+ struct nbr *nbr;
+ struct nbr_params *nbrp;
+ struct adj *adj;
+ struct pending_conn *pconn;
+
+ log_debug("%s: lsr-id %s transport-address %s", __func__,
+ inet_ntoa(id), log_addr(af, addr));
+
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal(__func__);
+
+ LIST_INIT(&nbr->adj_list);
+ nbr->state = NBR_STA_PRESENT;
+ nbr->peerid = 0;
+ nbr->af = af;
+ nbr->ds_tlv = ds_tlv;
+ if (af == AF_INET || ds_tlv)
+ nbr->v4_enabled = 1;
+ if (af == AF_INET6 || ds_tlv)
+ nbr->v6_enabled = 1;
+ nbr->id = id;
+ nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr;
+ nbr->raddr = *addr;
+ nbr->raddr_scope = scope_id;
+ nbr->conf_seqnum = 0;
+
+ LIST_FOREACH(adj, &global.adj_list, global_entry) {
+ if (adj->lsr_id.s_addr == nbr->id.s_addr) {
+ adj->nbr = nbr;
+ LIST_INSERT_HEAD(&nbr->adj_list, adj, nbr_entry);
+ }
+ }
+
+ if (RB_INSERT(nbr_id_head, &nbrs_by_id, nbr) != NULL)
+ fatalx("nbr_new: RB_INSERT(nbrs_by_id) failed");
+ if (RB_INSERT(nbr_addr_head, &nbrs_by_addr, nbr) != NULL)
+ fatalx("nbr_new: RB_INSERT(nbrs_by_addr) failed");
+
+ TAILQ_INIT(&nbr->mapping_list);
+ TAILQ_INIT(&nbr->withdraw_list);
+ TAILQ_INIT(&nbr->request_list);
+ TAILQ_INIT(&nbr->release_list);
+ TAILQ_INIT(&nbr->abortreq_list);
+
+ /* set event structures */
+ evtimer_set(&nbr->keepalive_timeout, nbr_ktimeout, nbr);
+ evtimer_set(&nbr->keepalive_timer, nbr_ktimer, nbr);
+ evtimer_set(&nbr->init_timeout, nbr_itimeout, nbr);
+ evtimer_set(&nbr->initdelay_timer, nbr_idtimer, nbr);
+
+ nbrp = nbr_params_find(leconf, nbr->id);
+ if (nbrp && pfkey_establish(nbr, nbrp) == -1)
+ fatalx("pfkey setup failed");
+
+ pconn = pending_conn_find(nbr->af, &nbr->raddr);
+ if (pconn) {
+ session_accept_nbr(nbr, pconn->fd);
+ pending_conn_del(pconn);
+ }
+
+ return (nbr);
+}
+
+void
+nbr_del(struct nbr *nbr)
+{
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+ pfkey_remove(nbr);
+
+ if (nbr_pending_connect(nbr))
+ event_del(&nbr->ev_connect);
+ nbr_stop_ktimer(nbr);
+ nbr_stop_ktimeout(nbr);
+ nbr_stop_itimeout(nbr);
+ nbr_stop_idtimer(nbr);
+
+ mapping_list_clr(&nbr->mapping_list);
+ mapping_list_clr(&nbr->withdraw_list);
+ mapping_list_clr(&nbr->request_list);
+ mapping_list_clr(&nbr->release_list);
+ mapping_list_clr(&nbr->abortreq_list);
+
+ if (nbr->peerid)
+ RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);
+ RB_REMOVE(nbr_id_head, &nbrs_by_id, nbr);
+ RB_REMOVE(nbr_addr_head, &nbrs_by_addr, nbr);
+
+ free(nbr);
+}
+
+static void
+nbr_update_peerid(struct nbr *nbr)
+{
+ static uint32_t peercnt = 1;
+
+ if (nbr->peerid)
+ RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);
+
+ /* get next unused peerid */
+ while (nbr_find_peerid(++peercnt))
+ ;
+ nbr->peerid = peercnt;
+
+ if (RB_INSERT(nbr_pid_head, &nbrs_by_pid, nbr) != NULL)
+ fatalx("nbr_update_peerid: RB_INSERT(nbrs_by_pid) failed");
+}
+
+struct nbr *
+nbr_find_ldpid(uint32_t lsr_id)
+{
+ struct nbr n;
+ n.id.s_addr = lsr_id;
+ return (RB_FIND(nbr_id_head, &nbrs_by_id, &n));
+}
+
+struct nbr *
+nbr_find_addr(int af, union ldpd_addr *addr)
+{
+ struct nbr n;
+ n.af = af;
+ n.raddr = *addr;
+ return (RB_FIND(nbr_addr_head, &nbrs_by_addr, &n));
+}
+
+struct nbr *
+nbr_find_peerid(uint32_t peerid)
+{
+ struct nbr n;
+ n.peerid = peerid;
+ return (RB_FIND(nbr_pid_head, &nbrs_by_pid, &n));
+}
+
+int
+nbr_adj_count(struct nbr *nbr, int af)
+{
+ struct adj *adj;
+ int total = 0;
+
+ LIST_FOREACH(adj, &nbr->adj_list, nbr_entry)
+ if (adj_get_af(adj) == af)
+ total++;
+
+ return (total);
+}
+
+int
+nbr_session_active_role(struct nbr *nbr)
+{
+ if (ldp_addrcmp(nbr->af, &nbr->laddr, &nbr->raddr) > 0)
+ return (1);
+
+ return (0);
+}
+
+/* timers */
+
+/* Keepalive timer: timer to send keepalive message to neighbors */
+
+static void
+nbr_ktimer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ send_keepalive(nbr);
+ nbr_start_ktimer(nbr);
+}
+
+static void
+nbr_start_ktimer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ /* send three keepalives per period */
+ timerclear(&tv);
+ tv.tv_sec = (time_t)(nbr->keepalive / KEEPALIVE_PER_PERIOD);
+ if (evtimer_add(&nbr->keepalive_timer, &tv) == -1)
+ fatal(__func__);
+}
+
+void
+nbr_stop_ktimer(struct nbr *nbr)
+{
+ if (evtimer_pending(&nbr->keepalive_timer, NULL) &&
+ evtimer_del(&nbr->keepalive_timer) == -1)
+ fatal(__func__);
+}
+
+/* Keepalive timeout: if the nbr hasn't sent keepalive */
+
+static void
+nbr_ktimeout(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ session_shutdown(nbr, S_KEEPALIVE_TMR, 0, 0);
+}
+
+static void
+nbr_start_ktimeout(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = nbr->keepalive;
+
+ if (evtimer_add(&nbr->keepalive_timeout, &tv) == -1)
+ fatal(__func__);
+}
+
+void
+nbr_stop_ktimeout(struct nbr *nbr)
+{
+ if (evtimer_pending(&nbr->keepalive_timeout, NULL) &&
+ evtimer_del(&nbr->keepalive_timeout) == -1)
+ fatal(__func__);
+}
+
+/* Session initialization timeout: if nbr got stuck in the initialization FSM */
+
+static void
+nbr_itimeout(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+}
+
+static void
+nbr_start_itimeout(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = INIT_FSM_TIMEOUT;
+ if (evtimer_add(&nbr->init_timeout, &tv) == -1)
+ fatal(__func__);
+}
+
+void
+nbr_stop_itimeout(struct nbr *nbr)
+{
+ if (evtimer_pending(&nbr->init_timeout, NULL) &&
+ evtimer_del(&nbr->init_timeout) == -1)
+ fatal(__func__);
+}
+
+/* Init delay timer: timer to retry to iniziatize session */
+
+static void
+nbr_idtimer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ nbr_establish_connection(nbr);
+}
+
+void
+nbr_start_idtimer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+
+ tv.tv_sec = INIT_DELAY_TMR;
+ switch(nbr->idtimer_cnt) {
+ default:
+ /* do not further increase the counter */
+ tv.tv_sec = MAX_DELAY_TMR;
+ break;
+ case 2:
+ tv.tv_sec *= 2;
+ /* FALLTHROUGH */
+ case 1:
+ tv.tv_sec *= 2;
+ /* FALLTHROUGH */
+ case 0:
+ nbr->idtimer_cnt++;
+ break;
+ }
+
+ if (evtimer_add(&nbr->initdelay_timer, &tv) == -1)
+ fatal(__func__);
+}
+
+void
+nbr_stop_idtimer(struct nbr *nbr)
+{
+ if (evtimer_pending(&nbr->initdelay_timer, NULL) &&
+ evtimer_del(&nbr->initdelay_timer) == -1)
+ fatal(__func__);
+}
+
+int
+nbr_pending_idtimer(struct nbr *nbr)
+{
+ if (evtimer_pending(&nbr->initdelay_timer, NULL))
+ return (1);
+
+ return (0);
+}
+
+int
+nbr_pending_connect(struct nbr *nbr)
+{
+ if (event_initialized(&nbr->ev_connect) &&
+ event_pending(&nbr->ev_connect, EV_WRITE, NULL))
+ return (1);
+
+ return (0);
+}
+
+static void
+nbr_connect_cb(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+ int error;
+ socklen_t len;
+
+ len = sizeof(error);
+ if (getsockopt(nbr->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ log_warn("%s: getsockopt SOL_SOCKET SO_ERROR", __func__);
+ return;
+ }
+
+ if (error) {
+ close(nbr->fd);
+ errno = error;
+ log_debug("%s: error while connecting to %s: %s", __func__,
+ log_addr(nbr->af, &nbr->raddr), strerror(errno));
+ return;
+ }
+
+ nbr_fsm(nbr, NBR_EVT_CONNECT_UP);
+}
+
+int
+nbr_establish_connection(struct nbr *nbr)
+{
+ struct sockaddr_storage local_sa;
+ struct sockaddr_storage remote_sa;
+ struct adj *adj;
+ struct nbr_params *nbrp;
+ int opt = 1;
+
+ nbr->fd = socket(nbr->af,
+ SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (nbr->fd == -1) {
+ log_warn("%s: error while creating socket", __func__);
+ return (-1);
+ }
+
+ nbrp = nbr_params_find(leconf, nbr->id);
+ if (nbrp && nbrp->auth.method == AUTH_MD5SIG) {
+ if (sysdep.no_pfkey || sysdep.no_md5sig) {
+ log_warnx("md5sig configured but not available");
+ close(nbr->fd);
+ return (-1);
+ }
+ if (setsockopt(nbr->fd, IPPROTO_TCP, TCP_MD5SIG,
+ &opt, sizeof(opt)) == -1) {
+ log_warn("setsockopt md5sig");
+ close(nbr->fd);
+ return (-1);
+ }
+ }
+
+ memcpy(&local_sa, addr2sa(nbr->af, &nbr->laddr, 0), sizeof(local_sa));
+ memcpy(&remote_sa, addr2sa(nbr->af, &nbr->raddr, LDP_PORT),
+ sizeof(local_sa));
+ if (nbr->af == AF_INET6 && nbr->raddr_scope)
+ addscope((struct sockaddr_in6 *)&remote_sa, nbr->raddr_scope);
+
+ if (bind(nbr->fd, (struct sockaddr *)&local_sa,
+ local_sa.ss_len) == -1) {
+ log_warn("%s: error while binding socket to %s", __func__,
+ log_sockaddr((struct sockaddr *)&local_sa));
+ close(nbr->fd);
+ return (-1);
+ }
+
+ if (nbr_gtsm_check(nbr->fd, nbr, nbrp)) {
+ close(nbr->fd);
+ return (-1);
+ }
+
+ /*
+ * Send an extra hello to guarantee that the remote peer has formed
+ * an adjacency as well.
+ */
+ LIST_FOREACH(adj, &nbr->adj_list, nbr_entry)
+ send_hello(adj->source.type, adj->source.link.ia,
+ adj->source.target);
+
+ if (connect(nbr->fd, (struct sockaddr *)&remote_sa,
+ remote_sa.ss_len) == -1) {
+ if (errno == EINPROGRESS) {
+ event_set(&nbr->ev_connect, nbr->fd, EV_WRITE,
+ nbr_connect_cb, nbr);
+ event_add(&nbr->ev_connect, NULL);
+ return (0);
+ }
+ log_warn("%s: error while connecting to %s", __func__,
+ log_sockaddr((struct sockaddr *)&remote_sa));
+ close(nbr->fd);
+ return (-1);
+ }
+
+ /* connection completed immediately */
+ nbr_fsm(nbr, NBR_EVT_CONNECT_UP);
+
+ return (0);
+}
+
+int
+nbr_gtsm_enabled(struct nbr *nbr, struct nbr_params *nbrp)
+{
+ /*
+ * RFC 6720 - Section 3:
+ * "This document allows for the implementation to provide an option to
+ * statically (e.g., via configuration) and/or dynamically override the
+ * default behavior and enable/disable GTSM on a per-peer basis".
+ */
+ if (nbrp && (nbrp->flags & F_NBRP_GTSM))
+ return (nbrp->gtsm_enabled);
+
+ if ((ldp_af_conf_get(leconf, nbr->af))->flags & F_LDPD_AF_NO_GTSM)
+ return (0);
+
+ /* By default, GTSM support has to be negotiated for LDPv4 */
+ if (nbr->af == AF_INET && !(nbr->flags & F_NBR_GTSM_NEGOTIATED))
+ return (0);
+
+ return (1);
+}
+
+int
+nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp)
+{
+ int ttl = 255;
+
+ if (nbrp && (nbrp->flags & F_NBRP_GTSM_HOPS))
+ ttl = 256 - nbrp->gtsm_hops;
+
+ switch (af) {
+ case AF_INET:
+ if (sock_set_ipv4_minttl(fd, ttl) == -1)
+ return (-1);
+ ttl = 255;
+ if (sock_set_ipv4_ucast_ttl(fd, ttl) == -1)
+ return (-1);
+ break;
+ case AF_INET6:
+ if (sock_set_ipv6_minhopcount(fd, ttl) == -1)
+ return (-1);
+ ttl = 255;
+ if (sock_set_ipv6_ucast_hops(fd, ttl) == -1)
+ return (-1);
+ break;
+ default:
+ fatalx("nbr_gtsm_setup: unknown af");
+ }
+
+ return (0);
+}
+
+int
+nbr_gtsm_check(int fd, struct nbr *nbr, struct nbr_params *nbrp)
+{
+ if (!nbr_gtsm_enabled(nbr, nbrp)) {
+ switch (nbr->af) {
+ case AF_INET:
+ sock_set_ipv4_ucast_ttl(fd, -1);
+ break;
+ case AF_INET6:
+ /*
+ * Send packets with a Hop Limit of 255 even when GSTM
+ * is disabled to guarantee interoperability.
+ */
+ sock_set_ipv6_ucast_hops(fd, 255);
+ break;
+ default:
+ fatalx("nbr_gtsm_check: unknown af");
+ break;
+ }
+ return (0);
+ }
+
+ if (nbr_gtsm_setup(fd, nbr->af, nbrp) == -1) {
+ log_warnx("%s: error enabling GTSM for lsr-id %s", __func__,
+ inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+nbr_act_session_operational(struct nbr *nbr)
+{
+ struct lde_nbr lde_nbr;
+
+ nbr->idtimer_cnt = 0;
+
+ /* this is necessary to avoid ipc synchronization issues */
+ nbr_update_peerid(nbr);
+
+ memset(&lde_nbr, 0, sizeof(lde_nbr));
+ lde_nbr.id = nbr->id;
+ lde_nbr.v4_enabled = nbr->v4_enabled;
+ lde_nbr.v6_enabled = nbr->v6_enabled;
+ return (ldpe_imsg_compose_lde(IMSG_NEIGHBOR_UP, nbr->peerid, 0,
+ &lde_nbr, sizeof(lde_nbr)));
+}
+
+static void
+nbr_send_labelmappings(struct nbr *nbr)
+{
+ ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0,
+ NULL, 0);
+}
+
+struct nbr_params *
+nbr_params_new(struct in_addr lsr_id)
+{
+ struct nbr_params *nbrp;
+
+ if ((nbrp = calloc(1, sizeof(*nbrp))) == NULL)
+ fatal(__func__);
+
+ nbrp->lsr_id = lsr_id;
+ nbrp->auth.method = AUTH_NONE;
+
+ return (nbrp);
+}
+
+struct nbr_params *
+nbr_params_find(struct ldpd_conf *xconf, struct in_addr lsr_id)
+{
+ struct nbr_params *nbrp;
+
+ LIST_FOREACH(nbrp, &xconf->nbrp_list, entry)
+ if (nbrp->lsr_id.s_addr == lsr_id.s_addr)
+ return (nbrp);
+
+ return (NULL);
+}
+
+uint16_t
+nbr_get_keepalive(int af, struct in_addr lsr_id)
+{
+ struct nbr_params *nbrp;
+
+ nbrp = nbr_params_find(leconf, lsr_id);
+ if (nbrp && (nbrp->flags & F_NBRP_KEEPALIVE))
+ return (nbrp->keepalive);
+
+ return ((ldp_af_conf_get(leconf, af))->keepalive);
+}
+
+struct ctl_nbr *
+nbr_to_ctl(struct nbr *nbr)
+{
+ static struct ctl_nbr nctl;
+ struct timeval now;
+
+ nctl.af = nbr->af;
+ nctl.id = nbr->id;
+ nctl.laddr = nbr->laddr;
+ nctl.raddr = nbr->raddr;
+ nctl.nbr_state = nbr->state;
+
+ gettimeofday(&now, NULL);
+ if (nbr->state == NBR_STA_OPER) {
+ nctl.uptime = now.tv_sec - nbr->uptime;
+ } else
+ nctl.uptime = 0;
+
+ return (&nctl);
+}
+
+void
+nbr_clear_ctl(struct ctl_nbr *nctl)
+{
+ struct nbr *nbr;
+
+ RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) {
+ if (ldp_addrisset(nctl->af, &nctl->raddr) &&
+ ldp_addrcmp(nctl->af, &nctl->raddr, &nbr->raddr))
+ continue;
+
+ log_debug("%s: neighbor %s manually cleared", __func__,
+ log_addr(nbr->af, &nbr->raddr));
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ }
+}
diff --git a/ldpd/notification.c b/ldpd/notification.c
new file mode 100644
index 000000000..f30646bb8
--- /dev/null
+++ b/ldpd/notification.c
@@ -0,0 +1,239 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@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 <arpa/inet.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldp.h"
+#include "log.h"
+#include "ldpe.h"
+
+void
+send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
+{
+ struct ibuf *buf;
+ uint16_t size;
+ int err = 0;
+
+ /* calculate size */
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE;
+ if (nm->flags & F_NOTIF_PW_STATUS)
+ size += PW_STATUS_TLV_SIZE;
+ if (nm->flags & F_NOTIF_FEC) {
+ size += TLV_HDR_SIZE;
+ switch (nm->fec.type) {
+ case MAP_TYPE_PWID:
+ size += FEC_PWID_ELM_MIN_LEN;
+ if (nm->fec.flags & F_MAP_PW_ID)
+ size += sizeof(uint32_t);
+ break;
+ }
+ }
+
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ err |= gen_ldp_hdr(buf, size);
+ size -= LDP_HDR_SIZE;
+ err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size);
+ err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type);
+ /* optional tlvs */
+ if (nm->flags & F_NOTIF_PW_STATUS)
+ err |= gen_pw_status_tlv(buf, nm->pw_status);
+ if (nm->flags & F_NOTIF_FEC)
+ err |= gen_fec_tlv(buf, &nm->fec);
+ if (err) {
+ ibuf_free(buf);
+ return;
+ }
+
+ if (tcp->nbr)
+ log_debug("msg-out: notification: lsr-id %s, status %s%s",
+ inet_ntoa(tcp->nbr->id), status_code_name(nm->status_code),
+ (nm->status_code & STATUS_FATAL) ? " (fatal)" : "");
+
+ evbuf_enqueue(&tcp->wbuf, buf);
+}
+
+/* send a notification without optional tlvs */
+void
+send_notification(uint32_t status_code, struct tcp_conn *tcp, uint32_t msg_id,
+ uint16_t msg_type)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = status_code;
+ nm.msg_id = msg_id;
+ nm.msg_type = msg_type;
+
+ send_notification_full(tcp, &nm);
+}
+
+void
+send_notification_nbr(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
+ uint16_t msg_type)
+{
+ send_notification(status_code, nbr->tcp, msg_id, msg_type);
+ nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+}
+
+int
+recv_notification(struct nbr *nbr, char *buf, uint16_t len)
+{
+ struct ldp_msg msg;
+ struct status_tlv st;
+ struct notify_msg nm;
+ int tlen;
+
+ memcpy(&msg, buf, sizeof(msg));
+ buf += LDP_MSG_SIZE;
+ len -= LDP_MSG_SIZE;
+
+ if (len < STATUS_SIZE) {
+ session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ memcpy(&st, buf, sizeof(st));
+
+ if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE ||
+ ntohs(st.length) > len - TLV_HDR_SIZE) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ buf += STATUS_SIZE;
+ len -= STATUS_SIZE;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = ntohl(st.status_code);
+
+ /* Optional Parameters */
+ while (len > 0) {
+ struct tlv tlv;
+ uint16_t tlv_len;
+
+ if (len < sizeof(tlv)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_len = ntohs(tlv.length);
+ if (tlv_len + TLV_HDR_SIZE > len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ buf += TLV_HDR_SIZE;
+ len -= TLV_HDR_SIZE;
+
+ switch (ntohs(tlv.type)) {
+ case TLV_TYPE_EXTSTATUS:
+ case TLV_TYPE_RETURNEDPDU:
+ case TLV_TYPE_RETURNEDMSG:
+ /* TODO is there any use for this? */
+ break;
+ case TLV_TYPE_PW_STATUS:
+ if (tlv_len != 4) {
+ session_shutdown(nbr, S_BAD_TLV_LEN,
+ msg.id, msg.type);
+ return (-1);
+ }
+
+ nm.pw_status = ntohl(*(uint32_t *)buf);
+ nm.flags |= F_NOTIF_PW_STATUS;
+ break;
+ case TLV_TYPE_FEC:
+ if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf,
+ tlv_len, &nm.fec)) == -1)
+ return (-1);
+ /* allow only one fec element */
+ if (tlen != tlv_len) {
+ session_shutdown(nbr, S_BAD_TLV_VAL,
+ msg.id, msg.type);
+ return (-1);
+ }
+ nm.flags |= F_NOTIF_FEC;
+ break;
+ default:
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ send_notification_nbr(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type);
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ }
+
+ if (nm.status_code == S_PW_STATUS) {
+ if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) {
+ send_notification_nbr(nbr, S_MISS_MSG,
+ msg.id, msg.type);
+ return (-1);
+ }
+
+ switch (nm.fec.type) {
+ case MAP_TYPE_PWID:
+ break;
+ default:
+ send_notification_nbr(nbr, S_BAD_TLV_VAL,
+ msg.id, msg.type);
+ return (-1);
+ }
+ }
+
+ log_warnx("msg-in: notification: lsr-id %s, status %s%s",
+ inet_ntoa(nbr->id), status_code_name(ntohl(st.status_code)),
+ (st.status_code & htonl(STATUS_FATAL)) ? " (fatal)" : "");
+
+ if (st.status_code & htonl(STATUS_FATAL)) {
+ if (nbr->state == NBR_STA_OPENSENT)
+ nbr_start_idtimer(nbr);
+
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+ return (-1);
+ }
+
+ if (nm.status_code == S_PW_STATUS)
+ ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
+ &nm, sizeof(nm));
+
+ return (0);
+}
+
+int
+gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
+ uint16_t msg_type)
+{
+ struct status_tlv st;
+
+ memset(&st, 0, sizeof(st));
+ st.type = htons(TLV_TYPE_STATUS);
+ st.length = htons(STATUS_TLV_LEN);
+ st.status_code = htonl(status_code);
+ /*
+ * For convenience, msg_id and msg_type are already in network
+ * byte order.
+ */
+ st.msg_id = msg_id;
+ st.msg_type = msg_type;
+
+ return (ibuf_add(buf, &st, STATUS_SIZE));
+}
diff --git a/ldpd/packet.c b/ldpd/packet.c
new file mode 100644
index 000000000..7cc375c31
--- /dev/null
+++ b/ldpd/packet.c
@@ -0,0 +1,788 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@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 <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+static struct iface *disc_find_iface(unsigned int, int,
+ union ldpd_addr *, int);
+static void session_read(int, short, void *);
+static void session_write(int, short, void *);
+static ssize_t session_get_pdu(struct ibuf_read *, char **);
+static void tcp_close(struct tcp_conn *);
+static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *);
+static void pending_conn_timeout(int, short, void *);
+
+int
+gen_ldp_hdr(struct ibuf *buf, uint16_t size)
+{
+ struct ldp_hdr ldp_hdr;
+
+ memset(&ldp_hdr, 0, sizeof(ldp_hdr));
+ ldp_hdr.version = htons(LDP_VERSION);
+ /* exclude the 'Version' and 'PDU Length' fields from the total */
+ ldp_hdr.length = htons(size - LDP_HDR_DEAD_LEN);
+ ldp_hdr.lsr_id = leconf->rtr_id.s_addr;
+ ldp_hdr.lspace_id = 0;
+
+ return (ibuf_add(buf, &ldp_hdr, LDP_HDR_SIZE));
+}
+
+int
+gen_msg_hdr(struct ibuf *buf, uint16_t type, uint16_t size)
+{
+ static int msgcnt = 0;
+ struct ldp_msg msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.type = htons(type);
+ /* exclude the 'Type' and 'Length' fields from the total */
+ msg.length = htons(size - LDP_MSG_DEAD_LEN);
+ msg.id = htonl(++msgcnt);
+
+ return (ibuf_add(buf, &msg, sizeof(msg)));
+}
+
+/* send packets */
+int
+send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia,
+ void *pkt, size_t len)
+{
+ struct sockaddr *sa;
+
+ switch (af) {
+ case AF_INET:
+ if (ia && IN_MULTICAST(ntohl(dst->v4.s_addr))) {
+ /* set outgoing interface for multicast traffic */
+ if (sock_set_ipv4_mcast(ia->iface) == -1) {
+ log_debug("%s: error setting multicast "
+ "interface, %s", __func__, ia->iface->name);
+ return (-1);
+ }
+ }
+ break;
+ case AF_INET6:
+ if (ia && IN6_IS_ADDR_MULTICAST(&dst->v6)) {
+ /* set outgoing interface for multicast traffic */
+ if (sock_set_ipv6_mcast(ia->iface) == -1) {
+ log_debug("%s: error setting multicast "
+ "interface, %s", __func__, ia->iface->name);
+ return (-1);
+ }
+ }
+ break;
+ default:
+ fatalx("send_packet: unknown af");
+ }
+
+ sa = addr2sa(af, dst, LDP_PORT);
+ if (sendto(fd, pkt, len, 0, sa, sa->sa_len) == -1) {
+ log_warn("%s: error sending packet to %s", __func__,
+ log_sockaddr(sa));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* Discovery functions */
+#define CMSG_MAXLEN max(sizeof(struct sockaddr_dl), sizeof(struct in6_pktinfo))
+void
+disc_recv_packet(int fd, short event, void *bula)
+{
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(CMSG_MAXLEN)];
+ } cmsgbuf;
+ struct msghdr m;
+ struct sockaddr_storage from;
+ struct iovec iov;
+ char *buf;
+ struct cmsghdr *cmsg;
+ ssize_t r;
+ int multicast;
+ int af;
+ union ldpd_addr src;
+ unsigned int ifindex = 0;
+ struct iface *iface;
+ uint16_t len;
+ struct ldp_hdr ldp_hdr;
+ uint16_t pdu_len;
+ struct ldp_msg msg;
+ uint16_t msg_len;
+ struct in_addr lsr_id;
+
+ if (event != EV_READ)
+ return;
+
+ /* setup buffer */
+ memset(&m, 0, sizeof(m));
+ iov.iov_base = buf = pkt_ptr;
+ iov.iov_len = IBUF_READ_SIZE;
+ m.msg_name = &from;
+ m.msg_namelen = sizeof(from);
+ m.msg_iov = &iov;
+ m.msg_iovlen = 1;
+ m.msg_control = &cmsgbuf.buf;
+ m.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if ((r = recvmsg(fd, &m, 0)) == -1) {
+ if (errno != EAGAIN && errno != EINTR)
+ log_debug("%s: read error: %s", __func__,
+ strerror(errno));
+ return;
+ }
+
+ multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0;
+ sa2addr((struct sockaddr *)&from, &af, &src);
+ if (bad_addr(af, &src)) {
+ log_debug("%s: invalid source address: %s", __func__,
+ log_addr(af, &src));
+ return;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&m, cmsg)) {
+ if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVIF) {
+ ifindex = ((struct sockaddr_dl *)
+ CMSG_DATA(cmsg))->sdl_index;
+ break;
+ }
+ if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_PKTINFO) {
+ ifindex = ((struct in6_pktinfo *)
+ CMSG_DATA(cmsg))->ipi6_ifindex;
+ break;
+ }
+ }
+
+ /* find a matching interface */
+ iface = disc_find_iface(ifindex, af, &src, multicast);
+ if (iface == NULL)
+ return;
+
+ /* check packet size */
+ len = (uint16_t)r;
+ if (len < (LDP_HDR_SIZE + LDP_MSG_SIZE) || len > LDP_MAX_LEN) {
+ log_debug("%s: bad packet size, source %s", __func__,
+ log_addr(af, &src));
+ return;
+ }
+
+ /* LDP header sanity checks */
+ memcpy(&ldp_hdr, buf, sizeof(ldp_hdr));
+ if (ntohs(ldp_hdr.version) != LDP_VERSION) {
+ log_debug("%s: invalid LDP version %d, source %s", __func__,
+ ntohs(ldp_hdr.version), log_addr(af, &src));
+ return;
+ }
+ if (ntohs(ldp_hdr.lspace_id) != 0) {
+ log_debug("%s: invalid label space %u, source %s", __func__,
+ ntohs(ldp_hdr.lspace_id), log_addr(af, &src));
+ return;
+ }
+ /* check "PDU Length" field */
+ pdu_len = ntohs(ldp_hdr.length);
+ if ((pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE)) ||
+ (pdu_len > (len - LDP_HDR_DEAD_LEN))) {
+ log_debug("%s: invalid LDP packet length %u, source %s",
+ __func__, ntohs(ldp_hdr.length), log_addr(af, &src));
+ return;
+ }
+ buf += LDP_HDR_SIZE;
+ len -= LDP_HDR_SIZE;
+
+ lsr_id.s_addr = ldp_hdr.lsr_id;
+
+ /*
+ * For UDP, we process only the first message of each packet. This does
+ * not impose any restrictions since LDP uses UDP only for sending Hello
+ * packets.
+ */
+ memcpy(&msg, buf, sizeof(msg));
+
+ /* check "Message Length" field */
+ msg_len = ntohs(msg.length);
+ if (msg_len < LDP_MSG_LEN || ((msg_len + LDP_MSG_DEAD_LEN) > pdu_len)) {
+ log_debug("%s: invalid LDP message length %u, source %s",
+ __func__, ntohs(msg.length), log_addr(af, &src));
+ return;
+ }
+ buf += LDP_MSG_SIZE;
+ len -= LDP_MSG_SIZE;
+
+ /* switch LDP packet type */
+ switch (ntohs(msg.type)) {
+ case MSG_TYPE_HELLO:
+ recv_hello(lsr_id, &msg, af, &src, iface, multicast, buf, len);
+ break;
+ default:
+ log_debug("%s: unknown LDP packet type, source %s", __func__,
+ log_addr(af, &src));
+ }
+}
+
+static struct iface *
+disc_find_iface(unsigned int ifindex, int af, union ldpd_addr *src,
+ int multicast)
+{
+ struct iface *iface;
+ struct iface_af *ia;
+ struct if_addr *if_addr;
+ in_addr_t mask;
+
+ iface = if_lookup(leconf, ifindex);
+ if (iface == NULL)
+ return (NULL);
+
+ /*
+ * For unicast packets, we just need to make sure that the interface
+ * is enabled for the given address-family.
+ */
+ if (!multicast) {
+ ia = iface_af_get(iface, af);
+ if (ia->enabled)
+ return (iface);
+ return (NULL);
+ }
+
+ switch (af) {
+ case AF_INET:
+ LIST_FOREACH(if_addr, &iface->addr_list, entry) {
+ if (if_addr->af != AF_INET)
+ continue;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ if (if_addr->dstbrd.v4.s_addr == src->v4.s_addr)
+ return (iface);
+ break;
+ default:
+ mask = prefixlen2mask(if_addr->prefixlen);
+ if ((if_addr->addr.v4.s_addr & mask) ==
+ (src->v4.s_addr & mask))
+ return (iface);
+ break;
+ }
+ }
+ break;
+ case AF_INET6:
+ if (IN6_IS_ADDR_LINKLOCAL(&src->v6))
+ return (iface);
+ break;
+ default:
+ fatalx("disc_find_iface: unknown af");
+ }
+
+ return (NULL);
+}
+
+void
+session_accept(int fd, short event, void *bula)
+{
+ struct sockaddr_storage src;
+ socklen_t len = sizeof(src);
+ int newfd;
+ int af;
+ union ldpd_addr addr;
+ struct nbr *nbr;
+ struct pending_conn *pconn;
+
+ if (!(event & EV_READ))
+ return;
+
+ newfd = accept4(fd, (struct sockaddr *)&src, &len,
+ SOCK_NONBLOCK | SOCK_CLOEXEC);
+ if (newfd == -1) {
+ /*
+ * Pause accept if we are out of file descriptors, or
+ * libevent will haunt us here too.
+ */
+ if (errno == ENFILE || errno == EMFILE) {
+ accept_pause();
+ } else if (errno != EWOULDBLOCK && errno != EINTR &&
+ errno != ECONNABORTED)
+ log_debug("%s: accept error: %s", __func__,
+ strerror(errno));
+ return;
+ }
+
+ sa2addr((struct sockaddr *)&src, &af, &addr);
+
+ /*
+ * Since we don't support label spaces, we can identify this neighbor
+ * just by its source address. This way we don't need to wait for its
+ * Initialization message to know who we are talking to.
+ */
+ nbr = nbr_find_addr(af, &addr);
+ if (nbr == NULL) {
+ /*
+ * According to RFC 5036, we would need to send a No Hello
+ * Error Notification message and close this TCP connection
+ * right now. But doing so would trigger the backoff exponential
+ * timer in the remote peer, which would considerably slow down
+ * the session establishment process. The trick here is to wait
+ * five seconds before sending the Notification Message. There's
+ * a good chance that the remote peer will send us a Hello
+ * message within this interval, so it's worth waiting before
+ * taking a more drastic measure.
+ */
+ pconn = pending_conn_find(af, &addr);
+ if (pconn)
+ close(newfd);
+ else
+ pending_conn_new(newfd, af, &addr);
+ return;
+ }
+ /* protection against buggy implementations */
+ if (nbr_session_active_role(nbr)) {
+ close(newfd);
+ return;
+ }
+ if (nbr->state != NBR_STA_PRESENT) {
+ log_debug("%s: lsr-id %s: rejecting additional transport "
+ "connection", __func__, inet_ntoa(nbr->id));
+ close(newfd);
+ return;
+ }
+
+ session_accept_nbr(nbr, newfd);
+}
+
+void
+session_accept_nbr(struct nbr *nbr, int fd)
+{
+ struct nbr_params *nbrp;
+ int opt;
+ socklen_t len;
+
+ nbrp = nbr_params_find(leconf, nbr->id);
+ if (nbr_gtsm_check(fd, nbr, nbrp)) {
+ close(fd);
+ return;
+ }
+
+ if (nbrp && nbrp->auth.method == AUTH_MD5SIG) {
+ if (sysdep.no_pfkey || sysdep.no_md5sig) {
+ log_warnx("md5sig configured but not available");
+ close(fd);
+ return;
+ }
+
+ len = sizeof(opt);
+ if (getsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, &len) == -1)
+ fatal("getsockopt TCP_MD5SIG");
+ if (!opt) { /* non-md5'd connection! */
+ log_warnx("connection attempt without md5 signature");
+ close(fd);
+ return;
+ }
+ }
+
+ nbr->tcp = tcp_new(fd, nbr);
+ nbr_fsm(nbr, NBR_EVT_MATCH_ADJ);
+}
+
+static void
+session_read(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+ struct tcp_conn *tcp = nbr->tcp;
+ struct ldp_hdr *ldp_hdr;
+ struct ldp_msg *msg;
+ char *buf, *pdu;
+ ssize_t n, len;
+ uint16_t pdu_len, msg_len, msg_size, max_pdu_len;
+ int ret;
+
+ if (event != EV_READ)
+ return;
+
+ if ((n = read(fd, tcp->rbuf->buf + tcp->rbuf->wpos,
+ sizeof(tcp->rbuf->buf) - tcp->rbuf->wpos)) == -1) {
+ if (errno != EINTR && errno != EAGAIN) {
+ log_warn("%s: read error", __func__);
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+ return;
+ }
+ /* retry read */
+ return;
+ }
+ if (n == 0) {
+ /* connection closed */
+ log_debug("%s: connection closed by remote end", __func__);
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+ return;
+ }
+ tcp->rbuf->wpos += n;
+
+ while ((len = session_get_pdu(tcp->rbuf, &buf)) > 0) {
+ pdu = buf;
+ ldp_hdr = (struct ldp_hdr *)pdu;
+ if (ntohs(ldp_hdr->version) != LDP_VERSION) {
+ session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0);
+ free(buf);
+ return;
+ }
+
+ pdu_len = ntohs(ldp_hdr->length);
+ /*
+ * RFC 5036 - Section 3.5.3:
+ * "Prior to completion of the negotiation, the maximum
+ * allowable length is 4096 bytes".
+ */
+ if (nbr->state == NBR_STA_OPER)
+ max_pdu_len = nbr->max_pdu_len;
+ else
+ max_pdu_len = LDP_MAX_LEN;
+ if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) ||
+ pdu_len > max_pdu_len) {
+ session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
+ free(buf);
+ return;
+ }
+ pdu_len -= LDP_HDR_PDU_LEN;
+ if (ldp_hdr->lsr_id != nbr->id.s_addr ||
+ ldp_hdr->lspace_id != 0) {
+ session_shutdown(nbr, S_BAD_LDP_ID, 0, 0);
+ free(buf);
+ return;
+ }
+ pdu += LDP_HDR_SIZE;
+ len -= LDP_HDR_SIZE;
+
+ nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
+
+ while (len >= LDP_MSG_SIZE) {
+ uint16_t type;
+
+ msg = (struct ldp_msg *)pdu;
+ type = ntohs(msg->type);
+ msg_len = ntohs(msg->length);
+ if (msg_len < LDP_MSG_LEN ||
+ (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ free(buf);
+ return;
+ }
+ msg_size = msg_len + LDP_MSG_DEAD_LEN;
+ pdu_len -= msg_size;
+
+ /* check for error conditions earlier */
+ switch (type) {
+ case MSG_TYPE_INIT:
+ if ((nbr->state != NBR_STA_INITIAL) &&
+ (nbr->state != NBR_STA_OPENSENT)) {
+ session_shutdown(nbr, S_SHUTDOWN,
+ msg->id, msg->type);
+ free(buf);
+ return;
+ }
+ break;
+ case MSG_TYPE_KEEPALIVE:
+ if ((nbr->state == NBR_STA_INITIAL) ||
+ (nbr->state == NBR_STA_OPENSENT)) {
+ session_shutdown(nbr, S_SHUTDOWN,
+ msg->id, msg->type);
+ free(buf);
+ return;
+ }
+ break;
+ case MSG_TYPE_ADDR:
+ case MSG_TYPE_ADDRWITHDRAW:
+ case MSG_TYPE_LABELMAPPING:
+ case MSG_TYPE_LABELREQUEST:
+ case MSG_TYPE_LABELWITHDRAW:
+ case MSG_TYPE_LABELRELEASE:
+ case MSG_TYPE_LABELABORTREQ:
+ if (nbr->state != NBR_STA_OPER) {
+ session_shutdown(nbr, S_SHUTDOWN,
+ msg->id, msg->type);
+ free(buf);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* switch LDP packet type */
+ switch (type) {
+ case MSG_TYPE_NOTIFICATION:
+ ret = recv_notification(nbr, pdu, msg_size);
+ break;
+ case MSG_TYPE_INIT:
+ ret = recv_init(nbr, pdu, msg_size);
+ break;
+ case MSG_TYPE_KEEPALIVE:
+ ret = recv_keepalive(nbr, pdu, msg_size);
+ break;
+ case MSG_TYPE_ADDR:
+ case MSG_TYPE_ADDRWITHDRAW:
+ ret = recv_address(nbr, pdu, msg_size);
+ break;
+ case MSG_TYPE_LABELMAPPING:
+ case MSG_TYPE_LABELREQUEST:
+ case MSG_TYPE_LABELWITHDRAW:
+ case MSG_TYPE_LABELRELEASE:
+ case MSG_TYPE_LABELABORTREQ:
+ ret = recv_labelmessage(nbr, pdu, msg_size,
+ type);
+ break;
+ default:
+ log_debug("%s: unknown LDP message from nbr %s",
+ __func__, inet_ntoa(nbr->id));
+ if (!(ntohs(msg->type) & UNKNOWN_FLAG))
+ send_notification_nbr(nbr,
+ S_UNKNOWN_MSG, msg->id, msg->type);
+ /* ignore the message */
+ ret = 0;
+ break;
+ }
+
+ if (ret == -1) {
+ /* parser failed, giving up */
+ free(buf);
+ return;
+ }
+
+ /* Analyse the next message */
+ pdu += msg_size;
+ len -= msg_size;
+ }
+ free(buf);
+ if (len != 0) {
+ session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
+ return;
+ }
+ }
+}
+
+static void
+session_write(int fd, short event, void *arg)
+{
+ struct tcp_conn *tcp = arg;
+ struct nbr *nbr = tcp->nbr;
+
+ if (!(event & EV_WRITE))
+ return;
+
+ if (msgbuf_write(&tcp->wbuf.wbuf) <= 0)
+ if (errno != EAGAIN && nbr)
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+
+ if (nbr == NULL && !tcp->wbuf.wbuf.queued) {
+ /*
+ * We are done sending the notification message, now we can
+ * close the socket.
+ */
+ tcp_close(tcp);
+ return;
+ }
+
+ evbuf_event_add(&tcp->wbuf);
+}
+
+void
+session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id,
+ uint32_t msg_type)
+{
+ switch (nbr->state) {
+ case NBR_STA_PRESENT:
+ if (nbr_pending_connect(nbr))
+ event_del(&nbr->ev_connect);
+ break;
+ case NBR_STA_INITIAL:
+ case NBR_STA_OPENREC:
+ case NBR_STA_OPENSENT:
+ case NBR_STA_OPER:
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ send_notification_nbr(nbr, status, msg_id, msg_type);
+
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+ break;
+ default:
+ fatalx("session_shutdown: unknown neighbor state");
+ }
+}
+
+void
+session_close(struct nbr *nbr)
+{
+ log_debug("%s: closing session with lsr-id %s", __func__,
+ inet_ntoa(nbr->id));
+
+ tcp_close(nbr->tcp);
+ nbr_stop_ktimer(nbr);
+ nbr_stop_ktimeout(nbr);
+ nbr_stop_itimeout(nbr);
+}
+
+static ssize_t
+session_get_pdu(struct ibuf_read *r, char **b)
+{
+ struct ldp_hdr l;
+ size_t av, dlen, left;
+
+ av = r->wpos;
+ if (av < sizeof(l))
+ return (0);
+
+ memcpy(&l, r->buf, sizeof(l));
+ dlen = ntohs(l.length) + LDP_HDR_DEAD_LEN;
+ if (dlen > av)
+ return (0);
+
+ if ((*b = malloc(dlen)) == NULL)
+ return (-1);
+
+ memcpy(*b, r->buf, dlen);
+ if (dlen < av) {
+ left = av - dlen;
+ memmove(r->buf, r->buf + dlen, left);
+ r->wpos = left;
+ } else
+ r->wpos = 0;
+
+ return (dlen);
+}
+
+struct tcp_conn *
+tcp_new(int fd, struct nbr *nbr)
+{
+ struct tcp_conn *tcp;
+
+ if ((tcp = calloc(1, sizeof(*tcp))) == NULL)
+ fatal(__func__);
+
+ tcp->fd = fd;
+ evbuf_init(&tcp->wbuf, tcp->fd, session_write, tcp);
+
+ if (nbr) {
+ if ((tcp->rbuf = calloc(1, sizeof(struct ibuf_read))) == NULL)
+ fatal(__func__);
+
+ event_set(&tcp->rev, tcp->fd, EV_READ | EV_PERSIST,
+ session_read, nbr);
+ event_add(&tcp->rev, NULL);
+ tcp->nbr = nbr;
+ }
+
+ return (tcp);
+}
+
+static void
+tcp_close(struct tcp_conn *tcp)
+{
+ /* try to flush write buffer */
+ msgbuf_write(&tcp->wbuf.wbuf);
+ evbuf_clear(&tcp->wbuf);
+
+ if (tcp->nbr) {
+ event_del(&tcp->rev);
+ free(tcp->rbuf);
+ tcp->nbr->tcp = NULL;
+ }
+
+ close(tcp->fd);
+ accept_unpause();
+ free(tcp);
+}
+
+static struct pending_conn *
+pending_conn_new(int fd, int af, union ldpd_addr *addr)
+{
+ struct pending_conn *pconn;
+ struct timeval tv;
+
+ if ((pconn = calloc(1, sizeof(*pconn))) == NULL)
+ fatal(__func__);
+
+ pconn->fd = fd;
+ pconn->af = af;
+ pconn->addr = *addr;
+ evtimer_set(&pconn->ev_timeout, pending_conn_timeout, pconn);
+ TAILQ_INSERT_TAIL(&global.pending_conns, pconn, entry);
+
+ timerclear(&tv);
+ tv.tv_sec = PENDING_CONN_TIMEOUT;
+ if (evtimer_add(&pconn->ev_timeout, &tv) == -1)
+ fatal(__func__);
+
+ return (pconn);
+}
+
+void
+pending_conn_del(struct pending_conn *pconn)
+{
+ if (evtimer_pending(&pconn->ev_timeout, NULL) &&
+ evtimer_del(&pconn->ev_timeout) == -1)
+ fatal(__func__);
+
+ TAILQ_REMOVE(&global.pending_conns, pconn, entry);
+ free(pconn);
+}
+
+struct pending_conn *
+pending_conn_find(int af, union ldpd_addr *addr)
+{
+ struct pending_conn *pconn;
+
+ TAILQ_FOREACH(pconn, &global.pending_conns, entry)
+ if (af == pconn->af &&
+ ldp_addrcmp(af, addr, &pconn->addr) == 0)
+ return (pconn);
+
+ return (NULL);
+}
+
+static void
+pending_conn_timeout(int fd, short event, void *arg)
+{
+ struct pending_conn *pconn = arg;
+ struct tcp_conn *tcp;
+
+ log_debug("%s: no adjacency with remote end: %s", __func__,
+ log_addr(pconn->af, &pconn->addr));
+
+ /*
+ * Create a write buffer detached from any neighbor to send a
+ * notification message reliably.
+ */
+ tcp = tcp_new(pconn->fd, NULL);
+ send_notification(S_NO_HELLO, tcp, 0, 0);
+ msgbuf_write(&tcp->wbuf.wbuf);
+
+ pending_conn_del(pconn);
+}
diff --git a/ldpd/pfkey.c b/ldpd/pfkey.c
new file mode 100644
index 000000000..f0f16c867
--- /dev/null
+++ b/ldpd/pfkey.c
@@ -0,0 +1,466 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2003, 2004 Markus Friedl <markus@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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+static int pfkey_send(int, uint8_t, uint8_t, uint8_t,
+ int, union ldpd_addr *, union ldpd_addr *,
+ uint32_t, uint8_t, int, char *, uint8_t, int, char *,
+ uint16_t, uint16_t);
+static int pfkey_reply(int, uint32_t *);
+static int pfkey_sa_add(int, union ldpd_addr *, union ldpd_addr *,
+ uint8_t, char *, uint32_t *);
+static int pfkey_sa_remove(int, union ldpd_addr *, union ldpd_addr *,
+ uint32_t *);
+static int pfkey_md5sig_establish(struct nbr *, struct nbr_params *nbrp);
+static int pfkey_md5sig_remove(struct nbr *);
+
+#define PFKEY2_CHUNK sizeof(uint64_t)
+#define ROUNDUP(x) (((x) + (PFKEY2_CHUNK - 1)) & ~(PFKEY2_CHUNK - 1))
+#define IOV_CNT 20
+
+static uint32_t sadb_msg_seq;
+static uint32_t pid; /* should pid_t but pfkey needs uint32_t */
+static int fd;
+
+static int
+pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
+ int af, union ldpd_addr *src, union ldpd_addr *dst, uint32_t spi,
+ uint8_t aalg, int alen, char *akey, uint8_t ealg, int elen, char *ekey,
+ uint16_t sport, uint16_t dport)
+{
+ struct sadb_msg smsg;
+ struct sadb_sa sa;
+ struct sadb_address sa_src, sa_dst;
+ struct sadb_key sa_akey, sa_ekey;
+ struct sadb_spirange sa_spirange;
+ struct iovec iov[IOV_CNT];
+ ssize_t n;
+ int len = 0;
+ int iov_cnt;
+ struct sockaddr_storage ssrc, sdst, smask, dmask;
+ struct sockaddr *saptr;
+
+ if (!pid)
+ pid = getpid();
+
+ /* we need clean sockaddr... no ports set */
+ memset(&ssrc, 0, sizeof(ssrc));
+ memset(&smask, 0, sizeof(smask));
+ if ((saptr = addr2sa(af, src, 0)))
+ memcpy(&ssrc, saptr, sizeof(ssrc));
+ switch (af) {
+ case AF_INET:
+ memset(&((struct sockaddr_in *)&smask)->sin_addr, 0xff, 32/8);
+ break;
+ case AF_INET6:
+ memset(&((struct sockaddr_in6 *)&smask)->sin6_addr, 0xff,
+ 128/8);
+ break;
+ default:
+ return (-1);
+ }
+ smask.ss_family = ssrc.ss_family;
+ smask.ss_len = ssrc.ss_len;
+
+ memset(&sdst, 0, sizeof(sdst));
+ memset(&dmask, 0, sizeof(dmask));
+ if ((saptr = addr2sa(af, dst, 0)))
+ memcpy(&sdst, saptr, sizeof(sdst));
+ switch (af) {
+ case AF_INET:
+ memset(&((struct sockaddr_in *)&dmask)->sin_addr, 0xff, 32/8);
+ break;
+ case AF_INET6:
+ memset(&((struct sockaddr_in6 *)&dmask)->sin6_addr, 0xff,
+ 128/8);
+ break;
+ default:
+ return (-1);
+ }
+ dmask.ss_family = sdst.ss_family;
+ dmask.ss_len = sdst.ss_len;
+
+ memset(&smsg, 0, sizeof(smsg));
+ smsg.sadb_msg_version = PF_KEY_V2;
+ smsg.sadb_msg_seq = ++sadb_msg_seq;
+ smsg.sadb_msg_pid = pid;
+ smsg.sadb_msg_len = sizeof(smsg) / 8;
+ smsg.sadb_msg_type = mtype;
+ smsg.sadb_msg_satype = satype;
+
+ switch (mtype) {
+ case SADB_GETSPI:
+ memset(&sa_spirange, 0, sizeof(sa_spirange));
+ sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+ sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8;
+ sa_spirange.sadb_spirange_min = 0x100;
+ sa_spirange.sadb_spirange_max = 0xffffffff;
+ sa_spirange.sadb_spirange_reserved = 0;
+ break;
+ case SADB_ADD:
+ case SADB_UPDATE:
+ case SADB_DELETE:
+ memset(&sa, 0, sizeof(sa));
+ sa.sadb_sa_exttype = SADB_EXT_SA;
+ sa.sadb_sa_len = sizeof(sa) / 8;
+ sa.sadb_sa_replay = 0;
+ sa.sadb_sa_spi = spi;
+ sa.sadb_sa_state = SADB_SASTATE_MATURE;
+ break;
+ }
+
+ memset(&sa_src, 0, sizeof(sa_src));
+ sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
+
+ memset(&sa_dst, 0, sizeof(sa_dst));
+ sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
+
+ sa.sadb_sa_auth = aalg;
+ sa.sadb_sa_encrypt = SADB_X_EALG_AES; /* XXX */
+
+ switch (mtype) {
+ case SADB_ADD:
+ case SADB_UPDATE:
+ memset(&sa_akey, 0, sizeof(sa_akey));
+ sa_akey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
+ sa_akey.sadb_key_len = (sizeof(sa_akey) +
+ ((alen + 7) / 8) * 8) / 8;
+ sa_akey.sadb_key_bits = 8 * alen;
+
+ memset(&sa_ekey, 0, sizeof(sa_ekey));
+ sa_ekey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+ sa_ekey.sadb_key_len = (sizeof(sa_ekey) +
+ ((elen + 7) / 8) * 8) / 8;
+ sa_ekey.sadb_key_bits = 8 * elen;
+
+ break;
+ }
+
+ iov_cnt = 0;
+
+ /* msghdr */
+ iov[iov_cnt].iov_base = &smsg;
+ iov[iov_cnt].iov_len = sizeof(smsg);
+ iov_cnt++;
+
+ switch (mtype) {
+ case SADB_ADD:
+ case SADB_UPDATE:
+ case SADB_DELETE:
+ /* SA hdr */
+ iov[iov_cnt].iov_base = &sa;
+ iov[iov_cnt].iov_len = sizeof(sa);
+ smsg.sadb_msg_len += sa.sadb_sa_len;
+ iov_cnt++;
+ break;
+ case SADB_GETSPI:
+ /* SPI range */
+ iov[iov_cnt].iov_base = &sa_spirange;
+ iov[iov_cnt].iov_len = sizeof(sa_spirange);
+ smsg.sadb_msg_len += sa_spirange.sadb_spirange_len;
+ iov_cnt++;
+ break;
+ }
+
+ /* dest addr */
+ iov[iov_cnt].iov_base = &sa_dst;
+ iov[iov_cnt].iov_len = sizeof(sa_dst);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &sdst;
+ iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
+ smsg.sadb_msg_len += sa_dst.sadb_address_len;
+ iov_cnt++;
+
+ /* src addr */
+ iov[iov_cnt].iov_base = &sa_src;
+ iov[iov_cnt].iov_len = sizeof(sa_src);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &ssrc;
+ iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
+ smsg.sadb_msg_len += sa_src.sadb_address_len;
+ iov_cnt++;
+
+ switch (mtype) {
+ case SADB_ADD:
+ case SADB_UPDATE:
+ if (alen) {
+ /* auth key */
+ iov[iov_cnt].iov_base = &sa_akey;
+ iov[iov_cnt].iov_len = sizeof(sa_akey);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = akey;
+ iov[iov_cnt].iov_len = ((alen + 7) / 8) * 8;
+ smsg.sadb_msg_len += sa_akey.sadb_key_len;
+ iov_cnt++;
+ }
+ if (elen) {
+ /* encryption key */
+ iov[iov_cnt].iov_base = &sa_ekey;
+ iov[iov_cnt].iov_len = sizeof(sa_ekey);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = ekey;
+ iov[iov_cnt].iov_len = ((elen + 7) / 8) * 8;
+ smsg.sadb_msg_len += sa_ekey.sadb_key_len;
+ iov_cnt++;
+ }
+ break;
+ }
+
+ len = smsg.sadb_msg_len * 8;
+ do {
+ n = writev(sd, iov, iov_cnt);
+ } while (n == -1 && (errno == EAGAIN || errno == EINTR));
+
+ if (n == -1) {
+ log_warn("writev (%d/%d)", iov_cnt, len);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+pfkey_read(int sd, struct sadb_msg *h)
+{
+ struct sadb_msg hdr;
+
+ if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
+ if (errno == EAGAIN || errno == EINTR)
+ return (0);
+ log_warn("pfkey peek");
+ return (-1);
+ }
+
+ /* XXX: Only one message can be outstanding. */
+ if (hdr.sadb_msg_seq == sadb_msg_seq &&
+ hdr.sadb_msg_pid == pid) {
+ if (h)
+ *h = hdr;
+ return (0);
+ }
+
+ /* not ours, discard */
+ if (read(sd, &hdr, sizeof(hdr)) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ return (0);
+ log_warn("pfkey read");
+ return (-1);
+ }
+
+ return (1);
+}
+
+static int
+pfkey_reply(int sd, uint32_t *spip)
+{
+ struct sadb_msg hdr, *msg;
+ struct sadb_ext *ext;
+ struct sadb_sa *sa;
+ uint8_t *data;
+ ssize_t len;
+ int rv;
+
+ do {
+ rv = pfkey_read(sd, &hdr);
+ if (rv == -1)
+ return (-1);
+ } while (rv);
+
+ if (hdr.sadb_msg_errno != 0) {
+ errno = hdr.sadb_msg_errno;
+ if (errno == ESRCH)
+ return (0);
+ else {
+ log_warn("pfkey");
+ return (-1);
+ }
+ }
+ if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEY2_CHUNK)) == NULL) {
+ log_warn("pfkey malloc");
+ return (-1);
+ }
+ len = hdr.sadb_msg_len * PFKEY2_CHUNK;
+ if (read(sd, data, len) != len) {
+ log_warn("pfkey read");
+ explicit_bzero(data, len);
+ free(data);
+ return (-1);
+ }
+
+ if (hdr.sadb_msg_type == SADB_GETSPI) {
+ if (spip == NULL) {
+ explicit_bzero(data, len);
+ free(data);
+ return (0);
+ }
+
+ msg = (struct sadb_msg *)data;
+ for (ext = (struct sadb_ext *)(msg + 1);
+ (size_t)((uint8_t *)ext - (uint8_t *)msg) <
+ msg->sadb_msg_len * PFKEY2_CHUNK;
+ ext = (struct sadb_ext *)((uint8_t *)ext +
+ ext->sadb_ext_len * PFKEY2_CHUNK)) {
+ if (ext->sadb_ext_type == SADB_EXT_SA) {
+ sa = (struct sadb_sa *) ext;
+ *spip = sa->sadb_sa_spi;
+ break;
+ }
+ }
+ }
+ explicit_bzero(data, len);
+ free(data);
+ return (0);
+}
+
+static int
+pfkey_sa_add(int af, union ldpd_addr *src, union ldpd_addr *dst, uint8_t keylen,
+ char *key, uint32_t *spi)
+{
+ if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_GETSPI, 0,
+ af, src, dst, 0, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0)
+ return (-1);
+ if (pfkey_reply(fd, spi) < 0)
+ return (-1);
+ if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_UPDATE, 0,
+ af, src, dst, *spi, 0, keylen, key, 0, 0, NULL, 0, 0) < 0)
+ return (-1);
+ if (pfkey_reply(fd, NULL) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+pfkey_sa_remove(int af, union ldpd_addr *src, union ldpd_addr *dst,
+ uint32_t *spi)
+{
+ if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_DELETE, 0,
+ af, src, dst, *spi, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0)
+ return (-1);
+ if (pfkey_reply(fd, NULL) < 0)
+ return (-1);
+ *spi = 0;
+ return (0);
+}
+
+static int
+pfkey_md5sig_establish(struct nbr *nbr, struct nbr_params *nbrp)
+{
+ sleep(1);
+
+ if (!nbr->auth.spi_out)
+ if (pfkey_sa_add(nbr->af, &nbr->laddr, &nbr->raddr,
+ nbrp->auth.md5key_len, nbrp->auth.md5key,
+ &nbr->auth.spi_out) == -1)
+ return (-1);
+ if (!nbr->auth.spi_in)
+ if (pfkey_sa_add(nbr->af, &nbr->raddr, &nbr->laddr,
+ nbrp->auth.md5key_len, nbrp->auth.md5key,
+ &nbr->auth.spi_in) == -1)
+ return (-1);
+
+ nbr->auth.established = 1;
+ return (0);
+}
+
+static int
+pfkey_md5sig_remove(struct nbr *nbr)
+{
+ if (nbr->auth.spi_out)
+ if (pfkey_sa_remove(nbr->af, &nbr->laddr, &nbr->raddr,
+ &nbr->auth.spi_out) == -1)
+ return (-1);
+ if (nbr->auth.spi_in)
+ if (pfkey_sa_remove(nbr->af, &nbr->raddr, &nbr->laddr,
+ &nbr->auth.spi_in) == -1)
+ return (-1);
+
+ nbr->auth.established = 0;
+ nbr->auth.spi_in = 0;
+ nbr->auth.spi_out = 0;
+ nbr->auth.method = AUTH_NONE;
+ memset(nbr->auth.md5key, 0, sizeof(nbr->auth.md5key));
+
+ return (0);
+}
+
+int
+pfkey_establish(struct nbr *nbr, struct nbr_params *nbrp)
+{
+ if (nbrp->auth.method == AUTH_NONE)
+ return (0);
+
+ /*
+ * make sure we keep copies of everything we need to
+ * remove SAs and flows later again.
+ */
+ nbr->auth.method = nbrp->auth.method;
+
+ switch (nbr->auth.method) {
+ case AUTH_MD5SIG:
+ strlcpy(nbr->auth.md5key, nbrp->auth.md5key,
+ sizeof(nbr->auth.md5key));
+ return (pfkey_md5sig_establish(nbr, nbrp));
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+int
+pfkey_remove(struct nbr *nbr)
+{
+ if (nbr->auth.method == AUTH_NONE || !nbr->auth.established)
+ return (0);
+
+ switch (nbr->auth.method) {
+ case AUTH_MD5SIG:
+ return (pfkey_md5sig_remove(nbr));
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+int
+pfkey_init(void)
+{
+ if ((fd = socket(PF_KEY, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ PF_KEY_V2)) == -1) {
+ if (errno == EPROTONOSUPPORT) {
+ log_warnx("PF_KEY not available");
+ sysdep.no_pfkey = 1;
+ return (-1);
+ } else
+ fatal("pfkey setup failed");
+ }
+ return (fd);
+}
diff --git a/ldpd/socket.c b/ldpd/socket.c
new file mode 100644
index 000000000..8f26771df
--- /dev/null
+++ b/ldpd/socket.c
@@ -0,0 +1,391 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@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 <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+int
+ldp_create_socket(int af, enum socket_type type)
+{
+ int fd, domain, proto;
+ union ldpd_addr addr;
+ struct sockaddr_storage local_sa;
+ int opt;
+
+ /* create socket */
+ switch (type) {
+ case LDP_SOCKET_DISC:
+ case LDP_SOCKET_EDISC:
+ domain = SOCK_DGRAM;
+ proto = IPPROTO_UDP;
+ break;
+ case LDP_SOCKET_SESSION:
+ domain = SOCK_STREAM;
+ proto = IPPROTO_TCP;
+ break;
+ default:
+ fatalx("ldp_create_socket: unknown socket type");
+ }
+ fd = socket(af, domain | SOCK_NONBLOCK | SOCK_CLOEXEC, proto);
+ if (fd == -1) {
+ log_warn("%s: error creating socket", __func__);
+ return (-1);
+ }
+
+ /* bind to a local address/port */
+ switch (type) {
+ case LDP_SOCKET_DISC:
+ /* listen on all addresses */
+ memset(&addr, 0, sizeof(addr));
+ memcpy(&local_sa, addr2sa(af, &addr, LDP_PORT),
+ sizeof(local_sa));
+ break;
+ case LDP_SOCKET_EDISC:
+ case LDP_SOCKET_SESSION:
+ addr = (ldp_af_conf_get(ldpd_conf, af))->trans_addr;
+ memcpy(&local_sa, addr2sa(af, &addr, LDP_PORT),
+ sizeof(local_sa));
+ if (sock_set_bindany(fd, 1) == -1) {
+ close(fd);
+ return (-1);
+ }
+ break;
+ }
+ if (sock_set_reuse(fd, 1) == -1) {
+ close(fd);
+ return (-1);
+ }
+ if (bind(fd, (struct sockaddr *)&local_sa, local_sa.ss_len) == -1) {
+ log_warn("%s: error binding socket", __func__);
+ close(fd);
+ return (-1);
+ }
+
+ /* set options */
+ switch (af) {
+ case AF_INET:
+ if (sock_set_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL) == -1) {
+ close(fd);
+ return (-1);
+ }
+ if (type == LDP_SOCKET_DISC) {
+ if (sock_set_ipv4_mcast_ttl(fd,
+ IP_DEFAULT_MULTICAST_TTL) == -1) {
+ close(fd);
+ return (-1);
+ }
+ if (sock_set_ipv4_mcast_loop(fd) == -1) {
+ close(fd);
+ return (-1);
+ }
+ }
+ if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) {
+ if (sock_set_ipv4_recvif(fd, 1) == -1) {
+ close(fd);
+ return (-1);
+ }
+ }
+ if (type == LDP_SOCKET_SESSION) {
+ if (sock_set_ipv4_ucast_ttl(fd, 255) == -1) {
+ close(fd);
+ return (-1);
+ }
+ }
+ break;
+ case AF_INET6:
+ if (sock_set_ipv6_dscp(fd, IPTOS_PREC_INTERNETCONTROL) == -1) {
+ close(fd);
+ return (-1);
+ }
+ if (type == LDP_SOCKET_DISC) {
+ if (sock_set_ipv6_mcast_loop(fd) == -1) {
+ close(fd);
+ return (-1);
+ }
+ if (sock_set_ipv6_mcast_hops(fd, 255) == -1) {
+ close(fd);
+ return (-1);
+ }
+ if (!(ldpd_conf->ipv6.flags & F_LDPD_AF_NO_GTSM)) {
+ if (sock_set_ipv6_minhopcount(fd, 255) == -1) {
+ close(fd);
+ return (-1);
+ }
+ }
+ }
+ if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) {
+ if (sock_set_ipv6_pktinfo(fd, 1) == -1) {
+ close(fd);
+ return (-1);
+ }
+ }
+ if (type == LDP_SOCKET_SESSION) {
+ if (sock_set_ipv6_ucast_hops(fd, 255) == -1) {
+ close(fd);
+ return (-1);
+ }
+ }
+ break;
+ }
+ switch (type) {
+ case LDP_SOCKET_DISC:
+ case LDP_SOCKET_EDISC:
+ sock_set_recvbuf(fd);
+ break;
+ case LDP_SOCKET_SESSION:
+ if (listen(fd, LDP_BACKLOG) == -1)
+ log_warn("%s: error listening on socket", __func__);
+
+ opt = 1;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt,
+ sizeof(opt)) == -1) {
+ if (errno == ENOPROTOOPT) { /* system w/o md5sig */
+ log_warnx("md5sig not available, disabling");
+ sysdep.no_md5sig = 1;
+ } else {
+ close(fd);
+ return (-1);
+ }
+ }
+ break;
+ }
+
+ return (fd);
+}
+
+void
+sock_set_recvbuf(int fd)
+{
+ int bsize;
+
+ bsize = 65535;
+ while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
+ sizeof(bsize)) == -1)
+ bsize /= 2;
+}
+
+int
+sock_set_reuse(int fd, int enable)
+{
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
+ sizeof(int)) < 0) {
+ log_warn("%s: error setting SO_REUSEADDR", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_bindany(int fd, int enable)
+{
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable,
+ sizeof(int)) < 0) {
+ log_warn("%s: error setting SO_BINDANY", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv4_tos(int fd, int tos)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
+ log_warn("%s: error setting IP_TOS to 0x%x", __func__, tos);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv4_recvif(int fd, int enable)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
+ sizeof(enable)) < 0) {
+ log_warn("%s: error setting IP_RECVIF", __func__);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+sock_set_ipv4_minttl(int fd, int ttl)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) {
+ log_warn("%s: error setting IP_MINTTL", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv4_ucast_ttl(int fd, int ttl)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
+ log_warn("%s: error setting IP_TTL", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ log_warn("%s: error setting IP_MULTICAST_TTL to %d",
+ __func__, ttl);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv4_mcast(struct iface *iface)
+{
+ in_addr_t addr;
+
+ addr = if_get_ipv4_addr(iface);
+
+ if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP, IP_MULTICAST_IF,
+ &addr, sizeof(addr)) < 0) {
+ log_warn("%s: error setting IP_MULTICAST_IF, interface %s",
+ __func__, iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv4_mcast_loop(int fd)
+{
+ uint8_t loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0) {
+ log_warn("%s: error setting IP_MULTICAST_LOOP", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv6_dscp(int fd, int dscp)
+{
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &dscp,
+ sizeof(dscp)) < 0) {
+ log_warn("%s: error setting IPV6_TCLASS", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv6_pktinfo(int fd, int enable)
+{
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable,
+ sizeof(enable)) < 0) {
+ log_warn("%s: error setting IPV6_RECVPKTINFO", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv6_minhopcount(int fd, int hoplimit)
+{
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MINHOPCOUNT,
+ &hoplimit, sizeof(hoplimit)) < 0) {
+ log_warn("%s: error setting IPV6_MINHOPCOUNT", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv6_ucast_hops(int fd, int hoplimit)
+{
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ &hoplimit, sizeof(hoplimit)) < 0) {
+ log_warn("%s: error setting IPV6_UNICAST_HOPS", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv6_mcast_hops(int fd, int hoplimit)
+{
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &hoplimit, sizeof(hoplimit)) < 0) {
+ log_warn("%s: error setting IPV6_MULTICAST_HOPS", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv6_mcast(struct iface *iface)
+{
+ if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
+ IPV6_MULTICAST_IF, &iface->ifindex, sizeof(iface->ifindex)) < 0) {
+ log_warn("%s: error setting IPV6_MULTICAST_IF, interface %s",
+ __func__, iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+sock_set_ipv6_mcast_loop(int fd)
+{
+ unsigned int loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &loop, sizeof(loop)) < 0) {
+ log_warn("%s: error setting IPV6_MULTICAST_LOOP", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/ldpd/util.c b/ldpd/util.c
new file mode 100644
index 000000000..981a5c8c2
--- /dev/null
+++ b/ldpd/util.c
@@ -0,0 +1,356 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2012 Alexander Bluhm <bluhm@openbsd.org>
+ * Copyright (c) 2004 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 <string.h>
+
+#include "ldpd.h"
+#include "log.h"
+
+uint8_t
+mask2prefixlen(in_addr_t ina)
+{
+ if (ina == 0)
+ return (0);
+ else
+ return (33 - ffs(ntohl(ina)));
+}
+
+uint8_t
+mask2prefixlen6(struct sockaddr_in6 *sa_in6)
+{
+ uint8_t l = 0, *ap, *ep;
+
+ /*
+ * sin6_len is the size of the sockaddr so substract the offset of
+ * the possibly truncated sin6_addr struct.
+ */
+ ap = (uint8_t *)&sa_in6->sin6_addr;
+ ep = (uint8_t *)sa_in6 + sa_in6->sin6_len;
+ for (; ap < ep; ap++) {
+ /* this "beauty" is adopted from sbin/route/show.c ... */
+ switch (*ap) {
+ case 0xff:
+ l += 8;
+ break;
+ case 0xfe:
+ l += 7;
+ return (l);
+ case 0xfc:
+ l += 6;
+ return (l);
+ case 0xf8:
+ l += 5;
+ return (l);
+ case 0xf0:
+ l += 4;
+ return (l);
+ case 0xe0:
+ l += 3;
+ return (l);
+ case 0xc0:
+ l += 2;
+ return (l);
+ case 0x80:
+ l += 1;
+ return (l);
+ case 0x00:
+ return (l);
+ default:
+ fatalx("non contiguous inet6 netmask");
+ }
+ }
+
+ return (l);
+}
+
+in_addr_t
+prefixlen2mask(uint8_t prefixlen)
+{
+ if (prefixlen == 0)
+ return (0);
+
+ return (htonl(0xffffffff << (32 - prefixlen)));
+}
+
+struct in6_addr *
+prefixlen2mask6(uint8_t prefixlen)
+{
+ static struct in6_addr mask;
+ int i;
+
+ memset(&mask, 0, sizeof(mask));
+ for (i = 0; i < prefixlen / 8; i++)
+ mask.s6_addr[i] = 0xff;
+ i = prefixlen % 8;
+ if (i)
+ mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+ return (&mask);
+}
+
+void
+ldp_applymask(int af, union ldpd_addr *dest, const union ldpd_addr *src,
+ int prefixlen)
+{
+ struct in6_addr mask;
+ int i;
+
+ switch (af) {
+ case AF_INET:
+ dest->v4.s_addr = src->v4.s_addr & prefixlen2mask(prefixlen);
+ break;
+ case AF_INET6:
+ memset(&mask, 0, sizeof(mask));
+ for (i = 0; i < prefixlen / 8; i++)
+ mask.s6_addr[i] = 0xff;
+ i = prefixlen % 8;
+ if (i)
+ mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+ for (i = 0; i < 16; i++)
+ dest->v6.s6_addr[i] = src->v6.s6_addr[i] &
+ mask.s6_addr[i];
+ break;
+ default:
+ fatalx("ldp_applymask: unknown af");
+ }
+}
+
+int
+ldp_addrcmp(int af, const union ldpd_addr *a, const union ldpd_addr *b)
+{
+ switch (af) {
+ case AF_INET:
+ if (a->v4.s_addr == b->v4.s_addr)
+ return (0);
+ return ((ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr)) ? 1 : -1);
+ case AF_INET6:
+ return (memcmp(&a->v6, &b->v6, sizeof(struct in6_addr)));
+ default:
+ fatalx("ldp_addrcmp: unknown af");
+ }
+}
+
+int
+ldp_addrisset(int af, const union ldpd_addr *addr)
+{
+ switch (af) {
+ case AF_UNSPEC:
+ return (0);
+ case AF_INET:
+ if (addr->v4.s_addr != INADDR_ANY)
+ return (1);
+ break;
+ case AF_INET6:
+ if (!IN6_IS_ADDR_UNSPECIFIED(&addr->v6))
+ return (1);
+ break;
+ default:
+ fatalx("ldp_addrisset: unknown af");
+ }
+
+ return (0);
+}
+
+int
+ldp_prefixcmp(int af, const union ldpd_addr *a, const union ldpd_addr *b,
+ uint8_t prefixlen)
+{
+ in_addr_t mask, aa, ba;
+ int i;
+ uint8_t m;
+
+ switch (af) {
+ case AF_INET:
+ if (prefixlen == 0)
+ return (0);
+ if (prefixlen > 32)
+ fatalx("ldp_prefixcmp: bad IPv4 prefixlen");
+ mask = htonl(prefixlen2mask(prefixlen));
+ aa = htonl(a->v4.s_addr) & mask;
+ ba = htonl(b->v4.s_addr) & mask;
+ return (aa - ba);
+ case AF_INET6:
+ if (prefixlen == 0)
+ return (0);
+ if (prefixlen > 128)
+ fatalx("ldp_prefixcmp: bad IPv6 prefixlen");
+ for (i = 0; i < prefixlen / 8; i++)
+ if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
+ return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
+ i = prefixlen % 8;
+ if (i) {
+ m = 0xff00 >> i;
+ if ((a->v6.s6_addr[prefixlen / 8] & m) !=
+ (b->v6.s6_addr[prefixlen / 8] & m))
+ return ((a->v6.s6_addr[prefixlen / 8] & m) -
+ (b->v6.s6_addr[prefixlen / 8] & m));
+ }
+ return (0);
+ default:
+ fatalx("ldp_prefixcmp: unknown af");
+ }
+ return (-1);
+}
+
+int
+bad_addr_v4(struct in_addr addr)
+{
+ uint32_t a = ntohl(addr.s_addr);
+
+ if (((a >> IN_CLASSA_NSHIFT) == 0) ||
+ ((a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) ||
+ IN_MULTICAST(a) || IN_BADCLASS(a))
+ return (1);
+
+ return (0);
+}
+
+int
+bad_addr_v6(struct in6_addr *addr)
+{
+ if (IN6_IS_ADDR_UNSPECIFIED(addr) ||
+ IN6_IS_ADDR_LOOPBACK(addr) ||
+ IN6_IS_ADDR_MULTICAST(addr) ||
+ IN6_IS_ADDR_SITELOCAL(addr) ||
+ IN6_IS_ADDR_V4MAPPED(addr) ||
+ IN6_IS_ADDR_V4COMPAT(addr))
+ return (1);
+
+ return (0);
+}
+
+int
+bad_addr(int af, union ldpd_addr *addr)
+{
+ switch (af) {
+ case AF_INET:
+ return (bad_addr_v4(addr->v4));
+ case AF_INET6:
+ return (bad_addr_v6(&addr->v6));
+ default:
+ fatalx("bad_addr: unknown af");
+ }
+}
+
+void
+embedscope(struct sockaddr_in6 *sin6)
+{
+ uint16_t tmp16;
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
+ memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
+ if (tmp16 != 0) {
+ log_warnx("%s: address %s already has embeded scope %u",
+ __func__, log_sockaddr(sin6), ntohs(tmp16));
+ }
+ tmp16 = htons(sin6->sin6_scope_id);
+ memcpy(&sin6->sin6_addr.s6_addr[2], &tmp16, sizeof(tmp16));
+ sin6->sin6_scope_id = 0;
+ }
+}
+
+void
+recoverscope(struct sockaddr_in6 *sin6)
+{
+ uint16_t tmp16;
+
+ if (sin6->sin6_scope_id != 0)
+ log_warnx("%s: address %s already has scope id %u",
+ __func__, log_sockaddr(sin6), sin6->sin6_scope_id);
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
+ memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
+ sin6->sin6_scope_id = ntohs(tmp16);
+ sin6->sin6_addr.s6_addr[2] = 0;
+ sin6->sin6_addr.s6_addr[3] = 0;
+ }
+}
+
+void
+addscope(struct sockaddr_in6 *sin6, uint32_t id)
+{
+ if (sin6->sin6_scope_id != 0)
+ log_warnx("%s: address %s already has scope id %u", __func__,
+ log_sockaddr(sin6), sin6->sin6_scope_id);
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
+ sin6->sin6_scope_id = id;
+}
+
+void
+clearscope(struct in6_addr *in6)
+{
+ if (IN6_IS_SCOPE_EMBED(in6)) {
+ in6->s6_addr[2] = 0;
+ in6->s6_addr[3] = 0;
+ }
+}
+
+struct sockaddr *
+addr2sa(int af, union ldpd_addr *addr, uint16_t port)
+{
+ static struct sockaddr_storage ss;
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)&ss;
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)&ss;
+
+ memset(&ss, 0, sizeof(ss));
+ switch (af) {
+ case AF_INET:
+ sa_in->sin_family = AF_INET;
+ sa_in->sin_len = sizeof(struct sockaddr_in);
+ sa_in->sin_addr = addr->v4;
+ sa_in->sin_port = htons(port);
+ break;
+ case AF_INET6:
+ sa_in6->sin6_family = AF_INET6;
+ sa_in6->sin6_len = sizeof(struct sockaddr_in6);
+ sa_in6->sin6_addr = addr->v6;
+ sa_in6->sin6_port = htons(port);
+ break;
+ default:
+ fatalx("addr2sa: unknown af");
+ }
+
+ return ((struct sockaddr *)&ss);
+}
+
+void
+sa2addr(struct sockaddr *sa, int *af, union ldpd_addr *addr)
+{
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)sa;
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa;
+
+ memset(addr, 0, sizeof(*addr));
+ switch (sa->sa_family) {
+ case AF_INET:
+ *af = AF_INET;
+ addr->v4 = sa_in->sin_addr;
+ break;
+ case AF_INET6:
+ *af = AF_INET6;
+ addr->v6 = sa_in6->sin6_addr;
+ break;
+ default:
+ fatalx("sa2addr: unknown af");
+ }
+}
diff --git a/lib/imsg-buffer.c b/lib/imsg-buffer.c
new file mode 100644
index 000000000..61b2c095a
--- /dev/null
+++ b/lib/imsg-buffer.c
@@ -0,0 +1,309 @@
+/* $OpenBSD$ */
+
+/*
+ * 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/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsg.h"
+
+int ibuf_realloc(struct ibuf *, size_t);
+void ibuf_enqueue(struct msgbuf *, struct ibuf *);
+void ibuf_dequeue(struct msgbuf *, struct ibuf *);
+
+struct ibuf *
+ibuf_open(size_t len)
+{
+ struct ibuf *buf;
+
+ if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = buf->max = len;
+ buf->fd = -1;
+
+ return (buf);
+}
+
+struct ibuf *
+ibuf_dynamic(size_t len, size_t max)
+{
+ struct ibuf *buf;
+
+ if (max < len)
+ return (NULL);
+
+ if ((buf = ibuf_open(len)) == NULL)
+ return (NULL);
+
+ if (max > 0)
+ buf->max = max;
+
+ return (buf);
+}
+
+int
+ibuf_realloc(struct ibuf *buf, size_t len)
+{
+ u_char *b;
+
+ /* on static buffers max is eq size and so the following fails */
+ if (buf->wpos + len > buf->max) {
+ errno = ERANGE;
+ return (-1);
+ }
+
+ b = realloc(buf->buf, buf->wpos + len);
+ if (b == NULL)
+ return (-1);
+ buf->buf = b;
+ buf->size = buf->wpos + len;
+
+ return (0);
+}
+
+int
+ibuf_add(struct ibuf *buf, const void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ if (ibuf_realloc(buf, len) == -1)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+void *
+ibuf_reserve(struct ibuf *buf, size_t len)
+{
+ void *b;
+
+ if (buf->wpos + len > buf->size)
+ if (ibuf_realloc(buf, len) == -1)
+ return (NULL);
+
+ b = buf->buf + buf->wpos;
+ buf->wpos += len;
+ return (b);
+}
+
+void *
+ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
+{
+ /* only allowed to seek in already written parts */
+ if (pos + len > buf->wpos)
+ return (NULL);
+
+ return (buf->buf + pos);
+}
+
+size_t
+ibuf_size(struct ibuf *buf)
+{
+ return (buf->wpos);
+}
+
+size_t
+ibuf_left(struct ibuf *buf)
+{
+ return (buf->max - buf->wpos);
+}
+
+void
+ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ ibuf_enqueue(msgbuf, buf);
+}
+
+int
+ibuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct ibuf *buf;
+ unsigned int i = 0;
+ ssize_t n;
+
+ memset(&iov, 0, sizeof(iov));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->wpos - buf->rpos;
+ i++;
+ }
+
+again:
+ if ((n = writev(msgbuf->fd, iov, i)) == -1) {
+ if (errno == EINTR)
+ goto again;
+ if (errno == ENOBUFS)
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (0);
+ }
+
+ msgbuf_drain(msgbuf, n);
+
+ return (1);
+}
+
+void
+ibuf_free(struct ibuf *buf)
+{
+ if (buf == NULL)
+ return;
+ free(buf->buf);
+ free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+ msgbuf->queued = 0;
+ msgbuf->fd = -1;
+ TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_drain(struct msgbuf *msgbuf, size_t n)
+{
+ struct ibuf *buf, *next;
+
+ for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+ buf = next) {
+ next = TAILQ_NEXT(buf, entry);
+ if (buf->rpos + n >= buf->wpos) {
+ n -= buf->wpos - buf->rpos;
+ ibuf_dequeue(msgbuf, buf);
+ } else {
+ buf->rpos += n;
+ n = 0;
+ }
+ }
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+ struct ibuf *buf;
+
+ while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+ ibuf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct ibuf *buf;
+ unsigned int i = 0;
+ ssize_t n;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+ } cmsgbuf;
+
+ memset(&iov, 0, sizeof(iov));
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->wpos - buf->rpos;
+ i++;
+ if (buf->fd != -1)
+ break;
+ }
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = i;
+
+ if (buf != NULL && buf->fd != -1) {
+ msg.msg_control = (caddr_t)&cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = buf->fd;
+ }
+
+again:
+ if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+ if (errno == EINTR)
+ goto again;
+ if (errno == ENOBUFS)
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (0);
+ }
+
+ /*
+ * assumption: fd got sent if sendmsg sent anything
+ * this works because fds are passed one at a time
+ */
+ if (buf != NULL && buf->fd != -1) {
+ close(buf->fd);
+ buf->fd = -1;
+ }
+
+ msgbuf_drain(msgbuf, n);
+
+ return (1);
+}
+
+void
+ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+ msgbuf->queued++;
+}
+
+void
+ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+
+ if (buf->fd != -1)
+ close(buf->fd);
+
+ msgbuf->queued--;
+ ibuf_free(buf);
+}
diff --git a/lib/imsg.c b/lib/imsg.c
new file mode 100644
index 000000000..0c1cb8220
--- /dev/null
+++ b/lib/imsg.c
@@ -0,0 +1,302 @@
+/* $OpenBSD$ */
+
+/*
+ * 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/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsg.h"
+
+int imsg_fd_overhead = 0;
+
+int imsg_get_fd(struct imsgbuf *);
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd)
+{
+ msgbuf_init(&ibuf->w);
+ memset(&ibuf->r, 0, sizeof(ibuf->r));
+ ibuf->fd = fd;
+ ibuf->w.fd = fd;
+ ibuf->pid = getpid();
+ TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int) * 1)];
+ } cmsgbuf;
+ struct iovec iov;
+ ssize_t n = -1;
+ int fd;
+ struct imsg_fd *ifd;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+
+ iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
+ iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
+ return (-1);
+
+again:
+ if (getdtablecount() + imsg_fd_overhead +
+ (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
+ >= getdtablesize()) {
+ errno = EAGAIN;
+ free(ifd);
+ return (-1);
+ }
+
+ if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
+ if (errno == EINTR)
+ goto again;
+ goto fail;
+ }
+
+ ibuf->r.wpos += n;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ int i;
+ int j;
+
+ /*
+ * We only accept one file descriptor. Due to C
+ * padding rules, our control buffer might contain
+ * more than one fd, and we must close them.
+ */
+ j = ((char *)cmsg + cmsg->cmsg_len -
+ (char *)CMSG_DATA(cmsg)) / sizeof(int);
+ for (i = 0; i < j; i++) {
+ fd = ((int *)CMSG_DATA(cmsg))[i];
+ if (ifd != NULL) {
+ ifd->fd = fd;
+ TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
+ entry);
+ ifd = NULL;
+ } else
+ close(fd);
+ }
+ }
+ /* we do not handle other ctl data level */
+ }
+
+fail:
+ free(ifd);
+ return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+ size_t av, left, datalen;
+
+ av = ibuf->r.wpos;
+
+ if (IMSG_HEADER_SIZE > av)
+ return (0);
+
+ memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+ if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+ imsg->hdr.len > MAX_IMSGSIZE) {
+ errno = ERANGE;
+ return (-1);
+ }
+ if (imsg->hdr.len > av)
+ return (0);
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+ if (datalen == 0)
+ imsg->data = NULL;
+ else if ((imsg->data = malloc(datalen)) == NULL)
+ return (-1);
+
+ if (imsg->hdr.flags & IMSGF_HASFD)
+ imsg->fd = imsg_get_fd(ibuf);
+ else
+ imsg->fd = -1;
+
+ memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+ if (imsg->hdr.len < av) {
+ left = av - imsg->hdr.len;
+ memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+ ibuf->r.wpos = left;
+ } else
+ ibuf->r.wpos = 0;
+
+ return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+ pid_t pid, int fd, const void *data, u_int16_t datalen)
+{
+ struct ibuf *wbuf;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ if (imsg_add(wbuf, data, datalen) == -1)
+ return (-1);
+
+ wbuf->fd = fd;
+
+ imsg_close(ibuf, wbuf);
+
+ return (1);
+}
+
+int
+imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+ pid_t pid, int fd, const struct iovec *iov, int iovcnt)
+{
+ struct ibuf *wbuf;
+ int i, datalen = 0;
+
+ for (i = 0; i < iovcnt; i++)
+ datalen += iov[i].iov_len;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ for (i = 0; i < iovcnt; i++)
+ if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
+ return (-1);
+
+ wbuf->fd = fd;
+
+ imsg_close(ibuf, wbuf);
+
+ return (1);
+}
+
+/* ARGSUSED */
+struct ibuf *
+imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+ pid_t pid, u_int16_t datalen)
+{
+ struct ibuf *wbuf;
+ struct imsg_hdr hdr;
+
+ datalen += IMSG_HEADER_SIZE;
+ if (datalen > MAX_IMSGSIZE) {
+ errno = ERANGE;
+ return (NULL);
+ }
+
+ hdr.type = type;
+ hdr.flags = 0;
+ hdr.peerid = peerid;
+ if ((hdr.pid = pid) == 0)
+ hdr.pid = ibuf->pid;
+ if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
+ return (NULL);
+ }
+ if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+ return (NULL);
+
+ return (wbuf);
+}
+
+int
+imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen)
+{
+ if (datalen)
+ if (ibuf_add(msg, data, datalen) == -1) {
+ ibuf_free(msg);
+ return (-1);
+ }
+ return (datalen);
+}
+
+void
+imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
+{
+ struct imsg_hdr *hdr;
+
+ hdr = (struct imsg_hdr *)msg->buf;
+
+ hdr->flags &= ~IMSGF_HASFD;
+ if (msg->fd != -1)
+ hdr->flags |= IMSGF_HASFD;
+
+ hdr->len = (u_int16_t)msg->wpos;
+
+ ibuf_close(&ibuf->w, msg);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+ free(imsg->data);
+}
+
+int
+imsg_get_fd(struct imsgbuf *ibuf)
+{
+ int fd;
+ struct imsg_fd *ifd;
+
+ if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
+ return (-1);
+
+ fd = ifd->fd;
+ TAILQ_REMOVE(&ibuf->fds, ifd, entry);
+ free(ifd);
+
+ return (fd);
+}
+
+int
+imsg_flush(struct imsgbuf *ibuf)
+{
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) <= 0)
+ return (-1);
+ return (0);
+}
+
+void
+imsg_clear(struct imsgbuf *ibuf)
+{
+ int fd;
+
+ msgbuf_clear(&ibuf->w);
+ while ((fd = imsg_get_fd(ibuf)) != -1)
+ close(fd);
+}
diff --git a/lib/imsg.h b/lib/imsg.h
new file mode 100644
index 000000000..d053d0195
--- /dev/null
+++ b/lib/imsg.h
@@ -0,0 +1,112 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@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.
+ */
+
+#ifndef _IMSG_H_
+#define _IMSG_H_
+
+#define IBUF_READ_SIZE 65535
+#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE 16384
+
+struct ibuf {
+ TAILQ_ENTRY(ibuf) entry;
+ u_char *buf;
+ size_t size;
+ size_t max;
+ size_t wpos;
+ size_t rpos;
+ int fd;
+};
+
+struct msgbuf {
+ TAILQ_HEAD(, ibuf) bufs;
+ u_int32_t queued;
+ int fd;
+};
+
+struct ibuf_read {
+ u_char buf[IBUF_READ_SIZE];
+ u_char *rptr;
+ size_t wpos;
+};
+
+struct imsg_fd {
+ TAILQ_ENTRY(imsg_fd) entry;
+ int fd;
+};
+
+struct imsgbuf {
+ TAILQ_HEAD(, imsg_fd) fds;
+ struct ibuf_read r;
+ struct msgbuf w;
+ int fd;
+ pid_t pid;
+};
+
+#define IMSGF_HASFD 1
+
+struct imsg_hdr {
+ u_int32_t type;
+ u_int16_t len;
+ u_int16_t flags;
+ u_int32_t peerid;
+ u_int32_t pid;
+};
+
+struct imsg {
+ struct imsg_hdr hdr;
+ int fd;
+ void *data;
+};
+
+
+/* buffer.c */
+struct ibuf *ibuf_open(size_t);
+struct ibuf *ibuf_dynamic(size_t, size_t);
+int ibuf_add(struct ibuf *, const void *, size_t);
+void *ibuf_reserve(struct ibuf *, size_t);
+void *ibuf_seek(struct ibuf *, size_t, size_t);
+size_t ibuf_size(struct ibuf *);
+size_t ibuf_left(struct ibuf *);
+void ibuf_close(struct msgbuf *, struct ibuf *);
+int ibuf_write(struct msgbuf *);
+void ibuf_free(struct ibuf *);
+void msgbuf_init(struct msgbuf *);
+void msgbuf_clear(struct msgbuf *);
+int msgbuf_write(struct msgbuf *);
+void msgbuf_drain(struct msgbuf *, size_t);
+
+/* imsg.c */
+void imsg_init(struct imsgbuf *, int);
+ssize_t imsg_read(struct imsgbuf *);
+ssize_t imsg_get(struct imsgbuf *, struct imsg *);
+int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+ int, const void *, u_int16_t);
+int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+ int, const struct iovec *, int);
+struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+ u_int16_t);
+int imsg_add(struct ibuf *, const void *, u_int16_t);
+void imsg_close(struct imsgbuf *, struct ibuf *);
+void imsg_free(struct imsg *);
+int imsg_flush(struct imsgbuf *);
+void imsg_clear(struct imsgbuf *);
+
+#endif
diff --git a/lib/openbsd-queue.h b/lib/openbsd-queue.h
new file mode 100644
index 000000000..5e81fdd13
--- /dev/null
+++ b/lib/openbsd-queue.h
@@ -0,0 +1,533 @@
+/* $OpenBSD: queue.h,v 1.43 2015/12/28 19:38:40 millert Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues and XOR simple queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * An XOR simple queue is used in the same way as a regular simple queue.
+ * The difference is that the head structure also includes a "cookie" that
+ * is XOR'd with the queue pointer (first, last or next) to generate the
+ * real pointer value.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST(head); \
+ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods.
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST(head); \
+ (var) && ((tvar) = LIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SIMPLEQ_FIRST(head); \
+ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+ == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_CONCAT(head1, head2) do { \
+ if (!SIMPLEQ_EMPTY((head2))) { \
+ *(head1)->sqh_last = (head2)->sqh_first; \
+ (head1)->sqh_last = (head2)->sqh_last; \
+ SIMPLEQ_INIT((head2)); \
+ } \
+} while (0)
+
+/*
+ * XOR Simple queue definitions.
+ */
+#define XSIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqx_first; /* first element */ \
+ struct type **sqx_last; /* addr of last next element */ \
+ unsigned long sqx_cookie; \
+}
+
+#define XSIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqx_next; /* next element */ \
+}
+
+/*
+ * XOR Simple queue access methods.
+ */
+#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
+ (unsigned long)(ptr)))
+#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
+#define XSIMPLEQ_END(head) NULL
+#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
+#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
+
+
+#define XSIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = XSIMPLEQ_FIRST(head); \
+ (var) != XSIMPLEQ_END(head); \
+ (var) = XSIMPLEQ_NEXT(head, var, field))
+
+#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = XSIMPLEQ_FIRST(head); \
+ (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * XOR Simple queue functions.
+ */
+#define XSIMPLEQ_INIT(head) do { \
+ arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
+ (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
+ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
+} while (0)
+
+#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqx_next = (head)->sqx_first) == \
+ XSIMPLEQ_XOR(head, NULL)) \
+ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+ (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
+} while (0)
+
+#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
+ *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
+ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+} while (0)
+
+#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
+ XSIMPLEQ_XOR(head, NULL)) \
+ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+ (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
+} while (0)
+
+#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
+ (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
+ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
+} while (0)
+
+#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
+ if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
+ (elm)->field.sqx_next)->field.sqx_next) \
+ == XSIMPLEQ_XOR(head, NULL)) \
+ (head)->sqx_last = \
+ XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+} while (0)
+
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue access methods.
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_PREV(var, headname, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ } \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/lib/openbsd-tree.h b/lib/openbsd-tree.h
new file mode 100644
index 000000000..e6502b1e7
--- /dev/null
+++ b/lib/openbsd-tree.h
@@ -0,0 +1,748 @@
+/* $OpenBSD: tree.h,v 1.14 2015/05/25 03:07:49 deraadt Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_TREE_H_
+#define _SYS_TREE_H_
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure. Every operation
+ * on the tree causes a splay to happen. The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree. On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n). The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute. It fulfills a set of conditions:
+ * - every search path from the root to a leaf consists of the
+ * same number of black nodes,
+ * - each red node (except for the root) has a black parent,
+ * - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type) \
+struct name { \
+ struct type *sph_root; /* root of the tree */ \
+}
+
+#define SPLAY_INITIALIZER(root) \
+ { NULL }
+
+#define SPLAY_INIT(root) do { \
+ (root)->sph_root = NULL; \
+} while (0)
+
+#define SPLAY_ENTRY(type) \
+struct { \
+ struct type *spe_left; /* left element */ \
+ struct type *spe_right; /* right element */ \
+}
+
+#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
+#define SPLAY_ROOT(head) (head)->sph_root
+#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do { \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
+} while (0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do { \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
+} while (0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
+ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
+ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
+} while (0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp) \
+void name##_SPLAY(struct name *, struct type *); \
+void name##_SPLAY_MINMAX(struct name *, int); \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
+ \
+/* Finds the node with the same key as elm */ \
+static __inline struct type * \
+name##_SPLAY_FIND(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) \
+ return(NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) \
+ return (head->sph_root); \
+ return (NULL); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_NEXT(struct name *head, struct type *elm) \
+{ \
+ name##_SPLAY(head, elm); \
+ if (SPLAY_RIGHT(elm, field) != NULL) { \
+ elm = SPLAY_RIGHT(elm, field); \
+ while (SPLAY_LEFT(elm, field) != NULL) { \
+ elm = SPLAY_LEFT(elm, field); \
+ } \
+ } else \
+ elm = NULL; \
+ return (elm); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_MIN_MAX(struct name *head, int val) \
+{ \
+ name##_SPLAY_MINMAX(head, val); \
+ return (SPLAY_ROOT(head)); \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp) \
+struct type * \
+name##_SPLAY_INSERT(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) { \
+ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
+ } else { \
+ int __comp; \
+ name##_SPLAY(head, elm); \
+ __comp = (cmp)(elm, (head)->sph_root); \
+ if(__comp < 0) { \
+ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+ SPLAY_RIGHT(elm, field) = (head)->sph_root; \
+ SPLAY_LEFT((head)->sph_root, field) = NULL; \
+ } else if (__comp > 0) { \
+ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT(elm, field) = (head)->sph_root; \
+ SPLAY_RIGHT((head)->sph_root, field) = NULL; \
+ } else \
+ return ((head)->sph_root); \
+ } \
+ (head)->sph_root = (elm); \
+ return (NULL); \
+} \
+ \
+struct type * \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *__tmp; \
+ if (SPLAY_EMPTY(head)) \
+ return (NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) { \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+ } else { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+ name##_SPLAY(head, elm); \
+ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
+ } \
+ return (elm); \
+ } \
+ return (NULL); \
+} \
+ \
+void \
+name##_SPLAY(struct name *head, struct type *elm) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+ int __comp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while ((__comp = (cmp)(elm, (head)->sph_root))) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) > 0){ \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+} \
+ \
+/* Splay with either the minimum or the maximum element \
+ * Used to find minimum or maximum element in tree. \
+ */ \
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while (1) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp > 0) { \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+}
+
+#define SPLAY_NEGINF -1
+#define SPLAY_INF 1
+
+#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head) \
+ for ((x) = SPLAY_MIN(name, head); \
+ (x) != NULL; \
+ (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type) \
+struct name { \
+ struct type *rbh_root; /* root of the tree */ \
+}
+
+#define RB_INITIALIZER(root) \
+ { NULL }
+
+#define RB_INIT(root) do { \
+ (root)->rbh_root = NULL; \
+} while (0)
+
+#define RB_BLACK 0
+#define RB_RED 1
+#define RB_ENTRY(type) \
+struct { \
+ struct type *rbe_left; /* left element */ \
+ struct type *rbe_right; /* right element */ \
+ struct type *rbe_parent; /* parent element */ \
+ int rbe_color; /* node color */ \
+}
+
+#define RB_LEFT(elm, field) (elm)->field.rbe_left
+#define RB_RIGHT(elm, field) (elm)->field.rbe_right
+#define RB_PARENT(elm, field) (elm)->field.rbe_parent
+#define RB_COLOR(elm, field) (elm)->field.rbe_color
+#define RB_ROOT(head) (head)->rbh_root
+#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do { \
+ RB_PARENT(elm, field) = parent; \
+ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
+ RB_COLOR(elm, field) = RB_RED; \
+} while (0)
+
+#define RB_SET_BLACKRED(black, red, field) do { \
+ RB_COLOR(black, field) = RB_BLACK; \
+ RB_COLOR(red, field) = RB_RED; \
+} while (0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x) do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
+ (tmp) = RB_RIGHT(elm, field); \
+ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \
+ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_LEFT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
+ (tmp) = RB_LEFT(elm, field); \
+ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \
+ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_RIGHT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
+attr struct type *name##_RB_INSERT(struct name *, struct type *); \
+attr struct type *name##_RB_FIND(struct name *, struct type *); \
+attr struct type *name##_RB_NFIND(struct name *, struct type *); \
+attr struct type *name##_RB_NEXT(struct type *); \
+attr struct type *name##_RB_PREV(struct type *); \
+attr struct type *name##_RB_MINMAX(struct name *, int); \
+ \
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define RB_GENERATE_STATIC(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
+attr void \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
+{ \
+ struct type *parent, *gparent, *tmp; \
+ while ((parent = RB_PARENT(elm, field)) && \
+ RB_COLOR(parent, field) == RB_RED) { \
+ gparent = RB_PARENT(parent, field); \
+ if (parent == RB_LEFT(gparent, field)) { \
+ tmp = RB_RIGHT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_RIGHT(parent, field) == elm) { \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_RIGHT(head, gparent, tmp, field); \
+ } else { \
+ tmp = RB_LEFT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_LEFT(parent, field) == elm) { \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_LEFT(head, gparent, tmp, field); \
+ } \
+ } \
+ RB_COLOR(head->rbh_root, field) = RB_BLACK; \
+} \
+ \
+attr void \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{ \
+ struct type *tmp; \
+ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
+ elm != RB_ROOT(head)) { \
+ if (RB_LEFT(parent, field) == elm) { \
+ tmp = RB_RIGHT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+ struct type *oleft; \
+ if ((oleft = RB_LEFT(tmp, field)))\
+ RB_COLOR(oleft, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_RIGHT(tmp, field)) \
+ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } else { \
+ tmp = RB_LEFT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+ struct type *oright; \
+ if ((oright = RB_RIGHT(tmp, field)))\
+ RB_COLOR(oright, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_LEFT(head, tmp, oright, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_LEFT(tmp, field)) \
+ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } \
+ } \
+ if (elm) \
+ RB_COLOR(elm, field) = RB_BLACK; \
+} \
+ \
+attr struct type * \
+name##_RB_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *child, *parent, *old = elm; \
+ int color; \
+ if (RB_LEFT(elm, field) == NULL) \
+ child = RB_RIGHT(elm, field); \
+ else if (RB_RIGHT(elm, field) == NULL) \
+ child = RB_LEFT(elm, field); \
+ else { \
+ struct type *left; \
+ elm = RB_RIGHT(elm, field); \
+ while ((left = RB_LEFT(elm, field))) \
+ elm = left; \
+ child = RB_RIGHT(elm, field); \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+ if (RB_PARENT(elm, field) == old) \
+ parent = elm; \
+ (elm)->field = (old)->field; \
+ if (RB_PARENT(old, field)) { \
+ if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+ RB_LEFT(RB_PARENT(old, field), field) = elm;\
+ else \
+ RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+ RB_AUGMENT(RB_PARENT(old, field)); \
+ } else \
+ RB_ROOT(head) = elm; \
+ RB_PARENT(RB_LEFT(old, field), field) = elm; \
+ if (RB_RIGHT(old, field)) \
+ RB_PARENT(RB_RIGHT(old, field), field) = elm; \
+ if (parent) { \
+ left = parent; \
+ do { \
+ RB_AUGMENT(left); \
+ } while ((left = RB_PARENT(left, field))); \
+ } \
+ goto color; \
+ } \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+color: \
+ if (color == RB_BLACK) \
+ name##_RB_REMOVE_COLOR(head, parent, child); \
+ return (old); \
+} \
+ \
+/* Inserts a node into the RB tree */ \
+attr struct type * \
+name##_RB_INSERT(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp; \
+ struct type *parent = NULL; \
+ int comp = 0; \
+ tmp = RB_ROOT(head); \
+ while (tmp) { \
+ parent = tmp; \
+ comp = (cmp)(elm, parent); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ RB_SET(elm, parent, field); \
+ if (parent != NULL) { \
+ if (comp < 0) \
+ RB_LEFT(parent, field) = elm; \
+ else \
+ RB_RIGHT(parent, field) = elm; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = elm; \
+ name##_RB_INSERT_COLOR(head, elm); \
+ return (NULL); \
+} \
+ \
+/* Finds the node with the same key as elm */ \
+attr struct type * \
+name##_RB_FIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (NULL); \
+} \
+ \
+/* Finds the first node greater than or equal to the search key */ \
+attr struct type * \
+name##_RB_NFIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *res = NULL; \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) { \
+ res = tmp; \
+ tmp = RB_LEFT(tmp, field); \
+ } \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (res); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_NEXT(struct type *elm) \
+{ \
+ if (RB_RIGHT(elm, field)) { \
+ elm = RB_RIGHT(elm, field); \
+ while (RB_LEFT(elm, field)) \
+ elm = RB_LEFT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_PREV(struct type *elm) \
+{ \
+ if (RB_LEFT(elm, field)) { \
+ elm = RB_LEFT(elm, field); \
+ while (RB_RIGHT(elm, field)) \
+ elm = RB_RIGHT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+attr struct type * \
+name##_RB_MINMAX(struct name *head, int val) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *parent = NULL; \
+ while (tmp) { \
+ parent = tmp; \
+ if (val < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else \
+ tmp = RB_RIGHT(tmp, field); \
+ } \
+ return (parent); \
+}
+
+#define RB_NEGINF -1
+#define RB_INF 1
+
+#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
+#define RB_PREV(name, x, y) name##_RB_PREV(y)
+#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head) \
+ for ((x) = RB_MIN(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_SAFE(x, name, head, y) \
+ for ((x) = RB_MIN(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head) \
+ for ((x) = RB_MAX(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
+ for ((x) = RB_MAX(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \
+ (x) = (y))
+
+#endif /* _SYS_TREE_H_ */