diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /ldpd/neighbor.c | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ldpd/neighbor.c')
-rw-r--r-- | ldpd/neighbor.c | 855 |
1 files changed, 855 insertions, 0 deletions
diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c new file mode 100644 index 0000000..5209c55 --- /dev/null +++ b/ldpd/neighbor.c @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: ISC +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + */ + +#include <zebra.h> + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" + +DEFINE_HOOK(ldp_nbr_state_change, (struct nbr * nbr, int old_state), + (nbr, old_state)); + +static __inline int nbr_id_compare(const struct nbr *, const struct nbr *); +static __inline int nbr_addr_compare(const struct nbr *, const struct nbr *); +static __inline int nbr_pid_compare(const struct nbr *, const struct nbr *); +static void nbr_update_peerid(struct nbr *); +static void nbr_ktimer(struct event *thread); +static void nbr_start_ktimer(struct nbr *); +static void nbr_ktimeout(struct event *thread); +static void nbr_start_ktimeout(struct nbr *); +static void nbr_itimeout(struct event *thread); +static void nbr_start_itimeout(struct nbr *); +static void nbr_idtimer(struct event *thread); +static int nbr_act_session_operational(struct nbr *); +static void nbr_send_labelmappings(struct nbr *); +static __inline int nbr_params_compare(const struct nbr_params *, + const struct nbr_params *); + +RB_GENERATE(nbr_id_head, nbr, id_tree, nbr_id_compare) +RB_GENERATE(nbr_addr_head, nbr, addr_tree, nbr_addr_compare) +RB_GENERATE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare) +RB_GENERATE(nbrp_head, nbr_params, entry, nbr_params_compare) + +const struct { + int state; + enum nbr_event event; + enum nbr_action action; + int new_state; +} nbr_fsm_tbl[] = { + /* current state event that happened action to take resulting state */ +/* Passive Role */ + {NBR_STA_PRESENT, NBR_EVT_MATCH_ADJ, NBR_ACT_NOTHING, NBR_STA_INITIAL}, + {NBR_STA_INITIAL, NBR_EVT_INIT_RCVD, NBR_ACT_PASSIVE_INIT, NBR_STA_OPENREC}, + {NBR_STA_OPENREC, NBR_EVT_KEEPALIVE_RCVD, NBR_ACT_SESSION_EST, NBR_STA_OPER}, +/* Active Role */ + {NBR_STA_PRESENT, NBR_EVT_CONNECT_UP, NBR_ACT_CONNECT_SETUP, NBR_STA_INITIAL}, + {NBR_STA_INITIAL, NBR_EVT_INIT_SENT, NBR_ACT_NOTHING, NBR_STA_OPENSENT}, + {NBR_STA_OPENSENT, NBR_EVT_INIT_RCVD, NBR_ACT_KEEPALIVE_SEND, NBR_STA_OPENREC}, +/* Session Maintenance */ + {NBR_STA_OPER, NBR_EVT_PDU_RCVD, NBR_ACT_RST_KTIMEOUT, 0}, + {NBR_STA_SESSION, NBR_EVT_PDU_RCVD, NBR_ACT_NOTHING, 0}, + {NBR_STA_OPER, NBR_EVT_PDU_SENT, NBR_ACT_RST_KTIMER, 0}, + {NBR_STA_SESSION, NBR_EVT_PDU_SENT, NBR_ACT_NOTHING, 0}, +/* Session Close */ + {NBR_STA_PRESENT, NBR_EVT_CLOSE_SESSION, NBR_ACT_NOTHING, 0}, + {NBR_STA_SESSION, NBR_EVT_CLOSE_SESSION, NBR_ACT_CLOSE_SESSION, NBR_STA_PRESENT}, + {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0}, +}; + +const char * const nbr_event_names[] = { + "NOTHING", + "ADJACENCY MATCHED", + "CONNECTION UP", + "SESSION CLOSE", + "INIT RECEIVED", + "KEEPALIVE RECEIVED", + "PDU RECEIVED", + "PDU SENT", + "INIT SENT" +}; + +const char * const nbr_action_names[] = { + "NOTHING", + "RESET KEEPALIVE TIMEOUT", + "START NEIGHBOR SESSION", + "RESET KEEPALIVE TIMER", + "SETUP NEIGHBOR CONNECTION", + "SEND INIT AND KEEPALIVE", + "SEND KEEPALIVE", + "CLOSE SESSION" +}; + +struct nbr_id_head nbrs_by_id = RB_INITIALIZER(&nbrs_by_id); +struct nbr_addr_head nbrs_by_addr = RB_INITIALIZER(&nbrs_by_addr); +struct nbr_pid_head nbrs_by_pid = RB_INITIALIZER(&nbrs_by_pid); + +static __inline int +nbr_id_compare(const struct nbr *a, const struct nbr *b) +{ + return (ntohl(a->id.s_addr) - ntohl(b->id.s_addr)); +} + +static __inline int +nbr_addr_compare(const struct nbr *a, const struct nbr *b) +{ + if (a->af < b->af) + return (-1); + if (a->af > b->af) + return (1); + + return (ldp_addrcmp(a->af, &a->raddr, &b->raddr)); +} + +static __inline int +nbr_pid_compare(const struct nbr *a, const struct nbr *b) +{ + return (a->peerid - b->peerid); +} + +int +nbr_fsm(struct nbr *nbr, enum nbr_event event) +{ + struct timeval now; + int old_state; + int new_state = 0; + int i; + + old_state = nbr->state; + for (i = 0; nbr_fsm_tbl[i].state != -1; i++) + if (CHECK_FLAG(nbr_fsm_tbl[i].state, old_state) && + (nbr_fsm_tbl[i].event == event)) { + new_state = nbr_fsm_tbl[i].new_state; + break; + } + + if (nbr_fsm_tbl[i].state == -1) { + /* event outside of the defined fsm, ignore it. */ + log_warnx("%s: lsr-id %pI4, event %s not expected in state %s", __func__, &nbr->id, + nbr_event_names[event], nbr_state_name(old_state)); + return (0); + } + + if (new_state != 0) + nbr->state = new_state; + + if (old_state != nbr->state) { + log_debug("%s: event %s resulted in action %s and changing state for lsr-id %pI4 from %s to %s", + __func__, nbr_event_names[event], + nbr_action_names[nbr_fsm_tbl[i].action], + &nbr->id, nbr_state_name(old_state), + nbr_state_name(nbr->state)); + + hook_call(ldp_nbr_state_change, nbr, old_state); + + if (nbr->state == NBR_STA_OPER) { + gettimeofday(&now, NULL); + nbr->uptime = now.tv_sec; + } + } + + if (nbr->state == NBR_STA_OPER || nbr->state == NBR_STA_PRESENT) + nbr_stop_itimeout(nbr); + else + nbr_start_itimeout(nbr); + + switch (nbr_fsm_tbl[i].action) { + case NBR_ACT_RST_KTIMEOUT: + nbr_start_ktimeout(nbr); + break; + case NBR_ACT_RST_KTIMER: + nbr_start_ktimer(nbr); + break; + case NBR_ACT_SESSION_EST: + nbr_act_session_operational(nbr); + nbr_start_ktimer(nbr); + nbr_start_ktimeout(nbr); + if (nbr->v4_enabled) + send_address_all(nbr, AF_INET); + if (nbr->v6_enabled) + send_address_all(nbr, AF_INET6); + nbr_send_labelmappings(nbr); + break; + case NBR_ACT_CONNECT_SETUP: + nbr->tcp = tcp_new(nbr->fd, nbr); + + /* trigger next state */ + send_init(nbr); + nbr_fsm(nbr, NBR_EVT_INIT_SENT); + break; + case NBR_ACT_PASSIVE_INIT: + send_init(nbr); + send_keepalive(nbr); + break; + case NBR_ACT_KEEPALIVE_SEND: + nbr_start_ktimeout(nbr); + send_keepalive(nbr); + break; + case NBR_ACT_CLOSE_SESSION: + ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0); + session_close(nbr); + break; + case NBR_ACT_NOTHING: + /* do nothing */ + break; + } + + return (0); +} + +struct nbr * +nbr_new(struct in_addr id, int af, int ds_tlv, union ldpd_addr *addr, + uint32_t scope_id) +{ + struct nbr *nbr; + struct nbr_params *nbrp; + struct adj *adj; + struct pending_conn *pconn; + + log_debug("%s: lsr-id %pI4 transport-address %s", __func__, + &id, log_addr(af, addr)); + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal(__func__); + + RB_INIT(nbr_adj_head, &nbr->adj_tree); + nbr->state = NBR_STA_PRESENT; + nbr->peerid = 0; + nbr->af = af; + nbr->ds_tlv = ds_tlv; + if (af == AF_INET || ds_tlv) + nbr->v4_enabled = 1; + if (af == AF_INET6 || ds_tlv) + nbr->v6_enabled = 1; + nbr->id = id; + nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr; + nbr->raddr = *addr; + nbr->raddr_scope = scope_id; + nbr->conf_seqnum = 0; + + RB_FOREACH(adj, global_adj_head, &global.adj_tree) { + if (adj->lsr_id.s_addr == nbr->id.s_addr) { + adj->nbr = nbr; + RB_INSERT(nbr_adj_head, &nbr->adj_tree, adj); + } + } + + if (RB_INSERT(nbr_id_head, &nbrs_by_id, nbr) != NULL) + fatalx("nbr_new: RB_INSERT(nbrs_by_id) failed"); + if (RB_INSERT(nbr_addr_head, &nbrs_by_addr, nbr) != NULL) + fatalx("nbr_new: RB_INSERT(nbrs_by_addr) failed"); + + TAILQ_INIT(&nbr->mapping_list); + TAILQ_INIT(&nbr->withdraw_list); + TAILQ_INIT(&nbr->request_list); + TAILQ_INIT(&nbr->release_list); + TAILQ_INIT(&nbr->abortreq_list); + + nbrp = nbr_params_find(leconf, nbr->id); + if (nbrp) { + nbr->auth.method = nbrp->auth.method; +#ifdef __OpenBSD__ + if (pfkey_establish(nbr, nbrp) == -1) + fatalx("pfkey setup failed"); +#else + sock_set_md5sig( + (ldp_af_global_get(&global, nbr->af))->ldp_session_socket, + nbr->af, &nbr->raddr, nbrp->auth.md5key); +#endif + } + + pconn = pending_conn_find(nbr->af, &nbr->raddr); + if (pconn) { + session_accept_nbr(nbr, pconn->fd); + pending_conn_del(pconn); + } + + return (nbr); +} + +void +nbr_del(struct nbr *nbr) +{ + struct adj *adj; + + log_debug("%s: lsr-id %pI4", __func__, &nbr->id); + + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); +#ifdef __OpenBSD__ + pfkey_remove(nbr); +#else + sock_set_md5sig( + (ldp_af_global_get(&global, nbr->af))->ldp_session_socket, + nbr->af, &nbr->raddr, NULL); +#endif + nbr->auth.method = AUTH_NONE; + + if (nbr_pending_connect(nbr)) + EVENT_OFF(nbr->ev_connect); + nbr_stop_ktimer(nbr); + nbr_stop_ktimeout(nbr); + nbr_stop_itimeout(nbr); + nbr_stop_idtimer(nbr); + + mapping_list_clr(&nbr->mapping_list); + mapping_list_clr(&nbr->withdraw_list); + mapping_list_clr(&nbr->request_list); + mapping_list_clr(&nbr->release_list); + mapping_list_clr(&nbr->abortreq_list); + + while (!RB_EMPTY(nbr_adj_head, &nbr->adj_tree)) { + adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree); + + adj->nbr = NULL; + RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj); + } + + if (nbr->peerid) + RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr); + RB_REMOVE(nbr_id_head, &nbrs_by_id, nbr); + RB_REMOVE(nbr_addr_head, &nbrs_by_addr, nbr); + + free(nbr); +} + +static void +nbr_update_peerid(struct nbr *nbr) +{ + static uint32_t peercnt = 1; + + if (nbr->peerid) + RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr); + + /* get next unused peerid */ + while (nbr_find_peerid(++peercnt)) + ; + nbr->peerid = peercnt; + + if (RB_INSERT(nbr_pid_head, &nbrs_by_pid, nbr) != NULL) + fatalx("nbr_update_peerid: RB_INSERT(nbrs_by_pid) failed"); +} + +struct nbr * +nbr_find_ldpid(uint32_t lsr_id) +{ + struct nbr n; + n.id.s_addr = lsr_id; + return (RB_FIND(nbr_id_head, &nbrs_by_id, &n)); +} + +struct nbr *nbr_get_first_ldpid(void) +{ + return (RB_MIN(nbr_id_head, &nbrs_by_id)); +} + +struct nbr * +nbr_get_next_ldpid(uint32_t lsr_id) +{ + struct nbr *nbr; + nbr = nbr_find_ldpid(lsr_id); + if (nbr) + return (RB_NEXT(nbr_id_head, nbr)); + return NULL; +} + + +struct nbr * +nbr_find_addr(int af, union ldpd_addr *addr) +{ + struct nbr n; + n.af = af; + n.raddr = *addr; + return (RB_FIND(nbr_addr_head, &nbrs_by_addr, &n)); +} + +struct nbr * +nbr_find_peerid(uint32_t peerid) +{ + struct nbr n; + n.peerid = peerid; + return (RB_FIND(nbr_pid_head, &nbrs_by_pid, &n)); +} + +int +nbr_adj_count(struct nbr *nbr, int af) +{ + struct adj *adj; + int total = 0; + + RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) + if (adj_get_af(adj) == af) + total++; + + return (total); +} + +int +nbr_session_active_role(struct nbr *nbr) +{ + if (ldp_addrcmp(nbr->af, &nbr->laddr, &nbr->raddr) > 0) + return (1); + + return (0); +} + +/* timers */ + +/* Keepalive timer: timer to send keepalive message to neighbors */ + +static void nbr_ktimer(struct event *thread) +{ + struct nbr *nbr = EVENT_ARG(thread); + + nbr->keepalive_timer = NULL; + send_keepalive(nbr); + nbr_start_ktimer(nbr); +} + +static void +nbr_start_ktimer(struct nbr *nbr) +{ + int secs; + + /* send three keepalives per period */ + secs = nbr->keepalive / KEEPALIVE_PER_PERIOD; + EVENT_OFF(nbr->keepalive_timer); + nbr->keepalive_timer = NULL; + event_add_timer(master, nbr_ktimer, nbr, secs, &nbr->keepalive_timer); +} + +void +nbr_stop_ktimer(struct nbr *nbr) +{ + EVENT_OFF(nbr->keepalive_timer); +} + +/* Keepalive timeout: if the nbr hasn't sent keepalive */ + +static void nbr_ktimeout(struct event *thread) +{ + struct nbr *nbr = EVENT_ARG(thread); + + nbr->keepalive_timeout = NULL; + + log_debug("%s: lsr-id %pI4", __func__, &nbr->id); + + session_shutdown(nbr, S_KEEPALIVE_TMR, 0, 0); +} + +static void +nbr_start_ktimeout(struct nbr *nbr) +{ + EVENT_OFF(nbr->keepalive_timeout); + nbr->keepalive_timeout = NULL; + event_add_timer(master, nbr_ktimeout, nbr, nbr->keepalive, + &nbr->keepalive_timeout); +} + +void +nbr_stop_ktimeout(struct nbr *nbr) +{ + EVENT_OFF(nbr->keepalive_timeout); +} + +/* Session initialization timeout: if nbr got stuck in the initialization FSM */ + +static void nbr_itimeout(struct event *thread) +{ + struct nbr *nbr = EVENT_ARG(thread); + + log_debug("%s: lsr-id %pI4", __func__, &nbr->id); + + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); +} + +static void +nbr_start_itimeout(struct nbr *nbr) +{ + int secs; + + secs = INIT_FSM_TIMEOUT; + EVENT_OFF(nbr->init_timeout); + nbr->init_timeout = NULL; + event_add_timer(master, nbr_itimeout, nbr, secs, &nbr->init_timeout); +} + +void +nbr_stop_itimeout(struct nbr *nbr) +{ + EVENT_OFF(nbr->init_timeout); +} + +/* Init delay timer: timer to retry to iniziatize session */ + +static void nbr_idtimer(struct event *thread) +{ + struct nbr *nbr = EVENT_ARG(thread); + + nbr->initdelay_timer = NULL; + + log_debug("%s: lsr-id %pI4", __func__, &nbr->id); + + nbr_establish_connection(nbr); +} + +void +nbr_start_idtimer(struct nbr *nbr) +{ + int secs; + + secs = INIT_DELAY_TMR; + switch(nbr->idtimer_cnt) { + default: + /* do not further increase the counter */ + secs = MAX_DELAY_TMR; + break; + case 2: + secs *= 2; + /* FALLTHROUGH */ + case 1: + secs *= 2; + /* FALLTHROUGH */ + case 0: + nbr->idtimer_cnt++; + break; + } + + EVENT_OFF(nbr->initdelay_timer); + nbr->initdelay_timer = NULL; + event_add_timer(master, nbr_idtimer, nbr, secs, &nbr->initdelay_timer); +} + +void +nbr_stop_idtimer(struct nbr *nbr) +{ + EVENT_OFF(nbr->initdelay_timer); +} + +int +nbr_pending_idtimer(struct nbr *nbr) +{ + return (nbr->initdelay_timer != NULL); +} + +int +nbr_pending_connect(struct nbr *nbr) +{ + return (nbr->ev_connect != NULL); +} + +static void nbr_connect_cb(struct event *thread) +{ + struct nbr *nbr = EVENT_ARG(thread); + int error; + socklen_t len; + + nbr->ev_connect = NULL; + + len = sizeof(error); + if (getsockopt(nbr->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + log_warn("%s: getsockopt SOL_SOCKET SO_ERROR", __func__); + return; + } + + if (error) { + close(nbr->fd); + errno = error; + log_debug("%s: error while connecting to %s: %s", __func__, + log_addr(nbr->af, &nbr->raddr), strerror(errno)); + return; + } + + nbr_fsm(nbr, NBR_EVT_CONNECT_UP); +} + +int +nbr_establish_connection(struct nbr *nbr) +{ + union sockunion local_su; + union sockunion remote_su; + struct adj *adj; + struct nbr_params *nbrp; +#ifdef __OpenBSD__ + int opt = 1; +#endif + + nbr->fd = socket(nbr->af, SOCK_STREAM, 0); + if (nbr->fd == -1) { + log_warn("%s: error while creating socket", __func__); + return (-1); + } + sock_set_nonblock(nbr->fd); + + nbrp = nbr_params_find(leconf, nbr->id); + if (nbrp && nbrp->auth.method == AUTH_MD5SIG) { +#ifdef __OpenBSD__ + if (sysdep.no_pfkey || sysdep.no_md5sig) { + log_warnx("md5sig configured but not available"); + close(nbr->fd); + return (-1); + } + if (setsockopt(nbr->fd, IPPROTO_TCP, TCP_MD5SIG, + &opt, sizeof(opt)) == -1) { + log_warn("setsockopt md5sig"); + close(nbr->fd); + return (-1); + } +#else + sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr, nbrp->auth.md5key); +#endif + } + + if (nbr->af == AF_INET) { + if (sock_set_ipv4_tos(nbr->fd, IPTOS_PREC_INTERNETCONTROL) == -1) + log_warn("%s: lsr-id %pI4, sock_set_ipv4_tos error", + __func__, &nbr->id); + } else if (nbr->af == AF_INET6) { + if (sock_set_ipv6_dscp(nbr->fd, IPTOS_PREC_INTERNETCONTROL) == -1) + log_warn("%s: lsr-id %pI4, sock_set_ipv6_dscp error", + __func__, &nbr->id); + } + + addr2sa(nbr->af, &nbr->laddr, 0, &local_su); + addr2sa(nbr->af, &nbr->raddr, LDP_PORT, &remote_su); + if (nbr->af == AF_INET6 && nbr->raddr_scope) + addscope(&remote_su.sin6, nbr->raddr_scope); + + if (bind(nbr->fd, &local_su.sa, sockaddr_len(&local_su.sa)) == -1) { + log_warn("%s: error while binding socket to %s", __func__, + log_sockaddr(&local_su.sa)); + close(nbr->fd); + return (-1); + } + + if (nbr_gtsm_check(nbr->fd, nbr, nbrp)) { + close(nbr->fd); + return (-1); + } + + /* + * Send an extra hello to guarantee that the remote peer has formed + * an adjacency as well. + */ + RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) + send_hello(adj->source.type, adj->source.link.ia, + adj->source.target); + + if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa)) == -1) { + if (errno == EINPROGRESS) { + event_add_write(master, nbr_connect_cb, nbr, nbr->fd, + &nbr->ev_connect); + return (0); + } + log_warn("%s: error while connecting to %s", __func__, + log_sockaddr(&remote_su.sa)); + close(nbr->fd); + return (-1); + } + + /* connection completed immediately */ + nbr_fsm(nbr, NBR_EVT_CONNECT_UP); + + return (0); +} + +int +nbr_gtsm_enabled(struct nbr *nbr, struct nbr_params *nbrp) +{ + /* + * RFC 6720 - Section 3: + * "This document allows for the implementation to provide an option to + * statically (e.g., via configuration) and/or dynamically override the + * default behavior and enable/disable GTSM on a per-peer basis". + */ + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM)) + return (nbrp->gtsm_enabled); + + if (CHECK_FLAG((ldp_af_conf_get(leconf, nbr->af))->flags, F_LDPD_AF_NO_GTSM)) + return (0); + + /* By default, GTSM support has to be negotiated for LDPv4 */ + if (nbr->af == AF_INET && !CHECK_FLAG(nbr->flags, F_NBR_GTSM_NEGOTIATED)) + return (0); + + return (1); +} + +int +nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp) +{ + int ttl = 255; + + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM_HOPS)) + ttl = 256 - nbrp->gtsm_hops; + + switch (af) { + case AF_INET: + if (sock_set_ipv4_minttl(fd, ttl) == -1) + return (-1); + ttl = 255; + if (sock_set_ipv4_ucast_ttl(fd, ttl) == -1) + return (-1); + break; + case AF_INET6: + /* ignore any possible error */ + sock_set_ipv6_minhopcount(fd, ttl); + ttl = 255; + if (sock_set_ipv6_ucast_hops(fd, ttl) == -1) + return (-1); + break; + default: + fatalx("nbr_gtsm_setup: unknown af"); + } + + return (0); +} + +int +nbr_gtsm_check(int fd, struct nbr *nbr, struct nbr_params *nbrp) +{ + if (!nbr_gtsm_enabled(nbr, nbrp)) { + switch (nbr->af) { + case AF_INET: + sock_set_ipv4_ucast_ttl(fd, -1); + break; + case AF_INET6: + /* + * Send packets with a Hop Limit of 255 even when GSTM + * is disabled to guarantee interoperability. + */ + sock_set_ipv6_ucast_hops(fd, 255); + break; + default: + fatalx("nbr_gtsm_check: unknown af"); + break; + } + return (0); + } + + if (nbr_gtsm_setup(fd, nbr->af, nbrp) == -1) { + log_warnx("%s: error enabling GTSM for lsr-id %pI4", __func__, &nbr->id); + return (-1); + } + + return (0); +} + +static int +nbr_act_session_operational(struct nbr *nbr) +{ + struct lde_nbr lde_nbr; + + nbr->idtimer_cnt = 0; + + /* this is necessary to avoid ipc synchronization issues */ + nbr_update_peerid(nbr); + + ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_LDP_SYNC_START); + + memset(&lde_nbr, 0, sizeof(lde_nbr)); + lde_nbr.id = nbr->id; + lde_nbr.v4_enabled = nbr->v4_enabled; + lde_nbr.v6_enabled = nbr->v6_enabled; + lde_nbr.flags = nbr->flags; + return (ldpe_imsg_compose_lde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, + &lde_nbr, sizeof(lde_nbr))); +} + +static void +nbr_send_labelmappings(struct nbr *nbr) +{ + ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0, NULL, 0); +} + +static __inline int +nbr_params_compare(const struct nbr_params *a, const struct nbr_params *b) +{ + return (ntohl(a->lsr_id.s_addr) - ntohl(b->lsr_id.s_addr)); +} + +struct nbr_params * +nbr_params_new(struct in_addr lsr_id) +{ + struct nbr_params *nbrp; + + if ((nbrp = calloc(1, sizeof(*nbrp))) == NULL) + fatal(__func__); + + nbrp->lsr_id = lsr_id; + nbrp->auth.method = AUTH_NONE; + + return (nbrp); +} + +struct nbr_params * +nbr_params_find(struct ldpd_conf *xconf, struct in_addr lsr_id) +{ + struct nbr_params nbrp; + nbrp.lsr_id = lsr_id; + return (RB_FIND(nbrp_head, &xconf->nbrp_tree, &nbrp)); +} + +uint16_t +nbr_get_keepalive(int af, struct in_addr lsr_id) +{ + struct nbr_params *nbrp; + + nbrp = nbr_params_find(leconf, lsr_id); + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_KEEPALIVE)) + return (nbrp->keepalive); + + return ((ldp_af_conf_get(leconf, af))->keepalive); +} + +struct ctl_nbr * +nbr_to_ctl(struct nbr *nbr) +{ + static struct ctl_nbr nctl; + struct timeval now; + + nctl.af = nbr->af; + nctl.id = nbr->id; + nctl.laddr = nbr->laddr; + nctl.lport = nbr->tcp ? nbr->tcp->lport : 0; + nctl.raddr = nbr->raddr; + nctl.rport = nbr->tcp ? nbr->tcp->rport : 0; + nctl.auth_method = nbr->auth.method; + nctl.holdtime = nbr->keepalive; + nctl.nbr_state = nbr->state; + nctl.stats = nbr->stats; + nctl.flags = nbr->flags; + nctl.max_pdu_len = nbr->max_pdu_len; + nctl.hold_time_remaining = event_timer_remain_second(nbr->keepalive_timer); + + gettimeofday(&now, NULL); + if (nbr->state == NBR_STA_OPER) { + nctl.uptime = now.tv_sec - nbr->uptime; + } else + nctl.uptime = 0; + + return (&nctl); +} + +void +nbr_clear_ctl(struct ctl_nbr *nctl) +{ + struct nbr *nbr; + + RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) { + if (ldp_addrisset(nctl->af, &nctl->raddr) && + ldp_addrcmp(nctl->af, &nctl->raddr, &nbr->raddr)) + continue; + + log_debug("%s: neighbor %s manually cleared", __func__, + log_addr(nbr->af, &nbr->raddr)); + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + } +} |