summaryrefslogtreecommitdiffstats
path: root/ldpd
diff options
context:
space:
mode:
Diffstat (limited to 'ldpd')
-rw-r--r--ldpd/.gitignore2
-rw-r--r--ldpd/Makefile10
-rw-r--r--ldpd/accept.c119
-rw-r--r--ldpd/address.c419
-rw-r--r--ldpd/adjacency.c383
-rw-r--r--ldpd/control.c272
-rw-r--r--ldpd/control.h26
-rw-r--r--ldpd/hello.c586
-rw-r--r--ldpd/init.c412
-rw-r--r--ldpd/interface.c968
-rw-r--r--ldpd/keepalive.c52
-rw-r--r--ldpd/l2vpn.c683
-rw-r--r--ldpd/labelmapping.c866
-rw-r--r--ldpd/lde.c2500
-rw-r--r--ldpd/lde.h254
-rw-r--r--ldpd/lde_lib.c1055
-rw-r--r--ldpd/ldp.h322
-rw-r--r--ldpd/ldp_debug.c194
-rw-r--r--ldpd/ldp_debug.h142
-rw-r--r--ldpd/ldp_snmp.c1224
-rw-r--r--ldpd/ldp_vty.h73
-rw-r--r--ldpd/ldp_vty_cmds.c902
-rw-r--r--ldpd/ldp_vty_conf.c1644
-rw-r--r--ldpd/ldp_vty_exec.c2145
-rw-r--r--ldpd/ldp_zebra.c713
-rw-r--r--ldpd/ldpd.c2035
-rw-r--r--ldpd/ldpd.h911
-rw-r--r--ldpd/ldpe.c1044
-rw-r--r--ldpd/ldpe.h313
-rw-r--r--ldpd/log.c138
-rw-r--r--ldpd/log.h36
-rw-r--r--ldpd/logmsg.c503
-rw-r--r--ldpd/neighbor.c855
-rw-r--r--ldpd/notification.c377
-rw-r--r--ldpd/packet.c802
-rw-r--r--ldpd/pfkey.c454
-rw-r--r--ldpd/rlfa.c275
-rw-r--r--ldpd/rlfa.h65
-rw-r--r--ldpd/socket.c476
-rw-r--r--ldpd/subdir.am68
-rw-r--r--ldpd/util.c371
41 files changed, 24689 insertions, 0 deletions
diff --git a/ldpd/.gitignore b/ldpd/.gitignore
new file mode 100644
index 0000000..ec8a5c4
--- /dev/null
+++ b/ldpd/.gitignore
@@ -0,0 +1,2 @@
+ldpd
+ldpd.conf
diff --git a/ldpd/Makefile b/ldpd/Makefile
new file mode 100644
index 0000000..464e02c
--- /dev/null
+++ b/ldpd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. ldpd/ldpd
+%: ALWAYS
+ @$(MAKE) -s -C .. ldpd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/ldpd/accept.c b/ldpd/accept.c
new file mode 100644
index 0000000..8e881e7
--- /dev/null
+++ b/ldpd/accept.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2012 Claudio Jeker <claudio@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+struct accept_ev {
+ LIST_ENTRY(accept_ev) entry;
+ struct event *ev;
+ void (*accept_cb)(struct event *);
+ 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(struct event *);
+static void accept_timeout(struct event *);
+
+void
+accept_init(void)
+{
+ LIST_INIT(&accept_queue.queue);
+}
+
+int accept_add(int fd, void (*cb)(struct event *), 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_add_read(master, accept_cb, av, av->fd, &av->ev);
+
+ 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_OFF(av->ev);
+ LIST_REMOVE(av, entry);
+ free(av);
+ return;
+ }
+}
+
+void
+accept_pause(void)
+{
+ log_debug(__func__);
+ accept_unarm();
+ event_add_timer(master, accept_timeout, NULL, 1, &accept_queue.evt);
+}
+
+void
+accept_unpause(void)
+{
+ if (accept_queue.evt != NULL) {
+ log_debug(__func__);
+ EVENT_OFF(accept_queue.evt);
+ accept_arm();
+ }
+}
+
+static void
+accept_arm(void)
+{
+ struct accept_ev *av;
+ LIST_FOREACH(av, &accept_queue.queue, entry) {
+ event_add_read(master, accept_cb, av, av->fd, &av->ev);
+ }
+}
+
+static void
+accept_unarm(void)
+{
+ struct accept_ev *av;
+ LIST_FOREACH(av, &accept_queue.queue, entry)
+ EVENT_OFF(av->ev);
+}
+
+static void accept_cb(struct event *thread)
+{
+ struct accept_ev *av = EVENT_ARG(thread);
+ event_add_read(master, accept_cb, av, av->fd, &av->ev);
+ av->accept_cb(thread);
+}
+
+static void accept_timeout(struct event *thread)
+{
+ accept_queue.evt = NULL;
+
+ log_debug(__func__);
+ accept_arm();
+}
diff --git a/ldpd/address.c b/ldpd/address.c
new file mode 100644
index 0000000..107eb5d
--- /dev/null
+++ b/ldpd/address.c
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+#include "ldp_debug.h"
+
+static void send_address(struct nbr *, int, struct if_addr_head *,
+ unsigned int, int);
+static int gen_address_list_tlv(struct ibuf *, int, struct if_addr_head *,
+ unsigned int);
+static int gen_mac_list_tlv(struct ibuf *, uint8_t *);
+static void address_list_add(struct if_addr_head *, struct if_addr *);
+static void address_list_clr(struct if_addr_head *);
+static void log_msg_address(int, uint16_t, struct nbr *, int,
+ union ldpd_addr *);
+static void log_msg_mac_withdrawal(int, struct nbr *, uint8_t *);
+
+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 (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__);
+
+ SET_FLAG(err, gen_ldp_hdr(buf, size));
+ size -= LDP_HDR_SIZE;
+ SET_FLAG(err, gen_msg_hdr(buf, msg_type, size));
+ size -= LDP_MSG_SIZE;
+ SET_FLAG(err, gen_address_list_tlv(buf, af, addr_list, tlv_addr_count));
+ (void)size;
+
+ if (err) {
+ address_list_clr(addr_list);
+ ibuf_free(buf);
+ return;
+ }
+
+ while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
+ log_msg_address(1, msg_type, nbr, af, &if_addr->addr);
+
+ LIST_REMOVE(if_addr, entry);
+ assert(if_addr != LIST_FIRST(addr_list));
+ free(if_addr);
+ if (--tlv_addr_count == 0)
+ break;
+ }
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+
+ /* no errors - update per neighbor message counters */
+ switch (msg_type) {
+ case MSG_TYPE_ADDR:
+ nbr->stats.addr_sent++;
+ break;
+ case MSG_TYPE_ADDRWITHDRAW:
+ nbr->stats.addrwdraw_sent++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ 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);
+}
+
+void
+send_mac_withdrawal(struct nbr *nbr, struct map *fec, uint8_t *mac)
+{
+ struct ibuf *buf;
+ uint16_t size;
+ int err;
+
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE + len_fec_tlv(fec) +
+ TLV_HDR_SIZE;
+ if (mac)
+ size += ETH_ALEN;
+
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ err = gen_ldp_hdr(buf, size);
+ size -= LDP_HDR_SIZE;
+
+ SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_ADDRWITHDRAW, size));
+ SET_FLAG(err, gen_address_list_tlv(buf, AF_INET, NULL, 0));
+ SET_FLAG(err, gen_fec_tlv(buf, fec));
+ SET_FLAG(err, gen_mac_list_tlv(buf, mac));
+
+ if (err) {
+ ibuf_free(buf);
+ return;
+ }
+
+ log_msg_mac_withdrawal(1, nbr, mac);
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+
+ nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+}
+
+int
+recv_address(struct nbr *nbr, char *buf, uint16_t len)
+{
+ struct ldp_msg msg;
+ uint16_t msg_type;
+ enum imsg_type type;
+ struct address_list_tlv alt;
+ uint16_t alt_len;
+ uint16_t alt_family;
+ struct lde_addr lde_addr;
+
+ memcpy(&msg, buf, sizeof(msg));
+ msg_type = ntohs(msg.type);
+ switch (msg_type) {
+ case MSG_TYPE_ADDR:
+ type = IMSG_ADDRESS_ADD;
+ break;
+ case MSG_TYPE_ADDRWITHDRAW:
+ type = IMSG_ADDRESS_DEL;
+ break;
+ default:
+ fatalx("recv_address: unexpected msg type");
+ }
+ 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));
+ alt_len = ntohs(alt.length);
+ alt_family = ntohs(alt.family);
+ if (alt_len > len - TLV_HDR_SIZE) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ if (ntohs(alt.type) != TLV_TYPE_ADDRLIST) {
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
+ return (-1);
+ }
+ switch (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->tcp, S_UNSUP_ADDR, msg.id, msg.type);
+ return (-1);
+ }
+ alt_len -= sizeof(alt.family);
+ buf += sizeof(alt);
+ len -= sizeof(alt);
+
+ /* Process all received addresses */
+ while (alt_len > 0) {
+ switch (alt_family) {
+ case AF_IPV4:
+ if (alt_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);
+ alt_len -= sizeof(struct in_addr);
+ break;
+ case AF_IPV6:
+ if (alt_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);
+ alt_len -= sizeof(struct in6_addr);
+ break;
+ default:
+ fatalx("recv_address: unknown af");
+ }
+
+ log_msg_address(0, msg_type, nbr, lde_addr.af, &lde_addr.addr);
+
+ ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr,
+ sizeof(lde_addr));
+ }
+
+ /* Optional Parameters */
+ while (len > 0) {
+ struct tlv tlv;
+ uint16_t tlv_type;
+ 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_type = ntohs(tlv.type);
+ 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 (tlv_type) {
+ default:
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ }
+
+ return (0);
+}
+
+static int
+gen_address_list_tlv(struct ibuf *buf, 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 = htons(TLV_TYPE_ADDRLIST);
+
+ 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");
+ }
+ alt.length = htons(sizeof(alt.family) + addr_size * tlv_addr_count);
+
+ SET_FLAG(err, ibuf_add(buf, &alt, sizeof(alt)));
+
+ if (addr_list == NULL)
+ return (err);
+
+ LIST_FOREACH(if_addr, addr_list, entry) {
+ SET_FLAG(err, ibuf_add(buf, &if_addr->addr, addr_size));
+
+ if (--tlv_addr_count == 0)
+ break;
+ }
+
+ return (err);
+}
+
+static int
+gen_mac_list_tlv(struct ibuf *buf, uint8_t *mac)
+{
+ struct tlv tlv;
+ int err;
+
+ memset(&tlv, 0, sizeof(tlv));
+ tlv.type = htons(TLV_TYPE_MAC_LIST);
+ if (mac)
+ tlv.length = htons(ETH_ALEN);
+ err = ibuf_add(buf, &tlv, sizeof(tlv));
+ if (mac)
+ SET_FLAG(err, ibuf_add(buf, mac, ETH_ALEN));
+
+ 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);
+ assert(if_addr != LIST_FIRST(addr_list));
+ free(if_addr);
+ }
+}
+
+static void
+log_msg_address(int out, uint16_t msg_type, struct nbr *nbr, int af,
+ union ldpd_addr *addr)
+{
+ debug_msg(out, "%s: lsr-id %pI4, address %s", msg_name(msg_type),
+ &nbr->id, log_addr(af, addr));
+}
+
+static void
+log_msg_mac_withdrawal(int out, struct nbr *nbr, uint8_t *mac)
+{
+ char buf[ETHER_ADDR_STRLEN];
+
+ debug_msg(out, "mac withdrawal: lsr-id %pI4, mac %s", &nbr->id,
+ (mac) ? prefix_mac2str((struct ethaddr *)mac, buf, sizeof(buf)) :
+ "wildcard");
+}
diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c
new file mode 100644
index 0000000..0108af8
--- /dev/null
+++ b/ldpd/adjacency.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+static __inline int adj_compare(const struct adj *, const struct adj *);
+static void adj_itimer(struct event *);
+static __inline int tnbr_compare(const struct tnbr *, const struct tnbr *);
+static void tnbr_del(struct ldpd_conf *, struct tnbr *);
+static void tnbr_start(struct tnbr *);
+static void tnbr_stop(struct tnbr *);
+static void tnbr_hello_timer(struct event *);
+static void tnbr_start_hello_timer(struct tnbr *);
+static void tnbr_stop_hello_timer(struct tnbr *);
+
+RB_GENERATE(global_adj_head, adj, global_entry, adj_compare)
+RB_GENERATE(nbr_adj_head, adj, nbr_entry, adj_compare)
+RB_GENERATE(ia_adj_head, adj, ia_entry, adj_compare)
+RB_GENERATE(tnbr_head, tnbr, entry, tnbr_compare)
+
+static __inline int
+adj_compare(const struct adj *a, const struct adj *b)
+{
+ if (adj_get_af(a) < adj_get_af(b))
+ return (-1);
+ if (adj_get_af(a) > adj_get_af(b))
+ return (1);
+
+ if (ntohl(a->lsr_id.s_addr) < ntohl(b->lsr_id.s_addr))
+ return (-1);
+ if (ntohl(a->lsr_id.s_addr) > ntohl(b->lsr_id.s_addr))
+ return (1);
+
+ if (a->source.type < b->source.type)
+ return (-1);
+ if (a->source.type > b->source.type)
+ return (1);
+
+ switch (a->source.type) {
+ case HELLO_LINK:
+ if (if_cmp_name_func(a->source.link.ia->iface->name,
+ b->source.link.ia->iface->name) < 0)
+ return (-1);
+ if (if_cmp_name_func(a->source.link.ia->iface->name,
+ b->source.link.ia->iface->name) > 0)
+ return (1);
+ return (ldp_addrcmp(a->source.link.ia->af,
+ &a->source.link.src_addr, &b->source.link.src_addr));
+ case HELLO_TARGETED:
+ return (ldp_addrcmp(a->source.target->af,
+ &a->source.target->addr, &b->source.target->addr));
+ default:
+ fatalx("adj_compare: unknown hello type");
+ }
+
+ return (0);
+}
+
+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 %pI4, %s", __func__, &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;
+
+ RB_INSERT(global_adj_head, &global.adj_tree, adj);
+
+ switch (source->type) {
+ case HELLO_LINK:
+ RB_INSERT(ia_adj_head, &source->link.ia->adj_tree, adj);
+ break;
+ case HELLO_TARGETED:
+ source->target->adj = adj;
+ break;
+ }
+
+ return (adj);
+}
+
+void
+adj_del(struct adj *adj, uint32_t notif_status)
+{
+ struct nbr *nbr = adj->nbr;
+
+ log_debug("%s: lsr-id %pI4, %s (%s)", __func__, &adj->lsr_id,
+ log_hello_src(&adj->source), af_name(adj_get_af(adj)));
+
+ adj_stop_itimer(adj);
+
+ RB_REMOVE(global_adj_head, &global.adj_tree, adj);
+ if (nbr)
+ RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj);
+ switch (adj->source.type) {
+ case HELLO_LINK:
+ RB_REMOVE(ia_adj_head, &adj->source.link.ia->adj_tree, adj);
+
+ if (nbr)
+ ldp_sync_fsm_adj_event(adj, LDP_SYNC_EVT_ADJ_DEL);
+ break;
+ case HELLO_TARGETED:
+ adj->source.target->adj = NULL;
+ break;
+ }
+
+ free(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) {
+ session_shutdown(nbr, notif_status, 0, 0);
+ nbr_del(nbr);
+ }
+}
+
+struct adj *
+adj_find(struct in_addr lsr_id, struct hello_source *source)
+{
+ struct adj adj;
+ adj.lsr_id = lsr_id;
+ adj.source = *source;
+ return (RB_FIND(global_adj_head, &global.adj_tree, &adj));
+}
+
+int
+adj_get_af(const 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(struct event *thread)
+{
+ struct adj *adj = EVENT_ARG(thread);
+
+ adj->inactivity_timer = NULL;
+
+ log_debug("%s: lsr-id %pI4", __func__, &adj->lsr_id);
+
+ if (adj->source.type == HELLO_TARGETED) {
+ if (!CHECK_FLAG(adj->source.target->flags, F_TNBR_CONFIGURED) &&
+ adj->source.target->pw_count == 0 &&
+ adj->source.target->rlfa_count == 0) {
+ /* remove dynamic targeted neighbor */
+ tnbr_del(leconf, adj->source.target);
+ return;
+ }
+ }
+
+ adj_del(adj, S_HOLDTIME_EXP);
+}
+
+void
+adj_start_itimer(struct adj *adj)
+{
+ EVENT_OFF(adj->inactivity_timer);
+ adj->inactivity_timer = NULL;
+ event_add_timer(master, adj_itimer, adj, adj->holdtime,
+ &adj->inactivity_timer);
+}
+
+void
+adj_stop_itimer(struct adj *adj)
+{
+ EVENT_OFF(adj->inactivity_timer);
+}
+
+/* targeted neighbors */
+
+static __inline int
+tnbr_compare(const struct tnbr *a, const struct tnbr *b)
+{
+ if (a->af < b->af)
+ return (-1);
+ if (a->af > b->af)
+ return (1);
+
+ return (ldp_addrcmp(a->af, &a->addr, &b->addr));
+}
+
+struct tnbr *
+tnbr_new(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;
+
+ return (tnbr);
+}
+
+static void
+tnbr_del(struct ldpd_conf *xconf, struct tnbr *tnbr)
+{
+ tnbr_stop(tnbr);
+ RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr);
+ free(tnbr);
+}
+
+struct tnbr *
+tnbr_find(struct ldpd_conf *xconf, int af, union ldpd_addr *addr)
+{
+ struct tnbr tnbr;
+ tnbr.af = af;
+ tnbr.addr = *addr;
+ return (RB_FIND(tnbr_head, &xconf->tnbr_tree, &tnbr));
+}
+
+struct tnbr *
+tnbr_check(struct ldpd_conf *xconf, struct tnbr *tnbr)
+{
+ if (!CHECK_FLAG(tnbr->flags, (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC)) &&
+ tnbr->pw_count == 0 && tnbr->rlfa_count == 0) {
+ tnbr_del(xconf, tnbr);
+ return (NULL);
+ }
+
+ return (tnbr);
+}
+
+static void
+tnbr_start(struct tnbr *tnbr)
+{
+ send_hello(HELLO_TARGETED, NULL, tnbr);
+ tnbr_start_hello_timer(tnbr);
+ tnbr->state = TNBR_STA_ACTIVE;
+}
+
+static void
+tnbr_stop(struct tnbr *tnbr)
+{
+ tnbr_stop_hello_timer(tnbr);
+ if (tnbr->adj)
+ adj_del(tnbr->adj, S_SHUTDOWN);
+ tnbr->state = TNBR_STA_DOWN;
+}
+
+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 (ldp_rtr_id_get(leconf) != 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_start(tnbr);
+ } else if (tnbr->state == TNBR_STA_ACTIVE) {
+ if (socket_ok && rtr_id_ok)
+ return;
+
+ tnbr_stop(tnbr);
+ }
+}
+
+void
+tnbr_update_all(int af)
+{
+ struct tnbr *tnbr;
+
+ /* update targeted neighbors */
+ RB_FOREACH(tnbr, tnbr_head, &leconf->tnbr_tree)
+ if (tnbr->af == af || af == AF_UNSPEC)
+ tnbr_update(tnbr);
+}
+
+uint16_t
+tnbr_get_hello_holdtime(struct tnbr *tnbr)
+{
+ if ((ldp_af_conf_get(leconf, tnbr->af))->thello_holdtime != 0)
+ return ((ldp_af_conf_get(leconf, tnbr->af))->thello_holdtime);
+
+ return (leconf->thello_holdtime);
+}
+
+uint16_t
+tnbr_get_hello_interval(struct tnbr *tnbr)
+{
+ if ((ldp_af_conf_get(leconf, tnbr->af))->thello_interval != 0)
+ return ((ldp_af_conf_get(leconf, tnbr->af))->thello_interval);
+
+ return (leconf->thello_interval);
+}
+
+/* target neighbors timers */
+
+/* ARGSUSED */
+static void tnbr_hello_timer(struct event *thread)
+{
+ struct tnbr *tnbr = EVENT_ARG(thread);
+
+ tnbr->hello_timer = NULL;
+ send_hello(HELLO_TARGETED, NULL, tnbr);
+ tnbr_start_hello_timer(tnbr);
+}
+
+static void
+tnbr_start_hello_timer(struct tnbr *tnbr)
+{
+ EVENT_OFF(tnbr->hello_timer);
+ tnbr->hello_timer = NULL;
+ event_add_timer(master, tnbr_hello_timer, tnbr,
+ tnbr_get_hello_interval(tnbr), &tnbr->hello_timer);
+}
+
+static void
+tnbr_stop_hello_timer(struct tnbr *tnbr)
+{
+ EVENT_OFF(tnbr->hello_timer);
+}
+
+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));
+ actl.src_addr = adj->source.link.src_addr;
+ break;
+ case HELLO_TARGETED:
+ actl.src_addr = adj->source.target->addr;
+ break;
+ }
+ actl.holdtime = adj->holdtime;
+ actl.holdtime_remaining =
+ event_timer_remain_second(adj->inactivity_timer);
+ actl.trans_addr = adj->trans_addr;
+ actl.ds_tlv = adj->ds_tlv;
+
+ return (&actl);
+}
diff --git a/ldpd/control.c b/ldpd/control.c
new file mode 100644
index 0000000..db52d46
--- /dev/null
+++ b/ldpd/control.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ */
+
+#include <zebra.h>
+#include <sys/un.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "control.h"
+
+#define CONTROL_BACKLOG 5
+
+static void control_accept(struct event *);
+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(struct event *);
+
+struct ctl_conns ctl_conns;
+
+static int control_fd;
+
+int
+control_init(char *path)
+{
+ struct sockaddr_un s_un;
+ int fd;
+ mode_t old_umask;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("%s: socket", __func__);
+ return (-1);
+ }
+ sock_set_nonblock(fd);
+
+ memset(&s_un, 0, sizeof(s_un));
+ s_un.sun_family = AF_UNIX;
+ strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path));
+
+ if (unlink(path) == -1)
+ if (errno != ENOENT) {
+ log_warn("%s: unlink %s", __func__, path);
+ close(fd);
+ return (-1);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
+ log_warn("%s: bind: %s", __func__, path);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("%s: chmod", __func__);
+ close(fd);
+ (void)unlink(path);
+ 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(char *path)
+{
+ accept_del(control_fd);
+ close(control_fd);
+ unlink(path);
+}
+
+/* ARGSUSED */
+static void control_accept(struct event *thread)
+{
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un s_un;
+ struct ctl_conn *c;
+
+ len = sizeof(s_un);
+ if ((connfd = accept(EVENT_FD(thread), (struct sockaddr *)&s_un,
+ &len)) == -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: accept", __func__);
+ return;
+ }
+ sock_set_nonblock(connfd);
+
+ if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
+ log_warn(__func__);
+ close(connfd);
+ return;
+ }
+
+ imsg_init(&c->iev.ibuf, connfd);
+ c->iev.handler_read = control_dispatch_imsg;
+ c->iev.ev_read = NULL;
+ event_add_read(master, c->iev.handler_read, &c->iev, c->iev.ibuf.fd,
+ &c->iev.ev_read);
+ c->iev.handler_write = ldp_write_handler;
+ c->iev.ev_write = NULL;
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+static struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ TAILQ_FOREACH(c, &ctl_conns, entry) {
+ if (c->iev.ibuf.fd == fd)
+ break;
+ }
+
+ return (c);
+}
+
+static struct ctl_conn *
+control_connbypid(pid_t pid)
+{
+ struct ctl_conn *c;
+
+ TAILQ_FOREACH(c, &ctl_conns, entry) {
+ if (c->iev.ibuf.pid == pid)
+ break;
+ }
+
+ 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_OFF(c->iev.ev_read);
+ EVENT_OFF(c->iev.ev_write);
+ close(c->iev.ibuf.fd);
+ accept_unpause();
+ free(c);
+}
+
+/* ARGSUSED */
+static void control_dispatch_imsg(struct event *thread)
+{
+ int fd = EVENT_FD(thread);
+ struct ctl_conn *c;
+ struct imsg imsg;
+ ssize_t n;
+ unsigned int ifidx;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warnx("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ c->iev.ev_read = NULL;
+
+ if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || n == 0) {
+ 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:
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_KROUTE_ADDR:
+ case IMSG_CTL_IFINFO:
+ /* ignore */
+ 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_DISCOVERY_DTL:
+ ldpe_adj_detail_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_SHOW_LDP_SYNC:
+ ldpe_ldp_sync_ctl(c);
+ break;
+ case IMSG_CTL_LOG_VERBOSE:
+ /* ignore */
+ 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 0000000..f45c97e
--- /dev/null
+++ b/ldpd/control.h
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ */
+
+#ifndef _CONTROL_H_
+#define _CONTROL_H_
+
+#include "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(char *);
+int control_listen(void);
+void control_cleanup(char *);
+int control_imsg_relay(struct imsg *);
+
+#endif /* _CONTROL_H_ */
diff --git a/ldpd/hello.c b/ldpd/hello.c
new file mode 100644
index 0000000..0b07f24
--- /dev/null
+++ b/ldpd/hello.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.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 = if_get_hello_holdtime(ia);
+ flags = 0;
+ fd = (ldp_af_global_get(&global, af))->ldp_disc_socket;
+
+ /* multicast destination address */
+ switch (af) {
+ case AF_INET:
+ if (!CHECK_FLAG(leconf->ipv4.flags, F_LDPD_AF_NO_GTSM))
+ SET_FLAG(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_get_hello_holdtime(tnbr);
+ flags = F_HELLO_TARGETED;
+ if (CHECK_FLAG(tnbr->flags, F_TNBR_CONFIGURED) ||
+ tnbr->pw_count ||
+ tnbr->rlfa_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__);
+
+ SET_FLAG(err, gen_ldp_hdr(buf, size));
+ size -= LDP_HDR_SIZE;
+ SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_HELLO, size));
+ SET_FLAG(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:
+ SET_FLAG(err, gen_opt4_hello_prms_tlv(buf, TLV_TYPE_IPV4TRANSADDR,
+ leconf->ipv4.trans_addr.v4.s_addr));
+ break;
+ case AF_INET6:
+ SET_FLAG(err, gen_opt16_hello_prms_tlv(buf, TLV_TYPE_IPV6TRANSADDR,
+ leconf->ipv6.trans_addr.v6.s6_addr));
+ break;
+ default:
+ fatalx("send_hello: unknown af");
+ }
+
+ SET_FLAG(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))
+ SET_FLAG(err, gen_ds_hello_prms_tlv(buf, leconf->trans_pref));
+
+ if (err) {
+ ibuf_free(buf);
+ return (-1);
+ }
+
+ switch (type) {
+ case HELLO_LINK:
+ debug_hello_send("iface %s (%s) holdtime %u", ia->iface->name,
+ af_name(ia->af), holdtime);
+ break;
+ case HELLO_TARGETED:
+ debug_hello_send("targeted-neighbor %s (%s) holdtime %u",
+ log_addr(tnbr->af, &tnbr->addr), af_name(tnbr->af),
+ holdtime);
+ break;
+ default:
+ fatalx("send_hello: unknown hello type");
+ }
+
+ 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 = 0, flags = 0;
+ int tlvs_rcvd;
+ int ds_tlv;
+ union ldpd_addr trans_addr;
+ ifindex_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 %pI4: failed to decode params", __func__, &lsr_id);
+ return;
+ }
+ /* safety checks */
+ if (holdtime != 0 && holdtime < MIN_HOLDTIME) {
+ log_debug("%s: lsr-id %pI4: invalid hello holdtime (%u)",
+ __func__, &lsr_id, holdtime);
+ return;
+ }
+ if (multicast && CHECK_FLAG(flags, F_HELLO_TARGETED)) {
+ log_debug("%s: lsr-id %pI4: multicast targeted hello", __func__, &lsr_id);
+ return;
+ }
+ if (!multicast && !CHECK_FLAG(flags, F_HELLO_TARGETED)) {
+ log_debug("%s: lsr-id %pI4: unicast link hello", __func__, &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 %pI4: failed to decode optional params",
+ __func__, &lsr_id);
+ return;
+ }
+ if (r != len) {
+ log_debug("%s: lsr-id %pI4: unexpected data in message",
+ __func__, &lsr_id);
+ return;
+ }
+ ds_tlv = CHECK_FLAG(tlvs_rcvd, F_HELLO_TLV_RCVD_DS) ? 1 : 0;
+
+ /* implicit transport address */
+ if (!CHECK_FLAG(tlvs_rcvd, F_HELLO_TLV_RCVD_ADDR))
+ trans_addr = *src;
+ if (bad_addr(af, &trans_addr)) {
+ log_debug("%s: lsr-id %pI4: invalid transport address %s",
+ __func__, &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 (CHECK_FLAG(flags, F_HELLO_TARGETED)) {
+ log_debug("%s: lsr-id %pI4: invalid targeted hello transport address %s", __func__, &lsr_id,
+ log_addr(af, &trans_addr));
+ return;
+ }
+ scope_id = iface->ifindex;
+ }
+
+ memset(&source, 0, sizeof(source));
+ if (CHECK_FLAG(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 %pI4: targeted hello with link-local source address", __func__,
+ &lsr_id);
+ return;
+ }
+
+ tnbr = tnbr_find(leconf, af, src);
+
+ /* remove the dynamic tnbr if the 'R' bit was cleared */
+ if (tnbr &&
+ CHECK_FLAG(tnbr->flags, F_TNBR_DYNAMIC) &&
+ !CHECK_FLAG(flags, F_HELLO_REQ_TARG)) {
+ UNSET_FLAG(tnbr->flags, F_TNBR_DYNAMIC);
+ tnbr = tnbr_check(leconf, tnbr);
+ }
+
+ if (!tnbr) {
+ struct ldpd_af_conf *af_conf;
+
+ if (!CHECK_FLAG(flags, F_HELLO_REQ_TARG))
+ return;
+ af_conf = ldp_af_conf_get(leconf, af);
+ if (!CHECK_FLAG(af_conf->flags, F_LDPD_AF_THELLO_ACCEPT))
+ return;
+ if (ldpe_acl_check(af_conf->acl_thello_accept_from, af,
+ src, (af == AF_INET) ? 32 : 128) != FILTER_PERMIT)
+ return;
+
+ tnbr = tnbr_new(af, src);
+ SET_FLAG(tnbr->flags, F_TNBR_DYNAMIC);
+ tnbr_update(tnbr);
+ RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr);
+ }
+
+ 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;
+ }
+
+ debug_hello_recv("%s lsr-id %pI4 transport-address %s holdtime %u%s",
+ log_hello_src(&source), &lsr_id, log_addr(af, &trans_addr),
+ holdtime, (ds_tlv) ? " (dual stack TLV present)" : "");
+
+ adj = adj_find(lsr_id, &source);
+ if (adj && adj->ds_tlv != ds_tlv) {
+ /*
+ * Transient condition, ignore packet and wait until adjacency
+ * times out.
+ */
+ return;
+ }
+ nbr = nbr_find_ldpid(lsr_id.s_addr);
+
+ /* check dual-stack tlv */
+ 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 %pI4: remote transport preference does not match the local preference", __func__, &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 %pI4: hello packet advertising a different transport address", __func__, &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 %pI4", __func__, log_addr(af,
+ &trans_addr), &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;
+ RB_INSERT(nbr_adj_head, &nbr->adj_tree, adj);
+ }
+ ldp_sync_fsm_adj_event(adj, LDP_SYNC_EVT_ADJ_NEW);
+ }
+ adj->ds_tlv = ds_tlv;
+
+ /*
+ * 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 (CHECK_FLAG(flags, F_HELLO_GTSM))
+ SET_FLAG(nbr->flags, F_NBR_GTSM_NEGOTIATED);
+ else
+ UNSET_FLAG(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(if_get_hello_holdtime(ia), holdtime);
+ break;
+ case HELLO_TARGETED:
+ if (holdtime == 0)
+ holdtime = TARGETED_DFLT_HOLDTIME;
+
+ adj->holdtime = MIN(tnbr_get_hello_holdtime(tnbr), 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 (CHECK_FLAG(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 (CHECK_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_ADDR))
+ break;
+ memcpy(&addr->v4, buf, sizeof(addr->v4));
+ SET_FLAG(*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 (CHECK_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_ADDR))
+ break;
+ memcpy(&addr->v6, buf, sizeof(addr->v6));
+ SET_FLAG(*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));
+ SET_FLAG(*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 (CHECK_FLAG(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;
+ }
+ SET_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_DS);
+ break;
+ default:
+ /* if unknown flag set, ignore TLV */
+ if (!CHECK_FLAG(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 0000000..f0cb98e
--- /dev/null
+++ b/ldpd/init.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.h"
+
+static int gen_init_prms_tlv(struct ibuf *, struct nbr *);
+static int gen_cap_dynamic_tlv(struct ibuf *);
+static int gen_cap_twcard_tlv(struct ibuf *, int);
+static int gen_cap_unotif_tlv(struct ibuf *, int);
+
+void
+send_init(struct nbr *nbr)
+{
+ struct ibuf *buf;
+ uint16_t size;
+ int err = 0;
+
+ debug_msg_send("initialization: lsr-id %pI4", &nbr->id);
+
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE +
+ CAP_TLV_DYNAMIC_SIZE + CAP_TLV_TWCARD_SIZE + CAP_TLV_UNOTIF_SIZE;
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ SET_FLAG(err, gen_ldp_hdr(buf, size));
+ size -= LDP_HDR_SIZE;
+ SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_INIT, size));
+ SET_FLAG(err, gen_init_prms_tlv(buf, nbr));
+ SET_FLAG(err, gen_cap_dynamic_tlv(buf));
+ SET_FLAG(err, gen_cap_twcard_tlv(buf, 1));
+ SET_FLAG(err, gen_cap_unotif_tlv(buf, 1));
+ 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;
+ int caps_rcvd = 0;
+
+ debug_msg_recv("initialization: lsr-id %pI4", &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 != ldp_rtr_id_get(leconf) ||
+ 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_type;
+ 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_type = ntohs(tlv.type);
+ 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;
+
+ /*
+ * RFC 5561 - Section 6:
+ * "The S-bit of a Capability Parameter in an Initialization
+ * message MUST be 1 and SHOULD be ignored on receipt".
+ */
+ switch (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);
+ case TLV_TYPE_DYNAMIC_CAP:
+ if (tlv_len != CAP_TLV_DYNAMIC_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_DYNAMIC)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ }
+ SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_DYNAMIC);
+
+ SET_FLAG(nbr->flags, F_NBR_CAP_DYNAMIC);
+
+ log_debug("%s: lsr-id %pI4 announced the Dynamic Capability Announcement capability", __func__,
+ &nbr->id);
+ break;
+ case TLV_TYPE_TWCARD_CAP:
+ if (tlv_len != CAP_TLV_TWCARD_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_TWCARD)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ }
+ SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_TWCARD);
+
+ SET_FLAG(nbr->flags, F_NBR_CAP_TWCARD);
+
+ log_debug("%s: lsr-id %pI4 announced the Typed Wildcard FEC capability", __func__, &nbr->id);
+ break;
+ case TLV_TYPE_UNOTIF_CAP:
+ if (tlv_len != CAP_TLV_UNOTIF_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_UNOTIF)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ }
+ SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_UNOTIF);
+
+ SET_FLAG(nbr->flags, F_NBR_CAP_UNOTIF);
+
+ log_debug("%s: lsr-id %pI4 announced the Unrecognized Notification capability", __func__,
+ &nbr->id);
+ break;
+ default:
+ if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG))
+ send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
+ /* 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);
+}
+
+void
+send_capability(struct nbr *nbr, uint16_t capability, int enable)
+{
+ struct ibuf *buf;
+ uint16_t size;
+ int err = 0;
+
+ log_debug("%s: lsr-id %pI4", __func__, &nbr->id);
+
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + CAP_TLV_DYNAMIC_SIZE;
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ SET_FLAG(err, gen_ldp_hdr(buf, size));
+ size -= LDP_HDR_SIZE;
+ SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size));
+
+ switch (capability) {
+ case TLV_TYPE_TWCARD_CAP:
+ SET_FLAG(err, gen_cap_twcard_tlv(buf, enable));
+ break;
+ case TLV_TYPE_UNOTIF_CAP:
+ SET_FLAG(err, gen_cap_unotif_tlv(buf, enable));
+ break;
+ case TLV_TYPE_DYNAMIC_CAP:
+ /*
+ * RFC 5561 - Section 9:
+ * "An LDP speaker MUST NOT include the Dynamic Capability
+ * Announcement Parameter in Capability messages sent to
+ * its peers".
+ */
+ /* FALLTHROUGH */
+ default:
+ fatalx("send_capability: unsupported capability");
+ }
+
+ if (err) {
+ ibuf_free(buf);
+ return;
+ }
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+ nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+ nbr->stats.capability_sent++;
+}
+
+int
+recv_capability(struct nbr *nbr, char *buf, uint16_t len)
+{
+ struct ldp_msg msg;
+ int enable = 0;
+ int caps_rcvd = 0;
+
+ log_debug("%s: lsr-id %pI4", __func__, &nbr->id);
+
+ memcpy(&msg, buf, sizeof(msg));
+ buf += LDP_MSG_SIZE;
+ len -= LDP_MSG_SIZE;
+
+ /* Optional Parameters */
+ while (len > 0) {
+ struct tlv tlv;
+ uint16_t tlv_type;
+ uint16_t tlv_len;
+ uint8_t reserved;
+
+ if (len < sizeof(tlv)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_type = ntohs(tlv.type);
+ 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 (tlv_type) {
+ case TLV_TYPE_TWCARD_CAP:
+ if (tlv_len != CAP_TLV_TWCARD_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_TWCARD)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ }
+ SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_TWCARD);
+
+ memcpy(&reserved, buf, sizeof(reserved));
+ enable = reserved & STATE_BIT;
+ if (enable)
+ SET_FLAG(nbr->flags, F_NBR_CAP_TWCARD);
+ else
+ UNSET_FLAG(nbr->flags, F_NBR_CAP_TWCARD);
+
+ log_debug("%s: lsr-id %pI4 %s the Typed Wildcard FEC capability", __func__, &nbr->id,
+ (enable) ? "announced" : "withdrew");
+ break;
+ case TLV_TYPE_UNOTIF_CAP:
+ if (tlv_len != CAP_TLV_UNOTIF_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_UNOTIF)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ }
+ SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_UNOTIF);
+
+ memcpy(&reserved, buf, sizeof(reserved));
+ enable = reserved & STATE_BIT;
+ if (enable)
+ SET_FLAG(nbr->flags, F_NBR_CAP_UNOTIF);
+ else
+ UNSET_FLAG(nbr->flags, F_NBR_CAP_UNOTIF);
+
+ log_debug("%s: lsr-id %pI4 %s the Unrecognized Notification capability", __func__,
+ &nbr->id, (enable) ? "announced" : "withdrew");
+ break;
+ case TLV_TYPE_DYNAMIC_CAP:
+ /*
+ * RFC 5561 - Section 9:
+ * "An LDP speaker that receives a Capability message
+ * from a peer that includes the Dynamic Capability
+ * Announcement Parameter SHOULD silently ignore the
+ * parameter and process any other Capability Parameters
+ * in the message".
+ */
+ /* FALLTHROUGH */
+ default:
+ if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG))
+ send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ }
+
+ nbr_fsm(nbr, NBR_EVT_PDU_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));
+}
+
+static int
+gen_cap_dynamic_tlv(struct ibuf *buf)
+{
+ struct capability_tlv cap;
+
+ memset(&cap, 0, sizeof(cap));
+ cap.type = htons(TLV_TYPE_DYNAMIC_CAP);
+ cap.length = htons(CAP_TLV_DYNAMIC_LEN);
+ /* the S-bit is always 1 for the Dynamic Capability Announcement */
+ cap.reserved = STATE_BIT;
+
+ return (ibuf_add(buf, &cap, CAP_TLV_DYNAMIC_SIZE));
+}
+
+static int
+gen_cap_twcard_tlv(struct ibuf *buf, int enable)
+{
+ struct capability_tlv cap;
+
+ memset(&cap, 0, sizeof(cap));
+ cap.type = htons(TLV_TYPE_TWCARD_CAP);
+ cap.length = htons(CAP_TLV_TWCARD_LEN);
+ if (enable)
+ cap.reserved = STATE_BIT;
+
+ return (ibuf_add(buf, &cap, CAP_TLV_TWCARD_SIZE));
+}
+
+static int
+gen_cap_unotif_tlv(struct ibuf *buf, int enable)
+{
+ struct capability_tlv cap;
+
+ memset(&cap, 0, sizeof(cap));
+ cap.type = htons(TLV_TYPE_UNOTIF_CAP);
+ cap.length = htons(CAP_TLV_UNOTIF_LEN);
+ if (enable)
+ cap.reserved = STATE_BIT;
+
+ return (ibuf_add(buf, &cap, CAP_TLV_UNOTIF_SIZE));
+}
diff --git a/ldpd/interface.c b/ldpd/interface.c
new file mode 100644
index 0000000..f0e70cb
--- /dev/null
+++ b/ldpd/interface.c
@@ -0,0 +1,968 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.h"
+
+#include "sockopt.h"
+
+static __inline int iface_compare(const struct iface *, const struct iface *);
+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 *);
+static void if_hello_timer(struct event *thread);
+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 *);
+
+static int ldp_sync_fsm_init(struct iface *iface, int state);
+static int ldp_sync_act_iface_start_sync(struct iface *iface);
+static void iface_wait_for_ldp_sync_timer(struct event *thread);
+static void start_wait_for_ldp_sync_timer(struct iface *iface);
+static void stop_wait_for_ldp_sync_timer(struct iface *iface);
+static int ldp_sync_act_ldp_start_sync(struct iface *iface);
+static int ldp_sync_act_ldp_complete_sync(struct iface *iface);
+static int iface_to_oper_nbr_count(struct iface *iface, unsigned int type);
+static void ldp_sync_get_peer_ldp_id(struct iface *iface,
+ struct in_addr *peer_ldp_id);
+
+RB_GENERATE(iface_head, iface, entry, iface_compare)
+
+static __inline int
+iface_compare(const struct iface *a, const struct iface *b)
+{
+ return if_cmp_name_func(a->name, b->name);
+}
+
+struct iface *
+if_new(const char *name)
+{
+ struct iface *iface;
+
+ if ((iface = calloc(1, sizeof(*iface))) == NULL)
+ fatal("if_new: calloc");
+
+ strlcpy(iface->name, name, sizeof(iface->name));
+
+ /* ipv4 */
+ iface->ipv4.af = AF_INET;
+ iface->ipv4.iface = iface;
+ iface->ipv4.enabled = 0;
+
+ /* ipv6 */
+ iface->ipv6.af = AF_INET6;
+ iface->ipv6.iface = iface;
+ iface->ipv6.enabled = 0;
+
+ return (iface);
+}
+
+void
+ldpe_if_init(struct iface *iface)
+{
+ log_debug("%s: interface %s", __func__, iface->name);
+
+ LIST_INIT(&iface->addr_list);
+
+ /* ipv4 */
+ iface->ipv4.iface = iface;
+ iface->ipv4.state = IF_STA_DOWN;
+ RB_INIT(ia_adj_head, &iface->ipv4.adj_tree);
+
+ /* ipv6 */
+ iface->ipv6.iface = iface;
+ iface->ipv6.state = IF_STA_DOWN;
+ RB_INIT(ia_adj_head, &iface->ipv6.adj_tree);
+
+ /* LGP IGP Sync */
+ ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH);
+}
+
+void
+ldpe_if_exit(struct iface *iface)
+{
+ struct if_addr *if_addr;
+
+ log_debug("%s: interface %s", __func__, iface->name);
+
+ ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF);
+
+ 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);
+ assert(if_addr != LIST_FIRST(&iface->addr_list));
+ free(if_addr);
+ }
+}
+
+struct iface *
+if_lookup(struct ldpd_conf *xconf, ifindex_t ifindex)
+{
+ struct iface *iface;
+
+ RB_FOREACH(iface, iface_head, &xconf->iface_tree)
+ if (iface->ifindex == ifindex)
+ return (iface);
+
+ return (NULL);
+}
+
+struct iface *
+if_lookup_name(struct ldpd_conf *xconf, const char *ifname)
+{
+ struct iface iface;
+ strlcpy(iface.name, ifname, sizeof(iface.name));
+ return (RB_FIND(iface_head, &xconf->iface_tree, &iface));
+}
+
+void
+if_update_info(struct iface *iface, struct kif *kif)
+{
+ /* get type */
+ if (CHECK_FLAG(kif->flags, IFF_POINTOPOINT))
+ iface->type = IF_TYPE_POINTOPOINT;
+ if (CHECK_FLAG(kif->flags, IFF_BROADCAST) &&
+ CHECK_FLAG(kif->flags, IFF_MULTICAST))
+ iface->type = IF_TYPE_BROADCAST;
+
+ if (ldpd_process == PROC_LDP_ENGINE && iface->operative && !kif->operative)
+ ldp_sync_fsm(iface, LDP_SYNC_EVT_IFACE_SHUTDOWN);
+
+ /* get index and flags */
+ iface->ifindex = kif->ifindex;
+ iface->operative = kif->operative;
+}
+
+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_name(leconf, ka->ifname);
+ 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);
+ ldp_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_name(leconf, ka->ifname);
+ 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);
+ ldp_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);
+ if_start_hello_timer(ia);
+ ia->state = IF_STA_ACTIVE;
+
+ 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 (!RB_EMPTY(ia_adj_head, &ia->adj_tree)) {
+ adj = RB_ROOT(ia_adj_head, &ia->adj_tree);
+
+ 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_reset: unknown af");
+ }
+
+ ia->state = IF_STA_DOWN;
+
+ return (0);
+}
+
+static void
+if_update_af(struct iface_af *ia)
+{
+ 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 (ldp_rtr_id_get(leconf) != INADDR_ANY)
+ rtr_id_ok = 1;
+ else
+ rtr_id_ok = 0;
+
+ if (ia->state == IF_STA_DOWN) {
+ if (!ia->enabled || !ia->iface->operative || !addr_ok ||
+ !socket_ok || !rtr_id_ok)
+ return;
+
+ if_start(ia->iface, ia->af);
+ } else if (ia->state == IF_STA_ACTIVE) {
+ if (ia->enabled && ia->iface->operative && addr_ok &&
+ socket_ok && rtr_id_ok)
+ return;
+
+ if_reset(ia->iface, ia->af);
+ }
+}
+
+void
+ldp_if_update(struct iface *iface, int af)
+{
+ if (af == AF_INET || af == AF_UNSPEC)
+ if_update_af(&iface->ipv4);
+ if (af == AF_INET6 || af == AF_UNSPEC)
+ if_update_af(&iface->ipv6);
+}
+
+void
+if_update_all(int af)
+{
+ struct iface *iface;
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree)
+ ldp_if_update(iface, af);
+}
+
+uint16_t
+if_get_hello_holdtime(struct iface_af *ia)
+{
+ if (ia->hello_holdtime != 0)
+ return (ia->hello_holdtime);
+
+ if ((ldp_af_conf_get(leconf, ia->af))->lhello_holdtime != 0)
+ return ((ldp_af_conf_get(leconf, ia->af))->lhello_holdtime);
+
+ return (leconf->lhello_holdtime);
+}
+
+uint16_t
+if_get_hello_interval(struct iface_af *ia)
+{
+ if (ia->hello_interval != 0)
+ return (ia->hello_interval);
+
+ if ((ldp_af_conf_get(leconf, ia->af))->lhello_interval != 0)
+ return ((ldp_af_conf_get(leconf, ia->af))->lhello_interval);
+
+ return (leconf->lhello_interval);
+}
+
+uint16_t
+if_get_wait_for_sync_interval(void)
+{
+ return (leconf->wait_for_sync_interval);
+}
+
+/* timers */
+/* ARGSUSED */
+static void if_hello_timer(struct event *thread)
+{
+ struct iface_af *ia = EVENT_ARG(thread);
+
+ ia->hello_timer = NULL;
+ send_hello(HELLO_LINK, ia, NULL);
+ if_start_hello_timer(ia);
+}
+
+static void
+if_start_hello_timer(struct iface_af *ia)
+{
+ EVENT_OFF(ia->hello_timer);
+ event_add_timer(master, if_hello_timer, ia, if_get_hello_interval(ia),
+ &ia->hello_timer);
+}
+
+static void
+if_stop_hello_timer(struct iface_af *ia)
+{
+ EVENT_OFF(ia->hello_timer);
+}
+
+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.type = ia->iface->type;
+ ictl.hello_holdtime = if_get_hello_holdtime(ia);
+ ictl.hello_interval = if_get_hello_interval(ia);
+
+ 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;
+ RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
+ ictl.adj_cnt++;
+
+ return (&ictl);
+}
+
+static void
+ldp_sync_get_peer_ldp_id(struct iface *iface, struct in_addr *peer_ldp_id)
+{
+ struct iface_af *ia;
+ struct adj *adj;
+
+ if (iface->ipv4.state == IF_STA_ACTIVE) {
+ ia = iface_af_get(iface, AF_INET);
+ RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
+ if (adj->nbr && adj->nbr->state == NBR_STA_OPER) {
+ *peer_ldp_id = adj->nbr->id;
+ return;
+ }
+ }
+
+ if (iface->ipv6.state == IF_STA_ACTIVE) {
+ ia = iface_af_get(iface, AF_INET6);
+ RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
+ if (adj->nbr && adj->nbr->state == NBR_STA_OPER) {
+ *peer_ldp_id = adj->nbr->id;
+ return;
+ }
+ }
+}
+
+struct ctl_ldp_sync *
+ldp_sync_to_ctl(struct iface *iface)
+{
+ static struct ctl_ldp_sync ictl;
+
+ memcpy(ictl.name, iface->name, sizeof(ictl.name));
+ ictl.ifindex = iface->ifindex;
+ ictl.in_sync = (iface->ldp_sync.state == LDP_SYNC_STA_ACH);
+ ictl.wait_time = if_get_wait_for_sync_interval();
+ ictl.timer_running = iface->ldp_sync.wait_for_sync_timer ? true : false;
+
+ ictl.wait_time_remaining =
+ event_timer_remain_second(iface->ldp_sync.wait_for_sync_timer);
+
+ memset(&ictl.peer_ldp_id, 0, sizeof(ictl.peer_ldp_id));
+
+ ldp_sync_get_peer_ldp_id(iface, &ictl.peer_ldp_id);
+
+ 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 in_addr if_addr;
+
+ log_debug("%s: interface %s addr %pI4", __func__, iface->name, addr);
+
+ if_addr.s_addr = if_get_ipv4_addr(iface);
+
+ if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket,
+ IP_ADD_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) {
+ log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %pI4",
+ __func__, iface->name, addr);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
+{
+ struct in_addr if_addr;
+
+ log_debug("%s: interface %s addr %pI4", __func__, iface->name, addr);
+
+ if_addr.s_addr = if_get_ipv4_addr(iface);
+
+ if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket,
+ IP_DROP_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) {
+ log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s address %pI4", __func__, iface->name, 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);
+}
+
+const struct {
+ int state;
+ enum ldp_sync_event event;
+ enum ldp_sync_action action;
+ int new_state;
+} ldp_sync_fsm_tbl[] = {
+ /* current state event that happened action to take resulting state */
+/* LDP IGP Sync not achieved */
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_LDP_START_SYNC, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_LDP_COMPLETE_SYNC, LDP_SYNC_STA_ACH},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_NOTHING, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_NOTHING, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0},
+/* LDP IGP Sync achieved */
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, LDP_SYNC_STA_NOT_ACH},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_NOTHING, 0},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_NOTHING, 0},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, LDP_SYNC_STA_NOT_ACH},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0},
+ {-1, LDP_SYNC_EVT_NOTHING, LDP_SYNC_ACT_NOTHING, 0},
+};
+
+const char * const ldp_sync_event_names[] = {
+ "NOTHING",
+ "LDP SYNC START",
+ "LDP SYNC COMPLETE",
+ "CONFIG LDP OFF",
+ "IFACE SYNC START (ADJ DEL)",
+ "IFACE SYNC START (ADJ NEW)",
+ "IFACE SYNC START (SESSION CLOSE)",
+ "IFACE SYNC START (CONFIG LDP ON)",
+ "IFACE SHUTDOWN",
+ "N/A"
+};
+
+const char * const ldp_sync_action_names[] = {
+ "NOTHING",
+ "IFACE SYNC START",
+ "LDP START SYNC",
+ "LDP COMPLETE SYNC",
+ "CONFIG LDP OFF",
+ "IFACE SHUTDOWN",
+ "N/A"
+};
+
+const char *
+ldp_sync_state_name(int state)
+{
+ switch (state) {
+ case LDP_SYNC_STA_NOT_ACH:
+ return ("NOT ACHIEVED");
+ case LDP_SYNC_STA_ACH:
+ return ("ACHIEVED");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+static int
+send_ldp_sync_state_update(char *name, int ifindex, int sync_start)
+{
+ debug_evt_ldp_sync("%s: interface %s (%d), sync_start=%d",
+ __func__, name, ifindex, sync_start);
+
+ struct ldp_igp_sync_if_state state;
+
+ state.ifindex = ifindex;
+ state.sync_start = sync_start;
+
+ return ldpe_imsg_compose_parent(IMSG_LDP_SYNC_IF_STATE_UPDATE,
+ getpid(), &state, sizeof(state));
+}
+
+static int
+ldp_sync_act_iface_start_sync(struct iface *iface)
+{
+ send_ldp_sync_state_update(iface->name, iface->ifindex, true);
+
+ return (0);
+}
+
+static void iface_wait_for_ldp_sync_timer(struct event *thread)
+{
+ struct iface *iface = EVENT_ARG(thread);
+
+ ldp_sync_fsm(iface, LDP_SYNC_EVT_LDP_SYNC_COMPLETE);
+}
+
+static void start_wait_for_ldp_sync_timer(struct iface *iface)
+{
+ if (iface->ldp_sync.wait_for_sync_timer)
+ return;
+
+ EVENT_OFF(iface->ldp_sync.wait_for_sync_timer);
+ event_add_timer(master, iface_wait_for_ldp_sync_timer, iface,
+ if_get_wait_for_sync_interval(),
+ &iface->ldp_sync.wait_for_sync_timer);
+}
+
+static void stop_wait_for_ldp_sync_timer(struct iface *iface)
+{
+ EVENT_OFF(iface->ldp_sync.wait_for_sync_timer);
+}
+
+static int
+ldp_sync_act_ldp_start_sync(struct iface *iface)
+{
+ start_wait_for_ldp_sync_timer(iface);
+
+ return 0;
+}
+
+static int
+ldp_sync_act_ldp_complete_sync(struct iface *iface)
+{
+ send_ldp_sync_state_update(iface->name, iface->ifindex, false);
+
+ return 0;
+}
+
+static int
+iface_to_oper_nbr_count(struct iface *iface, unsigned int type)
+{
+ int oper_nbr_count = 0;
+ struct adj *adj;
+
+ RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) {
+ if (type == adj->source.type && adj->nbr &&
+ adj->nbr->state == NBR_STA_OPER)
+ oper_nbr_count++;
+ }
+
+ RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) {
+ if (type == adj->source.type && adj->nbr &&
+ adj->nbr->state == NBR_STA_OPER)
+ oper_nbr_count++;
+ }
+
+ return oper_nbr_count;
+}
+
+int
+ldp_sync_fsm_adj_event(struct adj *adj, enum ldp_sync_event event)
+{
+ if (adj->source.type != HELLO_LINK)
+ return -1;
+
+ struct iface *iface = adj->source.link.ia->iface;
+
+ if (!iface->operative)
+ return 0;
+
+ if (event == LDP_SYNC_EVT_ADJ_NEW) {
+ struct nbr *nbr = adj->nbr;
+ if (nbr && nbr->state == NBR_STA_OPER) {
+ event = LDP_SYNC_EVT_LDP_SYNC_START;
+ }
+ } else if (event == LDP_SYNC_EVT_ADJ_DEL) {
+ /* Ignore if an operational neighbor exists.
+ */
+ int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK);
+ if (oper_nbr_count > 0)
+ return 0;
+ }
+
+ debug_evt_ldp_sync("%s: event %s, "
+ "adj iface %s (%d) lsr-id %pI4 "
+ "source address %s transport address %s",
+ __func__, ldp_sync_event_names[event],
+ adj->source.link.ia->iface->name,
+ adj->source.link.ia->iface->ifindex,
+ &adj->lsr_id,
+ log_addr(adj_get_af(adj), &adj->source.link.src_addr),
+ log_addr(adj_get_af(adj), &adj->trans_addr));
+
+ return ldp_sync_fsm(iface, event);
+}
+
+int
+ldp_sync_fsm_nbr_event(struct nbr *nbr, enum ldp_sync_event event)
+{
+ struct adj *adj;
+ struct iface *iface = NULL;
+ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) {
+ if (HELLO_LINK != adj->source.type)
+ continue;
+
+ iface = adj->source.link.ia->iface;
+
+ if (!iface || !iface->operative)
+ continue;
+
+ int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK);
+
+ if (event == LDP_SYNC_EVT_SESSION_CLOSE && oper_nbr_count > 0)
+ /* Ignore if an operational neighbor exists.
+ */
+ continue;
+
+ debug_evt_ldp_sync("%s: event %s, iface %s, lsr-id %pI4",
+ __func__, ldp_sync_event_names[event],
+ iface->name, &nbr->id);
+
+ ldp_sync_fsm(iface, event);
+ }
+
+ return 0;
+}
+
+int
+ldp_sync_fsm_state_req(struct ldp_igp_sync_if_state_req *state_req)
+{
+ debug_evt_ldp_sync("%s: interface %s (%d) proto %s",
+ __func__, state_req->name, state_req->ifindex,
+ zebra_route_string(state_req->proto));
+
+ struct iface *iface = if_lookup_name(leconf, state_req->name);
+
+ if (!iface) {
+ debug_evt_ldp_sync("%s: Warning: Ignoring LDP IGP SYNC "
+ "interface state request for interface %s (%d). "
+ "Interface does not exist in LDP.",
+ __func__, state_req->name, state_req->ifindex);
+
+ return 0;
+ }
+
+ return send_ldp_sync_state_update(state_req->name,
+ state_req->ifindex,
+ (iface->ldp_sync.state != LDP_SYNC_STA_ACH));
+}
+
+static int
+ldp_sync_fsm_init(struct iface *iface, int state)
+{
+ int old_state = iface->ldp_sync.state;
+
+ iface->ldp_sync.state = state;
+ stop_wait_for_ldp_sync_timer(iface);
+
+ send_ldp_sync_state_update(iface->name, iface->ifindex,
+ (iface->ldp_sync.state != LDP_SYNC_STA_ACH));
+
+ if (old_state != iface->ldp_sync.state) {
+ debug_evt_ldp_sync("%s: resulted in "
+ "changing state for interface %s (%d) from %s to %s",
+ __func__,
+ iface->name, iface->ifindex,
+ ldp_sync_state_name(old_state),
+ ldp_sync_state_name(iface->ldp_sync.state));
+ }
+
+ return 0;
+}
+
+int
+ldp_sync_fsm(struct iface *iface, enum ldp_sync_event event)
+{
+ int old_state = iface->ldp_sync.state;
+ int new_state = 0;
+ int i;
+
+ for (i = 0; ldp_sync_fsm_tbl[i].state != -1; i++)
+ if ((ldp_sync_fsm_tbl[i].state & old_state) &&
+ (ldp_sync_fsm_tbl[i].event == event)) {
+ new_state = ldp_sync_fsm_tbl[i].new_state;
+ break;
+ }
+
+ if (ldp_sync_fsm_tbl[i].state == -1) {
+ /* event outside of the defined fsm, ignore it. */
+ log_warnx("%s: interface %s, event %s not expected in "
+ "state %s ", __func__, iface->name,
+ ldp_sync_event_names[event],
+ ldp_sync_state_name(old_state));
+ return (0);
+ }
+
+ if (new_state != 0)
+ iface->ldp_sync.state = new_state;
+
+ switch (ldp_sync_fsm_tbl[i].action) {
+ case LDP_SYNC_ACT_IFACE_START_SYNC:
+ ldp_sync_act_iface_start_sync(iface);
+ break;
+ case LDP_SYNC_ACT_LDP_START_SYNC:
+ ldp_sync_act_ldp_start_sync(iface);
+ break;
+ case LDP_SYNC_ACT_LDP_COMPLETE_SYNC:
+ ldp_sync_act_ldp_complete_sync(iface);
+ break;
+ case LDP_SYNC_ACT_CONFIG_LDP_OFF:
+ ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH);
+ break;
+ case LDP_SYNC_ACT_IFACE_SHUTDOWN:
+ ldp_sync_fsm_init(iface, iface->ldp_sync.state);
+ break;
+ case LDP_SYNC_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ if (old_state != iface->ldp_sync.state) {
+
+ debug_evt_ldp_sync("%s: event %s resulted in action %s "
+ "for interface %s, changing state from %s to %s",
+ __func__, ldp_sync_event_names[event],
+ ldp_sync_action_names[ldp_sync_fsm_tbl[i].action],
+ iface->name, ldp_sync_state_name(old_state),
+ ldp_sync_state_name(iface->ldp_sync.state));
+
+ } else {
+ debug_evt_ldp_sync("%s: event %s resulted in action %s "
+ "for interface %s, remaining in state %s",
+ __func__, ldp_sync_event_names[event],
+ ldp_sync_action_names[ldp_sync_fsm_tbl[i].action],
+ iface->name,
+ ldp_sync_state_name(iface->ldp_sync.state));
+ }
+
+ return (0);
+}
+
+void
+ldp_sync_fsm_reset_all(void)
+{
+ struct iface *iface;
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree)
+ ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF);
+}
diff --git a/ldpd/keepalive.c b/ldpd/keepalive.c
new file mode 100644
index 0000000..7a71989
--- /dev/null
+++ b/ldpd/keepalive.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.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);
+
+ debug_kalive_send("keepalive: lsr-id %pI4", &nbr->id);
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+ nbr->stats.kalive_sent++;
+}
+
+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);
+ }
+
+ debug_kalive_recv("keepalive: lsr-id %pI4", &nbr->id);
+
+ 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 0000000..ce038ac
--- /dev/null
+++ b/ldpd/l2vpn.c
@@ -0,0 +1,683 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+
+static void l2vpn_pw_fec(struct l2vpn_pw *, struct fec *);
+static __inline int l2vpn_compare(const struct l2vpn *, const struct l2vpn *);
+static __inline int l2vpn_if_compare(const struct l2vpn_if *, const struct l2vpn_if *);
+static __inline int l2vpn_pw_compare(const struct l2vpn_pw *, const struct l2vpn_pw *);
+
+RB_GENERATE(l2vpn_head, l2vpn, entry, l2vpn_compare)
+RB_GENERATE(l2vpn_if_head, l2vpn_if, entry, l2vpn_if_compare)
+RB_GENERATE(l2vpn_pw_head, l2vpn_pw, entry, l2vpn_pw_compare)
+
+static __inline int
+l2vpn_compare(const struct l2vpn *a, const struct l2vpn *b)
+{
+ return (strcmp(a->name, b->name));
+}
+
+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;
+
+ RB_INIT(l2vpn_if_head, &l2vpn->if_tree);
+ RB_INIT(l2vpn_pw_head, &l2vpn->pw_tree);
+ RB_INIT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
+ return (l2vpn);
+}
+
+struct l2vpn *
+l2vpn_find(struct ldpd_conf *xconf, const char *name)
+{
+ struct l2vpn l2vpn;
+ strlcpy(l2vpn.name, name, sizeof(l2vpn.name));
+ return (RB_FIND(l2vpn_head, &xconf->l2vpn_tree, &l2vpn));
+}
+
+void
+l2vpn_del(struct l2vpn *l2vpn)
+{
+ struct l2vpn_if *lif;
+ struct l2vpn_pw *pw;
+
+ while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+ lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
+ RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
+ free(lif);
+ }
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
+ free(pw);
+ }
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ free(pw);
+ }
+
+ free(l2vpn);
+}
+
+void
+l2vpn_init(struct l2vpn *l2vpn)
+{
+ struct l2vpn_pw *pw;
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
+ l2vpn_pw_init(pw);
+}
+
+void
+l2vpn_exit(struct l2vpn *l2vpn)
+{
+ struct l2vpn_pw *pw;
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
+ l2vpn_pw_exit(pw);
+}
+
+static __inline int
+l2vpn_if_compare(const struct l2vpn_if *a, const struct l2vpn_if *b)
+{
+ return if_cmp_name_func(a->ifname, b->ifname);
+}
+
+struct l2vpn_if *
+l2vpn_if_new(struct l2vpn *l2vpn, const char *ifname)
+{
+ struct l2vpn_if *lif;
+
+ if ((lif = calloc(1, sizeof(*lif))) == NULL)
+ fatal("l2vpn_if_new: calloc");
+
+ lif->l2vpn = l2vpn;
+ strlcpy(lif->ifname, ifname, sizeof(lif->ifname));
+
+ return (lif);
+}
+
+struct l2vpn_if *
+l2vpn_if_find(struct l2vpn *l2vpn, const char *ifname)
+{
+ struct l2vpn_if lif;
+ strlcpy(lif.ifname, ifname, sizeof(lif.ifname));
+ return (RB_FIND(l2vpn_if_head, &l2vpn->if_tree, &lif));
+}
+
+void
+l2vpn_if_update_info(struct l2vpn_if *lif, struct kif *kif)
+{
+ lif->ifindex = kif->ifindex;
+ lif->operative = kif->operative;
+ memcpy(lif->mac, kif->mac, sizeof(lif->mac));
+}
+
+void
+l2vpn_if_update(struct l2vpn_if *lif)
+{
+ struct l2vpn *l2vpn = lif->l2vpn;
+ struct l2vpn_pw *pw;
+ struct map fec;
+ struct nbr *nbr;
+
+ if (lif->operative)
+ return;
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) {
+ nbr = nbr_find_ldpid(pw->lsr_id.s_addr);
+ if (nbr == NULL)
+ continue;
+
+ memset(&fec, 0, sizeof(fec));
+ fec.type = MAP_TYPE_PWID;
+ fec.fec.pwid.type = l2vpn->pw_type;
+ fec.fec.pwid.group_id = 0;
+ SET_FLAG(fec.flags, F_MAP_PW_ID);
+ fec.fec.pwid.pwid = pw->pwid;
+
+ send_mac_withdrawal(nbr, &fec, lif->mac);
+ }
+}
+
+static __inline int
+l2vpn_pw_compare(const struct l2vpn_pw *a, const struct l2vpn_pw *b)
+{
+ return if_cmp_name_func(a->ifname, b->ifname);
+}
+
+struct l2vpn_pw *
+l2vpn_pw_new(struct l2vpn *l2vpn, const char *ifname)
+{
+ struct l2vpn_pw *pw;
+
+ if ((pw = calloc(1, sizeof(*pw))) == NULL)
+ fatal("l2vpn_pw_new: calloc");
+
+ pw->l2vpn = l2vpn;
+ strlcpy(pw->ifname, ifname, sizeof(pw->ifname));
+
+ return (pw);
+}
+
+struct l2vpn_pw *
+l2vpn_pw_find(struct l2vpn *l2vpn, const char *ifname)
+{
+ struct l2vpn_pw *pw;
+ struct l2vpn_pw s;
+
+ strlcpy(s.ifname, ifname, sizeof(s.ifname));
+ pw = RB_FIND(l2vpn_pw_head, &l2vpn->pw_tree, &s);
+ if (pw)
+ return (pw);
+ return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_inactive_tree, &s));
+}
+
+struct l2vpn_pw *
+l2vpn_pw_find_active(struct l2vpn *l2vpn, const char *ifname)
+{
+ struct l2vpn_pw s;
+
+ strlcpy(s.ifname, ifname, sizeof(s.ifname));
+ return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_tree, &s));
+}
+
+struct l2vpn_pw *
+l2vpn_pw_find_inactive(struct l2vpn *l2vpn, const char *ifname)
+{
+ struct l2vpn_pw s;
+
+ strlcpy(s.ifname, ifname, sizeof(s.ifname));
+ return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_inactive_tree, &s));
+}
+
+void
+l2vpn_pw_update_info(struct l2vpn_pw *pw, struct kif *kif)
+{
+ pw->ifindex = kif->ifindex;
+}
+
+void
+l2vpn_pw_init(struct l2vpn_pw *pw)
+{
+ struct fec fec;
+ struct zapi_pw zpw;
+
+ l2vpn_pw_reset(pw);
+
+ pw2zpw(pw, &zpw);
+ lde_imsg_compose_parent(IMSG_KPW_ADD, 0, &zpw, sizeof(zpw));
+
+ l2vpn_pw_fec(pw, &fec);
+ lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0,
+ 0, 0, (void *)pw);
+ lde_kernel_update(&fec);
+}
+
+void
+l2vpn_pw_exit(struct l2vpn_pw *pw)
+{
+ struct fec fec;
+ struct zapi_pw zpw;
+
+ l2vpn_pw_fec(pw, &fec);
+ lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0, 0);
+ lde_kernel_update(&fec);
+
+ pw2zpw(pw, &zpw);
+ lde_imsg_compose_parent(IMSG_KPW_DELETE, 0, &zpw, sizeof(zpw));
+}
+
+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->local_status = PW_FORWARDING;
+ pw->remote_status = PW_NOT_FORWARDING;
+
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD_CONF))
+ SET_FLAG(pw->flags, F_PW_CWORD);
+ else
+ UNSET_FLAG(pw->flags, F_PW_CWORD);
+
+ if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV_CONF))
+ SET_FLAG(pw->flags, F_PW_STATUSTLV);
+ else
+ UNSET_FLAG(pw->flags, F_PW_STATUSTLV);
+
+ if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV_CONF)) {
+ struct fec_node *fn;
+ struct fec fec;
+ l2vpn_pw_fec(pw, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn)
+ pw->remote_status = fn->pw_remote_status;
+ }
+
+}
+
+int
+l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh)
+{
+ /* check for a remote label */
+ if (fnh->remote_label == NO_LABEL) {
+ log_warnx("%s: pseudowire %s: no remote label", __func__, pw->ifname);
+ pw->reason = F_PW_NO_REMOTE_LABEL;
+ return (0);
+ }
+
+ /* MTUs must match */
+ if (pw->l2vpn->mtu != pw->remote_mtu) {
+ log_warnx("%s: pseudowire %s: MTU mismatch detected", __func__,
+ pw->ifname);
+ pw->reason = F_PW_MTU_MISMATCH;
+ return (0);
+ }
+
+ /* check pw status if applicable */
+ if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV) &&
+ pw->remote_status != PW_FORWARDING) {
+ log_warnx("%s: pseudowire %s: remote end is down", __func__, pw->ifname);
+ pw->reason = F_PW_REMOTE_NOT_FWD;
+ return (0);
+ }
+
+ pw->reason = F_PW_NO_ERR;
+ 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 (CHECK_FLAG(map->flags, F_MAP_PW_CWORD) &&
+ !CHECK_FLAG(pw->flags, F_PW_CWORD_CONF)) {
+ /* ignore the received label mapping */
+ return (1);
+ } else if (!CHECK_FLAG(map->flags, F_MAP_PW_CWORD) &&
+ CHECK_FLAG(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, NULL, &st);
+
+ UNSET_FLAG(pw->flags, F_PW_CWORD);
+ lde_send_labelmapping(ln, fn, 1);
+ }
+ } else if (CHECK_FLAG(map->flags, F_MAP_PW_CWORD)) {
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD_CONF))
+ SET_FLAG(pw->flags, F_PW_CWORD);
+ else
+ /* act as if no label mapping had been received */
+ return (1);
+ } else
+ UNSET_FLAG(pw->flags, F_PW_CWORD);
+
+ /* RFC4447 - Section 5.4.3: pseudowire status negotiation */
+ if (fec_find(&ln->recv_map, &fn->fec) == NULL &&
+ !CHECK_FLAG(map->flags, F_MAP_PW_STATUS))
+ UNSET_FLAG(pw->flags, F_PW_STATUSTLV);
+
+ return (0);
+}
+
+void
+l2vpn_send_pw_status(struct lde_nbr *ln, 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;
+ SET_FLAG(nm.flags, F_NOTIF_PW_STATUS);
+ lde_fec2map(fec, &nm.fec);
+ SET_FLAG(nm.flags, F_NOTIF_FEC);
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, sizeof(nm));
+}
+
+void
+l2vpn_send_pw_status_wcard(struct lde_nbr *ln, uint32_t status,
+ uint16_t pw_type, uint32_t group_id)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_PW_STATUS;
+ nm.pw_status = status;
+ SET_FLAG(nm.flags, F_NOTIF_PW_STATUS);
+ nm.fec.type = MAP_TYPE_PWID;
+ nm.fec.fec.pwid.type = pw_type;
+ nm.fec.fec.pwid.group_id = group_id;
+ SET_FLAG(nm.flags, F_NOTIF_FEC);
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->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;
+
+ if (nm->fec.type == MAP_TYPE_TYPED_WCARD ||
+ !CHECK_FLAG(nm->fec.flags, F_MAP_PW_ID)) {
+ l2vpn_recv_pw_status_wcard(ln, nm);
+ return;
+ }
+
+ lde_map2fec(&nm->fec, ln->id, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn == NULL)
+ /* unknown fec */
+ return;
+
+ fn->pw_remote_status = nm->pw_status;
+
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL)
+ return;
+
+ fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0, 0, 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);
+}
+
+/* RFC4447 PWid group wildcard */
+void
+l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct l2vpn_pw *pw;
+ struct map *wcard = &nm->fec;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (fn->fec.type != FEC_TYPE_PWID)
+ continue;
+
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL)
+ continue;
+
+ switch (wcard->type) {
+ case MAP_TYPE_TYPED_WCARD:
+ if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD &&
+ wcard->fec.twcard.u.pw_type != fn->fec.u.pwid.type)
+ continue;
+ break;
+ case MAP_TYPE_PWID:
+ if (wcard->fec.pwid.type != fn->fec.u.pwid.type)
+ continue;
+ if (wcard->fec.pwid.group_id != pw->remote_group)
+ continue;
+ break;
+ }
+
+ fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id,
+ 0, 0, 0);
+ if (fnh == NULL)
+ continue;
+
+ /* remote status didn't change */
+ if (pw->remote_status == nm->pw_status)
+ continue;
+ 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);
+ }
+}
+
+int
+l2vpn_pw_status_update(struct zapi_pw_status *zpw)
+{
+ struct l2vpn *l2vpn;
+ struct l2vpn_pw *pw = NULL;
+ struct lde_nbr *ln;
+ struct fec fec;
+ uint32_t local_status;
+
+ RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree) {
+ pw = l2vpn_pw_find(l2vpn, zpw->ifname);
+ if (pw)
+ break;
+ }
+ if (!pw) {
+ log_warnx("%s: pseudowire %s not found", __func__, zpw->ifname);
+ return (1);
+ }
+
+ if (zpw->status == PW_FORWARDING) {
+ local_status = PW_FORWARDING;
+ pw->reason = F_PW_NO_ERR;
+ } else {
+ local_status = zpw->status;
+ pw->reason = F_PW_LOCAL_NOT_FWD;
+ }
+
+ /* local status didn't change */
+ if (pw->local_status == local_status)
+ return (0);
+ pw->local_status = local_status;
+
+ /* notify remote peer about the status update */
+ ln = lde_nbr_find_by_lsrid(pw->lsr_id);
+ if (ln == NULL)
+ return (0);
+ l2vpn_pw_fec(pw, &fec);
+ if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV))
+ l2vpn_send_pw_status(ln, local_status, &fec);
+ else {
+ struct fec_node *fn;
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn) {
+ if (pw->local_status == PW_FORWARDING)
+ lde_send_labelmapping(ln, fn, 1);
+ else
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+ }
+ }
+
+ return (0);
+}
+
+void
+l2vpn_pw_ctl(pid_t pid)
+{
+ struct l2vpn *l2vpn;
+ struct l2vpn_pw *pw;
+ static struct ctl_pw pwctl;
+
+ RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree)
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) {
+ memset(&pwctl, 0, sizeof(pwctl));
+ strlcpy(pwctl.l2vpn_name, pw->l2vpn->name,
+ sizeof(pwctl.l2vpn_name));
+ strlcpy(pwctl.ifname, pw->ifname,
+ sizeof(pwctl.ifname));
+ pwctl.pwid = pw->pwid;
+ pwctl.lsr_id = pw->lsr_id;
+ pwctl.status = PW_NOT_FORWARDING;
+ if (pw->enabled &&
+ pw->local_status == PW_FORWARDING &&
+ pw->remote_status == PW_FORWARDING)
+ pwctl.status = PW_FORWARDING;
+
+ 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 &&
+ RB_EMPTY(lde_map_head, &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;
+ pwctl.local_cword = CHECK_FLAG(pw->flags, F_PW_CWORD_CONF) ? 1 : 0;
+ pwctl.reason = pw->reason;
+ } else
+ pwctl.local_label = NO_LABEL;
+
+ RB_FOREACH(me, lde_map_head, &fn->downstream)
+ 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 (CHECK_FLAG(me->map.flags, F_MAP_PW_IFMTU))
+ pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu;
+ if (pw)
+ pwctl.remote_cword = CHECK_FLAG(pw->flags, F_PW_CWORD) ? 1 : 0;
+
+ 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;
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
+ ldpe_l2vpn_pw_init(pw);
+}
+
+void
+ldpe_l2vpn_exit(struct l2vpn *l2vpn)
+{
+ struct l2vpn_pw *pw;
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
+ 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(pw->af, &pw->addr);
+ tnbr_update(tnbr);
+ RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr);
+ }
+
+ 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(leconf, tnbr);
+ }
+}
diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c
new file mode 100644
index 0000000..3c5a5d9
--- /dev/null
+++ b/ldpd/labelmapping.c
@@ -0,0 +1,866 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2014, 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.h"
+
+#include "mpls.h"
+
+static void enqueue_pdu(struct nbr *, uint16_t, struct ibuf *, uint16_t);
+static int gen_label_tlv(struct ibuf *, uint32_t);
+static int gen_reqid_tlv(struct ibuf *, uint32_t);
+static void log_msg_mapping(int, uint16_t, struct nbr *, struct map *);
+
+static void
+enqueue_pdu(struct nbr *nbr, uint16_t type, 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 - LDP_HDR_DEAD_LEN);
+ 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 */
+ SET_FLAG(err, gen_ldp_hdr(buf, 0));
+
+ size = LDP_HDR_SIZE;
+ first = 0;
+ }
+
+ /* calculate size */
+ msg_size = LDP_MSG_SIZE;
+ msg_size += len_fec_tlv(&me->map);
+ if (me->map.label != NO_LABEL)
+ msg_size += LABEL_TLV_SIZE;
+ if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID))
+ msg_size += REQID_TLV_SIZE;
+ if (CHECK_FLAG(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, type, buf, size);
+ first = 1;
+ continue;
+ }
+
+ size += msg_size;
+
+ /* append message and tlvs */
+ SET_FLAG(err, gen_msg_hdr(buf, type, msg_size));
+ SET_FLAG(err, gen_fec_tlv(buf, &me->map));
+ if (me->map.label != NO_LABEL)
+ SET_FLAG(err, gen_label_tlv(buf, me->map.label));
+ if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID))
+ SET_FLAG(err, gen_reqid_tlv(buf, me->map.requestid));
+ if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS))
+ SET_FLAG(err, gen_pw_status_tlv(buf, me->map.pw_status));
+ if (CHECK_FLAG(me->map.flags, F_MAP_STATUS))
+ SET_FLAG(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_msg_mapping(1, type, nbr, &me->map);
+
+ /* no errors - update per neighbor message counters */
+ switch (type) {
+ case MSG_TYPE_LABELMAPPING:
+ nbr->stats.labelmap_sent++;
+ break;
+ case MSG_TYPE_LABELREQUEST:
+ nbr->stats.labelreq_sent++;
+ break;
+ case MSG_TYPE_LABELWITHDRAW:
+ nbr->stats.labelwdraw_sent++;
+ break;
+ case MSG_TYPE_LABELRELEASE:
+ nbr->stats.labelrel_sent++;
+ break;
+ case MSG_TYPE_LABELABORTREQ:
+ nbr->stats.labelabreq_sent++;
+ break;
+ default:
+ break;
+ }
+
+ TAILQ_REMOVE(mh, me, entry);
+ assert(me != TAILQ_FIRST(mh));
+ free(me);
+ }
+
+ enqueue_pdu(nbr, type, 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, tlen;
+ uint16_t current_tlv = 1;
+ 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->tcp, 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 &&
+ !CHECK_FLAG(map.flags, F_MAP_PW_ID) &&
+ type != MSG_TYPE_LABELWITHDRAW &&
+ type != MSG_TYPE_LABELRELEASE) {
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
+ goto err;
+ }
+
+ /*
+ * 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;
+ }
+ }
+
+ /*
+ * RFC 5561 - Section 4:
+ * "An LDP implementation that supports the Typed Wildcard
+ * FEC Element MUST support its use in Label Request, Label
+ * Withdraw, and Label Release messages".
+ */
+ if (map.type == MAP_TYPE_TYPED_WCARD) {
+ switch (type) {
+ case MSG_TYPE_LABELMAPPING:
+ 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);
+
+ /* Optional Parameters */
+ while (len > 0) {
+ struct tlv tlv;
+ uint16_t tlv_type;
+ 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_type = ntohs(tlv.type);
+ 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;
+
+ /*
+ * For Label Mapping messages the Label TLV is mandatory and
+ * should appear right after the FEC TLV.
+ */
+ if (current_tlv == 1 &&
+ type == MSG_TYPE_LABELMAPPING &&
+ !CHECK_FLAG(tlv_type, TLV_TYPE_GENERICLABEL)) {
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
+ goto err;
+ }
+
+ switch (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;
+ }
+
+ SET_FLAG(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_LABELMAPPING:
+ 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);
+ /* do not accept invalid labels */
+ if (label > MPLS_LABEL_MAX ||
+ (label <= MPLS_LABEL_RESERVED_MAX &&
+ label != MPLS_LABEL_IPV4_EXPLICIT_NULL &&
+ label != MPLS_LABEL_IPV6_EXPLICIT_NULL &&
+ label != MPLS_LABEL_IMPLICIT_NULL)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
+ goto err;
+ }
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ break;
+ case TLV_TYPE_ATMLABEL:
+ case TLV_TYPE_FRLABEL:
+ switch (type) {
+ case MSG_TYPE_LABELMAPPING:
+ 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;
+ }
+
+ SET_FLAG(flags, F_MAP_PW_STATUS);
+ memcpy(&statusbuf, buf, sizeof(statusbuf));
+ pw_status = ntohl(statusbuf);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ break;
+ default:
+ if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG))
+ send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ current_tlv++;
+ }
+
+ /* notify lde about the received message. */
+ while ((me = TAILQ_FIRST(&mh)) != NULL) {
+ int imsg_type = IMSG_NONE;
+
+ SET_FLAG(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_IPV6_EXPLICIT_NULL) {
+ 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_IPV4_EXPLICIT_NULL) {
+ 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 (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS))
+ me->map.pw_status = pw_status;
+ break;
+ default:
+ break;
+ }
+ me->map.label = label;
+ if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID))
+ me->map.requestid = reqid;
+
+ log_msg_mapping(0, type, nbr, &me->map);
+
+ 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);
+ assert(me != TAILQ_FIRST(&mh));
+ 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
+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)));
+}
+
+uint16_t
+len_fec_tlv(struct map *map)
+{
+ uint16_t len = TLV_HDR_SIZE;
+
+ switch (map->type) {
+ case MAP_TYPE_WILDCARD:
+ len += FEC_ELM_WCARD_LEN;
+ break;
+ case MAP_TYPE_PREFIX:
+ len += FEC_ELM_PREFIX_MIN_LEN +
+ PREFIX_SIZE(map->fec.prefix.prefixlen);
+ break;
+ case MAP_TYPE_PWID:
+ len += FEC_PWID_ELM_MIN_LEN;
+ if (CHECK_FLAG(map->flags, F_MAP_PW_ID))
+ len += PW_STATUS_TLV_LEN;
+ if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU))
+ len += FEC_SUBTLV_IFMTU_SIZE;
+ if (CHECK_FLAG(map->flags, F_MAP_PW_STATUS))
+ len += PW_STATUS_TLV_SIZE;
+ break;
+ case MAP_TYPE_TYPED_WCARD:
+ len += FEC_ELM_TWCARD_MIN_LEN;
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ case MAP_TYPE_PWID:
+ len += sizeof(uint16_t);
+ break;
+ default:
+ fatalx("len_fec_tlv: unexpected fec type");
+ }
+ break;
+ default:
+ fatalx("len_fec_tlv: unexpected fec type");
+ }
+
+ return (len);
+}
+
+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, twcard_len;
+ 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));
+ SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft)));
+ SET_FLAG(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);
+ SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft)));
+ SET_FLAG(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;
+ }
+ SET_FLAG(err, ibuf_add(buf, &family, sizeof(family)));
+ SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefixlen,
+ sizeof(map->fec.prefix.prefixlen)));
+ if (len)
+ SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefix, len));
+ break;
+ case MAP_TYPE_PWID:
+ if (CHECK_FLAG(map->flags, F_MAP_PW_ID))
+ pw_len += FEC_PWID_SIZE;
+ if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU))
+ pw_len += FEC_SUBTLV_IFMTU_SIZE;
+
+ len = FEC_PWID_ELM_MIN_LEN + pw_len;
+
+ ft.length = htons(len);
+ SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft)));
+
+ SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t)));
+ pw_type = map->fec.pwid.type;
+ if (CHECK_FLAG(map->flags, F_MAP_PW_CWORD))
+ SET_FLAG(pw_type, CONTROL_WORD_FLAG);
+ pw_type = htons(pw_type);
+ SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t)));
+ SET_FLAG(err, ibuf_add(buf, &pw_len, sizeof(uint8_t)));
+ group_id = htonl(map->fec.pwid.group_id);
+ SET_FLAG(err, ibuf_add(buf, &group_id, sizeof(uint32_t)));
+ if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) {
+ pwid = htonl(map->fec.pwid.pwid);
+ SET_FLAG(err, ibuf_add(buf, &pwid, sizeof(uint32_t)));
+ }
+ if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) {
+ struct subtlv stlv;
+
+ stlv.type = SUBTLV_IFMTU;
+ stlv.length = FEC_SUBTLV_IFMTU_SIZE;
+ SET_FLAG(err, ibuf_add(buf, &stlv, sizeof(uint16_t)));
+
+ ifmtu = htons(map->fec.pwid.ifmtu);
+ SET_FLAG(err, ibuf_add(buf, &ifmtu, sizeof(uint16_t)));
+ }
+ break;
+ case MAP_TYPE_TYPED_WCARD:
+ len = FEC_ELM_TWCARD_MIN_LEN;
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ case MAP_TYPE_PWID:
+ len += sizeof(uint16_t);
+ break;
+ default:
+ fatalx("gen_fec_tlv: unexpected fec type");
+ }
+ ft.length = htons(len);
+ SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft)));
+ SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t)));
+ SET_FLAG(err, ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t)));
+
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ twcard_len = sizeof(uint16_t);
+ SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t)));
+
+ switch (map->fec.twcard.u.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;
+ }
+
+ SET_FLAG(err, ibuf_add(buf, &family, sizeof(uint16_t)));
+ break;
+ case MAP_TYPE_PWID:
+ twcard_len = sizeof(uint16_t);
+ SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t)));
+ pw_type = htons(map->fec.twcard.u.pw_type);
+ SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t)));
+ break;
+ default:
+ fatalx("gen_fec_tlv: unexpected fec type");
+ }
+ 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, twcard_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->tcp, S_UNSUP_ADDR, msg->id, msg->type);
+ return (-1);
+ }
+
+ /* Prefix Length */
+ map->fec.prefix.prefixlen = buf[off];
+ off += sizeof(uint8_t);
+ if ((map->fec.prefix.af == AF_IPV4
+ && map->fec.prefix.prefixlen > IPV4_MAX_BITLEN)
+ || (map->fec.prefix.af == AF_IPV6
+ && map->fec.prefix.prefixlen > IPV6_MAX_BITLEN)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
+ return (-1);
+ }
+ 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 (CHECK_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG)) {
+ SET_FLAG(map->flags, F_MAP_PW_CWORD);
+ UNSET_FLAG(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);
+ SET_FLAG(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);
+ SET_FLAG(map->flags, F_MAP_PW_IFMTU);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ off += stlv.length;
+ pw_len -= stlv.length;
+ }
+
+ return (off);
+ case MAP_TYPE_TYPED_WCARD:
+ if (len < FEC_ELM_TWCARD_MIN_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
+ return (-1);
+ }
+
+ memcpy(&map->fec.twcard.type, buf + off, sizeof(uint8_t));
+ off += sizeof(uint8_t);
+ memcpy(&twcard_len, buf + off, sizeof(uint8_t));
+ off += sizeof(uint8_t);
+ if (len != FEC_ELM_TWCARD_MIN_LEN + twcard_len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
+ return (-1);
+ }
+
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ if (twcard_len != sizeof(uint16_t)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
+ return (-1);
+ }
+
+ memcpy(&map->fec.twcard.u.prefix_af, buf + off, sizeof(uint16_t));
+ map->fec.twcard.u.prefix_af = ntohs(map->fec.twcard.u.prefix_af);
+ off += sizeof(uint16_t);
+
+ switch (map->fec.twcard.u.prefix_af) {
+ case AF_IPV4:
+ map->fec.twcard.u.prefix_af = AF_INET;
+ break;
+ case AF_IPV6:
+ map->fec.twcard.u.prefix_af = AF_INET6;
+ break;
+ default:
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
+ return (-1);
+ }
+ break;
+ case MAP_TYPE_PWID:
+ if (twcard_len != sizeof(uint16_t)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
+ return (-1);
+ }
+
+ memcpy(&map->fec.twcard.u.pw_type, buf + off, sizeof(uint16_t));
+ map->fec.twcard.u.pw_type = ntohs(map->fec.twcard.u.pw_type);
+ /* ignore the reserved bit as per RFC 6667 */
+ map->fec.twcard.u.pw_type &= ~PW_TWCARD_RESERVED_BIT;
+ off += sizeof(uint16_t);
+ break;
+ default:
+ send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type);
+ return (-1);
+ }
+
+ return (off);
+ default:
+ send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type);
+ break;
+ }
+
+ return (-1);
+}
+
+static void
+log_msg_mapping(int out, uint16_t msg_type, struct nbr *nbr, struct map *map)
+{
+ debug_msg(out, "%s: lsr-id %pI4, fec %s, label %s", msg_name(msg_type),
+ &nbr->id, log_map(map), log_label(map->label));
+}
diff --git a/ldpd/lde.c b/ldpd/lde.c
new file mode 100644
index 0000000..c7e915d
--- /dev/null
+++ b/ldpd/lde.c
@@ -0,0 +1,2500 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldp.h"
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+#include "lde.h"
+#include "ldp_debug.h"
+#include "rlfa.h"
+
+#include <lib/log.h>
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "mpls.h"
+#include <lib/linklist.h>
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "libfrr.h"
+
+static void lde_shutdown(void);
+static void lde_dispatch_imsg(struct event *thread);
+static void lde_dispatch_parent(struct event *thread);
+static __inline int lde_nbr_compare(const struct lde_nbr *,
+ const struct lde_nbr *);
+static struct lde_nbr *lde_nbr_new(uint32_t, struct lde_nbr *);
+static void lde_nbr_del(struct lde_nbr *);
+static struct lde_nbr *lde_nbr_find(uint32_t);
+static void lde_nbr_clear(void);
+static void lde_nbr_addr_update(struct lde_nbr *,
+ struct lde_addr *, int);
+static __inline int lde_map_compare(const struct lde_map *,
+ const struct lde_map *);
+static void lde_map_free(void *);
+static int lde_address_add(struct lde_nbr *, struct lde_addr *);
+static int lde_address_del(struct lde_nbr *, struct lde_addr *);
+static void lde_address_list_free(struct lde_nbr *);
+static void zclient_sync_init(void);
+static void lde_label_list_init(void);
+static int lde_get_label_chunk(void);
+static void on_get_label_chunk_response(uint32_t start, uint32_t end);
+static uint32_t lde_get_next_label(void);
+static bool lde_fec_connected(const struct fec_node *);
+static bool lde_fec_outside_mpls_network(const struct fec_node *);
+static void lde_check_filter_af(int, struct ldpd_af_conf *,
+ const char *);
+
+RB_GENERATE(nbr_tree, lde_nbr, entry, lde_nbr_compare)
+RB_GENERATE(lde_map_head, lde_map, entry, lde_map_compare)
+
+struct ldpd_conf *ldeconf;
+struct nbr_tree lde_nbrs = RB_INITIALIZER(&lde_nbrs);
+
+static struct imsgev *iev_ldpe;
+static struct imsgev iev_main_sync_data;
+static struct imsgev *iev_main, *iev_main_sync;
+
+/* lde privileges */
+static zebra_capabilities_t _caps_p [] =
+{
+ ZCAP_NET_ADMIN
+};
+
+static struct zebra_privs_t lde_privs =
+{
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0
+};
+
+/* List of chunks of labels externally assigned by Zebra */
+static struct list *label_chunk_list;
+static struct listnode *current_label_chunk;
+
+/* Synchronous zclient to request labels */
+struct zclient *zclient_sync;
+
+/* SIGINT / SIGTERM handler. */
+static void
+sigint(void)
+{
+ lde_shutdown();
+}
+
+static struct frr_signal_t lde_signals[] =
+{
+ {
+ .signal = SIGHUP,
+ /* ignore */
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+/* label decision engine */
+void
+lde(void)
+{
+#ifdef HAVE_SETPROCTITLE
+ setproctitle("label decision engine");
+#endif
+ ldpd_process = PROC_LDE_ENGINE;
+ log_procname = log_procnames[PROC_LDE_ENGINE];
+
+ master = frr_init();
+ /* no frr_config_fork() here, allow frr_pthread to create threads */
+ frr_is_after_fork = true;
+
+ /* setup signal handler */
+ signal_init(master, array_size(lde_signals), lde_signals);
+
+ /* setup pipes and event handlers to the parent process */
+ if ((iev_main = calloc(1, sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC);
+ iev_main->handler_read = lde_dispatch_parent;
+ event_add_read(master, iev_main->handler_read, iev_main,
+ iev_main->ibuf.fd, &iev_main->ev_read);
+ iev_main->handler_write = ldp_write_handler;
+
+ memset(&iev_main_sync_data, 0, sizeof(iev_main_sync_data));
+ iev_main_sync = &iev_main_sync_data;
+ imsg_init(&iev_main_sync->ibuf, LDPD_FD_SYNC);
+
+ /* create base configuration */
+ ldeconf = config_new_empty();
+
+ struct event thread;
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+
+ /* NOTREACHED */
+ return;
+}
+
+void
+lde_init(struct ldpd_init *init)
+{
+ /* drop privileges */
+ lde_privs.user = init->user;
+ lde_privs.group = init->group;
+ zprivs_preinit(&lde_privs);
+ zprivs_init(&lde_privs);
+
+ /* start the LIB garbage collector */
+ lde_gc_start_timer();
+
+ /* Init synchronous zclient and label list */
+ frr_zclient_addr(&zclient_addr, &zclient_addr_len,
+ init->zclient_serv_path);
+ zclient_sync_init();
+}
+
+static void
+lde_shutdown(void)
+{
+ /* close pipes */
+ if (iev_ldpe) {
+ msgbuf_clear(&iev_ldpe->ibuf.w);
+ close(iev_ldpe->ibuf.fd);
+ iev_ldpe->ibuf.fd = -1;
+ }
+ msgbuf_clear(&iev_main->ibuf.w);
+ close(iev_main->ibuf.fd);
+ iev_main->ibuf.fd = -1;
+ msgbuf_clear(&iev_main_sync->ibuf.w);
+ close(iev_main_sync->ibuf.fd);
+ iev_main_sync->ibuf.fd = -1;
+
+ lde_gc_stop_timer();
+ lde_nbr_clear();
+ fec_tree_clear();
+
+ config_clear(ldeconf);
+
+ if (iev_ldpe)
+ free(iev_ldpe);
+ free(iev_main);
+
+ log_info("label decision engine exiting");
+
+ zlog_fini();
+ exit(0);
+}
+
+/* imesg */
+int
+lde_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ if (iev_main->ibuf.fd == -1)
+ return (0);
+ return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen));
+}
+
+void
+lde_imsg_compose_parent_sync(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ if (iev_main_sync->ibuf.fd == -1)
+ return;
+ imsg_compose_event(iev_main_sync, type, 0, pid, -1, data, datalen);
+ imsg_flush(&iev_main_sync->ibuf);
+}
+
+int
+lde_imsg_compose_ldpe(int type, uint32_t peerid, pid_t pid, void *data,
+ uint16_t datalen)
+{
+ if (iev_ldpe->ibuf.fd == -1)
+ return (0);
+ return (imsg_compose_event(iev_ldpe, type, peerid, pid,
+ -1, data, datalen));
+}
+
+/* ARGSUSED */
+static void lde_dispatch_imsg(struct event *thread)
+{
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ struct lde_nbr *ln;
+ struct map *map;
+ struct lde_addr *lde_addr;
+ struct notify_msg *nm;
+ ssize_t n;
+ int shut = 0;
+
+ iev->ev_read = NULL;
+
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("lde_dispatch_imsg: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_LABEL_MAPPING_FULL:
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ fec_snap(ln);
+ break;
+ case IMSG_LABEL_MAPPING:
+ case IMSG_LABEL_REQUEST:
+ case IMSG_LABEL_RELEASE:
+ case IMSG_LABEL_WITHDRAW:
+ case IMSG_LABEL_ABORT:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct map))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ map = imsg.data;
+
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_LABEL_MAPPING:
+ lde_check_mapping(map, ln, 1);
+ break;
+ case IMSG_LABEL_REQUEST:
+ lde_check_request(map, ln);
+ break;
+ case IMSG_LABEL_RELEASE:
+ lde_check_release(map, ln);
+ break;
+ case IMSG_LABEL_WITHDRAW:
+ lde_check_withdraw(map, ln);
+ break;
+ case IMSG_LABEL_ABORT:
+ /* not necessary */
+ break;
+ }
+ break;
+ case IMSG_ADDRESS_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_addr))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ lde_addr = imsg.data;
+
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ if (lde_address_add(ln, lde_addr) < 0) {
+ log_debug("%s: cannot add address %s, it already exists", __func__,
+ log_addr(lde_addr->af, &lde_addr->addr));
+ }
+ break;
+ case IMSG_ADDRESS_DEL:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_addr))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ lde_addr = imsg.data;
+
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ if (lde_address_del(ln, lde_addr) < 0) {
+ log_debug("%s: cannot delete address %s, it does not exist", __func__,
+ log_addr(lde_addr->af, &lde_addr->addr));
+ }
+ break;
+ case IMSG_NOTIFICATION:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct notify_msg))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+ nm = imsg.data;
+
+ ln = lde_nbr_find(imsg.hdr.peerid);
+ if (ln == NULL) {
+ log_debug("%s: cannot find lde neighbor", __func__);
+ break;
+ }
+
+ switch (nm->status_code) {
+ case S_PW_STATUS:
+ l2vpn_recv_pw_status(ln, nm);
+ break;
+ case S_ENDOFLIB:
+ /*
+ * Do nothing for now. Should be useful in
+ * the future when we implement LDP-IGP
+ * Synchronization (RFC 5443) and Graceful
+ * Restart (RFC 3478).
+ */
+ default:
+ break;
+ }
+ break;
+ case IMSG_NEIGHBOR_UP:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_nbr))
+ fatalx("lde_dispatch_imsg: wrong imsg len");
+
+ if (lde_nbr_find(imsg.hdr.peerid))
+ fatalx("lde_dispatch_imsg: neighbor already exists");
+ lde_nbr_new(imsg.hdr.peerid, imsg.data);
+ break;
+ case IMSG_NEIGHBOR_DOWN:
+ lde_nbr_del(lde_nbr_find(imsg.hdr.peerid));
+ break;
+ case IMSG_CTL_SHOW_LIB:
+ rt_dump(imsg.hdr.pid);
+
+ lde_imsg_compose_ldpe(IMSG_CTL_END, 0,
+ imsg.hdr.pid, NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ l2vpn_pw_ctl(imsg.hdr.pid);
+
+ lde_imsg_compose_ldpe(IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ l2vpn_binding_ctl(imsg.hdr.pid);
+
+ lde_imsg_compose_ldpe(IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0);
+ break;
+ default:
+ log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handlers and exit */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ lde_shutdown();
+ }
+}
+
+/* ARGSUSED */
+static void lde_dispatch_parent(struct event *thread)
+{
+ static struct ldpd_conf *nconf;
+ struct iface *iface, *niface;
+ struct tnbr *ntnbr;
+ struct nbr_params *nnbrp;
+ static struct l2vpn *l2vpn, *nl2vpn;
+ struct l2vpn_if *lif, *nlif;
+ struct l2vpn_pw *pw, *npw;
+ struct imsg imsg;
+ struct kif *kif;
+ struct kroute *kr;
+ int fd;
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ ssize_t n;
+ int shut = 0;
+ struct fec fec;
+ struct ldp_access *laccess;
+ struct ldp_rlfa_node *rnode, *rntmp;
+ struct ldp_rlfa_client *rclient;
+ struct zapi_rlfa_request *rlfa_req;
+ struct zapi_rlfa_igp *rlfa_igp;
+
+ iev->ev_read = NULL;
+
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("lde_dispatch_parent: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_IFSTATUS:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kif))
+ fatalx("IFSTATUS imsg with wrong len");
+ kif = imsg.data;
+
+ iface = if_lookup_name(ldeconf, kif->ifname);
+ if (iface) {
+ if_update_info(iface, kif);
+
+ /* if up see if any labels need to be updated */
+ if (kif->operative)
+ lde_route_update(iface, AF_UNSPEC);
+ break;
+ }
+
+ RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree) {
+ lif = l2vpn_if_find(l2vpn, kif->ifname);
+ if (lif) {
+ l2vpn_if_update_info(lif, kif);
+ break;
+ }
+ pw = l2vpn_pw_find(l2vpn, kif->ifname);
+ if (pw) {
+ l2vpn_pw_update_info(pw, kif);
+ break;
+ }
+ }
+ break;
+ case IMSG_PW_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct zapi_pw_status))
+ fatalx("PW_UPDATE imsg with wrong len");
+
+ if (l2vpn_pw_status_update(imsg.data) != 0)
+ log_warnx("%s: error updating PW status", __func__);
+ break;
+ case IMSG_NETWORK_ADD:
+ case IMSG_NETWORK_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kroute)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ kr = imsg.data;
+
+ switch (kr->af) {
+ case AF_INET:
+ fec.type = FEC_TYPE_IPV4;
+ fec.u.ipv4.prefix = kr->prefix.v4;
+ fec.u.ipv4.prefixlen = kr->prefixlen;
+ break;
+ case AF_INET6:
+ fec.type = FEC_TYPE_IPV6;
+ fec.u.ipv6.prefix = kr->prefix.v6;
+ fec.u.ipv6.prefixlen = kr->prefixlen;
+ break;
+ default:
+ fatalx("lde_dispatch_parent: unknown af");
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_NETWORK_ADD:
+ lde_kernel_insert(&fec, kr->af, &kr->nexthop,
+ kr->ifindex, kr->route_type, kr->route_instance,
+ CHECK_FLAG(kr->flags, F_CONNECTED), NULL);
+ break;
+ case IMSG_NETWORK_UPDATE:
+ lde_kernel_update(&fec);
+ break;
+ }
+ break;
+ case IMSG_SOCKET_IPC:
+ if (iev_ldpe) {
+ log_warnx("%s: received unexpected imsg fd to ldpe", __func__);
+ break;
+ }
+ if ((fd = imsg.fd) == -1) {
+ log_warnx("%s: expected to receive imsg fd to ldpe but didn't receive any", __func__);
+ break;
+ }
+
+ if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_ldpe->ibuf, fd);
+ iev_ldpe->handler_read = lde_dispatch_imsg;
+ event_add_read(master, iev_ldpe->handler_read, iev_ldpe,
+ iev_ldpe->ibuf.fd, &iev_ldpe->ev_read);
+ iev_ldpe->handler_write = ldp_write_handler;
+ iev_ldpe->ev_write = NULL;
+ break;
+ case IMSG_INIT:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ldpd_init))
+ fatalx("INIT imsg with wrong len");
+
+ memcpy(&init, imsg.data, sizeof(init));
+ lde_init(&init);
+ break;
+ case IMSG_AGENTX_ENABLED:
+ ldp_agentx_enabled();
+ break;
+ case IMSG_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct ldpd_conf));
+
+ RB_INIT(iface_head, &nconf->iface_tree);
+ RB_INIT(tnbr_head, &nconf->tnbr_tree);
+ RB_INIT(nbrp_head, &nconf->nbrp_tree);
+ RB_INIT(l2vpn_head, &nconf->l2vpn_tree);
+ break;
+ case IMSG_RECONF_IFACE:
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ RB_INSERT(iface_head, &nconf->iface_tree, niface);
+ break;
+ case IMSG_RECONF_TNBR:
+ if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL)
+ fatal(NULL);
+ memcpy(ntnbr, imsg.data, sizeof(struct tnbr));
+
+ RB_INSERT(tnbr_head, &nconf->tnbr_tree, ntnbr);
+ break;
+ case IMSG_RECONF_NBRP:
+ if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL)
+ fatal(NULL);
+ memcpy(nnbrp, imsg.data, sizeof(struct nbr_params));
+
+ RB_INSERT(nbrp_head, &nconf->nbrp_tree, nnbrp);
+ break;
+ case IMSG_RECONF_L2VPN:
+ if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL)
+ fatal(NULL);
+ memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn));
+
+ RB_INIT(l2vpn_if_head, &nl2vpn->if_tree);
+ RB_INIT(l2vpn_pw_head, &nl2vpn->pw_tree);
+ RB_INIT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree);
+
+ RB_INSERT(l2vpn_head, &nconf->l2vpn_tree, nl2vpn);
+ break;
+ case IMSG_RECONF_L2VPN_IF:
+ if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL)
+ fatal(NULL);
+ memcpy(nlif, imsg.data, sizeof(struct l2vpn_if));
+
+ RB_INSERT(l2vpn_if_head, &nl2vpn->if_tree, nlif);
+ break;
+ case IMSG_RECONF_L2VPN_PW:
+ if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL)
+ fatal(NULL);
+ memcpy(npw, imsg.data, sizeof(struct l2vpn_pw));
+
+ RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_tree, npw);
+ break;
+ case IMSG_RECONF_L2VPN_IPW:
+ if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL)
+ fatal(NULL);
+ memcpy(npw, imsg.data, sizeof(struct l2vpn_pw));
+
+ RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree, npw);
+ break;
+ case IMSG_RECONF_END:
+ merge_config(ldeconf, nconf);
+ ldp_clear_config(nconf);
+ nconf = NULL;
+ break;
+ case IMSG_DEBUG_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(ldp_debug)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug));
+ break;
+ case IMSG_FILTER_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ldp_access)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ laccess = imsg.data;
+ lde_check_filter_af(AF_INET, &ldeconf->ipv4,
+ laccess->name);
+ lde_check_filter_af(AF_INET6, &ldeconf->ipv6,
+ laccess->name);
+ break;
+ case IMSG_RLFA_REG:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_request)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_req = imsg.data;
+ rnode = rlfa_node_find(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ if (!rnode)
+ rnode = rlfa_node_new(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ rclient = rlfa_client_find(rnode, &rlfa_req->igp);
+ if (rclient)
+ /* RLFA already registered - do nothing */
+ break;
+ rclient = rlfa_client_new(rnode, &rlfa_req->igp);
+ lde_rlfa_check(rclient);
+ break;
+ case IMSG_RLFA_UNREG_ALL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_igp)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_igp = imsg.data;
+
+ RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head,
+ &rlfa_node_tree, rntmp) {
+ rclient = rlfa_client_find(rnode, rlfa_igp);
+ if (!rclient)
+ continue;
+
+ rlfa_client_del(rclient);
+ }
+ break;
+ default:
+ log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handlers and exit */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ lde_shutdown();
+ }
+}
+
+int
+lde_acl_check(char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen)
+{
+ return ldp_acl_request(iev_main_sync, acl_name, af, addr, prefixlen);
+}
+
+static bool lde_fec_connected(const struct fec_node *fn)
+{
+ struct fec_nh *fnh;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry)
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED))
+ return true;
+
+ return false;
+}
+
+static bool lde_fec_outside_mpls_network(const struct fec_node *fn)
+{
+ struct fec_nh *fnh;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry)
+ if (!CHECK_FLAG(fnh->flags, F_FEC_NH_NO_LDP))
+ return false;
+
+ return true;
+}
+
+uint32_t
+lde_update_label(struct fec_node *fn)
+{
+
+ /* should we allocate a label for this fec? */
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (CHECK_FLAG(ldeconf->ipv4.flags, F_LDPD_AF_ALLOCHOSTONLY)
+ && fn->fec.u.ipv4.prefixlen != IPV4_MAX_BITLEN)
+ return (NO_LABEL);
+
+ if (lde_acl_check(ldeconf->ipv4.acl_label_allocate_for,
+ AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
+ fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
+ return (NO_LABEL);
+ break;
+ case FEC_TYPE_IPV6:
+ if (CHECK_FLAG(ldeconf->ipv6.flags, F_LDPD_AF_ALLOCHOSTONLY)
+ && fn->fec.u.ipv6.prefixlen != IPV6_MAX_BITLEN)
+ return (NO_LABEL);
+
+ if (lde_acl_check(ldeconf->ipv6.acl_label_allocate_for,
+ AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
+ fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
+ return (NO_LABEL);
+ break;
+ case FEC_TYPE_PWID:
+ break;
+ }
+
+ /*
+ * If connected interface act as egress for fec.
+ * If LDP is not configured on an interface but there
+ * are other NHs with interfaces configured with LDP
+ * then don't act as an egress for the fec, otherwise
+ * act as an egress for the fec
+ */
+ if (lde_fec_connected(fn) || lde_fec_outside_mpls_network(fn)) {
+ /* choose implicit or explicit-null depending on configuration */
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!CHECK_FLAG(ldeconf->ipv4.flags, F_LDPD_AF_EXPNULL))
+ return (MPLS_LABEL_IMPLICIT_NULL);
+
+ if (lde_acl_check(ldeconf->ipv4.acl_label_expnull_for,
+ AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
+ fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
+ return (MPLS_LABEL_IMPLICIT_NULL);
+ return MPLS_LABEL_IPV4_EXPLICIT_NULL;
+ case FEC_TYPE_IPV6:
+ if (!CHECK_FLAG(ldeconf->ipv6.flags, F_LDPD_AF_EXPNULL))
+ return (MPLS_LABEL_IMPLICIT_NULL);
+
+ if (lde_acl_check(ldeconf->ipv6.acl_label_expnull_for,
+ AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
+ fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
+ return (MPLS_LABEL_IMPLICIT_NULL);
+ return MPLS_LABEL_IPV6_EXPLICIT_NULL;
+ case FEC_TYPE_PWID:
+ break;
+ }
+ }
+
+ /* preserve current label if there's no need to update it */
+ if (fn->local_label != NO_LABEL &&
+ fn->local_label > MPLS_LABEL_RESERVED_MAX)
+ return (fn->local_label);
+
+ return (lde_get_next_label());
+}
+
+void
+lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh)
+{
+ struct kroute kr;
+ struct zapi_pw zpw;
+ struct l2vpn_pw *pw;
+
+ /*
+ * Ordered Control: don't program label into HW until a
+ * labelmap msg has been received from upstream router
+ */
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_DEFER))
+ return;
+
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ memset(&kr, 0, sizeof(kr));
+ kr.af = AF_INET;
+ kr.prefix.v4 = fn->fec.u.ipv4.prefix;
+ kr.prefixlen = fn->fec.u.ipv4.prefixlen;
+ kr.nexthop.v4 = fnh->nexthop.v4;
+ kr.ifindex = fnh->ifindex;
+ kr.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.route_type = fnh->route_type;
+ kr.route_instance = fnh->route_instance;
+ lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr));
+ break;
+ case FEC_TYPE_IPV6:
+ memset(&kr, 0, sizeof(kr));
+ kr.af = AF_INET6;
+ kr.prefix.v6 = fn->fec.u.ipv6.prefix;
+ kr.prefixlen = fn->fec.u.ipv6.prefixlen;
+ kr.nexthop.v6 = fnh->nexthop.v6;
+ kr.ifindex = fnh->ifindex;
+ kr.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.route_type = fnh->route_type;
+ kr.route_instance = fnh->route_instance;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr));
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (!pw || fn->local_label == NO_LABEL ||
+ fnh->remote_label == NO_LABEL)
+ return;
+
+ pw->enabled = true;
+ pw2zpw(pw, &zpw);
+ zpw.local_label = fn->local_label;
+ zpw.remote_label = fnh->remote_label;
+ lde_imsg_compose_parent(IMSG_KPW_SET, 0, &zpw, sizeof(zpw));
+ break;
+ }
+}
+
+void
+lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh)
+{
+ struct kroute kr;
+ struct zapi_pw zpw;
+ struct l2vpn_pw *pw;
+
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ memset(&kr, 0, sizeof(kr));
+ kr.af = AF_INET;
+ kr.prefix.v4 = fn->fec.u.ipv4.prefix;
+ kr.prefixlen = fn->fec.u.ipv4.prefixlen;
+ kr.nexthop.v4 = fnh->nexthop.v4;
+ kr.ifindex = fnh->ifindex;
+ kr.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.route_type = fnh->route_type;
+ kr.route_instance = fnh->route_instance;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr));
+ break;
+ case FEC_TYPE_IPV6:
+ memset(&kr, 0, sizeof(kr));
+ kr.af = AF_INET6;
+ kr.prefix.v6 = fn->fec.u.ipv6.prefix;
+ kr.prefixlen = fn->fec.u.ipv6.prefixlen;
+ kr.nexthop.v6 = fnh->nexthop.v6;
+ kr.ifindex = fnh->ifindex;
+ kr.local_label = fn->local_label;
+ kr.remote_label = fnh->remote_label;
+ kr.route_type = fnh->route_type;
+ kr.route_instance = fnh->route_instance;
+
+ lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr));
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (!pw)
+ return;
+
+ pw->enabled = false;
+ pw2zpw(pw, &zpw);
+ zpw.local_label = fn->local_label;
+ zpw.remote_label = fnh->remote_label;
+ lde_imsg_compose_parent(IMSG_KPW_UNSET, 0, &zpw, sizeof(zpw));
+ break;
+ }
+}
+
+void
+lde_fec2prefix(const struct fec *fec, struct prefix *prefix)
+{
+ memset(prefix, 0, sizeof(*prefix));
+ switch (fec->type) {
+ case FEC_TYPE_IPV4:
+ prefix->family = AF_INET;
+ prefix->u.prefix4 = fec->u.ipv4.prefix;
+ prefix->prefixlen = fec->u.ipv4.prefixlen;
+ break;
+ case FEC_TYPE_IPV6:
+ prefix->family = AF_INET6;
+ prefix->u.prefix6 = fec->u.ipv6.prefix;
+ prefix->prefixlen = fec->u.ipv6.prefixlen;
+ break;
+ case FEC_TYPE_PWID:
+ prefix->family = AF_UNSPEC;
+ break;
+ }
+}
+
+void
+lde_prefix2fec(const struct prefix *prefix, struct fec *fec)
+{
+ memset(fec, 0, sizeof(*fec));
+ switch (prefix->family) {
+ case AF_INET:
+ fec->type = FEC_TYPE_IPV4;
+ fec->u.ipv4.prefix = prefix->u.prefix4;
+ fec->u.ipv4.prefixlen = prefix->prefixlen;
+ break;
+ case AF_INET6:
+ fec->type = FEC_TYPE_IPV6;
+ fec->u.ipv6.prefix = prefix->u.prefix6;
+ fec->u.ipv6.prefixlen = prefix->prefixlen;
+ break;
+ default:
+ fatalx("lde_prefix2fec: unknown af");
+ break;
+ }
+}
+
+void
+lde_fec2map(struct fec *fec, struct map *map)
+{
+ memset(map, 0, sizeof(*map));
+
+ switch (fec->type) {
+ case FEC_TYPE_IPV4:
+ map->type = MAP_TYPE_PREFIX;
+ map->fec.prefix.af = AF_INET;
+ map->fec.prefix.prefix.v4 = fec->u.ipv4.prefix;
+ map->fec.prefix.prefixlen = fec->u.ipv4.prefixlen;
+ break;
+ case FEC_TYPE_IPV6:
+ map->type = MAP_TYPE_PREFIX;
+ map->fec.prefix.af = AF_INET6;
+ map->fec.prefix.prefix.v6 = fec->u.ipv6.prefix;
+ map->fec.prefix.prefixlen = fec->u.ipv6.prefixlen;
+ break;
+ case FEC_TYPE_PWID:
+ map->type = MAP_TYPE_PWID;
+ map->fec.pwid.type = fec->u.pwid.type;
+ map->fec.pwid.group_id = 0;
+ SET_FLAG(map->flags, F_MAP_PW_ID);
+ map->fec.pwid.pwid = fec->u.pwid.pwid;
+ break;
+ }
+}
+
+void
+lde_map2fec(struct map *map, struct in_addr lsr_id, struct fec *fec)
+{
+ memset(fec, 0, sizeof(*fec));
+
+ switch (map->type) {
+ case MAP_TYPE_PREFIX:
+ switch (map->fec.prefix.af) {
+ case AF_INET:
+ fec->type = FEC_TYPE_IPV4;
+ fec->u.ipv4.prefix = map->fec.prefix.prefix.v4;
+ fec->u.ipv4.prefixlen = map->fec.prefix.prefixlen;
+ break;
+ case AF_INET6:
+ fec->type = FEC_TYPE_IPV6;
+ fec->u.ipv6.prefix = map->fec.prefix.prefix.v6;
+ fec->u.ipv6.prefixlen = map->fec.prefix.prefixlen;
+ break;
+ default:
+ fatalx("lde_map2fec: unknown af");
+ break;
+ }
+ break;
+ case MAP_TYPE_PWID:
+ fec->type = FEC_TYPE_PWID;
+ fec->u.pwid.type = map->fec.pwid.type;
+ fec->u.pwid.pwid = map->fec.pwid.pwid;
+ fec->u.pwid.lsr_id = lsr_id;
+ break;
+ }
+}
+
+void
+lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single)
+{
+ struct lde_wdraw *lw;
+ struct lde_map *me;
+ struct lde_req *lre;
+ struct map map;
+ struct l2vpn_pw *pw;
+ struct fec_nh *fnh;
+ bool allow = false;
+
+ /*
+ * Ordered Control: do not send a labelmap msg until
+ * a labelmap message is received from downstream router
+ * and don't send labelmap back to downstream router
+ */
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_DEFER))
+ continue;
+
+ if (lde_address_find(ln, fnh->af, &fnh->nexthop))
+ return;
+ allow = true;
+ break;
+ }
+ if (!allow)
+ return;
+ }
+
+ /*
+ * We shouldn't send a new label mapping if we have a pending
+ * label release to receive. In this case, schedule to send a
+ * label mapping as soon as a label release is received.
+ */
+ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
+ if (lw) {
+ if (!fec_find(&ln->sent_map_pending, &fn->fec)) {
+ debug_evt("%s: FEC %s: scheduling to send label mapping later (waiting for pending label release)",
+ __func__, log_fec(&fn->fec));
+ lde_map_pending_add(ln, fn);
+ }
+ return;
+ }
+
+ /*
+ * This function skips SL.1 - 3 and SL.9 - 14 because the label
+ * allocation is done way earlier (because of the merging nature of
+ * ldpd).
+ */
+
+ lde_fec2map(&fn->fec, &map);
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!ln->v4_enabled)
+ return;
+
+ if (lde_acl_check(ldeconf->ipv4.acl_label_advertise_to,
+ AF_INET, (union ldpd_addr *)&ln->id, 32) != FILTER_PERMIT)
+ return;
+
+ if (lde_acl_check(ldeconf->ipv4.acl_label_advertise_for,
+ AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
+ fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (!ln->v6_enabled)
+ return;
+
+ if (lde_acl_check(ldeconf->ipv6.acl_label_advertise_to,
+ AF_INET, (union ldpd_addr *)&ln->id, 32) != FILTER_PERMIT)
+ return;
+
+ if (lde_acl_check(ldeconf->ipv6.acl_label_advertise_for,
+ AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
+ fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
+ /* not the remote end of the pseudowire */
+ return;
+
+ SET_FLAG(map.flags, F_MAP_PW_IFMTU);
+ map.fec.pwid.ifmtu = pw->l2vpn->mtu;
+
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD))
+ SET_FLAG(map.flags, F_MAP_PW_CWORD);
+
+ if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV)) {
+ SET_FLAG(map.flags, F_MAP_PW_STATUS);
+ map.pw_status = pw->local_status;
+ }
+ break;
+ }
+ map.label = fn->local_label;
+
+ /* SL.6: is there a pending request for this mapping? */
+ lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec);
+ if (lre) {
+ /* set label request msg id in the mapping response. */
+ map.requestid = lre->msg_id;
+ map.flags = F_MAP_REQ_ID;
+
+ /* SL.7: delete record of pending request */
+ lde_req_del(ln, lre, 0);
+ }
+
+ /* SL.4: send label mapping */
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD, ln->peerid, 0,
+ &map, sizeof(map));
+ if (single)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0,
+ NULL, 0);
+
+ /* SL.5: record sent label mapping */
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (me == NULL)
+ me = lde_map_add(ln, fn, 1);
+ me->map = map;
+}
+
+void
+lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn,
+ struct map *wcard, struct status_tlv *st)
+{
+ struct lde_wdraw *lw;
+ struct map map;
+ struct fec *f;
+ struct l2vpn_pw *pw;
+
+ if (fn) {
+ lde_fec2map(&fn->fec, &map);
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!ln->v4_enabled)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (!ln->v6_enabled)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
+ /* not the remote end of the pseudowire */
+ return;
+
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD))
+ SET_FLAG(map.flags, F_MAP_PW_CWORD);
+ break;
+ }
+ map.label = fn->local_label;
+ } else
+ memcpy(&map, wcard, sizeof(map));
+
+ if (st) {
+ map.st.status_code = st->status_code;
+ map.st.msg_id = st->msg_id;
+ map.st.msg_type = st->msg_type;
+ SET_FLAG(map.flags, F_MAP_STATUS);
+ }
+
+ /* SWd.1: send label withdraw. */
+ lde_imsg_compose_ldpe(IMSG_WITHDRAW_ADD, ln->peerid, 0,
+ &map, sizeof(map));
+ lde_imsg_compose_ldpe(IMSG_WITHDRAW_ADD_END, ln->peerid, 0, NULL, 0);
+
+ /* SWd.2: record label withdraw. */
+ if (fn) {
+ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
+ if (lw == NULL)
+ lw = lde_wdraw_add(ln, fn);
+ lw->label = map.label;
+ } else {
+ struct lde_map *me;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (lde_wildcard_apply(wcard, &fn->fec, me) == 0)
+ continue;
+
+ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
+ if (lw == NULL)
+ lw = lde_wdraw_add(ln, fn);
+
+ lw->label = map.label;
+ }
+ }
+}
+
+void
+lde_send_labelwithdraw_wcard(struct lde_nbr *ln, uint32_t label)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_WILDCARD;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelwithdraw_twcard_prefix(struct lde_nbr *ln, uint16_t af,
+ uint32_t label)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_TYPED_WCARD;
+ wcard.fec.twcard.type = MAP_TYPE_PREFIX;
+ wcard.fec.twcard.u.prefix_af = af;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelwithdraw_twcard_pwid(struct lde_nbr *ln, uint16_t pw_type,
+ uint32_t label)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_TYPED_WCARD;
+ wcard.fec.twcard.type = MAP_TYPE_PWID;
+ wcard.fec.twcard.u.pw_type = pw_type;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *ln, uint16_t pw_type,
+ uint32_t group_id)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_PWID;
+ wcard.fec.pwid.type = pw_type;
+ wcard.fec.pwid.group_id = group_id;
+ /* we can not append a Label TLV when using PWid group wildcards. */
+ wcard.label = NO_LABEL;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn,
+ struct map *wcard, uint32_t label)
+{
+ struct map map;
+ struct l2vpn_pw *pw;
+
+ if (fn) {
+ lde_fec2map(&fn->fec, &map);
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!ln->v4_enabled)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (!ln->v6_enabled)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
+ /* not the remote end of the pseudowire */
+ return;
+
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD))
+ SET_FLAG(map.flags, F_MAP_PW_CWORD);
+ break;
+ }
+ } else
+ memcpy(&map, wcard, sizeof(map));
+ map.label = label;
+
+ lde_imsg_compose_ldpe(IMSG_RELEASE_ADD, ln->peerid, 0,
+ &map, sizeof(map));
+ lde_imsg_compose_ldpe(IMSG_RELEASE_ADD_END, ln->peerid, 0, NULL, 0);
+}
+
+void
+lde_send_labelrequest(struct lde_nbr *ln, struct fec_node *fn,
+ struct map *wcard, int single)
+{
+ struct map map;
+ struct fec *f;
+ struct lde_req *lre;
+
+ if (fn) {
+ lde_fec2map(&fn->fec, &map);
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ if (!ln->v4_enabled)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (!ln->v6_enabled)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ fatalx("lde_send_labelrequest: unknown af");
+ }
+ } else
+ memcpy(&map, wcard, sizeof(map));
+
+ map.label = NO_LABEL;
+
+ if (fn) {
+ /* SLR1.1: has label request for FEC been previously sent
+ * and still outstanding just return,
+ */
+ lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec);
+ if (lre == NULL) {
+ /* SLRq.3: send label request */
+ lde_imsg_compose_ldpe(IMSG_REQUEST_ADD, ln->peerid, 0,
+ &map, sizeof(map));
+ if (single)
+ lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END,
+ ln->peerid, 0, NULL, 0);
+
+ /* SLRq.4: record sent request */
+ lde_req_add(ln, &fn->fec, 1);
+ }
+ } else {
+ /* if Wilcard just send label request */
+ /* SLRq.3: send label request */
+ lde_imsg_compose_ldpe(IMSG_REQUEST_ADD,
+ ln->peerid, 0, &map, sizeof(map));
+ if (single)
+ lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END, ln->peerid, 0, NULL, 0);
+
+ /* SLRq.4: record sent request */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec);
+ if (lde_wildcard_apply(wcard, &fn->fec, NULL) == 0)
+ continue;
+ if (lre == NULL)
+ lde_req_add(ln, f, 1);
+ }
+ }
+}
+
+void
+lde_send_labelrequest_wcard(struct lde_nbr *ln, uint16_t af)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_TYPED_WCARD;
+ wcard.fec.twcard.type = MAP_TYPE_PREFIX;
+ wcard.fec.twcard.u.prefix_af = af;
+ lde_send_labelrequest(ln, NULL, &wcard, 1);
+}
+
+void
+lde_send_notification(struct lde_nbr *ln, uint32_t status_code, uint32_t msg_id,
+ uint16_t msg_type)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = status_code;
+ /* 'msg_id' and 'msg_type' should be in network byte order */
+ nm.msg_id = msg_id;
+ nm.msg_type = msg_type;
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
+ &nm, sizeof(nm));
+}
+
+void
+lde_send_notification_eol_prefix(struct lde_nbr *ln, int af)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_ENDOFLIB;
+ nm.fec.type = MAP_TYPE_TYPED_WCARD;
+ nm.fec.fec.twcard.type = MAP_TYPE_PREFIX;
+ nm.fec.fec.twcard.u.prefix_af = af;
+ SET_FLAG(nm.flags, F_NOTIF_FEC);
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
+ &nm, sizeof(nm));
+}
+
+void
+lde_send_notification_eol_pwid(struct lde_nbr *ln, uint16_t pw_type)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_ENDOFLIB;
+ nm.fec.type = MAP_TYPE_TYPED_WCARD;
+ nm.fec.fec.twcard.type = MAP_TYPE_PWID;
+ nm.fec.fec.twcard.u.pw_type = pw_type;
+ SET_FLAG(nm.flags, F_NOTIF_FEC);
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
+ &nm, sizeof(nm));
+}
+
+static __inline int
+lde_nbr_compare(const struct lde_nbr *a, const struct lde_nbr *b)
+{
+ return (a->peerid - b->peerid);
+}
+
+static struct lde_nbr *
+lde_nbr_new(uint32_t peerid, struct lde_nbr *new)
+{
+ struct lde_nbr *ln;
+
+ if ((ln = calloc(1, sizeof(*ln))) == NULL)
+ fatal(__func__);
+
+ ln->id = new->id;
+ ln->v4_enabled = new->v4_enabled;
+ ln->v6_enabled = new->v6_enabled;
+ ln->flags = new->flags;
+ ln->peerid = peerid;
+ fec_init(&ln->recv_map);
+ fec_init(&ln->sent_map);
+ fec_init(&ln->sent_map_pending);
+ fec_init(&ln->recv_req);
+ fec_init(&ln->sent_req);
+ fec_init(&ln->sent_wdraw);
+
+ TAILQ_INIT(&ln->addr_list);
+
+ if (RB_INSERT(nbr_tree, &lde_nbrs, ln) != NULL)
+ fatalx("lde_nbr_new: RB_INSERT failed");
+
+ return (ln);
+}
+
+static void
+lde_nbr_del(struct lde_nbr *ln)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct l2vpn_pw *pw;
+ struct lde_nbr *lnbr;
+
+ if (ln == NULL)
+ return;
+
+ /* uninstall received mappings */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ switch (f->type) {
+ case FEC_TYPE_IPV4:
+ case FEC_TYPE_IPV6:
+ if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
+ continue;
+
+ /*
+ * Ordered Control: must mark any non-connected
+ * NH to wait until we receive a labelmap msg
+ * before installing in kernel and sending to
+ * peer, must do this as NHs are not removed
+ * when lsps go down. Also send label withdraw
+ * to other neighbors for all fecs from neighbor
+ * going down
+ */
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
+ SET_FLAG(fnh->flags, F_FEC_NH_DEFER);
+
+ RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) {
+ if (ln->peerid == lnbr->peerid)
+ continue;
+ lde_send_labelwithdraw(lnbr, fn, NULL, NULL);
+ }
+ }
+ break;
+ case FEC_TYPE_PWID:
+ if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr)
+ continue;
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw) {
+ pw->reason = F_PW_NO_REMOTE_LABEL;
+ l2vpn_pw_reset(pw);
+ }
+ break;
+ default:
+ break;
+ }
+
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ }
+ }
+
+ lde_address_list_free(ln);
+
+ fec_clear(&ln->recv_map, lde_map_free);
+ fec_clear(&ln->sent_map, lde_map_free);
+ fec_clear(&ln->sent_map_pending, free);
+ fec_clear(&ln->recv_req, free);
+ fec_clear(&ln->sent_req, free);
+ fec_clear(&ln->sent_wdraw, free);
+
+ RB_REMOVE(nbr_tree, &lde_nbrs, ln);
+
+ free(ln);
+}
+
+static struct lde_nbr *
+lde_nbr_find(uint32_t peerid)
+{
+ struct lde_nbr ln;
+
+ ln.peerid = peerid;
+
+ return (RB_FIND(nbr_tree, &lde_nbrs, &ln));
+}
+
+struct lde_nbr *
+lde_nbr_find_by_lsrid(struct in_addr addr)
+{
+ struct lde_nbr *ln;
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ if (ln->id.s_addr == addr.s_addr)
+ return (ln);
+
+ return (NULL);
+}
+
+struct lde_nbr *
+lde_nbr_find_by_addr(int af, union ldpd_addr *addr)
+{
+ struct lde_nbr *ln;
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ if (lde_address_find(ln, af, addr) != NULL)
+ return (ln);
+
+ return (NULL);
+}
+
+static void
+lde_nbr_clear(void)
+{
+ struct lde_nbr *ln;
+
+ while (!RB_EMPTY(nbr_tree, &lde_nbrs)) {
+ ln = RB_ROOT(nbr_tree, &lde_nbrs);
+
+ lde_nbr_del(ln);
+ }
+}
+
+static void
+lde_nbr_addr_update(struct lde_nbr *ln, struct lde_addr *lde_addr, int removed)
+{
+ struct fec *fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_map *me;
+
+ RB_FOREACH(fec, fec_tree, &ln->recv_map) {
+ switch (fec->type) {
+ case FEC_TYPE_IPV4:
+ if (lde_addr->af != AF_INET)
+ continue;
+ break;
+ case FEC_TYPE_IPV6:
+ if (lde_addr->af != AF_INET6)
+ continue;
+ break;
+ case FEC_TYPE_PWID:
+ continue;
+ }
+
+ fn = (struct fec_node *)fec_find(&ft, fec);
+ if (fn == NULL)
+ /* shouldn't happen */
+ continue;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ if (ldp_addrcmp(fnh->af, &fnh->nexthop, &lde_addr->addr))
+ continue;
+
+ if (removed) {
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ } else {
+ me = (struct lde_map *)fec;
+ fnh->remote_label = me->map.label;
+ lde_send_change_klabel(fn, fnh);
+ }
+ break;
+ }
+ }
+}
+
+void
+lde_allow_broken_lsp_update(int new_config)
+{
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct fec *f;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ /* allow-broken-lsp config is changing so
+ * we need to reprogram labeled routes to
+ * have proper top-level label
+ */
+ if (!(new_config & F_LDPD_ALLOW_BROKEN_LSP))
+ lde_send_delete_klabel(fn, fnh);
+
+ if (fn->local_label != NO_LABEL)
+ lde_send_change_klabel(fn, fnh);
+ }
+ }
+}
+
+static __inline int
+lde_map_compare(const struct lde_map *a, const struct lde_map *b)
+{
+ return (ldp_addrcmp(AF_INET, (union ldpd_addr *)&a->nexthop->id,
+ (union ldpd_addr *)&b->nexthop->id));
+}
+
+struct lde_map *
+lde_map_add(struct lde_nbr *ln, struct fec_node *fn, int sent)
+{
+ struct lde_map *me;
+
+ me = calloc(1, sizeof(*me));
+ if (me == NULL)
+ fatal(__func__);
+
+ me->fec = fn->fec;
+ me->nexthop = ln;
+
+ if (sent) {
+ RB_INSERT(lde_map_head, &fn->upstream, me);
+ me->head = &fn->upstream;
+ if (fec_insert(&ln->sent_map, &me->fec))
+ log_warnx("failed to add %s to sent map", log_fec(&me->fec));
+ /* XXX on failure more cleanup is needed */
+ } else {
+ RB_INSERT(lde_map_head, &fn->downstream, me);
+ me->head = &fn->downstream;
+ if (fec_insert(&ln->recv_map, &me->fec))
+ log_warnx("failed to add %s to recv map", log_fec(&me->fec));
+ }
+
+ return (me);
+}
+
+void
+lde_map_del(struct lde_nbr *ln, struct lde_map *me, int sent)
+{
+ if (sent)
+ fec_remove(&ln->sent_map, &me->fec);
+ else
+ fec_remove(&ln->recv_map, &me->fec);
+
+ lde_map_free(me);
+}
+
+static void
+lde_map_free(void *ptr)
+{
+ struct lde_map *map = ptr;
+
+ RB_REMOVE(lde_map_head, map->head, map);
+ free(map);
+}
+
+struct fec *
+lde_map_pending_add(struct lde_nbr *ln, struct fec_node *fn)
+{
+ struct fec *map;
+
+ map = calloc(1, sizeof(*map));
+ if (map == NULL)
+ fatal(__func__);
+
+ *map = fn->fec;
+ if (fec_insert(&ln->sent_map_pending, map))
+ log_warnx("failed to add %s to sent map (pending)", log_fec(map));
+
+ return (map);
+}
+
+void
+lde_map_pending_del(struct lde_nbr *ln, struct fec *map)
+{
+ fec_remove(&ln->sent_map_pending, map);
+ free(map);
+}
+
+struct lde_req *
+lde_req_add(struct lde_nbr *ln, struct fec *fec, int sent)
+{
+ struct fec_tree *t;
+ struct lde_req *lre;
+
+ t = sent ? &ln->sent_req : &ln->recv_req;
+
+ lre = calloc(1, sizeof(*lre));
+ if (lre != NULL) {
+ lre->fec = *fec;
+
+ if (fec_insert(t, &lre->fec)) {
+ log_warnx("failed to add %s to %s req",
+ log_fec(&lre->fec), sent ? "sent" : "recv");
+ free(lre);
+ return (NULL);
+ }
+ }
+
+ return (lre);
+}
+
+void
+lde_req_del(struct lde_nbr *ln, struct lde_req *lre, int sent)
+{
+ if (sent)
+ fec_remove(&ln->sent_req, &lre->fec);
+ else
+ fec_remove(&ln->recv_req, &lre->fec);
+
+ free(lre);
+}
+
+struct lde_wdraw *
+lde_wdraw_add(struct lde_nbr *ln, struct fec_node *fn)
+{
+ struct lde_wdraw *lw;
+
+ lw = calloc(1, sizeof(*lw));
+ if (lw == NULL)
+ fatal(__func__);
+
+ lw->fec = fn->fec;
+
+ if (fec_insert(&ln->sent_wdraw, &lw->fec))
+ log_warnx("failed to add %s to sent wdraw", log_fec(&lw->fec));
+
+ return (lw);
+}
+
+void
+lde_wdraw_del(struct lde_nbr *ln, struct lde_wdraw *lw)
+{
+ fec_remove(&ln->sent_wdraw, &lw->fec);
+ free(lw);
+}
+
+void
+lde_change_egress_label(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+
+ /* explicitly withdraw all null labels */
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IMPLICIT_NULL);
+ if (ln->v4_enabled)
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV4_EXPLICIT_NULL);
+ if (ln->v6_enabled)
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV6_EXPLICIT_NULL);
+ }
+
+ /* update label of connected routes */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (fn->local_label > MPLS_LABEL_RESERVED_MAX)
+ continue;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ fatalx("lde_change_egress_label: unknown af");
+ }
+
+ fn->local_label = lde_update_label(fn);
+ if (fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0);
+}
+
+void
+lde_change_allocate_filter(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ uint32_t new_label;
+
+ /* reallocate labels for fecs that match this filter */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ fatalx("lde_change_allocate_filter: unknown af");
+ }
+
+ /*
+ * If the local label has changed to NO_LABEL, send a label
+ * withdraw to all peers.
+ * If the local label has changed and it's different from
+ * NO_LABEL, send a label mapping to all peers advertising
+ * the new label.
+ * If the local label hasn't changed, do nothing
+ */
+ new_label = lde_update_label(fn);
+ if (fn->local_label != new_label) {
+ if (new_label == NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+
+ fn->local_label = new_label;
+ if (fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0,
+ NULL, 0);
+}
+
+void
+lde_change_advertise_filter(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ char *acl_to_filter;
+ char *acl_for_filter;
+ union ldpd_addr *prefix;
+ uint8_t plen;
+ struct lde_map *me;
+
+ /* advertise label for fecs to neighbors if matches advertise filters */
+ switch (af) {
+ case AF_INET:
+ acl_to_filter = ldeconf->ipv4.acl_label_advertise_to;
+ acl_for_filter = ldeconf->ipv4.acl_label_advertise_for;
+ break;
+ case AF_INET6:
+ acl_to_filter = ldeconf->ipv6.acl_label_advertise_to;
+ acl_for_filter = ldeconf->ipv6.acl_label_advertise_for;
+ break;
+ default:
+ fatalx("lde_change_advertise_filter: unknown af");
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
+ if (lde_acl_check(acl_to_filter, af, (union ldpd_addr *)&ln->id,
+ IPV4_MAX_BITLEN) != FILTER_PERMIT)
+ lde_send_labelwithdraw_wcard(ln, NO_LABEL);
+ else {
+ /* This neighbor is allowed in to_filter, so
+ * send labels if fec also matches for_filter
+ */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix;
+ plen = fn->fec.u.ipv4.prefixlen;
+ break;
+ case FEC_TYPE_IPV6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix;
+ plen = fn->fec.u.ipv6.prefixlen;
+ break;
+ default:
+ continue;
+ }
+
+ if (lde_acl_check(acl_for_filter, af,
+ prefix, plen) != FILTER_PERMIT) {
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (me)
+ /* fec filtered withdraw */
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+ } else
+ /* fec allowed send map */
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0);
+ }
+ }
+}
+
+
+void
+lde_change_accept_filter(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ char *acl_for_filter;
+ char *acl_from_filter;
+ union ldpd_addr *prefix;
+ uint8_t plen;
+ struct lde_map *me;
+ enum fec_type type;
+
+ /* accept labels from neighbors specified in the from_filter and for
+ * fecs defined in the for_filter
+ */
+ switch (af) {
+ case AF_INET:
+ acl_for_filter = ldeconf->ipv4.acl_label_accept_for;
+ acl_from_filter = ldeconf->ipv4.acl_label_accept_from;
+ type = FEC_TYPE_IPV4;
+ break;
+ case AF_INET6:
+ acl_for_filter = ldeconf->ipv6.acl_label_accept_for;
+ acl_from_filter = ldeconf->ipv6.acl_label_accept_from;
+ type = FEC_TYPE_IPV6;
+ break;
+ default:
+ fatalx("lde_change_accept_filter: unknown af");
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
+ if (lde_acl_check(acl_from_filter, AF_INET, (union ldpd_addr *)
+ &ln->id, IPV4_MAX_BITLEN) != FILTER_PERMIT) {
+ /* This neighbor is now filtered so remove fecs from
+ * recv list
+ */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (fn->fec.type == type) {
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+ if (me)
+ lde_map_del(ln, me, 0);
+ }
+ }
+ } else if (CHECK_FLAG(ln->flags, F_NBR_CAP_TWCARD)) {
+ /* This neighbor is allowed and supports type
+ * wildcard so send a labelrequest
+ * to get any new labels from neighbor
+ * and make sure any fecs we currently have
+ * match for_filter.
+ */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix;
+ plen = fn->fec.u.ipv4.prefixlen;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix;
+ plen = fn->fec.u.ipv6.prefixlen;
+ break;
+ default:
+ continue;
+ }
+ if (lde_acl_check(acl_for_filter, af,
+ prefix, plen) != FILTER_PERMIT) {
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+ if (me)
+ lde_map_del(ln, me, 0);
+ }
+ }
+ lde_send_labelrequest_wcard(ln, af);
+ } else
+ /* Type Wildcard is not supported so restart session */
+ lde_imsg_compose_ldpe(IMSG_NBR_SHUTDOWN, ln->peerid, 0, NULL, 0);
+ }
+}
+
+void
+lde_change_expnull_for_filter(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ char *acl_name;
+ uint32_t exp_label;
+ union ldpd_addr *prefix;
+ uint8_t plen;
+
+ /* Configure explicit-null advertisement for all fecs in this filter */
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ acl_name = ldeconf->ipv4.acl_label_expnull_for;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix;
+ plen = fn->fec.u.ipv4.prefixlen;
+ exp_label = MPLS_LABEL_IPV4_EXPLICIT_NULL;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ acl_name = ldeconf->ipv6.acl_label_expnull_for;
+ prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix;
+ plen = fn->fec.u.ipv6.prefixlen;
+ exp_label = MPLS_LABEL_IPV6_EXPLICIT_NULL;
+ break;
+ default:
+ fatalx("lde_change_expnull_for_filter: unknown af");
+ }
+
+ if (lde_acl_check(acl_name, af, prefix, plen) == FILTER_PERMIT) {
+ /* for this fec change any imp-null to exp-null */
+ if (fn->local_label == MPLS_LABEL_IMPLICIT_NULL) {
+ fn->local_label= lde_update_label(fn);
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ } else {
+ /* for this fec change any exp-null back to imp-null */
+ if (fn->local_label == exp_label) {
+ fn->local_label = lde_update_label(fn);
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ }
+ }
+ }
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0,
+ NULL, 0);
+}
+
+static int
+lde_address_add(struct lde_nbr *ln, struct lde_addr *lde_addr)
+{
+ struct lde_addr *new;
+
+ if (lde_address_find(ln, lde_addr->af, &lde_addr->addr) != NULL)
+ return (-1);
+
+ if ((new = calloc(1, sizeof(*new))) == NULL)
+ fatal(__func__);
+
+ new->af = lde_addr->af;
+ new->addr = lde_addr->addr;
+ TAILQ_INSERT_TAIL(&ln->addr_list, new, entry);
+
+ /* reevaluate the previously received mappings from this neighbor */
+ lde_nbr_addr_update(ln, lde_addr, 0);
+
+ return (0);
+}
+
+static int
+lde_address_del(struct lde_nbr *ln, struct lde_addr *lde_addr)
+{
+ lde_addr = lde_address_find(ln, lde_addr->af, &lde_addr->addr);
+ if (lde_addr == NULL)
+ return (-1);
+
+ /* reevaluate the previously received mappings from this neighbor */
+ lde_nbr_addr_update(ln, lde_addr, 1);
+
+ TAILQ_REMOVE(&ln->addr_list, lde_addr, entry);
+ free(lde_addr);
+
+ return (0);
+}
+
+struct lde_addr *
+lde_address_find(struct lde_nbr *ln, int af, union ldpd_addr *addr)
+{
+ struct lde_addr *lde_addr;
+
+ TAILQ_FOREACH(lde_addr, &ln->addr_list, entry)
+ if (lde_addr->af == af &&
+ ldp_addrcmp(af, &lde_addr->addr, addr) == 0)
+ return (lde_addr);
+
+ return (NULL);
+}
+
+static void
+lde_address_list_free(struct lde_nbr *ln)
+{
+ struct lde_addr *lde_addr;
+
+ while ((lde_addr = TAILQ_POP_FIRST(&ln->addr_list, entry)) != NULL)
+ free(lde_addr);
+}
+
+/*
+ * Event callback used to retry the label-manager sync zapi session.
+ */
+static void zclient_sync_retry(struct event *thread)
+{
+ zclient_sync_init();
+}
+
+/*
+ * Initialize and open a synchronous zapi session. This is used by label chunk
+ * management code, which acquires and releases blocks of labels from the
+ * zebra label-manager module.
+ */
+static void zclient_sync_init(void)
+{
+ struct zclient_options options = zclient_options_default;
+
+ options.synchronous = true;
+
+ /* Initialize special zclient for synchronous message exchanges. */
+ zclient_sync = zclient_new(master, &options, NULL, 0);
+ zclient_sync->sock = -1;
+ zclient_sync->redist_default = ZEBRA_ROUTE_LDP;
+ zclient_sync->session_id = 1; /* Distinguish from main session */
+ zclient_sync->privs = &lde_privs;
+
+ if (zclient_socket_connect(zclient_sync) < 0) {
+ log_warnx("Error connecting synchronous zclient!");
+ goto retry;
+ }
+ /* make socket non-blocking */
+ sock_set_nonblock(zclient_sync->sock);
+
+ /* Send hello to notify zebra this is a synchronous client */
+ if (zclient_send_hello(zclient_sync) == ZCLIENT_SEND_FAILURE) {
+ log_warnx("Error sending hello for synchronous zclient!");
+ goto retry;
+ }
+
+ /* Connect to label manager */
+ if (lm_label_manager_connect(zclient_sync, 0) != 0) {
+ log_warnx("Error connecting to label manager!");
+ goto retry;
+ }
+
+ /* Finish label-manager init once the LM session is running */
+ lde_label_list_init();
+
+ return;
+
+retry:
+
+ /* Discard failed zclient object */
+ zclient_stop(zclient_sync);
+ zclient_free(zclient_sync);
+ zclient_sync = NULL;
+
+ /* Retry using a timer */
+ event_add_timer(master, zclient_sync_retry, NULL, 1, NULL);
+}
+
+static void
+lde_del_label_chunk(void *val)
+{
+ free(val);
+}
+
+static int
+lde_release_label_chunk(uint32_t start, uint32_t end)
+{
+ int ret;
+
+ ret = lm_release_label_chunk(zclient_sync, start, end);
+ if (ret < 0) {
+ log_warnx("Error releasing label chunk!");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+lde_get_label_chunk(void)
+{
+ int ret;
+ uint32_t start, end;
+
+ debug_labels("getting label chunk (size %u)", CHUNK_SIZE);
+ ret = lm_get_label_chunk(zclient_sync, 0, MPLS_LABEL_BASE_ANY,
+ CHUNK_SIZE, &start, &end);
+ if (ret < 0) {
+ log_warnx("Error getting label chunk!");
+ return -1;
+ }
+
+ on_get_label_chunk_response(start, end);
+
+ return (0);
+}
+
+static void
+lde_label_list_init(void)
+{
+ label_chunk_list = list_new();
+ label_chunk_list->del = lde_del_label_chunk;
+
+ /* get first chunk */
+ while (lde_get_label_chunk () != 0) {
+ log_warnx("Error getting first label chunk!");
+ sleep(1);
+ }
+}
+
+static void
+on_get_label_chunk_response(uint32_t start, uint32_t end)
+{
+ struct label_chunk *new_label_chunk;
+
+ debug_labels("label chunk assign: %u - %u", start, end);
+
+ new_label_chunk = calloc(1, sizeof(struct label_chunk));
+ if (!new_label_chunk) {
+ log_warn("Error trying to allocate label chunk %u - %u", start, end);
+ return;
+ }
+
+ new_label_chunk->start = start;
+ new_label_chunk->end = end;
+ new_label_chunk->used_mask = 0;
+
+ listnode_add(label_chunk_list, (void *)new_label_chunk);
+
+ /* let's update current if needed */
+ if (!current_label_chunk)
+ current_label_chunk = listtail(label_chunk_list);
+}
+
+void
+lde_free_label(uint32_t label)
+{
+ struct listnode *node;
+ struct label_chunk *label_chunk;
+ uint64_t pos;
+
+ for (ALL_LIST_ELEMENTS_RO(label_chunk_list, node, label_chunk)) {
+ if (label <= label_chunk->end && label >= label_chunk->start) {
+ pos = 1ULL << (label - label_chunk->start);
+ UNSET_FLAG(label_chunk->used_mask, pos);
+ /* if nobody is using this chunk and it's not current_label_chunk, then free it */
+ if (!label_chunk->used_mask && (current_label_chunk != node)) {
+ if (lde_release_label_chunk(label_chunk->start, label_chunk->end) != 0)
+ log_warnx("%s: Error releasing label chunk!", __func__);
+ else {
+ listnode_delete(label_chunk_list, label_chunk);
+ lde_del_label_chunk(label_chunk);
+ }
+ }
+ break;
+ }
+ }
+
+ return;
+}
+
+static uint32_t
+lde_get_next_label(void)
+{
+ struct label_chunk *label_chunk;
+ uint32_t i, size;
+ uint64_t pos;
+ uint32_t label = NO_LABEL;
+
+ while (current_label_chunk) {
+ label_chunk = listgetdata(current_label_chunk);
+ if (!label_chunk)
+ goto end;
+
+ /* try to get next free label in currently used label chunk */
+ size = label_chunk->end - label_chunk->start + 1;
+ for (i = 0, pos = 1; i < size; i++, pos <<= 1) {
+ if (!(pos & label_chunk->used_mask)) {
+ SET_FLAG(label_chunk->used_mask, pos);
+ label = label_chunk->start + i;
+ goto end;
+ }
+ }
+ current_label_chunk = listnextnode(current_label_chunk);
+ }
+
+end:
+ /* we moved till the last chunk, or were not able to find a label,
+ so let's ask for another one */
+ if (!current_label_chunk ||
+ current_label_chunk == listtail(label_chunk_list) ||
+ label == NO_LABEL) {
+ if (lde_get_label_chunk() != 0)
+ log_warn("%s: Error getting label chunk!", __func__);
+
+ }
+
+ return (label);
+}
+
+static void
+lde_check_filter_af(int af, struct ldpd_af_conf *af_conf,
+ const char *filter_name)
+{
+ if (strcmp(af_conf->acl_label_allocate_for, filter_name) == 0)
+ lde_change_allocate_filter(af);
+
+ if ((strcmp(af_conf->acl_label_advertise_to, filter_name) == 0)
+ || (strcmp(af_conf->acl_label_advertise_for, filter_name) == 0))
+ lde_change_advertise_filter(af);
+
+ if ((strcmp(af_conf->acl_label_accept_for, filter_name) == 0)
+ || (strcmp(af_conf->acl_label_accept_from, filter_name) == 0))
+ lde_change_accept_filter(af);
+
+ if (strcmp(af_conf->acl_label_expnull_for, filter_name) == 0)
+ lde_change_expnull_for_filter(af);
+}
+
+void lde_route_update(struct iface *iface, int af)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_nbr *ln;
+
+ /* update label of non-connected routes */
+ log_debug("update labels for interface %s", iface->name);
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (IS_MPLS_UNRESERVED_LABEL(fn->local_label))
+ continue;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ /* unspecified so process both address families */
+ break;
+ }
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ /*
+ * If connected leave existing label. If LDP
+ * configured on interface or a static route
+ * may need new label. If no LDP configured
+ * treat fec as a connected route
+ */
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED))
+ break;
+
+ if (fnh->ifindex != iface->ifindex)
+ continue;
+
+ UNSET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ if (IS_MPLS_RESERVED_LABEL(fn->local_label)) {
+ fn->local_label = NO_LABEL;
+ fn->local_label = lde_update_label(fn);
+ if (fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(
+ ln, fn, 0);
+ }
+ break;
+ }
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid,
+ 0, NULL, 0);
+}
+
+void lde_route_update_release(struct iface *iface, int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+
+ /* update label of interfaces no longer running LDP */
+ log_debug("release all labels for interface %s af %s", iface->name,
+ af == AF_INET ? "ipv4" : "ipv6");
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ fatalx("lde_route_update_release: unknown af");
+ }
+
+ if (fn->local_label == NO_LABEL)
+ continue;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ /*
+ * If connected leave existing label. If LDP
+ * removed from interface may need new label
+ * and would be treated as a connected route
+ */
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED))
+ break;
+
+ if (fnh->ifindex != iface->ifindex)
+ continue;
+
+ SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+ lde_free_label(fn->local_label);
+ fn->local_label = NO_LABEL;
+ fn->local_label = lde_update_label(fn);
+ if (fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 0);
+ break;
+ }
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid,
+ 0, NULL, 0);
+}
+
+void lde_route_update_release_all(int af)
+{
+ struct lde_nbr *ln;
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+
+ /* remove labels from all interfaces as LDP is no longer running for
+ * this address family
+ */
+ log_debug("release all labels for address family %s",
+ af == AF_INET ? "ipv4" : "ipv6");
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ switch (af) {
+ case AF_INET:
+ if (fn->fec.type != FEC_TYPE_IPV4)
+ continue;
+ break;
+ case AF_INET6:
+ if (fn->fec.type != FEC_TYPE_IPV6)
+ continue;
+ break;
+ default:
+ fatalx("lde_route_update_release: unknown af");
+ }
+
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ lde_send_delete_klabel(fn, fnh);
+ }
+ }
+}
diff --git a/ldpd/lde.h b/ldpd/lde.h
new file mode 100644
index 0000000..2688b6a
--- /dev/null
+++ b/ldpd/lde.h
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#ifndef _LDE_H_
+#define _LDE_H_
+
+#include "queue.h"
+#include "openbsd-tree.h"
+#include "if.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;
+ struct lde_map_head *head; /* fec_node's upstream/downstream */
+ RB_ENTRY(lde_map) entry;
+ struct lde_nbr *nexthop;
+ struct map map;
+};
+RB_HEAD(lde_map_head, lde_map);
+RB_PROTOTYPE(lde_map_head, lde_map, entry, lde_map_cmp);
+
+/* 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 */
+ int flags; /* capabilities */
+ struct fec_tree recv_req;
+ struct fec_tree sent_req;
+ struct fec_tree recv_map;
+ struct fec_tree sent_map;
+ struct fec_tree sent_map_pending;
+ 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;
+ ifindex_t ifindex;
+ uint32_t remote_label;
+ uint8_t route_type;
+ unsigned short route_instance;
+ uint8_t flags;
+};
+#define F_FEC_NH_NEW 0x01
+#define F_FEC_NH_CONNECTED 0x02
+#define F_FEC_NH_DEFER 0x04 /* running ordered control */
+#define F_FEC_NH_NO_LDP 0x08 /* no ldp on this interface */
+
+struct fec_node {
+ struct fec fec;
+
+ LIST_HEAD(, fec_nh) nexthops; /* fib nexthops */
+ struct lde_map_head downstream; /* recv mappings */
+ struct lde_map_head upstream; /* sent mappings */
+
+ uint32_t local_label;
+
+ uint32_t pw_remote_status;
+
+ void *data; /* fec specific data */
+};
+
+#define CHUNK_SIZE 64
+struct label_chunk {
+ uint32_t start;
+ uint32_t end;
+ uint64_t used_mask;
+};
+
+#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(void);
+void lde_init(struct ldpd_init *);
+int lde_imsg_compose_parent(int, pid_t, void *, uint16_t);
+void lde_imsg_compose_parent_sync(int, pid_t, void *, uint16_t);
+int lde_imsg_compose_ldpe(int, uint32_t, pid_t, void *, uint16_t);
+int lde_acl_check(char *, int, union ldpd_addr *, uint8_t);
+uint32_t lde_update_label(struct fec_node *);
+void lde_free_label(uint32_t label);
+void lde_send_change_klabel(struct fec_node *, struct fec_nh *);
+void lde_send_delete_klabel(struct fec_node *, struct fec_nh *);
+void lde_fec2prefix(const struct fec *fec, struct prefix *prefix);
+void lde_prefix2fec(const struct prefix *prefix, struct fec *fec);
+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 *,
+ struct map *, struct status_tlv *);
+void lde_send_labelwithdraw_wcard(struct lde_nbr *, uint32_t);
+void lde_send_labelwithdraw_twcard_prefix(struct lde_nbr *,
+ uint16_t, uint32_t);
+void lde_send_labelwithdraw_twcard_pwid(struct lde_nbr *, uint16_t,
+ uint32_t);
+void lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *, uint16_t,
+ uint32_t);
+void lde_send_labelrelease(struct lde_nbr *, struct fec_node *,
+ struct map *, uint32_t);
+void lde_send_labelrequest(struct lde_nbr *, struct fec_node *,
+ struct map *, int);
+void lde_send_labelrequest_wcard(struct lde_nbr *, uint16_t af);
+void lde_send_notification(struct lde_nbr *, uint32_t, uint32_t,
+ uint16_t);
+void lde_send_notification_eol_prefix(struct lde_nbr *, int);
+void lde_send_notification_eol_pwid(struct lde_nbr *, 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 fec *lde_map_pending_add(struct lde_nbr *, struct fec_node *);
+void lde_map_pending_del(struct lde_nbr *, struct fec *);
+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);
+void lde_change_allocate_filter(int);
+void lde_change_advertise_filter(int);
+void lde_change_accept_filter(int);
+void lde_change_expnull_for_filter(int);
+void lde_route_update(struct iface *, int);
+void lde_route_update_release(struct iface *, int);
+void lde_route_update_release_all(int);
+struct lde_addr *lde_address_find(struct lde_nbr *, int,
+ union ldpd_addr *);
+void lde_allow_broken_lsp_update(int new_config);
+
+/* 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 *,
+ ifindex_t, uint8_t, unsigned short);
+void lde_kernel_insert(struct fec *, int, union ldpd_addr *,
+ ifindex_t, uint8_t, unsigned short, int, void *);
+void lde_kernel_remove(struct fec *, int, union ldpd_addr *,
+ ifindex_t, uint8_t, unsigned short);
+void lde_kernel_update(struct fec *);
+void lde_check_mapping(struct map *, struct lde_nbr *, int);
+void lde_check_request(struct map *, struct lde_nbr *);
+void lde_check_request_wcard(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 *);
+int lde_wildcard_apply(struct map *, struct fec *,
+ struct lde_map *);
+void lde_gc_timer(struct event *thread);
+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 *, const char *);
+struct l2vpn_if *l2vpn_if_find(struct l2vpn *, const char *);
+void l2vpn_if_update_info(struct l2vpn_if *, struct kif *);
+void l2vpn_if_update(struct l2vpn_if *);
+struct l2vpn_pw *l2vpn_pw_new(struct l2vpn *, const char *);
+struct l2vpn_pw *l2vpn_pw_find(struct l2vpn *, const char *);
+struct l2vpn_pw *l2vpn_pw_find_active(struct l2vpn *, const char *);
+struct l2vpn_pw *l2vpn_pw_find_inactive(struct l2vpn *, const char *);
+void l2vpn_pw_update_info(struct l2vpn_pw *, struct kif *);
+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(struct lde_nbr *, uint32_t, struct fec *);
+void l2vpn_send_pw_status_wcard(struct lde_nbr *, uint32_t,
+ uint16_t, uint32_t);
+void l2vpn_recv_pw_status(struct lde_nbr *, struct notify_msg *);
+void l2vpn_recv_pw_status_wcard(struct lde_nbr *,
+ struct notify_msg *);
+int l2vpn_pw_status_update(struct zapi_pw_status *);
+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 0000000..04bff90
--- /dev/null
+++ b/ldpd/lde_lib.c
@@ -0,0 +1,1055 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+#include "rlfa.h"
+
+#include "mpls.h"
+
+static __inline int fec_compare(const struct fec *, const 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 *,
+ ifindex_t, uint8_t, unsigned short);
+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(fec_tree, fh);
+}
+
+static __inline int
+fec_compare(const struct fec *a, const 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 (!RB_EMPTY(fec_tree, fh)) {
+ f = RB_ROOT(fec_tree, fh);
+
+ 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 &&
+ RB_EMPTY(lde_map_head, &fn->downstream))
+ continue;
+
+ memset(&rtctl, 0, sizeof(rtctl));
+ 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;
+ case FEC_TYPE_PWID:
+ continue;
+ }
+
+ rtctl.local_label = fn->local_label;
+ if (RB_EMPTY(lde_map_head, &fn->downstream)) {
+ rtctl.in_use = 0;
+ rtctl.nexthop.s_addr = INADDR_ANY;
+ rtctl.remote_label = NO_LABEL;
+ rtctl.no_downstream = 1;
+ }
+ lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB_BEGIN, 0, pid, &rtctl,
+ sizeof(rtctl));
+
+ RB_FOREACH(me, lde_map_head, &fn->upstream) {
+ rtctl.nexthop = me->nexthop->id;
+ lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB_SENT, 0, pid,
+ &rtctl, sizeof(rtctl));
+ }
+
+ RB_FOREACH(me, lde_map_head, &fn->downstream) {
+ 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_RCVD, 0, pid,
+ &rtctl, sizeof(rtctl));
+ }
+ lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB_END, 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);
+ assert(fnh != LIST_FIRST(&fn->nexthops));
+ }
+ if (!RB_EMPTY(lde_map_head, &fn->downstream))
+ log_warnx("%s: fec %s downstream list not empty", __func__,
+ log_fec(&fn->fec));
+ if (!RB_EMPTY(lde_map_head, &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;
+ RB_INIT(lde_map_head, &fn->upstream);
+ RB_INIT(lde_map_head, &fn->downstream);
+ LIST_INIT(&fn->nexthops);
+
+ if (fec->type == FEC_TYPE_PWID)
+ fn->pw_remote_status = PW_FORWARDING;
+
+ 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,
+ ifindex_t ifindex, uint8_t route_type, unsigned short route_instance)
+{
+ struct fec_nh *fnh;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry)
+ if (fnh->af == af &&
+ ldp_addrcmp(af, &fnh->nexthop, nexthop) == 0 &&
+ fnh->ifindex == ifindex &&
+ fnh->route_type == route_type &&
+ fnh->route_instance == route_instance)
+ return (fnh);
+
+ return (NULL);
+}
+
+static struct fec_nh *
+fec_nh_add(struct fec_node *fn, int af, union ldpd_addr *nexthop,
+ ifindex_t ifindex, uint8_t route_type, unsigned short route_instance)
+{
+ struct fec_nh *fnh;
+
+ fnh = calloc(1, sizeof(*fnh));
+ if (fnh == NULL)
+ fatal(__func__);
+
+ fnh->af = af;
+ fnh->nexthop = *nexthop;
+ fnh->ifindex = ifindex;
+ fnh->remote_label = NO_LABEL;
+ fnh->route_type = route_type;
+ fnh->route_instance = route_instance;
+ LIST_INSERT_HEAD(&fn->nexthops, fnh, entry);
+
+ return (fnh);
+}
+
+static void
+fec_nh_del(struct fec_nh *fnh)
+{
+ LIST_REMOVE(fnh, entry);
+ free(fnh);
+}
+
+void
+lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop,
+ ifindex_t ifindex, uint8_t route_type, unsigned short route_instance,
+ int connected, void *data)
+{
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct iface *iface;
+
+ fn = (struct fec_node *)fec_find(&ft, fec);
+ if (fn == NULL)
+ fn = fec_add(fec);
+ if (data)
+ fn->data = data;
+
+ fnh = fec_nh_find(fn, af, nexthop, ifindex, route_type, route_instance);
+ if (fnh == NULL) {
+ fnh = fec_nh_add(fn, af, nexthop, ifindex, route_type,
+ route_instance);
+ /*
+ * Ordered Control: if not a connected route and not a route
+ * learned over an interface not running LDP and not a PW
+ * then mark to wait until we receive labelmap msg before
+ * installing in kernel and sending to peer
+ */
+ iface = if_lookup(ldeconf, ifindex);
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL) &&
+ !connected && iface != NULL && fec->type != FEC_TYPE_PWID)
+ SET_FLAG(fnh->flags, F_FEC_NH_DEFER);
+ }
+
+ SET_FLAG(fnh->flags, F_FEC_NH_NEW);
+ if (connected)
+ SET_FLAG(fnh->flags, F_FEC_NH_CONNECTED);
+}
+
+void
+lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop,
+ ifindex_t ifindex, uint8_t route_type, unsigned short route_instance)
+{
+ 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, ifindex, route_type, route_instance);
+ if (fnh == NULL)
+ /* route lost */
+ return;
+
+ lde_send_delete_klabel(fn, fnh);
+ fec_nh_del(fnh);
+}
+
+/*
+ * Whenever a route is changed, zebra advertises its new version without
+ * withdrawing the old one. So, after processing a ZEBRA_REDISTRIBUTE_IPV[46]_ADD
+ * message, we need to check for nexthops that were removed and, for each of
+ * them (if any), withdraw the associated labels from zebra.
+ */
+void
+lde_kernel_update(struct fec *fec)
+{
+ struct fec_node *fn;
+ struct fec_nh *fnh, *safe;
+ struct lde_nbr *ln;
+ struct lde_map *me;
+ struct iface *iface;
+
+ fn = (struct fec_node *)fec_find(&ft, fec);
+ if (fn == NULL)
+ return;
+
+ LIST_FOREACH_SAFE(fnh, &fn->nexthops, entry, safe) {
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_NEW)) {
+ UNSET_FLAG(fnh->flags, F_FEC_NH_NEW);
+ /*
+ * if LDP configured on interface or a static route
+ * clear flag else treat fec as a connected route
+ */
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ENABLED)) {
+ iface = if_lookup(ldeconf,fnh->ifindex);
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED) ||
+ iface ||
+ fnh->route_type == ZEBRA_ROUTE_STATIC)
+ UNSET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ else
+ SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ } else
+ SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
+ } else {
+ lde_send_delete_klabel(fn, fnh);
+ fec_nh_del(fnh);
+ }
+ }
+
+ if (LIST_EMPTY(&fn->nexthops)) {
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
+ fn->data = NULL;
+
+ /*
+ * Do not deallocate the local label now, do that only in the
+ * LIB garbage collector. This will prevent ldpd from changing
+ * the input label of some prefixes too often when running on
+ * an unstable network. Also, restart the garbage collector
+ * timer so that labels are deallocated only when the network
+ * is stabilized.
+ */
+ lde_gc_start_timer();
+ } else {
+ fn->local_label = lde_update_label(fn);
+ if (fn->local_label != NO_LABEL)
+ /* FEC.1: perform lsr label distribution procedure */
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 1);
+ }
+
+ /* if no label created yet then don't try to program labeled route */
+ if (fn->local_label == NO_LABEL)
+ return;
+
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ lde_send_change_klabel(fn, fnh);
+
+ switch (fn->fec.type) {
+ case FEC_TYPE_IPV4:
+ case FEC_TYPE_IPV6:
+ ln = lde_nbr_find_by_addr(fnh->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, 0);
+ }
+ }
+}
+
+void
+lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
+{
+ struct fec fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct lde_req *lre;
+ struct lde_map *me;
+ struct l2vpn_pw *pw;
+ bool send_map = false;
+
+ lde_map2fec(map, ln->id, &fec);
+
+ switch (fec.type) {
+ case FEC_TYPE_IPV4:
+ if (lde_acl_check(ldeconf->ipv4.acl_label_accept_from,
+ AF_INET, (union ldpd_addr *)&ln->id, 32) != FILTER_PERMIT)
+ return;
+ if (lde_acl_check(ldeconf->ipv4.acl_label_accept_for,
+ AF_INET, (union ldpd_addr *)&fec.u.ipv4.prefix,
+ fec.u.ipv4.prefixlen) != FILTER_PERMIT)
+ return;
+ break;
+ case FEC_TYPE_IPV6:
+ if (lde_acl_check(ldeconf->ipv6.acl_label_accept_from,
+ AF_INET, (union ldpd_addr *)&ln->id, 32) != FILTER_PERMIT)
+ return;
+ if (lde_acl_check(ldeconf->ipv6.acl_label_accept_for,
+ AF_INET6, (union ldpd_addr *)&fec.u.ipv6.prefix,
+ fec.u.ipv6.prefixlen) != FILTER_PERMIT)
+ return;
+ break;
+ case FEC_TYPE_PWID:
+ break;
+ }
+
+ 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)) {
+ if (rcvd_label_mapping && CHECK_FLAG(map->flags, F_MAP_PW_STATUS))
+ fn->pw_remote_status = map->pw_status;
+
+ 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, NULL, 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;
+
+ /*
+ * Ordered Control: labelmap msg received from
+ * NH so clear flag and send labelmap msg to
+ * peer
+ */
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
+ send_map = true;
+ UNSET_FLAG(fnh->flags, F_FEC_NH_DEFER);
+ }
+ fnh->remote_label = map->label;
+ if (fn->local_label != NO_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 (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU))
+ pw->remote_mtu = map->fec.pwid.ifmtu;
+ if (rcvd_label_mapping && CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) {
+ pw->remote_status = map->pw_status;
+ fn->pw_remote_status = map->pw_status;
+ }
+ else
+ pw->remote_status = PW_FORWARDING;
+ fnh->remote_label = map->label;
+ if (l2vpn_pw_ok(pw, fnh))
+ lde_send_change_klabel(fn, fnh);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(&fec, ln, map->label);
+
+ /* LMp.13 & LMp.16: Record the mapping from this peer */
+ if (me == NULL)
+ me = lde_map_add(ln, fn, 0);
+ me->map = *map;
+
+ /*
+ * 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.
+ */
+
+ /*
+ * Ordered Control: just received a labelmap for this fec from NH so
+ * need to send labelmap to all peers
+ * LMp.20 - LMp21 Execute procedure to send Label Mapping
+ */
+ if (send_map && fn->local_label != NO_LABEL)
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelmapping(ln, fn, 1);
+}
+
+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;
+
+ /* wildcard label request */
+ if (map->type == MAP_TYPE_TYPED_WCARD) {
+ lde_check_request_wcard(map, ln);
+ return;
+ }
+
+ /* 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, 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, S_LOOP_DETECTED, map->msg_id,
+ htons(MSG_TYPE_LABELREQUEST));
+ return;
+ case FEC_TYPE_PWID:
+ 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_request_wcard(struct map *map, struct lde_nbr *ln)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct lde_req *lre;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ /* only a typed wildcard is possible here */
+ if (lde_wildcard_apply(map, &fn->fec, NULL) == 0)
+ continue;
+
+ /* LRq.2: is there a next hop for fec? */
+ if (LIST_EMPTY(&fn->nexthops))
+ continue;
+
+ /* 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 */
+ continue;
+
+ /* 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);
+ }
+}
+
+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;
+ struct fec *pending_map;
+
+ /* wildcard label release */
+ if (map->type == MAP_TYPE_WILDCARD ||
+ map->type == MAP_TYPE_TYPED_WCARD ||
+ (map->type == MAP_TYPE_PWID && !CHECK_FLAG(map->flags, F_MAP_PW_ID))) {
+ lde_check_release_wcard(map, ln);
+ 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.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.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 || map->label == lw->label)) {
+ /* LRl.4: delete record of outstanding label withdraw */
+ lde_wdraw_del(ln, lw);
+
+ /* send pending label mapping if any */
+ pending_map = fec_find(&ln->sent_map_pending, &fn->fec);
+ if (pending_map) {
+ lde_send_labelmapping(ln, fn, 1);
+ lde_map_pending_del(ln, pending_map);
+ }
+ }
+
+ /*
+ * 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;
+ struct fec *pending_map;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+
+ /* LRl.1: does FEC match a known FEC? */
+ if (lde_wildcard_apply(map, &fn->fec, me) == 0)
+ continue;
+
+ /* LRl.6: check sent map list and remove it if available */
+ if (me &&
+ (map->label == NO_LABEL || map->label == me->map.label))
+ lde_map_del(ln, me, 1);
+
+ /* 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 || map->label == lw->label)) {
+ /* LRl.4: delete record of outstanding lbl withdraw */
+ lde_wdraw_del(ln, lw);
+
+ /* send pending label mapping if any */
+ pending_map = fec_find(&ln->sent_map_pending, &fn->fec);
+ if (pending_map) {
+ lde_send_labelmapping(ln, fn, 1);
+ lde_map_pending_del(ln, pending_map);
+ }
+ }
+
+ /*
+ * 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;
+ struct lde_nbr *lnbr;
+
+ /* wildcard label withdraw */
+ if (map->type == MAP_TYPE_WILDCARD ||
+ map->type == MAP_TYPE_TYPED_WCARD ||
+ (map->type == MAP_TYPE_PWID && !CHECK_FLAG(map->flags, F_MAP_PW_ID))) {
+ lde_check_withdraw_wcard(map, ln);
+ 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;
+ pw->remote_status = PW_NOT_FORWARDING;
+ break;
+ default:
+ break;
+ }
+ if (map->label != NO_LABEL && map->label != fnh->remote_label)
+ continue;
+
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ }
+
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(&fec, ln, MPLS_INVALID_LABEL);
+
+ /* LWd.2: send label release */
+ lde_send_labelrelease(ln, fn, NULL, 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);
+ else
+ /* LWd.13 done */
+ return;
+
+ /* Ordered Control: additional withdraw steps */
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
+ /* LWd.8: for each neighbor other that src of withdraw msg */
+ RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) {
+ if (ln->peerid == lnbr->peerid)
+ continue;
+
+ /* LWd.9: check if previously sent a label mapping */
+ me = (struct lde_map *)fec_find(&lnbr->sent_map, &fn->fec);
+
+ /*
+ * LWd.10: does label sent to peer "map" to withdraw
+ * label
+ */
+ if (me && lde_nbr_is_nexthop(fn, lnbr))
+ /* LWd.11: send label withdraw */
+ lde_send_labelwithdraw(lnbr, fn, NULL, NULL);
+ }
+ }
+
+}
+
+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;
+ struct l2vpn_pw *pw;
+ struct lde_nbr *lnbr;
+
+ /* LWd.2: send label release */
+ lde_send_labelrelease(ln, NULL, map, map->label);
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+
+ if (lde_wildcard_apply(map, &fn->fec, me) == 0)
+ continue;
+
+ /* 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;
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw)
+ pw->remote_status = PW_NOT_FORWARDING;
+ break;
+ default:
+ break;
+ }
+ if (map->label != NO_LABEL && map->label != fnh->remote_label)
+ continue;
+
+ lde_send_delete_klabel(fn, fnh);
+ fnh->remote_label = NO_LABEL;
+ }
+
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
+
+ /* LWd.3: check previously received label mapping */
+ 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);
+ else
+ /* LWd.13 done */
+ continue;
+
+ /* Ordered Control: additional withdraw steps */
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
+ /*
+ * LWd.8: for each neighbor other that src of
+ * withdraw msg
+ */
+ RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) {
+ if (ln->peerid == lnbr->peerid)
+ continue;
+
+ /* LWd.9: check if previously sent a label
+ * mapping
+ */
+ me = (struct lde_map *)fec_find(&lnbr->sent_map, &fn->fec);
+ /*
+ * LWd.10: does label sent to peer "map" to
+ * withdraw label
+ */
+ if (me && lde_nbr_is_nexthop(fn, lnbr))
+ /* LWd.11: send label withdraw */
+ lde_send_labelwithdraw(lnbr, fn, NULL, NULL);
+ }
+ }
+ }
+}
+
+int
+lde_wildcard_apply(struct map *wcard, struct fec *fec, struct lde_map *me)
+{
+ switch (wcard->type) {
+ case MAP_TYPE_WILDCARD:
+ /* full wildcard */
+ return (1);
+ case MAP_TYPE_TYPED_WCARD:
+ switch (wcard->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ if (wcard->fec.twcard.u.prefix_af == AF_INET &&
+ fec->type != FEC_TYPE_IPV4)
+ return (0);
+ if (wcard->fec.twcard.u.prefix_af == AF_INET6 &&
+ fec->type != FEC_TYPE_IPV6)
+ return (0);
+ return (1);
+ case MAP_TYPE_PWID:
+ if (fec->type != FEC_TYPE_PWID)
+ return (0);
+ if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD &&
+ wcard->fec.twcard.u.pw_type != fec->u.pwid.type)
+ return (0);
+ return (1);
+ default:
+ fatalx("lde_wildcard_apply: unexpected fec type");
+ }
+ break;
+ case MAP_TYPE_PWID:
+ /* RFC4447 pw-id group wildcard */
+ if (fec->type != FEC_TYPE_PWID)
+ return (0);
+ if (fec->u.pwid.type != wcard->fec.pwid.type)
+ return (0);
+ if (me == NULL || (me->map.fec.pwid.group_id !=
+ wcard->fec.pwid.group_id))
+ return (0);
+ return (1);
+ default:
+ fatalx("lde_wildcard_apply: unexpected fec type");
+ }
+}
+
+/* gabage collector timer: timer to remove dead entries from the LIB */
+
+/* ARGSUSED */
+void lde_gc_timer(struct event *thread)
+{
+ 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) ||
+ !RB_EMPTY(lde_map_head, &fn->downstream) ||
+ !RB_EMPTY(lde_map_head, &fn->upstream))
+ continue;
+
+ if (fn->local_label != NO_LABEL)
+ lde_free_label(fn->local_label);
+
+ 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)
+{
+ EVENT_OFF(gc_timer);
+ event_add_timer(master, lde_gc_timer, NULL, LDE_GC_INTERVAL, &gc_timer);
+}
+
+void
+lde_gc_stop_timer(void)
+{
+ EVENT_OFF(gc_timer);
+}
diff --git a/ldpd/ldp.h b/ldpd/ldp.h
new file mode 100644
index 0000000..33cf93c
--- /dev/null
+++ b/ldpd/ldp.h
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+/* LDP protocol definitions */
+
+#ifndef _LDP_H_
+#define _LDP_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 DFLT_WAIT_FOR_SYNC 10
+
+#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_CAPABILITY 0x0202 /* RFC 5561 */
+#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_MAC_LIST 0x8404
+#define TLV_TYPE_PW_STATUS 0x896A
+#define TLV_TYPE_PW_IF_PARAM 0x096B
+#define TLV_TYPE_PW_GROUP_ID 0x096C
+/* RFC 5561 */
+#define TLV_TYPE_RETURNED_TLVS 0x8304
+#define TLV_TYPE_DYNAMIC_CAP 0x8506
+/* RFC 5918 */
+#define TLV_TYPE_TWCARD_CAP 0x850B
+/* RFC 5919 */
+#define TLV_TYPE_UNOTIF_CAP 0x8603
+/* 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;
+} __attribute__ ((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 */
+} __attribute__ ((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 5561 */
+#define S_UNSSUPORTDCAP 0x0000002E
+/* RFC 5919 */
+#define S_ENDOFLIB 0x0000002F
+/* 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;
+} __attribute__ ((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;
+} __attribute__ ((packed));
+
+#define STATUS_SIZE 14
+#define STATUS_TLV_LEN 10
+#define STATUS_FATAL 0x80000000
+
+struct capability_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint8_t reserved;
+};
+#define STATE_BIT 0x80
+
+#define F_CAP_TLV_RCVD_DYNAMIC 0x01
+#define F_CAP_TLV_RCVD_TWCARD 0x02
+#define F_CAP_TLV_RCVD_UNOTIF 0x04
+
+#define CAP_TLV_DYNAMIC_SIZE 5
+#define CAP_TLV_DYNAMIC_LEN 1
+
+#define CAP_TLV_TWCARD_SIZE 5
+#define CAP_TLV_TWCARD_LEN 1
+
+#define CAP_TLV_UNOTIF_SIZE 5
+#define CAP_TLV_UNOTIF_LEN 1
+
+#define AF_IPV4 0x1
+#define AF_IPV6 0x2
+
+struct address_list_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint16_t family;
+ /* address entries */
+} __attribute__ ((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 FEC_PWID_SIZE 4
+#define FEC_ELM_TWCARD_MIN_LEN 3
+
+#define MAP_TYPE_WILDCARD 0x01
+#define MAP_TYPE_PREFIX 0x02
+#define MAP_TYPE_TYPED_WCARD 0x05
+#define MAP_TYPE_PWID 0x80
+#define MAP_TYPE_GENPWID 0x81
+
+#define CONTROL_WORD_FLAG 0x8000
+#define DEFAULT_PW_TYPE PW_TYPE_ETHERNET
+
+#define PW_TWCARD_RESERVED_BIT 0x8000
+
+/* 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 NO_LABEL UINT32_MAX
+
+#endif /* !_LDP_H_ */
diff --git a/ldpd/ldp_debug.c b/ldpd/ldp_debug.c
new file mode 100644
index 0000000..957fb8e
--- /dev/null
+++ b/ldpd/ldp_debug.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 by Open Source Routing.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "vty.h"
+
+#include "ldpd.h"
+#include "ldp_debug.h"
+#include "ldp_vty.h"
+
+struct ldp_debug conf_ldp_debug;
+struct ldp_debug ldp_debug;
+
+static int ldp_debug_config_write(struct vty *);
+
+/* Debug node. */
+struct cmd_node ldp_debug_node = {
+ .name = "debug",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = ldp_debug_config_write,
+};
+
+int
+ldp_vty_debug(struct vty *vty, const char *negate, const char *type_str,
+ const char *dir_str, const char *all)
+{
+ if (type_str == NULL)
+ return (CMD_WARNING_CONFIG_FAILED);
+
+ if (strcmp(type_str, "discovery") == 0) {
+ if (dir_str == NULL)
+ return (CMD_WARNING_CONFIG_FAILED);
+
+ if (dir_str[0] == 'r') {
+ if (negate)
+ DEBUG_OFF(hello, LDP_DEBUG_HELLO_RECV);
+ else
+ DEBUG_ON(hello, LDP_DEBUG_HELLO_RECV);
+ } else {
+ if (negate)
+ DEBUG_OFF(hello, LDP_DEBUG_HELLO_SEND);
+ else
+ DEBUG_ON(hello, LDP_DEBUG_HELLO_SEND);
+ }
+ } else if (strcmp(type_str, "errors") == 0) {
+ if (negate)
+ DEBUG_OFF(errors, LDP_DEBUG_ERRORS);
+ else
+ DEBUG_ON(errors, LDP_DEBUG_ERRORS);
+ } else if (strcmp(type_str, "event") == 0) {
+ if (negate)
+ DEBUG_OFF(event, LDP_DEBUG_EVENT);
+ else
+ DEBUG_ON(event, LDP_DEBUG_EVENT);
+ } else if (strcmp(type_str, "labels") == 0) {
+ if (negate)
+ DEBUG_OFF(labels, LDP_DEBUG_LABELS);
+ else
+ DEBUG_ON(labels, LDP_DEBUG_LABELS);
+ } else if (strcmp(type_str, "messages") == 0) {
+ if (dir_str == NULL)
+ return (CMD_WARNING_CONFIG_FAILED);
+
+ if (dir_str[0] == 'r') {
+ if (negate) {
+ DEBUG_OFF(msg, LDP_DEBUG_MSG_RECV);
+ DEBUG_OFF(msg, LDP_DEBUG_MSG_RECV_ALL);
+ } else {
+ DEBUG_ON(msg, LDP_DEBUG_MSG_RECV);
+ if (all)
+ DEBUG_ON(msg, LDP_DEBUG_MSG_RECV_ALL);
+ }
+ } else {
+ if (negate) {
+ DEBUG_OFF(msg, LDP_DEBUG_MSG_SEND);
+ DEBUG_OFF(msg, LDP_DEBUG_MSG_SEND_ALL);
+ } else {
+ DEBUG_ON(msg, LDP_DEBUG_MSG_SEND);
+ if (all)
+ DEBUG_ON(msg, LDP_DEBUG_MSG_SEND_ALL);
+ }
+ }
+ } else if (strcmp(type_str, "sync") == 0) {
+ if (negate)
+ DEBUG_OFF(sync, LDP_DEBUG_SYNC);
+ else
+ DEBUG_ON(sync, LDP_DEBUG_SYNC);
+ } else if (strcmp(type_str, "zebra") == 0) {
+ if (negate)
+ DEBUG_OFF(zebra, LDP_DEBUG_ZEBRA);
+ else
+ DEBUG_ON(zebra, LDP_DEBUG_ZEBRA);
+ }
+
+ main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, sizeof(ldp_debug));
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_show_debugging(struct vty *vty)
+{
+ vty_out (vty, "LDP debugging status:\n");
+
+ if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV))
+ vty_out (vty," LDP discovery debugging is on (inbound)\n");
+ if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND))
+ vty_out (vty," LDP discovery debugging is on (outbound)\n");
+ if (LDP_DEBUG(errors, LDP_DEBUG_ERRORS))
+ vty_out (vty, " LDP errors debugging is on\n");
+ if (LDP_DEBUG(event, LDP_DEBUG_EVENT))
+ vty_out (vty, " LDP events debugging is on\n");
+ if (LDP_DEBUG(labels, LDP_DEBUG_LABELS))
+ vty_out (vty, " LDP labels debugging is on\n");
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL))
+ vty_out (vty, " LDP detailed messages debugging is on (inbound)\n");
+ else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV))
+ vty_out (vty," LDP messages debugging is on (inbound)\n");
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL))
+ vty_out (vty, " LDP detailed messages debugging is on (outbound)\n");
+ else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND))
+ vty_out (vty," LDP messages debugging is on (outbound)\n");
+ if (LDP_DEBUG(sync, LDP_DEBUG_SYNC))
+ vty_out (vty, " LDP sync debugging is on\n");
+ if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA))
+ vty_out (vty, " LDP zebra debugging is on\n");
+ vty_out (vty, "\n");
+
+ return (CMD_SUCCESS);
+}
+
+static int
+ldp_debug_config_write(struct vty *vty)
+{
+ int write = 0;
+
+ if (CONF_LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV)) {
+ vty_out (vty,"debug mpls ldp discovery hello recv\n");
+ write = 1;
+ }
+
+ if (CONF_LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND)) {
+ vty_out (vty,"debug mpls ldp discovery hello sent\n");
+ write = 1;
+ }
+
+ if (CONF_LDP_DEBUG(errors, LDP_DEBUG_ERRORS)) {
+ vty_out (vty, "debug mpls ldp errors\n");
+ write = 1;
+ }
+
+ if (CONF_LDP_DEBUG(event, LDP_DEBUG_EVENT)) {
+ vty_out (vty, "debug mpls ldp event\n");
+ write = 1;
+ }
+
+ if (CONF_LDP_DEBUG(labels, LDP_DEBUG_LABELS)) {
+ vty_out (vty, "debug mpls ldp labels\n");
+ write = 1;
+ }
+
+ if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL)) {
+ vty_out (vty, "debug mpls ldp messages recv all\n");
+ write = 1;
+ } else if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV)) {
+ vty_out (vty, "debug mpls ldp messages recv\n");
+ write = 1;
+ }
+
+ if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL)) {
+ vty_out (vty, "debug mpls ldp messages sent all\n");
+ write = 1;
+ } else if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) {
+ vty_out (vty, "debug mpls ldp messages sent\n");
+ write = 1;
+ }
+
+ if (CONF_LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) {
+ vty_out (vty, "debug mpls ldp zebra\n");
+ write = 1;
+ }
+
+ if (CONF_LDP_DEBUG(sync, LDP_DEBUG_SYNC)) {
+ vty_out (vty, "debug mpls ldp sync\n");
+ write = 1;
+ }
+
+ return (write);
+}
diff --git a/ldpd/ldp_debug.h b/ldpd/ldp_debug.h
new file mode 100644
index 0000000..09fa711
--- /dev/null
+++ b/ldpd/ldp_debug.h
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 by Open Source Routing.
+ */
+
+#ifndef _LDP_DEBUG_H_
+#define _LDP_DEBUG_H_
+
+struct ldp_debug {
+ int hello;
+#define LDP_DEBUG_HELLO_RECV 0x01
+#define LDP_DEBUG_HELLO_SEND 0x02
+
+ int errors;
+#define LDP_DEBUG_ERRORS 0x01
+
+ int event;
+#define LDP_DEBUG_EVENT 0x01
+
+ int labels;
+#define LDP_DEBUG_LABELS 0x01
+
+ int msg;
+#define LDP_DEBUG_MSG_RECV 0x01
+#define LDP_DEBUG_MSG_RECV_ALL 0x02
+#define LDP_DEBUG_MSG_SEND 0x04
+#define LDP_DEBUG_MSG_SEND_ALL 0x08
+
+ int zebra;
+#define LDP_DEBUG_ZEBRA 0x01
+
+ int sync;
+#define LDP_DEBUG_SYNC 0x01
+
+};
+extern struct ldp_debug conf_ldp_debug;
+extern struct ldp_debug ldp_debug;
+
+#define CONF_DEBUG_ON(a, b) (conf_ldp_debug.a |= (b))
+#define CONF_DEBUG_OFF(a, b) (conf_ldp_debug.a &= ~(b))
+
+#define TERM_DEBUG_ON(a, b) (ldp_debug.a |= (b))
+#define TERM_DEBUG_OFF(a, b) (ldp_debug.a &= ~(b))
+
+#define DEBUG_ON(a, b) \
+ do { \
+ if (vty->node == CONFIG_NODE) { \
+ CONF_DEBUG_ON(a, b); \
+ TERM_DEBUG_ON(a, b); \
+ } else \
+ TERM_DEBUG_ON(a, b); \
+ } while (0)
+#define DEBUG_OFF(a, b) \
+ do { \
+ CONF_DEBUG_OFF(a, b); \
+ TERM_DEBUG_OFF(a, b); \
+ } while (0)
+
+#define LDP_DEBUG(a, b) (ldp_debug.a & b)
+#define CONF_LDP_DEBUG(a, b) (conf_ldp_debug.a & b)
+
+#define debug_hello_recv(emsg, ...) \
+do { \
+ if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV)) \
+ log_debug("discovery[recv]: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_hello_send(emsg, ...) \
+do { \
+ if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND)) \
+ log_debug("discovery[send]: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_err(emsg, ...) \
+do { \
+ if (LDP_DEBUG(errors, LDP_DEBUG_ERRORS)) \
+ log_debug("error: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_evt(emsg, ...) \
+do { \
+ if (LDP_DEBUG(event, LDP_DEBUG_EVENT)) \
+ log_debug("event: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_labels(emsg, ...) \
+do { \
+ if (LDP_DEBUG(labels, LDP_DEBUG_LABELS)) \
+ log_debug("labels: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_msg_recv(emsg, ...) \
+do { \
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV)) \
+ log_debug("msg[in]: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_msg_send(emsg, ...) \
+do { \
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) \
+ log_debug("msg[out]: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_msg(out, emsg, ...) \
+do { \
+ if (out) \
+ debug_msg_send(emsg, __VA_ARGS__); \
+ else \
+ debug_msg_recv(emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_kalive_recv(emsg, ...) \
+do { \
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL)) \
+ log_debug("kalive[in]: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_kalive_send(emsg, ...) \
+do { \
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL)) \
+ log_debug("kalive[out]: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_zebra_in(emsg, ...) \
+do { \
+ if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) \
+ log_debug("zebra[in]: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_zebra_out(emsg, ...) \
+do { \
+ if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) \
+ log_debug("zebra[out]: " emsg, __VA_ARGS__); \
+} while (0)
+
+#define debug_evt_ldp_sync(emsg, ...) \
+do { \
+ if (LDP_DEBUG(sync, LDP_DEBUG_SYNC)) \
+ log_debug("sync: " emsg, __VA_ARGS__); \
+} while (0)
+
+#endif /* _LDP_DEBUG_H_ */
diff --git a/ldpd/ldp_snmp.c b/ldpd/ldp_snmp.c
new file mode 100644
index 0000000..ed391ac
--- /dev/null
+++ b/ldpd/ldp_snmp.c
@@ -0,0 +1,1224 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LDP SNMP support
+ * Copyright (C) 2020 Volta Networks, Inc.
+ */
+
+/*
+ * This is minimal read-only implementations providing
+ * mplsLdpModuleReadOnlyCompliance as described in RFC 3815.
+ */
+
+#include <zebra.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "vrf.h"
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "memory.h"
+#include "smux.h"
+#include "libfrr.h"
+#include "lib/version.h"
+#include "ldpd.h"
+#include "ldpe.h"
+
+/* SNMP value hack. */
+#define COUNTER32 ASN_COUNTER
+#define INTEGER ASN_INTEGER
+#define UNSIGNED32 ASN_GAUGE
+#define TIMESTAMP ASN_TIMETICKS
+#define TIMETICKS ASN_TIMETICKS
+#define STRING ASN_OCTET_STR
+#define IPADDRESS ASN_IPADDRESS
+
+#define LDP_LSRID_IDX_LEN 6
+#define LDP_ENTITY_IDX_LEN 1
+#define LDP_ADJACENCY_IDX_LEN 1
+
+/* MPLS-LDP-STD-MIB. */
+#define MPLS_LDP_STD_MIB 1, 3, 6, 1, 2, 1, 10, 166, 4
+
+#define MPLS_LDP_LSR_ID 0
+#define MPLS_LDP_LSR_LOOP_DETECTION_CAPABLE 0
+#define MPLS_LDP_ENTITY_LAST_CHANGE 0
+#define MPLS_LDP_ENTITY_INDEX_NEXT 0
+
+/* Declare static local variables for convenience. */
+SNMP_LOCAL_VARIABLES
+
+/* LDP-MIB instances. */
+static oid ldp_oid[] = {MPLS_LDP_STD_MIB};
+static oid ldp_trap_oid[] = {MPLS_LDP_STD_MIB, 0};
+
+static uint8_t snmp_ldp_rtrid[6] = {0, 0, 0, 0, 0};
+
+#define LDP_DEFAULT_ENTITY_INDEX 1
+
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_NONE 1
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_OTHER 2
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_HOPCOUNT 3
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_PATHVECTOR 4
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_HOPCOUNTANDPATHVECTOR 5
+
+/* MPLS LDP mplsLdpHelloAdjacencyTable. */
+#define MPLSLDPHELLOADJACENCYINDEX 1
+#define MPLSLDPHELLOADJACENCYHOLDTIMEREM 2
+#define MPLSLDPHELLOADJACENCYHOLDTIME 3
+#define MPLSLDPHELLOADJACENCYTYPE 4
+
+/* enums for column mplsLdpHelloAdjacencyType */
+#define MPLSLDPHELLOADJACENCYTYPE_LINK 1
+#define MPLSLDPHELLOADJACENCYTYPE_TARGETED 2
+
+#define MPLSLDPPEERTRANSPORTADDRTYPE_UNKNOWN 0
+#define MPLSLDPPEERTRANSPORTADDRTYPE_IPV4 1
+#define MPLSLDPPEERTRANSPORTADDRTYPE_IPV6 2
+#define MPLSLDPPEERTRANSPORTADDRTYPE_IPV4Z 3
+#define MPLSLDPPEERTRANSPORTADDRTYPE_IPV6Z 4
+#define MPLSLDPPEERTRANSPORTADDRTYPE_DNS 16
+
+#define DOWNSTREAMONDEMAND 1
+#define DOWNSTREAMUNSOLICITED 2
+
+#define CONSERVATIVERETENTION 1
+#define LIBERALRETENTION 2
+
+#define TRANSPORTADDRINTERFACE 1
+#define TRANSPORTADDRLOOPBACK 2
+
+#define LABELTYPEGENERIC 1
+
+#define STORAGETYPENONVOLATILE 3
+
+#define ROWSTATUSACTIVE 4
+
+#define ADMINSTATUSENABLED 1
+
+#define OPERSTATUSENABLED 2
+
+/* MPLS LDP mplsLdpPeerTable */
+#define MPLSLDPPEERLDPID 1
+#define MPLSLDPPEERLABELDISTMETHOD 2
+#define MPLSLDPPEERPATHVECTORLIMIT 3
+#define MPLSLDPPEERTRANSPORTADDRTYPE 4
+#define MPLSLDPPEERTRANSPORTADDR 5
+
+#define MPLSLDPSESSIONROLE_UNKNOWN 1
+#define MPLSLDPSESSIONROLE_ACTIVE 2
+#define MPLSLDPSESSIONROLE_PASSIVE 3
+
+#define MPLSLDPSESSIONSTATE_NONEXISTENT 1
+#define MPLSLDPSESSIONSTATE_INITIALIZED 2
+#define MPLSLDPSESSIONSTATE_OPENREC 3
+#define MPLSLDPSESSIONSTATE_OPENSENT 4
+#define MPLSLDPSESSIONSTATE_OPERATIONAL 5
+
+/* MPLS LDP mplsLdpSessionTable */
+#define MPLSLDPSESSIONSTATELASTCHANGE 1
+#define MPLSLDPSESSIONSTATE 2
+#define MPLSLDPSESSIONROLE 3
+#define MPLSLDPSESSIONPROTOCOLVERSION 4
+#define MPLSLDPSESSIONKEEPALIVEHOLDTIMEREM 5
+#define MPLSLDPSESSIONKEEPALIVETIME 6
+#define MPLSLDPSESSIONMAXPDULENGTH 7
+#define MPLSLDPSESSIONDISCONTINUITYTIME 8
+
+/* MPLS LDP mplsLdpEntityTable */
+#define MPLSLDPENTITYLDPID 1
+#define MPLSLDPENTITYINDEX 2
+#define MPLSLDPENTITYPROTOCOLVERSION 3
+#define MPLSLDPENTITYADMINSTATUS 4
+#define MPLSLDPENTITYOPERSTATUS 5
+#define MPLSLDPENTITYTCPPORT 6
+#define MPLSLDPENTITYUDPDSCPORT 7
+#define MPLSLDPENTITYMAXPDULENGTH 8
+#define MPLSLDPENTITYKEEPALIVEHOLDTIMER 9
+#define MPLSLDPENTITYHELLOHOLDTIMER 10
+#define MPLSLDPENTITYINITSESSIONTHRESHOLD 11
+#define MPLSLDPENTITYLABELDISTMETHOD 12
+#define MPLSLDPENTITYLABELRETENTIONMODE 13
+#define MPLSLDPENTITYPATHVECTORLIMIT 14
+#define MPLSLDPENTITYHOPCOUNTLIMIT 15
+#define MPLSLDPENTITYTRANSPORTADDRKIND 16
+#define MPLSLDPENTITYTARGETPEER 17
+#define MPLSLDPENTITYTARGETPEERADDRTYPE 18
+#define MPLSLDPENTITYTARGETPEERADDR 19
+#define MPLSLDPENTITYLABELTYPE 20
+#define MPLSLDPENTITYDISCONTINUITYTIME 21
+#define MPLSLDPENTITYSTORAGETYPE 22
+#define MPLSLDPENTITYROWSTATUS 23
+
+/* MPLS LDP mplsLdpEntityStatsTable */
+#define MPLSLDPENTITYSTATSSESSIONATTEMPTS 1
+#define MPLSLDPENTITYSTATSSESSIONREJHELLO 2
+#define MPLSLDPENTITYSTATSSESSIONREJAD 3
+#define MPLSLDPENTITYSTATSSESSIONREJMAXPDU 4
+#define MPLSLDPENTITYSTATSSESSIONREJLR 5
+#define MPLSLDPENTITYSTATSBADLDPID 6
+#define MPLSLDPENTITYSTATSBADPDULENGTH 7
+#define MPLSLDPENTITYSTATSBADMSGLENGTH 8
+#define MPLSLDPENTITYSTATSBADTLVLENGTH 9
+#define MPLSLDPENTITYSTATSMALFORMEDTLV 10
+#define MPLSLDPENTITYSTATSKEEPALIVEEXP 11
+#define MPLSLDPENTITYSTATSSHUTDOWNRCVNOTIFY 12
+#define MPLSLDPENTITYSTATSSHUTDOWNSENTNOTIFY 13
+
+#define MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS 1
+#define MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS 2
+
+static uint8_t *ldpLsrId(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ *var_len = 4;
+ return (uint8_t *)&leconf->rtr_id.s_addr;
+}
+
+static uint8_t *ldpLoopDetectCap(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ return SNMP_INTEGER(MPLSLDPLSRLOOPDETECTIONCAPABLE_NONE);
+}
+
+extern uint32_t ldp_start_time;
+static uint8_t *ldpEntityLastChange(struct variable *v, oid name[],
+ size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ *var_len = sizeof(time_t);
+ return (uint8_t *) &(leconf->config_change_time);
+
+}
+
+static uint8_t *ldpEntityIndexNext(struct variable *v, oid name[],
+ size_t *length,int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ return SNMP_INTEGER(0);
+}
+
+#define LDP_ENTITY_TOTAL_LEN 21
+#define LDP_ENTITY_MAX_IDX_LEN 6
+
+static struct ldpd_af_conf *ldpEntityTable_lookup(struct variable *v, oid *name,
+ size_t *length, int exact,
+ uint32_t *index)
+{
+ int len;
+ struct ldpd_af_conf *af_v4, *af_v6;
+
+ af_v4 = &leconf->ipv4;
+ af_v6 = &leconf->ipv6;
+
+ if (exact) {
+ if (*length != LDP_ENTITY_TOTAL_LEN)
+ return NULL;
+
+ if (leconf->trans_pref == DUAL_STACK_LDPOV6 &&
+ af_v6->flags & F_LDPD_AF_ENABLED) {
+ *index = 2;
+ return af_v6;
+ } else {
+ *index = 1;
+ return af_v4;
+ }
+ } else {
+ /* only support one router id so can just skip */
+ len = *length - v->namelen - LDP_ENTITY_MAX_IDX_LEN;
+ if (len <= 0) {
+ if (leconf->trans_pref == DUAL_STACK_LDPOV6 &&
+ af_v6->flags & F_LDPD_AF_ENABLED) {
+ *index = 2;
+ return af_v6;
+ } else {
+ *index = 1;
+ return af_v4;
+ }
+ }
+ }
+ return NULL;
+}
+
+static uint8_t *ldpEntityTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct ldpd_af_conf *af;
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t index = 0;
+
+ *write_method = NULL;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ af = ldpEntityTable_lookup(v, name, length, exact, &index);
+ if (af == NULL)
+ return NULL;
+
+ if (!exact) {
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ *length = LDP_ENTITY_TOTAL_LEN;
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
+ }
+
+ /* Return the current value of the variable */
+ switch (v->magic) {
+ case MPLSLDPENTITYLDPID:
+ *var_len = 6;
+ memcpy (snmp_ldp_rtrid, &entityLdpId, IN_ADDR_SIZE);
+ return (uint8_t *)snmp_ldp_rtrid;
+ case MPLSLDPENTITYINDEX:
+ return SNMP_INTEGER(LDP_DEFAULT_ENTITY_INDEX);
+ case MPLSLDPENTITYPROTOCOLVERSION:
+ return SNMP_INTEGER(LDP_VERSION);
+ case MPLSLDPENTITYADMINSTATUS:
+ return SNMP_INTEGER(ADMINSTATUSENABLED);
+ case MPLSLDPENTITYOPERSTATUS:
+ return SNMP_INTEGER(OPERSTATUSENABLED);
+ case MPLSLDPENTITYTCPPORT:
+ return SNMP_INTEGER(LDP_PORT);
+ case MPLSLDPENTITYUDPDSCPORT:
+ return SNMP_INTEGER(LDP_PORT);
+ case MPLSLDPENTITYMAXPDULENGTH:
+ return SNMP_INTEGER(LDP_MAX_LEN);
+ case MPLSLDPENTITYKEEPALIVEHOLDTIMER:
+ return SNMP_INTEGER(af->keepalive);
+ case MPLSLDPENTITYHELLOHOLDTIMER:
+ return SNMP_INTEGER(af->lhello_holdtime);
+ case MPLSLDPENTITYINITSESSIONTHRESHOLD:
+ return SNMP_INTEGER(0); /* not supported */
+ case MPLSLDPENTITYLABELDISTMETHOD:
+ return SNMP_INTEGER(DOWNSTREAMUNSOLICITED);
+ case MPLSLDPENTITYLABELRETENTIONMODE:
+ return SNMP_INTEGER(LIBERALRETENTION);
+ case MPLSLDPENTITYPATHVECTORLIMIT:
+ return SNMP_INTEGER(0); /* not supported */
+ case MPLSLDPENTITYHOPCOUNTLIMIT:
+ return SNMP_INTEGER(0);
+ case MPLSLDPENTITYTRANSPORTADDRKIND:
+ return SNMP_INTEGER(TRANSPORTADDRLOOPBACK);
+ case MPLSLDPENTITYTARGETPEER:
+ return SNMP_INTEGER(1);
+ case MPLSLDPENTITYTARGETPEERADDRTYPE:
+ if (index == 1)
+ return SNMP_INTEGER(MPLSLDPPEERTRANSPORTADDRTYPE_IPV4);
+ else
+ return SNMP_INTEGER(MPLSLDPPEERTRANSPORTADDRTYPE_IPV6);
+ case MPLSLDPENTITYTARGETPEERADDR:
+ if (index == 1) {
+ *var_len = sizeof(af->trans_addr.v4);
+ return ((uint8_t *)&af->trans_addr.v4);
+ }else {
+ *var_len = sizeof(af->trans_addr.v6);
+ return ((uint8_t *)&af->trans_addr.v6);
+ }
+ case MPLSLDPENTITYLABELTYPE:
+ return SNMP_INTEGER(LABELTYPEGENERIC);
+ case MPLSLDPENTITYDISCONTINUITYTIME:
+ return SNMP_INTEGER(0);
+ case MPLSLDPENTITYSTORAGETYPE:
+ return SNMP_INTEGER(STORAGETYPENONVOLATILE);
+ case MPLSLDPENTITYROWSTATUS:
+ return SNMP_INTEGER(ROWSTATUSACTIVE);
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static uint8_t *ldpEntityStatsTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ int len;
+
+ *write_method = NULL;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ if (exact) {
+ if (*length != LDP_ENTITY_TOTAL_LEN)
+ return NULL;
+ } else {
+ len = *length - v->namelen - LDP_ENTITY_MAX_IDX_LEN;
+ if (len > 0)
+ return NULL;
+
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ *length = LDP_ENTITY_TOTAL_LEN;
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
+ }
+
+ /* Return the current value of the variable */
+ switch (v->magic) {
+ case MPLSLDPENTITYSTATSSESSIONATTEMPTS:
+ return SNMP_INTEGER(leconf->stats.session_attempts);
+ case MPLSLDPENTITYSTATSSESSIONREJHELLO:
+ return SNMP_INTEGER(leconf->stats.session_rejects_hello);
+ case MPLSLDPENTITYSTATSSESSIONREJAD:
+ return SNMP_INTEGER(leconf->stats.session_rejects_ad);
+ case MPLSLDPENTITYSTATSSESSIONREJMAXPDU:
+ return SNMP_INTEGER(leconf->stats.session_rejects_max_pdu);
+ case MPLSLDPENTITYSTATSSESSIONREJLR:
+ return SNMP_INTEGER(leconf->stats.session_rejects_lr);
+ case MPLSLDPENTITYSTATSBADLDPID:
+ return SNMP_INTEGER(leconf->stats.bad_ldp_id);
+ case MPLSLDPENTITYSTATSBADPDULENGTH:
+ return SNMP_INTEGER(leconf->stats.bad_pdu_len);
+ case MPLSLDPENTITYSTATSBADMSGLENGTH:
+ return SNMP_INTEGER(leconf->stats.bad_msg_len);
+ case MPLSLDPENTITYSTATSBADTLVLENGTH:
+ return SNMP_INTEGER(leconf->stats.bad_tlv_len);
+ case MPLSLDPENTITYSTATSMALFORMEDTLV:
+ return SNMP_INTEGER(leconf->stats.malformed_tlv);
+ case MPLSLDPENTITYSTATSKEEPALIVEEXP:
+ return SNMP_INTEGER(leconf->stats.keepalive_timer_exp);
+ case MPLSLDPENTITYSTATSSHUTDOWNRCVNOTIFY:
+ return SNMP_INTEGER(leconf->stats.shutdown_rcv_notify);
+ case MPLSLDPENTITYSTATSSHUTDOWNSENTNOTIFY:
+ return SNMP_INTEGER(leconf->stats.shutdown_send_notify);
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+#define LDP_ADJACENCY_ENTRY_MAX_IDX_LEN 14
+
+static void ldpHelloAdjacencyTable_oid_to_index(
+ struct variable *v, oid name[],
+ size_t *length,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId,
+ uint32_t *adjacencyIndex)
+{
+ oid *offset = name + v->namelen;
+ int offsetlen = *length - v->namelen;
+ int len = offsetlen;
+
+ if (len > LDP_ADJACENCY_ENTRY_MAX_IDX_LEN)
+ len = LDP_ADJACENCY_ENTRY_MAX_IDX_LEN;
+
+ if (len >= LDP_LSRID_IDX_LEN)
+ oid2in_addr(offset, sizeof(struct in_addr), entityLdpId);
+
+ offset += LDP_LSRID_IDX_LEN;
+ offsetlen -= LDP_LSRID_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_ENTITY_IDX_LEN)
+ len = LDP_ENTITY_IDX_LEN;
+
+ if (len >= LDP_ENTITY_IDX_LEN)
+ *entityIndex = offset[0];
+
+ offset += LDP_ENTITY_IDX_LEN;
+ offsetlen -= LDP_ENTITY_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_LSRID_IDX_LEN)
+ len = LDP_LSRID_IDX_LEN;
+
+ if (len >= LDP_LSRID_IDX_LEN)
+ oid2in_addr(offset, sizeof(struct in_addr), peerLdpId);
+
+ offset += LDP_LSRID_IDX_LEN;
+ offsetlen -= LDP_LSRID_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_ADJACENCY_IDX_LEN)
+ len = LDP_ADJACENCY_IDX_LEN;
+
+ if (len >= LDP_ADJACENCY_IDX_LEN)
+ *adjacencyIndex = offset[0];
+}
+
+static struct adj *
+nbr_get_adj_by_index(struct nbr *nbr, uint32_t adjacencyIndex)
+{
+ struct adj *adj;
+ uint32_t i = 0;
+
+ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree)
+ if (++i == adjacencyIndex)
+ return adj;
+
+ return NULL;
+}
+
+static struct ctl_adj *
+ldpHelloAdjacencyTable_lookup_helper(
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId,
+ uint32_t *adjacencyIndex)
+{
+ struct ctl_adj *ctl_adj = NULL;
+ struct adj *adj = NULL;
+ struct nbr *cur_nbr = nbr_find_ldpid(peerLdpId->s_addr);
+
+ if (cur_nbr)
+ /* If found nbr, then look to see if the
+ * adjacency exists
+ */
+ adj = nbr_get_adj_by_index(cur_nbr, *adjacencyIndex);
+
+ if (adj)
+ ctl_adj = adj_to_ctl(adj);
+
+ return ctl_adj;
+}
+
+static struct ctl_adj *
+ldpHelloAdjacencyTable_next_helper(
+ int first,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId,
+ uint32_t *adjacencyIndex)
+{
+ struct ctl_adj *ctl_adj = NULL;
+ struct nbr *nbr = NULL;
+ struct adj *adj = NULL;
+
+ if (first)
+ nbr = nbr_get_first_ldpid();
+ else {
+ struct nbr *cur_nbr = nbr_find_ldpid(peerLdpId->s_addr);
+ if (cur_nbr)
+ /* If found nbr, then look to see if the
+ * adjacency exists
+ */
+ adj = nbr_get_adj_by_index(cur_nbr, *adjacencyIndex + 1);
+ if (adj)
+ *adjacencyIndex += 1;
+ else
+ nbr = nbr_get_next_ldpid(peerLdpId->s_addr);
+ }
+
+ if (!adj && nbr) {
+ adj = RB_MIN(nbr_adj_head, &nbr->adj_tree);
+ *adjacencyIndex = 1;
+ }
+
+ if (adj)
+ ctl_adj = adj_to_ctl(adj);
+
+ return ctl_adj;
+}
+
+#define HELLO_ADJ_MAX_IDX_LEN 14
+
+static struct ctl_adj *
+ldpHelloAdjacencyTable_lookup(struct variable *v, oid name[],
+ size_t *length, int exact,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId,
+ uint32_t *adjacencyIndex)
+{
+ struct ctl_adj *hello_adj = NULL;
+
+ if (exact) {
+ if (*length < HELLO_ADJ_MAX_IDX_LEN)
+ return NULL;
+
+ ldpHelloAdjacencyTable_oid_to_index(
+ v, name, length,
+ entityLdpId, entityIndex, peerLdpId, adjacencyIndex);
+
+ hello_adj = ldpHelloAdjacencyTable_lookup_helper(
+ entityLdpId, entityIndex, peerLdpId, adjacencyIndex);
+ } else {
+ int first = 0;
+ int offsetlen = *length - v->namelen;
+
+ if (offsetlen < HELLO_ADJ_MAX_IDX_LEN)
+ first = 1;
+
+ ldpHelloAdjacencyTable_oid_to_index(
+ v, name, length,
+ entityLdpId, entityIndex, peerLdpId, adjacencyIndex);
+
+ hello_adj = ldpHelloAdjacencyTable_next_helper(first,
+ entityLdpId, entityIndex, peerLdpId, adjacencyIndex);
+
+ }
+ return hello_adj;
+}
+
+static uint8_t *ldpHelloAdjacencyTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+ uint32_t adjacencyIndex = 0;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ struct ctl_adj *ctl_adj = ldpHelloAdjacencyTable_lookup(v, name,
+ length, exact,
+ &entityLdpId, &entityIndex, &peerLdpId, &adjacencyIndex);
+
+ if (!ctl_adj)
+ return NULL;
+
+ if (!exact) {
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ struct in_addr entityLdpId = {.s_addr = 0};
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+
+ struct in_addr peerLdpId = ctl_adj->id;
+
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
+ oid_copy_in_addr(name + v->namelen + 7, &peerLdpId);
+ name[v->namelen + 11] = 0;
+ name[v->namelen + 12] = 0;
+ name[v->namelen + 13] = adjacencyIndex;
+
+ /* Set length */
+ *length = v->namelen + HELLO_ADJ_MAX_IDX_LEN;
+ }
+
+ switch (v->magic) {
+ case MPLSLDPHELLOADJACENCYINDEX:
+ return SNMP_INTEGER(adjacencyIndex);
+ case MPLSLDPHELLOADJACENCYHOLDTIMEREM:
+ return SNMP_INTEGER(ctl_adj->holdtime_remaining);
+ case MPLSLDPHELLOADJACENCYHOLDTIME:
+ return SNMP_INTEGER(ctl_adj->holdtime);
+ case MPLSLDPHELLOADJACENCYTYPE:
+ if (ctl_adj->type == HELLO_LINK)
+ return SNMP_INTEGER(MPLSLDPHELLOADJACENCYTYPE_LINK);
+ return SNMP_INTEGER(MPLSLDPHELLOADJACENCYTYPE_TARGETED);
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+#define LDP_LSRID_IDX_LEN 6
+#define LDP_ENTITY_IDX_LEN 1
+#define LDP_PEER_ENTRY_MAX_IDX_LEN 13
+
+static void ldpPeerTable_oid_to_index(
+ struct variable *v, oid name[],
+ size_t *length,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId)
+{
+ oid *offset = name + v->namelen;
+ int offsetlen = *length - v->namelen;
+ int len = offsetlen;
+
+ if (len > LDP_PEER_ENTRY_MAX_IDX_LEN)
+ len = LDP_PEER_ENTRY_MAX_IDX_LEN;
+
+ if (len >= LDP_LSRID_IDX_LEN)
+ oid2in_addr(offset, sizeof(struct in_addr), entityLdpId);
+
+ offset += LDP_LSRID_IDX_LEN;
+ offsetlen -= LDP_LSRID_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_ENTITY_IDX_LEN)
+ len = LDP_ENTITY_IDX_LEN;
+
+ if (len >= LDP_ENTITY_IDX_LEN)
+ *entityIndex = offset[0];
+
+ offset += LDP_ENTITY_IDX_LEN;
+ offsetlen -= LDP_ENTITY_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_LSRID_IDX_LEN)
+ len = LDP_LSRID_IDX_LEN;
+
+ if (len >= LDP_LSRID_IDX_LEN)
+ oid2in_addr(offset, sizeof(struct in_addr), peerLdpId);
+}
+
+static struct ctl_nbr *
+ldpPeerTable_lookup_next(int first,
+ struct in_addr peerLdpId)
+{
+ struct nbr *nbr = NULL;
+ struct ctl_nbr *ctl_nbr = NULL;;
+
+ if (first)
+ nbr = nbr_get_first_ldpid();
+ else
+ nbr = nbr_get_next_ldpid(peerLdpId.s_addr);
+
+ if (nbr)
+ ctl_nbr = nbr_to_ctl(nbr);
+
+ return ctl_nbr;
+}
+
+static struct ctl_nbr *
+ldpPeerTable_lookup(struct variable *v, oid name[],
+ size_t *length, int exact,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId)
+{
+ struct ctl_nbr *ctl_nbr = NULL;
+ struct nbr *nbr = NULL;
+ int first = 0;
+
+ if (exact) {
+ if (*length < (long unsigned int)v->namelen
+ + LDP_PEER_ENTRY_MAX_IDX_LEN)
+ return NULL;
+
+ ldpPeerTable_oid_to_index(
+ v, name, length,
+ entityLdpId, entityIndex, peerLdpId);
+
+ nbr = nbr_find_ldpid(peerLdpId->s_addr);
+ if (nbr)
+ ctl_nbr = nbr_to_ctl(nbr);
+
+ return ctl_nbr;
+ } else {
+
+ int offsetlen = *length - v->namelen;
+ if (offsetlen < LDP_LSRID_IDX_LEN)
+ first = 1;
+
+ ldpPeerTable_oid_to_index(
+ v, name, length,
+ entityLdpId, entityIndex, peerLdpId);
+
+ ctl_nbr = ldpPeerTable_lookup_next(first, *peerLdpId);
+ return ctl_nbr;
+ }
+ return NULL;
+}
+
+static uint8_t *ldpPeerTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+ struct ctl_nbr *ctl_nbr;
+
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ ctl_nbr = ldpPeerTable_lookup(v, name, length, exact, &entityLdpId,
+ &entityIndex, &peerLdpId);
+
+ if (!ctl_nbr)
+ return NULL;
+
+ if (!exact) {
+
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+ entityIndex = LDP_DEFAULT_ENTITY_INDEX;
+ peerLdpId = ctl_nbr->id;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
+
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = entityIndex;
+ oid_copy_in_addr(name + v->namelen + 7, &peerLdpId);
+ name[v->namelen + 11] = 0;
+ name[v->namelen + 12] = 0;
+
+ /* Set length */
+ *length = v->namelen + LDP_PEER_ENTRY_MAX_IDX_LEN;
+ }
+
+ switch (v->magic) {
+ case MPLSLDPPEERLDPID:
+ *var_len = 6;
+ memcpy(snmp_ldp_rtrid, &ctl_nbr->id, IN_ADDR_SIZE);
+ return snmp_ldp_rtrid;
+ case MPLSLDPPEERLABELDISTMETHOD:
+ return SNMP_INTEGER(DOWNSTREAMUNSOLICITED);
+ case MPLSLDPPEERPATHVECTORLIMIT:
+ return SNMP_INTEGER(0);
+ case MPLSLDPPEERTRANSPORTADDRTYPE:
+ if (ctl_nbr->af == AF_INET)
+ return SNMP_INTEGER(MPLSLDPPEERTRANSPORTADDRTYPE_IPV4);
+ else
+ return SNMP_INTEGER(MPLSLDPPEERTRANSPORTADDRTYPE_IPV6);
+ case MPLSLDPPEERTRANSPORTADDR:
+ if (ctl_nbr->af == AF_INET) {
+ *var_len = sizeof(ctl_nbr->raddr.v4);
+ return ((uint8_t *)&ctl_nbr->raddr.v4);
+ } else {
+ *var_len = sizeof(ctl_nbr->raddr.v6);
+ return ((uint8_t *)&ctl_nbr->raddr.v6);
+ }
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+static uint8_t *ldpSessionTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+ struct ctl_nbr *ctl_nbr;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ ctl_nbr = ldpPeerTable_lookup(v, name, length, exact, &entityLdpId,
+ &entityIndex, &peerLdpId);
+
+ if (!ctl_nbr)
+ return NULL;
+
+ if (!exact) {
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+ entityIndex = LDP_DEFAULT_ENTITY_INDEX;
+ peerLdpId = ctl_nbr->id;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
+
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = entityIndex;
+ oid_copy_in_addr(name + v->namelen + 7, &peerLdpId);
+ name[v->namelen + 11] = 0;
+ name[v->namelen + 12] = 0;
+
+ /* Set length */
+ *length = v->namelen + LDP_PEER_ENTRY_MAX_IDX_LEN;
+ }
+
+ switch (v->magic) {
+ case MPLSLDPSESSIONSTATELASTCHANGE:
+ *var_len = sizeof(time_t);
+ return (uint8_t *) &(ctl_nbr->uptime);
+ case MPLSLDPSESSIONSTATE:
+ switch (ctl_nbr->nbr_state) {
+ case NBR_STA_INITIAL:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_INITIALIZED);
+ case NBR_STA_OPENREC:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_OPENREC);
+ case NBR_STA_OPENSENT:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_OPENSENT);
+ case NBR_STA_OPER:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_OPERATIONAL);
+ default:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_NONEXISTENT);
+ }
+ case MPLSLDPSESSIONROLE:
+ if (ldp_addrcmp(ctl_nbr->af, &ctl_nbr->laddr, &ctl_nbr->raddr)
+ > 0)
+ return SNMP_INTEGER(MPLSLDPSESSIONROLE_ACTIVE);
+ else
+ return SNMP_INTEGER(MPLSLDPSESSIONROLE_PASSIVE);
+ case MPLSLDPSESSIONPROTOCOLVERSION:
+ return SNMP_INTEGER(LDP_VERSION);
+ case MPLSLDPSESSIONKEEPALIVEHOLDTIMEREM:
+ return SNMP_INTEGER(ctl_nbr->hold_time_remaining);
+ case MPLSLDPSESSIONKEEPALIVETIME:
+ return SNMP_INTEGER(ctl_nbr->holdtime);
+ case MPLSLDPSESSIONMAXPDULENGTH:
+ if (ctl_nbr->nbr_state == NBR_STA_OPER)
+ return SNMP_INTEGER(ctl_nbr->max_pdu_len);
+ else
+ return SNMP_INTEGER(LDP_MAX_LEN);
+ case MPLSLDPSESSIONDISCONTINUITYTIME:
+ return SNMP_INTEGER(0); /* not supported */
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static uint8_t *ldpSessionStatsTable(struct variable *v, oid name[],
+ size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ struct ctl_nbr *ctl_nbr = ldpPeerTable_lookup(v, name, length, exact,
+ &entityLdpId, &entityIndex, &peerLdpId);
+
+ if (!ctl_nbr)
+ return NULL;
+
+ if (!exact) {
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+ entityIndex = LDP_DEFAULT_ENTITY_INDEX;
+ peerLdpId = ctl_nbr->id;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = entityIndex;
+ oid_copy_in_addr(name + v->namelen + 7, &peerLdpId);
+ name[v->namelen + 11] = 0;
+ name[v->namelen + 12] = 0;
+
+ *length = v->namelen + LDP_PEER_ENTRY_MAX_IDX_LEN;
+ }
+
+ switch (v->magic) {
+ case MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS:
+ return SNMP_INTEGER(ctl_nbr->stats.unknown_msg);
+ case MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS:
+ return SNMP_INTEGER(ctl_nbr->stats.unknown_tlv);
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static struct variable ldpe_variables[] = {
+ {MPLS_LDP_LSR_ID, STRING, RONLY, ldpLsrId, 3, {1, 1, 1}},
+ {MPLS_LDP_LSR_LOOP_DETECTION_CAPABLE, INTEGER, RONLY,
+ ldpLoopDetectCap, 3, {1, 1, 2}},
+ {MPLS_LDP_ENTITY_LAST_CHANGE, TIMESTAMP, RONLY, ldpEntityLastChange,
+ 3, {1, 2, 1}},
+ {MPLS_LDP_ENTITY_INDEX_NEXT, UNSIGNED32, RONLY, ldpEntityIndexNext,
+ 3, {1, 2, 2}},
+
+ /* MPLS LDP mplsLdpEntityTable. */
+ {MPLSLDPENTITYLDPID, STRING, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 1}},
+ {MPLSLDPENTITYINDEX, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 2}},
+ {MPLSLDPENTITYPROTOCOLVERSION, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 3}},
+ {MPLSLDPENTITYADMINSTATUS, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 4}},
+ {MPLSLDPENTITYOPERSTATUS, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 5}},
+ {MPLSLDPENTITYTCPPORT, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 6}},
+ {MPLSLDPENTITYUDPDSCPORT, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 7}},
+ {MPLSLDPENTITYMAXPDULENGTH, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 8}},
+ {MPLSLDPENTITYKEEPALIVEHOLDTIMER, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 9}},
+ {MPLSLDPENTITYHELLOHOLDTIMER, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 10}},
+ {MPLSLDPENTITYINITSESSIONTHRESHOLD, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 11}},
+ {MPLSLDPENTITYLABELDISTMETHOD, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 12}},
+ {MPLSLDPENTITYLABELRETENTIONMODE, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 13}},
+ {MPLSLDPENTITYPATHVECTORLIMIT, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 14}},
+ {MPLSLDPENTITYHOPCOUNTLIMIT, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 15}},
+ {MPLSLDPENTITYTRANSPORTADDRKIND, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 16}},
+ {MPLSLDPENTITYTARGETPEER, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 17}},
+ {MPLSLDPENTITYTARGETPEERADDRTYPE, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 18}},
+ {MPLSLDPENTITYTARGETPEERADDR, STRING, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 19}},
+ {MPLSLDPENTITYLABELTYPE, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 20}},
+ {MPLSLDPENTITYDISCONTINUITYTIME, TIMESTAMP, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 21}},
+ {MPLSLDPENTITYSTORAGETYPE, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 22}},
+ {MPLSLDPENTITYROWSTATUS, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 23}},
+
+ /* MPLS LDP mplsLdpEntityStatsTable. */
+ { MPLSLDPENTITYSTATSSESSIONATTEMPTS, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 1}},
+ { MPLSLDPENTITYSTATSSESSIONREJHELLO, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 2}},
+ { MPLSLDPENTITYSTATSSESSIONREJAD, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 3}},
+ { MPLSLDPENTITYSTATSSESSIONREJMAXPDU, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 4}},
+ { MPLSLDPENTITYSTATSSESSIONREJLR, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 5}},
+ { MPLSLDPENTITYSTATSBADLDPID, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 6}},
+ { MPLSLDPENTITYSTATSBADPDULENGTH, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 7}},
+ { MPLSLDPENTITYSTATSBADMSGLENGTH, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 8}},
+ { MPLSLDPENTITYSTATSBADTLVLENGTH, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 9}},
+ { MPLSLDPENTITYSTATSMALFORMEDTLV, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 10}},
+ { MPLSLDPENTITYSTATSKEEPALIVEEXP, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 11}},
+ { MPLSLDPENTITYSTATSSHUTDOWNRCVNOTIFY, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 12}},
+ { MPLSLDPENTITYSTATSSHUTDOWNSENTNOTIFY, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 13}},
+
+ /* MPLS LDP mplsLdpPeerTable */
+ {MPLSLDPPEERLDPID, STRING, RONLY, ldpPeerTable, 5, {1, 3, 2, 1, 1}},
+ {MPLSLDPPEERLABELDISTMETHOD, INTEGER, RONLY, ldpPeerTable,
+ 5, {1, 3, 2, 1, 2}},
+ {MPLSLDPPEERPATHVECTORLIMIT, INTEGER, RONLY, ldpPeerTable,
+ 5, {1, 3, 2, 1, 3}},
+ {MPLSLDPPEERTRANSPORTADDRTYPE, INTEGER, RONLY, ldpPeerTable,
+ 5, {1, 3, 2, 1, 4}},
+ {MPLSLDPPEERTRANSPORTADDR, STRING, RONLY, ldpPeerTable,
+ 5, {1, 3, 2, 1, 5}},
+
+ /* MPLS LDP mplsLdpSessionTable */
+ {MPLSLDPSESSIONSTATELASTCHANGE, TIMESTAMP, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 1}},
+ {MPLSLDPSESSIONSTATE, INTEGER, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 2}},
+ {MPLSLDPSESSIONROLE, INTEGER, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 3}},
+ {MPLSLDPSESSIONPROTOCOLVERSION, UNSIGNED32, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 4}},
+ {MPLSLDPSESSIONKEEPALIVEHOLDTIMEREM, INTEGER, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 5}},
+ {MPLSLDPSESSIONKEEPALIVETIME, UNSIGNED32, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 6}},
+ {MPLSLDPSESSIONMAXPDULENGTH, UNSIGNED32, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 7}},
+ {MPLSLDPSESSIONDISCONTINUITYTIME, TIMESTAMP, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 8}},
+
+ /* MPLS LDP mplsLdpSessionStatsTable */
+ {MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS, COUNTER32, RONLY,
+ ldpSessionStatsTable, 5, {1, 3, 4, 1, 1}},
+ {MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS, COUNTER32, RONLY,
+ ldpSessionStatsTable, 5, {1, 3, 4, 1, 2}},
+
+ /* MPLS LDP mplsLdpHelloAdjacencyTable. */
+ {MPLSLDPHELLOADJACENCYINDEX, UNSIGNED32, RONLY,
+ ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 1}},
+ {MPLSLDPHELLOADJACENCYHOLDTIMEREM, INTEGER, RONLY,
+ ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 2}},
+ {MPLSLDPHELLOADJACENCYHOLDTIME, UNSIGNED32, RONLY,
+ ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 3}},
+ {MPLSLDPHELLOADJACENCYTYPE, INTEGER, RONLY,
+ ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 4}},
+};
+
+static struct variable lde_variables[] = {
+};
+
+static struct trap_object ldpSessionTrapList[] = {
+ {5, {1, 3, 3, 1, MPLSLDPSESSIONSTATE}},
+ {5, {1, 3, 3, 1, MPLSLDPSESSIONDISCONTINUITYTIME}},
+ {5, {1, 3, 4, 1, MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS}},
+ {5, {1, 3, 4, 1, MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS}}};
+
+/* LDP TRAP. */
+#define LDPINITSESSIONTHRESHOLDEXCEEDED 1
+#define LDPPATHVECTORLIMITMISMATCH 2
+#define LDPSESSIONUP 3
+#define LDPSESSIONDOWN 4
+
+static void
+ldpTrapSession(struct nbr * nbr, unsigned int sptrap)
+{
+ oid index[sizeof(oid) * (LDP_PEER_ENTRY_MAX_IDX_LEN + 1)];
+
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+
+ struct ctl_nbr *ctl_nbr = nbr_to_ctl(nbr);
+
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+ entityIndex = LDP_DEFAULT_ENTITY_INDEX;
+ peerLdpId = ctl_nbr->id;
+
+ oid_copy_in_addr(index, &entityLdpId);
+ index[4] = 0;
+ index[5] = 0;
+ index[6] = entityIndex;
+ oid_copy_in_addr(&index[7], &peerLdpId);
+ index[11] = 0;
+ index[12] = 0;
+
+ index[LDP_PEER_ENTRY_MAX_IDX_LEN] = 0;
+
+ smux_trap(ldpe_variables, array_size(ldpe_variables), ldp_trap_oid,
+ array_size(ldp_trap_oid), ldp_oid,
+ sizeof(ldp_oid) / sizeof(oid), index,
+ LDP_PEER_ENTRY_MAX_IDX_LEN + 1,
+ ldpSessionTrapList, array_size(ldpSessionTrapList), sptrap);
+}
+
+static void
+ldpTrapSessionUp(struct nbr * nbr)
+{
+ ldpTrapSession(nbr, LDPSESSIONUP);
+}
+
+static void
+ldpTrapSessionDown(struct nbr * nbr)
+{
+ ldpTrapSession(nbr, LDPSESSIONDOWN);
+}
+
+static int ldp_snmp_agentx_enabled(void)
+{
+ main_imsg_compose_both(IMSG_AGENTX_ENABLED, NULL, 0);
+
+ return 0;
+}
+
+static int ldp_snmp_nbr_state_change(struct nbr * nbr, int old_state)
+{
+ if (old_state == nbr->state)
+ return 0;
+
+ if (nbr->state == NBR_STA_OPER)
+ ldpTrapSessionUp(nbr);
+ else if (old_state == NBR_STA_OPER)
+ ldpTrapSessionDown(nbr);
+
+ return 0;
+}
+
+static int ldp_snmp_init(struct event_loop *tm)
+{
+ hook_register(agentx_enabled, ldp_snmp_agentx_enabled);
+
+ smux_init(tm);
+
+ return 0;
+}
+
+static int ldp_snmp_register_mib(struct event_loop *tm)
+{
+ static int registered = 0;
+
+ if (registered)
+ return 0;
+
+ registered = 1;
+
+ smux_init(tm);
+
+ smux_agentx_enable();
+
+ if (ldpd_process == PROC_LDE_ENGINE)
+ REGISTER_MIB("mibII/ldp", lde_variables, variable, ldp_oid);
+ else if (ldpd_process == PROC_LDP_ENGINE) {
+ REGISTER_MIB("mibII/ldp", ldpe_variables, variable, ldp_oid);
+
+ hook_register(ldp_nbr_state_change, ldp_snmp_nbr_state_change);
+ }
+
+ return 0;
+}
+
+static int ldp_snmp_module_init(void)
+{
+ if (ldpd_process == PROC_MAIN)
+ hook_register(frr_late_init, ldp_snmp_init);
+ else
+ hook_register(ldp_register_mib, ldp_snmp_register_mib);
+
+ return 0;
+}
+
+FRR_MODULE_SETUP(
+ .name = "ldp_snmp",
+ .version = FRR_VERSION,
+ .description = "ldp AgentX SNMP module",
+ .init = ldp_snmp_module_init,
+);
diff --git a/ldpd/ldp_vty.h b/ldpd/ldp_vty.h
new file mode 100644
index 0000000..5c83d1c
--- /dev/null
+++ b/ldpd/ldp_vty.h
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 by Open Source Routing.
+ */
+
+#ifndef _LDP_VTY_H_
+#define _LDP_VTY_H_
+
+#include "vty.h"
+
+extern struct cmd_node ldp_node;
+extern struct cmd_node ldp_ipv4_node;
+extern struct cmd_node ldp_ipv6_node;
+extern struct cmd_node ldp_ipv4_iface_node;
+extern struct cmd_node ldp_ipv6_iface_node;
+extern struct cmd_node ldp_l2vpn_node;
+extern struct cmd_node ldp_pseudowire_node;
+extern struct cmd_node ldp_debug_node;
+
+union ldpd_addr;
+int ldp_get_address(const char *, int *, union ldpd_addr *);
+int ldp_vty_mpls_ldp (struct vty *, const char *);
+int ldp_vty_allow_broken_lsp(struct vty *, const char *);
+int ldp_vty_address_family (struct vty *, const char *, const char *);
+int ldp_vty_disc_holdtime(struct vty *, const char *, enum hello_type, long);
+int ldp_vty_disc_interval(struct vty *, const char *, enum hello_type, long);
+int ldp_vty_targeted_hello_accept(struct vty *, const char *, const char *);
+int ldp_vty_nbr_session_holdtime(struct vty *, const char *, struct in_addr, long);
+int ldp_vty_af_session_holdtime(struct vty *, const char *, long);
+int ldp_vty_interface(struct vty *, const char *, const char *);
+int ldp_vty_trans_addr(struct vty *, const char *, const char *);
+int ldp_vty_neighbor_targeted(struct vty *, const char *, const char *);
+int ldp_vty_label_advertise(struct vty *, const char *, const char *, const char *);
+int ldp_vty_label_allocate(struct vty *, const char *, const char *, const char *);
+int ldp_vty_label_expnull(struct vty *, const char *, const char *);
+int ldp_vty_label_accept(struct vty *, const char *, const char *, const char *);
+int ldp_vty_ttl_security(struct vty *, const char *);
+int ldp_vty_router_id(struct vty *, const char *, struct in_addr);
+int ldp_vty_ordered_control(struct vty *, const char *);
+int ldp_vty_wait_for_sync_interval(struct vty *, const char *, long);
+int ldp_vty_ds_cisco_interop(struct vty *, const char *);
+int ldp_vty_trans_pref_ipv4(struct vty *, const char *);
+int ldp_vty_neighbor_password(struct vty *, const char *, struct in_addr, const char *);
+int ldp_vty_neighbor_ttl_security(struct vty *, const char *, struct in_addr, const char *);
+int ldp_vty_l2vpn(struct vty *, const char *, const char *);
+int ldp_vty_l2vpn_bridge(struct vty *, const char *, const char *);
+int ldp_vty_l2vpn_mtu(struct vty *, const char *, long);
+int ldp_vty_l2vpn_pwtype(struct vty *, const char *, const char *);
+int ldp_vty_l2vpn_interface(struct vty *, const char *, const char *);
+int ldp_vty_l2vpn_pseudowire(struct vty *, const char *, const char *);
+int ldp_vty_l2vpn_pw_cword(struct vty *, const char *, const char *);
+int ldp_vty_l2vpn_pw_nbr_addr(struct vty *, const char *, const char *);
+int ldp_vty_l2vpn_pw_nbr_id(struct vty *, const char *, struct in_addr);
+int ldp_vty_l2vpn_pw_pwid(struct vty *, const char *, long);
+int ldp_vty_l2vpn_pw_pwstatus(struct vty *, const char *);
+int ldp_vty_clear_nbr(struct vty *, const char *);
+int ldp_vty_debug(struct vty *, const char *, const char *, const char *, const char *);
+int ldp_vty_show_binding(struct vty *, const char *, const char *, int,
+ const char *, unsigned long, unsigned long, const char *, const char *);
+int ldp_vty_show_discovery(struct vty *, const char *, const char *, const char *);
+int ldp_vty_show_interface(struct vty *, const char *, const char *);
+int ldp_vty_show_capabilities(struct vty *, const char *);
+int ldp_vty_show_neighbor(struct vty *, const char *, int, const char *, const char *);
+int ldp_vty_show_ldp_sync(struct vty *, const char *);
+int ldp_vty_show_atom_binding(struct vty *, const char *, unsigned long,
+ unsigned long, const char *);
+int ldp_vty_show_atom_vc(struct vty *, const char *, const char *,
+ const char *, const char *);
+int ldp_vty_show_debugging(struct vty *);
+
+void ldp_vty_init(void);
+
+#endif /* _LDP_VTY_H_ */
diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c
new file mode 100644
index 0000000..e046ae9
--- /dev/null
+++ b/ldpd/ldp_vty_cmds.c
@@ -0,0 +1,902 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 by Open Source Routing.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "vty.h"
+#include "json.h"
+
+#include "ldpd/ldpd.h"
+#include "ldpd/ldp_vty.h"
+#include "ldpd/ldp_vty_cmds_clippy.c"
+
+DEFPY_NOSH(ldp_mpls_ldp,
+ ldp_mpls_ldp_cmd,
+ "mpls ldp",
+ "Global MPLS configuration subcommands\n"
+ "Label Distribution Protocol\n")
+{
+ return (ldp_vty_mpls_ldp(vty, NULL));
+}
+
+DEFPY (no_ldp_mpls_ldp,
+ no_ldp_mpls_ldp_cmd,
+ "no mpls ldp",
+ NO_STR
+ "Global MPLS configuration subcommands\n"
+ "Label Distribution Protocol\n")
+{
+ return (ldp_vty_mpls_ldp(vty, "no"));
+}
+
+DEFPY_NOSH(ldp_l2vpn,
+ ldp_l2vpn_cmd,
+ "l2vpn WORD$l2vpn_name type vpls",
+ "Configure l2vpn commands\n"
+ "L2VPN name\n"
+ "L2VPN type\n"
+ "Virtual Private LAN Service\n")
+{
+ return (ldp_vty_l2vpn(vty, NULL, l2vpn_name));
+}
+
+DEFPY (no_ldp_l2vpn,
+ no_ldp_l2vpn_cmd,
+ "no l2vpn WORD$l2vpn_name type vpls",
+ NO_STR
+ "Configure l2vpn commands\n"
+ "L2VPN name\n"
+ "L2VPN type\n"
+ "Virtual Private LAN Service\n")
+{
+ return (ldp_vty_l2vpn(vty, "no", l2vpn_name));
+}
+
+DEFPY_NOSH(ldp_address_family,
+ ldp_address_family_cmd,
+ "address-family <ipv4|ipv6>$af",
+ "Configure Address Family and its parameters\n"
+ "IPv4\n"
+ "IPv6\n")
+{
+ return (ldp_vty_address_family(vty, NULL, af));
+}
+
+DEFPY (no_ldp_address_family,
+ no_ldp_address_family_cmd,
+ "no address-family <ipv4|ipv6>$af",
+ NO_STR
+ "Configure Address Family and its parameters\n"
+ "IPv4\n"
+ "IPv6\n")
+{
+ return (ldp_vty_address_family(vty, "no", af));
+}
+
+DEFPY_NOSH(ldp_exit_address_family,
+ ldp_exit_address_family_cmd,
+ "exit-address-family",
+ "Exit from Address Family configuration mode\n")
+{
+ if (vty->node == LDP_IPV4_NODE || vty->node == LDP_IPV6_NODE)
+ vty->node = LDP_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFPY (ldp_discovery_link_holdtime,
+ ldp_discovery_link_holdtime_cmd,
+ "[no] discovery hello holdtime (1-65535)$holdtime",
+ NO_STR
+ "Configure discovery parameters\n"
+ "LDP Link Hellos\n"
+ "Hello holdtime\n"
+ "Time (seconds) - 65535 implies infinite\n")
+{
+ return (ldp_vty_disc_holdtime(vty, no, HELLO_LINK, holdtime));
+}
+
+DEFPY (ldp_discovery_targeted_holdtime,
+ ldp_discovery_targeted_holdtime_cmd,
+ "[no] discovery targeted-hello holdtime (1-65535)$holdtime",
+ NO_STR
+ "Configure discovery parameters\n"
+ "LDP Targeted Hellos\n"
+ "Hello holdtime\n"
+ "Time (seconds) - 65535 implies infinite\n")
+{
+ return (ldp_vty_disc_holdtime(vty, no, HELLO_TARGETED, holdtime));
+}
+
+DEFPY (ldp_discovery_link_interval,
+ ldp_discovery_link_interval_cmd,
+ "[no] discovery hello interval (1-65535)$interval",
+ NO_STR
+ "Configure discovery parameters\n"
+ "LDP Link Hellos\n"
+ "Hello interval\n"
+ "Time (seconds)\n")
+{
+ return (ldp_vty_disc_interval(vty, no, HELLO_LINK, interval));
+}
+
+DEFPY (ldp_discovery_targeted_interval,
+ ldp_discovery_targeted_interval_cmd,
+ "[no] discovery targeted-hello interval (1-65535)$interval",
+ NO_STR
+ "Configure discovery parameters\n"
+ "LDP Targeted Hellos\n"
+ "Hello interval\n"
+ "Time (seconds)\n")
+{
+ return (ldp_vty_disc_interval(vty, no, HELLO_TARGETED, interval));
+}
+
+DEFPY (ldp_dual_stack_transport_connection_prefer_ipv4,
+ ldp_dual_stack_transport_connection_prefer_ipv4_cmd,
+ "[no] dual-stack transport-connection prefer ipv4",
+ NO_STR
+ "Configure dual stack parameters\n"
+ "Configure TCP transport parameters\n"
+ "Configure preferred address family for TCP transport connection with neighbor\n"
+ "IPv4\n")
+{
+ return (ldp_vty_trans_pref_ipv4(vty, no));
+}
+
+DEFPY (ldp_dual_stack_cisco_interop,
+ ldp_dual_stack_cisco_interop_cmd,
+ "[no] dual-stack cisco-interop",
+ NO_STR
+ "Configure dual stack parameters\n"
+ "Use Cisco non-compliant format to send and interpret the Dual-Stack capability TLV\n")
+{
+ return (ldp_vty_ds_cisco_interop(vty, no));
+}
+
+DEFPY (ldp_neighbor_password,
+ ldp_neighbor_password_cmd,
+ "[no] neighbor A.B.C.D$neighbor password WORD$password",
+ NO_STR
+ "Configure neighbor parameters\n"
+ "LDP Id of neighbor\n"
+ "Configure password for MD5 authentication\n"
+ "The password\n")
+{
+ return (ldp_vty_neighbor_password(vty, no, neighbor, password));
+}
+
+DEFPY (ldp_neighbor_session_holdtime,
+ ldp_neighbor_session_holdtime_cmd,
+ "[no] neighbor A.B.C.D$neighbor session holdtime (15-65535)$holdtime",
+ NO_STR
+ "Configure neighbor parameters\n"
+ "LDP Id of neighbor\n"
+ "Configure session parameters\n"
+ "Configure session holdtime\n"
+ "Time (seconds)\n")
+{
+ return (ldp_vty_nbr_session_holdtime(vty, no, neighbor, holdtime));
+}
+
+DEFPY (ldp_neighbor_ttl_security,
+ ldp_neighbor_ttl_security_cmd,
+ "[no] neighbor A.B.C.D$neighbor ttl-security <disable|hops (1-254)$hops>",
+ NO_STR
+ "Configure neighbor parameters\n"
+ "LDP Id of neighbor\n"
+ "LDP ttl security check\n"
+ "Disable ttl security\n"
+ "IP hops\n"
+ "maximum number of hops\n")
+{
+ return (ldp_vty_neighbor_ttl_security(vty, no, neighbor, hops_str));
+}
+
+DEFPY (ldp_router_id,
+ ldp_router_id_cmd,
+ "[no] router-id A.B.C.D$address",
+ NO_STR
+ "Configure router Id\n"
+ "LSR Id (in form of an IPv4 address)\n")
+{
+ return (ldp_vty_router_id(vty, no, address));
+}
+
+DEFPY (ldp_ordered_control,
+ ldp_ordered_control_cmd,
+ "[no] ordered-control",
+ NO_STR
+ "Configure LDP ordered label distribution control mode\n")
+{
+ return (ldp_vty_ordered_control(vty, no));
+}
+
+DEFPY (ldp_wait_for_sync,
+ ldp_wait_for_sync_cmd,
+ "[no] wait-for-sync (1-10000)$waitforsync",
+ NO_STR
+ "Time to wait for LDP-IGP Sync to complete label exchange\n"
+ "Time (seconds)\n")
+{
+ return (ldp_vty_wait_for_sync_interval(vty, no, waitforsync));
+
+}
+
+DEFPY (ldp_allow_broken_lsps,
+ ldp_allow_broken_lsps_cmd,
+ "[no] install allow-broken-lsps",
+ NO_STR
+ "install lsps\n"
+ "if no remote-label install with imp-null\n")
+{
+ return (ldp_vty_allow_broken_lsp(vty, no));
+}
+
+DEFPY (ldp_discovery_targeted_hello_accept,
+ ldp_discovery_targeted_hello_accept_cmd,
+ "[no] discovery targeted-hello accept [from ACCESSLIST_NAME$from_acl]",
+ NO_STR
+ "Configure discovery parameters\n"
+ "LDP Targeted Hellos\n"
+ "Accept and respond to targeted hellos\n"
+ "Access list to specify acceptable targeted hello source\n"
+ "IP access-list name\n")
+{
+ return (ldp_vty_targeted_hello_accept(vty, no, from_acl));
+}
+
+DEFPY (ldp_discovery_transport_address_ipv4,
+ ldp_discovery_transport_address_ipv4_cmd,
+ "[no] discovery transport-address A.B.C.D$address",
+ NO_STR
+ "Configure discovery parameters\n"
+ "Specify transport address for TCP connection\n"
+ "IP address to be used as transport address\n")
+{
+ return (ldp_vty_trans_addr(vty, no, address_str));
+}
+
+DEFPY (ldp_discovery_transport_address_ipv6,
+ ldp_discovery_transport_address_ipv6_cmd,
+ "[no] discovery transport-address X:X::X:X$address",
+ NO_STR
+ "Configure discovery parameters\n"
+ "Specify transport address for TCP connection\n"
+ "IPv6 address to be used as transport address\n")
+{
+ return (ldp_vty_trans_addr(vty, no, address_str));
+}
+
+DEFPY (ldp_label_local_advertise,
+ ldp_label_local_advertise_cmd,
+ "[no] label local advertise [{to ACCESSLIST_NAME$to_acl|for ACCESSLIST_NAME$for_acl}]",
+ NO_STR
+ "Configure label control and policies\n"
+ "Configure local label control and policies\n"
+ "Configure outbound label advertisement control\n"
+ "IP Access-list specifying controls on LDP Peers\n"
+ "IP access-list name\n"
+ "IP access-list for destination prefixes\n"
+ "IP access-list name\n")
+{
+ return (ldp_vty_label_advertise(vty, no, to_acl, for_acl));
+}
+
+DEFPY (ldp_label_local_advertise_explicit_null,
+ ldp_label_local_advertise_explicit_null_cmd,
+ "[no] label local advertise explicit-null [for ACCESSLIST_NAME$for_acl]",
+ NO_STR
+ "Configure label control and policies\n"
+ "Configure local label control and policies\n"
+ "Configure outbound label advertisement control\n"
+ "Configure explicit-null advertisement\n"
+ "IP access-list for destination prefixes\n"
+ "IP access-list name\n")
+{
+ return (ldp_vty_label_expnull(vty, no, for_acl));
+}
+
+DEFPY (ldp_label_local_allocate,
+ ldp_label_local_allocate_cmd,
+ "[no] label local allocate <host-routes$host_routes|for ACCESSLIST_NAME$for_acl>",
+ NO_STR
+ "Configure label control and policies\n"
+ "Configure local label control and policies\n"
+ "Configure label allocation control\n"
+ "allocate local label for host routes only\n"
+ "IP access-list\n"
+ "IP access-list name\n")
+{
+ return (ldp_vty_label_allocate(vty, no, host_routes, for_acl));
+}
+
+DEFPY (ldp_label_remote_accept,
+ ldp_label_remote_accept_cmd,
+ "[no] label remote accept {from ACCESSLIST_NAME$from_acl|for ACCESSLIST_NAME$for_acl}",
+ NO_STR
+ "Configure label control and policies\n"
+ "Configure remote/peer label control and policies\n"
+ "Configure inbound label acceptance control\n"
+ "Neighbor from whom to accept label advertisement\n"
+ "IP access-list name\n"
+ "IP access-list for destination prefixes\n"
+ "IP access-list name\n")
+{
+ return (ldp_vty_label_accept(vty, no, from_acl, for_acl));
+}
+
+DEFPY (ldp_ttl_security_disable,
+ ldp_ttl_security_disable_cmd,
+ "[no] ttl-security disable",
+ NO_STR
+ "LDP ttl security check\n"
+ "Disable ttl security\n")
+{
+ return (ldp_vty_ttl_security(vty, no));
+}
+
+DEFPY (ldp_session_holdtime,
+ ldp_session_holdtime_cmd,
+ "[no] session holdtime (15-65535)$holdtime",
+ NO_STR
+ "Configure session parameters\n"
+ "Configure session holdtime\n"
+ "Time (seconds)\n")
+{
+ return (ldp_vty_af_session_holdtime(vty, no, holdtime));
+}
+
+DEFPY_NOSH(ldp_interface,
+ ldp_interface_cmd,
+ "interface IFNAME$ifname",
+ "Enable LDP on an interface and enter interface submode\n"
+ "Interface's name\n")
+{
+ return (ldp_vty_interface(vty, NULL, ifname));
+}
+
+DEFPY (no_ldp_interface,
+ no_ldp_interface_cmd,
+ "no interface IFNAME$ifname",
+ NO_STR
+ "Enable LDP on an interface and enter interface submode\n"
+ "Interface's name\n")
+{
+ return (ldp_vty_interface(vty, "no", ifname));
+}
+
+DEFPY (ldp_neighbor_ipv4_targeted,
+ ldp_neighbor_ipv4_targeted_cmd,
+ "[no] neighbor A.B.C.D$address targeted",
+ NO_STR
+ "Configure neighbor parameters\n"
+ "IP address of neighbor\n"
+ "Establish targeted session\n")
+{
+ return (ldp_vty_neighbor_targeted(vty, no, address_str));
+}
+
+DEFPY (ldp_neighbor_ipv6_targeted,
+ ldp_neighbor_ipv6_targeted_cmd,
+ "[no] neighbor X:X::X:X$address targeted",
+ NO_STR
+ "Configure neighbor parameters\n"
+ "IPv6 address of neighbor\n"
+ "Establish targeted session\n")
+{
+ return (ldp_vty_neighbor_targeted(vty, no, address_str));
+}
+
+DEFPY (ldp_bridge,
+ ldp_bridge_cmd,
+ "[no] bridge IFNAME$ifname",
+ NO_STR
+ "Bridge interface\n"
+ "Interface's name\n")
+{
+ return (ldp_vty_l2vpn_bridge(vty, no, ifname));
+}
+
+DEFPY (ldp_mtu,
+ ldp_mtu_cmd,
+ "[no] mtu (1500-9180)$mtu",
+ NO_STR
+ "Set Maximum Transmission Unit\n"
+ "Maximum Transmission Unit value\n")
+{
+ return (ldp_vty_l2vpn_mtu(vty, no, mtu));
+}
+
+DEFPY (ldp_member_interface,
+ ldp_member_interface_cmd,
+ "[no] member interface IFNAME$ifname",
+ NO_STR
+ "L2VPN member configuration\n"
+ "Local interface\n"
+ "Interface's name\n")
+{
+ return (ldp_vty_l2vpn_interface(vty, no, ifname));
+}
+
+DEFPY_NOSH(ldp_member_pseudowire,
+ ldp_member_pseudowire_cmd,
+ "member pseudowire IFNAME$ifname",
+ "L2VPN member configuration\n"
+ "Pseudowire interface\n"
+ "Interface's name\n")
+{
+ return (ldp_vty_l2vpn_pseudowire(vty, NULL, ifname));
+}
+
+DEFPY (no_ldp_member_pseudowire,
+ no_ldp_member_pseudowire_cmd,
+ "no member pseudowire IFNAME$ifname",
+ NO_STR
+ "L2VPN member configuration\n"
+ "Pseudowire interface\n"
+ "Interface's name\n")
+{
+ return (ldp_vty_l2vpn_pseudowire(vty, "no", ifname));
+}
+
+DEFPY (ldp_vc_type,
+ ldp_vc_type_cmd,
+ "[no] vc type <ethernet|ethernet-tagged>$vc_type",
+ NO_STR
+ "Virtual Circuit options\n"
+ "Virtual Circuit type to use\n"
+ "Ethernet (type 5)\n"
+ "Ethernet-tagged (type 4)\n")
+{
+ return (ldp_vty_l2vpn_pwtype(vty, no, vc_type));
+}
+
+DEFPY (ldp_control_word,
+ ldp_control_word_cmd,
+ "[no] control-word <exclude|include>$preference",
+ NO_STR
+ "Control-word options\n"
+ "Exclude control-word in pseudowire packets\n"
+ "Include control-word in pseudowire packets\n")
+{
+ return (ldp_vty_l2vpn_pw_cword(vty, no, preference));
+}
+
+DEFPY (ldp_neighbor_address,
+ ldp_neighbor_address_cmd,
+ "[no] neighbor address <A.B.C.D|X:X::X:X>$pw_address",
+ NO_STR
+ "Remote endpoint configuration\n"
+ "Specify the IPv4 or IPv6 address of the remote endpoint\n"
+ "IPv4 address\n"
+ "IPv6 address\n")
+{
+ return (ldp_vty_l2vpn_pw_nbr_addr(vty, no, pw_address_str));
+}
+
+DEFPY (ldp_neighbor_lsr_id,
+ ldp_neighbor_lsr_id_cmd,
+ "[no] neighbor lsr-id A.B.C.D$address",
+ NO_STR
+ "Remote endpoint configuration\n"
+ "Specify the LSR-ID of the remote endpoint\n"
+ "IPv4 address\n")
+{
+ return (ldp_vty_l2vpn_pw_nbr_id(vty, no, address));
+}
+
+DEFPY (ldp_pw_id,
+ ldp_pw_id_cmd,
+ "[no] pw-id (1-4294967295)$pwid",
+ NO_STR
+ "Set the Virtual Circuit ID\n"
+ "Virtual Circuit ID value\n")
+{
+ return (ldp_vty_l2vpn_pw_pwid(vty, no, pwid));
+}
+
+DEFPY (ldp_pw_status_disable,
+ ldp_pw_status_disable_cmd,
+ "[no] pw-status disable",
+ NO_STR
+ "Configure PW status\n"
+ "Disable PW status\n")
+{
+ return (ldp_vty_l2vpn_pw_pwstatus(vty, no));
+}
+
+DEFPY (ldp_clear_mpls_ldp_neighbor,
+ ldp_clear_mpls_ldp_neighbor_cmd,
+ "clear mpls ldp neighbor [<A.B.C.D|X:X::X:X>]$address",
+ "Reset functions\n"
+ "Reset MPLS statistical information\n"
+ "Clear LDP state\n"
+ "Clear LDP neighbor sessions\n"
+ "IPv4 address\n"
+ "IPv6 address\n")
+{
+ return (ldp_vty_clear_nbr(vty, address_str));
+}
+
+DEFPY (ldp_debug_mpls_ldp_discovery_hello,
+ ldp_debug_mpls_ldp_discovery_hello_cmd,
+ "[no] debug mpls ldp discovery hello <recv|sent>$dir",
+ NO_STR
+ "Debugging functions\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "Discovery messages\n"
+ "Discovery hello message\n"
+ "Received messages\n"
+ "Sent messages\n")
+{
+ return (ldp_vty_debug(vty, no, "discovery", dir, NULL));
+}
+
+DEFPY (ldp_debug_mpls_ldp_type,
+ ldp_debug_mpls_ldp_type_cmd,
+ "[no] debug mpls ldp <errors|event|labels|sync|zebra>$type",
+ NO_STR
+ "Debugging functions\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "Errors\n"
+ "LDP event information\n"
+ "LDP label allocation information\n"
+ "LDP sync information\n"
+ "LDP zebra information\n")
+{
+ return (ldp_vty_debug(vty, no, type, NULL, NULL));
+}
+
+DEFPY (ldp_debug_mpls_ldp_messages_recv,
+ ldp_debug_mpls_ldp_messages_recv_cmd,
+ "[no] debug mpls ldp messages recv [all]$all",
+ NO_STR
+ "Debugging functions\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "Messages\n"
+ "Received messages, excluding periodic Keep Alives\n"
+ "Received messages, including periodic Keep Alives\n")
+{
+ return (ldp_vty_debug(vty, no, "messages", "recv", all));
+}
+
+DEFPY (ldp_debug_mpls_ldp_messages_sent,
+ ldp_debug_mpls_ldp_messages_sent_cmd,
+ "[no] debug mpls ldp messages sent [all]$all",
+ NO_STR
+ "Debugging functions\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "Messages\n"
+ "Sent messages, excluding periodic Keep Alives\n"
+ "Sent messages, including periodic Keep Alives\n")
+{
+ return (ldp_vty_debug(vty, no, "messages", "sent", all));
+}
+
+DEFPY (ldp_show_mpls_ldp_binding,
+ ldp_show_mpls_ldp_binding_cmd,
+ "show mpls ldp [<ipv4|ipv6>]$af binding\
+ [<A.B.C.D/M|X:X::X:X/M>$prefix [longer-prefixes$longer_prefixes]]\
+ [{\
+ neighbor A.B.C.D$nbr\
+ |local-label (0-1048575)$local_label\
+ |remote-label (0-1048575)$remote_label\
+ }]\
+ [detail]$detail [json]$json",
+ "Show running system information\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "IPv4 Address Family\n"
+ "IPv6 Address Family\n"
+ "Label Information Base (LIB) information\n"
+ "Destination prefix (IPv4)\n"
+ "Destination prefix (IPv6)\n"
+ "Include longer matches\n"
+ "Display labels from LDP neighbor\n"
+ "Neighbor LSR-ID\n"
+ "Match locally assigned label values\n"
+ "Locally assigned label value\n"
+ "Match remotely assigned label values\n"
+ "Remotely assigned label value\n"
+ "Show detailed information\n"
+ JSON_STR)
+{
+ if (!(ldpd_conf->flags & F_LDPD_ENABLED))
+ return CMD_SUCCESS;
+ if (!local_label_str)
+ local_label = NO_LABEL;
+ if (!remote_label_str)
+ remote_label = NO_LABEL;
+ return (ldp_vty_show_binding(vty, af, prefix_str, !!longer_prefixes,
+ nbr_str, local_label, remote_label, detail, json));
+}
+
+DEFPY (ldp_show_mpls_ldp_discovery,
+ ldp_show_mpls_ldp_discovery_cmd,
+ "show mpls ldp [<ipv4|ipv6>]$af discovery [detail]$detail [json]$json",
+ "Show running system information\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "IPv4 Address Family\n"
+ "IPv6 Address Family\n"
+ "Discovery Hello Information\n"
+ "Show detailed information\n"
+ JSON_STR)
+{
+ return (ldp_vty_show_discovery(vty, af, detail, json));
+}
+
+DEFPY (ldp_show_mpls_ldp_interface,
+ ldp_show_mpls_ldp_interface_cmd,
+ "show mpls ldp [<ipv4|ipv6>]$af interface [json]$json",
+ "Show running system information\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "IPv4 Address Family\n"
+ "IPv6 Address Family\n"
+ "interface information\n"
+ JSON_STR)
+{
+ return (ldp_vty_show_interface(vty, af, json));
+}
+
+DEFPY (ldp_show_mpls_ldp_capabilities,
+ ldp_show_mpls_ldp_capabilities_cmd,
+ "show mpls ldp capabilities [json]$json",
+ "Show running system information\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "Display LDP Capabilities information\n"
+ JSON_STR)
+{
+ return (ldp_vty_show_capabilities(vty, json));
+}
+
+DEFPY (ldp_show_mpls_ldp_neighbor,
+ ldp_show_mpls_ldp_neighbor_cmd,
+ "show mpls ldp neighbor [A.B.C.D]$lsr_id [detail]$detail [json]$json",
+ "Show running system information\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "Neighbor information\n"
+ "Neighbor LSR-ID\n"
+ "Show detailed information\n"
+ JSON_STR)
+{
+ return (ldp_vty_show_neighbor(vty, lsr_id_str, 0, detail, json));
+}
+
+DEFPY (ldp_show_mpls_ldp_neighbor_capabilities,
+ ldp_show_mpls_ldp_neighbor_capabilities_cmd,
+ "show mpls ldp neighbor [A.B.C.D]$lsr_id capabilities [json]$json",
+ "Show running system information\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "Neighbor information\n"
+ "Neighbor LSR-ID\n"
+ "Display neighbor capability information\n"
+ JSON_STR)
+{
+ return (ldp_vty_show_neighbor(vty, lsr_id_str, 1, NULL, json));
+}
+
+DEFPY (ldp_show_mpls_ldp_igp_sync,
+ ldp_show_mpls_ldp_igp_sync_cmd,
+ "show mpls ldp igp-sync [json]$json",
+ "Show mpls ldp ldp-sync information\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n"
+ "LDP-IGP Sync information\n"
+ JSON_STR)
+{
+ return (ldp_vty_show_ldp_sync(vty, json));
+}
+
+DEFPY (ldp_show_l2vpn_atom_binding,
+ ldp_show_l2vpn_atom_binding_cmd,
+ "show l2vpn atom binding\
+ [{\
+ A.B.C.D$peer\
+ |local-label (16-1048575)$local_label\
+ |remote-label (16-1048575)$remote_label\
+ }]\
+ [json]$json",
+ "Show running system information\n"
+ "Show information about Layer2 VPN\n"
+ "Show Any Transport over MPLS information\n"
+ "Show AToM label binding information\n"
+ "Destination address of the VC\n"
+ "Match locally assigned label values\n"
+ "Locally assigned label value\n"
+ "Match remotely assigned label values\n"
+ "Remotely assigned label value\n"
+ JSON_STR)
+{
+ if (!local_label_str)
+ local_label = NO_LABEL;
+ if (!remote_label_str)
+ remote_label = NO_LABEL;
+ return (ldp_vty_show_atom_binding(vty, peer_str, local_label,
+ remote_label, json));
+}
+
+DEFPY (ldp_show_l2vpn_atom_vc,
+ ldp_show_l2vpn_atom_vc_cmd,
+ "show l2vpn atom vc\
+ [{\
+ A.B.C.D$peer\
+ |interface IFNAME$ifname\
+ |vc-id (1-4294967295)$vcid\
+ }]\
+ [json]$json",
+ "Show running system information\n"
+ "Show information about Layer2 VPN\n"
+ "Show Any Transport over MPLS information\n"
+ "Show AToM virtual circuit information\n"
+ "Destination address of the VC\n"
+ "Local interface of the pseudowire\n"
+ "Interface's name\n"
+ "VC ID\n"
+ "VC ID\n"
+ JSON_STR)
+{
+ return (ldp_vty_show_atom_vc(vty, peer_str, ifname, vcid_str, json));
+}
+
+DEFPY_NOSH (ldp_show_debugging_mpls_ldp,
+ ldp_show_debugging_mpls_ldp_cmd,
+ "show debugging [mpls ldp]",
+ "Show running system information\n"
+ "Debugging functions\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n")
+{
+ ldp_vty_show_debugging(vty);
+
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+static void
+l2vpn_autocomplete(vector comps, struct cmd_token *token)
+{
+ struct l2vpn *l2vpn;
+
+ RB_FOREACH(l2vpn, l2vpn_head, &vty_conf->l2vpn_tree)
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, l2vpn->name));
+}
+
+static const struct cmd_variable_handler l2vpn_var_handlers[] = {
+ {
+ .varname = "l2vpn_name",
+ .completions = l2vpn_autocomplete
+ },
+ {
+ .completions = NULL
+ }
+};
+
+void
+ldp_vty_init (void)
+{
+ cmd_variable_handler_register(l2vpn_var_handlers);
+
+ install_node(&ldp_node);
+ install_node(&ldp_ipv4_node);
+ install_node(&ldp_ipv6_node);
+ install_node(&ldp_ipv4_iface_node);
+ install_node(&ldp_ipv6_iface_node);
+ install_node(&ldp_l2vpn_node);
+ install_node(&ldp_pseudowire_node);
+ install_node(&ldp_debug_node);
+ install_default(LDP_NODE);
+ install_default(LDP_IPV4_NODE);
+ install_default(LDP_IPV6_NODE);
+ install_default(LDP_IPV4_IFACE_NODE);
+ install_default(LDP_IPV6_IFACE_NODE);
+ install_default(LDP_L2VPN_NODE);
+ install_default(LDP_PSEUDOWIRE_NODE);
+
+ install_element(CONFIG_NODE, &ldp_mpls_ldp_cmd);
+ install_element(CONFIG_NODE, &no_ldp_mpls_ldp_cmd);
+ install_element(CONFIG_NODE, &ldp_l2vpn_cmd);
+ install_element(CONFIG_NODE, &no_ldp_l2vpn_cmd);
+ install_element(CONFIG_NODE, &ldp_debug_mpls_ldp_discovery_hello_cmd);
+ install_element(CONFIG_NODE, &ldp_debug_mpls_ldp_type_cmd);
+ install_element(CONFIG_NODE, &ldp_debug_mpls_ldp_messages_recv_cmd);
+ install_element(CONFIG_NODE, &ldp_debug_mpls_ldp_messages_sent_cmd);
+
+ install_element(LDP_NODE, &ldp_address_family_cmd);
+ install_element(LDP_NODE, &no_ldp_address_family_cmd);
+ install_element(LDP_NODE, &ldp_discovery_link_holdtime_cmd);
+ install_element(LDP_NODE, &ldp_discovery_targeted_holdtime_cmd);
+ install_element(LDP_NODE, &ldp_discovery_link_interval_cmd);
+ install_element(LDP_NODE, &ldp_discovery_targeted_interval_cmd);
+ install_element(LDP_NODE, &ldp_dual_stack_transport_connection_prefer_ipv4_cmd);
+ install_element(LDP_NODE, &ldp_dual_stack_cisco_interop_cmd);
+ install_element(LDP_NODE, &ldp_neighbor_password_cmd);
+ install_element(LDP_NODE, &ldp_neighbor_session_holdtime_cmd);
+ install_element(LDP_NODE, &ldp_neighbor_ttl_security_cmd);
+ install_element(LDP_NODE, &ldp_router_id_cmd);
+ install_element(LDP_NODE, &ldp_ordered_control_cmd);
+ install_element(LDP_NODE, &ldp_wait_for_sync_cmd);
+ install_element(LDP_NODE, &ldp_allow_broken_lsps_cmd);
+
+ install_element(LDP_IPV4_NODE, &ldp_discovery_link_holdtime_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_discovery_targeted_holdtime_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_discovery_link_interval_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_discovery_targeted_interval_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_discovery_targeted_hello_accept_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_discovery_transport_address_ipv4_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_label_local_advertise_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_label_local_advertise_explicit_null_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_label_local_allocate_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_label_remote_accept_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_ttl_security_disable_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_interface_cmd);
+ install_element(LDP_IPV4_NODE, &no_ldp_interface_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_session_holdtime_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_neighbor_ipv4_targeted_cmd);
+ install_element(LDP_IPV4_NODE, &ldp_exit_address_family_cmd);
+
+ install_element(LDP_IPV6_NODE, &ldp_discovery_link_holdtime_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_discovery_targeted_holdtime_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_discovery_link_interval_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_discovery_targeted_interval_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_discovery_targeted_hello_accept_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_discovery_transport_address_ipv6_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_label_local_advertise_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_label_local_advertise_explicit_null_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_label_local_allocate_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_label_remote_accept_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_ttl_security_disable_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_interface_cmd);
+ install_element(LDP_IPV6_NODE, &no_ldp_interface_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_session_holdtime_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_neighbor_ipv6_targeted_cmd);
+ install_element(LDP_IPV6_NODE, &ldp_exit_address_family_cmd);
+
+ install_element(LDP_IPV4_IFACE_NODE, &ldp_discovery_link_holdtime_cmd);
+ install_element(LDP_IPV4_IFACE_NODE, &ldp_discovery_link_interval_cmd);
+
+ install_element(LDP_IPV6_IFACE_NODE, &ldp_discovery_link_holdtime_cmd);
+ install_element(LDP_IPV6_IFACE_NODE, &ldp_discovery_link_interval_cmd);
+
+ install_element(LDP_L2VPN_NODE, &ldp_bridge_cmd);
+ install_element(LDP_L2VPN_NODE, &ldp_mtu_cmd);
+ install_element(LDP_L2VPN_NODE, &ldp_member_interface_cmd);
+ install_element(LDP_L2VPN_NODE, &ldp_member_pseudowire_cmd);
+ install_element(LDP_L2VPN_NODE, &no_ldp_member_pseudowire_cmd);
+ install_element(LDP_L2VPN_NODE, &ldp_vc_type_cmd);
+
+ install_element(LDP_PSEUDOWIRE_NODE, &ldp_control_word_cmd);
+ install_element(LDP_PSEUDOWIRE_NODE, &ldp_neighbor_address_cmd);
+ install_element(LDP_PSEUDOWIRE_NODE, &ldp_neighbor_lsr_id_cmd);
+ install_element(LDP_PSEUDOWIRE_NODE, &ldp_pw_id_cmd);
+ install_element(LDP_PSEUDOWIRE_NODE, &ldp_pw_status_disable_cmd);
+
+ install_element(ENABLE_NODE, &ldp_clear_mpls_ldp_neighbor_cmd);
+ install_element(ENABLE_NODE, &ldp_debug_mpls_ldp_discovery_hello_cmd);
+ install_element(ENABLE_NODE, &ldp_debug_mpls_ldp_type_cmd);
+ install_element(ENABLE_NODE, &ldp_debug_mpls_ldp_messages_recv_cmd);
+ install_element(ENABLE_NODE, &ldp_debug_mpls_ldp_messages_sent_cmd);
+
+ install_element(VIEW_NODE, &ldp_show_mpls_ldp_binding_cmd);
+ install_element(VIEW_NODE, &ldp_show_mpls_ldp_discovery_cmd);
+ install_element(VIEW_NODE, &ldp_show_mpls_ldp_interface_cmd);
+ install_element(VIEW_NODE, &ldp_show_mpls_ldp_capabilities_cmd);
+ install_element(VIEW_NODE, &ldp_show_mpls_ldp_neighbor_cmd);
+ install_element(VIEW_NODE, &ldp_show_mpls_ldp_neighbor_capabilities_cmd);
+ install_element(VIEW_NODE, &ldp_show_l2vpn_atom_binding_cmd);
+ install_element(VIEW_NODE, &ldp_show_l2vpn_atom_vc_cmd);
+ install_element(VIEW_NODE, &ldp_show_debugging_mpls_ldp_cmd);
+ install_element(VIEW_NODE, &ldp_show_mpls_ldp_igp_sync_cmd);
+}
diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c
new file mode 100644
index 0000000..ffff676
--- /dev/null
+++ b/ldpd/ldp_vty_conf.c
@@ -0,0 +1,1644 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 by Open Source Routing.
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+
+#include "command.h"
+#include "vrf.h"
+#include "if.h"
+#include "vty.h"
+#include "ldp_vty.h"
+
+static int ldp_config_write(struct vty *);
+static void ldp_af_iface_config_write(struct vty *, int);
+static void ldp_af_config_write(struct vty *, int, struct ldpd_conf *,
+ struct ldpd_af_conf *);
+static int ldp_l2vpn_config_write(struct vty *);
+static void ldp_l2vpn_pw_config_write(struct vty *, struct l2vpn_pw *);
+static int ldp_vty_get_af(struct vty *);
+static int ldp_iface_is_configured(struct ldpd_conf *, const char *);
+
+struct cmd_node ldp_node = {
+ .name = "ldp",
+ .node = LDP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-ldp)# ",
+ .config_write = ldp_config_write,
+};
+
+struct cmd_node ldp_ipv4_node = {
+ .name = "ldp ipv4",
+ .node = LDP_IPV4_NODE,
+ .parent_node = LDP_NODE,
+ .prompt = "%s(config-ldp-af)# ",
+};
+
+struct cmd_node ldp_ipv6_node = {
+ .name = "ldp ipv6",
+ .node = LDP_IPV6_NODE,
+ .parent_node = LDP_NODE,
+ .prompt = "%s(config-ldp-af)# ",
+};
+
+struct cmd_node ldp_ipv4_iface_node = {
+ .name = "ldp ipv4 interface",
+ .node = LDP_IPV4_IFACE_NODE,
+ .parent_node = LDP_IPV4_NODE,
+ .prompt = "%s(config-ldp-af-if)# ",
+};
+
+struct cmd_node ldp_ipv6_iface_node = {
+ .name = "ldp ipv6 interface",
+ .node = LDP_IPV6_IFACE_NODE,
+ .parent_node = LDP_IPV6_NODE,
+ .prompt = "%s(config-ldp-af-if)# ",
+};
+
+struct cmd_node ldp_l2vpn_node = {
+ .name = "ldp l2vpn",
+ .node = LDP_L2VPN_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-l2vpn)# ",
+ .config_write = ldp_l2vpn_config_write,
+};
+
+struct cmd_node ldp_pseudowire_node = {
+ .name = "ldp",
+ .node = LDP_PSEUDOWIRE_NODE,
+ .parent_node = LDP_L2VPN_NODE,
+ .prompt = "%s(config-l2vpn-pw)# ",
+};
+
+int
+ldp_get_address(const char *str, int *af, union ldpd_addr *addr)
+{
+ if (!str || !af || !addr)
+ return (-1);
+
+ memset(addr, 0, sizeof(*addr));
+
+ if (inet_pton(AF_INET, str, &addr->v4) == 1) {
+ *af = AF_INET;
+ return (0);
+ }
+
+ if (inet_pton(AF_INET6, str, &addr->v6) == 1) {
+ *af = AF_INET6;
+ return (0);
+ }
+
+ return (-1);
+}
+
+static void
+ldp_af_iface_config_write(struct vty *vty, int af)
+{
+ struct iface *iface;
+ struct iface_af *ia;
+
+ RB_FOREACH(iface, iface_head, &ldpd_conf->iface_tree) {
+ ia = iface_af_get(iface, af);
+ if (!ia->enabled)
+ continue;
+
+ vty_out (vty, " !\n");
+ vty_out (vty, " interface %s\n", iface->name);
+
+ if (ia->hello_holdtime != LINK_DFLT_HOLDTIME &&
+ ia->hello_holdtime != 0)
+ vty_out (vty, " discovery hello holdtime %u\n",
+ ia->hello_holdtime);
+ if (ia->hello_interval != DEFAULT_HELLO_INTERVAL &&
+ ia->hello_interval != 0)
+ vty_out (vty, " discovery hello interval %u\n",
+ ia->hello_interval);
+
+ vty_out (vty, " exit\n");
+ }
+}
+
+static void
+ldp_af_config_write(struct vty *vty, int af, struct ldpd_conf *conf,
+ struct ldpd_af_conf *af_conf)
+{
+ struct tnbr *tnbr;
+
+ if (!(af_conf->flags & F_LDPD_AF_ENABLED))
+ return;
+
+ vty_out (vty, " !\n");
+ vty_out (vty, " address-family %s\n", af_name(af));
+
+ if (af_conf->lhello_holdtime != LINK_DFLT_HOLDTIME &&
+ af_conf->lhello_holdtime != 0 )
+ vty_out (vty, " discovery hello holdtime %u\n",
+ af_conf->lhello_holdtime);
+ if (af_conf->lhello_interval != DEFAULT_HELLO_INTERVAL &&
+ af_conf->lhello_interval != 0)
+ vty_out (vty, " discovery hello interval %u\n",
+ af_conf->lhello_interval);
+
+ if (af_conf->flags & F_LDPD_AF_THELLO_ACCEPT) {
+ vty_out(vty, " discovery targeted-hello accept");
+ if (af_conf->acl_thello_accept_from[0] != '\0')
+ vty_out(vty, " from %s",
+ af_conf->acl_thello_accept_from);
+ vty_out (vty, "\n");
+ }
+
+ if (af_conf->thello_holdtime != TARGETED_DFLT_HOLDTIME &&
+ af_conf->thello_holdtime != 0)
+ vty_out (vty, " discovery targeted-hello holdtime %u\n",
+ af_conf->thello_holdtime);
+ if (af_conf->thello_interval != DEFAULT_HELLO_INTERVAL &&
+ af_conf->thello_interval != 0)
+ vty_out (vty, " discovery targeted-hello interval %u\n",
+ af_conf->thello_interval);
+
+ if (ldp_addrisset(af, &af_conf->trans_addr))
+ vty_out (vty, " discovery transport-address %s\n",
+ log_addr(af, &af_conf->trans_addr));
+ else
+ vty_out (vty,
+ " ! Incomplete config, specify a discovery transport-address\n");
+
+ if ((af_conf->flags & F_LDPD_AF_ALLOCHOSTONLY) ||
+ af_conf->acl_label_allocate_for[0] != '\0') {
+ vty_out(vty, " label local allocate");
+ if (af_conf->flags & F_LDPD_AF_ALLOCHOSTONLY)
+ vty_out(vty, " host-routes");
+ else
+ vty_out(vty, " for %s",
+ af_conf->acl_label_allocate_for);
+ vty_out (vty, "\n");
+ }
+
+ if (af_conf->acl_label_advertise_for[0] != '\0' ||
+ af_conf->acl_label_advertise_to[0] != '\0') {
+ vty_out(vty, " label local advertise");
+ if (af_conf->acl_label_advertise_to[0] != '\0')
+ vty_out(vty, " to %s",
+ af_conf->acl_label_advertise_to);
+ if (af_conf->acl_label_advertise_for[0] != '\0')
+ vty_out(vty, " for %s",
+ af_conf->acl_label_advertise_for);
+ vty_out (vty, "\n");
+ }
+
+ if (af_conf->flags & F_LDPD_AF_EXPNULL) {
+ vty_out(vty, " label local advertise explicit-null");
+ if (af_conf->acl_label_expnull_for[0] != '\0')
+ vty_out(vty, " for %s",
+ af_conf->acl_label_expnull_for);
+ vty_out (vty, "\n");
+ }
+
+ if (af_conf->acl_label_accept_for[0] != '\0' ||
+ af_conf->acl_label_accept_from[0] != '\0') {
+ vty_out(vty, " label remote accept");
+ if (af_conf->acl_label_accept_from[0] != '\0')
+ vty_out(vty, " from %s",
+ af_conf->acl_label_accept_from);
+ if (af_conf->acl_label_accept_for[0] != '\0')
+ vty_out(vty, " for %s",
+ af_conf->acl_label_accept_for);
+ vty_out (vty, "\n");
+ }
+
+ if (af_conf->flags & F_LDPD_AF_NO_GTSM)
+ vty_out (vty, " ttl-security disable\n");
+
+ if (af_conf->keepalive != DEFAULT_KEEPALIVE)
+ vty_out (vty, " session holdtime %u\n",af_conf->keepalive);
+
+ RB_FOREACH(tnbr, tnbr_head, &ldpd_conf->tnbr_tree) {
+ if (tnbr->af == af) {
+ vty_out (vty, " !\n");
+ vty_out (vty, " neighbor %s targeted\n",
+ log_addr(tnbr->af, &tnbr->addr));
+ }
+ }
+
+ ldp_af_iface_config_write(vty, af);
+
+ vty_out(vty, " !\n");
+ vty_out(vty, " exit-address-family\n");
+}
+
+static int
+ldp_config_write(struct vty *vty)
+{
+ struct nbr_params *nbrp;
+
+ if (!(ldpd_conf->flags & F_LDPD_ENABLED))
+ return (0);
+
+ vty_out (vty, "mpls ldp\n");
+
+ if (ldpd_conf->rtr_id.s_addr != INADDR_ANY)
+ vty_out(vty, " router-id %pI4\n", &ldpd_conf->rtr_id);
+
+ if (ldpd_conf->lhello_holdtime != LINK_DFLT_HOLDTIME &&
+ ldpd_conf->lhello_holdtime != 0)
+ vty_out (vty, " discovery hello holdtime %u\n",
+ ldpd_conf->lhello_holdtime);
+ if (ldpd_conf->lhello_interval != DEFAULT_HELLO_INTERVAL &&
+ ldpd_conf->lhello_interval != 0)
+ vty_out (vty, " discovery hello interval %u\n",
+ ldpd_conf->lhello_interval);
+
+ if (ldpd_conf->thello_holdtime != TARGETED_DFLT_HOLDTIME &&
+ ldpd_conf->thello_holdtime != 0)
+ vty_out (vty, " discovery targeted-hello holdtime %u\n",
+ ldpd_conf->thello_holdtime);
+ if (ldpd_conf->thello_interval != DEFAULT_HELLO_INTERVAL &&
+ ldpd_conf->thello_interval != 0)
+ vty_out (vty, " discovery targeted-hello interval %u\n",
+ ldpd_conf->thello_interval);
+
+ if (ldpd_conf->trans_pref == DUAL_STACK_LDPOV4)
+ vty_out (vty,
+ " dual-stack transport-connection prefer ipv4\n");
+
+ if (ldpd_conf->flags & F_LDPD_DS_CISCO_INTEROP)
+ vty_out (vty, " dual-stack cisco-interop\n");
+
+ if (ldpd_conf->flags & F_LDPD_ORDERED_CONTROL)
+ vty_out (vty, " ordered-control\n");
+
+ if (ldpd_conf->wait_for_sync_interval != DFLT_WAIT_FOR_SYNC &&
+ ldpd_conf->wait_for_sync_interval != 0)
+ vty_out (vty, " wait-for-sync %u\n",
+ ldpd_conf->wait_for_sync_interval);
+
+ if (ldpd_conf->flags & F_LDPD_ALLOW_BROKEN_LSP)
+ vty_out(vty, " install allow-broken-lsp\n");
+
+ RB_FOREACH(nbrp, nbrp_head, &ldpd_conf->nbrp_tree) {
+ if (nbrp->flags & F_NBRP_KEEPALIVE)
+ vty_out (vty, " neighbor %pI4 session holdtime %u\n",
+ &nbrp->lsr_id,nbrp->keepalive);
+
+ if (nbrp->flags & F_NBRP_GTSM) {
+ if (nbrp->gtsm_enabled)
+ vty_out (vty, " neighbor %pI4 ttl-security hops %u\n", &nbrp->lsr_id,
+ nbrp->gtsm_hops);
+ else
+ vty_out (vty, " neighbor %pI4 ttl-security disable\n",&nbrp->lsr_id);
+ }
+
+ if (nbrp->auth.method == AUTH_MD5SIG)
+ vty_out (vty, " neighbor %pI4 password %s\n",
+ &nbrp->lsr_id,nbrp->auth.md5key);
+ }
+
+ ldp_af_config_write(vty, AF_INET, ldpd_conf, &ldpd_conf->ipv4);
+ ldp_af_config_write(vty, AF_INET6, ldpd_conf, &ldpd_conf->ipv6);
+ vty_out (vty, " !\n");
+ vty_out (vty, "exit\n");
+ vty_out (vty, "!\n");
+
+ return (1);
+}
+
+static void
+ldp_l2vpn_pw_config_write(struct vty *vty, struct l2vpn_pw *pw)
+{
+ int missing_lsrid = 0;
+ int missing_pwid = 0;
+
+ vty_out (vty, " !\n");
+ vty_out (vty, " member pseudowire %s\n", pw->ifname);
+
+ if (pw->lsr_id.s_addr != INADDR_ANY)
+ vty_out (vty, " neighbor lsr-id %pI4\n",&pw->lsr_id);
+ else
+ missing_lsrid = 1;
+
+ if (pw->flags & F_PW_STATIC_NBR_ADDR)
+ vty_out (vty, " neighbor address %s\n",
+ log_addr(pw->af, &pw->addr));
+
+ if (pw->pwid != 0)
+ vty_out (vty, " pw-id %u\n", pw->pwid);
+ else
+ missing_pwid = 1;
+
+ if (!(pw->flags & F_PW_CWORD_CONF))
+ vty_out (vty, " control-word exclude\n");
+
+ if (!(pw->flags & F_PW_STATUSTLV_CONF))
+ vty_out (vty, " pw-status disable\n");
+
+ if (missing_lsrid)
+ vty_out (vty,
+ " ! Incomplete config, specify a neighbor lsr-id\n");
+ if (missing_pwid)
+ vty_out (vty," ! Incomplete config, specify a pw-id\n");
+
+ vty_out (vty, " exit\n");
+}
+
+static int
+ldp_l2vpn_config_write(struct vty *vty)
+{
+ struct l2vpn *l2vpn;
+ struct l2vpn_if *lif;
+ struct l2vpn_pw *pw;
+
+ RB_FOREACH(l2vpn, l2vpn_head, &ldpd_conf->l2vpn_tree) {
+ vty_out (vty, "l2vpn %s type vpls\n", l2vpn->name);
+
+ if (l2vpn->pw_type != DEFAULT_PW_TYPE)
+ vty_out (vty, " vc type ethernet-tagged\n");
+
+ if (l2vpn->mtu != DEFAULT_L2VPN_MTU)
+ vty_out (vty, " mtu %u\n", l2vpn->mtu);
+
+ if (l2vpn->br_ifname[0] != '\0')
+ vty_out (vty, " bridge %s\n",l2vpn->br_ifname);
+
+ RB_FOREACH(lif, l2vpn_if_head, &l2vpn->if_tree)
+ vty_out (vty, " member interface %s\n",lif->ifname);
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
+ ldp_l2vpn_pw_config_write(vty, pw);
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_inactive_tree)
+ ldp_l2vpn_pw_config_write(vty, pw);
+
+ vty_out (vty, " !\n");
+ vty_out (vty, "exit\n");
+ vty_out (vty, "!\n");
+ }
+
+ return (0);
+}
+
+static int
+ldp_vty_get_af(struct vty *vty)
+{
+ switch (vty->node) {
+ case LDP_IPV4_NODE:
+ case LDP_IPV4_IFACE_NODE:
+ return (AF_INET);
+ case LDP_IPV6_NODE:
+ case LDP_IPV6_IFACE_NODE:
+ return (AF_INET6);
+ default:
+ fatalx("ldp_vty_get_af: unexpected node");
+ }
+}
+
+static int
+ldp_iface_is_configured(struct ldpd_conf *xconf, const char *ifname)
+{
+ struct l2vpn *l2vpn;
+
+ if (if_lookup_name(xconf, ifname))
+ return (1);
+
+ RB_FOREACH(l2vpn, l2vpn_head, &xconf->l2vpn_tree) {
+ if (l2vpn_if_find(l2vpn, ifname))
+ return (1);
+ if (l2vpn_pw_find(l2vpn, ifname))
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+ldp_vty_mpls_ldp(struct vty *vty, const char *negate)
+{
+ if (negate)
+ vty_conf->flags &= ~F_LDPD_ENABLED;
+ else {
+ vty->node = LDP_NODE;
+ vty_conf->flags |= F_LDPD_ENABLED;
+ }
+
+ /* register / de-register to recv info from zebra */
+ ldp_zebra_regdereg_zebra_info(!negate);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_address_family(struct vty *vty, const char *negate, const char *af_str)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ if (af_str == NULL)
+ return (CMD_WARNING_CONFIG_FAILED);
+
+ if (strcmp(af_str, "ipv4") == 0) {
+ af = AF_INET;
+ af_conf = &vty_conf->ipv4;
+ } else if (strcmp(af_str, "ipv6") == 0) {
+ af = AF_INET6;
+ af_conf = &vty_conf->ipv6;
+ } else
+ return (CMD_WARNING_CONFIG_FAILED);
+
+ if (negate) {
+ af_conf->flags &= ~F_LDPD_AF_ENABLED;
+ ldp_config_apply(vty, vty_conf);
+ return (CMD_SUCCESS);
+ }
+
+ switch (af) {
+ case AF_INET:
+ vty->node = LDP_IPV4_NODE;
+ break;
+ case AF_INET6:
+ vty->node = LDP_IPV6_NODE;
+ break;
+ default:
+ fatalx("ldp_vty_address_family: unknown af");
+ }
+ af_conf->flags |= F_LDPD_AF_ENABLED;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int ldp_vty_disc_holdtime(struct vty *vty, const char *negate,
+ enum hello_type hello_type, long secs)
+{
+ struct ldpd_af_conf *af_conf;
+ struct iface *iface;
+ struct iface_af *ia;
+ int af;
+ switch (vty->node) {
+ case LDP_NODE:
+ if (negate) {
+ switch (hello_type) {
+ case HELLO_LINK:
+ vty_conf->lhello_holdtime = LINK_DFLT_HOLDTIME;
+ break;
+ case HELLO_TARGETED:
+ vty_conf->thello_holdtime =
+ TARGETED_DFLT_HOLDTIME;
+ break;
+ }
+ } else {
+ switch (hello_type) {
+ case HELLO_LINK:
+ vty_conf->lhello_holdtime = secs;
+ break;
+ case HELLO_TARGETED:
+ vty_conf->thello_holdtime = secs;
+ break;
+ }
+ }
+ ldp_config_apply(vty, vty_conf);
+ break;
+ case LDP_IPV4_NODE:
+ case LDP_IPV6_NODE:
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate) {
+ switch (hello_type) {
+ case HELLO_LINK:
+ af_conf->lhello_holdtime = 0;
+ break;
+ case HELLO_TARGETED:
+ af_conf->thello_holdtime = 0;
+ break;
+ }
+ } else {
+ switch (hello_type) {
+ case HELLO_LINK:
+ af_conf->lhello_holdtime = secs;
+ break;
+ case HELLO_TARGETED:
+ af_conf->thello_holdtime = secs;
+ break;
+ }
+ }
+ ldp_config_apply(vty, vty_conf);
+ break;
+ case LDP_IPV4_IFACE_NODE:
+ case LDP_IPV6_IFACE_NODE:
+ af = ldp_vty_get_af(vty);
+ iface = VTY_GET_CONTEXT(iface);
+ VTY_CHECK_CONTEXT(iface);
+
+ ia = iface_af_get(iface, af);
+ if (negate)
+ ia->hello_holdtime = 0;
+ else
+ ia->hello_holdtime = secs;
+
+ ldp_config_apply(vty, vty_conf);
+ break;
+ default:
+ fatalx("ldp_vty_disc_holdtime: unexpected node");
+ }
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_disc_interval(struct vty *vty, const char *negate,
+ enum hello_type hello_type, long secs)
+{
+ struct ldpd_af_conf *af_conf;
+ struct iface *iface;
+ struct iface_af *ia;
+ int af;
+
+ switch (vty->node) {
+ case LDP_NODE:
+ if (negate) {
+ switch (hello_type) {
+ case HELLO_LINK:
+ vty_conf->lhello_interval =
+ DEFAULT_HELLO_INTERVAL;
+ break;
+ case HELLO_TARGETED:
+ vty_conf->thello_interval =
+ DEFAULT_HELLO_INTERVAL;
+ break;
+ }
+ } else {
+ switch (hello_type) {
+ case HELLO_LINK:
+ vty_conf->lhello_interval = secs;
+ break;
+ case HELLO_TARGETED:
+ vty_conf->thello_interval = secs;
+ break;
+ }
+ }
+ ldp_config_apply(vty, vty_conf);
+ break;
+ case LDP_IPV4_NODE:
+ case LDP_IPV6_NODE:
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate) {
+ switch (hello_type) {
+ case HELLO_LINK:
+ af_conf->lhello_interval = 0;
+ break;
+ case HELLO_TARGETED:
+ af_conf->thello_interval = 0;
+ break;
+ }
+ } else {
+ switch (hello_type) {
+ case HELLO_LINK:
+ af_conf->lhello_interval = secs;
+ break;
+ case HELLO_TARGETED:
+ af_conf->thello_interval = secs;
+ break;
+ }
+ }
+ ldp_config_apply(vty, vty_conf);
+ break;
+ case LDP_IPV4_IFACE_NODE:
+ case LDP_IPV6_IFACE_NODE:
+ af = ldp_vty_get_af(vty);
+ iface = VTY_GET_CONTEXT(iface);
+ VTY_CHECK_CONTEXT(iface);
+
+ ia = iface_af_get(iface, af);
+ if (negate)
+ ia->hello_interval = 0;
+ else
+ ia->hello_interval = secs;
+
+ ldp_config_apply(vty, vty_conf);
+ break;
+ default:
+ fatalx("ldp_vty_disc_interval: unexpected node");
+ }
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_targeted_hello_accept(struct vty *vty, const char *negate,
+ const char *acl_from_str)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate) {
+ af_conf->flags &= ~F_LDPD_AF_THELLO_ACCEPT;
+ af_conf->acl_thello_accept_from[0] = '\0';
+ } else {
+ af_conf->flags |= F_LDPD_AF_THELLO_ACCEPT;
+ if (acl_from_str)
+ strlcpy(af_conf->acl_thello_accept_from, acl_from_str,
+ sizeof(af_conf->acl_thello_accept_from));
+ else
+ af_conf->acl_thello_accept_from[0] = '\0';
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_nbr_session_holdtime(struct vty *vty, const char *negate,
+ struct in_addr lsr_id, long secs)
+{
+ struct nbr_params *nbrp;
+
+ if (bad_addr_v4(lsr_id)) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ nbrp = nbr_params_find(vty_conf, lsr_id);
+
+ if (negate) {
+ if (nbrp == NULL)
+ return (CMD_SUCCESS);
+
+ nbrp->keepalive = 0;
+ nbrp->flags &= ~F_NBRP_KEEPALIVE;
+ } else {
+ if (nbrp == NULL) {
+ nbrp = nbr_params_new(lsr_id);
+ RB_INSERT(nbrp_head, &vty_conf->nbrp_tree, nbrp);
+ QOBJ_REG(nbrp, nbr_params);
+ } else if (nbrp->keepalive == secs)
+ return (CMD_SUCCESS);
+
+ nbrp->keepalive = secs;
+ nbrp->flags |= F_NBRP_KEEPALIVE;
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_af_session_holdtime(struct vty *vty, const char *negate, long secs)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate)
+ af_conf->keepalive = DEFAULT_KEEPALIVE;
+ else
+ af_conf->keepalive = secs;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_interface(struct vty *vty, const char *negate, const char *ifname)
+{
+ int af;
+ struct iface *iface;
+ struct iface_af *ia;
+
+ if (ifname == NULL) {
+ vty_out (vty, "%% Missing IF name\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ af = ldp_vty_get_af(vty);
+ iface = if_lookup_name(vty_conf, ifname);
+
+ if (negate) {
+ if (iface == NULL)
+ return (CMD_SUCCESS);
+
+ ia = iface_af_get(iface, af);
+ if (ia->enabled == 0)
+ return (CMD_SUCCESS);
+
+ ia->enabled = 0;
+ ia->hello_holdtime = 0;
+ ia->hello_interval = 0;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+ }
+
+ if (iface == NULL) {
+ if (ldp_iface_is_configured(vty_conf, ifname)) {
+ vty_out (vty,"%% Interface is already in use\n");
+ return (CMD_SUCCESS);
+ }
+
+ iface = if_new(ifname);
+ ia = iface_af_get(iface, af);
+ ia->enabled = 1;
+ RB_INSERT(iface_head, &vty_conf->iface_tree, iface);
+ QOBJ_REG(iface, iface);
+
+ ldp_config_apply(vty, vty_conf);
+ } else {
+ ia = iface_af_get(iface, af);
+ if (!ia->enabled) {
+ ia->enabled = 1;
+ ldp_config_apply(vty, vty_conf);
+ }
+ }
+
+ switch (af) {
+ case AF_INET:
+ VTY_PUSH_CONTEXT(LDP_IPV4_IFACE_NODE, iface);
+ break;
+ case AF_INET6:
+ VTY_PUSH_CONTEXT(LDP_IPV6_IFACE_NODE, iface);
+ break;
+ default:
+ break;
+ }
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_trans_addr(struct vty *vty, const char *negate, const char *addr_str)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate)
+ memset(&af_conf->trans_addr, 0, sizeof(af_conf->trans_addr));
+ else {
+ if (addr_str == NULL
+ || inet_pton(af, addr_str, &af_conf->trans_addr) != 1
+ || bad_addr(af, &af_conf->trans_addr)) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_SUCCESS);
+ }
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_neighbor_targeted(struct vty *vty, const char *negate, const char *addr_str)
+{
+ int af;
+ union ldpd_addr addr;
+ struct tnbr *tnbr;
+
+ af = ldp_vty_get_af(vty);
+
+ if (addr_str == NULL || inet_pton(af, addr_str, &addr) != 1 ||
+ bad_addr(af, &addr)) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+ if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&addr.v6)) {
+ vty_out (vty, "%% Address can not be link-local\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ tnbr = tnbr_find(vty_conf, af, &addr);
+
+ if (negate) {
+ if (tnbr == NULL)
+ return (CMD_SUCCESS);
+
+ QOBJ_UNREG(tnbr);
+ RB_REMOVE(tnbr_head, &vty_conf->tnbr_tree, tnbr);
+ free(tnbr);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+ }
+
+ if (tnbr)
+ return (CMD_SUCCESS);
+
+ tnbr = tnbr_new(af, &addr);
+ tnbr->flags |= F_TNBR_CONFIGURED;
+ RB_INSERT(tnbr_head, &vty_conf->tnbr_tree, tnbr);
+ QOBJ_REG(tnbr, tnbr);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_label_advertise(struct vty *vty, const char *negate, const char *acl_to_str,
+ const char *acl_for_str)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate) {
+ af_conf->acl_label_advertise_to[0] = '\0';
+ af_conf->acl_label_advertise_for[0] = '\0';
+ } else {
+ if (acl_to_str)
+ strlcpy(af_conf->acl_label_advertise_to, acl_to_str,
+ sizeof(af_conf->acl_label_advertise_to));
+ else
+ af_conf->acl_label_advertise_to[0] = '\0';
+ if (acl_for_str)
+ strlcpy(af_conf->acl_label_advertise_for, acl_for_str,
+ sizeof(af_conf->acl_label_advertise_for));
+ else
+ af_conf->acl_label_advertise_for[0] = '\0';
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_label_allocate(struct vty *vty, const char *negate, const char *host_routes,
+ const char *acl_for_str)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ af_conf->flags &= ~F_LDPD_AF_ALLOCHOSTONLY;
+ af_conf->acl_label_allocate_for[0] = '\0';
+ if (!negate) {
+ if (host_routes)
+ af_conf->flags |= F_LDPD_AF_ALLOCHOSTONLY;
+ else
+ strlcpy(af_conf->acl_label_allocate_for, acl_for_str,
+ sizeof(af_conf->acl_label_allocate_for));
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_label_expnull(struct vty *vty, const char *negate, const char *acl_for_str)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate) {
+ af_conf->flags &= ~F_LDPD_AF_EXPNULL;
+ af_conf->acl_label_expnull_for[0] = '\0';
+ } else {
+ af_conf->flags |= F_LDPD_AF_EXPNULL;
+ if (acl_for_str)
+ strlcpy(af_conf->acl_label_expnull_for, acl_for_str,
+ sizeof(af_conf->acl_label_expnull_for));
+ else
+ af_conf->acl_label_expnull_for[0] = '\0';
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_label_accept(struct vty *vty, const char *negate, const char *acl_from_str,
+ const char *acl_for_str)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate) {
+ af_conf->acl_label_accept_from[0] = '\0';
+ af_conf->acl_label_accept_for[0] = '\0';
+ } else {
+ if (acl_from_str)
+ strlcpy(af_conf->acl_label_accept_from, acl_from_str,
+ sizeof(af_conf->acl_label_accept_from));
+ else
+ af_conf->acl_label_accept_from[0] = '\0';
+ if (acl_for_str)
+ strlcpy(af_conf->acl_label_accept_for, acl_for_str,
+ sizeof(af_conf->acl_label_accept_for));
+ else
+ af_conf->acl_label_accept_for[0] = '\0';
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_ttl_security(struct vty *vty, const char *negate)
+{
+ struct ldpd_af_conf *af_conf;
+ int af;
+
+ af = ldp_vty_get_af(vty);
+ af_conf = ldp_af_conf_get(vty_conf, af);
+
+ if (negate)
+ af_conf->flags &= ~F_LDPD_AF_NO_GTSM;
+ else
+ af_conf->flags |= F_LDPD_AF_NO_GTSM;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_router_id(struct vty *vty, const char *negate, struct in_addr address)
+{
+ if (negate)
+ vty_conf->rtr_id.s_addr = INADDR_ANY;
+ else {
+ if (bad_addr_v4(address)) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_SUCCESS);
+ }
+ vty_conf->rtr_id = address;
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_ordered_control(struct vty *vty, const char *negate)
+{
+ if (negate)
+ vty_conf->flags &= ~F_LDPD_ORDERED_CONTROL;
+ else
+ vty_conf->flags |= F_LDPD_ORDERED_CONTROL;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int ldp_vty_wait_for_sync_interval(struct vty *vty, const char *negate,
+ long secs)
+{
+ switch (vty->node) {
+ case LDP_NODE:
+ if (negate)
+ vty_conf->wait_for_sync_interval = DFLT_WAIT_FOR_SYNC;
+ else
+ vty_conf->wait_for_sync_interval = secs;
+
+ ldp_config_apply(vty, vty_conf);
+ break;
+ default:
+ fatalx("ldp_vty_wait_for_sync_interval: unexpected node");
+ }
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_allow_broken_lsp(struct vty *vty, const char *negate)
+{
+ if (negate)
+ vty_conf->flags &= ~F_LDPD_ALLOW_BROKEN_LSP;
+ else
+ vty_conf->flags |= F_LDPD_ALLOW_BROKEN_LSP;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_ds_cisco_interop(struct vty *vty, const char * negate)
+{
+ if (negate)
+ vty_conf->flags &= ~F_LDPD_DS_CISCO_INTEROP;
+ else
+ vty_conf->flags |= F_LDPD_DS_CISCO_INTEROP;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_trans_pref_ipv4(struct vty *vty, const char *negate)
+{
+ if (negate)
+ vty_conf->trans_pref = DUAL_STACK_LDPOV6;
+ else
+ vty_conf->trans_pref = DUAL_STACK_LDPOV4;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_neighbor_password(struct vty *vty, const char *negate, struct in_addr lsr_id,
+ const char *password_str)
+{
+ size_t password_len;
+ struct nbr_params *nbrp;
+
+ if (password_str == NULL) {
+ vty_out (vty, "%% Missing password\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ if (bad_addr_v4(lsr_id)) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ nbrp = nbr_params_find(vty_conf, lsr_id);
+
+ if (negate) {
+ if (nbrp == NULL)
+ return (CMD_SUCCESS);
+
+ memset(&nbrp->auth, 0, sizeof(nbrp->auth));
+ nbrp->auth.method = AUTH_NONE;
+ } else {
+ if (nbrp == NULL) {
+ nbrp = nbr_params_new(lsr_id);
+ RB_INSERT(nbrp_head, &vty_conf->nbrp_tree, nbrp);
+ QOBJ_REG(nbrp, nbr_params);
+ } else if (nbrp->auth.method == AUTH_MD5SIG &&
+ strcmp(nbrp->auth.md5key, password_str) == 0)
+ return (CMD_SUCCESS);
+
+ password_len = strlcpy(nbrp->auth.md5key, password_str,
+ sizeof(nbrp->auth.md5key));
+ if (password_len >= sizeof(nbrp->auth.md5key))
+ vty_out(vty, "%% password has been truncated to %zu characters.", sizeof(nbrp->auth.md5key) - 1);
+ nbrp->auth.md5key_len = strlen(nbrp->auth.md5key);
+ nbrp->auth.method = AUTH_MD5SIG;
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_neighbor_ttl_security(struct vty *vty, const char *negate,
+ struct in_addr lsr_id, const char *hops_str)
+{
+ struct nbr_params *nbrp;
+ long int hops = 0;
+ char *ep;
+
+ if (bad_addr_v4(lsr_id)) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ if (hops_str) {
+ hops = strtol(hops_str, &ep, 10);
+ if (*ep != '\0' || hops < 1 || hops > 254) {
+ vty_out (vty, "%% Invalid hop count\n");
+ return (CMD_SUCCESS);
+ }
+ }
+
+ nbrp = nbr_params_find(vty_conf, lsr_id);
+
+ if (negate) {
+ if (nbrp == NULL)
+ return (CMD_SUCCESS);
+
+ nbrp->flags &= ~(F_NBRP_GTSM|F_NBRP_GTSM_HOPS);
+ nbrp->gtsm_enabled = 0;
+ nbrp->gtsm_hops = 0;
+ } else {
+ if (nbrp == NULL) {
+ nbrp = nbr_params_new(lsr_id);
+ RB_INSERT(nbrp_head, &vty_conf->nbrp_tree, nbrp);
+ QOBJ_REG(nbrp, nbr_params);
+ }
+
+ nbrp->flags |= F_NBRP_GTSM;
+ nbrp->flags &= ~F_NBRP_GTSM_HOPS;
+ if (hops_str) {
+ nbrp->gtsm_enabled = 1;
+ nbrp->gtsm_hops = hops;
+ nbrp->flags |= F_NBRP_GTSM_HOPS;
+ } else
+ nbrp->gtsm_enabled = 0;
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn(struct vty *vty, const char *negate, const char *name_str)
+{
+ struct l2vpn *l2vpn;
+ struct l2vpn_if *lif;
+ struct l2vpn_pw *pw;
+
+ if (name_str == NULL) {
+ vty_out (vty, "%% Missing name\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ l2vpn = l2vpn_find(vty_conf, name_str);
+
+ if (negate) {
+ if (l2vpn == NULL)
+ return (CMD_SUCCESS);
+
+ RB_FOREACH(lif, l2vpn_if_head, &l2vpn->if_tree)
+ QOBJ_UNREG(lif);
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
+ QOBJ_UNREG(pw);
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_inactive_tree)
+ QOBJ_UNREG(pw);
+ QOBJ_UNREG(l2vpn);
+ RB_REMOVE(l2vpn_head, &vty_conf->l2vpn_tree, l2vpn);
+ l2vpn_del(l2vpn);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+ }
+
+ if (l2vpn) {
+ VTY_PUSH_CONTEXT(LDP_L2VPN_NODE, l2vpn);
+ return (CMD_SUCCESS);
+ }
+
+ l2vpn = l2vpn_new(name_str);
+ l2vpn->type = L2VPN_TYPE_VPLS;
+ RB_INSERT(l2vpn_head, &vty_conf->l2vpn_tree, l2vpn);
+ QOBJ_REG(l2vpn, l2vpn);
+
+ VTY_PUSH_CONTEXT(LDP_L2VPN_NODE, l2vpn);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_bridge(struct vty *vty, const char *negate, const char *ifname)
+{
+ VTY_DECLVAR_CONTEXT(l2vpn, l2vpn);
+
+ if (negate)
+ memset(l2vpn->br_ifname, 0, sizeof(l2vpn->br_ifname));
+ else {
+ if (ifname == NULL) {
+ vty_out (vty, "%% Missing IF name\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+ strlcpy(l2vpn->br_ifname, ifname, sizeof(l2vpn->br_ifname));
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_mtu(struct vty *vty, const char *negate, long mtu)
+{
+ VTY_DECLVAR_CONTEXT(l2vpn, l2vpn);
+
+ if (negate)
+ l2vpn->mtu = DEFAULT_L2VPN_MTU;
+ else
+ l2vpn->mtu = mtu;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_pwtype(struct vty *vty, const char *negate, const char *type_str)
+{
+ VTY_DECLVAR_CONTEXT(l2vpn, l2vpn);
+ int pw_type;
+
+ if (type_str == NULL) {
+ vty_out (vty, "%% Missing type\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ if (strcmp(type_str, "ethernet") == 0)
+ pw_type = PW_TYPE_ETHERNET;
+ else
+ pw_type = PW_TYPE_ETHERNET_TAGGED;
+
+ if (negate)
+ l2vpn->pw_type = DEFAULT_PW_TYPE;
+ else
+ l2vpn->pw_type = pw_type;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_interface(struct vty *vty, const char *negate, const char *ifname)
+{
+ VTY_DECLVAR_CONTEXT(l2vpn, l2vpn);
+ struct l2vpn_if *lif;
+
+ if (ifname == NULL) {
+ vty_out (vty, "%% Missing IF name\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ lif = l2vpn_if_find(l2vpn, ifname);
+
+ if (negate) {
+ if (lif == NULL)
+ return (CMD_SUCCESS);
+
+ QOBJ_UNREG(lif);
+ RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
+ free(lif);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+ }
+
+ if (lif)
+ return (CMD_SUCCESS);
+
+ if (ldp_iface_is_configured(vty_conf, ifname)) {
+ vty_out (vty, "%% Interface is already in use\n");
+ return (CMD_SUCCESS);
+ }
+
+ lif = l2vpn_if_new(l2vpn, ifname);
+ RB_INSERT(l2vpn_if_head, &l2vpn->if_tree, lif);
+ QOBJ_REG(lif, l2vpn_if);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_pseudowire(struct vty *vty, const char *negate, const char *ifname)
+{
+ VTY_DECLVAR_CONTEXT(l2vpn, l2vpn);
+ struct l2vpn_pw *pw;
+
+ if (ifname == NULL) {
+ vty_out (vty, "%% Missing IF name\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ pw = l2vpn_pw_find(l2vpn, ifname);
+
+ if (negate) {
+ if (pw == NULL)
+ return (CMD_SUCCESS);
+
+ QOBJ_UNREG(pw);
+ if (pw->lsr_id.s_addr == INADDR_ANY || pw->pwid == 0)
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ else
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
+ free(pw);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+ }
+
+ if (pw) {
+ VTY_PUSH_CONTEXT_SUB(LDP_PSEUDOWIRE_NODE, pw);
+ return (CMD_SUCCESS);
+ }
+
+ if (ldp_iface_is_configured(vty_conf, ifname)) {
+ vty_out (vty, "%% Interface is already in use\n");
+ return (CMD_SUCCESS);
+ }
+
+ pw = l2vpn_pw_new(l2vpn, ifname);
+ pw->flags = F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF;
+ RB_INSERT(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ QOBJ_REG(pw, l2vpn_pw);
+
+ VTY_PUSH_CONTEXT_SUB(LDP_PSEUDOWIRE_NODE, pw);
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_pw_cword(struct vty *vty, const char *negate, const char *preference_str)
+{
+ VTY_DECLVAR_CONTEXT_SUB(l2vpn_pw, pw);
+
+ if (negate)
+ pw->flags |= F_PW_CWORD_CONF;
+ else {
+ if (!preference_str) {
+ vty_out (vty, "%% Missing preference\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+ if (preference_str[0] == 'e')
+ pw->flags &= ~F_PW_CWORD_CONF;
+ else
+ pw->flags |= F_PW_CWORD_CONF;
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_pw_nbr_addr(struct vty *vty, const char *negate, const char *addr_str)
+{
+ VTY_DECLVAR_CONTEXT_SUB(l2vpn_pw, pw);
+ int af;
+ union ldpd_addr addr;
+
+ if (ldp_get_address(addr_str, &af, &addr) == -1 ||
+ bad_addr(af, &addr)) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ if (negate) {
+ pw->af = AF_UNSPEC;
+ memset(&pw->addr, 0, sizeof(pw->addr));
+ pw->flags &= ~F_PW_STATIC_NBR_ADDR;
+ } else {
+ pw->af = af;
+ pw->addr = addr;
+ pw->flags |= F_PW_STATIC_NBR_ADDR;
+ }
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_pw_nbr_id(struct vty *vty, const char *negate, struct in_addr lsr_id)
+{
+ VTY_DECLVAR_CONTEXT_SUB(l2vpn_pw, pw);
+
+ if (bad_addr_v4(lsr_id)) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_WARNING_CONFIG_FAILED);
+ }
+
+ if (negate)
+ pw->lsr_id.s_addr = INADDR_ANY;
+ else
+ pw->lsr_id = lsr_id;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_pw_pwid(struct vty *vty, const char *negate, long pwid)
+{
+ VTY_DECLVAR_CONTEXT_SUB(l2vpn_pw, pw);
+
+ if (negate)
+ pw->pwid = 0;
+ else
+ pw->pwid = pwid;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+int
+ldp_vty_l2vpn_pw_pwstatus(struct vty *vty, const char *negate)
+{
+ VTY_DECLVAR_CONTEXT_SUB(l2vpn_pw, pw);
+
+ if (negate)
+ pw->flags |= F_PW_STATUSTLV_CONF;
+ else
+ pw->flags &= ~F_PW_STATUSTLV_CONF;
+
+ ldp_config_apply(vty, vty_conf);
+
+ return (CMD_SUCCESS);
+}
+
+struct iface *
+iface_new_api(struct ldpd_conf *conf, const char *name)
+{
+ const char *ifname = name;
+ struct iface *iface;
+
+ if (ldp_iface_is_configured(conf, ifname))
+ return (NULL);
+
+ iface = if_new(name);
+ RB_INSERT(iface_head, &conf->iface_tree, iface);
+ QOBJ_REG(iface, iface);
+ return (iface);
+}
+
+void
+iface_del_api(struct ldpd_conf *conf, struct iface *iface)
+{
+ QOBJ_UNREG(iface);
+ RB_REMOVE(iface_head, &conf->iface_tree, iface);
+ free(iface);
+}
+
+struct tnbr *
+tnbr_new_api(struct ldpd_conf *conf, int af, union ldpd_addr *addr)
+{
+ struct tnbr *tnbr;
+
+ if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&addr->v6))
+ return (NULL);
+
+ if (tnbr_find(conf, af, addr))
+ return (NULL);
+
+ tnbr = tnbr_new(af, addr);
+ tnbr->flags |= F_TNBR_CONFIGURED;
+ RB_INSERT(tnbr_head, &conf->tnbr_tree, tnbr);
+ QOBJ_REG(tnbr, tnbr);
+ return (tnbr);
+}
+
+void
+tnbr_del_api(struct ldpd_conf *conf, struct tnbr *tnbr)
+{
+ QOBJ_UNREG(tnbr);
+ RB_REMOVE(tnbr_head, &conf->tnbr_tree, tnbr);
+ free(tnbr);
+}
+
+struct nbr_params *
+nbrp_new_api(struct ldpd_conf *conf, struct in_addr lsr_id)
+{
+ struct nbr_params *nbrp;
+
+ if (nbr_params_find(conf, lsr_id))
+ return (NULL);
+
+ nbrp = nbr_params_new(lsr_id);
+ RB_INSERT(nbrp_head, &conf->nbrp_tree, nbrp);
+ QOBJ_REG(nbrp, nbr_params);
+ return (nbrp);
+}
+
+void
+nbrp_del_api(struct ldpd_conf *conf, struct nbr_params *nbrp)
+{
+ QOBJ_UNREG(nbrp);
+ RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
+ free(nbrp);
+}
+
+struct l2vpn *
+l2vpn_new_api(struct ldpd_conf *conf, const char *name)
+{
+ struct l2vpn *l2vpn;
+
+ if (l2vpn_find(conf, name))
+ return (NULL);
+
+ l2vpn = l2vpn_new(name);
+ l2vpn->type = L2VPN_TYPE_VPLS;
+ RB_INSERT(l2vpn_head, &conf->l2vpn_tree, l2vpn);
+ QOBJ_REG(l2vpn, l2vpn);
+ return (l2vpn);
+}
+
+void
+l2vpn_del_api(struct ldpd_conf *conf, struct l2vpn *l2vpn)
+{
+ struct l2vpn_if *lif;
+ struct l2vpn_pw *pw;
+
+ while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+ lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
+ QOBJ_UNREG(lif);
+ RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
+ free(lif);
+ }
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
+ QOBJ_UNREG(pw);
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
+ free(pw);
+ }
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
+ QOBJ_UNREG(pw);
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ free(pw);
+ }
+ QOBJ_UNREG(l2vpn);
+ RB_REMOVE(l2vpn_head, &conf->l2vpn_tree, l2vpn);
+ free(l2vpn);
+}
+
+struct l2vpn_if *
+l2vpn_if_new_api(struct ldpd_conf *conf, struct l2vpn *l2vpn,
+ const char *ifname)
+{
+ struct l2vpn_if *lif;
+
+ if (ldp_iface_is_configured(conf, ifname))
+ return (NULL);
+
+ lif = l2vpn_if_new(l2vpn, ifname);
+ RB_INSERT(l2vpn_if_head, &l2vpn->if_tree, lif);
+ QOBJ_REG(lif, l2vpn_if);
+ return (lif);
+}
+
+void
+l2vpn_if_del_api(struct l2vpn *l2vpn, struct l2vpn_if *lif)
+{
+ QOBJ_UNREG(lif);
+ RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
+ free(lif);
+}
+
+struct l2vpn_pw *
+l2vpn_pw_new_api(struct ldpd_conf *conf, struct l2vpn *l2vpn,
+ const char *ifname)
+{
+ struct l2vpn_pw *pw;
+
+ if (ldp_iface_is_configured(conf, ifname))
+ return (NULL);
+
+ pw = l2vpn_pw_new(l2vpn, ifname);
+ pw->flags = F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF;
+ RB_INSERT(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ QOBJ_REG(pw, l2vpn_pw);
+ return (pw);
+}
+
+void
+l2vpn_pw_del_api(struct l2vpn *l2vpn, struct l2vpn_pw *pw)
+{
+ QOBJ_UNREG(pw);
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ free(pw);
+}
diff --git a/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c
new file mode 100644
index 0000000..906b5c1
--- /dev/null
+++ b/ldpd/ldp_vty_exec.c
@@ -0,0 +1,2145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 by Open Source Routing.
+ */
+
+#include <zebra.h>
+#include <sys/un.h>
+#include "lib/printfrr.h"
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+#include "ldp_vty.h"
+#include "lib/json.h"
+
+#include "command.h"
+#include "vty.h"
+#include "mpls.h"
+
+enum show_command {
+ SHOW_DISC,
+ SHOW_IFACE,
+ SHOW_NBR,
+ SHOW_LIB,
+ SHOW_L2VPN_PW,
+ SHOW_L2VPN_BINDING,
+ SHOW_LDP_SYNC
+};
+
+struct show_params {
+ int family;
+ union ldpd_addr addr;
+ uint8_t prefixlen;
+ int detail;
+ int json;
+ union {
+ struct {
+ struct in_addr lsr_id;
+ int capabilities;
+ } neighbor;
+ struct {
+ struct prefix prefix;
+ int longer_prefixes;
+ struct in_addr neighbor;
+ uint32_t local_label;
+ uint32_t remote_label;
+ } lib;
+ struct {
+ struct in_addr peer;
+ uint32_t local_label;
+ uint32_t remote_label;
+ char ifname[IFNAMSIZ];
+ uint32_t vcid;
+ } l2vpn;
+ };
+};
+
+#define LDPBUFSIZ 65535
+
+static int show_interface_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static int show_interface_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+static int show_discovery_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static void show_discovery_detail_adj(struct vty *, char *,
+ struct ctl_adj *);
+static int show_discovery_detail_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static int show_discovery_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+static void show_discovery_detail_adj_json(json_object *,
+ struct ctl_adj *);
+static int show_discovery_detail_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+static int show_ldp_sync_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static int show_ldp_sync_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+
+static int show_nbr_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static int show_nbr_msg_json(struct imsg *, struct show_params *,
+ json_object *);
+static void show_nbr_detail_adj(struct vty *, char *,
+ struct ctl_adj *);
+static int show_nbr_detail_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static void show_nbr_detail_adj_json(struct ctl_adj *,
+ json_object *);
+static int show_nbr_detail_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+static void show_nbr_capabilities(struct vty *, struct ctl_nbr *);
+static int show_nbr_capabilities_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static void show_nbr_capabilities_json(struct ctl_nbr *,
+ json_object *);
+static int show_nbr_capabilities_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+static int show_lib_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static int show_lib_detail_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static int show_lib_msg_json(struct imsg *, struct show_params *,
+ json_object *);
+static int show_lib_detail_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+static int show_l2vpn_binding_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static int show_l2vpn_binding_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+static int show_l2vpn_pw_msg(struct vty *, struct imsg *,
+ struct show_params *);
+static int show_l2vpn_pw_msg_json(struct imsg *,
+ struct show_params *, json_object *);
+static int ldp_vty_dispatch_msg(struct vty *, struct imsg *,
+ enum show_command, struct show_params *,
+ json_object *);
+static int ldp_vty_dispatch(struct vty *, struct imsgbuf *,
+ enum show_command, struct show_params *);
+static int ldp_vty_get_af(const char *, int *);
+
+static int
+show_interface_msg(struct vty *vty, struct imsg *imsg,
+ struct show_params *params)
+{
+ struct ctl_iface *iface;
+ char timers[BUFSIZ];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_INTERFACE:
+ iface = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != iface->af)
+ break;
+
+ snprintf(timers, sizeof(timers), "%u/%u",
+ iface->hello_interval, iface->hello_holdtime);
+
+ vty_out (vty, "%-4s %-11s %-6s %-8s %-12s %3u\n",
+ af_name(iface->af), iface->name,
+ if_state_name(iface->state), iface->uptime == 0 ?
+ "00:00:00" : log_time(iface->uptime), timers,
+ iface->adj_cnt);
+ break;
+ case IMSG_CTL_END:
+ vty_out (vty, "\n");
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_interface_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_iface *iface;
+ json_object *json_iface;
+ char key_name[64];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_INTERFACE:
+ iface = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != iface->af)
+ break;
+
+ json_iface = json_object_new_object();
+ json_object_string_add(json_iface, "name", iface->name);
+ json_object_string_add(json_iface, "addressFamily",
+ af_name(iface->af));
+ json_object_string_add(json_iface, "state",
+ if_state_name(iface->state));
+ json_object_string_add(json_iface, "upTime",
+ log_time(iface->uptime));
+ json_object_int_add(json_iface, "helloInterval",
+ iface->hello_interval);
+ json_object_int_add(json_iface, "helloHoldtime",
+ iface->hello_holdtime);
+ json_object_int_add(json_iface, "adjacencyCount",
+ iface->adj_cnt);
+
+ snprintf(key_name, sizeof(key_name), "%s: %s", iface->name,
+ af_name(iface->af));
+ json_object_object_add(json, key_name, json_iface);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_ldp_sync_msg(struct vty *vty, struct imsg *imsg,
+ struct show_params *params)
+{
+ struct ctl_ldp_sync *iface;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LDP_SYNC:
+ iface = imsg->data;
+
+ vty_out (vty, "%s:\n", iface->name);
+ if (iface->in_sync)
+ vty_out (vty, " Status: initial label exchange complete\n");
+ else
+ vty_out (vty, " Status: label exchange not complete\n");
+
+ if (iface->timer_running) {
+ vty_out (vty, " Wait time: %d seconds (%d seconds left)\n",
+ iface->wait_time, iface->wait_time_remaining);
+ vty_out (vty, " Timer is running\n");
+ } else {
+ vty_out (vty, " Wait time: %d seconds\n",
+ iface->wait_time);
+ vty_out (vty, " Timer is not running\n");
+ }
+
+ if (iface->peer_ldp_id.s_addr)
+ vty_out (vty, " Peer LDP Identifier: %pI4:0\n",
+ &iface->peer_ldp_id);
+
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_ldp_sync_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_ldp_sync *iface;
+ json_object *json_iface;
+ char buf[PREFIX_STRLEN];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LDP_SYNC:
+ iface = imsg->data;
+
+ json_iface = json_object_new_object();
+ json_object_string_add(json_iface, "state",
+ iface->in_sync
+ ? "labelExchangeComplete"
+ : "labelExchangeNotComplete");
+ json_object_int_add(json_iface, "waitTime",
+ iface->wait_time);
+ json_object_int_add(json_iface, "waitTimeRemaining",
+ iface->wait_time_remaining);
+
+ if (iface->timer_running)
+ json_object_boolean_true_add(json_iface, "timerRunning");
+ else
+ json_object_boolean_false_add(json_iface, "timerRunning");
+
+ json_object_string_add(json_iface, "peerLdpId",
+ iface->peer_ldp_id.s_addr ?
+ inet_ntop(AF_INET, &iface->peer_ldp_id, buf, sizeof(buf)) :
+ "");
+
+ json_object_object_add(json, iface->name, json_iface);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_discovery_msg(struct vty *vty, struct imsg *imsg,
+ struct show_params *params)
+{
+ struct ctl_adj *adj;
+ const char *addr;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_DISCOVERY:
+ adj = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != adj->af)
+ break;
+
+ vty_out(vty, "%-4s %-15pI4 ", af_name(adj->af), &adj->id);
+ switch(adj->type) {
+ case HELLO_LINK:
+ vty_out(vty, "%-8s %-15s ", "Link", adj->ifname);
+ break;
+ case HELLO_TARGETED:
+ addr = log_addr(adj->af, &adj->src_addr);
+
+ vty_out(vty, "%-8s %-15s ", "Targeted", addr);
+ if (strlen(addr) > 15)
+ vty_out(vty, "\n%46s", " ");
+ break;
+ }
+ vty_out (vty, "%9u\n", adj->holdtime);
+ break;
+ case IMSG_CTL_END:
+ vty_out (vty, "\n");
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static void
+show_discovery_detail_adj(struct vty *vty, char *buffer, struct ctl_adj *adj)
+{
+ size_t buflen = strlen(buffer);
+
+ snprintfrr(buffer + buflen, LDPBUFSIZ - buflen,
+ " LSR Id: %pI4:0\n", &adj->id);
+ buflen = strlen(buffer);
+ snprintf(buffer + buflen, LDPBUFSIZ - buflen,
+ " Source address: %s\n",
+ log_addr(adj->af, &adj->src_addr));
+ buflen = strlen(buffer);
+ snprintf(buffer + buflen, LDPBUFSIZ - buflen,
+ " Transport address: %s\n",
+ log_addr(adj->af, &adj->trans_addr));
+ buflen = strlen(buffer);
+ snprintf(buffer + buflen, LDPBUFSIZ - buflen,
+ " Hello hold time: %u secs (due in %u secs)\n",
+ adj->holdtime, adj->holdtime_remaining);
+ buflen = strlen(buffer);
+ snprintf(buffer + buflen, LDPBUFSIZ - buflen,
+ " Dual-stack capability TLV: %s\n",
+ (adj->ds_tlv) ? "yes" : "no");
+}
+
+static int
+show_discovery_detail_msg(struct vty *vty, struct imsg *imsg,
+ struct show_params *params)
+{
+ struct ctl_adj *adj;
+ struct ctl_disc_if *iface;
+ struct ctl_disc_tnbr *tnbr;
+ struct in_addr rtr_id;
+ union ldpd_addr *trans_addr;
+ size_t buflen;
+ static char ifaces_buffer[LDPBUFSIZ];
+ static char tnbrs_buffer[LDPBUFSIZ];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_DISCOVERY:
+ ifaces_buffer[0] = '\0';
+ tnbrs_buffer[0] = '\0';
+ break;
+ case IMSG_CTL_SHOW_DISC_IFACE:
+ iface = imsg->data;
+
+ if (params->family != AF_UNSPEC &&
+ ((params->family == AF_INET && !iface->active_v4) ||
+ (params->family == AF_INET6 && !iface->active_v6)))
+ break;
+
+ buflen = strlen(ifaces_buffer);
+ snprintf(ifaces_buffer + buflen, LDPBUFSIZ - buflen,
+ " %s: %s\n", iface->name, (iface->no_adj) ?
+ "(no adjacencies)" : "");
+ break;
+ case IMSG_CTL_SHOW_DISC_TNBR:
+ tnbr = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != tnbr->af)
+ break;
+
+ trans_addr = &(ldp_af_conf_get(ldpd_conf,
+ tnbr->af))->trans_addr;
+ buflen = strlen(tnbrs_buffer);
+ snprintf(tnbrs_buffer + buflen, LDPBUFSIZ - buflen,
+ " %s -> %s: %s\n", log_addr(tnbr->af, trans_addr),
+ log_addr(tnbr->af, &tnbr->addr), (tnbr->no_adj) ?
+ "(no adjacencies)" : "");
+ break;
+ case IMSG_CTL_SHOW_DISC_ADJ:
+ adj = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != adj->af)
+ break;
+
+ switch(adj->type) {
+ case HELLO_LINK:
+ show_discovery_detail_adj(vty, ifaces_buffer, adj);
+ break;
+ case HELLO_TARGETED:
+ show_discovery_detail_adj(vty, tnbrs_buffer, adj);
+ break;
+ }
+ break;
+ case IMSG_CTL_END:
+ rtr_id.s_addr = ldp_rtr_id_get(ldpd_conf);
+ vty_out (vty, "Local:\n");
+ vty_out (vty, " LSR Id: %pI4:0\n",&rtr_id);
+ if (CHECK_FLAG(ldpd_conf->ipv4.flags, F_LDPD_AF_ENABLED))
+ vty_out (vty, " Transport Address (IPv4): %s\n",
+ log_addr(AF_INET, &ldpd_conf->ipv4.trans_addr));
+ if (CHECK_FLAG(ldpd_conf->ipv6.flags, F_LDPD_AF_ENABLED))
+ vty_out (vty, " Transport Address (IPv6): %s\n",
+ log_addr(AF_INET6, &ldpd_conf->ipv6.trans_addr));
+ vty_out (vty, "Discovery Sources:\n");
+ vty_out (vty, " Interfaces:\n");
+ vty_out(vty, "%s", ifaces_buffer);
+ vty_out (vty, " Targeted Hellos:\n");
+ vty_out(vty, "%s", tnbrs_buffer);
+ vty_out (vty, "\n");
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_discovery_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_adj *adj;
+ json_object *json_array;
+ json_object *json_adj;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_DISCOVERY:
+ adj = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != adj->af)
+ break;
+
+ json_object_object_get_ex(json, "adjacencies", &json_array);
+ if (!json_array) {
+ json_array = json_object_new_array();
+ json_object_object_add(json, "adjacencies", json_array);
+ }
+
+ json_adj = json_object_new_object();
+ json_object_string_add(json_adj, "addressFamily",
+ af_name(adj->af));
+ json_object_string_addf(json_adj, "neighborId", "%pI4",
+ &adj->id);
+ switch(adj->type) {
+ case HELLO_LINK:
+ json_object_string_add(json_adj, "type", "link");
+ json_object_string_add(json_adj, "interface",
+ adj->ifname);
+ break;
+ case HELLO_TARGETED:
+ json_object_string_add(json_adj, "type", "targeted");
+ json_object_string_add(json_adj, "peer",
+ log_addr(adj->af, &adj->src_addr));
+ break;
+ }
+ json_object_int_add(json_adj, "helloHoldtime", adj->holdtime);
+
+ json_object_array_add(json_array, json_adj);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static void
+show_discovery_detail_adj_json(json_object *json, struct ctl_adj *adj)
+{
+ json_object *json_adj;
+ json_object *json_array;
+
+ json_object_object_get_ex(json, "adjacencies", &json_array);
+ if (!json_array) {
+ json_array = json_object_new_array();
+ json_object_object_add(json, "adjacencies", json_array);
+ }
+
+ json_adj = json_object_new_object();
+ json_object_string_addf(json_adj, "lsrId", "%pI4", &adj->id);
+ json_object_string_add(json_adj, "sourceAddress", log_addr(adj->af,
+ &adj->src_addr));
+ json_object_string_add(json_adj, "transportAddress", log_addr(adj->af,
+ &adj->trans_addr));
+ json_object_int_add(json_adj, "helloHoldtime", adj->holdtime);
+ json_object_int_add(json_adj, "helloHoldtimeRemaining",
+ adj->holdtime_remaining);
+ json_object_int_add(json_adj, "dualStackCapabilityTlv",
+ adj->ds_tlv);
+ json_object_array_add(json_array, json_adj);
+}
+
+static int
+show_discovery_detail_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_adj *adj;
+ struct ctl_disc_if *iface;
+ struct ctl_disc_tnbr *tnbr;
+ struct in_addr rtr_id;
+ union ldpd_addr *trans_addr;
+ json_object *json_interface;
+ json_object *json_target;
+ static json_object *json_interfaces;
+ static json_object *json_targets;
+ static json_object *json_container;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_DISCOVERY:
+ rtr_id.s_addr = ldp_rtr_id_get(ldpd_conf);
+ json_object_string_addf(json, "lsrId", "%pI4", &rtr_id);
+ if (CHECK_FLAG(ldpd_conf->ipv4.flags, F_LDPD_AF_ENABLED))
+ json_object_string_add(json, "transportAddressIPv4",
+ log_addr(AF_INET, &ldpd_conf->ipv4.trans_addr));
+ if (CHECK_FLAG(ldpd_conf->ipv6.flags, F_LDPD_AF_ENABLED))
+ json_object_string_add(json, "transportAddressIPv6",
+ log_addr(AF_INET6, &ldpd_conf->ipv6.trans_addr));
+ json_interfaces = json_object_new_object();
+ json_object_object_add(json, "interfaces", json_interfaces);
+ json_targets = json_object_new_object();
+ json_object_object_add(json, "targetedHellos", json_targets);
+ json_container = NULL;
+ break;
+ case IMSG_CTL_SHOW_DISC_IFACE:
+ iface = imsg->data;
+
+ if (params->family != AF_UNSPEC &&
+ ((params->family == AF_INET && !iface->active_v4) ||
+ (params->family == AF_INET6 && !iface->active_v6)))
+ break;
+
+ json_interface = json_object_new_object();
+ json_object_object_add(json_interfaces, iface->name,
+ json_interface);
+ json_container = json_interface;
+ break;
+ case IMSG_CTL_SHOW_DISC_TNBR:
+ tnbr = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != tnbr->af)
+ break;
+
+ trans_addr = &(ldp_af_conf_get(ldpd_conf, tnbr->af))->trans_addr;
+
+ json_target = json_object_new_object();
+ json_object_string_add(json_target, "sourceAddress",
+ log_addr(tnbr->af, trans_addr));
+ json_object_object_add(json_targets, log_addr(tnbr->af,
+ &tnbr->addr), json_target);
+ json_container = json_target;
+ break;
+ case IMSG_CTL_SHOW_DISC_ADJ:
+ adj = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != adj->af)
+ break;
+
+ switch(adj->type) {
+ case HELLO_LINK:
+ show_discovery_detail_adj_json(json_container, adj);
+ break;
+ case HELLO_TARGETED:
+ show_discovery_detail_adj_json(json_container, adj);
+ break;
+ }
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_nbr_msg(struct vty *vty, struct imsg *imsg, struct show_params *params)
+{
+ struct ctl_nbr *nbr;
+ const char *addr;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_NBR:
+ nbr = imsg->data;
+
+ addr = log_addr(nbr->af, &nbr->raddr);
+
+ vty_out(vty, "%-4s %-15pI4 %-11s %-15s",
+ af_name(nbr->af), &nbr->id,
+ nbr_state_name(nbr->nbr_state), addr);
+ if (strlen(addr) > 15)
+ vty_out(vty, "\n%48s", " ");
+ vty_out (vty, " %8s\n", log_time(nbr->uptime));
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static void
+show_nbr_detail_adj(struct vty *vty, char *buffer, struct ctl_adj *adj)
+{
+ size_t buflen = strlen(buffer);
+
+ switch (adj->type) {
+ case HELLO_LINK:
+ snprintf(buffer + buflen, LDPBUFSIZ - buflen,
+ " Interface: %s\n", adj->ifname);
+ break;
+ case HELLO_TARGETED:
+ snprintf(buffer + buflen, LDPBUFSIZ - buflen,
+ " Targeted Hello: %s\n", log_addr(adj->af,
+ &adj->src_addr));
+ break;
+ }
+}
+
+static int
+show_nbr_detail_msg(struct vty *vty, struct imsg *imsg,
+ struct show_params *params)
+{
+ struct ctl_nbr *nbr;
+ struct ldp_stats *stats;
+ struct ctl_adj *adj;
+ static char v4adjs_buffer[LDPBUFSIZ];
+ static char v6adjs_buffer[LDPBUFSIZ];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_NBR:
+ nbr = imsg->data;
+
+ v4adjs_buffer[0] = '\0';
+ v6adjs_buffer[0] = '\0';
+ vty_out (vty, "Peer LDP Identifier: %pI4:0\n",
+ &nbr->id);
+ vty_out (vty, " TCP connection: %s:%u - %s:%u\n",
+ log_addr(nbr->af, &nbr->laddr), ntohs(nbr->lport),
+ log_addr(nbr->af, &nbr->raddr),ntohs(nbr->rport));
+ vty_out (vty, " Authentication: %s\n",
+ (nbr->auth_method == AUTH_MD5SIG) ? "TCP MD5 Signature" : "none");
+ vty_out(vty, " Session Holdtime: %u secs; KeepAlive interval: %u secs\n", nbr->holdtime,
+ nbr->holdtime / KEEPALIVE_PER_PERIOD);
+ vty_out(vty, " State: %s; Downstream-Unsolicited\n",
+ nbr_state_name(nbr->nbr_state));
+ vty_out (vty, " Up time: %s\n",log_time(nbr->uptime));
+
+ stats = &nbr->stats;
+ vty_out (vty, " Messages sent/rcvd:\n");
+ vty_out (vty, " - Keepalive Messages: %u/%u\n",
+ stats->kalive_sent, stats->kalive_rcvd);
+ vty_out (vty, " - Address Messages: %u/%u\n",
+ stats->addr_sent, stats->addr_rcvd);
+ vty_out (vty, " - Address Withdraw Messages: %u/%u\n",
+ stats->addrwdraw_sent, stats->addrwdraw_rcvd);
+ vty_out (vty, " - Notification Messages: %u/%u\n",
+ stats->notif_sent, stats->notif_rcvd);
+ vty_out (vty, " - Capability Messages: %u/%u\n",
+ stats->capability_sent, stats->capability_rcvd);
+ vty_out (vty, " - Label Mapping Messages: %u/%u\n",
+ stats->labelmap_sent, stats->labelmap_rcvd);
+ vty_out (vty, " - Label Request Messages: %u/%u\n",
+ stats->labelreq_sent, stats->labelreq_rcvd);
+ vty_out (vty, " - Label Withdraw Messages: %u/%u\n",
+ stats->labelwdraw_sent, stats->labelwdraw_rcvd);
+ vty_out (vty, " - Label Release Messages: %u/%u\n",
+ stats->labelrel_sent, stats->labelrel_rcvd);
+ vty_out (vty, " - Label Abort Request Messages: %u/%u\n",
+ stats->labelabreq_sent, stats->labelabreq_rcvd);
+
+ show_nbr_capabilities(vty, nbr);
+ break;
+ case IMSG_CTL_SHOW_NBR_DISC:
+ adj = imsg->data;
+
+ switch (adj->af) {
+ case AF_INET:
+ show_nbr_detail_adj(vty, v4adjs_buffer, adj);
+ break;
+ case AF_INET6:
+ show_nbr_detail_adj(vty, v6adjs_buffer, adj);
+ break;
+ default:
+ fatalx("show_nbr_detail_msg: unknown af");
+ }
+ break;
+ case IMSG_CTL_SHOW_NBR_END:
+ vty_out (vty, " LDP Discovery Sources:\n");
+ if (v4adjs_buffer[0] != '\0') {
+ vty_out (vty, " IPv4:\n");
+ vty_out(vty, "%s", v4adjs_buffer);
+ }
+ if (v6adjs_buffer[0] != '\0') {
+ vty_out (vty, " IPv6:\n");
+ vty_out(vty, "%s", v6adjs_buffer);
+ }
+ vty_out (vty, "\n");
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_nbr_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_nbr *nbr;
+ json_object *json_array;
+ json_object *json_nbr;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_NBR:
+ nbr = imsg->data;
+
+ json_object_object_get_ex(json, "neighbors", &json_array);
+ if (!json_array) {
+ json_array = json_object_new_array();
+ json_object_object_add(json, "neighbors", json_array);
+ }
+
+ json_nbr = json_object_new_object();
+ json_object_string_add(json_nbr, "addressFamily",
+ af_name(nbr->af));
+ json_object_string_addf(json_nbr, "neighborId", "%pI4",
+ &nbr->id);
+ json_object_string_add(json_nbr, "state",
+ nbr_state_name(nbr->nbr_state));
+ json_object_string_add(json_nbr, "transportAddress",
+ log_addr(nbr->af, &nbr->raddr));
+ json_object_string_add(json_nbr, "upTime",
+ log_time(nbr->uptime));
+
+ json_object_array_add(json_array, json_nbr);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static void
+show_nbr_detail_adj_json(struct ctl_adj *adj, json_object *adj_list)
+{
+ char adj_string[128];
+
+ switch (adj->type) {
+ case HELLO_LINK:
+ strlcpy(adj_string, "interface: ", sizeof(adj_string));
+ strlcat(adj_string, adj->ifname, sizeof(adj_string));
+ break;
+ case HELLO_TARGETED:
+ strlcpy(adj_string, "targetedHello: ", sizeof(adj_string));
+ strlcat(adj_string, log_addr(adj->af, &adj->src_addr),
+ sizeof(adj_string));
+ break;
+ }
+
+ json_object_array_add(adj_list, json_object_new_string(adj_string));
+}
+
+static int
+show_nbr_detail_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_nbr *nbr;
+ struct ldp_stats *stats;
+ struct ctl_adj *adj;
+ char buf[PREFIX_STRLEN];
+ json_object *json_nbr;
+ json_object *json_array;
+ json_object *json_counter;
+ static json_object *json_nbr_sources;
+ static json_object *json_v4adjs;
+ static json_object *json_v6adjs;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_NBR:
+ nbr = imsg->data;
+
+ json_nbr = json_object_new_object();
+ json_object_object_add(json,
+ inet_ntop(AF_INET, &nbr->id, buf,
+ sizeof(buf)), json_nbr);
+ json_object_string_addf(json_nbr, "peerId", "%pI4", &nbr->id);
+ json_object_string_add(json_nbr, "tcpLocalAddress",
+ log_addr(nbr->af, &nbr->laddr));
+ json_object_int_add(json_nbr, "tcpLocalPort",
+ ntohs(nbr->lport));
+ json_object_string_add(json_nbr, "tcpRemoteAddress",
+ log_addr(nbr->af, &nbr->raddr));
+ json_object_int_add(json_nbr, "tcpRemotePort",
+ ntohs(nbr->rport));
+ json_object_string_add(json_nbr, "authentication",
+ (nbr->auth_method == AUTH_MD5SIG) ? "TCP MD5 Signature" :
+ "none");
+ json_object_int_add(json_nbr, "sessionHoldtime", nbr->holdtime);
+ json_object_int_add(json_nbr, "keepAliveInterval",
+ nbr->holdtime / KEEPALIVE_PER_PERIOD);
+ json_object_string_add(json_nbr, "state",
+ nbr_state_name(nbr->nbr_state));
+ json_object_string_add(json_nbr, "upTime",
+ log_time(nbr->uptime));
+
+ /* message_counters */
+ stats = &nbr->stats;
+ json_array = json_object_new_array();
+ json_object_object_add(json_nbr, "sentMessages", json_array);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "keepalive",
+ stats->kalive_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "address",
+ stats->addr_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "addressWithdraw",
+ stats->addrwdraw_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "notification",
+ stats->notif_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "capability",
+ stats->capability_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelMapping",
+ stats->labelmap_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelRequest",
+ stats->labelreq_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelWithdraw",
+ stats->labelwdraw_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelRelease",
+ stats->labelrel_sent);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelAbortRequest",
+ stats->labelabreq_sent);
+ json_object_array_add(json_array, json_counter);
+
+ json_array = json_object_new_array();
+ json_object_object_add(json_nbr, "receivedMessages", json_array);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "keepalive",
+ stats->kalive_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "address",
+ stats->addr_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "addressWithdraw",
+ stats->addrwdraw_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "notification",
+ stats->notif_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "capability",
+ stats->capability_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelMapping",
+ stats->labelmap_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelRequest",
+ stats->labelreq_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelWithdraw",
+ stats->labelwdraw_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelRelease",
+ stats->labelrel_rcvd);
+ json_object_array_add(json_array, json_counter);
+ json_counter = json_object_new_object();
+ json_object_int_add(json_counter, "labelAbortRequest",
+ stats->labelabreq_rcvd);
+ json_object_array_add(json_array, json_counter);
+
+ /* capabilities */
+ show_nbr_capabilities_json(nbr, json_nbr);
+
+ /* discovery sources */
+ json_nbr_sources = json_object_new_object();
+ json_object_object_add(json_nbr, "discoverySources",
+ json_nbr_sources);
+ json_v4adjs = NULL;
+ json_v6adjs = NULL;
+ break;
+ case IMSG_CTL_SHOW_NBR_DISC:
+ adj = imsg->data;
+
+ switch (adj->af) {
+ case AF_INET:
+ if (!json_v4adjs) {
+ json_v4adjs = json_object_new_array();
+ json_object_object_add(json_nbr_sources, "ipv4",
+ json_v4adjs);
+ }
+ show_nbr_detail_adj_json(adj, json_v4adjs);
+ break;
+ case AF_INET6:
+ if (!json_v6adjs) {
+ json_v6adjs = json_object_new_array();
+ json_object_object_add(json_nbr_sources, "ipv6",
+ json_v6adjs);
+ }
+ show_nbr_detail_adj_json(adj, json_v6adjs);
+ break;
+ default:
+ fatalx("show_nbr_detail_msg_json: unknown af");
+ }
+ break;
+ case IMSG_CTL_SHOW_NBR_END:
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+void
+show_nbr_capabilities(struct vty *vty, struct ctl_nbr *nbr)
+{
+ vty_out (vty, " Capabilities Sent:\n"
+ " - Dynamic Announcement (0x0506)\n"
+ " - Typed Wildcard (0x050B)\n"
+ " - Unrecognized Notification (0x0603)\n");
+ vty_out (vty, " Capabilities Received:\n");
+ if (CHECK_FLAG(nbr->flags, F_NBR_CAP_DYNAMIC))
+ vty_out (vty," - Dynamic Announcement (0x0506)\n");
+ if (CHECK_FLAG(nbr->flags, F_NBR_CAP_TWCARD))
+ vty_out (vty, " - Typed Wildcard (0x050B)\n");
+ if (CHECK_FLAG(nbr->flags, F_NBR_CAP_UNOTIF))
+ vty_out (vty," - Unrecognized Notification (0x0603)\n");
+}
+
+static int
+show_nbr_capabilities_msg(struct vty *vty, struct imsg *imsg, struct show_params *params)
+{
+ struct ctl_nbr *nbr;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_NBR:
+ nbr = imsg->data;
+
+ if (nbr->nbr_state != NBR_STA_OPER)
+ break;
+
+ vty_out (vty, "Peer LDP Identifier: %pI4:0\n",
+ &nbr->id);
+ show_nbr_capabilities(vty, nbr);
+ vty_out (vty, "\n");
+ break;
+ case IMSG_CTL_END:
+ vty_out (vty, "\n");
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static void
+show_nbr_capabilities_json(struct ctl_nbr *nbr, json_object *json_nbr)
+{
+ json_object *json_array;
+ json_object *json_cap;
+
+ /* sent capabilities */
+ json_array = json_object_new_array();
+ json_object_object_add(json_nbr, "sentCapabilities", json_array);
+
+ /* Dynamic Announcement (0x0506) */
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description", "Dynamic Announcement");
+ json_object_string_add(json_cap, "tlvType", "0x0506");
+ json_object_array_add(json_array, json_cap);
+
+ /* Typed Wildcard (0x050B) */
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description", "Typed Wildcard");
+ json_object_string_add(json_cap, "tlvType", "0x050B");
+ json_object_array_add(json_array, json_cap);
+
+ /* Unrecognized Notification (0x0603) */
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description",
+ "Unrecognized Notification");
+ json_object_string_add(json_cap, "tlvType", "0x0603");
+ json_object_array_add(json_array, json_cap);
+
+ /* received capabilities */
+ json_array = json_object_new_array();
+ json_object_object_add(json_nbr, "receivedCapabilities", json_array);
+
+ /* Dynamic Announcement (0x0506) */
+ if (CHECK_FLAG(nbr->flags, F_NBR_CAP_DYNAMIC)) {
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description",
+ "Dynamic Announcement");
+ json_object_string_add(json_cap, "tlvType", "0x0506");
+ json_object_array_add(json_array, json_cap);
+ }
+
+ /* Typed Wildcard (0x050B) */
+ if (CHECK_FLAG(nbr->flags, F_NBR_CAP_TWCARD)) {
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description",
+ "Typed Wildcard");
+ json_object_string_add(json_cap, "tlvType", "0x050B");
+ json_object_array_add(json_array, json_cap);
+ }
+
+ /* Unrecognized Notification (0x0603) */
+ if (CHECK_FLAG(nbr->flags, F_NBR_CAP_UNOTIF)) {
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description",
+ "Unrecognized Notification");
+ json_object_string_add(json_cap, "tlvType", "0x0603");
+ json_object_array_add(json_array, json_cap);
+ }
+}
+
+static int
+show_nbr_capabilities_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_nbr *nbr;
+ char buf[PREFIX_STRLEN];
+ json_object *json_nbr;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_NBR:
+ nbr = imsg->data;
+
+ if (nbr->nbr_state != NBR_STA_OPER)
+ break;
+
+ json_nbr = json_object_new_object();
+ json_object_object_add(json, inet_ntop(AF_INET, &nbr->id, buf,
+ sizeof(buf)), json_nbr);
+ show_nbr_capabilities_json(nbr, json_nbr);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_lib_msg(struct vty *vty, struct imsg *imsg, struct show_params *params)
+{
+ struct ctl_rt *rt;
+ char dstnet[BUFSIZ];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LIB_BEGIN:
+ rt = imsg->data;
+
+ if (params->lib.remote_label != NO_LABEL &&
+ params->lib.remote_label != rt->remote_label)
+ return (0);
+ /* FALLTHROUGH */
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ rt = imsg->data;
+
+ if (imsg->hdr.type == IMSG_CTL_SHOW_LIB_BEGIN &&
+ !rt->no_downstream)
+ break;
+
+ snprintf(dstnet, sizeof(dstnet), "%s/%d",
+ log_addr(rt->af, &rt->prefix), rt->prefixlen);
+
+ vty_out(vty, "%-4s %-20s", af_name(rt->af), dstnet);
+ if (strlen(dstnet) > 20)
+ vty_out(vty, "\n%25s", " ");
+ vty_out (vty, " %-15pI4 %-11s %-13s %6s\n",
+ &rt->nexthop, log_label(rt->local_label),
+ log_label(rt->remote_label),
+ rt->in_use ? "yes" : "no");
+ break;
+ case IMSG_CTL_END:
+ vty_out (vty, "\n");
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_lib_detail_msg(struct vty *vty, struct imsg *imsg, struct show_params *params)
+{
+ struct ctl_rt *rt = NULL;
+ static char dstnet[BUFSIZ];
+ static int upstream, downstream;
+ size_t buflen;
+ static char sent_buffer[LDPBUFSIZ];
+ static char rcvd_buffer[LDPBUFSIZ];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LIB_BEGIN:
+ rt = imsg->data;
+
+ upstream = 0;
+ downstream = 0;
+ sent_buffer[0] = '\0';
+ rcvd_buffer[0] = '\0';
+ snprintf(dstnet, sizeof(dstnet), "%s/%d",
+ log_addr(rt->af, &rt->prefix), rt->prefixlen);
+ break;
+ case IMSG_CTL_SHOW_LIB_SENT:
+ rt = imsg->data;
+
+ upstream = 1;
+ buflen = strlen(sent_buffer);
+ snprintfrr(sent_buffer + buflen, LDPBUFSIZ - buflen,
+ "%12s%pI4:0\n", "", &rt->nexthop);
+ break;
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ rt = imsg->data;
+ downstream = 1;
+ buflen = strlen(rcvd_buffer);
+ snprintfrr(rcvd_buffer + buflen, LDPBUFSIZ - buflen,
+ "%12s%pI4:0, label %s%s\n", "", &rt->nexthop,
+ log_label(rt->remote_label),
+ rt->in_use ? " (in use)" : "");
+ break;
+ case IMSG_CTL_SHOW_LIB_END:
+ rt = imsg->data;
+
+ if (params->lib.remote_label != NO_LABEL &&
+ !downstream)
+ break;
+ vty_out(vty, "%s\n", dstnet);
+ vty_out(vty, "%-8sLocal binding: label: %s\n", "",
+ log_label(rt->local_label));
+ if (upstream) {
+ vty_out (vty, "%-8sAdvertised to:\n", "");
+ vty_out(vty, "%s", sent_buffer);
+ }
+ if (downstream) {
+ vty_out (vty, "%-8sRemote bindings:\n", "");
+ vty_out(vty, "%s", rcvd_buffer);
+ } else
+ vty_out (vty, "%-8sNo remote bindings\n","");
+ break;
+ case IMSG_CTL_END:
+ vty_out (vty, "\n");
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_lib_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_rt *rt;
+ json_object *json_array;
+ json_object *json_lib_entry;
+ char dstnet[BUFSIZ];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LIB_BEGIN:
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ rt = imsg->data;
+
+ if (imsg->hdr.type == IMSG_CTL_SHOW_LIB_BEGIN &&
+ !rt->no_downstream)
+ break;
+
+ json_object_object_get_ex(json, "bindings", &json_array);
+ if (!json_array) {
+ json_array = json_object_new_array();
+ json_object_object_add(json, "bindings", json_array);
+ }
+
+ json_lib_entry = json_object_new_object();
+ json_object_string_add(json_lib_entry, "addressFamily",
+ af_name(rt->af));
+ snprintf(dstnet, sizeof(dstnet), "%s/%d",
+ log_addr(rt->af, &rt->prefix), rt->prefixlen);
+ json_object_string_add(json_lib_entry, "prefix", dstnet);
+ json_object_string_addf(json_lib_entry, "neighborId", "%pI4",
+ &rt->nexthop);
+ json_object_string_add(json_lib_entry, "localLabel",
+ log_label(rt->local_label));
+ json_object_string_add(json_lib_entry, "remoteLabel",
+ log_label(rt->remote_label));
+ json_object_int_add(json_lib_entry, "inUse", rt->in_use);
+
+ json_object_array_add(json_array, json_lib_entry);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_lib_detail_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_rt *rt = NULL;
+ char dstnet[BUFSIZ];
+ static json_object *json_lib_entry;
+ static json_object *json_adv_labels;
+ json_object *json_adv_label;
+ static json_object *json_remote_labels;
+ json_object *json_remote_label;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LIB_BEGIN:
+ rt = imsg->data;
+
+ snprintf(dstnet, sizeof(dstnet), "%s/%d",
+ log_addr(rt->af, &rt->prefix), rt->prefixlen);
+
+ json_lib_entry = json_object_new_object();
+ json_object_string_add(json_lib_entry, "localLabel",
+ log_label(rt->local_label));
+
+ json_adv_labels = json_object_new_array();
+ json_object_object_add(json_lib_entry, "advertisedTo",
+ json_adv_labels);
+
+ json_remote_labels = json_object_new_array();
+ json_object_object_add(json_lib_entry, "remoteLabels",
+ json_remote_labels);
+
+ json_object_object_add(json, dstnet, json_lib_entry);
+ break;
+ case IMSG_CTL_SHOW_LIB_SENT:
+ rt = imsg->data;
+
+ json_adv_label = json_object_new_object();
+ json_object_string_addf(json_adv_label, "neighborId", "%pI4",
+ &rt->nexthop);
+ json_object_array_add(json_adv_labels, json_adv_label);
+ break;
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ rt = imsg->data;
+
+ json_remote_label = json_object_new_object();
+ json_object_string_addf(json_remote_label, "neighborId", "%pI4",
+ &rt->nexthop);
+ json_object_string_add(json_remote_label, "label",
+ log_label(rt->remote_label));
+ json_object_int_add(json_remote_label, "inUse", rt->in_use);
+ json_object_array_add(json_remote_labels, json_remote_label);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_l2vpn_binding_msg(struct vty *vty, struct imsg *imsg,
+ struct show_params *params)
+{
+ struct ctl_pw *pw;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ pw = imsg->data;
+
+ vty_out (vty, " Destination Address: %pI4, VC ID: %u\n",
+ &pw->lsr_id, pw->pwid);
+
+ /* local binding */
+ if (pw->local_label != NO_LABEL) {
+ vty_out (vty, " Local Label: %u\n",
+ pw->local_label);
+ vty_out (vty, "%-8sCbit: %u, VC Type: %s, GroupID: %u\n", "", pw->local_cword,
+ pw_type_name(pw->type),pw->local_gid);
+ vty_out (vty, "%-8sMTU: %u\n", "",pw->local_ifmtu);
+ vty_out (vty, "%-8sLast failure: %s\n", "",
+ pw_error_code(pw->reason));
+ } else
+ vty_out (vty," Local Label: unassigned\n");
+
+ /* remote binding */
+ if (pw->remote_label != NO_LABEL) {
+ vty_out (vty, " Remote Label: %u\n",
+ pw->remote_label);
+ vty_out (vty, "%-8sCbit: %u, VC Type: %s, GroupID: %u\n", "", pw->remote_cword,
+ pw_type_name(pw->type),pw->remote_gid);
+ vty_out (vty, "%-8sMTU: %u\n", "",pw->remote_ifmtu);
+ } else
+ vty_out (vty," Remote Label: unassigned\n");
+ break;
+ case IMSG_CTL_END:
+ vty_out (vty, "\n");
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_l2vpn_binding_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_pw *pw;
+ json_object *json_pw;
+ char key_name[64];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ pw = imsg->data;
+
+ json_pw = json_object_new_object();
+ json_object_string_addf(json_pw, "destination", "%pI4",
+ &pw->lsr_id);
+ json_object_int_add(json_pw, "vcId", pw->pwid);
+
+ /* local binding */
+ if (pw->local_label != NO_LABEL) {
+ json_object_int_add(json_pw, "localLabel",
+ pw->local_label);
+ json_object_int_add(json_pw, "localControlWord",
+ pw->local_cword);
+ json_object_string_add(json_pw, "localVcType",
+ pw_type_name(pw->type));
+ json_object_int_add(json_pw, "localGroupID",
+ pw->local_gid);
+ json_object_int_add(json_pw, "localIfMtu",
+ pw->local_ifmtu);
+ json_object_string_add(json_pw, "lastFailureReason",
+ pw_error_code(pw->reason));
+ } else
+ json_object_string_add(json_pw, "localLabel",
+ "unassigned");
+
+ /* remote binding */
+ if (pw->remote_label != NO_LABEL) {
+ json_object_int_add(json_pw, "remoteLabel",
+ pw->remote_label);
+ json_object_int_add(json_pw, "remoteControlWord",
+ pw->remote_cword);
+ json_object_string_add(json_pw, "remoteVcType",
+ pw_type_name(pw->type));
+ json_object_int_add(json_pw, "remoteGroupID",
+ pw->remote_gid);
+ json_object_int_add(json_pw, "remoteIfMtu",
+ pw->remote_ifmtu);
+ } else
+ json_object_string_add(json_pw, "remoteLabel",
+ "unassigned");
+
+ snprintfrr(key_name, sizeof(key_name), "%pI4: %u",
+ &pw->lsr_id, pw->pwid);
+ json_object_object_add(json, key_name, json_pw);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_l2vpn_pw_msg(struct vty *vty, struct imsg *imsg, struct show_params *params)
+{
+ struct ctl_pw *pw;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ pw = imsg->data;
+
+ vty_out (vty, "%-9s %-15pI4 %-10u %-16s %-10s\n", pw->ifname,
+ &pw->lsr_id, pw->pwid, pw->l2vpn_name,
+ (pw->status == PW_FORWARDING ? "UP" : "DOWN"));
+ break;
+ case IMSG_CTL_END:
+ vty_out (vty, "\n");
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+show_l2vpn_pw_msg_json(struct imsg *imsg, struct show_params *params,
+ json_object *json)
+{
+ struct ctl_pw *pw;
+ json_object *json_pw;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ pw = imsg->data;
+
+ json_pw = json_object_new_object();
+ json_object_string_addf(json_pw, "peerId", "%pI4", &pw->lsr_id);
+ json_object_int_add(json_pw, "vcId", pw->pwid);
+ json_object_string_add(json_pw, "vpnName", pw->l2vpn_name);
+ if (pw->status == PW_FORWARDING)
+ json_object_string_add(json_pw, "status", "up");
+ else
+ json_object_string_add(json_pw, "status", "down");
+ json_object_object_add(json, pw->ifname, json_pw);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+ldp_vty_connect(struct imsgbuf *ibuf)
+{
+ struct sockaddr_un s_un;
+ int ctl_sock;
+
+ /* connect to ldpd control socket */
+ if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("%s: socket", __func__);
+ return (-1);
+ }
+
+ memset(&s_un, 0, sizeof(s_un));
+ s_un.sun_family = AF_UNIX;
+ strlcpy(s_un.sun_path, ctl_sock_path, sizeof(s_un.sun_path));
+ if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
+ log_warn("%s: connect: %s", __func__, ctl_sock_path);
+ close(ctl_sock);
+ return (-1);
+ }
+
+ imsg_init(ibuf, ctl_sock);
+
+ return (0);
+}
+
+static int
+ldp_vty_dispatch_iface(struct vty *vty, struct imsg *imsg,
+ struct show_params *params, json_object *json)
+{
+ int ret;
+
+ if (params->json)
+ ret = show_interface_msg_json(imsg, params, json);
+ else
+ ret = show_interface_msg(vty, imsg, params);
+
+ return (ret);
+}
+
+static int
+ldp_vty_dispatch_ldp_sync(struct vty *vty, struct imsg *imsg,
+ struct show_params *params, json_object *json)
+{
+ int ret;
+
+ if (params->json)
+ ret = show_ldp_sync_msg_json(imsg, params, json);
+ else
+ ret = show_ldp_sync_msg(vty, imsg, params);
+
+ return (ret);
+}
+
+static int
+ldp_vty_dispatch_disc(struct vty *vty, struct imsg *imsg,
+ struct show_params *params, json_object *json)
+{
+ int ret;
+
+ if (params->detail) {
+ if (params->json)
+ ret = show_discovery_detail_msg_json(imsg, params,
+ json);
+ else
+ ret = show_discovery_detail_msg(vty, imsg, params);
+ } else {
+ if (params->json)
+ ret = show_discovery_msg_json(imsg, params, json);
+ else
+ ret = show_discovery_msg(vty, imsg, params);
+ }
+
+ return (ret);
+}
+
+static int
+ldp_vty_dispatch_nbr(struct vty *vty, struct imsg *imsg,
+ struct show_params *params, json_object *json)
+{
+ static bool filtered = false;
+ struct ctl_nbr *nbr;
+ int ret;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_NBR:
+ filtered = false;
+ nbr = imsg->data;
+
+ if (params->neighbor.lsr_id.s_addr != INADDR_ANY &&
+ params->neighbor.lsr_id.s_addr != nbr->id.s_addr) {
+ filtered = true;
+ return (0);
+ }
+ break;
+ case IMSG_CTL_SHOW_NBR_DISC:
+ case IMSG_CTL_SHOW_NBR_END:
+ if (filtered)
+ return (0);
+ break;
+ default:
+ break;
+ }
+
+ if (params->neighbor.capabilities) {
+ if (params->json)
+ ret = show_nbr_capabilities_msg_json(imsg, params,
+ json);
+ else
+ ret = show_nbr_capabilities_msg(vty, imsg, params);
+ } else if (params->detail) {
+ if (params->json)
+ ret = show_nbr_detail_msg_json(imsg, params, json);
+ else
+ ret = show_nbr_detail_msg(vty, imsg, params);
+ } else {
+ if (params->json)
+ ret = show_nbr_msg_json(imsg, params, json);
+ else
+ ret = show_nbr_msg(vty, imsg, params);
+ }
+
+ return (ret);
+}
+
+static int
+ldp_vty_dispatch_lib(struct vty *vty, struct imsg *imsg,
+ struct show_params *params, json_object *json)
+{
+ static bool filtered = false;
+ struct ctl_rt *rt = NULL;
+ struct prefix prefix;
+ int ret;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LIB_BEGIN:
+ filtered = false;
+ break;
+ case IMSG_CTL_SHOW_LIB_SENT:
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ case IMSG_CTL_SHOW_LIB_END:
+ if (filtered)
+ return (0);
+ break;
+ default:
+ break;
+ }
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LIB_BEGIN:
+ case IMSG_CTL_SHOW_LIB_SENT:
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ case IMSG_CTL_SHOW_LIB_END:
+ rt = imsg->data;
+
+ if (params->family != AF_UNSPEC && params->family != rt->af) {
+ filtered = true;
+ return (0);
+ }
+
+ prefix.family = rt->af;
+ prefix.prefixlen = rt->prefixlen;
+ memcpy(&prefix.u.val, &rt->prefix, sizeof(prefix.u.val));
+ if (params->lib.prefix.family != AF_UNSPEC) {
+ if (!params->lib.longer_prefixes &&
+ !prefix_same(&params->lib.prefix, &prefix)) {
+ filtered = true;
+ return (0);
+ } else if (params->lib.longer_prefixes &&
+ !prefix_match(&params->lib.prefix, &prefix)) {
+ filtered = true;
+ return (0);
+ }
+ }
+
+ if (params->lib.local_label != NO_LABEL &&
+ params->lib.local_label != rt->local_label) {
+ filtered = true;
+ return (0);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LIB_SENT:
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ if (params->lib.neighbor.s_addr != INADDR_ANY &&
+ params->lib.neighbor.s_addr != rt->nexthop.s_addr)
+ return (0);
+ break;
+ default:
+ break;
+ }
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ if (params->lib.remote_label != NO_LABEL &&
+ params->lib.remote_label != rt->remote_label)
+ return (0);
+ break;
+ default:
+ break;
+ }
+
+ if (params->detail) {
+ if (params->json)
+ ret = show_lib_detail_msg_json(imsg, params, json);
+ else
+ ret = show_lib_detail_msg(vty, imsg, params);
+ } else {
+ if (params->json)
+ ret = show_lib_msg_json(imsg, params, json);
+ else
+ ret = show_lib_msg(vty, imsg, params);
+ }
+
+ return (ret);
+}
+
+static int
+ldp_vty_dispatch_l2vpn_pw(struct vty *vty, struct imsg *imsg,
+ struct show_params *params, json_object *json)
+{
+ struct ctl_pw *pw;
+ int ret;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ pw = imsg->data;
+ if (params->l2vpn.peer.s_addr != INADDR_ANY &&
+ params->l2vpn.peer.s_addr != pw->lsr_id.s_addr)
+ return (0);
+ if (params->l2vpn.ifname[0] != '\0' &&
+ strcmp(params->l2vpn.ifname, pw->ifname))
+ return (0);
+ if (params->l2vpn.vcid && params->l2vpn.vcid != pw->pwid)
+ return (0);
+ break;
+ default:
+ break;
+ }
+
+ if (params->json)
+ ret = show_l2vpn_pw_msg_json(imsg, params, json);
+ else
+ ret = show_l2vpn_pw_msg(vty, imsg, params);
+
+ return (ret);
+}
+
+static int
+ldp_vty_dispatch_l2vpn_binding(struct vty *vty, struct imsg *imsg,
+ struct show_params *params, json_object *json)
+{
+ struct ctl_pw *pw;
+ int ret;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ pw = imsg->data;
+ if (params->l2vpn.peer.s_addr != INADDR_ANY &&
+ params->l2vpn.peer.s_addr != pw->lsr_id.s_addr)
+ return (0);
+ if (params->l2vpn.local_label != NO_LABEL &&
+ params->l2vpn.local_label != pw->local_label)
+ return (0);
+ if (params->l2vpn.remote_label != NO_LABEL &&
+ params->l2vpn.remote_label != pw->remote_label)
+ return (0);
+ break;
+ default:
+ break;
+ }
+
+ if (params->json)
+ ret = show_l2vpn_binding_msg_json(imsg, params, json);
+ else
+ ret = show_l2vpn_binding_msg(vty, imsg, params);
+
+ return (ret);
+}
+
+static int
+ldp_vty_dispatch_msg(struct vty *vty, struct imsg *imsg, enum show_command cmd,
+ struct show_params *params, json_object *json)
+{
+ switch (cmd) {
+ case SHOW_IFACE:
+ return (ldp_vty_dispatch_iface(vty, imsg, params, json));
+ case SHOW_DISC:
+ return (ldp_vty_dispatch_disc(vty, imsg, params, json));
+ case SHOW_NBR:
+ return (ldp_vty_dispatch_nbr(vty, imsg, params, json));
+ case SHOW_LIB:
+ return (ldp_vty_dispatch_lib(vty, imsg, params, json));
+ case SHOW_L2VPN_PW:
+ return (ldp_vty_dispatch_l2vpn_pw(vty, imsg, params, json));
+ case SHOW_L2VPN_BINDING:
+ return (ldp_vty_dispatch_l2vpn_binding(vty, imsg, params,
+ json));
+ case SHOW_LDP_SYNC:
+ return (ldp_vty_dispatch_ldp_sync(vty, imsg, params, json));
+ default:
+ return (0);
+ }
+}
+
+static int
+ldp_vty_dispatch(struct vty *vty, struct imsgbuf *ibuf, enum show_command cmd,
+ struct show_params *params)
+{
+ struct imsg imsg;
+ int n, done = 0, ret = CMD_SUCCESS;
+ json_object *json = NULL;
+
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) {
+ log_warn("write error");
+ close(ibuf->fd);
+ return (CMD_WARNING);
+ }
+
+ if (params->json)
+ json = json_object_new_object();
+
+ while (!done) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) {
+ log_warnx("imsg_read error");
+ ret = CMD_WARNING;
+ goto done;
+ }
+ if (n == 0) {
+ log_warnx("pipe closed");
+ ret = CMD_WARNING;
+ goto done;
+ }
+
+ while (!done) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1) {
+ log_warnx("imsg_get error");
+ ret = CMD_WARNING;
+ goto done;
+ }
+ if (n == 0)
+ break;
+ done = ldp_vty_dispatch_msg(vty, &imsg, cmd, params,
+ json);
+ imsg_free(&imsg);
+ }
+ }
+
+ done:
+ close(ibuf->fd);
+ if (json) {
+ vty_json(vty, json);
+ }
+
+ return (ret);
+}
+
+static int
+ldp_vty_get_af(const char *str, int *af)
+{
+ if (str == NULL) {
+ *af = AF_UNSPEC;
+ return (0);
+ } else if (strcmp(str, "ipv4") == 0) {
+ *af = AF_INET;
+ return (0);
+ } else if (strcmp(str, "ipv6") == 0) {
+ *af = AF_INET6;
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+ldp_vty_show_binding(struct vty *vty, const char *af_str, const char *prefix,
+ int longer_prefixes, const char *neighbor, unsigned long local_label,
+ unsigned long remote_label, const char *detail, const char *json)
+{
+ struct imsgbuf ibuf;
+ struct show_params params;
+ int af;
+
+ if (ldp_vty_connect(&ibuf) < 0)
+ return (CMD_WARNING);
+
+ if (ldp_vty_get_af(af_str, &af) < 0)
+ return (CMD_ERR_NO_MATCH);
+
+ memset(&params, 0, sizeof(params));
+ params.family = af;
+ params.detail = (detail) ? 1 : 0;
+ params.json = (json) ? 1 : 0;
+ if (prefix) {
+ (void)str2prefix(prefix, &params.lib.prefix);
+ params.lib.longer_prefixes = longer_prefixes;
+ }
+ if (neighbor &&
+ (inet_pton(AF_INET, neighbor, &params.lib.neighbor) != 1 ||
+ bad_addr_v4(params.lib.neighbor))) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_SUCCESS);
+ }
+ params.lib.local_label = local_label;
+ params.lib.remote_label = remote_label;
+
+ if (!params.detail && !params.json)
+ vty_out (vty, "%-4s %-20s %-15s %-11s %-13s %6s\n", "AF",
+ "Destination", "Nexthop", "Local Label", "Remote Label",
+ "In Use");
+
+ imsg_compose(&ibuf, IMSG_CTL_SHOW_LIB, 0, 0, -1, NULL, 0);
+ return (ldp_vty_dispatch(vty, &ibuf, SHOW_LIB, &params));
+}
+
+int
+ldp_vty_show_discovery(struct vty *vty, const char *af_str, const char *detail,
+ const char *json)
+{
+ struct imsgbuf ibuf;
+ struct show_params params;
+ int af;
+
+ if (ldp_vty_connect(&ibuf) < 0)
+ return (CMD_WARNING);
+
+ if (ldp_vty_get_af(af_str, &af) < 0)
+ return (CMD_ERR_NO_MATCH);
+
+ memset(&params, 0, sizeof(params));
+ params.family = af;
+ params.detail = (detail) ? 1 : 0;
+ params.json = (json) ? 1 : 0;
+
+ if (!params.detail && !params.json)
+ vty_out (vty, "%-4s %-15s %-8s %-15s %9s\n",
+ "AF", "ID", "Type", "Source", "Holdtime");
+
+ if (params.detail)
+ imsg_compose(&ibuf, IMSG_CTL_SHOW_DISCOVERY_DTL, 0, 0, -1,
+ NULL, 0);
+ else
+ imsg_compose(&ibuf, IMSG_CTL_SHOW_DISCOVERY, 0, 0, -1, NULL, 0);
+ return (ldp_vty_dispatch(vty, &ibuf, SHOW_DISC, &params));
+}
+
+int
+ldp_vty_show_interface(struct vty *vty, const char *af_str, const char *json)
+{
+ struct imsgbuf ibuf;
+ struct show_params params;
+ unsigned int ifidx = 0;
+ int af;
+
+ if (ldp_vty_connect(&ibuf) < 0)
+ return (CMD_WARNING);
+
+ if (ldp_vty_get_af(af_str, &af) < 0)
+ return (CMD_ERR_NO_MATCH);
+
+ memset(&params, 0, sizeof(params));
+ params.family = af;
+ params.json = (json) ? 1 : 0;
+
+ /* header */
+ if (!params.json) {
+ vty_out (vty, "%-4s %-11s %-6s %-8s %-12s %3s\n", "AF",
+ "Interface", "State", "Uptime", "Hello Timers","ac");
+ }
+
+ imsg_compose(&ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, &ifidx,
+ sizeof(ifidx));
+ return (ldp_vty_dispatch(vty, &ibuf, SHOW_IFACE, &params));
+}
+
+int
+ldp_vty_show_capabilities(struct vty *vty, const char *json)
+{
+ if (json) {
+ json_object *json;
+ json_object *json_array;
+ json_object *json_cap;
+
+ json = json_object_new_object();
+ json_array = json_object_new_array();
+ json_object_object_add(json, "capabilities", json_array);
+
+ /* Dynamic Announcement (0x0506) */
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description",
+ "Dynamic Announcement");
+ json_object_string_add(json_cap, "tlvType",
+ "0x0506");
+ json_object_array_add(json_array, json_cap);
+
+ /* Typed Wildcard (0x050B) */
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description",
+ "Typed Wildcard");
+ json_object_string_add(json_cap, "tlvType",
+ "0x050B");
+ json_object_array_add(json_array, json_cap);
+
+ /* Unrecognized Notification (0x0603) */
+ json_cap = json_object_new_object();
+ json_object_string_add(json_cap, "description",
+ "Unrecognized Notification");
+ json_object_string_add(json_cap, "tlvType",
+ "0x0603");
+ json_object_array_add(json_array, json_cap);
+
+ vty_json(vty, json);
+ return (0);
+ }
+
+ vty_out (vty,
+ "Supported LDP Capabilities\n"
+ " * Dynamic Announcement (0x0506)\n"
+ " * Typed Wildcard (0x050B)\n"
+ " * Unrecognized Notification (0x0603)\n\n");
+
+ return (0);
+}
+
+int
+ldp_vty_show_neighbor(struct vty *vty, const char *lsr_id, int capabilities,
+ const char *detail, const char *json)
+{
+ struct imsgbuf ibuf;
+ struct show_params params;
+
+ if (ldp_vty_connect(&ibuf) < 0)
+ return (CMD_WARNING);
+
+ memset(&params, 0, sizeof(params));
+ params.detail = (detail) ? 1 : 0;
+ params.json = (json) ? 1 : 0;
+ params.neighbor.capabilities = capabilities;
+ if (lsr_id &&
+ (inet_pton(AF_INET, lsr_id, &params.neighbor.lsr_id) != 1 ||
+ bad_addr_v4(params.neighbor.lsr_id))) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_SUCCESS);
+ }
+
+ if (params.neighbor.capabilities)
+ params.detail = 1;
+
+ if (!params.detail && !params.json)
+ vty_out (vty, "%-4s %-15s %-11s %-15s %8s\n",
+ "AF", "ID", "State", "Remote Address","Uptime");
+
+ imsg_compose(&ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0);
+ return (ldp_vty_dispatch(vty, &ibuf, SHOW_NBR, &params));
+}
+
+int
+ldp_vty_show_ldp_sync(struct vty *vty, const char *json)
+{
+ struct imsgbuf ibuf;
+ struct show_params params;
+
+ if (ldp_vty_connect(&ibuf) < 0)
+ return (CMD_WARNING);
+
+ memset(&params, 0, sizeof(params));
+ params.json = (json) ? 1 : 0;
+
+ imsg_compose(&ibuf, IMSG_CTL_SHOW_LDP_SYNC, 0, 0, -1, NULL, 0);
+ return (ldp_vty_dispatch(vty, &ibuf, SHOW_LDP_SYNC, &params));
+}
+
+int
+ldp_vty_show_atom_binding(struct vty *vty, const char *peer,
+ unsigned long local_label, unsigned long remote_label, const char *json)
+{
+ struct imsgbuf ibuf;
+ struct show_params params;
+
+ if (ldp_vty_connect(&ibuf) < 0)
+ return (CMD_WARNING);
+
+ memset(&params, 0, sizeof(params));
+ params.json = (json) ? 1 : 0;
+ if (peer &&
+ (inet_pton(AF_INET, peer, &params.l2vpn.peer) != 1 ||
+ bad_addr_v4(params.l2vpn.peer))) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_SUCCESS);
+ }
+ params.l2vpn.local_label = local_label;
+ params.l2vpn.remote_label = remote_label;
+
+ imsg_compose(&ibuf, IMSG_CTL_SHOW_L2VPN_BINDING, 0, 0, -1, NULL, 0);
+ return (ldp_vty_dispatch(vty, &ibuf, SHOW_L2VPN_BINDING, &params));
+}
+
+int
+ldp_vty_show_atom_vc(struct vty *vty, const char *peer, const char *ifname,
+ const char *vcid, const char *json)
+{
+ struct imsgbuf ibuf;
+ struct show_params params;
+
+ if (ldp_vty_connect(&ibuf) < 0)
+ return (CMD_WARNING);
+
+ memset(&params, 0, sizeof(params));
+ params.json = (json) ? 1 : 0;
+ if (peer &&
+ (inet_pton(AF_INET, peer, &params.l2vpn.peer) != 1 ||
+ bad_addr_v4(params.l2vpn.peer))) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_SUCCESS);
+ }
+ if (ifname)
+ strlcpy(params.l2vpn.ifname, ifname,
+ sizeof(params.l2vpn.ifname));
+ if (vcid)
+ params.l2vpn.vcid = atoi(vcid);
+
+ if (!params.json) {
+ /* header */
+ vty_out (vty, "%-9s %-15s %-10s %-16s %-10s\n",
+ "Interface", "Peer ID", "VC ID", "Name","Status");
+ vty_out (vty, "%-9s %-15s %-10s %-16s %-10s\n",
+ "---------", "---------------", "----------",
+ "----------------", "----------");
+ }
+
+ imsg_compose(&ibuf, IMSG_CTL_SHOW_L2VPN_PW, 0, 0, -1, NULL, 0);
+ return (ldp_vty_dispatch(vty, &ibuf, SHOW_L2VPN_PW, &params));
+}
+
+int
+ldp_vty_clear_nbr(struct vty *vty, const char *addr_str)
+{
+ struct imsgbuf ibuf;
+ struct ctl_nbr nbr;
+
+ memset(&nbr, 0, sizeof(nbr));
+ if (addr_str &&
+ (ldp_get_address(addr_str, &nbr.af, &nbr.raddr) == -1 ||
+ bad_addr(nbr.af, &nbr.raddr))) {
+ vty_out (vty, "%% Malformed address\n");
+ return (CMD_WARNING);
+ }
+
+ if (ldp_vty_connect(&ibuf) < 0)
+ return (CMD_WARNING);
+
+ imsg_compose(&ibuf, IMSG_CTL_CLEAR_NBR, 0, 0, -1, &nbr, sizeof(nbr));
+
+ while (ibuf.w.queued)
+ if (msgbuf_write(&ibuf.w) <= 0 && errno != EAGAIN) {
+ log_warn("write error");
+ close(ibuf.fd);
+ return (CMD_WARNING);
+ }
+
+ close(ibuf.fd);
+
+ return (CMD_SUCCESS);
+}
diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c
new file mode 100644
index 0000000..0fd5d46
--- /dev/null
+++ b/ldpd/ldp_zebra.c
@@ -0,0 +1,713 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 by Open Source Routing.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "stream.h"
+#include "memory.h"
+#include "zclient.h"
+#include "command.h"
+#include "network.h"
+#include "linklist.h"
+#include "mpls.h"
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "ldp_sync.h"
+#include "log.h"
+#include "ldp_debug.h"
+
+static void ifp2kif(struct interface *, struct kif *);
+static void ifc2kaddr(struct interface *, struct connected *, struct kaddr *);
+static int ldp_zebra_send_mpls_labels(int, struct kroute *);
+static int ldp_router_id_update(ZAPI_CALLBACK_ARGS);
+static int ldp_interface_address_add(ZAPI_CALLBACK_ARGS);
+static int ldp_interface_address_delete(ZAPI_CALLBACK_ARGS);
+static int ldp_zebra_read_route(ZAPI_CALLBACK_ARGS);
+static int ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS);
+static void ldp_zebra_connected(struct zclient *);
+static void ldp_zebra_filter_update(struct access_list *access);
+
+static void ldp_zebra_opaque_register(void);
+static void ldp_zebra_opaque_unregister(void);
+static int ldp_sync_zebra_send_announce(void);
+static int ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS);
+static void ldp_sync_zebra_init(void);
+
+static struct zclient *zclient;
+extern struct zclient *zclient_sync;
+static bool zebra_registered = false;
+
+static void
+ifp2kif(struct interface *ifp, struct kif *kif)
+{
+ memset(kif, 0, sizeof(*kif));
+ strlcpy(kif->ifname, ifp->name, sizeof(kif->ifname));
+ kif->ifindex = ifp->ifindex;
+ kif->operative = if_is_operative(ifp);
+ if (ifp->ll_type == ZEBRA_LLT_ETHER)
+ memcpy(kif->mac, ifp->hw_addr, ETH_ALEN);
+}
+
+static void
+ifc2kaddr(struct interface *ifp, struct connected *ifc, struct kaddr *ka)
+{
+ memset(ka, 0, sizeof(*ka));
+ strlcpy(ka->ifname, ifp->name, sizeof(ka->ifname));
+ ka->ifindex = ifp->ifindex;
+ ka->af = ifc->address->family;
+ ka->prefixlen = ifc->address->prefixlen;
+
+ switch (ka->af) {
+ case AF_INET:
+ ka->addr.v4 = ifc->address->u.prefix4;
+ if (ifc->destination)
+ ka->dstbrd.v4 = ifc->destination->u.prefix4;
+ break;
+ case AF_INET6:
+ ka->addr.v6 = ifc->address->u.prefix6;
+ if (ifc->destination)
+ ka->dstbrd.v6 = ifc->destination->u.prefix6;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw)
+{
+ memset(zpw, 0, sizeof(*zpw));
+ strlcpy(zpw->ifname, pw->ifname, sizeof(zpw->ifname));
+ zpw->ifindex = pw->ifindex;
+ zpw->type = pw->l2vpn->pw_type;
+ zpw->af = pw->af;
+ zpw->nexthop.ipv6 = pw->addr.v6;
+ zpw->local_label = NO_LABEL;
+ zpw->remote_label = NO_LABEL;
+ if (CHECK_FLAG(pw->flags, F_PW_CWORD))
+ zpw->flags = F_PSEUDOWIRE_CWORD;
+ zpw->data.ldp.lsr_id = pw->lsr_id;
+ zpw->data.ldp.pwid = pw->pwid;
+ strlcpy(zpw->data.ldp.vpn_name, pw->l2vpn->name,
+ sizeof(zpw->data.ldp.vpn_name));
+}
+
+static void
+ldp_zebra_opaque_register(void)
+{
+ zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
+ zclient_register_opaque(zclient, LDP_RLFA_REGISTER);
+ zclient_register_opaque(zclient, LDP_RLFA_UNREGISTER_ALL);
+}
+
+static void
+ldp_zebra_opaque_unregister(void)
+{
+ zclient_unregister_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
+ zclient_unregister_opaque(zclient, LDP_RLFA_REGISTER);
+ zclient_unregister_opaque(zclient, LDP_RLFA_UNREGISTER_ALL);
+}
+
+int
+ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *state)
+{
+ if (zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE,
+ (const uint8_t *)state, sizeof(*state))
+ == ZCLIENT_SEND_FAILURE)
+ return -1;
+ else
+ return 0;
+}
+
+static int
+ldp_sync_zebra_send_announce(void)
+{
+ struct ldp_igp_sync_announce announce;
+ announce.proto = ZEBRA_ROUTE_LDP;
+
+ if (zclient_send_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE,
+ (const uint8_t *)&announce, sizeof(announce))
+ == ZCLIENT_SEND_FAILURE)
+ return -1;
+ else
+ return 0;
+}
+
+int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *rlfa_labels)
+{
+ int ret;
+
+ ret = zclient_send_opaque(zclient, LDP_RLFA_LABELS,
+ (const uint8_t *)rlfa_labels,
+ sizeof(*rlfa_labels));
+ if (ret == ZCLIENT_SEND_FAILURE) {
+ log_warn("failed to send RLFA labels to IGP");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s;
+ struct zapi_opaque_msg info;
+ struct ldp_igp_sync_if_state_req state_req;
+ struct zapi_rlfa_igp igp;
+ struct zapi_rlfa_request rlfa;
+
+ s = zclient->ibuf;
+
+ if(zclient_opaque_decode(s, &info) != 0)
+ return -1;
+
+ switch (info.type) {
+ case LDP_IGP_SYNC_IF_STATE_REQUEST:
+ STREAM_GET(&state_req, s, sizeof(state_req));
+ main_imsg_compose_ldpe(IMSG_LDP_SYNC_IF_STATE_REQUEST, 0, &state_req,
+ sizeof(state_req));
+ break;
+ case LDP_RLFA_REGISTER:
+ STREAM_GET(&rlfa, s, sizeof(rlfa));
+ main_imsg_compose_both(IMSG_RLFA_REG, &rlfa, sizeof(rlfa));
+ break;
+ case LDP_RLFA_UNREGISTER_ALL:
+ STREAM_GET(&igp, s, sizeof(igp));
+ main_imsg_compose_both(IMSG_RLFA_UNREG_ALL, &igp, sizeof(igp));
+ break;
+ default:
+ break;
+ }
+
+stream_failure:
+ return 0;
+}
+
+static void
+ldp_sync_zebra_init(void)
+{
+ ldp_sync_zebra_send_announce();
+}
+
+static int
+ldp_zebra_send_mpls_labels(int cmd, struct kroute *kr)
+{
+ struct zapi_labels zl = {};
+ struct zapi_nexthop *znh;
+
+ if (kr->local_label < MPLS_LABEL_RESERVED_MAX)
+ return (0);
+
+ debug_zebra_out("prefix %s/%u nexthop %s ifindex %u labels %s/%s (%s)",
+ log_addr(kr->af, &kr->prefix), kr->prefixlen,
+ log_addr(kr->af, &kr->nexthop), kr->ifindex,
+ log_label(kr->local_label), log_label(kr->remote_label),
+ (cmd == ZEBRA_MPLS_LABELS_ADD) ? "add" : "delete");
+
+ zl.type = ZEBRA_LSP_LDP;
+ zl.local_label = kr->local_label;
+
+ /* Set prefix. */
+ if (kr->remote_label != NO_LABEL) {
+ SET_FLAG(zl.message, ZAPI_LABELS_FTN);
+ zl.route.prefix.family = kr->af;
+ switch (kr->af) {
+ case AF_INET:
+ zl.route.prefix.u.prefix4 = kr->prefix.v4;
+ break;
+ case AF_INET6:
+ zl.route.prefix.u.prefix6 = kr->prefix.v6;
+ break;
+ default:
+ fatalx("ldp_zebra_send_mpls_labels: unknown af");
+ }
+ zl.route.prefix.prefixlen = kr->prefixlen;
+ zl.route.type = kr->route_type;
+ zl.route.instance = kr->route_instance;
+ }
+
+ /* If allow-broken-lsps is enabled then if an lsp is received with
+ * no remote label, instruct the forwarding plane to pop the top-level
+ * label and forward packets normally. This is a best-effort attempt
+ * to deliver labeled IP packets to their final destination (instead of
+ * dropping them).
+ */
+ if (kr->remote_label == NO_LABEL
+ && !CHECK_FLAG(ldpd_conf->flags, F_LDPD_ALLOW_BROKEN_LSP)
+ && cmd == ZEBRA_MPLS_LABELS_ADD)
+ return 0;
+
+ if (kr->remote_label == NO_LABEL)
+ kr->remote_label = MPLS_LABEL_IMPLICIT_NULL;
+
+ /* Set nexthop. */
+ zl.nexthop_num = 1;
+ znh = &zl.nexthops[0];
+ switch (kr->af) {
+ case AF_INET:
+ znh->gate.ipv4 = kr->nexthop.v4;
+ if (kr->ifindex)
+ znh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ else
+ znh->type = NEXTHOP_TYPE_IPV4;
+ break;
+ case AF_INET6:
+ znh->gate.ipv6 = kr->nexthop.v6;
+ if (kr->ifindex)
+ znh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ else
+ znh->type = NEXTHOP_TYPE_IPV6;
+ break;
+ default:
+ break;
+ }
+ znh->ifindex = kr->ifindex;
+ znh->label_num = 1;
+ znh->labels[0] = kr->remote_label;
+
+ if (zebra_send_mpls_labels(zclient, cmd, &zl) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+int
+kr_change(struct kroute *kr)
+{
+ return (ldp_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, kr));
+}
+
+int
+kr_delete(struct kroute *kr)
+{
+ return (ldp_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, kr));
+}
+
+int
+kmpw_add(struct zapi_pw *zpw)
+{
+ debug_zebra_out("pseudowire %s nexthop %s (add)",
+ zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop));
+
+ return zebra_send_pw(zclient, ZEBRA_PW_ADD, zpw) == ZCLIENT_SEND_FAILURE;
+}
+
+int
+kmpw_del(struct zapi_pw *zpw)
+{
+ debug_zebra_out("pseudowire %s nexthop %s (del)",
+ zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop));
+
+ return zebra_send_pw(zclient, ZEBRA_PW_DELETE, zpw) == ZCLIENT_SEND_FAILURE;
+}
+
+int
+kmpw_set(struct zapi_pw *zpw)
+{
+ debug_zebra_out("pseudowire %s nexthop %s labels %u/%u (set)",
+ zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop),
+ zpw->local_label, zpw->remote_label);
+
+ return zebra_send_pw(zclient, ZEBRA_PW_SET, zpw) == ZCLIENT_SEND_FAILURE;
+}
+
+int
+kmpw_unset(struct zapi_pw *zpw)
+{
+ debug_zebra_out("pseudowire %s nexthop %s (unset)",
+ zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop));
+
+ return zebra_send_pw(zclient, ZEBRA_PW_UNSET, zpw) == ZCLIENT_SEND_FAILURE;
+}
+
+void
+kif_redistribute(const char *ifname)
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct listnode *cnode;
+ struct interface *ifp;
+ struct connected *ifc;
+ struct kif kif;
+ struct kaddr ka;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if (ifname && strcmp(ifname, ifp->name) != 0)
+ continue;
+
+ ifp2kif(ifp, &kif);
+ main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif));
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) {
+ ifc2kaddr(ifp, ifc, &ka);
+ main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka));
+ }
+ }
+}
+
+static int
+ldp_router_id_update(ZAPI_CALLBACK_ARGS)
+{
+ struct prefix router_id;
+
+ zebra_router_id_update_read(zclient->ibuf, &router_id);
+
+ if (bad_addr_v4(router_id.u.prefix4))
+ return (0);
+
+ debug_zebra_in("router-id update %pI4", &router_id.u.prefix4);
+
+ global.rtr_id.s_addr = router_id.u.prefix4.s_addr;
+ main_imsg_compose_ldpe(IMSG_RTRID_UPDATE, 0, &global.rtr_id,
+ sizeof(global.rtr_id));
+
+ return (0);
+}
+
+static int
+ldp_ifp_create(struct interface *ifp)
+{
+ struct kif kif;
+
+ debug_zebra_in("interface add %s index %d mtu %d", ifp->name,
+ ifp->ifindex, ifp->mtu);
+
+ ifp2kif(ifp, &kif);
+ main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif));
+
+ return 0;
+}
+
+static int
+ldp_ifp_destroy(struct interface *ifp)
+{
+ struct kif kif;
+
+ debug_zebra_in("interface delete %s index %d mtu %d", ifp->name,
+ ifp->ifindex, ifp->mtu);
+
+ ifp2kif(ifp, &kif);
+ main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif));
+
+ return (0);
+}
+
+static int
+ldp_interface_status_change(struct interface *ifp)
+{
+ struct listnode *node;
+ struct connected *ifc;
+ struct kif kif;
+ struct kaddr ka;
+
+ debug_zebra_in("interface %s state update", ifp->name);
+
+ ifp2kif(ifp, &kif);
+ main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif));
+
+ if (if_is_operative(ifp)) {
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+ ifc2kaddr(ifp, ifc, &ka);
+ main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka));
+ }
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+ ifc2kaddr(ifp, ifc, &ka);
+ main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka, sizeof(ka));
+ }
+ }
+
+ return (0);
+}
+
+static int ldp_ifp_up(struct interface *ifp)
+{
+ return ldp_interface_status_change(ifp);
+}
+
+static int ldp_ifp_down(struct interface *ifp)
+{
+ return ldp_interface_status_change(ifp);
+}
+
+static int
+ldp_interface_address_add(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *ifc;
+ struct interface *ifp;
+ struct kaddr ka;
+
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+ if (ifc == NULL)
+ return (0);
+
+ ifp = ifc->ifp;
+ ifc2kaddr(ifp, ifc, &ka);
+
+ /* Filter invalid addresses. */
+ if (bad_addr(ka.af, &ka.addr))
+ return (0);
+
+ debug_zebra_in("address add %s/%u interface %s",
+ log_addr(ka.af, &ka.addr), ka.prefixlen, ifp->name);
+
+ /* notify ldpe about new address */
+ main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka));
+
+ return (0);
+}
+
+static int
+ldp_interface_address_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *ifc;
+ struct interface *ifp;
+ struct kaddr ka;
+
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+ if (ifc == NULL)
+ return (0);
+
+ ifp = ifc->ifp;
+ ifc2kaddr(ifp, ifc, &ka);
+ connected_free(&ifc);
+
+ /* Filter invalid addresses. */
+ if (bad_addr(ka.af, &ka.addr))
+ return (0);
+
+ debug_zebra_in("address delete %s/%u interface %s",
+ log_addr(ka.af, &ka.addr), ka.prefixlen, ifp->name);
+
+ /* notify ldpe about removed address */
+ main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka, sizeof(ka));
+
+ return (0);
+}
+
+static int
+ldp_zebra_read_route(ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_route api;
+ struct zapi_nexthop *api_nh;
+ struct kroute kr;
+ int i, add = 0;
+
+ if (zapi_route_decode(zclient->ibuf, &api) < 0)
+ return -1;
+
+ /* we completely ignore srcdest routes for now. */
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
+ return (0);
+
+ memset(&kr, 0, sizeof(kr));
+ kr.af = api.prefix.family;
+ switch (kr.af) {
+ case AF_INET:
+ kr.prefix.v4 = api.prefix.u.prefix4;
+ break;
+ case AF_INET6:
+ kr.prefix.v6 = api.prefix.u.prefix6;
+ break;
+ default:
+ break;
+ }
+ kr.prefixlen = api.prefix.prefixlen;
+ kr.route_type = api.type;
+ kr.route_instance = api.instance;
+
+ switch (api.type) {
+ case ZEBRA_ROUTE_CONNECT:
+ SET_FLAG(kr.flags, F_CONNECTED);
+ break;
+ case ZEBRA_ROUTE_BGP:
+ /* LDP should follow the IGP and ignore BGP routes */
+ return (0);
+ default:
+ break;
+ }
+
+ if (bad_addr(kr.af, &kr.prefix) ||
+ (kr.af == AF_INET6 && IN6_IS_SCOPE_EMBED(&kr.prefix.v6)))
+ return (0);
+
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ add = 1;
+
+ if (api.nexthop_num == 0)
+ debug_zebra_in("route %s %s/%d (%s)", (add) ? "add" : "delete",
+ log_addr(kr.af, &kr.prefix), kr.prefixlen,
+ zebra_route_string(api.type));
+
+ /* loop through all the nexthops */
+ for (i = 0; i < api.nexthop_num; i++) {
+ api_nh = &api.nexthops[i];
+ switch (api_nh->type) {
+ case NEXTHOP_TYPE_IPV4:
+ if (kr.af != AF_INET)
+ continue;
+ kr.nexthop.v4 = api_nh->gate.ipv4;
+ kr.ifindex = 0;
+ break;
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ if (kr.af != AF_INET)
+ continue;
+ kr.nexthop.v4 = api_nh->gate.ipv4;
+ kr.ifindex = api_nh->ifindex;
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ if (kr.af != AF_INET6)
+ continue;
+ kr.nexthop.v6 = api_nh->gate.ipv6;
+ kr.ifindex = 0;
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ if (kr.af != AF_INET6)
+ continue;
+ kr.nexthop.v6 = api_nh->gate.ipv6;
+ kr.ifindex = api_nh->ifindex;
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ if (!CHECK_FLAG(kr.flags, F_CONNECTED))
+ continue;
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ continue;
+ }
+
+ debug_zebra_in("route %s %s/%d nexthop %s ifindex %u (%s)",
+ (add) ? "add" : "delete", log_addr(kr.af, &kr.prefix),
+ kr.prefixlen, log_addr(kr.af, &kr.nexthop), kr.ifindex,
+ zebra_route_string(api.type));
+
+ if (add)
+ main_imsg_compose_lde(IMSG_NETWORK_ADD, 0, &kr, sizeof(kr));
+ }
+
+ main_imsg_compose_lde(IMSG_NETWORK_UPDATE, 0, &kr, sizeof(kr));
+
+ return (0);
+}
+
+/*
+ * Receive PW status update from Zebra and send it to LDE process.
+ */
+static int
+ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_pw_status zpw;
+
+ zebra_read_pw_status_update(cmd, zclient, length, vrf_id, &zpw);
+
+ debug_zebra_in("pseudowire %s status %s 0x%x", zpw.ifname,
+ (zpw.status == PW_FORWARDING) ? "up" : "down",
+ zpw.status);
+
+ main_imsg_compose_lde(IMSG_PW_UPDATE, 0, &zpw, sizeof(zpw));
+
+ return (0);
+}
+
+void ldp_zebra_regdereg_zebra_info(bool want_register)
+{
+ if (zebra_registered == want_register)
+ return;
+
+ log_debug("%s to receive default VRF information",
+ want_register ? "Register" : "De-register");
+
+ if (want_register) {
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP,
+ ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT);
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient,
+ AFI_IP6, ZEBRA_ROUTE_ALL, 0,
+ VRF_DEFAULT);
+ } else {
+ zclient_send_dereg_requests(zclient, VRF_DEFAULT);
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient,
+ AFI_IP, ZEBRA_ROUTE_ALL, 0,
+ VRF_DEFAULT);
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient,
+ AFI_IP6, ZEBRA_ROUTE_ALL, 0,
+ VRF_DEFAULT);
+ }
+ zebra_registered = want_register;
+}
+
+static void
+ldp_zebra_connected(struct zclient *zclient)
+{
+ zebra_registered = false;
+
+ /* if MPLS was already enabled and we are re-connecting, register again
+ */
+ if (CHECK_FLAG(vty_conf->flags, F_LDPD_ENABLED))
+ ldp_zebra_regdereg_zebra_info(true);
+
+ ldp_zebra_opaque_register();
+
+ ldp_sync_zebra_init();
+}
+
+static void
+ldp_zebra_filter_update(struct access_list *access)
+{
+ struct ldp_access laccess;
+
+ if (access && access->name[0] != '\0') {
+ strlcpy(laccess.name, access->name, sizeof(laccess.name));
+ debug_evt("%s ACL update filter name %s", __func__, access->name);
+
+ main_imsg_compose_both(IMSG_FILTER_UPDATE, &laccess, sizeof(laccess));
+ }
+}
+
+extern struct zebra_privs_t ldpd_privs;
+
+static zclient_handler *const ldp_handlers[] = {
+ [ZEBRA_ROUTER_ID_UPDATE] = ldp_router_id_update,
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = ldp_interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = ldp_interface_address_delete,
+ [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ldp_zebra_read_route,
+ [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ldp_zebra_read_route,
+ [ZEBRA_PW_STATUS_UPDATE] = ldp_zebra_read_pw_status_update,
+ [ZEBRA_OPAQUE_MESSAGE] = ldp_zebra_opaque_msg_handler,
+};
+
+void ldp_zebra_init(struct event_loop *master)
+{
+ if_zapi_callbacks(ldp_ifp_create, ldp_ifp_up, ldp_ifp_down, ldp_ifp_destroy);
+
+ /* Set default values. */
+ zclient = zclient_new(master, &zclient_options_default, ldp_handlers,
+ array_size(ldp_handlers));
+ zclient_init(zclient, ZEBRA_ROUTE_LDP, 0, &ldpd_privs);
+
+ /* set callbacks */
+ zclient->zebra_connected = ldp_zebra_connected;
+
+ /* Access list initialize. */
+ access_list_add_hook(ldp_zebra_filter_update);
+ access_list_delete_hook(ldp_zebra_filter_update);
+}
+
+void
+ldp_zebra_destroy(void)
+{
+ ldp_zebra_opaque_unregister();
+ zclient_stop(zclient);
+ zclient_free(zclient);
+ zclient = NULL;
+
+ if (zclient_sync == NULL)
+ return;
+ zclient_stop(zclient_sync);
+ zclient_free(zclient_sync);
+ zclient_sync = NULL;
+}
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
new file mode 100644
index 0000000..3c616d4
--- /dev/null
+++ b/ldpd/ldpd.c
@@ -0,0 +1,2035 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.h>
+#include <sys/wait.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+#include "ldp_vty.h"
+#include "ldp_debug.h"
+
+#include <lib/version.h>
+#include <lib/log.h>
+#include "getopt.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "zclient.h"
+#include "vrf.h"
+#include "filter.h"
+#include "qobj.h"
+#include "libfrr.h"
+#include "lib_errors.h"
+
+static void ldpd_shutdown(void);
+static pid_t start_child(enum ldpd_process, char *, int, int);
+static void main_dispatch_ldpe(struct event *thread);
+static void main_dispatch_lde(struct event *thread);
+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 void ldp_config_normalize(struct ldpd_conf *);
+static void ldp_config_reset(struct ldpd_conf *);
+static void ldp_config_reset_main(struct ldpd_conf *);
+static void ldp_config_reset_af(struct ldpd_conf *, int);
+static void ldp_config_reset_l2vpns(struct ldpd_conf *);
+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 *);
+
+DEFINE_QOBJ_TYPE(iface);
+DEFINE_QOBJ_TYPE(tnbr);
+DEFINE_QOBJ_TYPE(nbr_params);
+DEFINE_QOBJ_TYPE(l2vpn_if);
+DEFINE_QOBJ_TYPE(l2vpn_pw);
+DEFINE_QOBJ_TYPE(l2vpn);
+DEFINE_QOBJ_TYPE(ldpd_conf);
+
+struct ldpd_global global;
+struct ldpd_init init;
+struct ldpd_conf *ldpd_conf, *vty_conf;
+
+static struct imsgev *iev_ldpe, *iev_ldpe_sync;
+static struct imsgev *iev_lde, *iev_lde_sync;
+static pid_t ldpe_pid;
+static pid_t lde_pid;
+
+static struct frr_daemon_info ldpd_di;
+
+DEFINE_HOOK(ldp_register_mib, (struct event_loop * tm), (tm));
+
+static void ldp_load_module(const char *name)
+{
+ const char *dir;
+ dir = ldpd_di.module_path ? ldpd_di.module_path : frr_moduledir;
+ struct frrmod_runtime *module;
+
+ module = frrmod_load(name, dir, NULL,NULL);
+ if (!module) {
+ fprintf(stderr, "%s: failed to load %s", __func__, name);
+ log_warnx("%s: failed to load %s", __func__, name);
+ }
+}
+
+void ldp_agentx_enabled(void)
+{
+ ldp_load_module("snmp");
+ hook_call(ldp_register_mib, master);
+}
+
+enum ldpd_process ldpd_process;
+
+#define LDP_DEFAULT_CONFIG "ldpd.conf"
+#define LDP_VTY_PORT 2612
+
+/* Master of threads. */
+struct event_loop *master;
+
+/* ldpd privileges */
+static zebra_capabilities_t _caps_p [] =
+{
+ ZCAP_BIND,
+ ZCAP_NET_ADMIN
+};
+
+struct zebra_privs_t ldpd_privs =
+{
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0
+};
+
+/* CTL Socket path */
+char ctl_sock_path[MAXPATHLEN];
+
+/* LDPd options. */
+#define OPTION_CTLSOCK 1001
+static const struct option longopts[] =
+{
+ { "ctl_socket", required_argument, NULL, OPTION_CTLSOCK},
+ { "instance", required_argument, NULL, 'n'},
+ { 0 }
+};
+
+/* SIGHUP handler. */
+static void
+sighup(void)
+{
+ log_info("SIGHUP received");
+
+ /*
+ * Do a full configuration reload. In other words, reset vty_conf
+ * and build a new configuartion from scratch.
+ */
+ ldp_config_reset(vty_conf);
+ vty_read_config(NULL, ldpd_di.config_file, config_default);
+ ldp_config_apply(NULL, vty_conf);
+}
+
+/* SIGINT / SIGTERM handler. */
+static void
+sigint(void)
+{
+ log_info("SIGINT received");
+ ldpd_shutdown();
+}
+
+/* SIGUSR1 handler. */
+static void
+sigusr1(void)
+{
+ zlog_rotate();
+}
+
+static struct frr_signal_t ldp_signals[] =
+{
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ }
+};
+
+static const struct frr_yang_module_info *const ldpd_yang_modules[] = {
+ &frr_filter_info,
+ &frr_vrf_info,
+};
+
+FRR_DAEMON_INFO(ldpd, LDP,
+ .vty_port = LDP_VTY_PORT,
+
+ .proghelp = "Implementation of the LDP protocol.",
+
+ .signals = ldp_signals,
+ .n_signals = array_size(ldp_signals),
+
+ .privs = &ldpd_privs,
+
+ .yang_modules = ldpd_yang_modules,
+ .n_yang_modules = array_size(ldpd_yang_modules),
+);
+
+static void ldp_config_fork_apply(struct event *t)
+{
+ /*
+ * So the frr_config_fork() function schedules
+ * the read of the vty config( if there is a
+ * non-integrated config ) to be after the
+ * end of startup and we are starting the
+ * main process loop. We need to schedule
+ * the application of this if necessary
+ * after the read in of the config.
+ */
+ ldp_config_apply(NULL, vty_conf);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *saved_argv0;
+ int lflag = 0, eflag = 0;
+ int pipe_parent2ldpe[2], pipe_parent2ldpe_sync[2];
+ int pipe_parent2lde[2], pipe_parent2lde_sync[2];
+ char *ctl_sock_name;
+ bool ctl_sock_used = false;
+
+ snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET,
+ "", "");
+
+ ldpd_process = PROC_MAIN;
+ log_procname = log_procnames[ldpd_process];
+
+ saved_argv0 = argv[0];
+ if (saved_argv0 == NULL)
+ saved_argv0 = (char *)"ldpd";
+
+ frr_preinit(&ldpd_di, argc, argv);
+ frr_opt_add("LEn:", longopts,
+ " --ctl_socket Override ctl socket path\n"
+ " -n, --instance Instance id\n");
+
+ while (1) {
+ int opt;
+
+ opt = frr_getopt(argc, argv, NULL);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ case OPTION_CTLSOCK:
+ ctl_sock_used = true;
+ ctl_sock_name = strrchr(LDPD_SOCKET, '/');
+ if (ctl_sock_name)
+ /* skip '/' */
+ ctl_sock_name++;
+ else
+ /*
+ * LDPD_SOCKET configured as relative path
+ * during config? Should really never happen for
+ * sensible config
+ */
+ ctl_sock_name = (char *)LDPD_SOCKET;
+ strlcpy(ctl_sock_path, optarg, sizeof(ctl_sock_path));
+ strlcat(ctl_sock_path, "/", sizeof(ctl_sock_path));
+ strlcat(ctl_sock_path, ctl_sock_name,
+ sizeof(ctl_sock_path));
+ break;
+ case 'n':
+ init.instance = atoi(optarg);
+ if (init.instance < 1)
+ exit(0);
+ break;
+ case 'L':
+ lflag = 1;
+ break;
+ case 'E':
+ eflag = 1;
+ break;
+ default:
+ frr_help_exit(1);
+ }
+ }
+
+ if (ldpd_di.pathspace && !ctl_sock_used)
+ snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET,
+ "/", ldpd_di.pathspace);
+
+ strlcpy(init.user, ldpd_privs.user, sizeof(init.user));
+ strlcpy(init.group, ldpd_privs.group, sizeof(init.group));
+ strlcpy(init.ctl_sock_path, ctl_sock_path, sizeof(init.ctl_sock_path));
+ strlcpy(init.zclient_serv_path, frr_zclientpath,
+ sizeof(init.zclient_serv_path));
+
+ argc -= optind;
+ if (argc > 0 || (lflag && eflag))
+ frr_help_exit(1);
+
+ /* check for root privileges */
+ if (geteuid() != 0) {
+ errno = EPERM;
+ perror(ldpd_di.progname);
+ exit(1);
+ }
+
+ if (lflag || eflag) {
+ struct zprivs_ids_t ids;
+
+ zprivs_preinit(&ldpd_privs);
+ zprivs_get_ids(&ids);
+
+ zlog_init(ldpd_di.progname, "LDP", 0,
+ ids.uid_normal, ids.gid_normal);
+ }
+ if (lflag)
+ lde();
+ else if (eflag)
+ ldpe();
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2ldpe) == -1)
+ fatal("socketpair");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
+ pipe_parent2ldpe_sync) == -1)
+ fatal("socketpair");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2lde) == -1)
+ fatal("socketpair");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
+ pipe_parent2lde_sync) == -1)
+ fatal("socketpair");
+
+ sock_set_nonblock(pipe_parent2ldpe[0]);
+ sock_set_cloexec(pipe_parent2ldpe[0]);
+ sock_set_nonblock(pipe_parent2ldpe[1]);
+ sock_set_cloexec(pipe_parent2ldpe[1]);
+ sock_set_nonblock(pipe_parent2ldpe_sync[0]);
+ sock_set_cloexec(pipe_parent2ldpe_sync[0]);
+ sock_set_cloexec(pipe_parent2ldpe_sync[1]);
+ sock_set_nonblock(pipe_parent2lde[0]);
+ sock_set_cloexec(pipe_parent2lde[0]);
+ sock_set_nonblock(pipe_parent2lde[1]);
+ sock_set_cloexec(pipe_parent2lde[1]);
+ sock_set_nonblock(pipe_parent2lde_sync[0]);
+ sock_set_cloexec(pipe_parent2lde_sync[0]);
+ sock_set_cloexec(pipe_parent2lde_sync[1]);
+
+ /* start children */
+ lde_pid = start_child(PROC_LDE_ENGINE, saved_argv0,
+ pipe_parent2lde[1], pipe_parent2lde_sync[1]);
+ ldpe_pid = start_child(PROC_LDP_ENGINE, saved_argv0,
+ pipe_parent2ldpe[1], pipe_parent2ldpe_sync[1]);
+
+ master = frr_init();
+
+ vrf_init(NULL, NULL, NULL, NULL);
+ access_list_init();
+ ldp_vty_init();
+ ldp_zebra_init(master);
+
+ /*
+ * Create base configuration with sane defaults. All configuration
+ * requests (e.g. CLI) act on vty_conf and then call ldp_config_apply()
+ * to merge the changes into ldpd_conf, which contains the actual
+ * running configuration.
+ */
+ ldpd_conf = config_new_empty();
+ vty_conf = config_new_empty();
+ QOBJ_REG(vty_conf, ldpd_conf);
+
+ /* read configuration file and daemonize */
+ frr_config_fork();
+
+ /* apply configuration */
+ event_add_event(master, ldp_config_fork_apply, NULL, 0, NULL);
+
+ /* setup pipes to children */
+ if ((iev_ldpe = calloc(1, sizeof(struct imsgev))) == NULL ||
+ (iev_ldpe_sync = calloc(1, sizeof(struct imsgev))) == NULL ||
+ (iev_lde = calloc(1, sizeof(struct imsgev))) == NULL ||
+ (iev_lde_sync = calloc(1, sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+
+ imsg_init(&iev_ldpe->ibuf, pipe_parent2ldpe[0]);
+ iev_ldpe->handler_read = main_dispatch_ldpe;
+ event_add_read(master, iev_ldpe->handler_read, iev_ldpe,
+ iev_ldpe->ibuf.fd, &iev_ldpe->ev_read);
+ iev_ldpe->handler_write = ldp_write_handler;
+
+ imsg_init(&iev_ldpe_sync->ibuf, pipe_parent2ldpe_sync[0]);
+ iev_ldpe_sync->handler_read = main_dispatch_ldpe;
+ event_add_read(master, iev_ldpe_sync->handler_read, iev_ldpe_sync,
+ iev_ldpe_sync->ibuf.fd, &iev_ldpe_sync->ev_read);
+ iev_ldpe_sync->handler_write = ldp_write_handler;
+
+ imsg_init(&iev_lde->ibuf, pipe_parent2lde[0]);
+ iev_lde->handler_read = main_dispatch_lde;
+ event_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd,
+ &iev_lde->ev_read);
+ iev_lde->handler_write = ldp_write_handler;
+
+ imsg_init(&iev_lde_sync->ibuf, pipe_parent2lde_sync[0]);
+ iev_lde_sync->handler_read = main_dispatch_lde;
+ event_add_read(master, iev_lde_sync->handler_read, iev_lde_sync,
+ iev_lde_sync->ibuf.fd, &iev_lde_sync->ev_read);
+ iev_lde_sync->handler_write = ldp_write_handler;
+
+ if (main_imsg_send_ipc_sockets(&iev_ldpe->ibuf, &iev_lde->ibuf))
+ fatal("could not establish imsg links");
+
+ main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, sizeof(ldp_debug));
+ main_imsg_compose_both(IMSG_INIT, &init, sizeof(init));
+ main_imsg_send_config(ldpd_conf);
+
+ if (CHECK_FLAG(ldpd_conf->ipv4.flags, F_LDPD_AF_ENABLED))
+ main_imsg_send_net_sockets(AF_INET);
+
+ if (CHECK_FLAG(ldpd_conf->ipv6.flags, F_LDPD_AF_ENABLED))
+ main_imsg_send_net_sockets(AF_INET6);
+
+ frr_run(master);
+
+ /* NOTREACHED */
+ return (0);
+}
+
+static void
+ldpd_shutdown(void)
+{
+ pid_t pid;
+ int status;
+
+ frr_early_fini();
+
+ /* close pipes */
+ msgbuf_clear(&iev_ldpe->ibuf.w);
+ close(iev_ldpe->ibuf.fd);
+ msgbuf_clear(&iev_lde->ibuf.w);
+ close(iev_lde->ibuf.fd);
+
+ config_clear(ldpd_conf);
+
+ ldp_config_reset(vty_conf);
+ QOBJ_UNREG(vty_conf);
+ free(vty_conf);
+
+ log_debug("waiting for children to terminate");
+
+ while (true) {
+ /* Wait for child process. */
+ pid = wait(&status);
+ if (pid == -1) {
+ /* We got interrupted, try again. */
+ if (errno == EINTR)
+ continue;
+ /* No more processes were found. */
+ if (errno == ECHILD)
+ break;
+
+ /* Unhandled errno condition. */
+ fatal("wait");
+ /* UNREACHABLE */
+ }
+
+ /* We found something, lets announce it. */
+ if (WIFSIGNALED(status))
+ log_warnx("%s terminated; signal %d",
+ (pid == lde_pid ? "label decision engine"
+ : "ldp engine"),
+ WTERMSIG(status));
+
+ /* Repeat until there are no more child processes. */
+ }
+
+ free(iev_ldpe);
+ free(iev_lde);
+
+ log_info("terminating");
+
+ vrf_terminate();
+ access_list_reset();
+ ldp_zebra_destroy();
+
+ frr_fini();
+ exit(0);
+}
+
+static pid_t
+start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync)
+{
+ char *argv[7];
+ int argc = 0, nullfd;
+ pid_t pid;
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ fatal("cannot fork");
+ case 0:
+ break;
+ default:
+ close(fd_async);
+ close(fd_sync);
+ return (pid);
+ }
+
+ nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
+ if (nullfd == -1) {
+ flog_err_sys(EC_LIB_SYSTEM_CALL,
+ "%s: failed to open /dev/null: %s", __func__,
+ safe_strerror(errno));
+ } else {
+ dup2(nullfd, 0);
+ dup2(nullfd, 1);
+ dup2(nullfd, 2);
+ close(nullfd);
+ }
+
+ if (dup2(fd_async, LDPD_FD_ASYNC) == -1)
+ fatal("cannot setup imsg async fd");
+
+ if (dup2(fd_sync, LDPD_FD_SYNC) == -1)
+ fatal("cannot setup imsg sync fd");
+
+ argv[argc++] = argv0;
+ switch (p) {
+ case PROC_MAIN:
+ fatalx("Can not start main process");
+ case PROC_LDE_ENGINE:
+ argv[argc++] = (char *)"-L";
+ break;
+ case PROC_LDP_ENGINE:
+ argv[argc++] = (char *)"-E";
+ break;
+ }
+
+ argv[argc++] = (char *)"-u";
+ argv[argc++] = (char *)ldpd_privs.user;
+ argv[argc++] = (char *)"-g";
+ argv[argc++] = (char *)ldpd_privs.group;
+ argv[argc++] = NULL;
+
+ execvp(argv0, argv);
+ fatal("execvp");
+}
+
+/* imsg handling */
+/* ARGSUSED */
+static void main_dispatch_ldpe(struct event *thread)
+{
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ int af;
+ ssize_t n;
+ int shut = 0;
+
+ iev->ev_read = NULL;
+
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_LOG:
+ logit(imsg.hdr.pid, "%s", (const char *)imsg.data);
+ break;
+ case IMSG_REQUEST_SOCKETS:
+ af = imsg.hdr.pid;
+ main_imsg_send_net_sockets(af);
+ break;
+ case IMSG_ACL_CHECK:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct acl_check))
+ fatalx("IMSG_ACL_CHECK imsg with wrong len");
+ ldp_acl_reply(iev, (struct acl_check *)imsg.data);
+ break;
+ case IMSG_LDP_SYNC_IF_STATE_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ldp_igp_sync_if_state))
+ fatalx("IMSG_LDP_SYNC_IF_STATE_UPDATE imsg with wrong len");
+
+ ldp_sync_zebra_send_state_update((struct ldp_igp_sync_if_state *)imsg.data);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handlers and exit */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ ldpe_pid = 0;
+
+ if (lde_pid == 0)
+ ldpd_shutdown();
+ else
+ kill(lde_pid, SIGTERM);
+ }
+}
+
+/* ARGSUSED */
+static void main_dispatch_lde(struct event *thread)
+{
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ ssize_t n;
+ int shut = 0;
+ struct zapi_rlfa_response *rlfa_labels;
+
+ iev->ev_read = NULL;
+
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_LOG:
+ logit(imsg.hdr.pid, "%s", (const char *)imsg.data);
+ break;
+ 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_KPW_ADD:
+ case IMSG_KPW_DELETE:
+ case IMSG_KPW_SET:
+ case IMSG_KPW_UNSET:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct zapi_pw))
+ fatalx("invalid size of IMSG_KPWLABEL_CHANGE");
+
+ switch (imsg.hdr.type) {
+ case IMSG_KPW_ADD:
+ if (kmpw_add(imsg.data))
+ log_warnx("%s: error adding pseudowire", __func__);
+ break;
+ case IMSG_KPW_DELETE:
+ if (kmpw_del(imsg.data))
+ log_warnx("%s: error deleting pseudowire", __func__);
+ break;
+ case IMSG_KPW_SET:
+ if (kmpw_set(imsg.data))
+ log_warnx("%s: error setting pseudowire", __func__);
+ break;
+ case IMSG_KPW_UNSET:
+ if (kmpw_unset(imsg.data))
+ log_warnx("%s: error unsetting pseudowire", __func__);
+ break;
+ }
+ break;
+ case IMSG_ACL_CHECK:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct acl_check))
+ fatalx("IMSG_ACL_CHECK imsg with wrong len");
+ ldp_acl_reply(iev, (struct acl_check *)imsg.data);
+ break;
+ case IMSG_RLFA_LABELS:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_response)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_labels = imsg.data;
+ ldp_zebra_send_rlfa_labels(rlfa_labels);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+
+ imsg_free(&imsg);
+ }
+
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handlers and exit */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ lde_pid = 0;
+ if (ldpe_pid == 0)
+ ldpd_shutdown();
+ else
+ kill(ldpe_pid, SIGTERM);
+ }
+}
+
+/* ARGSUSED */
+void ldp_write_handler(struct event *thread)
+{
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ ssize_t n;
+
+ iev->ev_write = NULL;
+
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handlers */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ return;
+ }
+
+ imsg_event_add(iev);
+}
+
+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);
+}
+
+int
+main_imsg_compose_both(enum imsg_type type, void *buf, uint16_t len)
+{
+ if (iev_ldpe == NULL || iev_lde == NULL)
+ return (0);
+
+ 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)
+{
+ if (iev->handler_read)
+ event_add_read(master, iev->handler_read, iev, iev->ibuf.fd,
+ &iev->ev_read);
+
+ if (iev->handler_write && iev->ibuf.w.queued)
+ event_add_write(master, iev->handler_write, iev, iev->ibuf.fd,
+ &iev->ev_write);
+}
+
+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_write(master, eb->handler, eb->arg, eb->wbuf.fd,
+ &eb->ev);
+}
+
+void evbuf_init(struct evbuf *eb, int fd, void (*handler)(struct event *),
+ void *arg)
+{
+ msgbuf_init(&eb->wbuf);
+ eb->wbuf.fd = fd;
+ eb->handler = handler;
+ eb->arg = arg;
+}
+
+void
+evbuf_clear(struct evbuf *eb)
+{
+ EVENT_OFF(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, PF_UNSPEC, pipe_ldpe2lde) == -1)
+ return (-1);
+ sock_set_nonblock(pipe_ldpe2lde[0]);
+ sock_set_nonblock(pipe_ldpe2lde[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)
+{
+ if (!ldp_addrisset(af, &(ldp_af_conf_get(ldpd_conf, af))->trans_addr))
+ return;
+
+ 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));
+}
+
+int
+ldp_acl_request(struct imsgev *iev, char *acl_name, int af,
+ union ldpd_addr *addr, uint8_t prefixlen)
+{
+ struct imsg imsg;
+ struct acl_check acl_check;
+
+ if (acl_name[0] == '\0')
+ return FILTER_PERMIT;
+
+ /* build request */
+ strlcpy(acl_check.acl, acl_name, sizeof(acl_check.acl));
+ acl_check.af = af;
+ acl_check.addr = *addr;
+ acl_check.prefixlen = prefixlen;
+
+ /* send (blocking) */
+ imsg_compose_event(iev, IMSG_ACL_CHECK, 0, 0, -1, &acl_check,
+ sizeof(acl_check));
+ imsg_flush(&iev->ibuf);
+
+ /* receive (blocking) and parse result */
+ if (imsg_read(&iev->ibuf) == -1)
+ fatal("imsg_read error");
+
+ if (imsg_get(&iev->ibuf, &imsg) == -1)
+ fatal("imsg_get");
+
+ if (imsg.hdr.type != IMSG_ACL_CHECK ||
+ imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(int))
+ fatalx("ldp_acl_request: invalid response");
+
+ return (*((int *)imsg.data));
+}
+
+void
+ldp_acl_reply(struct imsgev *iev, struct acl_check *acl_check)
+{
+ struct access_list *alist;
+ struct prefix prefix;
+ int result;
+
+ alist = access_list_lookup(family2afi(acl_check->af), acl_check->acl);
+ if (alist == NULL)
+ result = FILTER_DENY;
+ else {
+ prefix.family = acl_check->af;
+ switch (prefix.family) {
+ case AF_INET:
+ prefix.u.prefix4 = acl_check->addr.v4;
+ break;
+ case AF_INET6:
+ prefix.u.prefix6 = acl_check->addr.v6;
+ break;
+ default:
+ fatalx("ldp_acl_reply: unknown af");
+ }
+ prefix.prefixlen = acl_check->prefixlen;
+ result = access_list_apply(alist, &prefix);
+ }
+
+ imsg_compose_event(iev, IMSG_ACL_CHECK, 0, 0, -1, &result,
+ sizeof(result));
+}
+
+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 (CHECK_FLAG(xconf->ipv4.flags, F_LDPD_AF_ENABLED)
+ && CHECK_FLAG(xconf->ipv6.flags, F_LDPD_AF_ENABLED));
+}
+
+in_addr_t
+ldp_rtr_id_get(struct ldpd_conf *xconf)
+{
+ if (xconf->rtr_id.s_addr != INADDR_ANY)
+ return (xconf->rtr_id.s_addr);
+ else
+ return (global.rtr_id.s_addr);
+}
+
+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);
+
+ RB_FOREACH(iface, iface_head, &xconf->iface_tree) {
+ if (main_imsg_compose_both(IMSG_RECONF_IFACE, iface,
+ sizeof(*iface)) == -1)
+ return (-1);
+ }
+
+ RB_FOREACH(tnbr, tnbr_head, &xconf->tnbr_tree) {
+ if (main_imsg_compose_both(IMSG_RECONF_TNBR, tnbr,
+ sizeof(*tnbr)) == -1)
+ return (-1);
+ }
+
+ RB_FOREACH(nbrp, nbrp_head, &xconf->nbrp_tree) {
+ if (main_imsg_compose_both(IMSG_RECONF_NBRP, nbrp,
+ sizeof(*nbrp)) == -1)
+ return (-1);
+ }
+
+ RB_FOREACH(l2vpn, l2vpn_head, &xconf->l2vpn_tree) {
+ if (main_imsg_compose_both(IMSG_RECONF_L2VPN, l2vpn,
+ sizeof(*l2vpn)) == -1)
+ return (-1);
+
+ RB_FOREACH(lif, l2vpn_if_head, &l2vpn->if_tree) {
+ if (main_imsg_compose_both(IMSG_RECONF_L2VPN_IF, lif,
+ sizeof(*lif)) == -1)
+ return (-1);
+ }
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) {
+ if (main_imsg_compose_both(IMSG_RECONF_L2VPN_PW, pw,
+ sizeof(*pw)) == -1)
+ return (-1);
+ }
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_inactive_tree) {
+ if (main_imsg_compose_both(IMSG_RECONF_L2VPN_IPW, pw,
+ sizeof(*pw)) == -1)
+ return (-1);
+ }
+ }
+
+ if (main_imsg_compose_both(IMSG_RECONF_END, NULL, 0) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+ldp_config_apply(struct vty *vty, struct ldpd_conf *xconf)
+{
+ /*
+ * When reading from a configuration file (startup and sighup), we
+ * call merge_config() only once after the whole config has been read.
+ * This is the optimal and least disruptive way to update the running
+ * configuration.
+ */
+ if (vty && vty->type == VTY_FILE)
+ return (0);
+
+ ldp_config_normalize(xconf);
+
+ if (main_imsg_send_config(xconf) == -1)
+ return (-1);
+
+ merge_config(ldpd_conf, xconf);
+
+ return (0);
+}
+
+static void
+ldp_config_normalize(struct ldpd_conf *xconf)
+{
+ struct iface *iface, *itmp;
+ struct nbr_params *nbrp, *ntmp;
+ struct l2vpn *l2vpn;
+ struct l2vpn_pw *pw, *ptmp;
+
+ if (!CHECK_FLAG(xconf->flags, F_LDPD_ENABLED))
+ ldp_config_reset_main(xconf);
+ else {
+ if (!CHECK_FLAG(xconf->ipv4.flags, F_LDPD_AF_ENABLED))
+ ldp_config_reset_af(xconf, AF_INET);
+ if (!CHECK_FLAG(xconf->ipv6.flags, F_LDPD_AF_ENABLED))
+ ldp_config_reset_af(xconf, AF_INET6);
+
+ RB_FOREACH_SAFE(iface, iface_head, &xconf->iface_tree, itmp) {
+ if (iface->ipv4.enabled || iface->ipv6.enabled)
+ continue;
+
+ QOBJ_UNREG(iface);
+ RB_REMOVE(iface_head, &vty_conf->iface_tree, iface);
+ free(iface);
+ }
+
+ RB_FOREACH_SAFE(nbrp, nbrp_head, &xconf->nbrp_tree, ntmp) {
+ if (CHECK_FLAG(nbrp->flags, (F_NBRP_KEEPALIVE|F_NBRP_GTSM)))
+ continue;
+ if (nbrp->auth.method != AUTH_NONE)
+ continue;
+
+ QOBJ_UNREG(nbrp);
+ RB_REMOVE(nbrp_head, &vty_conf->nbrp_tree, nbrp);
+ free(nbrp);
+ }
+ }
+
+ RB_FOREACH(l2vpn, l2vpn_head, &xconf->l2vpn_tree) {
+ RB_FOREACH_SAFE(pw, l2vpn_pw_head, &l2vpn->pw_tree, ptmp) {
+ if (!CHECK_FLAG(pw->flags, F_PW_STATIC_NBR_ADDR)) {
+ pw->af = AF_INET;
+ pw->addr.v4 = pw->lsr_id;
+ }
+
+ if (pw->lsr_id.s_addr != INADDR_ANY && pw->pwid != 0)
+ continue;
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
+ RB_INSERT(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ }
+
+ RB_FOREACH_SAFE(pw, l2vpn_pw_head, &l2vpn->pw_inactive_tree,
+ ptmp) {
+ if (!CHECK_FLAG(pw->flags, F_PW_STATIC_NBR_ADDR)) {
+ pw->af = AF_INET;
+ pw->addr.v4 = pw->lsr_id;
+ }
+
+ if (pw->lsr_id.s_addr == INADDR_ANY || pw->pwid == 0)
+ continue;
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ RB_INSERT(l2vpn_pw_head, &l2vpn->pw_tree, pw);
+ }
+ }
+}
+
+static void
+ldp_config_reset(struct ldpd_conf *conf)
+{
+ ldp_config_reset_main(conf);
+ ldp_config_reset_l2vpns(conf);
+}
+
+static void
+ldp_config_reset_main(struct ldpd_conf *conf)
+{
+ struct iface *iface;
+ struct nbr_params *nbrp;
+
+ while (!RB_EMPTY(iface_head, &conf->iface_tree)) {
+ iface = RB_ROOT(iface_head, &conf->iface_tree);
+
+ QOBJ_UNREG(iface);
+ RB_REMOVE(iface_head, &conf->iface_tree, iface);
+ free(iface);
+ }
+
+ while (!RB_EMPTY(nbrp_head, &conf->nbrp_tree)) {
+ nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree);
+
+ QOBJ_UNREG(nbrp);
+ RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
+ free(nbrp);
+ }
+
+ conf->rtr_id.s_addr = INADDR_ANY;
+ ldp_config_reset_af(conf, AF_INET);
+ ldp_config_reset_af(conf, AF_INET6);
+ conf->lhello_holdtime = LINK_DFLT_HOLDTIME;
+ conf->lhello_interval = DEFAULT_HELLO_INTERVAL;
+ conf->thello_holdtime = TARGETED_DFLT_HOLDTIME;
+ conf->thello_interval = DEFAULT_HELLO_INTERVAL;
+ conf->wait_for_sync_interval = DFLT_WAIT_FOR_SYNC;
+ conf->trans_pref = DUAL_STACK_LDPOV6;
+ conf->flags = 0;
+}
+
+static void
+ldp_config_reset_af(struct ldpd_conf *conf, int af)
+{
+ struct ldpd_af_conf *af_conf;
+ struct iface *iface;
+ struct iface_af *ia;
+ struct tnbr *tnbr, *ttmp;
+
+ RB_FOREACH(iface, iface_head, &conf->iface_tree) {
+ ia = iface_af_get(iface, af);
+ ia->enabled = 0;
+ }
+
+ RB_FOREACH_SAFE(tnbr, tnbr_head, &conf->tnbr_tree, ttmp) {
+ if (tnbr->af != af)
+ continue;
+
+ QOBJ_UNREG(tnbr);
+ RB_REMOVE(tnbr_head, &conf->tnbr_tree, tnbr);
+ free(tnbr);
+ }
+
+ af_conf = ldp_af_conf_get(conf, af);
+ af_conf->keepalive = 180;
+ af_conf->lhello_holdtime = 0;
+ af_conf->lhello_interval = 0;
+ af_conf->thello_holdtime = 0;
+ af_conf->thello_interval = 0;
+ memset(&af_conf->trans_addr, 0, sizeof(af_conf->trans_addr));
+ af_conf->flags = 0;
+}
+
+static void
+ldp_config_reset_l2vpns(struct ldpd_conf *conf)
+{
+ struct l2vpn *l2vpn;
+ struct l2vpn_if *lif;
+ struct l2vpn_pw *pw;
+
+ while (!RB_EMPTY(l2vpn_head, &conf->l2vpn_tree)) {
+ l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree);
+ while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+ lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
+ QOBJ_UNREG(lif);
+ RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
+ free(lif);
+ }
+
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
+ QOBJ_UNREG(pw);
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
+ free(pw);
+ }
+
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
+ QOBJ_UNREG(pw);
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ free(pw);
+ }
+
+ QOBJ_UNREG(l2vpn);
+ RB_REMOVE(l2vpn_head, &conf->l2vpn_tree, l2vpn);
+ free(l2vpn);
+ }
+}
+
+void
+ldp_clear_config(struct ldpd_conf *xconf)
+{
+ struct iface *iface;
+ struct tnbr *tnbr;
+ struct nbr_params *nbrp;
+ struct l2vpn *l2vpn;
+
+ while (!RB_EMPTY(iface_head, &xconf->iface_tree)) {
+ iface = RB_ROOT(iface_head, &xconf->iface_tree);
+
+ RB_REMOVE(iface_head, &xconf->iface_tree, iface);
+ free(iface);
+ }
+
+ while (!RB_EMPTY(tnbr_head, &xconf->tnbr_tree)) {
+ tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree);
+
+ RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr);
+ free(tnbr);
+ }
+
+ while (!RB_EMPTY(nbrp_head, &xconf->nbrp_tree)) {
+ nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree);
+
+ RB_REMOVE(nbrp_head, &xconf->nbrp_tree, nbrp);
+ free(nbrp);
+ }
+
+ while (!RB_EMPTY(l2vpn_head, &xconf->l2vpn_tree)) {
+ l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree);
+
+ RB_REMOVE(l2vpn_head, &xconf->l2vpn_tree, l2vpn);
+ l2vpn_del(l2vpn);
+ }
+
+ free(xconf);
+}
+
+#define COPY(a, b) do { \
+ a = malloc(sizeof(*a)); \
+ if (a == NULL) \
+ fatal(__func__); \
+ *a = *b; \
+ } while (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);
+}
+
+static void
+merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ /* Removing global LDP config requires resetting LDP IGP Sync FSM */
+ if (CHECK_FLAG(conf->flags, F_LDPD_ENABLED)
+ && (!CHECK_FLAG(xconf->flags, F_LDPD_ENABLED)))
+ {
+ if (ldpd_process == PROC_LDP_ENGINE)
+ ldp_sync_fsm_reset_all();
+ }
+
+ /* 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_UNSPEC);
+ 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;
+ }
+
+ /*
+ * Configuration of ordered-control or independent-control
+ * requires resetting all neighborships.
+ */
+ if (CHECK_FLAG(conf->flags, F_LDPD_ORDERED_CONTROL) !=
+ CHECK_FLAG(xconf->flags, F_LDPD_ORDERED_CONTROL))
+ ldpe_reset_nbrs(AF_UNSPEC);
+
+ conf->lhello_holdtime = xconf->lhello_holdtime;
+ conf->lhello_interval = xconf->lhello_interval;
+ conf->thello_holdtime = xconf->thello_holdtime;
+ conf->thello_interval = xconf->thello_interval;
+ conf->wait_for_sync_interval = xconf->wait_for_sync_interval;
+
+ if (conf->trans_pref != xconf->trans_pref) {
+ if (ldpd_process == PROC_LDP_ENGINE)
+ ldpe_reset_ds_nbrs();
+ conf->trans_pref = xconf->trans_pref;
+ }
+
+ if (CHECK_FLAG(conf->flags, F_LDPD_DS_CISCO_INTEROP) !=
+ CHECK_FLAG(xconf->flags, F_LDPD_DS_CISCO_INTEROP)) {
+ if (ldpd_process == PROC_LDP_ENGINE)
+ ldpe_reset_ds_nbrs();
+ }
+
+ /*
+ * Configuration of allow-broken-lsp requires reprograming all
+ * labeled routes
+ */
+ if (CHECK_FLAG(conf->flags, F_LDPD_ALLOW_BROKEN_LSP) !=
+ CHECK_FLAG(xconf->flags, F_LDPD_ALLOW_BROKEN_LSP)) {
+ if (ldpd_process == PROC_LDE_ENGINE)
+ lde_allow_broken_lsp_update(xconf->flags);
+ }
+
+ if (ldpd_process == PROC_LDP_ENGINE)
+ ldpe_set_config_change_time();
+
+ conf->flags = xconf->flags;
+}
+
+static void
+merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa)
+{
+ int stop_init_backoff = 0;
+ int remove_dynamic_tnbrs = 0;
+ int change_egress_label = 0;
+ int change_host_label = 0;
+ int reset_nbrs_ipv4 = 0;
+ int reset_nbrs = 0;
+ int update_sockets = 0;
+ int change_ldp_disabled = 0;
+
+ /* update timers */
+ if (af_conf->keepalive != xa->keepalive) {
+ af_conf->keepalive = xa->keepalive;
+ stop_init_backoff = 1;
+ }
+
+ af_conf->lhello_holdtime = xa->lhello_holdtime;
+ af_conf->lhello_interval = xa->lhello_interval;
+ af_conf->thello_holdtime = xa->thello_holdtime;
+ af_conf->thello_interval = xa->thello_interval;
+
+ /* update flags */
+ if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_THELLO_ACCEPT) &&
+ !CHECK_FLAG(xa->flags, F_LDPD_AF_THELLO_ACCEPT))
+ remove_dynamic_tnbrs = 1;
+
+ if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_NO_GTSM) !=
+ CHECK_FLAG(xa->flags, F_LDPD_AF_NO_GTSM)) {
+ if (af == AF_INET6)
+ /* need to set/unset IPV6_MINHOPCOUNT */
+ update_sockets = 1;
+ else
+ /* for LDPv4 just resetting the neighbors is enough */
+ reset_nbrs_ipv4 = 1;
+ }
+ if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_EXPNULL) !=
+ CHECK_FLAG(xa->flags, F_LDPD_AF_EXPNULL))
+ change_egress_label = 1;
+
+ /* changing config of host only fec filtering */
+ if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_ALLOCHOSTONLY)
+ != CHECK_FLAG(xa->flags, F_LDPD_AF_ALLOCHOSTONLY))
+ change_host_label = 1;
+
+ /* disabling LDP for address family */
+ if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_ENABLED) &&
+ !CHECK_FLAG(xa->flags, F_LDPD_AF_ENABLED))
+ change_ldp_disabled = 1;
+
+ af_conf->flags = xa->flags;
+
+ /* update the transport address */
+ if (ldp_addrcmp(af, &af_conf->trans_addr, &xa->trans_addr)) {
+ af_conf->trans_addr = xa->trans_addr;
+ update_sockets = 1;
+ }
+
+ /* update ACLs */
+ if (strcmp(af_conf->acl_label_allocate_for, xa->acl_label_allocate_for))
+ change_host_label = 1;
+
+ if (strcmp(af_conf->acl_label_advertise_to, xa->acl_label_advertise_to) ||
+ strcmp(af_conf->acl_label_advertise_for, xa->acl_label_advertise_for) ||
+ strcmp(af_conf->acl_label_accept_from, xa->acl_label_accept_from) ||
+ strcmp(af_conf->acl_label_accept_for, xa->acl_label_accept_for))
+ reset_nbrs = 1;
+
+ if (strcmp(af_conf->acl_thello_accept_from, xa->acl_thello_accept_from))
+ remove_dynamic_tnbrs = 1;
+
+ if (strcmp(af_conf->acl_label_expnull_for, xa->acl_label_expnull_for))
+ change_egress_label = 1;
+
+ strlcpy(af_conf->acl_thello_accept_from, xa->acl_thello_accept_from,
+ sizeof(af_conf->acl_thello_accept_from));
+
+ strlcpy(af_conf->acl_label_allocate_for, xa->acl_label_allocate_for,
+ sizeof(af_conf->acl_label_allocate_for));
+
+ strlcpy(af_conf->acl_label_advertise_to, xa->acl_label_advertise_to,
+ sizeof(af_conf->acl_label_advertise_to));
+
+ strlcpy(af_conf->acl_label_advertise_for, xa->acl_label_advertise_for,
+ sizeof(af_conf->acl_label_advertise_for));
+
+ strlcpy(af_conf->acl_label_accept_from, xa->acl_label_accept_from,
+ sizeof(af_conf->acl_label_accept_from));
+
+ strlcpy(af_conf->acl_label_accept_for, xa->acl_label_accept_for,
+ sizeof(af_conf->acl_label_accept_for));
+
+ strlcpy(af_conf->acl_label_expnull_for, xa->acl_label_expnull_for,
+ sizeof(af_conf->acl_label_expnull_for));
+
+ /* apply the new configuration */
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ if (change_egress_label)
+ lde_change_egress_label(af);
+
+ if (change_host_label)
+ lde_change_allocate_filter(af);
+
+ if (change_ldp_disabled)
+ lde_route_update_release_all(af);
+
+ break;
+ case PROC_LDP_ENGINE:
+ if (stop_init_backoff)
+ ldpe_stop_init_backoff(af);
+ if (remove_dynamic_tnbrs)
+ ldpe_remove_dynamic_tnbrs(af);
+ if (reset_nbrs)
+ ldpe_reset_nbrs(AF_UNSPEC);
+ else if (reset_nbrs_ipv4)
+ ldpe_reset_nbrs(AF_INET);
+ break;
+ case PROC_MAIN:
+ if (update_sockets && iev_ldpe)
+ imsg_compose_event(iev_ldpe, IMSG_CLOSE_SOCKETS, af,
+ 0, -1, NULL, 0);
+ break;
+ }
+}
+
+static void
+merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ struct iface *iface, *itmp, *xi;
+
+ RB_FOREACH_SAFE(iface, iface_head, &conf->iface_tree, itmp) {
+ /* find deleted interfaces, which occurs when LDP is removed
+ * for all address families
+ */
+ if (if_lookup_name(xconf, iface->name) == NULL) {
+ switch (ldpd_process) {
+ case PROC_LDP_ENGINE:
+ ldpe_if_exit(iface);
+ break;
+ case PROC_LDE_ENGINE:
+ if (iface->ipv4.enabled)
+ lde_route_update_release(iface,
+ AF_INET);
+ if (iface->ipv6.enabled)
+ lde_route_update_release(iface,
+ AF_INET6);
+ break;
+ case PROC_MAIN:
+ break;
+ }
+ RB_REMOVE(iface_head, &conf->iface_tree, iface);
+ free(iface);
+ }
+ }
+ RB_FOREACH_SAFE(xi, iface_head, &xconf->iface_tree, itmp) {
+ /* find new interfaces */
+ if ((iface = if_lookup_name(conf, xi->name)) == NULL) {
+ COPY(iface, xi);
+ RB_INSERT(iface_head, &conf->iface_tree, iface);
+
+ switch (ldpd_process) {
+ case PROC_LDP_ENGINE:
+ ldpe_if_init(iface);
+ break;
+ case PROC_LDE_ENGINE:
+ break;
+ case PROC_MAIN:
+ /* resend addresses to activate new interfaces */
+ kif_redistribute(iface->name);
+ break;
+ }
+ continue;
+ }
+
+ /* update labels when adding or removing ldp on an
+ * interface
+ */
+ if (ldpd_process == PROC_LDE_ENGINE) {
+ /* if we are removing lpd config for an address
+ * family on an interface then advertise routes
+ * learned over this interface as if they were
+ * connected routes
+ */
+ if (iface->ipv4.enabled && !xi->ipv4.enabled)
+ lde_route_update_release(iface, AF_INET);
+
+ if (iface->ipv6.enabled && !xi->ipv6.enabled)
+ lde_route_update_release(iface, AF_INET6);
+
+ /* if we are adding lpd config for an address
+ * family on an interface then add proper labels
+ */
+ if (!iface->ipv4.enabled && xi->ipv4.enabled)
+ lde_route_update(iface, AF_INET);
+
+ if (!iface->ipv6.enabled && xi->ipv6.enabled)
+ lde_route_update(iface, AF_INET6);
+ }
+
+ /* update existing interfaces */
+ merge_iface_af(&iface->ipv4, &xi->ipv4);
+ merge_iface_af(&iface->ipv6, &xi->ipv6);
+ }
+}
+
+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)
+ ldp_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;
+
+ RB_FOREACH_SAFE(tnbr, tnbr_head, &conf->tnbr_tree, ttmp) {
+ if (!CHECK_FLAG(tnbr->flags, F_TNBR_CONFIGURED))
+ continue;
+
+ /* find deleted tnbrs */
+ if (tnbr_find(xconf, tnbr->af, &tnbr->addr) == NULL) {
+ switch (ldpd_process) {
+ case PROC_LDP_ENGINE:
+ UNSET_FLAG(tnbr->flags, F_TNBR_CONFIGURED);
+ tnbr_check(conf, tnbr);
+ break;
+ case PROC_LDE_ENGINE:
+ case PROC_MAIN:
+ RB_REMOVE(tnbr_head, &conf->tnbr_tree, tnbr);
+ free(tnbr);
+ break;
+ }
+ }
+ }
+ RB_FOREACH_SAFE(xt, tnbr_head, &xconf->tnbr_tree, ttmp) {
+ /* find new tnbrs */
+ if ((tnbr = tnbr_find(conf, xt->af, &xt->addr)) == NULL) {
+ COPY(tnbr, xt);
+ RB_INSERT(tnbr_head, &conf->tnbr_tree, tnbr);
+
+ switch (ldpd_process) {
+ case PROC_LDP_ENGINE:
+ tnbr_update(tnbr);
+ break;
+ case PROC_LDE_ENGINE:
+ case PROC_MAIN:
+ break;
+ }
+ continue;
+ }
+
+ /* update existing tnbrs */
+ if (!CHECK_FLAG(tnbr->flags, F_TNBR_CONFIGURED))
+ SET_FLAG(tnbr->flags, F_TNBR_CONFIGURED);
+ }
+}
+
+static void
+merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ struct nbr_params *nbrp, *ntmp, *xn;
+ struct nbr *nbr;
+ int nbrp_changed;
+
+ RB_FOREACH_SAFE(nbrp, nbrp_head, &conf->nbrp_tree, ntmp) {
+ /* find deleted nbrps */
+ if (nbr_params_find(xconf, nbrp->lsr_id) != NULL)
+ continue;
+
+ switch (ldpd_process) {
+ case PROC_LDP_ENGINE:
+ nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr);
+ if (nbr) {
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+#ifdef __OpenBSD__
+ pfkey_remove(nbr);
+#else
+ sock_set_md5sig(
+ (ldp_af_global_get(&global, nbr->af))
+ ->ldp_session_socket,
+ nbr->af, &nbr->raddr, NULL);
+#endif
+ nbr->auth.method = AUTH_NONE;
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ break;
+ case PROC_LDE_ENGINE:
+ case PROC_MAIN:
+ break;
+ }
+ RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
+ free(nbrp);
+ }
+
+ RB_FOREACH_SAFE(xn, nbrp_head, &xconf->nbrp_tree, ntmp) {
+ /* find new nbrps */
+ if ((nbrp = nbr_params_find(conf, xn->lsr_id)) == NULL) {
+ COPY(nbrp, xn);
+ RB_INSERT(nbrp_head, &conf->nbrp_tree, nbrp);
+
+ switch (ldpd_process) {
+ case PROC_LDP_ENGINE:
+ nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr);
+ if (nbr) {
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ nbr->auth.method = nbrp->auth.method;
+#ifdef __OpenBSD__
+ if (pfkey_establish(nbr, nbrp) == -1)
+ fatalx("pfkey setup failed");
+#else
+ sock_set_md5sig(
+ (ldp_af_global_get(&global,
+ nbr->af))->ldp_session_socket,
+ nbr->af, &nbr->raddr,
+ nbrp->auth.md5key);
+#endif
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ break;
+ case PROC_LDE_ENGINE:
+ case PROC_MAIN:
+ break;
+ }
+ 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);
+#ifdef __OpenBSD__
+ pfkey_remove(nbr);
+ nbr->auth.method = nbrp->auth.method;
+ if (pfkey_establish(nbr, nbrp) == -1)
+ fatalx("pfkey setup failed");
+#else
+ nbr->auth.method = nbrp->auth.method;
+ sock_set_md5sig((ldp_af_global_get(&global,
+ nbr->af))->ldp_session_socket, nbr->af,
+ &nbr->raddr, nbrp->auth.md5key);
+#endif
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ }
+ }
+}
+
+static void
+merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf)
+{
+ struct l2vpn *l2vpn, *ltmp, *xl;
+
+ RB_FOREACH_SAFE(l2vpn, l2vpn_head, &conf->l2vpn_tree, ltmp) {
+ /* find deleted l2vpns */
+ if (l2vpn_find(xconf, l2vpn->name) == NULL) {
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ l2vpn_exit(l2vpn);
+ break;
+ case PROC_LDP_ENGINE:
+ ldpe_l2vpn_exit(l2vpn);
+ break;
+ case PROC_MAIN:
+ break;
+ }
+ RB_REMOVE(l2vpn_head, &conf->l2vpn_tree, l2vpn);
+ l2vpn_del(l2vpn);
+ }
+ }
+ RB_FOREACH_SAFE(xl, l2vpn_head, &xconf->l2vpn_tree, ltmp) {
+ /* find new l2vpns */
+ if ((l2vpn = l2vpn_find(conf, xl->name)) == NULL) {
+ COPY(l2vpn, xl);
+ RB_INSERT(l2vpn_head, &conf->l2vpn_tree, l2vpn);
+ RB_INIT(l2vpn_if_head, &l2vpn->if_tree);
+ RB_INIT(l2vpn_pw_head, &l2vpn->pw_tree);
+ RB_INIT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ l2vpn_init(l2vpn);
+ break;
+ case PROC_LDP_ENGINE:
+ ldpe_l2vpn_init(l2vpn);
+ break;
+ case PROC_MAIN:
+ break;
+ }
+ }
+
+ /* update existing l2vpns */
+ merge_l2vpn(conf, l2vpn, 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 interfaces */
+ RB_FOREACH_SAFE(lif, l2vpn_if_head, &l2vpn->if_tree, ftmp) {
+ /* find deleted interfaces */
+ if (l2vpn_if_find(xl, lif->ifname) == NULL) {
+ RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
+ free(lif);
+ }
+ }
+ RB_FOREACH_SAFE(xf, l2vpn_if_head, &xl->if_tree, ftmp) {
+ /* find new interfaces */
+ if (l2vpn_if_find(l2vpn, xf->ifname) == NULL) {
+ COPY(lif, xf);
+ RB_INSERT(l2vpn_if_head, &l2vpn->if_tree, lif);
+ lif->l2vpn = l2vpn;
+
+ switch (ldpd_process) {
+ case PROC_LDP_ENGINE:
+ case PROC_LDE_ENGINE:
+ break;
+ case PROC_MAIN:
+ kif_redistribute(lif->ifname);
+ break;
+ }
+ }
+ }
+
+ /* merge active pseudowires */
+ RB_FOREACH_SAFE(pw, l2vpn_pw_head, &l2vpn->pw_tree, ptmp) {
+ /* find deleted active pseudowires */
+ if (l2vpn_pw_find_active(xl, pw->ifname) == 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;
+ }
+
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
+ free(pw);
+ }
+ }
+ RB_FOREACH_SAFE(xp, l2vpn_pw_head, &xl->pw_tree, ptmp) {
+ /* find new active pseudowires */
+ if ((pw = l2vpn_pw_find_active(l2vpn, xp->ifname)) == NULL) {
+ COPY(pw, xp);
+ RB_INSERT(l2vpn_pw_head, &l2vpn->pw_tree, pw);
+ pw->l2vpn = l2vpn;
+
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ l2vpn_pw_init(pw);
+ break;
+ case PROC_LDP_ENGINE:
+ ldpe_l2vpn_pw_init(pw);
+ break;
+ case PROC_MAIN:
+ kif_redistribute(pw->ifname);
+ break;
+ }
+ continue;
+ }
+
+ /* update existing active 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 (CHECK_FLAG(pw->flags, (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF)) !=
+ CHECK_FLAG(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 && 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 (CHECK_FLAG(xp->flags, F_PW_CWORD_CONF))
+ SET_FLAG(pw->flags, F_PW_CWORD_CONF);
+ else
+ UNSET_FLAG(pw->flags, F_PW_CWORD_CONF);
+
+ if (CHECK_FLAG(xp->flags, F_PW_STATUSTLV_CONF))
+ SET_FLAG(pw->flags, F_PW_STATUSTLV_CONF);
+ else
+ UNSET_FLAG(pw->flags, F_PW_STATUSTLV_CONF);
+
+ if (CHECK_FLAG(xp->flags, F_PW_STATIC_NBR_ADDR))
+ SET_FLAG(pw->flags, F_PW_STATIC_NBR_ADDR);
+ else
+ UNSET_FLAG(pw->flags, F_PW_STATIC_NBR_ADDR);
+
+ if (ldpd_process == PROC_LDP_ENGINE && reinstall_tnbr)
+ ldpe_l2vpn_pw_init(pw);
+
+ if (ldpd_process == PROC_LDE_ENGINE && 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;
+ }
+ }
+
+ /* merge inactive pseudowires */
+ RB_FOREACH_SAFE(pw, l2vpn_pw_head, &l2vpn->pw_inactive_tree, ptmp) {
+ /* find deleted inactive pseudowires */
+ if (l2vpn_pw_find_inactive(xl, pw->ifname) == NULL) {
+ RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ free(pw);
+ }
+ }
+ RB_FOREACH_SAFE(xp, l2vpn_pw_head, &xl->pw_inactive_tree, ptmp) {
+ /* find new inactive pseudowires */
+ if ((pw = l2vpn_pw_find_inactive(l2vpn, xp->ifname)) == NULL) {
+ COPY(pw, xp);
+ RB_INSERT(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
+ pw->l2vpn = l2vpn;
+
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ case PROC_LDP_ENGINE:
+ break;
+ case PROC_MAIN:
+ kif_redistribute(pw->ifname);
+ break;
+ }
+ continue;
+ }
+
+ /* update existing inactive pseudowire */
+ pw->lsr_id.s_addr = xp->lsr_id.s_addr;
+ pw->af = xp->af;
+ pw->addr = xp->addr;
+ pw->pwid = xp->pwid;
+ strlcpy(pw->ifname, xp->ifname, sizeof(pw->ifname));
+ pw->ifindex = xp->ifindex;
+ pw->flags = xp->flags;
+ }
+
+ 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);
+
+ RB_INIT(iface_head, &xconf->iface_tree);
+ RB_INIT(tnbr_head, &xconf->tnbr_tree);
+ RB_INIT(nbrp_head, &xconf->nbrp_tree);
+ RB_INIT(l2vpn_head, &xconf->l2vpn_tree);
+
+ /* set default values */
+ ldp_config_reset(xconf);
+
+ 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(xconf);
+ free(conf);
+}
diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h
new file mode 100644
index 0000000..1fec5be
--- /dev/null
+++ b/ldpd/ldpd.h
@@ -0,0 +1,911 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#ifndef _LDPD_H_
+#define _LDPD_H_
+
+#include "queue.h"
+#include "openbsd-tree.h"
+#include "imsg.h"
+#include "frrevent.h"
+#include "qobj.h"
+#include "prefix.h"
+#include "filter.h"
+#include "vty.h"
+#include "pw.h"
+#include "zclient.h"
+
+#include "ldp.h"
+#include "lib/ldp_sync.h"
+
+#define CONF_FILE "/etc/ldpd.conf"
+#define LDPD_USER "_ldpd"
+
+#define LDPD_FD_ASYNC 3
+#define LDPD_FD_SYNC 4
+
+#define LDPD_OPT_VERBOSE 0x00000001
+#define LDPD_OPT_VERBOSE2 0x00000002
+#define LDPD_OPT_NOACTION 0x00000004
+
+#define TCP_MD5_KEY_LEN 80
+
+#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;
+ void (*handler)(struct event *);
+ void *arg;
+};
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler_write)(struct event *);
+ struct event *ev_write;
+ void (*handler_read)(struct event *);
+ struct event *ev_read;
+};
+
+enum imsg_type {
+ IMSG_NONE,
+ IMSG_CTL_RELOAD,
+ IMSG_CTL_SHOW_INTERFACE,
+ IMSG_CTL_SHOW_DISCOVERY,
+ IMSG_CTL_SHOW_DISCOVERY_DTL,
+ IMSG_CTL_SHOW_DISC_IFACE,
+ IMSG_CTL_SHOW_DISC_TNBR,
+ IMSG_CTL_SHOW_DISC_ADJ,
+ IMSG_CTL_SHOW_NBR,
+ IMSG_CTL_SHOW_NBR_DISC,
+ IMSG_CTL_SHOW_NBR_END,
+ IMSG_CTL_SHOW_LIB,
+ IMSG_CTL_SHOW_LIB_BEGIN,
+ IMSG_CTL_SHOW_LIB_SENT,
+ IMSG_CTL_SHOW_LIB_RCVD,
+ IMSG_CTL_SHOW_LIB_END,
+ IMSG_CTL_SHOW_L2VPN_PW,
+ IMSG_CTL_SHOW_L2VPN_BINDING,
+ IMSG_CTL_SHOW_LDP_SYNC,
+ 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_KPW_ADD,
+ IMSG_KPW_DELETE,
+ IMSG_KPW_SET,
+ IMSG_KPW_UNSET,
+ IMSG_IFSTATUS,
+ IMSG_NEWADDR,
+ IMSG_DELADDR,
+ IMSG_RTRID_UPDATE,
+ 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_UPDATE,
+ 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_L2VPN_IPW,
+ IMSG_RECONF_END,
+ IMSG_DEBUG_UPDATE,
+ IMSG_LOG,
+ IMSG_ACL_CHECK,
+ IMSG_INIT,
+ IMSG_PW_UPDATE,
+ IMSG_FILTER_UPDATE,
+ IMSG_NBR_SHUTDOWN,
+ IMSG_LDP_SYNC_IF_STATE_REQUEST,
+ IMSG_LDP_SYNC_IF_STATE_UPDATE,
+ IMSG_RLFA_REG,
+ IMSG_RLFA_UNREG_ALL,
+ IMSG_RLFA_LABELS,
+ IMSG_AGENTX_ENABLED,
+};
+
+struct ldpd_init {
+ char user[256];
+ char group[256];
+ char ctl_sock_path[MAXPATHLEN];
+ char zclient_serv_path[MAXPATHLEN];
+ unsigned short instance;
+};
+
+struct ldp_access {
+ char name[ACL_NAMSIZ];
+};
+
+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
+};
+
+/* LDP IGP Sync states */
+#define LDP_SYNC_STA_UNKNOWN 0x0000
+#define LDP_SYNC_STA_NOT_ACH 0x0001
+#define LDP_SYNC_STA_ACH 0x0002
+
+/* LDP IGP Sync events */
+enum ldp_sync_event {
+ LDP_SYNC_EVT_NOTHING,
+ LDP_SYNC_EVT_LDP_SYNC_START,
+ LDP_SYNC_EVT_LDP_SYNC_COMPLETE,
+ LDP_SYNC_EVT_CONFIG_LDP_OFF,
+ LDP_SYNC_EVT_ADJ_DEL,
+ LDP_SYNC_EVT_ADJ_NEW,
+ LDP_SYNC_EVT_SESSION_CLOSE,
+ LDP_SYNC_EVT_CONFIG_LDP_ON,
+ LDP_SYNC_EVT_IFACE_SHUTDOWN
+};
+
+/* LDP IGP Sync actions */
+enum ldp_sync_action {
+ LDP_SYNC_ACT_NOTHING,
+ LDP_SYNC_ACT_IFACE_START_SYNC,
+ LDP_SYNC_ACT_LDP_START_SYNC,
+ LDP_SYNC_ACT_LDP_COMPLETE_SYNC,
+ LDP_SYNC_ACT_CONFIG_LDP_OFF,
+ LDP_SYNC_ACT_IFACE_SHUTDOWN
+};
+
+/* forward declarations */
+RB_HEAD(global_adj_head, adj);
+RB_HEAD(nbr_adj_head, adj);
+RB_HEAD(ia_adj_head, adj);
+
+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;
+ struct {
+ uint8_t type;
+ union {
+ uint16_t prefix_af;
+ uint16_t pw_type;
+ } u;
+ } twcard;
+ } 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;
+ struct {
+ uint16_t type;
+ uint16_t length;
+ char *data;
+ } rtlvs;
+ uint8_t flags;
+};
+#define F_NOTIF_PW_STATUS 0x01 /* pseudowire status tlv present */
+#define F_NOTIF_FEC 0x02 /* fec tlv present */
+#define F_NOTIF_RETURNED_TLVS 0x04 /* returned tlvs 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;
+ struct ia_adj_head adj_tree;
+ time_t uptime;
+ struct event *hello_timer;
+ uint16_t hello_holdtime;
+ uint16_t hello_interval;
+};
+
+struct iface_ldp_sync {
+ int state;
+ struct event *wait_for_sync_timer;
+};
+
+struct iface {
+ RB_ENTRY(iface) entry;
+ char name[INTERFACE_NAMSIZ];
+ ifindex_t ifindex;
+ struct if_addr_head addr_list;
+ struct in6_addr linklocal;
+ enum iface_type type;
+ int operative;
+ struct iface_af ipv4;
+ struct iface_af ipv6;
+ struct iface_ldp_sync ldp_sync;
+ QOBJ_FIELDS;
+};
+RB_HEAD(iface_head, iface);
+RB_PROTOTYPE(iface_head, iface, entry, iface_compare);
+DECLARE_QOBJ_TYPE(iface);
+
+/* source of targeted hellos */
+struct tnbr {
+ RB_ENTRY(tnbr) entry;
+ struct event *hello_timer;
+ struct adj *adj;
+ int af;
+ union ldpd_addr addr;
+ int state;
+ uint16_t pw_count;
+ uint32_t rlfa_count;
+ uint8_t flags;
+ QOBJ_FIELDS;
+};
+RB_HEAD(tnbr_head, tnbr);
+RB_PROTOTYPE(tnbr_head, tnbr, entry, tnbr_compare);
+DECLARE_QOBJ_TYPE(tnbr);
+#define F_TNBR_CONFIGURED 0x01
+#define F_TNBR_DYNAMIC 0x02
+
+enum auth_method {
+ AUTH_NONE,
+ AUTH_MD5SIG
+};
+
+/* neighbor specific parameters */
+struct nbr_params {
+ RB_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;
+ QOBJ_FIELDS;
+};
+RB_HEAD(nbrp_head, nbr_params);
+RB_PROTOTYPE(nbrp_head, nbr_params, entry, nbr_params_compare);
+DECLARE_QOBJ_TYPE(nbr_params);
+#define F_NBRP_KEEPALIVE 0x01
+#define F_NBRP_GTSM 0x02
+#define F_NBRP_GTSM_HOPS 0x04
+
+struct ldp_stats {
+ uint32_t kalive_sent;
+ uint32_t kalive_rcvd;
+ uint32_t addr_sent;
+ uint32_t addr_rcvd;
+ uint32_t addrwdraw_sent;
+ uint32_t addrwdraw_rcvd;
+ uint32_t notif_sent;
+ uint32_t notif_rcvd;
+ uint32_t capability_sent;
+ uint32_t capability_rcvd;
+ uint32_t labelmap_sent;
+ uint32_t labelmap_rcvd;
+ uint32_t labelreq_sent;
+ uint32_t labelreq_rcvd;
+ uint32_t labelwdraw_sent;
+ uint32_t labelwdraw_rcvd;
+ uint32_t labelrel_sent;
+ uint32_t labelrel_rcvd;
+ uint32_t labelabreq_sent;
+ uint32_t labelabreq_rcvd;
+ uint32_t unknown_tlv;
+ uint32_t unknown_msg;
+
+};
+
+struct ldp_entity_stats {
+ uint32_t session_attempts;
+ uint32_t session_rejects_hello;
+ uint32_t session_rejects_ad;
+ uint32_t session_rejects_max_pdu;
+ uint32_t session_rejects_lr;
+ uint32_t bad_ldp_id;
+ uint32_t bad_pdu_len;
+ uint32_t bad_msg_len;
+ uint32_t bad_tlv_len;
+ uint32_t malformed_tlv;
+ uint32_t keepalive_timer_exp;
+ uint32_t shutdown_rcv_notify;
+ uint32_t shutdown_send_notify;
+};
+
+struct l2vpn_if {
+ RB_ENTRY(l2vpn_if) entry;
+ struct l2vpn *l2vpn;
+ char ifname[INTERFACE_NAMSIZ];
+ ifindex_t ifindex;
+ int operative;
+ uint8_t mac[ETH_ALEN];
+ QOBJ_FIELDS;
+};
+RB_HEAD(l2vpn_if_head, l2vpn_if);
+RB_PROTOTYPE(l2vpn_if_head, l2vpn_if, entry, l2vpn_if_compare);
+DECLARE_QOBJ_TYPE(l2vpn_if);
+
+struct l2vpn_pw {
+ RB_ENTRY(l2vpn_pw) entry;
+ struct l2vpn *l2vpn;
+ struct in_addr lsr_id;
+ int af;
+ union ldpd_addr addr;
+ uint32_t pwid;
+ char ifname[INTERFACE_NAMSIZ];
+ ifindex_t ifindex;
+ bool enabled;
+ uint32_t remote_group;
+ uint16_t remote_mtu;
+ uint32_t local_status;
+ uint32_t remote_status;
+ uint8_t flags;
+ uint8_t reason;
+ QOBJ_FIELDS;
+};
+RB_HEAD(l2vpn_pw_head, l2vpn_pw);
+RB_PROTOTYPE(l2vpn_pw_head, l2vpn_pw, entry, l2vpn_pw_compare);
+DECLARE_QOBJ_TYPE(l2vpn_pw);
+#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_STATIC_NBR_ADDR 0x10 /* static neighbor address configured */
+
+#define F_PW_NO_ERR 0x00 /* no error reported */
+#define F_PW_LOCAL_NOT_FWD 0x01 /* locally can't forward over PW */
+#define F_PW_REMOTE_NOT_FWD 0x02 /* remote end of PW reported fwd error*/
+#define F_PW_NO_REMOTE_LABEL 0x03 /* have not recvd label from peer */
+#define F_PW_MTU_MISMATCH 0x04 /* mtu mismatch between peers */
+
+struct l2vpn {
+ RB_ENTRY(l2vpn) entry;
+ char name[L2VPN_NAME_LEN];
+ int type;
+ int pw_type;
+ int mtu;
+ char br_ifname[INTERFACE_NAMSIZ];
+ ifindex_t br_ifindex;
+ struct l2vpn_if_head if_tree;
+ struct l2vpn_pw_head pw_tree;
+ struct l2vpn_pw_head pw_inactive_tree;
+ QOBJ_FIELDS;
+};
+RB_HEAD(l2vpn_head, l2vpn);
+RB_PROTOTYPE(l2vpn_head, l2vpn, entry, l2vpn_compare);
+DECLARE_QOBJ_TYPE(l2vpn);
+#define L2VPN_TYPE_VPWS 1
+#define L2VPN_TYPE_VPLS 2
+
+/* ldp_conf */
+extern enum ldpd_process {
+ PROC_MAIN,
+ PROC_LDP_ENGINE,
+ PROC_LDE_ENGINE
+} ldpd_process;
+
+static const char * const log_procnames[] = {
+ "parent",
+ "ldpe",
+ "lde"
+};
+
+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 lhello_holdtime;
+ uint16_t lhello_interval;
+ uint16_t thello_holdtime;
+ uint16_t thello_interval;
+ union ldpd_addr trans_addr;
+ char acl_thello_accept_from[ACL_NAMSIZ];
+ char acl_label_allocate_for[ACL_NAMSIZ];
+ char acl_label_advertise_to[ACL_NAMSIZ];
+ char acl_label_advertise_for[ACL_NAMSIZ];
+ char acl_label_expnull_for[ACL_NAMSIZ];
+ char acl_label_accept_from[ACL_NAMSIZ];
+ char acl_label_accept_for[ACL_NAMSIZ];
+ 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
+#define F_LDPD_AF_ALLOCHOSTONLY 0x0010
+
+struct ldpd_conf {
+ struct in_addr rtr_id;
+ struct ldpd_af_conf ipv4;
+ struct ldpd_af_conf ipv6;
+ struct iface_head iface_tree;
+ struct tnbr_head tnbr_tree;
+ struct nbrp_head nbrp_tree;
+ struct l2vpn_head l2vpn_tree;
+ uint16_t lhello_holdtime;
+ uint16_t lhello_interval;
+ uint16_t thello_holdtime;
+ uint16_t thello_interval;
+ uint16_t trans_pref;
+ uint16_t wait_for_sync_interval;
+ int flags;
+ time_t config_change_time;
+ struct ldp_entity_stats stats;
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(ldpd_conf);
+#define F_LDPD_NO_FIB_UPDATE 0x0001
+#define F_LDPD_DS_CISCO_INTEROP 0x0002
+#define F_LDPD_ENABLED 0x0004
+#define F_LDPD_ORDERED_CONTROL 0x0008
+#define F_LDPD_ALLOW_BROKEN_LSP 0x0010
+
+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;
+ struct in_addr rtr_id;
+ struct ldpd_af_global ipv4;
+ struct ldpd_af_global ipv6;
+ uint32_t conf_seqnum;
+ int pfkeysock;
+ struct if_addr_head addr_list;
+ struct global_adj_head adj_tree;
+ 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;
+ ifindex_t ifindex;
+ uint8_t route_type;
+ uint8_t route_instance;
+ uint16_t flags;
+};
+
+struct kaddr {
+ char ifname[INTERFACE_NAMSIZ];
+ ifindex_t ifindex;
+ int af;
+ union ldpd_addr addr;
+ uint8_t prefixlen;
+ union ldpd_addr dstbrd;
+};
+
+struct kif {
+ char ifname[INTERFACE_NAMSIZ];
+ ifindex_t ifindex;
+ int flags;
+ int operative;
+ uint8_t mac[ETH_ALEN];
+ int mtu;
+};
+
+struct acl_check {
+ char acl[ACL_NAMSIZ];
+ int af;
+ union ldpd_addr addr;
+ uint8_t prefixlen;
+};
+
+/* control data structures */
+struct ctl_iface {
+ int af;
+ char name[INTERFACE_NAMSIZ];
+ ifindex_t ifindex;
+ int state;
+ enum iface_type type;
+ uint16_t hello_holdtime;
+ uint16_t hello_interval;
+ time_t uptime;
+ uint16_t adj_cnt;
+};
+
+struct ctl_disc_if {
+ char name[INTERFACE_NAMSIZ];
+ int active_v4;
+ int active_v6;
+ int no_adj;
+};
+
+struct ctl_disc_tnbr {
+ int af;
+ union ldpd_addr addr;
+ int no_adj;
+};
+
+struct ctl_adj {
+ int af;
+ struct in_addr id;
+ enum hello_type type;
+ char ifname[INTERFACE_NAMSIZ];
+ union ldpd_addr src_addr;
+ uint16_t holdtime;
+ uint16_t holdtime_remaining;
+ union ldpd_addr trans_addr;
+ int ds_tlv;
+};
+
+struct ctl_nbr {
+ int af;
+ struct in_addr id;
+ union ldpd_addr laddr;
+ in_port_t lport;
+ union ldpd_addr raddr;
+ in_port_t rport;
+ enum auth_method auth_method;
+ uint16_t holdtime;
+ time_t uptime;
+ int nbr_state;
+ struct ldp_stats stats;
+ int flags;
+ uint16_t max_pdu_len;
+ uint16_t hold_time_remaining;
+};
+
+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;
+ int no_downstream;
+};
+
+struct ctl_pw {
+ uint16_t type;
+ char l2vpn_name[L2VPN_NAME_LEN];
+ char ifname[INTERFACE_NAMSIZ];
+ uint32_t pwid;
+ struct in_addr lsr_id;
+ uint32_t local_label;
+ uint32_t local_gid;
+ uint16_t local_ifmtu;
+ uint8_t local_cword;
+ uint32_t remote_label;
+ uint32_t remote_gid;
+ uint16_t remote_ifmtu;
+ uint8_t remote_cword;
+ uint32_t status;
+ uint8_t reason;
+};
+
+struct ctl_ldp_sync {
+ char name[INTERFACE_NAMSIZ];
+ ifindex_t ifindex;
+ bool in_sync;
+ bool timer_running;
+ uint16_t wait_time;
+ uint16_t wait_time_remaining;
+ struct in_addr peer_ldp_id;
+};
+
+extern struct ldpd_conf *ldpd_conf, *vty_conf;
+extern struct ldpd_global global;
+extern struct ldpd_init init;
+
+/* parse.y */
+struct ldpd_conf *parse_config(char *);
+int cmdline_symset(char *);
+
+/* kroute.c */
+void pw2zpw(struct l2vpn_pw *, struct zapi_pw *);
+void kif_redistribute(const char *);
+int kr_change(struct kroute *);
+int kr_delete(struct kroute *);
+int kmpw_add(struct zapi_pw *);
+int kmpw_del(struct zapi_pw *);
+int kmpw_set(struct zapi_pw *);
+int kmpw_unset(struct zapi_pw *);
+
+/* 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 *);
+void addr2sa(int af, const union ldpd_addr *, uint16_t,
+ union sockunion *su);
+void sa2addr(struct sockaddr *, int *, union ldpd_addr *,
+ in_port_t *);
+socklen_t sockaddr_len(struct sockaddr *);
+
+/* ldpd.c */
+void ldp_write_handler(struct event *thread);
+void main_imsg_compose_ldpe(int, pid_t, void *, uint16_t);
+void main_imsg_compose_lde(int, pid_t, void *, uint16_t);
+int main_imsg_compose_both(enum imsg_type, 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 (*)(struct event *), void *);
+void evbuf_clear(struct evbuf *);
+int ldp_acl_request(struct imsgev *, char *, int,
+ union ldpd_addr *, uint8_t);
+void ldp_acl_reply(struct imsgev *, struct acl_check *);
+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 *);
+in_addr_t ldp_rtr_id_get(struct ldpd_conf *);
+int ldp_config_apply(struct vty *, struct ldpd_conf *);
+void ldp_clear_config(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 *);
+
+/* ldp_vty_conf.c */
+/* NOTE: the parameters' names should be preserved because of codegen */
+struct iface *iface_new_api(struct ldpd_conf *conf,
+ const char *name);
+void iface_del_api(struct ldpd_conf *conf,
+ struct iface *iface);
+struct tnbr *tnbr_new_api(struct ldpd_conf *conf, int af,
+ union ldpd_addr *addr);
+void tnbr_del_api(struct ldpd_conf *conf, struct tnbr *tnbr);
+struct nbr_params *nbrp_new_api(struct ldpd_conf *conf,
+ struct in_addr lsr_id);
+void nbrp_del_api(struct ldpd_conf *conf,
+ struct nbr_params *nbrp);
+struct l2vpn *l2vpn_new_api(struct ldpd_conf *conf, const char *name);
+void l2vpn_del_api(struct ldpd_conf *conf,
+ struct l2vpn *l2vpn);
+struct l2vpn_if *l2vpn_if_new_api(struct ldpd_conf *conf,
+ struct l2vpn *l2vpn, const char *ifname);
+void l2vpn_if_del_api(struct l2vpn *l2vpn,
+ struct l2vpn_if *lif);
+struct l2vpn_pw *l2vpn_pw_new_api(struct ldpd_conf *conf,
+ struct l2vpn *l2vpn, const char *ifname);
+void l2vpn_pw_del_api(struct l2vpn *l2vpn,
+ struct l2vpn_pw *pw);
+
+/* socket.c */
+int ldp_create_socket(int, enum socket_type);
+void sock_set_nonblock(int);
+void sock_set_cloexec(int);
+void sock_set_recvbuf(int);
+int sock_set_reuse(int, int);
+int sock_set_bindany(int, int);
+int sock_set_md5sig(int, int, union ldpd_addr *, const char *);
+int sock_set_ipv4_tos(int, int);
+int sock_set_ipv4_pktinfo(int, int);
+int sock_set_ipv4_recvdstaddr(int fd, ifindex_t ifindex);
+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);
+
+/* logmsg.h */
+struct in6_addr;
+union ldpd_addr;
+struct hello_source;
+struct fec;
+
+const char *log_sockaddr(void *);
+const char *log_in6addr(const struct in6_addr *);
+const char *log_in6addr_scope(const struct in6_addr *addr,
+ ifindex_t ifidx);
+const char *log_addr(int, const union ldpd_addr *);
+char *log_label(uint32_t);
+const char *log_time(time_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);
+const char *pw_error_code(uint8_t);
+
+/* quagga */
+extern struct event_loop *master;
+extern char ctl_sock_path[MAXPATHLEN];
+
+/* ldp_zebra.c */
+void ldp_zebra_init(struct event_loop *m);
+void ldp_zebra_destroy(void);
+int ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *);
+int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *
+ rlfa_labels);
+
+void ldp_zebra_regdereg_zebra_info(bool want_register);
+
+/* compatibility */
+#ifndef __OpenBSD__
+#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f)
+#define __IPV6_ADDR_SCOPE_INTFACELOCAL 0x01
+#define IN6_IS_ADDR_MC_INTFACELOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_INTFACELOCAL))
+#endif
+
+DECLARE_HOOK(ldp_register_mib, (struct event_loop * tm), (tm));
+
+extern void ldp_agentx_enabled(void);
+
+#endif /* _LDPD_H_ */
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
new file mode 100644
index 0000000..e66b9e9
--- /dev/null
+++ b/ldpd/ldpe.c
@@ -0,0 +1,1044 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "control.h"
+#include "log.h"
+#include "ldp_debug.h"
+#include "rlfa.h"
+
+#include <lib/log.h>
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "libfrr.h"
+
+static void ldpe_shutdown(void);
+static void ldpe_dispatch_main(struct event *thread);
+static void ldpe_dispatch_lde(struct event *thread);
+#ifdef __OpenBSD__
+static void ldpe_dispatch_pfkey(struct event *thread);
+#endif
+static void ldpe_setup_sockets(int, int, int, int);
+static void ldpe_close_sockets(int);
+static void ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t ifidx);
+static void ldpe_check_filter_af(int, struct ldpd_af_conf *, const char *);
+
+struct ldpd_conf *leconf;
+#ifdef __OpenBSD__
+struct ldpd_sysdep sysdep;
+#endif
+
+static struct imsgev iev_main_data;
+static struct imsgev *iev_main, *iev_main_sync;
+static struct imsgev *iev_lde;
+#ifdef __OpenBSD__
+static struct event *pfkey_ev;
+#endif
+
+/* ldpe privileges */
+static zebra_capabilities_t _caps_p [] =
+{
+ ZCAP_BIND,
+ ZCAP_NET_ADMIN
+};
+
+struct zebra_privs_t ldpe_privs =
+{
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0
+};
+
+/* SIGINT / SIGTERM handler. */
+static void
+sigint(void)
+{
+ ldpe_shutdown();
+}
+
+static struct frr_signal_t ldpe_signals[] =
+{
+ {
+ .signal = SIGHUP,
+ /* ignore */
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+char *pkt_ptr; /* packet buffer */
+
+/* label distribution protocol engine */
+void
+ldpe(void)
+{
+#ifdef HAVE_SETPROCTITLE
+ setproctitle("ldp engine");
+#endif
+ ldpd_process = PROC_LDP_ENGINE;
+ log_procname = log_procnames[ldpd_process];
+
+ master = frr_init();
+ /* no frr_config_fork() here, allow frr_pthread to create threads */
+ frr_is_after_fork = true;
+
+ /* setup signal handler */
+ signal_init(master, array_size(ldpe_signals), ldpe_signals);
+
+ /* setup pipes and event handlers to the parent process */
+ if ((iev_main = calloc(1, sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC);
+ iev_main->handler_read = ldpe_dispatch_main;
+ event_add_read(master, iev_main->handler_read, iev_main,
+ iev_main->ibuf.fd, &iev_main->ev_read);
+ iev_main->handler_write = ldp_write_handler;
+
+ memset(&iev_main_data, 0, sizeof(iev_main_data));
+ iev_main_sync = &iev_main_data;
+ imsg_init(&iev_main_sync->ibuf, LDPD_FD_SYNC);
+
+ /* create base configuration */
+ leconf = config_new_empty();
+
+ struct event thread;
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+
+ /* NOTREACHED */
+ return;
+}
+
+void
+ldpe_init(struct ldpd_init *init)
+{
+#ifdef __OpenBSD__
+ /* This socket must be open before dropping privileges. */
+ global.pfkeysock = pfkey_init();
+ if (sysdep.no_pfkey == 0) {
+ event_add_read(master, ldpe_dispatch_pfkey, NULL,
+ global.pfkeysock, &pfkey_ev);
+ }
+#endif
+
+ /* drop privileges */
+ ldpe_privs.user = init->user;
+ ldpe_privs.group = init->group;
+ zprivs_preinit(&ldpe_privs);
+ zprivs_init(&ldpe_privs);
+
+ /* listen on ldpd control socket */
+ strlcpy(ctl_sock_path, init->ctl_sock_path, sizeof(ctl_sock_path));
+ if (control_init(ctl_sock_path) == -1)
+ fatalx("control socket setup failed");
+ TAILQ_INIT(&ctl_conns);
+ control_listen();
+
+ LIST_INIT(&global.addr_list);
+ RB_INIT(global_adj_head, &global.adj_tree);
+ TAILQ_INIT(&global.pending_conns);
+ if (inet_pton(AF_INET, AllRouters_v4, &global.mcast_addr_v4) != 1)
+ fatal("inet_pton");
+ if (inet_pton(AF_INET6, AllRouters_v6, &global.mcast_addr_v6) != 1)
+ fatal("inet_pton");
+
+ /* mark sockets as closed */
+ global.ipv4.ldp_disc_socket = -1;
+ global.ipv4.ldp_edisc_socket = -1;
+ global.ipv4.ldp_session_socket = -1;
+ global.ipv6.ldp_disc_socket = -1;
+ global.ipv6.ldp_edisc_socket = -1;
+ global.ipv6.ldp_session_socket = -1;
+
+ if ((pkt_ptr = calloc(1, IBUF_READ_SIZE)) == NULL)
+ fatal(__func__);
+
+ accept_init();
+}
+
+static void
+ldpe_shutdown(void)
+{
+ struct if_addr *if_addr;
+ struct adj *adj;
+
+ /* close pipes */
+ if (iev_lde) {
+ msgbuf_clear(&iev_lde->ibuf.w);
+ close(iev_lde->ibuf.fd);
+ iev_lde->ibuf.fd = -1;
+ }
+ msgbuf_clear(&iev_main->ibuf.w);
+ close(iev_main->ibuf.fd);
+ iev_main->ibuf.fd = -1;
+ msgbuf_clear(&iev_main_sync->ibuf.w);
+ close(iev_main_sync->ibuf.fd);
+ iev_main_sync->ibuf.fd = -1;
+
+ control_cleanup(ctl_sock_path);
+
+#ifdef __OpenBSD__
+ if (sysdep.no_pfkey == 0) {
+ EVENT_OFF(pfkey_ev);
+ close(global.pfkeysock);
+ }
+#endif
+ ldpe_close_sockets(AF_INET);
+ ldpe_close_sockets(AF_INET6);
+
+ /* remove addresses from global list */
+ while ((if_addr = LIST_FIRST(&global.addr_list)) != NULL) {
+ LIST_REMOVE(if_addr, entry);
+ assert(if_addr != LIST_FIRST(&global.addr_list));
+ free(if_addr);
+ }
+ while (!RB_EMPTY(global_adj_head, &global.adj_tree)) {
+ adj = RB_ROOT(global_adj_head, &global.adj_tree);
+
+ adj_del(adj, S_SHUTDOWN);
+ }
+
+ config_clear(leconf);
+ /* clean up */
+ if (iev_lde)
+ free(iev_lde);
+ free(iev_main);
+ free(pkt_ptr);
+
+ log_info("ldp engine exiting");
+
+ zlog_fini();
+
+ exit(0);
+}
+
+/* imesg */
+int
+ldpe_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ if (iev_main->ibuf.fd == -1)
+ return (0);
+ return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen));
+}
+
+void
+ldpe_imsg_compose_parent_sync(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ if (iev_main_sync->ibuf.fd == -1)
+ return;
+ imsg_compose_event(iev_main_sync, type, 0, pid, -1, data, datalen);
+ imsg_flush(&iev_main_sync->ibuf);
+}
+
+int
+ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data,
+ uint16_t datalen)
+{
+ if (iev_lde->ibuf.fd == -1)
+ return (0);
+ return (imsg_compose_event(iev_lde, type, peerid, pid, -1, data, datalen));
+}
+
+/* ARGSUSED */
+static void ldpe_dispatch_main(struct event *thread)
+{
+ static struct ldpd_conf *nconf;
+ struct iface *niface;
+ struct tnbr *ntnbr;
+ struct nbr_params *nnbrp;
+ static struct l2vpn *l2vpn, *nl2vpn;
+ struct l2vpn_if *lif, *nlif;
+ struct l2vpn_pw *pw, *npw;
+ struct imsg imsg;
+ int fd;
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct iface *iface = NULL;
+ struct kif *kif;
+ int af;
+ enum socket_type *socket_type;
+ static int disc_socket = -1;
+ static int edisc_socket = -1;
+ static int session_socket = -1;
+ struct nbr *nbr;
+#ifdef __OpenBSD__
+ struct nbr_params *nbrp;
+#endif
+ int n, shut = 0;
+ struct ldp_access *laccess;
+ struct ldp_igp_sync_if_state_req *ldp_sync_if_state_req;
+ struct ldp_rlfa_node *rnode, *rntmp;
+ struct ldp_rlfa_client *rclient;
+ struct zapi_rlfa_request *rlfa_req;
+ struct zapi_rlfa_igp *rlfa_igp;
+
+ iev->ev_read = NULL;
+
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("ldpe_dispatch_main: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_IFSTATUS:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kif))
+ fatalx("IFSTATUS imsg with wrong len");
+ kif = imsg.data;
+
+ iface = if_lookup_name(leconf, kif->ifname);
+ if (iface) {
+ if_update_info(iface, kif);
+ ldp_if_update(iface, AF_UNSPEC);
+ break;
+ }
+
+ RB_FOREACH(l2vpn, l2vpn_head, &leconf->l2vpn_tree) {
+ lif = l2vpn_if_find(l2vpn, kif->ifname);
+ if (lif) {
+ l2vpn_if_update_info(lif, kif);
+ l2vpn_if_update(lif);
+ break;
+ }
+ pw = l2vpn_pw_find(l2vpn, kif->ifname);
+ if (pw) {
+ l2vpn_pw_update_info(pw, kif);
+ break;
+ }
+ }
+ break;
+ case IMSG_NEWADDR:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr))
+ fatalx("NEWADDR imsg with wrong len");
+
+ if_addr_add(imsg.data);
+ break;
+ case IMSG_DELADDR:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr))
+ fatalx("DELADDR imsg with wrong len");
+
+ if_addr_del(imsg.data);
+ break;
+ case IMSG_SOCKET_IPC:
+ if (iev_lde) {
+ log_warnx("%s: received unexpected imsg fd to lde", __func__);
+ break;
+ }
+ if ((fd = imsg.fd) == -1) {
+ log_warnx("%s: expected to receive imsg fd to lde but didn't receive any", __func__);
+ break;
+ }
+
+ if ((iev_lde = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_lde->ibuf, fd);
+ iev_lde->handler_read = ldpe_dispatch_lde;
+ event_add_read(master, iev_lde->handler_read, iev_lde,
+ iev_lde->ibuf.fd, &iev_lde->ev_read);
+ iev_lde->handler_write = ldp_write_handler;
+ iev_lde->ev_write = NULL;
+ break;
+ case IMSG_INIT:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ldpd_init))
+ fatalx("INIT imsg with wrong len");
+
+ memcpy(&init, imsg.data, sizeof(init));
+ ldpe_init(&init);
+ break;
+ case IMSG_AGENTX_ENABLED:
+ ldp_agentx_enabled();
+ break;
+ case IMSG_CLOSE_SOCKETS:
+ af = imsg.hdr.peerid;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->af != af)
+ continue;
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+#ifdef __OpenBSD__
+ pfkey_remove(nbr);
+#endif
+ nbr->auth.method = AUTH_NONE;
+ }
+ ldpe_close_sockets(af);
+ if_update_all(af);
+ tnbr_update_all(af);
+
+ disc_socket = -1;
+ edisc_socket = -1;
+ session_socket = -1;
+ if (CHECK_FLAG((ldp_af_conf_get(leconf, af))->flags, F_LDPD_AF_ENABLED))
+ ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, af, NULL, 0);
+ break;
+ case IMSG_SOCKET_NET:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(enum socket_type))
+ fatalx("SOCKET_NET imsg with wrong len");
+ socket_type = imsg.data;
+
+ switch (*socket_type) {
+ case LDP_SOCKET_DISC:
+ disc_socket = imsg.fd;
+ break;
+ case LDP_SOCKET_EDISC:
+ edisc_socket = imsg.fd;
+ break;
+ case LDP_SOCKET_SESSION:
+ session_socket = imsg.fd;
+ break;
+ }
+ break;
+ case IMSG_SETUP_SOCKETS:
+ af = imsg.hdr.peerid;
+ if (disc_socket == -1 || edisc_socket == -1 ||
+ session_socket == -1) {
+ if (disc_socket != -1)
+ close(disc_socket);
+ if (edisc_socket != -1)
+ close(edisc_socket);
+ if (session_socket != -1)
+ close(session_socket);
+ break;
+ }
+
+ ldpe_setup_sockets(af, disc_socket, edisc_socket, session_socket);
+ if_update_all(af);
+ tnbr_update_all(af);
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->af != af)
+ continue;
+ nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr;
+#ifdef __OpenBSD__
+ nbrp = nbr_params_find(leconf, nbr->id);
+ if (nbrp) {
+ nbr->auth.method = nbrp->auth.method;
+ if (pfkey_establish(nbr, nbrp) == -1)
+ fatalx("pfkey setup failed");
+ }
+#endif
+ if (nbr_session_active_role(nbr))
+ nbr_establish_connection(nbr);
+ }
+ break;
+ case IMSG_RTRID_UPDATE:
+ memcpy(&global.rtr_id, imsg.data, sizeof(global.rtr_id));
+ if (leconf->rtr_id.s_addr == INADDR_ANY) {
+ ldpe_reset_nbrs(AF_UNSPEC);
+ }
+ if_update_all(AF_UNSPEC);
+ tnbr_update_all(AF_UNSPEC);
+ break;
+ case IMSG_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct ldpd_conf));
+
+ RB_INIT(iface_head, &nconf->iface_tree);
+ RB_INIT(tnbr_head, &nconf->tnbr_tree);
+ RB_INIT(nbrp_head, &nconf->nbrp_tree);
+ RB_INIT(l2vpn_head, &nconf->l2vpn_tree);
+ break;
+ case IMSG_RECONF_IFACE:
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ RB_INSERT(iface_head, &nconf->iface_tree, niface);
+ break;
+ case IMSG_RECONF_TNBR:
+ if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL)
+ fatal(NULL);
+ memcpy(ntnbr, imsg.data, sizeof(struct tnbr));
+
+ RB_INSERT(tnbr_head, &nconf->tnbr_tree, ntnbr);
+ break;
+ case IMSG_RECONF_NBRP:
+ if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL)
+ fatal(NULL);
+ memcpy(nnbrp, imsg.data, sizeof(struct nbr_params));
+
+ RB_INSERT(nbrp_head, &nconf->nbrp_tree, nnbrp);
+ break;
+ case IMSG_RECONF_L2VPN:
+ if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL)
+ fatal(NULL);
+ memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn));
+
+ RB_INIT(l2vpn_if_head, &nl2vpn->if_tree);
+ RB_INIT(l2vpn_pw_head, &nl2vpn->pw_tree);
+ RB_INIT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree);
+
+ RB_INSERT(l2vpn_head, &nconf->l2vpn_tree, nl2vpn);
+ break;
+ case IMSG_RECONF_L2VPN_IF:
+ if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL)
+ fatal(NULL);
+ memcpy(nlif, imsg.data, sizeof(struct l2vpn_if));
+
+ RB_INSERT(l2vpn_if_head, &nl2vpn->if_tree, nlif);
+ break;
+ case IMSG_RECONF_L2VPN_PW:
+ if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL)
+ fatal(NULL);
+ memcpy(npw, imsg.data, sizeof(struct l2vpn_pw));
+
+ RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_tree, npw);
+ break;
+ case IMSG_RECONF_L2VPN_IPW:
+ if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL)
+ fatal(NULL);
+ memcpy(npw, imsg.data, sizeof(struct l2vpn_pw));
+
+ RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree, npw);
+ break;
+ case IMSG_RECONF_END:
+ merge_config(leconf, nconf);
+ ldp_clear_config(nconf);
+ nconf = NULL;
+ global.conf_seqnum++;
+ break;
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ break;
+ case IMSG_DEBUG_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(ldp_debug)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug));
+ break;
+ case IMSG_FILTER_UPDATE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ldp_access)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ laccess = imsg.data;
+ ldpe_check_filter_af(AF_INET, &leconf->ipv4, laccess->name);
+ ldpe_check_filter_af(AF_INET6, &leconf->ipv6, laccess->name);
+ break;
+ case IMSG_LDP_SYNC_IF_STATE_REQUEST:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ldp_igp_sync_if_state_req)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ ldp_sync_if_state_req = imsg.data;
+ ldp_sync_fsm_state_req(ldp_sync_if_state_req);
+ break;
+ case IMSG_RLFA_REG:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_request)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_req = imsg.data;
+
+ rnode = rlfa_node_find(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ if (!rnode)
+ rnode = rlfa_node_new(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ rclient = rlfa_client_find(rnode, &rlfa_req->igp);
+ if (rclient)
+ /* RLFA already registered - do nothing */
+ break;
+ rclient = rlfa_client_new(rnode, &rlfa_req->igp);
+ ldpe_rlfa_init(rclient);
+ break;
+ case IMSG_RLFA_UNREG_ALL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_igp)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_igp = imsg.data;
+
+ RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head,
+ &rlfa_node_tree, rntmp) {
+ rclient = rlfa_client_find(rnode, rlfa_igp);
+ if (!rclient)
+ continue;
+
+ ldpe_rlfa_exit(rclient);
+ rlfa_client_del(rclient);
+ }
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handlers and exit */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ ldpe_shutdown();
+ }
+}
+
+/* ARGSUSED */
+static void ldpe_dispatch_lde(struct event *thread)
+{
+ struct imsgev *iev = EVENT_ARG(thread);
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ struct map *map;
+ struct notify_msg *nm;
+ struct nbr *nbr;
+ int n, shut = 0;
+
+ iev->ev_read = NULL;
+
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("ldpe_dispatch_lde: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_MAPPING_ADD:
+ case IMSG_RELEASE_ADD:
+ case IMSG_REQUEST_ADD:
+ case IMSG_WITHDRAW_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct map))
+ fatalx("invalid size of map request");
+ map = imsg.data;
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+ if (nbr->state != NBR_STA_OPER)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_MAPPING_ADD:
+ mapping_list_add(&nbr->mapping_list, map);
+ break;
+ case IMSG_RELEASE_ADD:
+ mapping_list_add(&nbr->release_list, map);
+ break;
+ case IMSG_REQUEST_ADD:
+ mapping_list_add(&nbr->request_list, map);
+ break;
+ case IMSG_WITHDRAW_ADD:
+ mapping_list_add(&nbr->withdraw_list, map);
+ break;
+ }
+ break;
+ case IMSG_MAPPING_ADD_END:
+ case IMSG_RELEASE_ADD_END:
+ case IMSG_REQUEST_ADD_END:
+ case IMSG_WITHDRAW_ADD_END:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+ if (nbr->state != NBR_STA_OPER)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_MAPPING_ADD_END:
+ send_labelmessage(nbr, MSG_TYPE_LABELMAPPING,
+ &nbr->mapping_list);
+ break;
+ case IMSG_RELEASE_ADD_END:
+ send_labelmessage(nbr, MSG_TYPE_LABELRELEASE,
+ &nbr->release_list);
+ break;
+ case IMSG_REQUEST_ADD_END:
+ send_labelmessage(nbr, MSG_TYPE_LABELREQUEST,
+ &nbr->request_list);
+ break;
+ case IMSG_WITHDRAW_ADD_END:
+ send_labelmessage(nbr, MSG_TYPE_LABELWITHDRAW,
+ &nbr->withdraw_list);
+ break;
+ }
+ break;
+ case IMSG_NOTIFICATION_SEND:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct notify_msg))
+ fatalx("invalid size of OE request");
+ nm = imsg.data;
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find neighbor", __func__);
+ break;
+ }
+ if (nbr->state != NBR_STA_OPER)
+ break;
+
+ send_notification_full(nbr->tcp, nm);
+ break;
+ case IMSG_CTL_END:
+ case IMSG_CTL_SHOW_LIB_BEGIN:
+ case IMSG_CTL_SHOW_LIB_RCVD:
+ case IMSG_CTL_SHOW_LIB_SENT:
+ case IMSG_CTL_SHOW_LIB_END:
+ case IMSG_CTL_SHOW_L2VPN_PW:
+ case IMSG_CTL_SHOW_L2VPN_BINDING:
+ control_imsg_relay(&imsg);
+ break;
+ case IMSG_NBR_SHUTDOWN:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find neighbor", __func__);
+ break;
+ }
+ if (nbr->state != NBR_STA_OPER)
+ break;
+ session_shutdown(nbr,S_SHUTDOWN,0,0);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handlers and exit */
+ EVENT_OFF(iev->ev_read);
+ EVENT_OFF(iev->ev_write);
+ ldpe_shutdown();
+ }
+}
+
+#ifdef __OpenBSD__
+/* ARGSUSED */
+static void ldpe_dispatch_pfkey(struct event *thread)
+{
+ int fd = EVENT_FD(thread);
+
+ event_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock,
+ &pfkey_ev);
+
+ if (pfkey_read(fd, NULL) == -1)
+ fatal("pfkey_read failed, exiting...");
+}
+#endif /* __OpenBSD__ */
+
+static void
+ldpe_setup_sockets(int af, int disc_socket, int edisc_socket,
+ int session_socket)
+{
+ struct ldpd_af_global *af_global;
+
+ af_global = ldp_af_global_get(&global, af);
+
+ /* discovery socket */
+ af_global->ldp_disc_socket = disc_socket;
+ event_add_read(master, disc_recv_packet, &af_global->disc_ev,
+ af_global->ldp_disc_socket, &af_global->disc_ev);
+
+ /* extended discovery socket */
+ af_global->ldp_edisc_socket = edisc_socket;
+ event_add_read(master, disc_recv_packet, &af_global->edisc_ev,
+ af_global->ldp_edisc_socket, &af_global->edisc_ev);
+
+ /* session socket */
+ af_global->ldp_session_socket = session_socket;
+ accept_add(af_global->ldp_session_socket, session_accept, NULL);
+}
+
+static void
+ldpe_close_sockets(int af)
+{
+ struct ldpd_af_global *af_global;
+
+ af_global = ldp_af_global_get(&global, af);
+
+ /* discovery socket */
+ EVENT_OFF(af_global->disc_ev);
+ if (af_global->ldp_disc_socket != -1) {
+ close(af_global->ldp_disc_socket);
+ af_global->ldp_disc_socket = -1;
+ }
+
+ /* extended discovery socket */
+ EVENT_OFF(af_global->edisc_ev);
+ if (af_global->ldp_edisc_socket != -1) {
+ close(af_global->ldp_edisc_socket);
+ af_global->ldp_edisc_socket = -1;
+ }
+
+ /* session socket */
+ if (af_global->ldp_session_socket != -1) {
+ accept_del(af_global->ldp_session_socket);
+ close(af_global->ldp_session_socket);
+ af_global->ldp_session_socket = -1;
+ }
+}
+
+int
+ldpe_acl_check(char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen)
+{
+ return ldp_acl_request(iev_main_sync, acl_name, af, addr, prefixlen);
+}
+
+void
+ldpe_reset_nbrs(int af)
+{
+ struct nbr *nbr;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (af == AF_UNSPEC || nbr->af == af)
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ }
+}
+
+void
+ldpe_reset_ds_nbrs(void)
+{
+ struct nbr *nbr;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->ds_tlv)
+ session_shutdown(nbr, S_SHUTDOWN, 0, 0);
+ }
+}
+
+void
+ldpe_remove_dynamic_tnbrs(int af)
+{
+ struct tnbr *tnbr, *safe;
+
+ RB_FOREACH_SAFE(tnbr, tnbr_head, &leconf->tnbr_tree, safe) {
+ if (tnbr->af != af)
+ continue;
+
+ UNSET_FLAG(tnbr->flags, F_TNBR_DYNAMIC);
+ tnbr_check(leconf, tnbr);
+ }
+}
+
+void
+ldpe_stop_init_backoff(int af)
+{
+ struct nbr *nbr;
+
+ RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
+ if (nbr->af == af && nbr_pending_idtimer(nbr)) {
+ nbr_stop_idtimer(nbr);
+ nbr_establish_connection(nbr);
+ }
+ }
+}
+
+static void
+ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t idx)
+{
+ struct iface *iface;
+ struct iface_af *ia;
+ struct ctl_iface *ictl;
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree) {
+ if (idx == 0 || idx == iface->ifindex) {
+ ia = iface_af_get(iface, af);
+ if (!ia->enabled)
+ continue;
+
+ ictl = if_to_ctl(ia);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_INTERFACE,
+ 0, 0, -1, ictl, sizeof(struct ctl_iface));
+ }
+ }
+}
+
+void
+ldpe_iface_ctl(struct ctl_conn *c, ifindex_t idx)
+{
+ ldpe_iface_af_ctl(c, AF_INET, idx);
+ ldpe_iface_af_ctl(c, AF_INET6, idx);
+}
+
+void
+ldpe_adj_ctl(struct ctl_conn *c)
+{
+ struct adj *adj;
+ struct ctl_adj *actl;
+
+ RB_FOREACH(adj, global_adj_head, &global.adj_tree) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0,
+ -1, actl, sizeof(struct ctl_adj));
+ }
+
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
+
+void
+ldpe_adj_detail_ctl(struct ctl_conn *c)
+{
+ struct iface *iface;
+ struct tnbr *tnbr;
+ struct adj *adj;
+ struct ctl_adj *actl;
+ struct ctl_disc_if ictl;
+ struct ctl_disc_tnbr tctl;
+
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, -1, NULL, 0);
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree) {
+ memset(&ictl, 0, sizeof(ictl));
+ ictl.active_v4 = (iface->ipv4.state == IF_STA_ACTIVE);
+ ictl.active_v6 = (iface->ipv6.state == IF_STA_ACTIVE);
+
+ if (!ictl.active_v4 && !ictl.active_v6)
+ continue;
+
+ strlcpy(ictl.name, iface->name, sizeof(ictl.name));
+ if (RB_EMPTY(ia_adj_head, &iface->ipv4.adj_tree) &&
+ RB_EMPTY(ia_adj_head, &iface->ipv6.adj_tree))
+ ictl.no_adj = 1;
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_IFACE, 0, 0,
+ -1, &ictl, sizeof(ictl));
+
+ RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ,
+ 0, 0, -1, actl, sizeof(struct ctl_adj));
+ }
+ RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ,
+ 0, 0, -1, actl, sizeof(struct ctl_adj));
+ }
+ }
+
+ RB_FOREACH(tnbr, tnbr_head, &leconf->tnbr_tree) {
+ memset(&tctl, 0, sizeof(tctl));
+ tctl.af = tnbr->af;
+ tctl.addr = tnbr->addr;
+ if (tnbr->adj == NULL)
+ tctl.no_adj = 1;
+
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_TNBR, 0, 0,
+ -1, &tctl, sizeof(tctl));
+
+ if (tnbr->adj == NULL)
+ continue;
+
+ actl = adj_to_ctl(tnbr->adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, 0, 0,
+ -1, actl, sizeof(struct ctl_adj));
+ }
+
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
+
+void
+ldpe_nbr_ctl(struct ctl_conn *c)
+{
+ struct adj *adj;
+ struct ctl_adj *actl;
+ struct nbr *nbr;
+ struct ctl_nbr *nctl;
+
+ RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) {
+ if (nbr->state == NBR_STA_PRESENT)
+ continue;
+
+ nctl = nbr_to_ctl(nbr);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 0, -1, nctl,
+ sizeof(struct ctl_nbr));
+
+ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) {
+ actl = adj_to_ctl(adj);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_DISC,
+ 0, 0, -1, actl, sizeof(struct ctl_adj));
+ }
+
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_END, 0, 0, -1,
+ NULL, 0);
+ }
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
+
+void
+ldpe_ldp_sync_ctl(struct ctl_conn *c)
+{
+ struct iface *iface;
+ struct ctl_ldp_sync *ictl;
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree) {
+ ictl = ldp_sync_to_ctl(iface);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_LDP_SYNC,
+ 0, 0, -1, ictl, sizeof(struct ctl_ldp_sync));
+ }
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
+
+void
+mapping_list_add(struct mapping_head *mh, struct map *map)
+{
+ struct mapping_entry *me;
+
+ me = calloc(1, sizeof(*me));
+ if (me == NULL)
+ fatal(__func__);
+ me->map = *map;
+
+ TAILQ_INSERT_TAIL(mh, me, entry);
+}
+
+void
+mapping_list_clr(struct mapping_head *mh)
+{
+ struct mapping_entry *me;
+
+ while ((me = TAILQ_FIRST(mh)) != NULL) {
+ TAILQ_REMOVE(mh, me, entry);
+ assert(me != TAILQ_FIRST(mh));
+ free(me);
+ }
+}
+
+void
+ldpe_check_filter_af(int af, struct ldpd_af_conf *af_conf,
+ const char *filter_name)
+{
+ if (strcmp(af_conf->acl_thello_accept_from, filter_name) == 0)
+ ldpe_remove_dynamic_tnbrs(af);
+}
+
+void
+ldpe_set_config_change_time(void)
+{
+ /* SNMP update time when ever there is a config change */
+ leconf->config_change_time = time(NULL);
+}
diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h
new file mode 100644
index 0000000..f310ba5
--- /dev/null
+++ b/ldpd/ldpe.h
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#ifndef _LDPE_H_
+#define _LDPE_H_
+
+#include "queue.h"
+#include "openbsd-tree.h"
+#ifdef __OpenBSD__
+#include <net/pfkeyv2.h>
+#endif
+
+#include "ldpd.h"
+#include "lib/ldp_sync.h"
+
+/* forward declarations */
+TAILQ_HEAD(mapping_head, mapping_entry);
+
+struct hello_source {
+ enum hello_type type;
+ struct {
+ struct iface_af *ia;
+ union ldpd_addr src_addr;
+ } link;
+ struct tnbr *target;
+};
+
+struct adj {
+ RB_ENTRY(adj) global_entry, nbr_entry, 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;
+};
+RB_PROTOTYPE(global_adj_head, adj, global_entry, adj_compare)
+RB_PROTOTYPE(nbr_adj_head, adj, nbr_entry, adj_compare)
+RB_PROTOTYPE(ia_adj_head, adj, ia_entry, adj_compare)
+
+struct tcp_conn {
+ struct nbr *nbr;
+ int fd;
+ struct ibuf_read *rbuf;
+ struct evbuf wbuf;
+ struct event *rev;
+ in_port_t lport;
+ in_port_t rport;
+};
+
+struct nbr {
+ RB_ENTRY(nbr) id_tree, addr_tree, pid_tree;
+ struct tcp_conn *tcp;
+ struct nbr_adj_head adj_tree; /* 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 */
+ ifindex_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 ldp_stats stats;
+
+ 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
+#define F_NBR_CAP_DYNAMIC 0x02
+#define F_NBR_CAP_TWCARD 0x04
+#define F_NBR_CAP_UNOTIF 0x08
+
+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 (*)(struct event *), 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);
+void send_capability(struct nbr *, uint16_t, int);
+int recv_capability(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(struct tcp_conn *, uint32_t, uint32_t, uint16_t);
+void send_notification_rtlvs(struct nbr *, uint32_t, uint32_t, uint16_t,
+ uint16_t, uint16_t, char *);
+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);
+void send_mac_withdrawal(struct nbr *, struct map *, uint8_t *);
+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);
+uint16_t len_fec_tlv(struct map *);
+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(void);
+void ldpe_init(struct ldpd_init *);
+int ldpe_imsg_compose_parent(int, pid_t, void *,
+ uint16_t);
+void ldpe_imsg_compose_parent_sync(int, pid_t, void *, uint16_t);
+int ldpe_imsg_compose_lde(int, uint32_t, pid_t, void *,
+ uint16_t);
+int ldpe_acl_check(char *, int, union ldpd_addr *, uint8_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 *c, ifindex_t ifidx);
+void ldpe_adj_ctl(struct ctl_conn *);
+void ldpe_adj_detail_ctl(struct ctl_conn *);
+void ldpe_nbr_ctl(struct ctl_conn *);
+void ldpe_ldp_sync_ctl(struct ctl_conn *);
+void mapping_list_add(struct mapping_head *, struct map *);
+void mapping_list_clr(struct mapping_head *);
+void ldpe_set_config_change_time(void);
+
+/* interface.c */
+struct iface *if_new(const char *);
+void ldpe_if_init(struct iface *);
+void ldpe_if_exit(struct iface *);
+struct iface *if_lookup(struct ldpd_conf *c, ifindex_t ifidx);
+struct iface *if_lookup_name(struct ldpd_conf *, const char *);
+void if_update_info(struct iface *, struct kif *);
+struct iface_af *iface_af_get(struct iface *, int);
+void if_addr_add(struct kaddr *);
+void if_addr_del(struct kaddr *);
+void ldp_if_update(struct iface *, int);
+void if_update_all(int);
+uint16_t if_get_hello_holdtime(struct iface_af *);
+uint16_t if_get_hello_interval(struct iface_af *);
+uint16_t if_get_wait_for_sync_interval(void);
+struct ctl_iface *if_to_ctl(struct iface_af *);
+in_addr_t if_get_ipv4_addr(struct iface *);
+int ldp_sync_fsm_adj_event(struct adj *, enum ldp_sync_event);
+int ldp_sync_fsm_nbr_event(struct nbr *, enum ldp_sync_event);
+int ldp_sync_fsm_state_req(struct ldp_igp_sync_if_state_req *);
+int ldp_sync_fsm(struct iface *, enum ldp_sync_event);
+void ldp_sync_fsm_reset_all(void);
+const char *ldp_sync_state_name(int);
+const char *ldp_sync_event_name(int);
+struct ctl_ldp_sync *ldp_sync_to_ctl(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 in_addr, struct hello_source *);
+int adj_get_af(const struct adj *adj);
+void adj_start_itimer(struct adj *);
+void adj_stop_itimer(struct adj *);
+struct tnbr *tnbr_new(int, union ldpd_addr *);
+struct tnbr *tnbr_find(struct ldpd_conf *, int, union ldpd_addr *);
+struct tnbr *tnbr_check(struct ldpd_conf *, struct tnbr *);
+void tnbr_update(struct tnbr *);
+void tnbr_update_all(int);
+uint16_t tnbr_get_hello_holdtime(struct tnbr *);
+uint16_t tnbr_get_hello_interval(struct tnbr *);
+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_get_first_ldpid(void);
+struct nbr *nbr_get_next_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(struct event *thread);
+void session_accept(struct event *thread);
+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 *);
+
+extern char *pkt_ptr; /* packet buffer */
+
+/* pfkey.c */
+#ifdef __OpenBSD__
+int pfkey_read(int, struct sadb_msg *);
+int pfkey_establish(struct nbr *, struct nbr_params *);
+int pfkey_remove(struct nbr *);
+int pfkey_init(void);
+#endif
+
+/* 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 *);
+
+DECLARE_HOOK(ldp_nbr_state_change, (struct nbr * nbr, int old_state),
+ (nbr, old_state));
+
+#endif /* _LDPE_H_ */
diff --git a/ldpd/log.c b/ldpd/log.c
new file mode 100644
index 0000000..7c4d782
--- /dev/null
+++ b/ldpd/log.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+#include "printfrr.h"
+
+#include <lib/log.h>
+
+const char *log_procname;
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char buf[1024];
+
+ switch (ldpd_process) {
+ case PROC_LDE_ENGINE:
+ vsnprintfrr(buf, sizeof(buf), fmt, ap);
+ lde_imsg_compose_parent_sync(IMSG_LOG, pri, buf, strlen(buf) + 1);
+ break;
+ case PROC_LDP_ENGINE:
+ vsnprintfrr(buf, sizeof(buf), fmt, ap);
+ ldpe_imsg_compose_parent_sync(IMSG_LOG, pri, buf, strlen(buf) + 1);
+ break;
+ case PROC_MAIN:
+ vzlog(pri, fmt, ap);
+ break;
+ }
+}
+
+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_ERR, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_ERR, emsg, ap);
+ logit(LOG_ERR, "%s", strerror(errno));
+ } else {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ /* format extended above */
+ vlog(LOG_ERR, nfmt, ap);
+#pragma GCC diagnostic pop
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_ERR, 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_notice(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_NOTICE, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ 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", log_procname, strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal in %s: %s: %s",
+ log_procname, emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal in %s: %s", log_procname, emsg);
+
+ exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
diff --git a/ldpd/log.h b/ldpd/log.h
new file mode 100644
index 0000000..641ad8a
--- /dev/null
+++ b/ldpd/log.h
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ */
+
+#ifndef LOG_H
+#define LOG_H
+
+#include <stdarg.h>
+
+extern const char *log_procname;
+
+void logit(int, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void vlog(int, const char *, va_list)
+ __attribute__((__format__ (printf, 2, 0)));
+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_notice(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_debug(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void fatal(const char *)
+ __attribute__ ((noreturn))
+ __attribute__((__format__ (printf, 1, 0)));
+void fatalx(const char *)
+ __attribute__ ((noreturn))
+ __attribute__((__format__ (printf, 1, 0)));
+
+#endif /* LOG_H */
diff --git a/ldpd/logmsg.c b/ldpd/logmsg.c
new file mode 100644
index 0000000..75f4293
--- /dev/null
+++ b/ldpd/logmsg.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ */
+
+#include <zebra.h>
+#include "lib/printfrr.h"
+
+#include "mpls.h"
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+
+#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, sockaddr_len(sa), 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));
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sa_in6.sin6_len = sizeof(sa_in6);
+#endif
+ 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, ifindex_t ifindex)
+{
+ struct sockaddr_in6 sa_in6;
+
+ memset(&sa_in6, 0, sizeof(sa_in6));
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sa_in6.sin6_len = sizeof(sa_in6);
+#endif
+ 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_IMPLICIT_NULL:
+ snprintf(buf, TF_LEN, "imp-null");
+ break;
+ case MPLS_LABEL_IPV4_EXPLICIT_NULL:
+ case MPLS_LABEL_IPV6_EXPLICIT_NULL:
+ snprintf(buf, TF_LEN, "exp-null");
+ break;
+ default:
+ snprintf(buf, TF_LEN, "%u", label);
+ break;
+ }
+
+ return (buf);
+}
+
+const char *
+log_time(time_t t)
+{
+ char *buf;
+ static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */
+ static int idx = 0;
+ uint64_t sec, min, hrs, day, week;
+
+ buf = tfbuf[idx++];
+ if (idx == TF_BUFS)
+ idx = 0;
+
+ week = t;
+
+ sec = week % 60;
+ week /= 60;
+ min = week % 60;
+ week /= 60;
+ hrs = week % 24;
+ week /= 24;
+ day = week % 7;
+ week /= 7;
+
+ if (week > 0)
+ snprintfrr(buf, TF_LEN,
+ "%02" PRIu64 "w%01" PRIu64 "d%02" PRIu64 "h", week,
+ day, hrs);
+ else if (day > 0)
+ snprintfrr(buf, TF_LEN,
+ "%01" PRIu64 "d%02" PRIu64 "h%02" PRIu64 "m", day,
+ hrs, min);
+ else
+ snprintfrr(buf, TF_LEN,
+ "%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64, hrs, min,
+ sec);
+
+ 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[128];
+
+ 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), "pw-id %u group-id %u (%s)",
+ map->fec.pwid.pwid, map->fec.pwid.group_id,
+ pw_type_name(map->fec.pwid.type)) == -1)
+ return ("???");
+ break;
+ case MAP_TYPE_TYPED_WCARD:
+ if (snprintf(buf, sizeof(buf), "typed wildcard") < 0)
+ return ("???");
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ if (snprintf(buf + strlen(buf), sizeof(buf) -
+ strlen(buf), " (prefix, address-family %s)",
+ af_name(map->fec.twcard.u.prefix_af)) < 0)
+ return ("???");
+ break;
+ case MAP_TYPE_PWID:
+ if (snprintf(buf + strlen(buf), sizeof(buf) -
+ strlen(buf), " (pwid, type %s)",
+ pw_type_name(map->fec.twcard.u.pw_type)) < 0)
+ return ("???");
+ break;
+ default:
+ if (snprintf(buf + strlen(buf), sizeof(buf) -
+ strlen(buf), " (unknown type)") < 0)
+ return ("???");
+ break;
+ }
+ 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 (snprintfrr(buf, sizeof(buf),
+ "pwid %u (%s) - %pI4",
+ fec->u.pwid.pwid, pw_type_name(fec->u.pwid.type),
+ &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");
+#ifdef AF_MPLS
+ case AF_MPLS:
+ return ("mpls");
+#endif
+ 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_CAPABILITY:
+ return ("capability");
+ 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_UNSSUPORTDCAP:
+ return ("Unsupported Capability");
+ case S_ENDOFLIB:
+ return ("End-of-LIB");
+ 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");
+ case PW_TYPE_WILDCARD:
+ return ("Wildcard");
+ default:
+ snprintf(buf, sizeof(buf), "[%0x]", pw_type);
+ return (buf);
+ }
+}
+
+const char *
+pw_error_code(uint8_t status)
+{
+ static char buf[16];
+
+ switch (status) {
+ case F_PW_NO_ERR:
+ return ("No Error");
+ case F_PW_LOCAL_NOT_FWD:
+ return ("local not forwarding");
+ case F_PW_REMOTE_NOT_FWD:
+ return ("remote not forwarding");
+ case F_PW_NO_REMOTE_LABEL:
+ return ("no remote label");
+ case F_PW_MTU_MISMATCH:
+ return ("mtu mismatch between peers");
+ default:
+ snprintf(buf, sizeof(buf), "[%0x]", status);
+ return (buf);
+ }
+}
diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c
new file mode 100644
index 0000000..5209c55
--- /dev/null
+++ b/ldpd/neighbor.c
@@ -0,0 +1,855 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+
+DEFINE_HOOK(ldp_nbr_state_change, (struct nbr * nbr, int old_state),
+ (nbr, old_state));
+
+static __inline int nbr_id_compare(const struct nbr *, const struct nbr *);
+static __inline int nbr_addr_compare(const struct nbr *, const struct nbr *);
+static __inline int nbr_pid_compare(const struct nbr *, const struct nbr *);
+static void nbr_update_peerid(struct nbr *);
+static void nbr_ktimer(struct event *thread);
+static void nbr_start_ktimer(struct nbr *);
+static void nbr_ktimeout(struct event *thread);
+static void nbr_start_ktimeout(struct nbr *);
+static void nbr_itimeout(struct event *thread);
+static void nbr_start_itimeout(struct nbr *);
+static void nbr_idtimer(struct event *thread);
+static int nbr_act_session_operational(struct nbr *);
+static void nbr_send_labelmappings(struct nbr *);
+static __inline int nbr_params_compare(const struct nbr_params *,
+ const struct nbr_params *);
+
+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)
+RB_GENERATE(nbrp_head, nbr_params, entry, nbr_params_compare)
+
+const 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(const struct nbr *a, const struct nbr *b)
+{
+ return (ntohl(a->id.s_addr) - ntohl(b->id.s_addr));
+}
+
+static __inline int
+nbr_addr_compare(const struct nbr *a, const 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(const struct nbr *a, const 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 (CHECK_FLAG(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 %pI4, event %s not expected in state %s", __func__, &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 %pI4 from %s to %s",
+ __func__, nbr_event_names[event],
+ nbr_action_names[nbr_fsm_tbl[i].action],
+ &nbr->id, nbr_state_name(old_state),
+ nbr_state_name(nbr->state));
+
+ hook_call(ldp_nbr_state_change, nbr, old_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 %pI4 transport-address %s", __func__,
+ &id, log_addr(af, addr));
+
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal(__func__);
+
+ RB_INIT(nbr_adj_head, &nbr->adj_tree);
+ 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;
+
+ RB_FOREACH(adj, global_adj_head, &global.adj_tree) {
+ if (adj->lsr_id.s_addr == nbr->id.s_addr) {
+ adj->nbr = nbr;
+ RB_INSERT(nbr_adj_head, &nbr->adj_tree, adj);
+ }
+ }
+
+ 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);
+
+ nbrp = nbr_params_find(leconf, nbr->id);
+ if (nbrp) {
+ nbr->auth.method = nbrp->auth.method;
+#ifdef __OpenBSD__
+ if (pfkey_establish(nbr, nbrp) == -1)
+ fatalx("pfkey setup failed");
+#else
+ sock_set_md5sig(
+ (ldp_af_global_get(&global, nbr->af))->ldp_session_socket,
+ nbr->af, &nbr->raddr, nbrp->auth.md5key);
+#endif
+ }
+
+ 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)
+{
+ struct adj *adj;
+
+ log_debug("%s: lsr-id %pI4", __func__, &nbr->id);
+
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+#ifdef __OpenBSD__
+ pfkey_remove(nbr);
+#else
+ sock_set_md5sig(
+ (ldp_af_global_get(&global, nbr->af))->ldp_session_socket,
+ nbr->af, &nbr->raddr, NULL);
+#endif
+ nbr->auth.method = AUTH_NONE;
+
+ if (nbr_pending_connect(nbr))
+ EVENT_OFF(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);
+
+ while (!RB_EMPTY(nbr_adj_head, &nbr->adj_tree)) {
+ adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree);
+
+ adj->nbr = NULL;
+ RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj);
+ }
+
+ 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_get_first_ldpid(void)
+{
+ return (RB_MIN(nbr_id_head, &nbrs_by_id));
+}
+
+struct nbr *
+nbr_get_next_ldpid(uint32_t lsr_id)
+{
+ struct nbr *nbr;
+ nbr = nbr_find_ldpid(lsr_id);
+ if (nbr)
+ return (RB_NEXT(nbr_id_head, nbr));
+ return NULL;
+}
+
+
+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;
+
+ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree)
+ 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(struct event *thread)
+{
+ struct nbr *nbr = EVENT_ARG(thread);
+
+ nbr->keepalive_timer = NULL;
+ send_keepalive(nbr);
+ nbr_start_ktimer(nbr);
+}
+
+static void
+nbr_start_ktimer(struct nbr *nbr)
+{
+ int secs;
+
+ /* send three keepalives per period */
+ secs = nbr->keepalive / KEEPALIVE_PER_PERIOD;
+ EVENT_OFF(nbr->keepalive_timer);
+ nbr->keepalive_timer = NULL;
+ event_add_timer(master, nbr_ktimer, nbr, secs, &nbr->keepalive_timer);
+}
+
+void
+nbr_stop_ktimer(struct nbr *nbr)
+{
+ EVENT_OFF(nbr->keepalive_timer);
+}
+
+/* Keepalive timeout: if the nbr hasn't sent keepalive */
+
+static void nbr_ktimeout(struct event *thread)
+{
+ struct nbr *nbr = EVENT_ARG(thread);
+
+ nbr->keepalive_timeout = NULL;
+
+ log_debug("%s: lsr-id %pI4", __func__, &nbr->id);
+
+ session_shutdown(nbr, S_KEEPALIVE_TMR, 0, 0);
+}
+
+static void
+nbr_start_ktimeout(struct nbr *nbr)
+{
+ EVENT_OFF(nbr->keepalive_timeout);
+ nbr->keepalive_timeout = NULL;
+ event_add_timer(master, nbr_ktimeout, nbr, nbr->keepalive,
+ &nbr->keepalive_timeout);
+}
+
+void
+nbr_stop_ktimeout(struct nbr *nbr)
+{
+ EVENT_OFF(nbr->keepalive_timeout);
+}
+
+/* Session initialization timeout: if nbr got stuck in the initialization FSM */
+
+static void nbr_itimeout(struct event *thread)
+{
+ struct nbr *nbr = EVENT_ARG(thread);
+
+ log_debug("%s: lsr-id %pI4", __func__, &nbr->id);
+
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+}
+
+static void
+nbr_start_itimeout(struct nbr *nbr)
+{
+ int secs;
+
+ secs = INIT_FSM_TIMEOUT;
+ EVENT_OFF(nbr->init_timeout);
+ nbr->init_timeout = NULL;
+ event_add_timer(master, nbr_itimeout, nbr, secs, &nbr->init_timeout);
+}
+
+void
+nbr_stop_itimeout(struct nbr *nbr)
+{
+ EVENT_OFF(nbr->init_timeout);
+}
+
+/* Init delay timer: timer to retry to iniziatize session */
+
+static void nbr_idtimer(struct event *thread)
+{
+ struct nbr *nbr = EVENT_ARG(thread);
+
+ nbr->initdelay_timer = NULL;
+
+ log_debug("%s: lsr-id %pI4", __func__, &nbr->id);
+
+ nbr_establish_connection(nbr);
+}
+
+void
+nbr_start_idtimer(struct nbr *nbr)
+{
+ int secs;
+
+ secs = INIT_DELAY_TMR;
+ switch(nbr->idtimer_cnt) {
+ default:
+ /* do not further increase the counter */
+ secs = MAX_DELAY_TMR;
+ break;
+ case 2:
+ secs *= 2;
+ /* FALLTHROUGH */
+ case 1:
+ secs *= 2;
+ /* FALLTHROUGH */
+ case 0:
+ nbr->idtimer_cnt++;
+ break;
+ }
+
+ EVENT_OFF(nbr->initdelay_timer);
+ nbr->initdelay_timer = NULL;
+ event_add_timer(master, nbr_idtimer, nbr, secs, &nbr->initdelay_timer);
+}
+
+void
+nbr_stop_idtimer(struct nbr *nbr)
+{
+ EVENT_OFF(nbr->initdelay_timer);
+}
+
+int
+nbr_pending_idtimer(struct nbr *nbr)
+{
+ return (nbr->initdelay_timer != NULL);
+}
+
+int
+nbr_pending_connect(struct nbr *nbr)
+{
+ return (nbr->ev_connect != NULL);
+}
+
+static void nbr_connect_cb(struct event *thread)
+{
+ struct nbr *nbr = EVENT_ARG(thread);
+ int error;
+ socklen_t len;
+
+ nbr->ev_connect = NULL;
+
+ 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)
+{
+ union sockunion local_su;
+ union sockunion remote_su;
+ struct adj *adj;
+ struct nbr_params *nbrp;
+#ifdef __OpenBSD__
+ int opt = 1;
+#endif
+
+ nbr->fd = socket(nbr->af, SOCK_STREAM, 0);
+ if (nbr->fd == -1) {
+ log_warn("%s: error while creating socket", __func__);
+ return (-1);
+ }
+ sock_set_nonblock(nbr->fd);
+
+ nbrp = nbr_params_find(leconf, nbr->id);
+ if (nbrp && nbrp->auth.method == AUTH_MD5SIG) {
+#ifdef __OpenBSD__
+ 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);
+ }
+#else
+ sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr, nbrp->auth.md5key);
+#endif
+ }
+
+ if (nbr->af == AF_INET) {
+ if (sock_set_ipv4_tos(nbr->fd, IPTOS_PREC_INTERNETCONTROL) == -1)
+ log_warn("%s: lsr-id %pI4, sock_set_ipv4_tos error",
+ __func__, &nbr->id);
+ } else if (nbr->af == AF_INET6) {
+ if (sock_set_ipv6_dscp(nbr->fd, IPTOS_PREC_INTERNETCONTROL) == -1)
+ log_warn("%s: lsr-id %pI4, sock_set_ipv6_dscp error",
+ __func__, &nbr->id);
+ }
+
+ addr2sa(nbr->af, &nbr->laddr, 0, &local_su);
+ addr2sa(nbr->af, &nbr->raddr, LDP_PORT, &remote_su);
+ if (nbr->af == AF_INET6 && nbr->raddr_scope)
+ addscope(&remote_su.sin6, nbr->raddr_scope);
+
+ if (bind(nbr->fd, &local_su.sa, sockaddr_len(&local_su.sa)) == -1) {
+ log_warn("%s: error while binding socket to %s", __func__,
+ log_sockaddr(&local_su.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.
+ */
+ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree)
+ send_hello(adj->source.type, adj->source.link.ia,
+ adj->source.target);
+
+ if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa)) == -1) {
+ if (errno == EINPROGRESS) {
+ event_add_write(master, nbr_connect_cb, nbr, nbr->fd,
+ &nbr->ev_connect);
+ return (0);
+ }
+ log_warn("%s: error while connecting to %s", __func__,
+ log_sockaddr(&remote_su.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 && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM))
+ return (nbrp->gtsm_enabled);
+
+ if (CHECK_FLAG((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 && !CHECK_FLAG(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 && CHECK_FLAG(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:
+ /* ignore any possible error */
+ sock_set_ipv6_minhopcount(fd, ttl);
+ 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 %pI4", __func__, &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);
+
+ ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_LDP_SYNC_START);
+
+ 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;
+ lde_nbr.flags = nbr->flags;
+ 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);
+}
+
+static __inline int
+nbr_params_compare(const struct nbr_params *a, const struct nbr_params *b)
+{
+ return (ntohl(a->lsr_id.s_addr) - ntohl(b->lsr_id.s_addr));
+}
+
+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;
+ nbrp.lsr_id = lsr_id;
+ return (RB_FIND(nbrp_head, &xconf->nbrp_tree, &nbrp));
+}
+
+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 && CHECK_FLAG(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.lport = nbr->tcp ? nbr->tcp->lport : 0;
+ nctl.raddr = nbr->raddr;
+ nctl.rport = nbr->tcp ? nbr->tcp->rport : 0;
+ nctl.auth_method = nbr->auth.method;
+ nctl.holdtime = nbr->keepalive;
+ nctl.nbr_state = nbr->state;
+ nctl.stats = nbr->stats;
+ nctl.flags = nbr->flags;
+ nctl.max_pdu_len = nbr->max_pdu_len;
+ nctl.hold_time_remaining = event_timer_remain_second(nbr->keepalive_timer);
+
+ 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 0000000..1709098
--- /dev/null
+++ b/ldpd/notification.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldp.h"
+#include "log.h"
+#include "ldpe.h"
+#include "ldp_debug.h"
+
+static int gen_returned_tlvs(struct ibuf *, uint16_t, uint16_t, char *);
+static void log_msg_notification(int, struct nbr *, struct notify_msg *);
+
+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 (CHECK_FLAG(nm->flags, F_NOTIF_PW_STATUS))
+ size += PW_STATUS_TLV_SIZE;
+ if (CHECK_FLAG(nm->flags, F_NOTIF_FEC))
+ size += len_fec_tlv(&nm->fec);
+ if (CHECK_FLAG(nm->flags, F_NOTIF_RETURNED_TLVS))
+ size += TLV_HDR_SIZE * 2 + nm->rtlvs.length;
+
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ SET_FLAG(err, gen_ldp_hdr(buf, size));
+ size -= LDP_HDR_SIZE;
+ SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size));
+ SET_FLAG(err, gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type));
+ /* optional tlvs */
+ if (CHECK_FLAG(nm->flags, F_NOTIF_PW_STATUS))
+ SET_FLAG(err, gen_pw_status_tlv(buf, nm->pw_status));
+ if (CHECK_FLAG(nm->flags, F_NOTIF_FEC))
+ SET_FLAG(err, gen_fec_tlv(buf, &nm->fec));
+ if (CHECK_FLAG(nm->flags, F_NOTIF_RETURNED_TLVS))
+ SET_FLAG(err, gen_returned_tlvs(buf, nm->rtlvs.type, nm->rtlvs.length,
+ nm->rtlvs.data));
+ if (err) {
+ ibuf_free(buf);
+ return;
+ }
+
+ if (tcp->nbr) {
+ log_msg_notification(1, tcp->nbr, nm);
+ nbr_fsm(tcp->nbr, NBR_EVT_PDU_SENT);
+ tcp->nbr->stats.notif_sent++;
+ }
+
+ /* update SNMP session counters */
+ switch (nm->status_code) {
+ case S_NO_HELLO:
+ leconf->stats.session_rejects_hello++;
+ break;
+ case S_BAD_LDP_ID:
+ leconf->stats.bad_ldp_id++;
+ break;
+ case S_BAD_PDU_LEN:
+ leconf->stats.bad_pdu_len++;
+ break;
+ case S_BAD_MSG_LEN:
+ leconf->stats.bad_msg_len++;
+ break;
+ case S_BAD_TLV_LEN:
+ leconf->stats.bad_tlv_len++;
+ break;
+ case S_BAD_TLV_VAL:
+ leconf->stats.malformed_tlv++;
+ break;
+ case S_KEEPALIVE_TMR:
+ leconf->stats.keepalive_timer_exp++;
+ break;
+ case S_SHUTDOWN:
+ leconf->stats.shutdown_send_notify++;
+ break;
+ default:
+ break;
+ }
+
+ evbuf_enqueue(&tcp->wbuf, buf);
+}
+
+/* send a notification without optional tlvs */
+void
+send_notification(struct tcp_conn *tcp, 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;
+ nm.msg_id = msg_id;
+ nm.msg_type = msg_type;
+
+ send_notification_full(tcp, &nm);
+}
+
+void
+send_notification_rtlvs(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
+ uint16_t msg_type, uint16_t tlv_type, uint16_t tlv_len, char *tlv_data)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = status_code;
+ nm.msg_id = msg_id;
+ nm.msg_type = msg_type;
+ /* do not append the given TLV if it's too big (shouldn't happen) */
+ if (tlv_len < 1024) {
+ nm.rtlvs.type = tlv_type;
+ nm.rtlvs.length = tlv_len;
+ nm.rtlvs.data = tlv_data;
+ SET_FLAG(nm.flags, F_NOTIF_RETURNED_TLVS);
+ }
+
+ send_notification_full(nbr->tcp, &nm);
+}
+
+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);
+ leconf->stats.bad_msg_len++;
+ 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);
+ leconf->stats.bad_tlv_len++;
+ 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_type;
+ uint16_t tlv_len;
+
+ if (len < sizeof(tlv)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ leconf->stats.bad_tlv_len++;
+ return (-1);
+ }
+
+ memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_type = ntohs(tlv.type);
+ tlv_len = ntohs(tlv.length);
+ if (tlv_len + TLV_HDR_SIZE > len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ leconf->stats.bad_tlv_len++;
+ return (-1);
+ }
+ buf += TLV_HDR_SIZE;
+ len -= TLV_HDR_SIZE;
+
+ switch (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);
+ SET_FLAG(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);
+ leconf->stats.bad_tlv_len++;
+ return (-1);
+ }
+ SET_FLAG(nm.flags, F_NOTIF_FEC);
+ break;
+ default:
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) {
+ nbr->stats.unknown_tlv++;
+ send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
+ }
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ }
+
+ /* sanity checks */
+ switch (nm.status_code) {
+ case S_PW_STATUS:
+ if (!CHECK_FLAG(nm.flags, (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) {
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
+ return (-1);
+ }
+
+ switch (nm.fec.type) {
+ case MAP_TYPE_PWID:
+ break;
+ default:
+ send_notification(nbr->tcp, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ }
+ break;
+ case S_ENDOFLIB:
+ if (!CHECK_FLAG(nm.flags, F_NOTIF_FEC)) {
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
+ return (-1);
+ }
+ if (nm.fec.type != MAP_TYPE_TYPED_WCARD) {
+ send_notification(nbr->tcp, S_BAD_TLV_VAL, msg.id, msg.type);
+ return (-1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ log_msg_notification(0, nbr, &nm);
+
+ if (CHECK_FLAG(st.status_code, htonl(STATUS_FATAL))) {
+ if (nbr->state == NBR_STA_OPENSENT)
+ nbr_start_idtimer(nbr);
+
+ /*
+ * RFC 5036 - Section 3.5.1.1:
+ * "When an LSR receives a Shutdown message during session
+ * initialization, it SHOULD transmit a Shutdown message and
+ * then close the transport connection".
+ */
+ if (nbr->state != NBR_STA_OPER && nm.status_code == S_SHUTDOWN) {
+ leconf->stats.session_attempts++;
+ send_notification(nbr->tcp, S_SHUTDOWN, msg.id, msg.type);
+ }
+
+ leconf->stats.shutdown_rcv_notify++;
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+ return (-1);
+ }
+
+ /* lde needs to know about a few notification messages
+ * and update SNMP session counters
+ */
+ switch (nm.status_code) {
+ case S_PW_STATUS:
+ case S_ENDOFLIB:
+ ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0, &nm, sizeof(nm));
+ break;
+ case S_NO_HELLO:
+ leconf->stats.session_rejects_hello++;
+ break;
+ case S_PARM_ADV_MODE:
+ leconf->stats.session_rejects_ad++;
+ break;
+ case S_MAX_PDU_LEN:
+ leconf->stats.session_rejects_max_pdu++;
+ break;
+ case S_PARM_L_RANGE:
+ leconf->stats.session_rejects_lr++;
+ break;
+ case S_BAD_LDP_ID:
+ leconf->stats.bad_ldp_id++;
+ break;
+ case S_BAD_PDU_LEN:
+ leconf->stats.bad_pdu_len++;
+ break;
+ case S_BAD_MSG_LEN:
+ leconf->stats.bad_msg_len++;
+ break;
+ case S_BAD_TLV_LEN:
+ leconf->stats.bad_tlv_len++;
+ break;
+ case S_BAD_TLV_VAL:
+ leconf->stats.malformed_tlv++;
+ break;
+ case S_SHUTDOWN:
+ leconf->stats.shutdown_rcv_notify++;
+ break;
+ default:
+ break;
+ }
+
+ 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));
+}
+
+static int
+gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length,
+ char *tlv_data)
+{
+ struct tlv rtlvs;
+ struct tlv tlv;
+ int err;
+
+ rtlvs.type = htons(TLV_TYPE_RETURNED_TLVS);
+ rtlvs.length = htons(length + TLV_HDR_SIZE);
+ tlv.type = htons(type);
+ tlv.length = htons(length);
+
+ err = ibuf_add(buf, &rtlvs, sizeof(rtlvs));
+ SET_FLAG(err, ibuf_add(buf, &tlv, sizeof(tlv)));
+ SET_FLAG(err, ibuf_add(buf, tlv_data, length));
+
+ return (err);
+}
+
+void
+log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm)
+{
+ if (nm->status_code & STATUS_FATAL) {
+ debug_msg(out, "notification: lsr-id %pI4, status %s (fatal error)", &nbr->id,
+ status_code_name(nm->status_code));
+ return;
+ }
+
+ debug_msg(out, "notification: lsr-id %pI4, status %s",
+ &nbr->id, status_code_name(nm->status_code));
+ if (CHECK_FLAG(nm->flags, F_NOTIF_FEC))
+ debug_msg(out, "notification: fec %s", log_map(&nm->fec));
+ if (CHECK_FLAG(nm->flags, F_NOTIF_PW_STATUS))
+ debug_msg(out, "notification: pw-status %s",
+ (nm->pw_status == PW_FORWARDING) ? "forwarding" : "not forwarding");
+}
diff --git a/ldpd/packet.c b/ldpd/packet.c
new file mode 100644
index 0000000..d2d9305
--- /dev/null
+++ b/ldpd/packet.c
@@ -0,0 +1,802 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+#include "sockopt.h"
+
+static struct iface *disc_find_iface(unsigned int, int, union ldpd_addr *);
+static void session_read(struct event *thread);
+static void session_write(struct event *thread);
+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(struct event *thread);
+
+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 = ldp_rtr_id_get(leconf);
+ 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)
+{
+ union sockunion su;
+
+ 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");
+ }
+
+ addr2sa(af, dst, LDP_PORT, &su);
+ if (sendto(fd, pkt, len, 0, &su.sa, sockaddr_len(&su.sa)) == -1) {
+ log_warn("%s: error sending packet to %s", __func__,
+ log_sockaddr(&su.sa));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* Discovery functions */
+void disc_recv_packet(struct event *thread)
+{
+ int fd = EVENT_FD(thread);
+ struct event **threadp = EVENT_ARG(thread);
+
+ union {
+ struct cmsghdr hdr;
+#ifdef HAVE_STRUCT_SOCKADDR_DL
+ char buf[CMSG_SPACE(sizeof(struct sockaddr_dl))];
+#else
+ char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+#endif
+ } cmsgbuf;
+ struct msghdr m;
+ struct sockaddr_storage from;
+ struct iovec iov;
+ char *buf;
+#ifndef MSG_MCAST
+ struct cmsghdr *cmsg;
+#endif
+ ssize_t r;
+ int multicast;
+ int af;
+ union ldpd_addr src;
+ unsigned int ifindex = 0;
+ struct iface *iface = NULL;
+ 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;
+
+ /* reschedule read */
+ event_add_read(master, disc_recv_packet, threadp, fd, threadp);
+
+ /* 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;
+ }
+
+ sa2addr((struct sockaddr *)&from, &af, &src, NULL);
+#ifdef MSG_MCAST
+ multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0;
+#else
+ multicast = 0;
+ for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; cmsg = CMSG_NXTHDR(&m, cmsg)) {
+#if defined(HAVE_IP_PKTINFO)
+ if (af == AF_INET &&
+ cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_PKTINFO) {
+ struct in_pktinfo *pktinfo;
+
+ pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+ if (IN_MULTICAST(ntohl(pktinfo->ipi_addr.s_addr)))
+ multicast = 1;
+ break;
+ }
+#elif defined(HAVE_IP_RECVDSTADDR)
+ if (af == AF_INET &&
+ cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVDSTADDR) {
+ struct in_addr *addr;
+
+ addr = (struct in_addr *)CMSG_DATA(cmsg);
+ if (IN_MULTICAST(ntohl(addr->s_addr)))
+ multicast = 1;
+ break;
+ }
+#else
+#error "Unsupported socket API"
+#endif
+ if (af == AF_INET6 &&
+ cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo *pktinfo;
+
+ pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+ if (IN6_IS_ADDR_MULTICAST(&pktinfo->ipi6_addr))
+ multicast = 1;
+ break;
+ }
+ }
+#endif /* MSG_MCAST */
+ if (bad_addr(af, &src)) {
+ log_debug("%s: invalid source address: %s", __func__, log_addr(af, &src));
+ return;
+ }
+ ifindex = getsockopt_ifindex(af, &m);
+
+ /* find a matching interface */
+ if (multicast) {
+ iface = disc_find_iface(ifindex, af, &src);
+ 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)
+{
+ struct iface *iface;
+ struct iface_af *ia;
+
+ iface = if_lookup(leconf, ifindex);
+ if (iface == NULL)
+ return (NULL);
+
+ ia = iface_af_get(iface, af);
+ if (!ia->enabled)
+ return (NULL);
+
+ /*
+ * RFC 7552 - Section 5.1:
+ * "Link-local IPv6 address MUST be used as the source IP address in
+ * IPv6 LDP Link Hellos".
+ */
+ if (af == AF_INET6 && !IN6_IS_ADDR_LINKLOCAL(&src->v6))
+ return (NULL);
+
+ return (iface);
+}
+
+void session_accept(struct event *thread)
+{
+ int fd = EVENT_FD(thread);
+ struct sockaddr_storage src;
+ socklen_t len = sizeof(src);
+ int newfd;
+ int af;
+ union ldpd_addr addr;
+ struct nbr *nbr;
+ struct pending_conn *pconn;
+
+ newfd = accept(fd, (struct sockaddr *)&src, &len);
+ 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;
+ }
+ sock_set_nonblock(newfd);
+
+ sa2addr((struct sockaddr *)&src, &af, &addr, NULL);
+
+ /*
+ * 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 %pI4: rejecting additional transport connection", __func__, &nbr->id);
+ close(newfd);
+ return;
+ }
+
+ session_accept_nbr(nbr, newfd);
+}
+
+void
+session_accept_nbr(struct nbr *nbr, int fd)
+{
+#ifdef __OpenBSD__
+ 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;
+ }
+ }
+#endif
+
+ nbr->tcp = tcp_new(fd, nbr);
+ nbr_fsm(nbr, NBR_EVT_MATCH_ADJ);
+}
+
+static void session_read(struct event *thread)
+{
+ int fd = EVENT_FD(thread);
+ struct nbr *nbr = EVENT_ARG(thread);
+ struct tcp_conn *tcp = nbr->tcp;
+ struct ldp_hdr *ldp_hdr;
+ struct ldp_msg *msg;
+ char *buf = NULL, *pdu;
+ ssize_t n, len;
+ uint16_t pdu_len, msg_len, msg_size, max_pdu_len;
+ int ret;
+
+ event_add_read(master, session_read, nbr, fd, &tcp->rev);
+
+ 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_MSG_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_NOTIFICATION:
+ break;
+ default:
+ if (nbr->state != NBR_STA_OPER) {
+ session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type);
+ free(buf);
+ return;
+ }
+ 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_CAPABILITY:
+ ret = recv_capability(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 %pI4",
+ __func__, &nbr->id);
+ if (!(ntohs(msg->type) & UNKNOWN_FLAG)) {
+ nbr->stats.unknown_msg++;
+ send_notification(nbr->tcp, S_UNKNOWN_MSG, msg->id, msg->type);
+ }
+ /* ignore the message */
+ ret = 0;
+ break;
+ }
+
+ if (ret == -1) {
+ /* parser failed, giving up */
+ free(buf);
+ return;
+ }
+
+ /* no errors - update per neighbor message counters */
+ switch (type) {
+ case MSG_TYPE_NOTIFICATION:
+ nbr->stats.notif_rcvd++;
+ break;
+ case MSG_TYPE_KEEPALIVE:
+ nbr->stats.kalive_rcvd++;
+ break;
+ case MSG_TYPE_CAPABILITY:
+ nbr->stats.capability_rcvd++;
+ break;
+ case MSG_TYPE_ADDR:
+ nbr->stats.addr_rcvd++;
+ break;
+ case MSG_TYPE_ADDRWITHDRAW:
+ nbr->stats.addrwdraw_rcvd++;
+ break;
+ case MSG_TYPE_LABELMAPPING:
+ nbr->stats.labelmap_rcvd++;
+ break;
+ case MSG_TYPE_LABELREQUEST:
+ nbr->stats.labelreq_rcvd++;
+ break;
+ case MSG_TYPE_LABELWITHDRAW:
+ nbr->stats.labelwdraw_rcvd++;
+ break;
+ case MSG_TYPE_LABELRELEASE:
+ nbr->stats.labelrel_rcvd++;
+ break;
+ case MSG_TYPE_LABELABORTREQ:
+ nbr->stats.labelabreq_rcvd++;
+ break;
+ default:
+ break;
+ }
+
+ /* Analyse the next message */
+ pdu += msg_size;
+ len -= msg_size;
+ }
+ free(buf);
+ buf = NULL;
+ if (len != 0) {
+ session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
+ return;
+ }
+ }
+
+ /* shouldn't happen, session_get_pdu should be > 0 if buf was
+ * allocated - but let's get rid of the SA warning.
+ */
+ free(buf);
+}
+
+static void session_write(struct event *thread)
+{
+ struct tcp_conn *tcp = EVENT_ARG(thread);
+ struct nbr *nbr = tcp->nbr;
+
+ tcp->wbuf.ev = NULL;
+
+ 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_OFF(nbr->ev_connect);
+ break;
+ case NBR_STA_INITIAL:
+ case NBR_STA_OPENREC:
+ case NBR_STA_OPENSENT:
+ /* update SNMP session counters during initialization */
+ leconf->stats.session_attempts++;
+ send_notification(nbr->tcp, status, msg_id, msg_type);
+
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+ break;
+ case NBR_STA_OPER:
+ send_notification(nbr->tcp, 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 %pI4", __func__, &nbr->id);
+
+ ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_SESSION_CLOSE);
+
+ 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;
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+
+ 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_add_read(master, session_read, nbr, tcp->fd, &tcp->rev);
+ tcp->nbr = nbr;
+ }
+
+ if (getsockname(fd, (struct sockaddr *)&ss, &len) != 0)
+ log_warn("%s: getsockname", __func__);
+ else
+ sa2addr((struct sockaddr *)&ss, NULL, NULL, &tcp->lport);
+ if (getpeername(fd, (struct sockaddr *)&ss, &len) != 0)
+ log_warn("%s: getpeername", __func__);
+ else
+ sa2addr((struct sockaddr *)&ss, NULL, NULL, &tcp->rport);
+
+ 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_OFF(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;
+
+ if ((pconn = calloc(1, sizeof(*pconn))) == NULL)
+ fatal(__func__);
+
+ pconn->fd = fd;
+ pconn->af = af;
+ pconn->addr = *addr;
+ TAILQ_INSERT_TAIL(&global.pending_conns, pconn, entry);
+ pconn->ev_timeout = NULL;
+ event_add_timer(master, pending_conn_timeout, pconn,
+ PENDING_CONN_TIMEOUT, &pconn->ev_timeout);
+
+ return (pconn);
+}
+
+void
+pending_conn_del(struct pending_conn *pconn)
+{
+ EVENT_OFF(pconn->ev_timeout);
+ 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(struct event *thread)
+{
+ struct pending_conn *pconn = EVENT_ARG(thread);
+ struct tcp_conn *tcp;
+
+ pconn->ev_timeout = NULL;
+
+ 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(tcp, S_NO_HELLO, 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 0000000..ae771ca
--- /dev/null
+++ b/ldpd/pfkey.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: ISC
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __OpenBSD__
+#include <sys/types.h>
+#include <sys/socket.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 smask, dmask;
+ union sockunion su_src, su_dst;
+
+ if (!pid)
+ pid = getpid();
+
+ /* we need clean sockaddr... no ports set */
+ memset(&smask, 0, sizeof(smask));
+
+ addr2sa(af, src, 0, &su_src);
+
+ 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 = su_src.sa.sa_family;
+ smask.ss_len = sockaddr_len(&su_src.sa);
+
+ memset(&dmask, 0, sizeof(dmask));
+
+ addr2sa(af, dst, 0, &su_dst);
+
+ 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 = su_dst.sa.sa_family;
+ dmask.ss_len = sockaddr_len(&su_dst.sa);
+
+ 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 = htonl(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(sockaddr_len(&su_src.sa))) / 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(sockaddr_len(&su_dst.sa))) / 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 = &su_dst;
+ iov[iov_cnt].iov_len = ROUNDUP(sockaddr_len(&su_dst.sa));
+ 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 = &su_src;
+ iov[iov_cnt].iov_len = ROUNDUP(sockaddr_len(&su_src.sa));
+ 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 *spi)
+{
+ 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 (spi == 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;
+ *spi = ntohl(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)
+{
+ switch (nbr->auth.method) {
+ case AUTH_MD5SIG:
+ strlcpy(nbr->auth.md5key, nbrp->auth.md5key, sizeof(nbr->auth.md5key));
+ return pfkey_md5sig_establish(nbr, nbrp);
+ case AUTH_NONE:
+ return 0;
+ }
+
+ assert(!"Reached end of function where we are not expecting to");
+}
+
+int
+pfkey_remove(struct nbr *nbr)
+{
+ if (!nbr->auth.established)
+ return 0;
+
+ switch (nbr->auth.method) {
+ case AUTH_MD5SIG:
+ return pfkey_md5sig_remove(nbr);
+ case AUTH_NONE:
+ return 0;
+ break;
+ }
+
+ assert(!"Reached end of function where we are not expecting to");
+}
+
+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);
+}
+#endif /* __OpenBSD__ */
diff --git a/ldpd/rlfa.c b/ldpd/rlfa.c
new file mode 100644
index 0000000..d898a13
--- /dev/null
+++ b/ldpd/rlfa.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "lde.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.h"
+#include "rlfa.h"
+
+#include <lib/log.h>
+
+struct ldp_rlfa_node_head rlfa_node_tree;
+
+static int ldp_rlfa_client_compare(const struct ldp_rlfa_client *a,
+ const struct ldp_rlfa_client *b)
+{
+ if (a->igp.vrf_id < b->igp.vrf_id)
+ return -1;
+ if (a->igp.vrf_id > b->igp.vrf_id)
+ return 1;
+
+ if (a->igp.protocol < b->igp.protocol)
+ return -1;
+ if (a->igp.protocol > b->igp.protocol)
+ return 1;
+
+ if (a->igp.isis.spf.tree_id < b->igp.isis.spf.tree_id)
+ return -1;
+ if (a->igp.isis.spf.tree_id > b->igp.isis.spf.tree_id)
+ return 1;
+
+ if (a->igp.isis.spf.level < b->igp.isis.spf.level)
+ return -1;
+ if (a->igp.isis.spf.level > b->igp.isis.spf.level)
+ return 1;
+
+ return 0;
+}
+RB_GENERATE(ldp_rlfa_client_head, ldp_rlfa_client, entry,
+ ldp_rlfa_client_compare)
+
+static int ldp_rlfa_node_compare(const struct ldp_rlfa_node *a,
+ const struct ldp_rlfa_node *b)
+{
+ if (ntohl(a->pq_address.s_addr) < ntohl(b->pq_address.s_addr))
+ return -1;
+ if (ntohl(a->pq_address.s_addr) > ntohl(b->pq_address.s_addr))
+ return 1;
+
+ return prefix_cmp(&a->destination, &b->destination);
+}
+RB_GENERATE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare)
+
+struct ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp)
+{
+ struct ldp_rlfa_client *rclient;
+
+ if ((rclient = calloc(1, sizeof(*rclient))) == NULL)
+ fatal(__func__);
+
+ rclient->igp = *igp;
+ rclient->node = rnode;
+ RB_INSERT(ldp_rlfa_client_head, &rnode->clients, rclient);
+
+ return rclient;
+}
+
+void rlfa_client_del(struct ldp_rlfa_client *rclient)
+{
+ struct ldp_rlfa_node *rnode = rclient->node;
+
+ RB_REMOVE(ldp_rlfa_client_head, &rnode->clients, rclient);
+ free(rclient);
+
+ /* Delete RLFA node if it's empty. */
+ if (RB_EMPTY(ldp_rlfa_client_head, &rnode->clients))
+ rlfa_node_del(rnode);
+}
+
+struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp)
+{
+ struct ldp_rlfa_client rclient;
+
+ rclient.igp = *igp;
+ return RB_FIND(ldp_rlfa_client_head, &rnode->clients, &rclient);
+}
+
+struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination,
+ struct in_addr pq_address)
+{
+ struct ldp_rlfa_node *rnode;
+
+ if ((rnode = calloc(1, sizeof(*rnode))) == NULL)
+ fatal(__func__);
+
+ rnode->destination = *destination;
+ rnode->pq_address = pq_address;
+ rnode->pq_label = MPLS_INVALID_LABEL;
+ RB_INIT(ldp_rlfa_client_head, &rnode->clients);
+ RB_INSERT(ldp_rlfa_node_head, &rlfa_node_tree, rnode);
+
+ return rnode;
+}
+
+void rlfa_node_del(struct ldp_rlfa_node *rnode)
+{
+ /* Delete RLFA clients. */
+ while (!RB_EMPTY(ldp_rlfa_client_head, &rnode->clients)) {
+ struct ldp_rlfa_client *rclient;
+
+ rclient = RB_ROOT(ldp_rlfa_client_head, &rnode->clients);
+ rlfa_client_del(rclient);
+ }
+
+ RB_REMOVE(ldp_rlfa_node_head, &rlfa_node_tree, rnode);
+ free(rnode);
+}
+
+struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination,
+ struct in_addr pq_address)
+{
+ struct ldp_rlfa_node rnode = {};
+
+ rnode.destination = *destination;
+ rnode.pq_address = pq_address;
+ return RB_FIND(ldp_rlfa_node_head, &rlfa_node_tree, &rnode);
+}
+
+void lde_rlfa_client_send(struct ldp_rlfa_client *rclient)
+{
+ struct ldp_rlfa_node *rnode = rclient->node;
+ struct zapi_rlfa_response rlfa_labels = {};
+ struct fec fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ int i = 0;
+
+ /* Fill in inner label (allocated by PQ node). */
+ rlfa_labels.igp = rclient->igp;
+ rlfa_labels.destination = rnode->destination;
+ rlfa_labels.pq_label = rnode->pq_label;
+
+ /* Fill in outer label(s) (allocated by the nexthop routers). */
+ fec.type = FEC_TYPE_IPV4;
+ fec.u.ipv4.prefix = rnode->pq_address;
+ fec.u.ipv4.prefixlen = IPV4_MAX_BITLEN;
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (!fn)
+ return;
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ if (fnh->remote_label == NO_LABEL)
+ continue;
+
+ rlfa_labels.nexthops[i].family = fnh->af;
+ switch (fnh->af) {
+ case AF_INET:
+ rlfa_labels.nexthops[i].gate.ipv4 = fnh->nexthop.v4;
+ break;
+ case AF_INET6:
+ rlfa_labels.nexthops[i].gate.ipv6 = fnh->nexthop.v6;
+ break;
+ default:
+ continue;
+ }
+ rlfa_labels.nexthops[i].label = fnh->remote_label;
+ i++;
+ }
+ rlfa_labels.nexthop_num = i;
+
+ lde_imsg_compose_parent(IMSG_RLFA_LABELS, 0, &rlfa_labels,
+ sizeof(rlfa_labels));
+}
+
+void lde_rlfa_label_update(const struct fec *fec)
+{
+ struct ldp_rlfa_node *rnode;
+
+ if (fec->type != FEC_TYPE_IPV4
+ || fec->u.ipv4.prefixlen != IPV4_MAX_BITLEN)
+ return;
+
+ /*
+ * TODO: use an rb-tree lookup to restrict the iteration to the RLFAs
+ * that were effectivelly affected by the label update.
+ */
+ RB_FOREACH (rnode, ldp_rlfa_node_head, &rlfa_node_tree) {
+ struct ldp_rlfa_client *rclient;
+
+ if (!IPV4_ADDR_SAME(&rnode->pq_address, &fec->u.ipv4.prefix))
+ continue;
+
+ RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients)
+ lde_rlfa_client_send(rclient);
+ }
+}
+
+void lde_rlfa_check(struct ldp_rlfa_client *rclient)
+{
+ struct lde_nbr *ln;
+ struct lde_map *me;
+ struct fec fec;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ ln = lde_nbr_find_by_addr(AF_INET, &pq_address);
+ if (!ln)
+ return;
+
+ lde_prefix2fec(&rclient->node->destination, &fec);
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fec);
+ if (!me)
+ return;
+
+ rclient->node->pq_label = me->map.label;
+ lde_rlfa_client_send(rclient);
+}
+
+/*
+ * Check if there's any registered RLFA client for this prefix/neighbor (PQ
+ * node) and notify about the updated label.
+ */
+void lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln,
+ uint32_t label)
+{
+ struct prefix rlfa_dest;
+ struct ldp_rlfa_node *rnode;
+
+ lde_fec2prefix(fec, &rlfa_dest);
+ rnode = rlfa_node_find(&rlfa_dest, ln->id);
+ if (rnode) {
+ struct ldp_rlfa_client *rclient;
+
+ rnode->pq_label = label;
+ RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients)
+ lde_rlfa_client_send(rclient);
+ } else
+ lde_rlfa_label_update(fec);
+}
+
+void ldpe_rlfa_init(struct ldp_rlfa_client *rclient)
+{
+ struct tnbr *tnbr;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ tnbr = tnbr_find(leconf, AF_INET, &pq_address);
+ if (tnbr == NULL) {
+ tnbr = tnbr_new(AF_INET, &pq_address);
+ tnbr_update(tnbr);
+ RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr);
+ }
+
+ tnbr->rlfa_count++;
+}
+
+void ldpe_rlfa_exit(struct ldp_rlfa_client *rclient)
+{
+ struct tnbr *tnbr;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ tnbr = tnbr_find(leconf, AF_INET, &pq_address);
+ if (tnbr) {
+ tnbr->rlfa_count--;
+ tnbr_check(leconf, tnbr);
+ }
+}
diff --git a/ldpd/rlfa.h b/ldpd/rlfa.h
new file mode 100644
index 0000000..4083742
--- /dev/null
+++ b/ldpd/rlfa.h
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#ifndef _LDPD_RLFA_H_
+#define _LDPD_RLFA_H_
+
+#include "openbsd-tree.h"
+#include "zclient.h"
+
+struct ldp_rlfa_client {
+ RB_ENTRY(ldp_rlfa_client) entry;
+
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Backpointer to RLFA node. */
+ struct ldp_rlfa_node *node;
+};
+RB_HEAD(ldp_rlfa_client_head, ldp_rlfa_client);
+RB_PROTOTYPE(ldp_rlfa_client_head, ldp_rlfa_client, entry,
+ ldp_rlfa_client_compare);
+
+struct ldp_rlfa_node {
+ RB_ENTRY(ldp_rlfa_node) entry;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* PQ node address. */
+ struct in_addr pq_address;
+
+ /* RLFA clients. */
+ struct ldp_rlfa_client_head clients;
+
+ /* Label allocated by the PQ node to the RLFA destination. */
+ mpls_label_t pq_label;
+};
+RB_HEAD(ldp_rlfa_node_head, ldp_rlfa_node);
+RB_PROTOTYPE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare);
+
+extern struct ldp_rlfa_node_head rlfa_node_tree;
+
+/* prototypes */
+struct ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp);
+void rlfa_client_del(struct ldp_rlfa_client *rclient);
+struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp);
+struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination,
+ struct in_addr pq_address);
+void rlfa_node_del(struct ldp_rlfa_node *rnode);
+struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination,
+ struct in_addr pq_address);
+void lde_rlfa_check(struct ldp_rlfa_client *rclient);
+void lde_rlfa_client_send(struct ldp_rlfa_client *rclient);
+void lde_rlfa_label_update(const struct fec *fec);
+void lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln,
+ uint32_t label);
+void ldpe_rlfa_init(struct ldp_rlfa_client *rclient);
+void ldpe_rlfa_exit(struct ldp_rlfa_client *rclient);
+
+#endif /* _LDPD_RLFA_H_ */
diff --git a/ldpd/socket.c b/ldpd/socket.c
new file mode 100644
index 0000000..6b7e475
--- /dev/null
+++ b/ldpd/socket.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "log.h"
+
+#include "lib/log.h"
+#include "privs.h"
+#include "sockopt.h"
+
+extern struct zebra_privs_t ldpd_privs;
+extern struct zebra_privs_t ldpe_privs;
+
+int
+ldp_create_socket(int af, enum socket_type type)
+{
+ int fd, domain, proto;
+ union ldpd_addr addr;
+ union sockunion local_su;
+#ifdef __OpenBSD__
+ int opt;
+#endif
+
+ /* 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, proto);
+ if (fd == -1) {
+ log_warn("%s: error creating socket", __func__);
+ return (-1);
+ }
+ sock_set_nonblock(fd);
+ sockopt_v6only(af, fd);
+
+ /* bind to a local address/port */
+ switch (type) {
+ case LDP_SOCKET_DISC:
+ /* listen on all addresses */
+ memset(&addr, 0, sizeof(addr));
+ addr2sa(af, &addr, LDP_PORT, &local_su);
+ break;
+ case LDP_SOCKET_EDISC:
+ case LDP_SOCKET_SESSION:
+ addr = (ldp_af_conf_get(ldpd_conf, af))->trans_addr;
+ addr2sa(af, &addr, LDP_PORT, &local_su);
+ /* ignore any possible error */
+ sock_set_bindany(fd, 1);
+ break;
+ }
+ frr_with_privs(&ldpd_privs) {
+ if (sock_set_reuse(fd, 1) == -1) {
+ close(fd);
+ return (-1);
+ }
+ if (bind(fd, &local_su.sa, sockaddr_len(&local_su.sa)) == -1) {
+ log_warnx("%s: error binding socket: %s", __func__,
+ safe_strerror(errno));
+ 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);
+ }
+#ifndef MSG_MCAST
+#if defined(HAVE_IP_PKTINFO)
+ if (sock_set_ipv4_pktinfo(fd, 1) == -1) {
+ close(fd);
+ return (-1);
+ }
+#elif defined(HAVE_IP_RECVDSTADDR)
+ if (sock_set_ipv4_recvdstaddr(fd, 1) == -1) {
+ close(fd);
+ return (-1);
+ }
+#else
+#error "Unsupported socket API"
+#endif
+#endif /* MSG_MCAST */
+ }
+ 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 (!CHECK_FLAG(ldpd_conf->ipv6.flags, F_LDPD_AF_NO_GTSM)) {
+ /* ignore any possible error */
+ sock_set_ipv6_minhopcount(fd, 255);
+ }
+ }
+ 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__);
+
+#ifdef __OpenBSD__
+ 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);
+ }
+ }
+#endif
+ break;
+ }
+
+ return (fd);
+}
+
+void
+sock_set_nonblock(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fcntl F_GETFL");
+
+ SET_FLAG(flags, O_NONBLOCK);
+
+ if (fcntl(fd, F_SETFL, flags) == -1)
+ fatal("fcntl F_SETFL");
+}
+
+void
+sock_set_cloexec(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFD, 0)) == -1)
+ fatal("fcntl F_GETFD");
+
+ SET_FLAG(flags, FD_CLOEXEC);
+
+ if (fcntl(fd, F_SETFD, flags) == -1)
+ fatal("fcntl F_SETFD");
+}
+
+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)
+{
+#ifdef HAVE_SO_BINDANY
+ frr_with_privs(&ldpd_privs) {
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable, sizeof(int)) < 0) {
+ log_warn("%s: error setting SO_BINDANY", __func__);
+ return (-1);
+ }
+ }
+ return (0);
+#elif defined(HAVE_IP_FREEBIND)
+ if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &enable, sizeof(int)) < 0) {
+ log_warn("%s: error setting IP_FREEBIND", __func__);
+ return (-1);
+ }
+ return (0);
+#elif defined(IP_BINDANY)
+ frr_with_privs(&ldpd_privs) {
+ if (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &enable, sizeof(int)) < 0) {
+ log_warn("%s: error setting IP_BINDANY", __func__);
+ return (-1);
+ }
+ }
+ return (0);
+#else
+ log_warnx(
+ "%s: missing SO_BINDANY, IP_FREEBIND and IP_BINDANY, unable to bind to a nonlocal IP address",
+ __func__);
+ return (-1);
+#endif /* HAVE_SO_BINDANY */
+}
+
+#ifndef __OpenBSD__
+/*
+ * Set MD5 key for the socket, for the given peer address. If the password
+ * is NULL or zero-length, the option will be disabled.
+ */
+int
+sock_set_md5sig(int fd, int af, union ldpd_addr *addr, const char *password)
+{
+ int ret = -1;
+ int save_errno = ENOSYS;
+#if HAVE_DECL_TCP_MD5SIG
+ union sockunion su;
+#endif
+
+ if (fd == -1)
+ return (0);
+#if HAVE_DECL_TCP_MD5SIG
+ addr2sa(af, addr, 0, &su);
+
+ frr_with_privs(&ldpe_privs) {
+ ret = sockopt_tcp_signature(fd, &su, password);
+ save_errno = errno;
+ }
+#endif /* HAVE_TCP_MD5SIG */
+ if (ret < 0)
+ log_warnx("%s: can't set TCP_MD5SIG option on fd %d: %s",
+ __func__, fd, safe_strerror(save_errno));
+
+ return (ret);
+}
+#endif
+
+int
+sock_set_ipv4_tos(int fd, int tos)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, &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, ifindex_t enable)
+{
+ return (setsockopt_ifindex(AF_INET, fd, enable));
+}
+
+int
+sock_set_ipv4_minttl(int fd, int ttl)
+{
+ return (sockopt_minttl(AF_INET, fd, ttl));
+}
+
+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);
+}
+
+#ifndef MSG_MCAST
+#if defined(HAVE_IP_PKTINFO)
+int
+sock_set_ipv4_pktinfo(int fd, int enable)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable)) < 0) {
+ log_warn("%s: error setting IP_PKTINFO", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+#elif defined(HAVE_IP_RECVDSTADDR)
+int
+sock_set_ipv4_recvdstaddr(int fd, int enable)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &enable, sizeof(enable)) < 0) {
+ log_warn("%s: error setting IP_RECVDSTADDR", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+#else
+#error "Unsupported socket API"
+#endif
+#endif /* MSG_MCAST */
+
+int
+sock_set_ipv4_mcast(struct iface *iface)
+{
+ struct in_addr if_addr;
+
+ if_addr.s_addr = if_get_ipv4_addr(iface);
+
+ if (setsockopt_ipv4_multicast_if(global.ipv4.ldp_disc_socket,
+ if_addr, iface->ifindex) < 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)
+{
+ return (setsockopt_ipv4_multicast_loop(fd, 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)
+{
+ return (sockopt_minttl(AF_INET6, fd, hoplimit));
+}
+
+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/subdir.am b/ldpd/subdir.am
new file mode 100644
index 0000000..0b948ad
--- /dev/null
+++ b/ldpd/subdir.am
@@ -0,0 +1,68 @@
+#
+# ldpd
+#
+
+if LDPD
+noinst_LIBRARIES += ldpd/libldp.a
+sbin_PROGRAMS += ldpd/ldpd
+vtysh_daemons += ldpd
+man8 += $(MANBUILD)/frr-ldpd.8
+endif
+
+ldpd_libldp_a_SOURCES = \
+ ldpd/accept.c \
+ ldpd/address.c \
+ ldpd/adjacency.c \
+ ldpd/control.c \
+ ldpd/hello.c \
+ ldpd/init.c \
+ ldpd/interface.c \
+ ldpd/keepalive.c \
+ ldpd/l2vpn.c \
+ ldpd/labelmapping.c \
+ ldpd/lde.c \
+ ldpd/lde_lib.c \
+ ldpd/ldp_debug.c \
+ ldpd/ldp_vty_cmds.c \
+ ldpd/ldp_vty_conf.c \
+ ldpd/ldp_vty_exec.c \
+ ldpd/ldp_zebra.c \
+ ldpd/ldpe.c \
+ ldpd/log.c \
+ ldpd/logmsg.c \
+ ldpd/neighbor.c \
+ ldpd/notification.c \
+ ldpd/packet.c \
+ ldpd/pfkey.c \
+ ldpd/rlfa.c \
+ ldpd/socket.c \
+ ldpd/util.c \
+ # end
+
+if SNMP
+module_LTLIBRARIES += ldpd/ldpd_snmp.la
+endif
+
+clippy_scan += \
+ ldpd/ldp_vty_cmds.c \
+ # end
+
+noinst_HEADERS += \
+ ldpd/control.h \
+ ldpd/lde.h \
+ ldpd/ldp.h \
+ ldpd/ldp_debug.h \
+ ldpd/ldp_vty.h \
+ ldpd/ldpd.h \
+ ldpd/ldpe.h \
+ ldpd/log.h \
+ ldpd/rlfa.h \
+ # end
+
+ldpd_ldpd_SOURCES = ldpd/ldpd.c
+ldpd_ldpd_LDADD = ldpd/libldp.a lib/libfrr.la $(LIBCAP)
+
+ldpd_ldpd_snmp_la_SOURCES = ldpd/ldp_snmp.c
+ldpd_ldpd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
+ldpd_ldpd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
+ldpd_ldpd_snmp_la_LIBADD = lib/libfrrsnmp.la
diff --git a/ldpd/util.c b/ldpd/util.c
new file mode 100644
index 0000000..85ba25c
--- /dev/null
+++ b/ldpd/util.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: ISC
+/* $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>
+ */
+
+#include <zebra.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 + sockaddr_len((struct sockaddr *)sa_in6);
+ 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 > IPV4_MAX_BITLEN)
+ 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 > IPV6_MAX_BITLEN)
+ 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 embedded 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;
+ }
+}
+
+void
+addr2sa(int af, const union ldpd_addr *addr, uint16_t port, union sockunion *su)
+{
+ struct sockaddr_in *sa_in = &su->sin;
+ struct sockaddr_in6 *sa_in6 = &su->sin6;
+
+ memset(su, 0, sizeof(*su));
+ switch (af) {
+ case AF_INET:
+ sa_in->sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sa_in->sin_len = sizeof(struct sockaddr_in);
+#endif
+ sa_in->sin_addr = addr->v4;
+ sa_in->sin_port = htons(port);
+ break;
+ case AF_INET6:
+ sa_in6->sin6_family = AF_INET6;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sa_in6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ sa_in6->sin6_addr = addr->v6;
+ sa_in6->sin6_port = htons(port);
+ break;
+ default:
+ fatalx("addr2sa: unknown af");
+ }
+}
+
+void
+sa2addr(struct sockaddr *sa, int *af, union ldpd_addr *addr, in_port_t *port)
+{
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)sa;
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa;
+
+ if (addr)
+ memset(addr, 0, sizeof(*addr));
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (af)
+ *af = AF_INET;
+ if (addr)
+ addr->v4 = sa_in->sin_addr;
+ if (port)
+ *port = sa_in->sin_port;
+ break;
+ case AF_INET6:
+ if (af)
+ *af = AF_INET6;
+ if (addr)
+ addr->v6 = sa_in6->sin6_addr;
+ if (port)
+ *port = sa_in6->sin6_port;
+ break;
+ default:
+ fatalx("sa2addr: unknown af");
+ }
+}
+
+socklen_t
+sockaddr_len(struct sockaddr *sa)
+{
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ return (sa->sa_len);
+#else
+ switch (sa->sa_family) {
+ case AF_INET:
+ return (sizeof(struct sockaddr_in));
+ case AF_INET6:
+ return (sizeof(struct sockaddr_in6));
+ default:
+ fatalx("sockaddr_len: unknown af");
+ }
+#endif
+}