/* $OpenBSD$ */ /* * Copyright (c) 2013, 2016 Renato Westphal * Copyright (c) 2005 Claudio Jeker * Copyright (c) 2004, 2008 Esben Norby * Copyright (c) 2003, 2004 Henning Brauer * * 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 "lde.h" #include "control.h" #include "log.h" #include "ldp_debug.h" #include #include "memory.h" #include "privs.h" #include "sigevent.h" static void ldpe_shutdown(void); static int ldpe_dispatch_main(struct thread *); static int ldpe_dispatch_lde(struct thread *); #ifdef __OpenBSD__ static int ldpe_dispatch_pfkey(struct thread *); #endif static void ldpe_setup_sockets(int, int, int, int); static void ldpe_close_sockets(int); static void ldpe_iface_af_ctl(struct ctl_conn *, int, unsigned int); struct ldpd_conf *leconf; #ifdef __OpenBSD__ struct ldpd_sysdep sysdep; #endif static struct imsgev *iev_main, *iev_main_sync; static struct imsgev *iev_lde; #ifdef __OpenBSD__ static struct thread *pfkey_ev; #endif /* Master of threads. */ struct thread_master *master; /* ldpe privileges */ static zebra_capabilities_t _caps_p [] = { ZCAP_BIND, ZCAP_NET_ADMIN }; struct zebra_privs_t ldpe_privs = { #if defined(FRR_USER) && defined(FRR_GROUP) .user = FRR_USER, .group = FRR_GROUP, #endif #if defined(VTY_GROUP) .vty_group = VTY_GROUP, #endif .caps_p = _caps_p, .cap_num_p = array_size(_caps_p), .cap_num_i = 0 }; /* SIGINT / SIGTERM handler. */ static void sigint(void) { ldpe_shutdown(); } static struct quagga_signal_t ldpe_signals[] = { { .signal = SIGHUP, /* ignore */ }, { .signal = SIGINT, .handler = &sigint, }, { .signal = SIGTERM, .handler = &sigint, }, }; /* label distribution protocol engine */ void ldpe(void) { struct thread thread; #ifdef HAVE_SETPROCTITLE setproctitle("ldp engine"); #endif ldpd_process = PROC_LDP_ENGINE; log_procname = log_procnames[ldpd_process]; master = thread_master_create(); /* setup signal handler */ signal_init(master, array_size(ldpe_signals), ldpe_signals); /* setup pipes and event handlers to the parent process */ if ((iev_main = calloc(1, sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC); iev_main->handler_read = ldpe_dispatch_main; iev_main->ev_read = thread_add_read(master, iev_main->handler_read, iev_main, iev_main->ibuf.fd); iev_main->handler_write = ldp_write_handler; if ((iev_main_sync = calloc(1, sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_main_sync->ibuf, LDPD_FD_SYNC); /* create base configuration */ leconf = config_new_empty(); /* Fetch next active thread. */ while (thread_fetch(master, &thread)) thread_call(&thread); } void ldpe_init(struct ldpd_init *init) { /* drop privileges */ if (init->user) ldpe_privs.user = init->user; if (init->group) ldpe_privs.group = init->group; zprivs_init(&ldpe_privs); /* listen on ldpd control socket */ strlcpy(ctl_sock_path, init->ctl_sock_path, sizeof(ctl_sock_path)); if (control_init(ctl_sock_path) == -1) fatalx("control socket setup failed"); TAILQ_INIT(&ctl_conns); control_listen(); #ifdef HAVE_PLEDGE if (pledge("stdio cpath inet mcast recvfd", NULL) == -1) fatal("pledge"); #endif LIST_INIT(&global.addr_list); RB_INIT(&global.adj_tree); TAILQ_INIT(&global.pending_conns); if (inet_pton(AF_INET, AllRouters_v4, &global.mcast_addr_v4) != 1) fatal("inet_pton"); if (inet_pton(AF_INET6, AllRouters_v6, &global.mcast_addr_v6) != 1) fatal("inet_pton"); #ifdef __OpenBSD__ global.pfkeysock = pfkey_init(); if (sysdep.no_pfkey == 0) pfkey_ev = thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock); #endif /* mark sockets as closed */ global.ipv4.ldp_disc_socket = -1; global.ipv4.ldp_edisc_socket = -1; global.ipv4.ldp_session_socket = -1; global.ipv6.ldp_disc_socket = -1; global.ipv6.ldp_edisc_socket = -1; global.ipv6.ldp_session_socket = -1; if ((pkt_ptr = calloc(1, IBUF_READ_SIZE)) == NULL) fatal(__func__); accept_init(); } static void ldpe_shutdown(void) { struct if_addr *if_addr; struct adj *adj; /* close pipes */ if (iev_lde) { msgbuf_write(&iev_lde->ibuf.w); msgbuf_clear(&iev_lde->ibuf.w); close(iev_lde->ibuf.fd); } msgbuf_write(&iev_main->ibuf.w); msgbuf_clear(&iev_main->ibuf.w); close(iev_main->ibuf.fd); msgbuf_clear(&iev_main_sync->ibuf.w); close(iev_main_sync->ibuf.fd); control_cleanup(ctl_sock_path); config_clear(leconf); #ifdef __OpenBSD__ if (sysdep.no_pfkey == 0) { THREAD_READ_OFF(pfkey_ev); close(global.pfkeysock); } #endif ldpe_close_sockets(AF_INET); ldpe_close_sockets(AF_INET6); /* remove addresses from global list */ while ((if_addr = LIST_FIRST(&global.addr_list)) != NULL) { LIST_REMOVE(if_addr, entry); free(if_addr); } while ((adj = RB_ROOT(&global.adj_tree)) != NULL) adj_del(adj, S_SHUTDOWN); /* clean up */ if (iev_lde) free(iev_lde); free(iev_main); free(iev_main_sync); free(pkt_ptr); log_info("ldp engine exiting"); exit(0); } /* imesg */ int ldpe_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen) { return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen)); } void ldpe_imsg_compose_parent_sync(int type, pid_t pid, void *data, uint16_t datalen) { imsg_compose_event(iev_main_sync, type, 0, pid, -1, data, datalen); imsg_flush(&iev_main_sync->ibuf); } int ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data, uint16_t datalen) { return (imsg_compose_event(iev_lde, type, peerid, pid, -1, data, datalen)); } /* ARGSUSED */ static int ldpe_dispatch_main(struct thread *thread) { static struct ldpd_conf *nconf; struct iface *niface; struct tnbr *ntnbr; struct nbr_params *nnbrp; static struct l2vpn *l2vpn, *nl2vpn; struct l2vpn_if *lif, *nlif; struct l2vpn_pw *pw, *npw; struct imsg imsg; int fd = THREAD_FD(thread); struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct iface *iface = NULL; struct kif *kif; int af; enum socket_type *socket_type; static int disc_socket = -1; static int edisc_socket = -1; static int session_socket = -1; struct nbr *nbr; #ifdef __OpenBSD__ struct nbr_params *nbrp; #endif int n, shut = 0; iev->ev_read = NULL; if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); if (n == 0) /* connection closed */ shut = 1; for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) fatal("ldpe_dispatch_main: imsg_get error"); if (n == 0) break; switch (imsg.hdr.type) { case IMSG_IFSTATUS: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kif)) fatalx("IFSTATUS imsg with wrong len"); kif = imsg.data; iface = if_lookup_name(leconf, kif->ifname); if (iface) { if_update_info(iface, kif); ldp_if_update(iface, AF_UNSPEC); break; } RB_FOREACH(l2vpn, l2vpn_head, &leconf->l2vpn_tree) { lif = l2vpn_if_find(l2vpn, kif->ifname); if (lif) { l2vpn_if_update_info(lif, kif); l2vpn_if_update(lif); break; } pw = l2vpn_pw_find(l2vpn, kif->ifname); if (pw) { l2vpn_pw_update_info(pw, kif); break; } } break; case IMSG_NEWADDR: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr)) fatalx("NEWADDR imsg with wrong len"); if_addr_add(imsg.data); break; case IMSG_DELADDR: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr)) fatalx("DELADDR imsg with wrong len"); if_addr_del(imsg.data); break; case IMSG_SOCKET_IPC: if (iev_lde) { log_warnx("%s: received unexpected imsg fd " "to lde", __func__); break; } if ((fd = imsg.fd) == -1) { log_warnx("%s: expected to receive imsg fd to " "lde but didn't receive any", __func__); break; } if ((iev_lde = malloc(sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_lde->ibuf, fd); iev_lde->handler_read = ldpe_dispatch_lde; iev_lde->ev_read = thread_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd); iev_lde->handler_write = ldp_write_handler; iev_lde->ev_write = NULL; break; case IMSG_INIT: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ldpd_init)) fatalx("INIT imsg with wrong len"); memcpy(&init, imsg.data, sizeof(init)); ldpe_init(&init); break; case IMSG_CLOSE_SOCKETS: af = imsg.hdr.peerid; RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { if (nbr->af != af) continue; session_shutdown(nbr, S_SHUTDOWN, 0, 0); #ifdef __OpenBSD__ pfkey_remove(nbr); #endif nbr->auth.method = AUTH_NONE; } ldpe_close_sockets(af); if_update_all(af); tnbr_update_all(af); disc_socket = -1; edisc_socket = -1; session_socket = -1; if ((ldp_af_conf_get(leconf, af))->flags & F_LDPD_AF_ENABLED) ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, af, NULL, 0); break; case IMSG_SOCKET_NET: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(enum socket_type)) fatalx("SOCKET_NET imsg with wrong len"); socket_type = imsg.data; switch (*socket_type) { case LDP_SOCKET_DISC: disc_socket = imsg.fd; break; case LDP_SOCKET_EDISC: edisc_socket = imsg.fd; break; case LDP_SOCKET_SESSION: session_socket = imsg.fd; break; } break; case IMSG_SETUP_SOCKETS: af = imsg.hdr.peerid; if (disc_socket == -1 || edisc_socket == -1 || session_socket == -1) { if (disc_socket != -1) close(disc_socket); if (edisc_socket != -1) close(edisc_socket); if (session_socket != -1) close(session_socket); break; } ldpe_setup_sockets(af, disc_socket, edisc_socket, session_socket); if_update_all(af); tnbr_update_all(af); RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { if (nbr->af != af) continue; nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr; #ifdef __OpenBSD__ nbrp = nbr_params_find(leconf, nbr->id); if (nbrp) { nbr->auth.method = nbrp->auth.method; if (pfkey_establish(nbr, nbrp) == -1) fatalx("pfkey setup failed"); } #endif if (nbr_session_active_role(nbr)) nbr_establish_connection(nbr); } break; case IMSG_RTRID_UPDATE: memcpy(&global.rtr_id, imsg.data, sizeof(global.rtr_id)); if (leconf->rtr_id.s_addr == INADDR_ANY) { ldpe_reset_nbrs(AF_UNSPEC); } if_update_all(AF_UNSPEC); tnbr_update_all(AF_UNSPEC); break; case IMSG_RECONF_CONF: if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL) fatal(NULL); memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); RB_INIT(&nconf->iface_tree); RB_INIT(&nconf->tnbr_tree); RB_INIT(&nconf->nbrp_tree); RB_INIT(&nconf->l2vpn_tree); break; case IMSG_RECONF_IFACE: if ((niface = malloc(sizeof(struct iface))) == NULL) fatal(NULL); memcpy(niface, imsg.data, sizeof(struct iface)); RB_INSERT(iface_head, &nconf->iface_tree, niface); break; case IMSG_RECONF_TNBR: if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL) fatal(NULL); memcpy(ntnbr, imsg.data, sizeof(struct tnbr)); RB_INSERT(tnbr_head, &nconf->tnbr_tree, ntnbr); break; case IMSG_RECONF_NBRP: if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL) fatal(NULL); memcpy(nnbrp, imsg.data, sizeof(struct nbr_params)); RB_INSERT(nbrp_head, &nconf->nbrp_tree, nnbrp); break; case IMSG_RECONF_L2VPN: if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL) fatal(NULL); memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn)); RB_INIT(&nl2vpn->if_tree); RB_INIT(&nl2vpn->pw_tree); RB_INIT(&nl2vpn->pw_inactive_tree); RB_INSERT(l2vpn_head, &nconf->l2vpn_tree, nl2vpn); break; case IMSG_RECONF_L2VPN_IF: if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL) fatal(NULL); memcpy(nlif, imsg.data, sizeof(struct l2vpn_if)); RB_INSERT(l2vpn_if_head, &nl2vpn->if_tree, nlif); break; case IMSG_RECONF_L2VPN_PW: if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) fatal(NULL); memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_tree, npw); break; case IMSG_RECONF_L2VPN_IPW: if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) fatal(NULL); memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); RB_INSERT(l2vpn_pw_head, &nl2vpn->pw_inactive_tree, npw); break; case IMSG_RECONF_END: merge_config(leconf, nconf); ldp_clear_config(nconf); nconf = NULL; global.conf_seqnum++; break; case IMSG_CTL_END: control_imsg_relay(&imsg); break; case IMSG_DEBUG_UPDATE: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(ldp_debug)) { log_warnx("%s: wrong imsg len", __func__); break; } memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug)); break; default: log_debug("ldpe_dispatch_main: error handling imsg %d", imsg.hdr.type); break; } imsg_free(&imsg); } if (!shut) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handlers and exit */ THREAD_READ_OFF(iev->ev_read); THREAD_WRITE_OFF(iev->ev_write); ldpe_shutdown(); } return (0); } /* ARGSUSED */ static int ldpe_dispatch_lde(struct thread *thread) { struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; struct map *map; struct notify_msg *nm; struct nbr *nbr; int n, shut = 0; iev->ev_read = NULL; if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); if (n == 0) /* connection closed */ shut = 1; for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) fatal("ldpe_dispatch_lde: imsg_get error"); if (n == 0) break; switch (imsg.hdr.type) { case IMSG_MAPPING_ADD: case IMSG_RELEASE_ADD: case IMSG_REQUEST_ADD: case IMSG_WITHDRAW_ADD: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct map)) fatalx("invalid size of map request"); map = imsg.data; nbr = nbr_find_peerid(imsg.hdr.peerid); if (nbr == NULL) { log_debug("ldpe_dispatch_lde: cannot find " "neighbor"); break; } if (nbr->state != NBR_STA_OPER) break; switch (imsg.hdr.type) { case IMSG_MAPPING_ADD: mapping_list_add(&nbr->mapping_list, map); break; case IMSG_RELEASE_ADD: mapping_list_add(&nbr->release_list, map); break; case IMSG_REQUEST_ADD: mapping_list_add(&nbr->request_list, map); break; case IMSG_WITHDRAW_ADD: mapping_list_add(&nbr->withdraw_list, map); break; } break; case IMSG_MAPPING_ADD_END: case IMSG_RELEASE_ADD_END: case IMSG_REQUEST_ADD_END: case IMSG_WITHDRAW_ADD_END: nbr = nbr_find_peerid(imsg.hdr.peerid); if (nbr == NULL) { log_debug("ldpe_dispatch_lde: cannot find " "neighbor"); break; } if (nbr->state != NBR_STA_OPER) break; switch (imsg.hdr.type) { case IMSG_MAPPING_ADD_END: send_labelmessage(nbr, MSG_TYPE_LABELMAPPING, &nbr->mapping_list); break; case IMSG_RELEASE_ADD_END: send_labelmessage(nbr, MSG_TYPE_LABELRELEASE, &nbr->release_list); break; case IMSG_REQUEST_ADD_END: send_labelmessage(nbr, MSG_TYPE_LABELREQUEST, &nbr->request_list); break; case IMSG_WITHDRAW_ADD_END: send_labelmessage(nbr, MSG_TYPE_LABELWITHDRAW, &nbr->withdraw_list); break; } break; case IMSG_NOTIFICATION_SEND: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct notify_msg)) fatalx("invalid size of OE request"); nm = imsg.data; nbr = nbr_find_peerid(imsg.hdr.peerid); if (nbr == NULL) { log_debug("ldpe_dispatch_lde: cannot find " "neighbor"); break; } if (nbr->state != NBR_STA_OPER) break; send_notification_full(nbr->tcp, nm); break; case IMSG_CTL_END: case IMSG_CTL_SHOW_LIB_BEGIN: case IMSG_CTL_SHOW_LIB_RCVD: case IMSG_CTL_SHOW_LIB_SENT: case IMSG_CTL_SHOW_LIB_END: case IMSG_CTL_SHOW_L2VPN_PW: case IMSG_CTL_SHOW_L2VPN_BINDING: control_imsg_relay(&imsg); break; default: log_debug("ldpe_dispatch_lde: error handling imsg %d", imsg.hdr.type); break; } imsg_free(&imsg); } if (!shut) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handlers and exit */ THREAD_READ_OFF(iev->ev_read); THREAD_WRITE_OFF(iev->ev_write); ldpe_shutdown(); } return (0); } #ifdef __OpenBSD__ /* ARGSUSED */ static int ldpe_dispatch_pfkey(struct thread *thread) { int fd = THREAD_FD(thread); pfkey_ev = thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock); if (pfkey_read(fd, NULL) == -1) fatal("pfkey_read failed, exiting..."); return (0); } #endif /* __OpenBSD__ */ static void ldpe_setup_sockets(int af, int disc_socket, int edisc_socket, int session_socket) { struct ldpd_af_global *af_global; af_global = ldp_af_global_get(&global, af); /* discovery socket */ af_global->ldp_disc_socket = disc_socket; af_global->disc_ev = thread_add_read(master, disc_recv_packet, &af_global->disc_ev, af_global->ldp_disc_socket); /* extended discovery socket */ af_global->ldp_edisc_socket = edisc_socket; af_global->edisc_ev = thread_add_read(master, disc_recv_packet, &af_global->edisc_ev, af_global->ldp_edisc_socket); /* session socket */ af_global->ldp_session_socket = session_socket; accept_add(af_global->ldp_session_socket, session_accept, NULL); } static void ldpe_close_sockets(int af) { struct ldpd_af_global *af_global; af_global = ldp_af_global_get(&global, af); /* discovery socket */ THREAD_READ_OFF(af_global->disc_ev); if (af_global->ldp_disc_socket != -1) { close(af_global->ldp_disc_socket); af_global->ldp_disc_socket = -1; } /* extended discovery socket */ THREAD_READ_OFF(af_global->edisc_ev); if (af_global->ldp_edisc_socket != -1) { close(af_global->ldp_edisc_socket); af_global->ldp_edisc_socket = -1; } /* session socket */ if (af_global->ldp_session_socket != -1) { accept_del(af_global->ldp_session_socket); close(af_global->ldp_session_socket); af_global->ldp_session_socket = -1; } } int ldpe_acl_check(char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen) { return ldp_acl_request(iev_main_sync, acl_name, af, addr, prefixlen); } void ldpe_reset_nbrs(int af) { struct nbr *nbr; RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { if (af == AF_UNSPEC || nbr->af == af) session_shutdown(nbr, S_SHUTDOWN, 0, 0); } } void ldpe_reset_ds_nbrs(void) { struct nbr *nbr; RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { if (nbr->ds_tlv) session_shutdown(nbr, S_SHUTDOWN, 0, 0); } } void ldpe_remove_dynamic_tnbrs(int af) { struct tnbr *tnbr, *safe; RB_FOREACH_SAFE(tnbr, tnbr_head, &leconf->tnbr_tree, safe) { if (tnbr->af != af) continue; tnbr->flags &= ~F_TNBR_DYNAMIC; tnbr_check(leconf, tnbr); } } void ldpe_stop_init_backoff(int af) { struct nbr *nbr; RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { if (nbr->af == af && nbr_pending_idtimer(nbr)) { nbr_stop_idtimer(nbr); nbr_establish_connection(nbr); } } } static void ldpe_iface_af_ctl(struct ctl_conn *c, int af, unsigned int idx) { struct iface *iface; struct iface_af *ia; struct ctl_iface *ictl; RB_FOREACH(iface, iface_head, &leconf->iface_tree) { if (idx == 0 || idx == iface->ifindex) { ia = iface_af_get(iface, af); if (!ia->enabled) continue; ictl = if_to_ctl(ia); imsg_compose_event(&c->iev, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, ictl, sizeof(struct ctl_iface)); } } } void ldpe_iface_ctl(struct ctl_conn *c, unsigned int idx) { ldpe_iface_af_ctl(c, AF_INET, idx); ldpe_iface_af_ctl(c, AF_INET6, idx); } void ldpe_adj_ctl(struct ctl_conn *c) { struct adj *adj; struct ctl_adj *actl; RB_FOREACH(adj, global_adj_head, &global.adj_tree) { actl = adj_to_ctl(adj); imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, -1, actl, sizeof(struct ctl_adj)); } imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); } void ldpe_adj_detail_ctl(struct ctl_conn *c) { struct iface *iface; struct tnbr *tnbr; struct adj *adj; struct ctl_adj *actl; struct ctl_disc_if ictl; struct ctl_disc_tnbr tctl; imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, -1, NULL, 0); RB_FOREACH(iface, iface_head, &leconf->iface_tree) { memset(&ictl, 0, sizeof(ictl)); ictl.active_v4 = (iface->ipv4.state == IF_STA_ACTIVE); ictl.active_v6 = (iface->ipv6.state == IF_STA_ACTIVE); if (!ictl.active_v4 && !ictl.active_v6) continue; strlcpy(ictl.name, iface->name, sizeof(ictl.name)); if (RB_EMPTY(&iface->ipv4.adj_tree) && RB_EMPTY(&iface->ipv6.adj_tree)) ictl.no_adj = 1; imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_IFACE, 0, 0, -1, &ictl, sizeof(ictl)); RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) { actl = adj_to_ctl(adj); imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, 0, 0, -1, actl, sizeof(struct ctl_adj)); } RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) { actl = adj_to_ctl(adj); imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, 0, 0, -1, actl, sizeof(struct ctl_adj)); } } RB_FOREACH(tnbr, tnbr_head, &leconf->tnbr_tree) { memset(&tctl, 0, sizeof(tctl)); tctl.af = tnbr->af; tctl.addr = tnbr->addr; if (tnbr->adj == NULL) tctl.no_adj = 1; imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_TNBR, 0, 0, -1, &tctl, sizeof(tctl)); if (tnbr->adj == NULL) continue; actl = adj_to_ctl(tnbr->adj); imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, 0, 0, -1, actl, sizeof(struct ctl_adj)); } imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); } void ldpe_nbr_ctl(struct ctl_conn *c) { struct adj *adj; struct ctl_adj *actl; struct nbr *nbr; struct ctl_nbr *nctl; RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) { if (nbr->state == NBR_STA_PRESENT) continue; nctl = nbr_to_ctl(nbr); imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 0, -1, nctl, sizeof(struct ctl_nbr)); RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) { actl = adj_to_ctl(adj); imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_DISC, 0, 0, -1, actl, sizeof(struct ctl_adj)); } imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_END, 0, 0, -1, NULL, 0); } imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); } void mapping_list_add(struct mapping_head *mh, struct map *map) { struct mapping_entry *me; me = calloc(1, sizeof(*me)); if (me == NULL) fatal(__func__); me->map = *map; TAILQ_INSERT_TAIL(mh, me, entry); } void mapping_list_clr(struct mapping_head *mh) { struct mapping_entry *me; while ((me = TAILQ_FIRST(mh)) != NULL) { TAILQ_REMOVE(mh, me, entry); free(me); } }