summaryrefslogtreecommitdiffstats
path: root/ldpd/adjacency.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldpd/adjacency.c')
-rw-r--r--ldpd/adjacency.c394
1 files changed, 394 insertions, 0 deletions
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);
+}