diff options
Diffstat (limited to 'ldpd/adjacency.c')
-rw-r--r-- | ldpd/adjacency.c | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c new file mode 100644 index 0000000..0108af8 --- /dev/null +++ b/ldpd/adjacency.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: ISC +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + */ + +#include <zebra.h> + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +static __inline int adj_compare(const struct adj *, const struct adj *); +static void adj_itimer(struct event *); +static __inline int tnbr_compare(const struct tnbr *, const struct tnbr *); +static void tnbr_del(struct ldpd_conf *, struct tnbr *); +static void tnbr_start(struct tnbr *); +static void tnbr_stop(struct tnbr *); +static void tnbr_hello_timer(struct event *); +static void tnbr_start_hello_timer(struct tnbr *); +static void tnbr_stop_hello_timer(struct tnbr *); + +RB_GENERATE(global_adj_head, adj, global_entry, adj_compare) +RB_GENERATE(nbr_adj_head, adj, nbr_entry, adj_compare) +RB_GENERATE(ia_adj_head, adj, ia_entry, adj_compare) +RB_GENERATE(tnbr_head, tnbr, entry, tnbr_compare) + +static __inline int +adj_compare(const struct adj *a, const struct adj *b) +{ + if (adj_get_af(a) < adj_get_af(b)) + return (-1); + if (adj_get_af(a) > adj_get_af(b)) + return (1); + + if (ntohl(a->lsr_id.s_addr) < ntohl(b->lsr_id.s_addr)) + return (-1); + if (ntohl(a->lsr_id.s_addr) > ntohl(b->lsr_id.s_addr)) + return (1); + + if (a->source.type < b->source.type) + return (-1); + if (a->source.type > b->source.type) + return (1); + + switch (a->source.type) { + case HELLO_LINK: + if (if_cmp_name_func(a->source.link.ia->iface->name, + b->source.link.ia->iface->name) < 0) + return (-1); + if (if_cmp_name_func(a->source.link.ia->iface->name, + b->source.link.ia->iface->name) > 0) + return (1); + return (ldp_addrcmp(a->source.link.ia->af, + &a->source.link.src_addr, &b->source.link.src_addr)); + case HELLO_TARGETED: + return (ldp_addrcmp(a->source.target->af, + &a->source.target->addr, &b->source.target->addr)); + default: + fatalx("adj_compare: unknown hello type"); + } + + return (0); +} + +struct adj * +adj_new(struct in_addr lsr_id, struct hello_source *source, + union ldpd_addr *addr) +{ + struct adj *adj; + + log_debug("%s: lsr-id %pI4, %s", __func__, &lsr_id, + log_hello_src(source)); + + if ((adj = calloc(1, sizeof(*adj))) == NULL) + fatal(__func__); + + adj->lsr_id = lsr_id; + adj->nbr = NULL; + adj->source = *source; + adj->trans_addr = *addr; + + RB_INSERT(global_adj_head, &global.adj_tree, adj); + + switch (source->type) { + case HELLO_LINK: + RB_INSERT(ia_adj_head, &source->link.ia->adj_tree, adj); + break; + case HELLO_TARGETED: + source->target->adj = adj; + break; + } + + return (adj); +} + +void +adj_del(struct adj *adj, uint32_t notif_status) +{ + struct nbr *nbr = adj->nbr; + + log_debug("%s: lsr-id %pI4, %s (%s)", __func__, &adj->lsr_id, + log_hello_src(&adj->source), af_name(adj_get_af(adj))); + + adj_stop_itimer(adj); + + RB_REMOVE(global_adj_head, &global.adj_tree, adj); + if (nbr) + RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj); + switch (adj->source.type) { + case HELLO_LINK: + RB_REMOVE(ia_adj_head, &adj->source.link.ia->adj_tree, adj); + + if (nbr) + ldp_sync_fsm_adj_event(adj, LDP_SYNC_EVT_ADJ_DEL); + break; + case HELLO_TARGETED: + adj->source.target->adj = NULL; + break; + } + + free(adj); + + /* + * If the neighbor still exists but none of its remaining + * adjacencies (if any) are from the preferred address-family, + * then delete it. + */ + if (nbr && nbr_adj_count(nbr, nbr->af) == 0) { + session_shutdown(nbr, notif_status, 0, 0); + nbr_del(nbr); + } +} + +struct adj * +adj_find(struct in_addr lsr_id, struct hello_source *source) +{ + struct adj adj; + adj.lsr_id = lsr_id; + adj.source = *source; + return (RB_FIND(global_adj_head, &global.adj_tree, &adj)); +} + +int +adj_get_af(const struct adj *adj) +{ + switch (adj->source.type) { + case HELLO_LINK: + return (adj->source.link.ia->af); + case HELLO_TARGETED: + return (adj->source.target->af); + default: + fatalx("adj_get_af: unknown hello type"); + } +} + +/* adjacency timers */ + +/* ARGSUSED */ +static void adj_itimer(struct event *thread) +{ + struct adj *adj = EVENT_ARG(thread); + + adj->inactivity_timer = NULL; + + log_debug("%s: lsr-id %pI4", __func__, &adj->lsr_id); + + if (adj->source.type == HELLO_TARGETED) { + if (!CHECK_FLAG(adj->source.target->flags, F_TNBR_CONFIGURED) && + adj->source.target->pw_count == 0 && + adj->source.target->rlfa_count == 0) { + /* remove dynamic targeted neighbor */ + tnbr_del(leconf, adj->source.target); + return; + } + } + + adj_del(adj, S_HOLDTIME_EXP); +} + +void +adj_start_itimer(struct adj *adj) +{ + EVENT_OFF(adj->inactivity_timer); + adj->inactivity_timer = NULL; + event_add_timer(master, adj_itimer, adj, adj->holdtime, + &adj->inactivity_timer); +} + +void +adj_stop_itimer(struct adj *adj) +{ + EVENT_OFF(adj->inactivity_timer); +} + +/* targeted neighbors */ + +static __inline int +tnbr_compare(const struct tnbr *a, const struct tnbr *b) +{ + if (a->af < b->af) + return (-1); + if (a->af > b->af) + return (1); + + return (ldp_addrcmp(a->af, &a->addr, &b->addr)); +} + +struct tnbr * +tnbr_new(int af, union ldpd_addr *addr) +{ + struct tnbr *tnbr; + + if ((tnbr = calloc(1, sizeof(*tnbr))) == NULL) + fatal(__func__); + + tnbr->af = af; + tnbr->addr = *addr; + tnbr->state = TNBR_STA_DOWN; + + return (tnbr); +} + +static void +tnbr_del(struct ldpd_conf *xconf, struct tnbr *tnbr) +{ + tnbr_stop(tnbr); + RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr); + free(tnbr); +} + +struct tnbr * +tnbr_find(struct ldpd_conf *xconf, int af, union ldpd_addr *addr) +{ + struct tnbr tnbr; + tnbr.af = af; + tnbr.addr = *addr; + return (RB_FIND(tnbr_head, &xconf->tnbr_tree, &tnbr)); +} + +struct tnbr * +tnbr_check(struct ldpd_conf *xconf, struct tnbr *tnbr) +{ + if (!CHECK_FLAG(tnbr->flags, (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC)) && + tnbr->pw_count == 0 && tnbr->rlfa_count == 0) { + tnbr_del(xconf, tnbr); + return (NULL); + } + + return (tnbr); +} + +static void +tnbr_start(struct tnbr *tnbr) +{ + send_hello(HELLO_TARGETED, NULL, tnbr); + tnbr_start_hello_timer(tnbr); + tnbr->state = TNBR_STA_ACTIVE; +} + +static void +tnbr_stop(struct tnbr *tnbr) +{ + tnbr_stop_hello_timer(tnbr); + if (tnbr->adj) + adj_del(tnbr->adj, S_SHUTDOWN); + tnbr->state = TNBR_STA_DOWN; +} + +void +tnbr_update(struct tnbr *tnbr) +{ + int socket_ok, rtr_id_ok; + + if ((ldp_af_global_get(&global, tnbr->af))->ldp_edisc_socket != -1) + socket_ok = 1; + else + socket_ok = 0; + + if (ldp_rtr_id_get(leconf) != INADDR_ANY) + rtr_id_ok = 1; + else + rtr_id_ok = 0; + + if (tnbr->state == TNBR_STA_DOWN) { + if (!socket_ok || !rtr_id_ok) + return; + + tnbr_start(tnbr); + } else if (tnbr->state == TNBR_STA_ACTIVE) { + if (socket_ok && rtr_id_ok) + return; + + tnbr_stop(tnbr); + } +} + +void +tnbr_update_all(int af) +{ + struct tnbr *tnbr; + + /* update targeted neighbors */ + RB_FOREACH(tnbr, tnbr_head, &leconf->tnbr_tree) + if (tnbr->af == af || af == AF_UNSPEC) + tnbr_update(tnbr); +} + +uint16_t +tnbr_get_hello_holdtime(struct tnbr *tnbr) +{ + if ((ldp_af_conf_get(leconf, tnbr->af))->thello_holdtime != 0) + return ((ldp_af_conf_get(leconf, tnbr->af))->thello_holdtime); + + return (leconf->thello_holdtime); +} + +uint16_t +tnbr_get_hello_interval(struct tnbr *tnbr) +{ + if ((ldp_af_conf_get(leconf, tnbr->af))->thello_interval != 0) + return ((ldp_af_conf_get(leconf, tnbr->af))->thello_interval); + + return (leconf->thello_interval); +} + +/* target neighbors timers */ + +/* ARGSUSED */ +static void tnbr_hello_timer(struct event *thread) +{ + struct tnbr *tnbr = EVENT_ARG(thread); + + tnbr->hello_timer = NULL; + send_hello(HELLO_TARGETED, NULL, tnbr); + tnbr_start_hello_timer(tnbr); +} + +static void +tnbr_start_hello_timer(struct tnbr *tnbr) +{ + EVENT_OFF(tnbr->hello_timer); + tnbr->hello_timer = NULL; + event_add_timer(master, tnbr_hello_timer, tnbr, + tnbr_get_hello_interval(tnbr), &tnbr->hello_timer); +} + +static void +tnbr_stop_hello_timer(struct tnbr *tnbr) +{ + EVENT_OFF(tnbr->hello_timer); +} + +struct ctl_adj * +adj_to_ctl(struct adj *adj) +{ + static struct ctl_adj actl; + + actl.af = adj_get_af(adj); + actl.id = adj->lsr_id; + actl.type = adj->source.type; + switch (adj->source.type) { + case HELLO_LINK: + memcpy(actl.ifname, adj->source.link.ia->iface->name, + sizeof(actl.ifname)); + actl.src_addr = adj->source.link.src_addr; + break; + case HELLO_TARGETED: + actl.src_addr = adj->source.target->addr; + break; + } + actl.holdtime = adj->holdtime; + actl.holdtime_remaining = + event_timer_remain_second(adj->inactivity_timer); + actl.trans_addr = adj->trans_addr; + actl.ds_tlv = adj->ds_tlv; + + return (&actl); +} |