diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
commit | 2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch) | |
tree | c05dc0f8e6aa3accc84e3e5cffc933ed94941383 /ldpd | |
parent | Initial commit. (diff) | |
download | frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.tar.xz frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.zip |
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ldpd')
-rw-r--r-- | ldpd/.gitignore | 2 | ||||
-rw-r--r-- | ldpd/Makefile | 10 | ||||
-rw-r--r-- | ldpd/accept.c | 130 | ||||
-rw-r--r-- | ldpd/address.c | 425 | ||||
-rw-r--r-- | ldpd/adjacency.c | 394 | ||||
-rw-r--r-- | ldpd/control.c | 289 | ||||
-rw-r--r-- | ldpd/control.h | 37 | ||||
-rw-r--r-- | ldpd/hello.c | 602 | ||||
-rw-r--r-- | ldpd/init.c | 434 | ||||
-rw-r--r-- | ldpd/interface.c | 983 | ||||
-rw-r--r-- | ldpd/keepalive.c | 63 | ||||
-rw-r--r-- | ldpd/l2vpn.c | 700 | ||||
-rw-r--r-- | ldpd/labelmapping.c | 915 | ||||
-rw-r--r-- | ldpd/lde.c | 2535 | ||||
-rw-r--r-- | ldpd/lde.h | 265 | ||||
-rw-r--r-- | ldpd/lde_lib.c | 1080 | ||||
-rw-r--r-- | ldpd/ldp.h | 333 | ||||
-rw-r--r-- | ldpd/ldp_debug.c | 211 | ||||
-rw-r--r-- | ldpd/ldp_debug.h | 156 | ||||
-rw-r--r-- | ldpd/ldp_snmp.c | 1237 | ||||
-rw-r--r-- | ldpd/ldp_vty.h | 87 | ||||
-rw-r--r-- | ldpd/ldp_vty_cmds.c | 914 | ||||
-rw-r--r-- | ldpd/ldp_vty_conf.c | 1658 | ||||
-rw-r--r-- | ldpd/ldp_vty_exec.c | 2163 | ||||
-rw-r--r-- | ldpd/ldp_zebra.c | 733 | ||||
-rw-r--r-- | ldpd/ldpd.c | 2002 | ||||
-rw-r--r-- | ldpd/ldpd.h | 922 | ||||
-rw-r--r-- | ldpd/ldpe.c | 1074 | ||||
-rw-r--r-- | ldpd/ldpe.h | 324 | ||||
-rw-r--r-- | ldpd/log.c | 149 | ||||
-rw-r--r-- | ldpd/log.h | 47 | ||||
-rw-r--r-- | ldpd/logmsg.c | 510 | ||||
-rw-r--r-- | ldpd/neighbor.c | 876 | ||||
-rw-r--r-- | ldpd/notification.c | 397 | ||||
-rw-r--r-- | ldpd/packet.c | 828 | ||||
-rw-r--r-- | ldpd/pfkey.c | 469 | ||||
-rw-r--r-- | ldpd/rlfa.c | 288 | ||||
-rw-r--r-- | ldpd/rlfa.h | 78 | ||||
-rw-r--r-- | ldpd/socket.c | 499 | ||||
-rw-r--r-- | ldpd/subdir.am | 69 | ||||
-rw-r--r-- | ldpd/util.c | 382 |
41 files changed, 25270 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..1a04c56 --- /dev/null +++ b/ldpd/accept.c @@ -0,0 +1,130 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Claudio Jeker <claudio@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <zebra.h> + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +struct accept_ev { + LIST_ENTRY(accept_ev) entry; + struct thread *ev; + void (*accept_cb)(struct thread *); + void *arg; + int fd; +}; + +struct { + LIST_HEAD(, accept_ev) queue; + struct thread *evt; +} accept_queue; + +static void accept_arm(void); +static void accept_unarm(void); +static void accept_cb(struct thread *); +static void accept_timeout(struct thread *); + +void +accept_init(void) +{ + LIST_INIT(&accept_queue.queue); +} + +int accept_add(int fd, void (*cb)(struct thread *), 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); + + thread_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); + THREAD_OFF(av->ev); + LIST_REMOVE(av, entry); + free(av); + return; + } +} + +void +accept_pause(void) +{ + log_debug(__func__); + accept_unarm(); + thread_add_timer(master, accept_timeout, NULL, 1, &accept_queue.evt); +} + +void +accept_unpause(void) +{ + if (accept_queue.evt != NULL) { + log_debug(__func__); + THREAD_OFF(accept_queue.evt); + accept_arm(); + } +} + +static void +accept_arm(void) +{ + struct accept_ev *av; + LIST_FOREACH(av, &accept_queue.queue, entry) { + thread_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) + THREAD_OFF(av->ev); +} + +static void accept_cb(struct thread *thread) +{ + struct accept_ev *av = THREAD_ARG(thread); + thread_add_read(master, accept_cb, av, av->fd, &av->ev); + av->accept_cb(thread); +} + +static void accept_timeout(struct thread *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..c3e2735 --- /dev/null +++ b/ldpd/address.c @@ -0,0 +1,425 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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__); + + err |= gen_ldp_hdr(buf, size); + size -= LDP_HDR_SIZE; + err |= gen_msg_hdr(buf, msg_type, size); + size -= LDP_MSG_SIZE; + err |= gen_address_list_tlv(buf, 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; + err |= gen_msg_hdr(buf, MSG_TYPE_ADDRWITHDRAW, size); + err |= gen_address_list_tlv(buf, AF_INET, NULL, 0); + err |= gen_fec_tlv(buf, fec); + 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); + + err |= ibuf_add(buf, &alt, sizeof(alt)); + if (addr_list == NULL) + return (err); + + LIST_FOREACH(if_addr, addr_list, entry) { + err |= ibuf_add(buf, &if_addr->addr, addr_size); + if (--tlv_addr_count == 0) + break; + } + + return (err); +} + +static 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) + 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..8caa3c4 --- /dev/null +++ b/ldpd/adjacency.c @@ -0,0 +1,394 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 thread *); +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 thread *); +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 thread *thread) +{ + struct adj *adj = THREAD_ARG(thread); + + adj->inactivity_timer = NULL; + + log_debug("%s: lsr-id %pI4", __func__, &adj->lsr_id); + + if (adj->source.type == HELLO_TARGETED) { + if (!(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) +{ + THREAD_OFF(adj->inactivity_timer); + adj->inactivity_timer = NULL; + thread_add_timer(master, adj_itimer, adj, adj->holdtime, + &adj->inactivity_timer); +} + +void +adj_stop_itimer(struct adj *adj) +{ + THREAD_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 (!(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 thread *thread) +{ + struct tnbr *tnbr = THREAD_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) +{ + THREAD_OFF(tnbr->hello_timer); + tnbr->hello_timer = NULL; + thread_add_timer(master, tnbr_hello_timer, tnbr, tnbr_get_hello_interval(tnbr), + &tnbr->hello_timer); +} + +static void +tnbr_stop_hello_timer(struct tnbr *tnbr) +{ + THREAD_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 = + thread_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..09e9121 --- /dev/null +++ b/ldpd/control.c @@ -0,0 +1,289 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 thread *); +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 thread *); + +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 thread *thread) +{ + int connfd; + socklen_t len; + struct sockaddr_un s_un; + struct ctl_conn *c; + + len = sizeof(s_un); + if ((connfd = accept(THREAD_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; + thread_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); + + THREAD_OFF(c->iev.ev_read); + THREAD_OFF(c->iev.ev_write); + close(c->iev.ibuf.fd); + accept_unpause(); + free(c); +} + +/* ARGSUSED */ +static void control_dispatch_imsg(struct thread *thread) +{ + int fd = THREAD_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..23edb5f --- /dev/null +++ b/ldpd/control.h @@ -0,0 +1,37 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CONTROL_H_ +#define _CONTROL_H_ + +#include "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..629fe46 --- /dev/null +++ b/ldpd/hello.c @@ -0,0 +1,602 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 (!(leconf->ipv4.flags & F_LDPD_AF_NO_GTSM)) + flags |= F_HELLO_GTSM; + dst.v4 = global.mcast_addr_v4; + break; + case AF_INET6: + dst.v6 = global.mcast_addr_v6; + break; + default: + fatalx("send_hello: unknown af"); + } + break; + case HELLO_TARGETED: + af = tnbr->af; + holdtime = tnbr_get_hello_holdtime(tnbr); + flags = F_HELLO_TARGETED; + if ((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__); + + err |= gen_ldp_hdr(buf, size); + size -= LDP_HDR_SIZE; + err |= gen_msg_hdr(buf, MSG_TYPE_HELLO, size); + err |= gen_hello_prms_tlv(buf, holdtime, flags); + + /* + * RFC 7552 - Section 6.1: + * "An LSR MUST include only the transport address whose address + * family is the same as that of the IP packet carrying the Hello + * message". + */ + switch (af) { + case AF_INET: + err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_IPV4TRANSADDR, + leconf->ipv4.trans_addr.v4.s_addr); + break; + case AF_INET6: + err |= gen_opt16_hello_prms_tlv(buf, TLV_TYPE_IPV6TRANSADDR, + leconf->ipv6.trans_addr.v6.s6_addr); + break; + default: + fatalx("send_hello: unknown af"); + } + + err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_CONFIG, + htonl(global.conf_seqnum)); + + /* + * RFC 7552 - Section 6.1.1: + * "A Dual-stack LSR (i.e., an LSR supporting Dual-stack LDP for a peer) + * MUST include the Dual-Stack capability TLV in all of its LDP Hellos". + */ + if (ldp_is_dual_stack(leconf)) + err |= gen_ds_hello_prms_tlv(buf, leconf->trans_pref); + + if (err) { + ibuf_free(buf); + return (-1); + } + + 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 && (flags & F_HELLO_TARGETED)) { + log_debug("%s: lsr-id %pI4: multicast targeted hello", __func__, + &lsr_id); + return; + } + if (!multicast && !((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 = (tlvs_rcvd & F_HELLO_TLV_RCVD_DS) ? 1 : 0; + + /* implicit transport address */ + if (!(tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR)) + trans_addr = *src; + if (bad_addr(af, &trans_addr)) { + log_debug("%s: lsr-id %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 (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 (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 && (tnbr->flags & F_TNBR_DYNAMIC) && + !((flags & F_HELLO_REQ_TARG))) { + tnbr->flags &= ~F_TNBR_DYNAMIC; + tnbr = tnbr_check(leconf, tnbr); + } + + if (!tnbr) { + struct ldpd_af_conf *af_conf; + + if (!(flags & F_HELLO_REQ_TARG)) + return; + af_conf = ldp_af_conf_get(leconf, af); + if (!(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); + 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 (flags & F_HELLO_GTSM) + nbr->flags |= F_NBR_GTSM_NEGOTIATED; + else + nbr->flags &= ~F_NBR_GTSM_NEGOTIATED; + } + + /* update neighbor's configuration sequence number */ + if (nbr && (tlvs_rcvd & F_HELLO_TLV_RCVD_CONF)) { + if (conf_seqnum > nbr->conf_seqnum && + nbr_pending_idtimer(nbr)) + nbr_stop_idtimer(nbr); + nbr->conf_seqnum = conf_seqnum; + } + + /* always update the holdtime to properly handle runtime changes */ + switch (source.type) { + case HELLO_LINK: + if (holdtime == 0) + holdtime = LINK_DFLT_HOLDTIME; + + adj->holdtime = MIN(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 (leconf->flags & F_LDPD_DS_CISCO_INTEROP) + value = htonl(value); + else + value = htonl(value << 28); + + return (gen_opt4_hello_prms_tlv(buf, TLV_TYPE_DUALSTACK, value)); +} + +static int +tlv_decode_hello_prms(char *buf, uint16_t len, uint16_t *holdtime, + uint16_t *flags) +{ + struct hello_prms_tlv tlv; + + if (len < sizeof(tlv)) + return (-1); + memcpy(&tlv, buf, sizeof(tlv)); + + if (tlv.type != htons(TLV_TYPE_COMMONHELLO)) + return (-1); + if (ntohs(tlv.length) != sizeof(tlv) - TLV_HDR_SIZE) + return (-1); + + *holdtime = ntohs(tlv.holdtime); + *flags = ntohs(tlv.flags); + + return (sizeof(tlv)); +} + +static int +tlv_decode_opt_hello_prms(char *buf, uint16_t len, int *tlvs_rcvd, int af, + union ldpd_addr *addr, uint32_t *conf_number, uint16_t *trans_pref) +{ + struct tlv tlv; + uint16_t tlv_len; + int total = 0; + + *tlvs_rcvd = 0; + memset(addr, 0, sizeof(*addr)); + *conf_number = 0; + *trans_pref = 0; + + /* + * RFC 7552 - Section 6.1: + * "An LSR SHOULD accept the Hello message that contains both IPv4 and + * IPv6 Transport Address optional objects but MUST use only the + * transport address whose address family is the same as that of the + * IP packet carrying the Hello message. An LSR SHOULD accept only + * the first Transport Address optional object for a given address + * family in the received Hello message and ignore the rest if the + * LSR receives more than one Transport Address optional object for a + * given address family". + */ + while (len >= sizeof(tlv)) { + memcpy(&tlv, buf, TLV_HDR_SIZE); + tlv_len = ntohs(tlv.length); + if (tlv_len + TLV_HDR_SIZE > len) + return (-1); + buf += TLV_HDR_SIZE; + len -= TLV_HDR_SIZE; + total += TLV_HDR_SIZE; + + switch (ntohs(tlv.type)) { + case TLV_TYPE_IPV4TRANSADDR: + if (tlv_len != sizeof(addr->v4)) + return (-1); + if (af != AF_INET) + return (-1); + if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR) + break; + memcpy(&addr->v4, buf, sizeof(addr->v4)); + *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR; + break; + case TLV_TYPE_IPV6TRANSADDR: + if (tlv_len != sizeof(addr->v6)) + return (-1); + if (af != AF_INET6) + return (-1); + if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR) + break; + memcpy(&addr->v6, buf, sizeof(addr->v6)); + *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR; + break; + case TLV_TYPE_CONFIG: + if (tlv_len != sizeof(uint32_t)) + return (-1); + memcpy(conf_number, buf, sizeof(uint32_t)); + *tlvs_rcvd |= F_HELLO_TLV_RCVD_CONF; + break; + case TLV_TYPE_DUALSTACK: + if (tlv_len != sizeof(uint32_t)) + return (-1); + /* + * RFC 7552 - Section 6.1: + * "A Single-stack LSR does not need to use the + * Dual-Stack capability in Hello messages and SHOULD + * ignore this capability if received". + */ + if (!ldp_is_dual_stack(leconf)) + break; + /* Shame on you, Cisco! */ + if (leconf->flags & F_LDPD_DS_CISCO_INTEROP) { + memcpy(trans_pref, buf + sizeof(uint16_t), + sizeof(uint16_t)); + *trans_pref = ntohs(*trans_pref); + } else { + memcpy(trans_pref, buf , sizeof(uint16_t)); + *trans_pref = ntohs(*trans_pref) >> 12; + } + *tlvs_rcvd |= F_HELLO_TLV_RCVD_DS; + break; + default: + /* if unknown flag set, ignore TLV */ + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + return (-1); + break; + } + buf += tlv_len; + len -= tlv_len; + total += tlv_len; + } + + return (total); +} diff --git a/ldpd/init.c b/ldpd/init.c new file mode 100644 index 0000000..0ee64c1 --- /dev/null +++ b/ldpd/init.c @@ -0,0 +1,434 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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__); + + err |= gen_ldp_hdr(buf, size); + size -= LDP_HDR_SIZE; + err |= gen_msg_hdr(buf, MSG_TYPE_INIT, size); + err |= gen_init_prms_tlv(buf, nbr); + err |= gen_cap_dynamic_tlv(buf); + err |= gen_cap_twcard_tlv(buf, 1); + 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 (caps_rcvd & F_CAP_TLV_RCVD_DYNAMIC) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_DYNAMIC; + + 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 (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_TWCARD; + + 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 (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF; + + nbr->flags |= F_NBR_CAP_UNOTIF; + + log_debug("%s: lsr-id %pI4 announced the Unrecognized Notification capability", __func__, + &nbr->id); + break; + default: + if (!(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__); + + err |= gen_ldp_hdr(buf, size); + size -= LDP_HDR_SIZE; + err |= gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size); + + switch (capability) { + case TLV_TYPE_TWCARD_CAP: + err |= gen_cap_twcard_tlv(buf, enable); + break; + case TLV_TYPE_UNOTIF_CAP: + 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 (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_TWCARD; + + memcpy(&reserved, buf, sizeof(reserved)); + enable = reserved & STATE_BIT; + if (enable) + nbr->flags |= F_NBR_CAP_TWCARD; + else + 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 (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF; + + memcpy(&reserved, buf, sizeof(reserved)); + enable = reserved & STATE_BIT; + if (enable) + nbr->flags |= F_NBR_CAP_UNOTIF; + else + 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 (!(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..392e254 --- /dev/null +++ b/ldpd/interface.c @@ -0,0 +1,983 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 thread *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 thread *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 (kif->flags & IFF_POINTOPOINT) + iface->type = IF_TYPE_POINTOPOINT; + if (kif->flags & IFF_BROADCAST && + 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 thread *thread) +{ + struct iface_af *ia = THREAD_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) +{ + THREAD_OFF(ia->hello_timer); + thread_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) +{ + THREAD_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 = + thread_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 thread *thread) +{ + struct iface *iface = THREAD_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; + + THREAD_OFF(iface->ldp_sync.wait_for_sync_timer); + thread_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) +{ + THREAD_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..b031271 --- /dev/null +++ b/ldpd/keepalive.c @@ -0,0 +1,63 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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..2c68f3e --- /dev/null +++ b/ldpd/l2vpn.c @@ -0,0 +1,700 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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; + 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 (pw->flags & F_PW_CWORD_CONF) + pw->flags |= F_PW_CWORD; + else + pw->flags &= ~F_PW_CWORD; + + if (pw->flags & F_PW_STATUSTLV_CONF) + pw->flags |= F_PW_STATUSTLV; + else + pw->flags &= ~F_PW_STATUSTLV; + + if (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 ((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 ((map->flags & F_MAP_PW_CWORD) && + !(pw->flags & F_PW_CWORD_CONF)) { + /* ignore the received label mapping */ + return (1); + } else if (!(map->flags & F_MAP_PW_CWORD) && + (pw->flags & F_PW_CWORD_CONF)) { + /* append a "Wrong C-bit" status code */ + st.status_code = S_WRONG_CBIT; + st.msg_id = map->msg_id; + st.msg_type = htons(MSG_TYPE_LABELMAPPING); + lde_send_labelwithdraw(ln, fn, NULL, &st); + + pw->flags &= ~F_PW_CWORD; + lde_send_labelmapping(ln, fn, 1); + } + } else if (map->flags & F_MAP_PW_CWORD) { + if (pw->flags & F_PW_CWORD_CONF) + pw->flags |= F_PW_CWORD; + else + /* act as if no label mapping had been received */ + return (1); + } else + pw->flags &= ~F_PW_CWORD; + + /* RFC4447 - Section 5.4.3: pseudowire status negotiation */ + if (fec_find(&ln->recv_map, &fn->fec) == NULL && + !(map->flags & F_MAP_PW_STATUS)) + pw->flags &= ~F_PW_STATUSTLV; + + return (0); +} + +void +l2vpn_send_pw_status(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; + nm.flags |= F_NOTIF_PW_STATUS; + lde_fec2map(fec, &nm.fec); + 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; + 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; + 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 || + !(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 (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 = (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 (me->map.flags & F_MAP_PW_IFMTU) + pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu; + if (pw) + pwctl.remote_cword = (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..13d3243 --- /dev/null +++ b/ldpd/labelmapping.c @@ -0,0 +1,915 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2014, 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 */ + 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 (me->map.flags & F_MAP_REQ_ID) + msg_size += REQID_TLV_SIZE; + if (me->map.flags & F_MAP_STATUS) + msg_size += STATUS_SIZE; + + /* maximum pdu length exceeded, we need a new ldp pdu */ + if (size + msg_size > nbr->max_pdu_len) { + enqueue_pdu(nbr, type, buf, size); + first = 1; + continue; + } + + size += msg_size; + + /* append message and tlvs */ + err |= gen_msg_hdr(buf, type, msg_size); + err |= gen_fec_tlv(buf, &me->map); + if (me->map.label != NO_LABEL) + err |= gen_label_tlv(buf, me->map.label); + if (me->map.flags & F_MAP_REQ_ID) + err |= gen_reqid_tlv(buf, me->map.requestid); + if (me->map.flags & F_MAP_PW_STATUS) + err |= gen_pw_status_tlv(buf, me->map.pw_status); + if (me->map.flags & F_MAP_STATUS) + err |= gen_status_tlv(buf, me->map.st.status_code, + me->map.st.msg_id, me->map.st.msg_type); + if (err) { + ibuf_free(buf); + mapping_list_clr(mh); + return; + } + + log_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 && + !(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 && + !(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; + } + + 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; + } + + flags |= F_MAP_PW_STATUS; + memcpy(&statusbuf, buf, sizeof(statusbuf)); + pw_status = ntohl(statusbuf); + break; + default: + /* ignore */ + break; + } + break; + default: + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + send_notification_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; + + 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 (me->map.flags & F_MAP_PW_STATUS) + me->map.pw_status = pw_status; + break; + default: + break; + } + me->map.label = label; + if (me->map.flags & F_MAP_REQ_ID) + me->map.requestid = reqid; + + log_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, <, 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 (map->flags & F_MAP_PW_ID) + len += PW_STATUS_TLV_LEN; + if (map->flags & F_MAP_PW_IFMTU) + len += FEC_SUBTLV_IFMTU_SIZE; + if (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)); + err |= ibuf_add(buf, &ft, sizeof(ft)); + err |= ibuf_add(buf, &map->type, sizeof(map->type)); + break; + case MAP_TYPE_PREFIX: + len = PREFIX_SIZE(map->fec.prefix.prefixlen); + ft.length = htons(sizeof(map->type) + sizeof(family) + + sizeof(map->fec.prefix.prefixlen) + len); + err |= ibuf_add(buf, &ft, sizeof(ft)); + err |= ibuf_add(buf, &map->type, sizeof(map->type)); + switch (map->fec.prefix.af) { + case AF_INET: + family = htons(AF_IPV4); + break; + case AF_INET6: + family = htons(AF_IPV6); + break; + default: + fatalx("gen_fec_tlv: unknown af"); + break; + } + err |= ibuf_add(buf, &family, sizeof(family)); + err |= ibuf_add(buf, &map->fec.prefix.prefixlen, + sizeof(map->fec.prefix.prefixlen)); + if (len) + err |= ibuf_add(buf, &map->fec.prefix.prefix, len); + break; + case MAP_TYPE_PWID: + if (map->flags & F_MAP_PW_ID) + pw_len += FEC_PWID_SIZE; + if (map->flags & F_MAP_PW_IFMTU) + pw_len += FEC_SUBTLV_IFMTU_SIZE; + + len = FEC_PWID_ELM_MIN_LEN + pw_len; + + ft.length = htons(len); + err |= ibuf_add(buf, &ft, sizeof(ft)); + + err |= ibuf_add(buf, &map->type, sizeof(uint8_t)); + pw_type = map->fec.pwid.type; + if (map->flags & F_MAP_PW_CWORD) + pw_type |= CONTROL_WORD_FLAG; + pw_type = htons(pw_type); + err |= ibuf_add(buf, &pw_type, sizeof(uint16_t)); + err |= ibuf_add(buf, &pw_len, sizeof(uint8_t)); + group_id = htonl(map->fec.pwid.group_id); + err |= ibuf_add(buf, &group_id, sizeof(uint32_t)); + if (map->flags & F_MAP_PW_ID) { + pwid = htonl(map->fec.pwid.pwid); + err |= ibuf_add(buf, &pwid, sizeof(uint32_t)); + } + if (map->flags & F_MAP_PW_IFMTU) { + struct subtlv stlv; + + stlv.type = SUBTLV_IFMTU; + stlv.length = FEC_SUBTLV_IFMTU_SIZE; + err |= ibuf_add(buf, &stlv, sizeof(uint16_t)); + + ifmtu = htons(map->fec.pwid.ifmtu); + err |= ibuf_add(buf, &ifmtu, sizeof(uint16_t)); + } + break; + 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); + err |= ibuf_add(buf, &ft, sizeof(ft)); + err |= ibuf_add(buf, &map->type, sizeof(uint8_t)); + 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); + 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; + } + + err |= ibuf_add(buf, &family, sizeof(uint16_t)); + break; + case MAP_TYPE_PWID: + twcard_len = sizeof(uint16_t); + err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t)); + pw_type = htons(map->fec.twcard.u.pw_type); + 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 (map->fec.pwid.type & CONTROL_WORD_FLAG) { + map->flags |= F_MAP_PW_CWORD; + map->fec.pwid.type &= ~CONTROL_WORD_FLAG; + } + off += sizeof(uint16_t); + + /* PW info Length */ + pw_len = buf[off]; + off += sizeof(uint8_t); + + if (len != FEC_PWID_ELM_MIN_LEN + pw_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + /* Group ID */ + memcpy(&map->fec.pwid.group_id, buf + off, sizeof(uint32_t)); + map->fec.pwid.group_id = ntohl(map->fec.pwid.group_id); + off += sizeof(uint32_t); + + /* PW ID */ + if (pw_len == 0) + return (off); + + if (pw_len < sizeof(uint32_t)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t)); + map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid); + map->flags |= F_MAP_PW_ID; + off += sizeof(uint32_t); + pw_len -= sizeof(uint32_t); + + /* Optional Interface Parameter Sub-TLVs */ + while (pw_len > 0) { + struct subtlv stlv; + + if (pw_len < sizeof(stlv)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + memcpy(&stlv, buf + off, sizeof(stlv)); + if (stlv.length > pw_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + switch (stlv.type) { + case SUBTLV_IFMTU: + if (stlv.length != FEC_SUBTLV_IFMTU_SIZE) { + session_shutdown(nbr, S_BAD_TLV_LEN, + msg->id, msg->type); + return (-1); + } + memcpy(&map->fec.pwid.ifmtu, buf + off + + SUBTLV_HDR_SIZE, sizeof(uint16_t)); + map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu); + map->flags |= F_MAP_PW_IFMTU; + break; + default: + /* ignore */ + break; + } + off += stlv.length; + pw_len -= stlv.length; + } + + return (off); + 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..efc07b4 --- /dev/null +++ b/ldpd/lde.c @@ -0,0 +1,2535 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 thread *thread); +static void lde_dispatch_parent(struct thread *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 */ +static 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; + thread_add_read(master, iev_main->handler_read, iev_main, iev_main->ibuf.fd, + &iev_main->ev_read); + iev_main->handler_write = ldp_write_handler; + + memset(&iev_main_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 thread thread; + while (thread_fetch(master, &thread)) + thread_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 thread *thread) +{ + struct imsgev *iev = THREAD_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 */ + THREAD_OFF(iev->ev_read); + THREAD_OFF(iev->ev_write); + lde_shutdown(); + } +} + +/* ARGSUSED */ +static void lde_dispatch_parent(struct thread *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 = THREAD_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, + 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; + thread_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 */ + THREAD_OFF(iev->ev_read); + THREAD_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 (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 (!(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 ((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 ((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; + default: + 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 (!(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 (!(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; + default: + 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 (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; + default: + 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; + 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 (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + LIST_FOREACH(fnh, &fn->nexthops, entry) { + if (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; + + map.flags |= F_MAP_PW_IFMTU; + map.fec.pwid.ifmtu = pw->l2vpn->mtu; + if (pw->flags & F_PW_CWORD) + map.flags |= F_MAP_PW_CWORD; + if (pw->flags & F_PW_STATUSTLV) { + map.flags |= F_MAP_PW_STATUS; + 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 (pw->flags & F_PW_CWORD) + 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; + 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 (pw->flags & F_PW_CWORD) + 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; + default: + 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; + 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; + 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 (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + 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; + default: + 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 (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 thread *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 */ + thread_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); + 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)) { + 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 (fnh->flags & F_FEC_NH_CONNECTED) + break; + + if (fnh->ifindex != iface->ifindex) + continue; + + 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 (fnh->flags & F_FEC_NH_CONNECTED) + break; + + if (fnh->ifindex != iface->ifindex) + continue; + + 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) { + 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..bcb1e1c --- /dev/null +++ b/ldpd/lde.h @@ -0,0 +1,265 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDE_H_ +#define _LDE_H_ + +#include "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 thread *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 thread *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..4366560 --- /dev/null +++ b/ldpd/lde_lib.c @@ -0,0 +1,1080 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 thread *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; + default: + 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 ((ldeconf->flags & F_LDPD_ORDERED_CONTROL) && + !connected && iface != NULL && fec->type != FEC_TYPE_PWID) + fnh->flags |= F_FEC_NH_DEFER; + } + + fnh->flags |= F_FEC_NH_NEW; + if (connected) + 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 (fnh->flags & F_FEC_NH_NEW) { + 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 (ldeconf->flags & F_LDPD_ENABLED) { + iface = if_lookup(ldeconf,fnh->ifindex); + if (fnh->flags & F_FEC_NH_CONNECTED || + iface || + fnh->route_type == ZEBRA_ROUTE_STATIC) + fnh->flags &=~F_FEC_NH_NO_LDP; + else + fnh->flags |= F_FEC_NH_NO_LDP; + } else + 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; + default: + 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 && 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 (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + send_map = true; + 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 (map->flags & F_MAP_PW_IFMTU) + pw->remote_mtu = map->fec.pwid.ifmtu; + if (rcvd_label_mapping && 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; + default: + break; + } + } + + /* LRq.6: first check if we have a pending request running */ + lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec); + if (lre != NULL) + /* LRq.7: duplicate request */ + return; + + /* LRq.8: record label request */ + lre = lde_req_add(ln, &fn->fec, 0); + if (lre != NULL) + lre->msg_id = ntohl(map->msg_id); + + /* LRq.9: perform LSR label distribution */ + lde_send_labelmapping(ln, fn, 1); + + /* + * LRq.10: do nothing (Request Never) since we use liberal + * label retention. + * LRq.11 - 12 are unnecessary since we are merging capable. + */ +} + +void +lde_check_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 && !(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 && !(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 (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 (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 thread *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) +{ + THREAD_OFF(gc_timer); + thread_add_timer(master, lde_gc_timer, NULL, LDE_GC_INTERVAL, + &gc_timer); +} + +void +lde_gc_stop_timer(void) +{ + THREAD_OFF(gc_timer); +} diff --git a/ldpd/ldp.h b/ldpd/ldp.h new file mode 100644 index 0000000..4bad3af --- /dev/null +++ b/ldpd/ldp.h @@ -0,0 +1,333 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* LDP protocol definitions */ + +#ifndef _LDP_H_ +#define _LDP_H_ + +/* 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..a8d9833 --- /dev/null +++ b/ldpd/ldp_debug.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#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..977734b --- /dev/null +++ b/ldpd/ldp_debug.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#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..dfc7d14 --- /dev/null +++ b/ldpd/ldp_snmp.c @@ -0,0 +1,1237 @@ +/* + * LDP SNMP support + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * 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 thread_master *tm) +{ + hook_register(agentx_enabled, ldp_snmp_agentx_enabled); + + smux_init(tm); + + return 0; +} + +static int ldp_snmp_register_mib(struct thread_master *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..d788fa0 --- /dev/null +++ b/ldpd/ldp_vty.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#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..11d6930 --- /dev/null +++ b/ldpd/ldp_vty_cmds.c @@ -0,0 +1,914 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "command.h" +#include "vty.h" +#include "json.h" + +#include "ldpd/ldpd.h" +#include "ldpd/ldp_vty.h" +#ifndef VTYSH_EXTRACT_PL +#include "ldpd/ldp_vty_cmds_clippy.c" +#endif + +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") +{ + 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") +{ + return (ldp_vty_show_debugging(vty)); +} + +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..fbd718b --- /dev/null +++ b/ldpd/ldp_vty_conf.c @@ -0,0 +1,1658 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#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..c4053f5 --- /dev/null +++ b/ldpd/ldp_vty_exec.c @@ -0,0 +1,2163 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#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 (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 (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 (ldpd_conf->ipv4.flags & F_LDPD_AF_ENABLED) + json_object_string_add(json, "transportAddressIPv4", + log_addr(AF_INET, &ldpd_conf->ipv4.trans_addr)); + if (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 (nbr->flags & F_NBR_CAP_DYNAMIC) + vty_out (vty," - Dynamic Announcement (0x0506)\n"); + if (nbr->flags & F_NBR_CAP_TWCARD) + vty_out (vty, " - Typed Wildcard (0x050B)\n"); + if (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 (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 (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 (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); +#if CONFDATE > 20230131 +CPP_NOTICE("Remove JSON object commands with keys starting with capital") +#endif + json_object_string_add(json_pw, "VpnName", pw->l2vpn_name); + 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(¶ms->lib.prefix, &prefix)) { + filtered = true; + return (0); + } else if (params->lib.longer_prefixes && + !prefix_match(¶ms->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(¶ms, 0, sizeof(params)); + params.family = af; + params.detail = (detail) ? 1 : 0; + params.json = (json) ? 1 : 0; + if (prefix) { + (void)str2prefix(prefix, ¶ms.lib.prefix); + params.lib.longer_prefixes = longer_prefixes; + } + if (neighbor && + (inet_pton(AF_INET, neighbor, ¶ms.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, ¶ms)); +} + +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(¶ms, 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, ¶ms)); +} + +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(¶ms, 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, ¶ms)); +} + +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(¶ms, 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, ¶ms.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, ¶ms)); +} + +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(¶ms, 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, ¶ms)); +} + +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(¶ms, 0, sizeof(params)); + params.json = (json) ? 1 : 0; + if (peer && + (inet_pton(AF_INET, peer, ¶ms.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, ¶ms)); +} + +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(¶ms, 0, sizeof(params)); + params.json = (json) ? 1 : 0; + if (peer && + (inet_pton(AF_INET, peer, ¶ms.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, ¶ms)); +} + +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..0591159 --- /dev/null +++ b/ldpd/ldp_zebra.c @@ -0,0 +1,733 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#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; +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 (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 + && !(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: + 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 (!(kr.flags & F_CONNECTED)) + continue; + break; + default: + 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 (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 thread_master *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; +} diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c new file mode 100644 index 0000000..87d78af --- /dev/null +++ b/ldpd/ldpd.c @@ -0,0 +1,2002 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2008 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <zebra.h> +#include <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 thread *thread); +static void main_dispatch_lde(struct thread *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 thread_master * 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 thread_master *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 thread *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 */ + thread_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; + thread_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; + thread_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; + thread_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd, + &iev_lde->ev_read); + iev_lde->handler_write = ldp_write_handler; + + imsg_init(&iev_lde_sync->ibuf, pipe_parent2lde_sync[0]); + iev_lde_sync->handler_read = main_dispatch_lde; + thread_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 (ldpd_conf->ipv4.flags & F_LDPD_AF_ENABLED) + main_imsg_send_net_sockets(AF_INET); + if (ldpd_conf->ipv6.flags & F_LDPD_AF_ENABLED) + main_imsg_send_net_sockets(AF_INET6); + + 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 thread *thread) +{ + struct imsgev *iev = THREAD_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 */ + THREAD_OFF(iev->ev_read); + THREAD_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 thread *thread) +{ + struct imsgev *iev = THREAD_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 */ + THREAD_OFF(iev->ev_read); + THREAD_OFF(iev->ev_write); + lde_pid = 0; + if (ldpe_pid == 0) + ldpd_shutdown(); + else + kill(ldpe_pid, SIGTERM); + } +} + +/* ARGSUSED */ +void ldp_write_handler(struct thread *thread) +{ + struct imsgev *iev = THREAD_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 */ + THREAD_OFF(iev->ev_read); + THREAD_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) + thread_add_read(master, iev->handler_read, iev, iev->ibuf.fd, + &iev->ev_read); + + if (iev->handler_write && iev->ibuf.w.queued) + thread_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) + thread_add_write(master, eb->handler, eb->arg, eb->wbuf.fd, + &eb->ev); +} + +void evbuf_init(struct evbuf *eb, int fd, void (*handler)(struct thread *), + void *arg) +{ + msgbuf_init(&eb->wbuf); + eb->wbuf.fd = fd; + eb->handler = handler; + eb->arg = arg; +} + +void +evbuf_clear(struct evbuf *eb) +{ + THREAD_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 ((xconf->ipv4.flags & F_LDPD_AF_ENABLED) && + (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 (!(xconf->flags & F_LDPD_ENABLED)) + ldp_config_reset_main(xconf); + else { + if (!(xconf->ipv4.flags & F_LDPD_AF_ENABLED)) + ldp_config_reset_af(xconf, AF_INET); + if (!(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 (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 (!(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 (!(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 ((conf->flags & F_LDPD_ENABLED) && + (!(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 ((conf->flags & F_LDPD_ORDERED_CONTROL) != + (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 ((conf->flags & F_LDPD_DS_CISCO_INTEROP) != + (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 ((conf->flags & F_LDPD_ALLOW_BROKEN_LSP) != + (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 ((af_conf->flags & F_LDPD_AF_THELLO_ACCEPT) && + !(xa->flags & F_LDPD_AF_THELLO_ACCEPT)) + remove_dynamic_tnbrs = 1; + if ((af_conf->flags & F_LDPD_AF_NO_GTSM) != + (xa->flags & F_LDPD_AF_NO_GTSM)) { + if (af == AF_INET6) + /* need to set/unset IPV6_MINHOPCOUNT */ + update_sockets = 1; + else + /* for LDPv4 just resetting the neighbors is enough */ + reset_nbrs_ipv4 = 1; + } + if ((af_conf->flags & F_LDPD_AF_EXPNULL) != + (xa->flags & F_LDPD_AF_EXPNULL)) + change_egress_label = 1; + + /* changing config of host only fec filtering */ + if ((af_conf->flags & F_LDPD_AF_ALLOCHOSTONLY) + != (xa->flags & F_LDPD_AF_ALLOCHOSTONLY)) + change_host_label = 1; + + /* disabling LDP for address family */ + if ((af_conf->flags & F_LDPD_AF_ENABLED) && + !(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 (!(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: + 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 (!(tnbr->flags & F_TNBR_CONFIGURED)) + 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 ((pw->flags & (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF)) != + (xp->flags & (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF))) + reset_nbr = 1; + else + reset_nbr = 0; + + if (l2vpn->pw_type != xl->pw_type || l2vpn->mtu != xl->mtu || + pw->pwid != xp->pwid || reinstall_tnbr || reset_nbr || + pw->lsr_id.s_addr != xp->lsr_id.s_addr) + reinstall_pwfec = 1; + else + reinstall_pwfec = 0; + + if (ldpd_process == PROC_LDP_ENGINE) { + if (reinstall_tnbr) + ldpe_l2vpn_pw_exit(pw); + if (reset_nbr) { + nbr = nbr_find_ldpid(pw->lsr_id.s_addr); + if (nbr && nbr->state == NBR_STA_OPER) + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + } + } + if (ldpd_process == PROC_LDE_ENGINE && reinstall_pwfec) + l2vpn_pw_exit(pw); + pw->lsr_id = xp->lsr_id; + pw->af = xp->af; + pw->addr = xp->addr; + pw->pwid = xp->pwid; + strlcpy(pw->ifname, xp->ifname, sizeof(pw->ifname)); + pw->ifindex = xp->ifindex; + if (xp->flags & F_PW_CWORD_CONF) + pw->flags |= F_PW_CWORD_CONF; + else + pw->flags &= ~F_PW_CWORD_CONF; + if (xp->flags & F_PW_STATUSTLV_CONF) + pw->flags |= F_PW_STATUSTLV_CONF; + else + pw->flags &= ~F_PW_STATUSTLV_CONF; + if (xp->flags & F_PW_STATIC_NBR_ADDR) + pw->flags |= F_PW_STATIC_NBR_ADDR; + else + 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..02eaf10 --- /dev/null +++ b/ldpd/ldpd.h @@ -0,0 +1,922 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDPD_H_ +#define _LDPD_H_ + +#include "queue.h" +#include "openbsd-tree.h" +#include "imsg.h" +#include "thread.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 thread *ev; + void (*handler)(struct thread *); + void *arg; +}; + +struct imsgev { + struct imsgbuf ibuf; + void (*handler_write)(struct thread *); + struct thread *ev_write; + void (*handler_read)(struct thread *); + struct thread *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 thread *hello_timer; + uint16_t hello_holdtime; + uint16_t hello_interval; +}; + +struct iface_ldp_sync { + int state; + struct thread *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 thread *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 thread *disc_ev; + struct thread *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 thread *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 thread *), 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 thread_master *master; +extern char ctl_sock_path[MAXPATHLEN]; + +/* ldp_zebra.c */ +void ldp_zebra_init(struct thread_master *); +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 thread_master * 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..1b5c0e9 --- /dev/null +++ b/ldpd/ldpe.c @@ -0,0 +1,1074 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2008 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <zebra.h> + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "control.h" +#include "log.h" +#include "ldp_debug.h" +#include "rlfa.h" + +#include <lib/log.h> +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "libfrr.h" + +static void ldpe_shutdown(void); +static void ldpe_dispatch_main(struct thread *thread); +static void ldpe_dispatch_lde(struct thread *thread); +#ifdef __OpenBSD__ +static void ldpe_dispatch_pfkey(struct thread *thread); +#endif +static void ldpe_setup_sockets(int, int, int, int); +static void ldpe_close_sockets(int); +static void ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t ifidx); +static void ldpe_check_filter_af(int, struct ldpd_af_conf *, const char *); + +struct ldpd_conf *leconf; +#ifdef __OpenBSD__ +struct ldpd_sysdep sysdep; +#endif + +static struct imsgev iev_main_data; +static struct imsgev *iev_main, *iev_main_sync; +static struct imsgev *iev_lde; +#ifdef __OpenBSD__ +static struct thread *pfkey_ev; +#endif + +/* ldpe privileges */ +static zebra_capabilities_t _caps_p [] = +{ + ZCAP_BIND, + ZCAP_NET_ADMIN +}; + +struct zebra_privs_t ldpe_privs = +{ +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0 +}; + +/* SIGINT / SIGTERM handler. */ +static void +sigint(void) +{ + ldpe_shutdown(); +} + +static struct frr_signal_t ldpe_signals[] = +{ + { + .signal = SIGHUP, + /* ignore */ + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +char *pkt_ptr; /* packet buffer */ + +/* label distribution protocol engine */ +void +ldpe(void) +{ +#ifdef HAVE_SETPROCTITLE + setproctitle("ldp engine"); +#endif + ldpd_process = PROC_LDP_ENGINE; + log_procname = log_procnames[ldpd_process]; + + master = frr_init(); + /* no frr_config_fork() here, allow frr_pthread to create threads */ + frr_is_after_fork = true; + + /* setup signal handler */ + signal_init(master, array_size(ldpe_signals), ldpe_signals); + + /* setup pipes and event handlers to the parent process */ + if ((iev_main = calloc(1, sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC); + iev_main->handler_read = ldpe_dispatch_main; + thread_add_read(master, iev_main->handler_read, iev_main, iev_main->ibuf.fd, + &iev_main->ev_read); + iev_main->handler_write = ldp_write_handler; + + memset(&iev_main_data, 0, sizeof(iev_main_data)); + iev_main_sync = &iev_main_data; + imsg_init(&iev_main_sync->ibuf, LDPD_FD_SYNC); + + /* create base configuration */ + leconf = config_new_empty(); + + struct thread thread; + while (thread_fetch(master, &thread)) + thread_call(&thread); + + /* NOTREACHED */ + return; +} + +void +ldpe_init(struct ldpd_init *init) +{ +#ifdef __OpenBSD__ + /* This socket must be open before dropping privileges. */ + global.pfkeysock = pfkey_init(); + if (sysdep.no_pfkey == 0) { + thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock, + &pfkey_ev); + } +#endif + + /* drop privileges */ + ldpe_privs.user = init->user; + ldpe_privs.group = init->group; + zprivs_preinit(&ldpe_privs); + zprivs_init(&ldpe_privs); + + /* listen on ldpd control socket */ + strlcpy(ctl_sock_path, init->ctl_sock_path, sizeof(ctl_sock_path)); + if (control_init(ctl_sock_path) == -1) + fatalx("control socket setup failed"); + TAILQ_INIT(&ctl_conns); + control_listen(); + + LIST_INIT(&global.addr_list); + RB_INIT(global_adj_head, &global.adj_tree); + TAILQ_INIT(&global.pending_conns); + if (inet_pton(AF_INET, AllRouters_v4, &global.mcast_addr_v4) != 1) + fatal("inet_pton"); + if (inet_pton(AF_INET6, AllRouters_v6, &global.mcast_addr_v6) != 1) + fatal("inet_pton"); + + /* mark sockets as closed */ + global.ipv4.ldp_disc_socket = -1; + global.ipv4.ldp_edisc_socket = -1; + global.ipv4.ldp_session_socket = -1; + global.ipv6.ldp_disc_socket = -1; + global.ipv6.ldp_edisc_socket = -1; + global.ipv6.ldp_session_socket = -1; + + if ((pkt_ptr = calloc(1, IBUF_READ_SIZE)) == NULL) + fatal(__func__); + + accept_init(); +} + +static void +ldpe_shutdown(void) +{ + struct if_addr *if_addr; + struct adj *adj; + + /* close pipes */ + if (iev_lde) { + msgbuf_clear(&iev_lde->ibuf.w); + close(iev_lde->ibuf.fd); + iev_lde->ibuf.fd = -1; + } + msgbuf_clear(&iev_main->ibuf.w); + close(iev_main->ibuf.fd); + iev_main->ibuf.fd = -1; + msgbuf_clear(&iev_main_sync->ibuf.w); + close(iev_main_sync->ibuf.fd); + iev_main_sync->ibuf.fd = -1; + + control_cleanup(ctl_sock_path); + config_clear(leconf); + +#ifdef __OpenBSD__ + if (sysdep.no_pfkey == 0) { + THREAD_OFF(pfkey_ev); + close(global.pfkeysock); + } +#endif + ldpe_close_sockets(AF_INET); + ldpe_close_sockets(AF_INET6); + + /* remove addresses from global list */ + while ((if_addr = LIST_FIRST(&global.addr_list)) != NULL) { + LIST_REMOVE(if_addr, entry); + assert(if_addr != LIST_FIRST(&global.addr_list)); + free(if_addr); + } + while (!RB_EMPTY(global_adj_head, &global.adj_tree)) { + adj = RB_ROOT(global_adj_head, &global.adj_tree); + + adj_del(adj, S_SHUTDOWN); + } + + /* clean up */ + if (iev_lde) + free(iev_lde); + free(iev_main); + free(pkt_ptr); + + log_info("ldp engine exiting"); + + zlog_fini(); + + exit(0); +} + +/* imesg */ +int +ldpe_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen) +{ + if (iev_main->ibuf.fd == -1) + return (0); + return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen)); +} + +void +ldpe_imsg_compose_parent_sync(int type, pid_t pid, void *data, uint16_t datalen) +{ + if (iev_main_sync->ibuf.fd == -1) + return; + imsg_compose_event(iev_main_sync, type, 0, pid, -1, data, datalen); + imsg_flush(&iev_main_sync->ibuf); +} + +int +ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data, + uint16_t datalen) +{ + if (iev_lde->ibuf.fd == -1) + return (0); + return (imsg_compose_event(iev_lde, type, peerid, pid, -1, + data, datalen)); +} + +/* ARGSUSED */ +static void ldpe_dispatch_main(struct thread *thread) +{ + static struct ldpd_conf *nconf; + struct iface *niface; + struct tnbr *ntnbr; + struct nbr_params *nnbrp; + static struct l2vpn *l2vpn, *nl2vpn; + struct l2vpn_if *lif, *nlif; + struct l2vpn_pw *pw, *npw; + struct imsg imsg; + int fd; + struct imsgev *iev = THREAD_ARG(thread); + struct imsgbuf *ibuf = &iev->ibuf; + struct iface *iface = NULL; + struct kif *kif; + int af; + enum socket_type *socket_type; + static int disc_socket = -1; + static int edisc_socket = -1; + static int session_socket = -1; + struct nbr *nbr; +#ifdef __OpenBSD__ + struct nbr_params *nbrp; +#endif + int n, shut = 0; + struct ldp_access *laccess; + struct ldp_igp_sync_if_state_req *ldp_sync_if_state_req; + struct ldp_rlfa_node *rnode, *rntmp; + struct ldp_rlfa_client *rclient; + struct zapi_rlfa_request *rlfa_req; + struct zapi_rlfa_igp *rlfa_igp; + + iev->ev_read = NULL; + + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("ldpe_dispatch_main: imsg_get error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_IFSTATUS: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct kif)) + fatalx("IFSTATUS imsg with wrong len"); + kif = imsg.data; + + iface = if_lookup_name(leconf, kif->ifname); + if (iface) { + if_update_info(iface, kif); + ldp_if_update(iface, AF_UNSPEC); + break; + } + + RB_FOREACH(l2vpn, l2vpn_head, &leconf->l2vpn_tree) { + lif = l2vpn_if_find(l2vpn, kif->ifname); + if (lif) { + l2vpn_if_update_info(lif, kif); + l2vpn_if_update(lif); + break; + } + pw = l2vpn_pw_find(l2vpn, kif->ifname); + if (pw) { + l2vpn_pw_update_info(pw, kif); + break; + } + } + break; + case IMSG_NEWADDR: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct kaddr)) + fatalx("NEWADDR imsg with wrong len"); + + if_addr_add(imsg.data); + break; + case IMSG_DELADDR: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct kaddr)) + fatalx("DELADDR imsg with wrong len"); + + if_addr_del(imsg.data); + break; + case IMSG_SOCKET_IPC: + if (iev_lde) { + log_warnx("%s: received unexpected imsg fd to lde", __func__); + break; + } + if ((fd = imsg.fd) == -1) { + log_warnx("%s: expected to receive imsg fd to lde but didn't receive any", __func__); + break; + } + + if ((iev_lde = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_lde->ibuf, fd); + iev_lde->handler_read = ldpe_dispatch_lde; + thread_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd, + &iev_lde->ev_read); + iev_lde->handler_write = ldp_write_handler; + iev_lde->ev_write = NULL; + break; + case IMSG_INIT: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldpd_init)) + fatalx("INIT imsg with wrong len"); + + memcpy(&init, imsg.data, sizeof(init)); + ldpe_init(&init); + break; + case IMSG_AGENTX_ENABLED: + ldp_agentx_enabled(); + break; + case IMSG_CLOSE_SOCKETS: + af = imsg.hdr.peerid; + + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->af != af) + continue; + session_shutdown(nbr, S_SHUTDOWN, 0, 0); +#ifdef __OpenBSD__ + pfkey_remove(nbr); +#endif + nbr->auth.method = AUTH_NONE; + } + ldpe_close_sockets(af); + if_update_all(af); + tnbr_update_all(af); + + disc_socket = -1; + edisc_socket = -1; + session_socket = -1; + if ((ldp_af_conf_get(leconf, af))->flags & + F_LDPD_AF_ENABLED) + ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, + af, NULL, 0); + break; + case IMSG_SOCKET_NET: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(enum socket_type)) + fatalx("SOCKET_NET imsg with wrong len"); + socket_type = imsg.data; + + switch (*socket_type) { + case LDP_SOCKET_DISC: + disc_socket = imsg.fd; + break; + case LDP_SOCKET_EDISC: + edisc_socket = imsg.fd; + break; + case LDP_SOCKET_SESSION: + session_socket = imsg.fd; + break; + } + break; + case IMSG_SETUP_SOCKETS: + af = imsg.hdr.peerid; + if (disc_socket == -1 || edisc_socket == -1 || + session_socket == -1) { + if (disc_socket != -1) + close(disc_socket); + if (edisc_socket != -1) + close(edisc_socket); + if (session_socket != -1) + close(session_socket); + break; + } + + ldpe_setup_sockets(af, disc_socket, edisc_socket, + session_socket); + if_update_all(af); + tnbr_update_all(af); + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->af != af) + continue; + nbr->laddr = (ldp_af_conf_get(leconf, + af))->trans_addr; +#ifdef __OpenBSD__ + nbrp = nbr_params_find(leconf, nbr->id); + if (nbrp) { + nbr->auth.method = nbrp->auth.method; + if (pfkey_establish(nbr, nbrp) == -1) + fatalx("pfkey setup failed"); + } +#endif + if (nbr_session_active_role(nbr)) + nbr_establish_connection(nbr); + } + break; + case IMSG_RTRID_UPDATE: + memcpy(&global.rtr_id, imsg.data, + sizeof(global.rtr_id)); + if (leconf->rtr_id.s_addr == INADDR_ANY) { + ldpe_reset_nbrs(AF_UNSPEC); + } + if_update_all(AF_UNSPEC); + tnbr_update_all(AF_UNSPEC); + break; + case IMSG_RECONF_CONF: + if ((nconf = malloc(sizeof(struct ldpd_conf))) == + NULL) + fatal(NULL); + memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); + + RB_INIT(iface_head, &nconf->iface_tree); + RB_INIT(tnbr_head, &nconf->tnbr_tree); + RB_INIT(nbrp_head, &nconf->nbrp_tree); + RB_INIT(l2vpn_head, &nconf->l2vpn_tree); + break; + case IMSG_RECONF_IFACE: + if ((niface = malloc(sizeof(struct iface))) == NULL) + fatal(NULL); + memcpy(niface, imsg.data, sizeof(struct iface)); + + RB_INSERT(iface_head, &nconf->iface_tree, niface); + break; + case IMSG_RECONF_TNBR: + if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL) + fatal(NULL); + memcpy(ntnbr, imsg.data, sizeof(struct tnbr)); + + RB_INSERT(tnbr_head, &nconf->tnbr_tree, ntnbr); + break; + case IMSG_RECONF_NBRP: + if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL) + fatal(NULL); + memcpy(nnbrp, imsg.data, sizeof(struct nbr_params)); + + RB_INSERT(nbrp_head, &nconf->nbrp_tree, nnbrp); + break; + case IMSG_RECONF_L2VPN: + if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL) + fatal(NULL); + memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn)); + + RB_INIT(l2vpn_if_head, &nl2vpn->if_tree); + RB_INIT(l2vpn_pw_head, &nl2vpn->pw_tree); + RB_INIT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree); + + RB_INSERT(l2vpn_head, &nconf->l2vpn_tree, nl2vpn); + break; + case IMSG_RECONF_L2VPN_IF: + if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL) + fatal(NULL); + memcpy(nlif, imsg.data, sizeof(struct l2vpn_if)); + + RB_INSERT(l2vpn_if_head, &nl2vpn->if_tree, nlif); + break; + case IMSG_RECONF_L2VPN_PW: + if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) + fatal(NULL); + memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); + + RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_tree, npw); + break; + case IMSG_RECONF_L2VPN_IPW: + if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) + fatal(NULL); + memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); + + RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree, npw); + break; + case IMSG_RECONF_END: + merge_config(leconf, nconf); + ldp_clear_config(nconf); + nconf = NULL; + global.conf_seqnum++; + break; + case IMSG_CTL_END: + control_imsg_relay(&imsg); + break; + case IMSG_DEBUG_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(ldp_debug)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug)); + break; + case IMSG_FILTER_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldp_access)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + laccess = imsg.data; + ldpe_check_filter_af(AF_INET, &leconf->ipv4, + laccess->name); + ldpe_check_filter_af(AF_INET6, &leconf->ipv6, + laccess->name); + break; + case IMSG_LDP_SYNC_IF_STATE_REQUEST: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldp_igp_sync_if_state_req)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + ldp_sync_if_state_req = imsg.data; + ldp_sync_fsm_state_req(ldp_sync_if_state_req); + break; + case IMSG_RLFA_REG: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct zapi_rlfa_request)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + rlfa_req = imsg.data; + + rnode = rlfa_node_find(&rlfa_req->destination, + rlfa_req->pq_address); + if (!rnode) + rnode = rlfa_node_new(&rlfa_req->destination, + rlfa_req->pq_address); + rclient = rlfa_client_find(rnode, &rlfa_req->igp); + if (rclient) + /* RLFA already registered - do nothing */ + break; + rclient = rlfa_client_new(rnode, &rlfa_req->igp); + ldpe_rlfa_init(rclient); + break; + case IMSG_RLFA_UNREG_ALL: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct zapi_rlfa_igp)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + rlfa_igp = imsg.data; + + RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head, + &rlfa_node_tree, rntmp) { + rclient = rlfa_client_find(rnode, rlfa_igp); + if (!rclient) + continue; + + ldpe_rlfa_exit(rclient); + rlfa_client_del(rclient); + } + break; + default: + log_debug("%s: error handling imsg %d", + __func__, imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* this pipe is dead, so remove the event handlers and exit */ + THREAD_OFF(iev->ev_read); + THREAD_OFF(iev->ev_write); + ldpe_shutdown(); + } +} + +/* ARGSUSED */ +static void ldpe_dispatch_lde(struct thread *thread) +{ + struct imsgev *iev = THREAD_ARG(thread); + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg imsg; + struct map *map; + struct notify_msg *nm; + struct nbr *nbr; + int n, shut = 0; + + iev->ev_read = NULL; + + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("ldpe_dispatch_lde: imsg_get error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_MAPPING_ADD: + case IMSG_RELEASE_ADD: + case IMSG_REQUEST_ADD: + case IMSG_WITHDRAW_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct map)) + fatalx("invalid size of map request"); + map = imsg.data; + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + if (nbr->state != NBR_STA_OPER) + break; + + switch (imsg.hdr.type) { + case IMSG_MAPPING_ADD: + mapping_list_add(&nbr->mapping_list, map); + break; + case IMSG_RELEASE_ADD: + mapping_list_add(&nbr->release_list, map); + break; + case IMSG_REQUEST_ADD: + mapping_list_add(&nbr->request_list, map); + break; + case IMSG_WITHDRAW_ADD: + mapping_list_add(&nbr->withdraw_list, map); + break; + } + break; + case IMSG_MAPPING_ADD_END: + case IMSG_RELEASE_ADD_END: + case IMSG_REQUEST_ADD_END: + case IMSG_WITHDRAW_ADD_END: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + if (nbr->state != NBR_STA_OPER) + break; + + switch (imsg.hdr.type) { + case IMSG_MAPPING_ADD_END: + send_labelmessage(nbr, MSG_TYPE_LABELMAPPING, + &nbr->mapping_list); + break; + case IMSG_RELEASE_ADD_END: + send_labelmessage(nbr, MSG_TYPE_LABELRELEASE, + &nbr->release_list); + break; + case IMSG_REQUEST_ADD_END: + send_labelmessage(nbr, MSG_TYPE_LABELREQUEST, + &nbr->request_list); + break; + case IMSG_WITHDRAW_ADD_END: + send_labelmessage(nbr, MSG_TYPE_LABELWITHDRAW, + &nbr->withdraw_list); + break; + } + break; + case IMSG_NOTIFICATION_SEND: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct notify_msg)) + fatalx("invalid size of OE request"); + nm = imsg.data; + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("%s: cannot find neighbor", __func__); + break; + } + if (nbr->state != NBR_STA_OPER) + break; + + send_notification_full(nbr->tcp, nm); + break; + case IMSG_CTL_END: + case IMSG_CTL_SHOW_LIB_BEGIN: + case IMSG_CTL_SHOW_LIB_RCVD: + case IMSG_CTL_SHOW_LIB_SENT: + case IMSG_CTL_SHOW_LIB_END: + case IMSG_CTL_SHOW_L2VPN_PW: + case IMSG_CTL_SHOW_L2VPN_BINDING: + control_imsg_relay(&imsg); + break; + case IMSG_NBR_SHUTDOWN: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("%s: cannot find neighbor", __func__); + break; + } + if (nbr->state != NBR_STA_OPER) + break; + session_shutdown(nbr,S_SHUTDOWN,0,0); + break; + default: + log_debug("%s: error handling imsg %d", + __func__, imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* this pipe is dead, so remove the event handlers and exit */ + THREAD_OFF(iev->ev_read); + THREAD_OFF(iev->ev_write); + ldpe_shutdown(); + } +} + +#ifdef __OpenBSD__ +/* ARGSUSED */ +static void ldpe_dispatch_pfkey(struct thread *thread) +{ + int fd = THREAD_FD(thread); + + thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock, + &pfkey_ev); + + if (pfkey_read(fd, NULL) == -1) + fatal("pfkey_read failed, exiting..."); +} +#endif /* __OpenBSD__ */ + +static void +ldpe_setup_sockets(int af, int disc_socket, int edisc_socket, + int session_socket) +{ + struct ldpd_af_global *af_global; + + af_global = ldp_af_global_get(&global, af); + + /* discovery socket */ + af_global->ldp_disc_socket = disc_socket; + thread_add_read(master, disc_recv_packet, &af_global->disc_ev, af_global->ldp_disc_socket, + &af_global->disc_ev); + + /* extended discovery socket */ + af_global->ldp_edisc_socket = edisc_socket; + thread_add_read(master, disc_recv_packet, &af_global->edisc_ev, af_global->ldp_edisc_socket, + &af_global->edisc_ev); + + /* session socket */ + af_global->ldp_session_socket = session_socket; + accept_add(af_global->ldp_session_socket, session_accept, NULL); +} + +static void +ldpe_close_sockets(int af) +{ + struct ldpd_af_global *af_global; + + af_global = ldp_af_global_get(&global, af); + + /* discovery socket */ + THREAD_OFF(af_global->disc_ev); + if (af_global->ldp_disc_socket != -1) { + close(af_global->ldp_disc_socket); + af_global->ldp_disc_socket = -1; + } + + /* extended discovery socket */ + THREAD_OFF(af_global->edisc_ev); + if (af_global->ldp_edisc_socket != -1) { + close(af_global->ldp_edisc_socket); + af_global->ldp_edisc_socket = -1; + } + + /* session socket */ + if (af_global->ldp_session_socket != -1) { + accept_del(af_global->ldp_session_socket); + close(af_global->ldp_session_socket); + af_global->ldp_session_socket = -1; + } +} + +int +ldpe_acl_check(char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen) +{ + return ldp_acl_request(iev_main_sync, acl_name, af, addr, prefixlen); +} + +void +ldpe_reset_nbrs(int af) +{ + struct nbr *nbr; + + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (af == AF_UNSPEC || nbr->af == af) + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + } +} + +void +ldpe_reset_ds_nbrs(void) +{ + struct nbr *nbr; + + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->ds_tlv) + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + } +} + +void +ldpe_remove_dynamic_tnbrs(int af) +{ + struct tnbr *tnbr, *safe; + + RB_FOREACH_SAFE(tnbr, tnbr_head, &leconf->tnbr_tree, safe) { + if (tnbr->af != af) + continue; + + tnbr->flags &= ~F_TNBR_DYNAMIC; + tnbr_check(leconf, tnbr); + } +} + +void +ldpe_stop_init_backoff(int af) +{ + struct nbr *nbr; + + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->af == af && nbr_pending_idtimer(nbr)) { + nbr_stop_idtimer(nbr); + nbr_establish_connection(nbr); + } + } +} + +static void +ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t idx) +{ + struct iface *iface; + struct iface_af *ia; + struct ctl_iface *ictl; + + RB_FOREACH(iface, iface_head, &leconf->iface_tree) { + if (idx == 0 || idx == iface->ifindex) { + ia = iface_af_get(iface, af); + if (!ia->enabled) + continue; + + ictl = if_to_ctl(ia); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_INTERFACE, + 0, 0, -1, ictl, sizeof(struct ctl_iface)); + } + } +} + +void +ldpe_iface_ctl(struct ctl_conn *c, ifindex_t idx) +{ + ldpe_iface_af_ctl(c, AF_INET, idx); + ldpe_iface_af_ctl(c, AF_INET6, idx); +} + +void +ldpe_adj_ctl(struct ctl_conn *c) +{ + struct adj *adj; + struct ctl_adj *actl; + + RB_FOREACH(adj, global_adj_head, &global.adj_tree) { + actl = adj_to_ctl(adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, + -1, actl, sizeof(struct ctl_adj)); + } + + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} + +void +ldpe_adj_detail_ctl(struct ctl_conn *c) +{ + struct iface *iface; + struct tnbr *tnbr; + struct adj *adj; + struct ctl_adj *actl; + struct ctl_disc_if ictl; + struct ctl_disc_tnbr tctl; + + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, -1, NULL, 0); + + RB_FOREACH(iface, iface_head, &leconf->iface_tree) { + memset(&ictl, 0, sizeof(ictl)); + ictl.active_v4 = (iface->ipv4.state == IF_STA_ACTIVE); + ictl.active_v6 = (iface->ipv6.state == IF_STA_ACTIVE); + + if (!ictl.active_v4 && !ictl.active_v6) + continue; + + strlcpy(ictl.name, iface->name, sizeof(ictl.name)); + if (RB_EMPTY(ia_adj_head, &iface->ipv4.adj_tree) && + RB_EMPTY(ia_adj_head, &iface->ipv6.adj_tree)) + ictl.no_adj = 1; + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_IFACE, 0, 0, + -1, &ictl, sizeof(ictl)); + + RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) { + actl = adj_to_ctl(adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, + 0, 0, -1, actl, sizeof(struct ctl_adj)); + } + RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) { + actl = adj_to_ctl(adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, + 0, 0, -1, actl, sizeof(struct ctl_adj)); + } + } + + RB_FOREACH(tnbr, tnbr_head, &leconf->tnbr_tree) { + memset(&tctl, 0, sizeof(tctl)); + tctl.af = tnbr->af; + tctl.addr = tnbr->addr; + if (tnbr->adj == NULL) + tctl.no_adj = 1; + + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_TNBR, 0, 0, + -1, &tctl, sizeof(tctl)); + + if (tnbr->adj == NULL) + continue; + + actl = adj_to_ctl(tnbr->adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, 0, 0, + -1, actl, sizeof(struct ctl_adj)); + } + + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} + +void +ldpe_nbr_ctl(struct ctl_conn *c) +{ + struct adj *adj; + struct ctl_adj *actl; + struct nbr *nbr; + struct ctl_nbr *nctl; + + RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) { + if (nbr->state == NBR_STA_PRESENT) + continue; + + nctl = nbr_to_ctl(nbr); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 0, -1, nctl, + sizeof(struct ctl_nbr)); + + RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) { + actl = adj_to_ctl(adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_DISC, + 0, 0, -1, actl, sizeof(struct ctl_adj)); + } + + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_END, 0, 0, -1, + NULL, 0); + } + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} + +void +ldpe_ldp_sync_ctl(struct ctl_conn *c) +{ + struct iface *iface; + struct ctl_ldp_sync *ictl; + + RB_FOREACH(iface, iface_head, &leconf->iface_tree) { + ictl = ldp_sync_to_ctl(iface); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_LDP_SYNC, + 0, 0, -1, ictl, sizeof(struct ctl_ldp_sync)); + } + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} + +void +mapping_list_add(struct mapping_head *mh, struct map *map) +{ + struct mapping_entry *me; + + me = calloc(1, sizeof(*me)); + if (me == NULL) + fatal(__func__); + me->map = *map; + + TAILQ_INSERT_TAIL(mh, me, entry); +} + +void +mapping_list_clr(struct mapping_head *mh) +{ + struct mapping_entry *me; + + while ((me = TAILQ_FIRST(mh)) != NULL) { + TAILQ_REMOVE(mh, me, entry); + assert(me != TAILQ_FIRST(mh)); + free(me); + } +} + +void +ldpe_check_filter_af(int af, struct ldpd_af_conf *af_conf, + const char *filter_name) +{ + if (strcmp(af_conf->acl_thello_accept_from, filter_name) == 0) + ldpe_remove_dynamic_tnbrs(af); +} + +void +ldpe_set_config_change_time(void) +{ + /* SNMP update time when ever there is a config change */ + leconf->config_change_time = time(NULL); +} diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h new file mode 100644 index 0000000..ddb0e0d --- /dev/null +++ b/ldpd/ldpe.h @@ -0,0 +1,324 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDPE_H_ +#define _LDPE_H_ + +#include "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 thread *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 thread *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 thread *ev_connect; + struct thread *keepalive_timer; + struct thread *keepalive_timeout; + struct thread *init_timeout; + struct thread *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 thread *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 thread *), 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 thread *thread); +void session_accept(struct thread *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..1aaad41 --- /dev/null +++ b/ldpd/log.c @@ -0,0 +1,149 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 { + vlog(LOG_ERR, nfmt, ap); + 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..8c236ff --- /dev/null +++ b/ldpd/log.h @@ -0,0 +1,47 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef LOG_H +#define LOG_H + +#include <stdarg.h> + +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..ff9294f --- /dev/null +++ b/ldpd/logmsg.c @@ -0,0 +1,510 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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; + unsigned int 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) + snprintf(buf, TF_LEN, "%02uw%01ud%02uh", week, day, hrs); + else if (day > 0) + snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); + else + snprintf(buf, TF_LEN, "%02u:%02u:%02u", 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..e1db9e8 --- /dev/null +++ b/ldpd/neighbor.c @@ -0,0 +1,876 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 thread *thread); +static void nbr_start_ktimer(struct nbr *); +static void nbr_ktimeout(struct thread *thread); +static void nbr_start_ktimeout(struct nbr *); +static void nbr_itimeout(struct thread *thread); +static void nbr_start_itimeout(struct nbr *); +static void nbr_idtimer(struct thread *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 ((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)) + THREAD_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() +{ + 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 thread *thread) +{ + struct nbr *nbr = THREAD_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; + THREAD_OFF(nbr->keepalive_timer); + nbr->keepalive_timer = NULL; + thread_add_timer(master, nbr_ktimer, nbr, secs, &nbr->keepalive_timer); +} + +void +nbr_stop_ktimer(struct nbr *nbr) +{ + THREAD_OFF(nbr->keepalive_timer); +} + +/* Keepalive timeout: if the nbr hasn't sent keepalive */ + +static void nbr_ktimeout(struct thread *thread) +{ + struct nbr *nbr = THREAD_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) +{ + THREAD_OFF(nbr->keepalive_timeout); + nbr->keepalive_timeout = NULL; + thread_add_timer(master, nbr_ktimeout, nbr, nbr->keepalive, + &nbr->keepalive_timeout); +} + +void +nbr_stop_ktimeout(struct nbr *nbr) +{ + THREAD_OFF(nbr->keepalive_timeout); +} + +/* Session initialization timeout: if nbr got stuck in the initialization FSM */ + +static void nbr_itimeout(struct thread *thread) +{ + struct nbr *nbr = THREAD_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; + THREAD_OFF(nbr->init_timeout); + nbr->init_timeout = NULL; + thread_add_timer(master, nbr_itimeout, nbr, secs, &nbr->init_timeout); +} + +void +nbr_stop_itimeout(struct nbr *nbr) +{ + THREAD_OFF(nbr->init_timeout); +} + +/* Init delay timer: timer to retry to iniziatize session */ + +static void nbr_idtimer(struct thread *thread) +{ + struct nbr *nbr = THREAD_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; + } + + THREAD_OFF(nbr->initdelay_timer); + nbr->initdelay_timer = NULL; + thread_add_timer(master, nbr_idtimer, nbr, secs, + &nbr->initdelay_timer); +} + +void +nbr_stop_idtimer(struct nbr *nbr) +{ + THREAD_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 thread *thread) +{ + struct nbr *nbr = THREAD_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) { + thread_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 && (nbrp->flags & F_NBRP_GTSM)) + return (nbrp->gtsm_enabled); + + if ((ldp_af_conf_get(leconf, nbr->af))->flags & F_LDPD_AF_NO_GTSM) + return (0); + + /* By default, GTSM support has to be negotiated for LDPv4 */ + if (nbr->af == AF_INET && !(nbr->flags & F_NBR_GTSM_NEGOTIATED)) + return (0); + + return (1); +} + +int +nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp) +{ + int ttl = 255; + + if (nbrp && (nbrp->flags & F_NBRP_GTSM_HOPS)) + ttl = 256 - nbrp->gtsm_hops; + + switch (af) { + case AF_INET: + if (sock_set_ipv4_minttl(fd, ttl) == -1) + return (-1); + ttl = 255; + if (sock_set_ipv4_ucast_ttl(fd, ttl) == -1) + return (-1); + break; + case AF_INET6: + /* 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 && (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 = + thread_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..3ecf5d4 --- /dev/null +++ b/ldpd/notification.c @@ -0,0 +1,397 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 (nm->flags & F_NOTIF_PW_STATUS) + size += PW_STATUS_TLV_SIZE; + if (nm->flags & F_NOTIF_FEC) + size += len_fec_tlv(&nm->fec); + if (nm->flags & F_NOTIF_RETURNED_TLVS) + size += TLV_HDR_SIZE * 2 + nm->rtlvs.length; + + if ((buf = ibuf_open(size)) == NULL) + fatal(__func__); + + err |= gen_ldp_hdr(buf, size); + size -= LDP_HDR_SIZE; + err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size); + err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type); + /* optional tlvs */ + if (nm->flags & F_NOTIF_PW_STATUS) + err |= gen_pw_status_tlv(buf, nm->pw_status); + if (nm->flags & F_NOTIF_FEC) + err |= gen_fec_tlv(buf, &nm->fec); + if (nm->flags & F_NOTIF_RETURNED_TLVS) + 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; + 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); + 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); + } + 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 (!(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 (!(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 (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)); + err |= ibuf_add(buf, &tlv, sizeof(tlv)); + 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 (nm->flags & F_NOTIF_FEC) + debug_msg(out, "notification: fec %s", log_map(&nm->fec)); + if (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..2cca524 --- /dev/null +++ b/ldpd/packet.c @@ -0,0 +1,828 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 thread *thread); +static void session_write(struct thread *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 thread *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 thread *thread) +{ + int fd = THREAD_FD(thread); + struct thread **threadp = THREAD_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 */ + thread_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 thread *thread) +{ + int fd = THREAD_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 thread *thread) +{ + int fd = THREAD_FD(thread); + struct nbr *nbr = THREAD_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; + + thread_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 thread *thread) +{ + struct tcp_conn *tcp = THREAD_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)) + THREAD_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__); + + thread_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) { + THREAD_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; + thread_add_timer(master, pending_conn_timeout, pconn, PENDING_CONN_TIMEOUT, + &pconn->ev_timeout); + + return (pconn); +} + +void +pending_conn_del(struct pending_conn *pconn) +{ + THREAD_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 thread *thread) +{ + struct pending_conn *pconn = THREAD_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..a719d0c --- /dev/null +++ b/ldpd/pfkey.c @@ -0,0 +1,469 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#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) +{ + if (nbrp->auth.method == AUTH_NONE) + return (0); + + switch (nbr->auth.method) { + case AUTH_MD5SIG: + strlcpy(nbr->auth.md5key, nbrp->auth.md5key, + sizeof(nbr->auth.md5key)); + return (pfkey_md5sig_establish(nbr, nbrp)); + default: + break; + } + + return (0); +} + +int +pfkey_remove(struct nbr *nbr) +{ + if (nbr->auth.method == AUTH_NONE || !nbr->auth.established) + return (0); + + switch (nbr->auth.method) { + case AUTH_MD5SIG: + return (pfkey_md5sig_remove(nbr)); + default: + break; + } + + return (0); +} + +int +pfkey_init(void) +{ + if ((fd = socket(PF_KEY, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_KEY_V2)) == -1) { + if (errno == EPROTONOSUPPORT) { + log_warnx("PF_KEY not available"); + sysdep.no_pfkey = 1; + return (-1); + } else + fatal("pfkey setup failed"); + } + return (fd); +} +#endif /* __OpenBSD__ */ diff --git a/ldpd/rlfa.c b/ldpd/rlfa.c new file mode 100644 index 0000000..697ec08 --- /dev/null +++ b/ldpd/rlfa.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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..fe67917 --- /dev/null +++ b/ldpd/rlfa.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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..e865707 --- /dev/null +++ b/ldpd/socket.c @@ -0,0 +1,499 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 (!(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"); + + 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"); + + 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..083effb --- /dev/null +++ b/ldpd/subdir.am @@ -0,0 +1,69 @@ +# +# ldpd +# + +if LDPD +noinst_LIBRARIES += ldpd/libldp.a +sbin_PROGRAMS += ldpd/ldpd +vtysh_scan += ldpd/ldp_vty_cmds.c +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..019d846 --- /dev/null +++ b/ldpd/util.c @@ -0,0 +1,382 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2012 Alexander Bluhm <bluhm@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <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 +} |