/* $OpenBSD$ */ /* * Copyright (c) 2013, 2015 Renato Westphal * Copyright (c) 2009 Michele Marchetto * Copyright (c) 2005 Claudio Jeker * Copyright (c) 2004, 2005, 2008 Esben Norby * * 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 #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); }