// SPDX-License-Identifier: ISC /* $OpenBSD$ */ /* * Copyright (c) 2013, 2016 Renato Westphal * Copyright (c) 2009 Michele Marchetto * Copyright (c) 2005 Claudio Jeker * Copyright (c) 2004, 2005, 2008 Esben Norby */ #include #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; if (nbr->idtimer_cnt > 2) { /* do not further increase the counter */ secs = MAX_DELAY_TMR; } else { secs = INIT_DELAY_TMR * (1 << nbr->idtimer_cnt); nbr->idtimer_cnt++; } 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); } }