1 /* $OpenBSD: eigrpe.c,v 1.40 2023/03/08 04:43:13 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Renato Westphal <renato@openbsd.org> 5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 6 * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 7 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/types.h> 23 #include <netinet/in.h> 24 #include <netinet/ip.h> 25 26 #include <arpa/inet.h> 27 #include <errno.h> 28 #include <pwd.h> 29 #include <signal.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "eigrpd.h" 35 #include "eigrpe.h" 36 #include "rde.h" 37 #include "log.h" 38 #include "control.h" 39 40 static void eigrpe_sig_handler(int, short, void *); 41 static __dead void eigrpe_shutdown(void); 42 static void eigrpe_dispatch_main(int, short, void *); 43 static void eigrpe_dispatch_rde(int, short, void *); 44 45 struct eigrpd_conf *econf; 46 47 static struct event ev4; 48 static struct event ev6; 49 static struct imsgev *iev_main; 50 static struct imsgev *iev_rde; 51 52 static void 53 eigrpe_sig_handler(int sig, short event, void *bula) 54 { 55 switch (sig) { 56 case SIGINT: 57 case SIGTERM: 58 eigrpe_shutdown(); 59 /* NOTREACHED */ 60 default: 61 fatalx("unexpected signal"); 62 } 63 } 64 65 /* eigrp engine */ 66 void 67 eigrpe(int debug, int verbose, char *sockname) 68 { 69 struct passwd *pw; 70 struct event ev_sigint, ev_sigterm; 71 72 econf = config_new_empty(); 73 74 log_init(debug); 75 log_verbose(verbose); 76 77 /* create eigrpd control socket outside chroot */ 78 if (control_init(sockname) == -1) 79 fatalx("control socket setup failed"); 80 81 if (inet_pton(AF_INET, AllEIGRPRouters_v4, &global.mcast_addr_v4) != 1) 82 fatal("inet_pton"); 83 if (inet_pton(AF_INET6, AllEIGRPRouters_v6, &global.mcast_addr_v6) != 1) 84 fatal("inet_pton"); 85 86 /* create the raw ipv4 socket */ 87 if ((global.eigrp_socket_v4 = socket(AF_INET, 88 SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_EIGRP)) == -1) 89 fatal("error creating raw ipv4 socket"); 90 91 /* set some defaults */ 92 if (if_set_ipv4_mcast_ttl(global.eigrp_socket_v4, EIGRP_IP_TTL) == -1) 93 fatal("if_set_ipv4_mcast_ttl"); 94 if (if_set_ipv4_mcast_loop(global.eigrp_socket_v4) == -1) 95 fatal("if_set_ipv4_mcast_loop"); 96 if (if_set_ipv4_recvif(global.eigrp_socket_v4, 1) == -1) 97 fatal("if_set_ipv4_recvif"); 98 if (if_set_ipv4_hdrincl(global.eigrp_socket_v4) == -1) 99 fatal("if_set_ipv4_hdrincl"); 100 if_set_sockbuf(global.eigrp_socket_v4); 101 102 /* create the raw ipv6 socket */ 103 if ((global.eigrp_socket_v6 = socket(AF_INET6, 104 SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_EIGRP)) == -1) 105 fatal("error creating raw ipv6 socket"); 106 107 /* set some defaults */ 108 if (if_set_ipv6_mcast_loop(global.eigrp_socket_v6) == -1) 109 fatal("if_set_ipv6_mcast_loop"); 110 if (if_set_ipv6_pktinfo(global.eigrp_socket_v6, 1) == -1) 111 fatal("if_set_ipv6_pktinfo"); 112 if (if_set_ipv6_dscp(global.eigrp_socket_v6, 113 IPTOS_PREC_NETCONTROL) == -1) 114 fatal("if_set_ipv6_dscp"); 115 if_set_sockbuf(global.eigrp_socket_v6); 116 117 if ((pw = getpwnam(EIGRPD_USER)) == NULL) 118 fatal("getpwnam"); 119 120 if (chroot(pw->pw_dir) == -1) 121 fatal("chroot"); 122 if (chdir("/") == -1) 123 fatal("chdir(\"/\")"); 124 125 setproctitle("eigrp engine"); 126 log_procname = "eigrpe"; 127 128 if (setgroups(1, &pw->pw_gid) || 129 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 130 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 131 fatal("can't drop privileges"); 132 133 if (pledge("stdio inet mcast recvfd", NULL) == -1) 134 fatal("pledge"); 135 136 event_init(); 137 138 /* setup signal handler */ 139 signal_set(&ev_sigint, SIGINT, eigrpe_sig_handler, NULL); 140 signal_set(&ev_sigterm, SIGTERM, eigrpe_sig_handler, NULL); 141 signal_add(&ev_sigint, NULL); 142 signal_add(&ev_sigterm, NULL); 143 signal(SIGPIPE, SIG_IGN); 144 signal(SIGHUP, SIG_IGN); 145 146 /* setup pipe and event handler to the parent process */ 147 if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 148 fatal(NULL); 149 imsg_init(&iev_main->ibuf, 3); 150 iev_main->handler = eigrpe_dispatch_main; 151 iev_main->events = EV_READ; 152 event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 153 iev_main->handler, iev_main); 154 event_add(&iev_main->ev, NULL); 155 156 event_set(&ev4, global.eigrp_socket_v4, EV_READ|EV_PERSIST, 157 recv_packet, econf); 158 event_add(&ev4, NULL); 159 160 event_set(&ev6, global.eigrp_socket_v6, EV_READ|EV_PERSIST, 161 recv_packet, econf); 162 event_add(&ev6, NULL); 163 164 /* listen on eigrpd control socket */ 165 control_listen(); 166 167 event_dispatch(); 168 169 eigrpe_shutdown(); 170 } 171 172 static __dead void 173 eigrpe_shutdown(void) 174 { 175 /* close pipes */ 176 msgbuf_write(&iev_rde->ibuf.w); 177 msgbuf_clear(&iev_rde->ibuf.w); 178 close(iev_rde->ibuf.fd); 179 msgbuf_write(&iev_main->ibuf.w); 180 msgbuf_clear(&iev_main->ibuf.w); 181 close(iev_main->ibuf.fd); 182 183 config_clear(econf, PROC_EIGRP_ENGINE); 184 185 event_del(&ev4); 186 event_del(&ev6); 187 close(global.eigrp_socket_v4); 188 close(global.eigrp_socket_v6); 189 190 /* clean up */ 191 free(iev_rde); 192 free(iev_main); 193 194 log_info("eigrp engine exiting"); 195 exit(0); 196 } 197 198 /* imesg */ 199 int 200 eigrpe_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen) 201 { 202 return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen)); 203 } 204 205 int 206 eigrpe_imsg_compose_rde(int type, uint32_t peerid, pid_t pid, 207 void *data, uint16_t datalen) 208 { 209 return (imsg_compose_event(iev_rde, type, peerid, pid, -1, 210 data, datalen)); 211 } 212 213 static void 214 eigrpe_dispatch_main(int fd, short event, void *bula) 215 { 216 static struct eigrpd_conf *nconf; 217 static struct iface *niface; 218 static struct eigrp *neigrp; 219 struct eigrp_iface *nei; 220 struct imsg imsg; 221 struct imsgev *iev = bula; 222 struct imsgbuf *ibuf = &iev->ibuf; 223 struct iface *iface = NULL; 224 struct kif *kif; 225 struct kaddr *ka; 226 int n, shut = 0; 227 228 if (event & EV_READ) { 229 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 230 fatal("imsg_read error"); 231 if (n == 0) /* connection closed */ 232 shut = 1; 233 } 234 if (event & EV_WRITE) { 235 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 236 fatal("msgbuf_write"); 237 if (n == 0) /* connection closed */ 238 shut = 1; 239 } 240 241 for (;;) { 242 if ((n = imsg_get(ibuf, &imsg)) == -1) 243 fatal("eigrpe_dispatch_main: imsg_get error"); 244 if (n == 0) 245 break; 246 247 switch (imsg.hdr.type) { 248 case IMSG_IFINFO: 249 if (imsg.hdr.len != IMSG_HEADER_SIZE + 250 sizeof(struct kif)) 251 fatalx("IFSTATUS imsg with wrong len"); 252 kif = imsg.data; 253 254 iface = if_lookup(econf, kif->ifindex); 255 if (!iface) 256 break; 257 258 iface->flags = kif->flags; 259 iface->linkstate = kif->link_state; 260 if_update(iface, AF_UNSPEC); 261 break; 262 case IMSG_NEWADDR: 263 if (imsg.hdr.len != IMSG_HEADER_SIZE + 264 sizeof(struct kaddr)) 265 fatalx("NEWADDR imsg with wrong len"); 266 ka = imsg.data; 267 268 iface = if_lookup(econf, ka->ifindex); 269 if (iface == NULL) 270 break; 271 272 if_addr_new(iface, ka); 273 break; 274 case IMSG_DELADDR: 275 if (imsg.hdr.len != IMSG_HEADER_SIZE + 276 sizeof(struct kaddr)) 277 fatalx("DELADDR imsg with wrong len"); 278 ka = imsg.data; 279 280 iface = if_lookup(econf, ka->ifindex); 281 if (iface == NULL) 282 break; 283 284 if_addr_del(iface, ka); 285 break; 286 case IMSG_SOCKET_IPC: 287 if (iev_rde) { 288 log_warnx("%s: received unexpected imsg fd " 289 "to rde", __func__); 290 break; 291 } 292 if ((fd = imsg.fd) == -1) { 293 log_warnx("%s: expected to receive imsg fd to " 294 "rde but didn't receive any", __func__); 295 break; 296 } 297 298 iev_rde = malloc(sizeof(struct imsgev)); 299 if (iev_rde == NULL) 300 fatal(NULL); 301 imsg_init(&iev_rde->ibuf, fd); 302 iev_rde->handler = eigrpe_dispatch_rde; 303 iev_rde->events = EV_READ; 304 event_set(&iev_rde->ev, iev_rde->ibuf.fd, 305 iev_rde->events, iev_rde->handler, iev_rde); 306 event_add(&iev_rde->ev, NULL); 307 break; 308 case IMSG_RECONF_CONF: 309 if ((nconf = malloc(sizeof(struct eigrpd_conf))) == 310 NULL) 311 fatal(NULL); 312 memcpy(nconf, imsg.data, sizeof(struct eigrpd_conf)); 313 314 TAILQ_INIT(&nconf->iface_list); 315 TAILQ_INIT(&nconf->instances); 316 break; 317 case IMSG_RECONF_INSTANCE: 318 if ((neigrp = malloc(sizeof(struct eigrp))) == NULL) 319 fatal(NULL); 320 memcpy(neigrp, imsg.data, sizeof(struct eigrp)); 321 322 SIMPLEQ_INIT(&neigrp->redist_list); 323 TAILQ_INIT(&neigrp->ei_list); 324 RB_INIT(&neigrp->nbrs); 325 RB_INIT(&neigrp->topology); 326 TAILQ_INSERT_TAIL(&nconf->instances, neigrp, entry); 327 break; 328 case IMSG_RECONF_IFACE: 329 niface = imsg.data; 330 niface = if_lookup(nconf, niface->ifindex); 331 if (niface) 332 break; 333 334 if ((niface = malloc(sizeof(struct iface))) == NULL) 335 fatal(NULL); 336 memcpy(niface, imsg.data, sizeof(struct iface)); 337 338 TAILQ_INIT(&niface->ei_list); 339 TAILQ_INIT(&niface->addr_list); 340 TAILQ_INSERT_TAIL(&nconf->iface_list, niface, entry); 341 break; 342 case IMSG_RECONF_EIGRP_IFACE: 343 if (niface == NULL) 344 break; 345 if ((nei = malloc(sizeof(struct eigrp_iface))) == NULL) 346 fatal(NULL); 347 memcpy(nei, imsg.data, sizeof(struct eigrp_iface)); 348 349 nei->iface = niface; 350 nei->eigrp = neigrp; 351 TAILQ_INIT(&nei->nbr_list); 352 TAILQ_INIT(&nei->update_list); 353 TAILQ_INIT(&nei->query_list); 354 TAILQ_INIT(&nei->summary_list); 355 TAILQ_INSERT_TAIL(&niface->ei_list, nei, i_entry); 356 TAILQ_INSERT_TAIL(&neigrp->ei_list, nei, e_entry); 357 if (RB_INSERT(iface_id_head, &ifaces_by_id, nei) != 358 NULL) 359 fatalx("eigrpe_dispatch_main: " 360 "RB_INSERT(ifaces_by_id) failed"); 361 break; 362 case IMSG_RECONF_END: 363 merge_config(econf, nconf, PROC_EIGRP_ENGINE); 364 nconf = NULL; 365 break; 366 case IMSG_CTL_KROUTE: 367 case IMSG_CTL_IFINFO: 368 case IMSG_CTL_END: 369 control_imsg_relay(&imsg); 370 break; 371 default: 372 log_debug("%s: error handling imsg %d", __func__, 373 imsg.hdr.type); 374 break; 375 } 376 imsg_free(&imsg); 377 } 378 if (!shut) 379 imsg_event_add(iev); 380 else { 381 /* this pipe is dead, so remove the event handler */ 382 event_del(&iev->ev); 383 event_loopexit(NULL); 384 } 385 } 386 387 static void 388 eigrpe_dispatch_rde(int fd, short event, void *bula) 389 { 390 struct imsgev *iev = bula; 391 struct imsgbuf *ibuf = &iev->ibuf; 392 struct imsg imsg; 393 struct nbr *nbr; 394 struct eigrp_iface *ei; 395 struct rinfo rinfo; 396 int n, shut = 0; 397 398 if (event & EV_READ) { 399 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 400 fatal("imsg_read error"); 401 if (n == 0) /* connection closed */ 402 shut = 1; 403 } 404 if (event & EV_WRITE) { 405 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 406 fatal("msgbuf_write"); 407 if (n == 0) /* connection closed */ 408 shut = 1; 409 } 410 411 for (;;) { 412 if ((n = imsg_get(ibuf, &imsg)) == -1) 413 fatal("eigrpe_dispatch_rde: imsg_get error"); 414 if (n == 0) 415 break; 416 417 switch (imsg.hdr.type) { 418 case IMSG_SEND_UPDATE: 419 case IMSG_SEND_QUERY: 420 case IMSG_SEND_REPLY: 421 if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rinfo)) 422 fatalx("invalid size of rinfo"); 423 memcpy(&rinfo, imsg.data, sizeof(rinfo)); 424 425 nbr = nbr_find_peerid(imsg.hdr.peerid); 426 if (nbr == NULL) { 427 log_debug("%s: cannot find rde neighbor", 428 __func__); 429 break; 430 } 431 432 switch (imsg.hdr.type) { 433 case IMSG_SEND_UPDATE: 434 message_add(&nbr->update_list, &rinfo); 435 break; 436 case IMSG_SEND_QUERY: 437 message_add(&nbr->query_list, &rinfo); 438 break; 439 case IMSG_SEND_REPLY: 440 message_add(&nbr->reply_list, &rinfo); 441 break; 442 } 443 break; 444 case IMSG_SEND_MUPDATE: 445 case IMSG_SEND_MQUERY: 446 if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rinfo)) 447 fatalx("invalid size of rinfo"); 448 memcpy(&rinfo, imsg.data, sizeof(rinfo)); 449 450 ei = eigrp_if_lookup_id(imsg.hdr.peerid); 451 if (ei == NULL) { 452 log_debug("%s: cannot find interface", 453 __func__); 454 break; 455 } 456 457 switch (imsg.hdr.type) { 458 case IMSG_SEND_MUPDATE: 459 message_add(&ei->update_list, &rinfo); 460 break; 461 case IMSG_SEND_MQUERY: 462 message_add(&ei->query_list, &rinfo); 463 break; 464 } 465 break; 466 case IMSG_SEND_UPDATE_END: 467 case IMSG_SEND_REPLY_END: 468 case IMSG_SEND_SIAQUERY_END: 469 case IMSG_SEND_SIAREPLY_END: 470 nbr = nbr_find_peerid(imsg.hdr.peerid); 471 if (nbr == NULL) { 472 log_debug("%s: cannot find rde neighbor", 473 __func__); 474 break; 475 } 476 477 switch (imsg.hdr.type) { 478 case IMSG_SEND_UPDATE_END: 479 send_update(nbr->ei, nbr, 0, &nbr->update_list); 480 message_list_clr(&nbr->update_list); 481 break; 482 case IMSG_SEND_REPLY_END: 483 send_reply(nbr, &nbr->reply_list, 0); 484 message_list_clr(&nbr->reply_list); 485 break; 486 case IMSG_SEND_SIAQUERY_END: 487 send_query(nbr->ei, nbr, &nbr->query_list, 1); 488 message_list_clr(&nbr->query_list); 489 break; 490 case IMSG_SEND_SIAREPLY_END: 491 send_reply(nbr, &nbr->reply_list, 1); 492 message_list_clr(&nbr->reply_list); 493 break; 494 } 495 break; 496 case IMSG_SEND_MUPDATE_END: 497 case IMSG_SEND_MQUERY_END: 498 ei = eigrp_if_lookup_id(imsg.hdr.peerid); 499 if (ei == NULL) { 500 log_debug("%s: cannot find interface", 501 __func__); 502 break; 503 } 504 505 switch (imsg.hdr.type) { 506 case IMSG_SEND_MUPDATE_END: 507 send_update(ei, NULL, 0, &ei->update_list); 508 message_list_clr(&ei->update_list); 509 break; 510 case IMSG_SEND_MQUERY_END: 511 send_query(ei, NULL, &ei->query_list, 0); 512 message_list_clr(&ei->query_list); 513 break; 514 } 515 break; 516 case IMSG_NEIGHBOR_DOWN: 517 nbr = nbr_find_peerid(imsg.hdr.peerid); 518 if (nbr == NULL) { 519 log_debug("%s: cannot find rde neighbor", 520 __func__); 521 break; 522 } 523 /* announce that this neighborship is dead */ 524 send_peerterm(nbr); 525 nbr_del(nbr); 526 break; 527 case IMSG_CTL_SHOW_TOPOLOGY: 528 case IMSG_CTL_END: 529 control_imsg_relay(&imsg); 530 break; 531 default: 532 log_debug("%s: error handling imsg %d", __func__, 533 imsg.hdr.type); 534 break; 535 } 536 imsg_free(&imsg); 537 } 538 if (!shut) 539 imsg_event_add(iev); 540 else { 541 /* this pipe is dead, so remove the event handler */ 542 event_del(&iev->ev); 543 event_loopexit(NULL); 544 } 545 } 546 547 void 548 eigrpe_instance_init(struct eigrp *eigrp) 549 { 550 } 551 552 void 553 eigrpe_instance_del(struct eigrp *eigrp) 554 { 555 struct eigrp_iface *ei; 556 557 while ((ei = TAILQ_FIRST(&eigrp->ei_list)) != NULL) 558 eigrp_if_del(ei); 559 560 free(eigrp); 561 } 562 563 void 564 message_add(struct rinfo_head *rinfo_list, struct rinfo *rinfo) 565 { 566 struct rinfo_entry *re; 567 568 re = calloc(1, sizeof(*re)); 569 if (re == NULL) 570 fatal("message_add"); 571 re->rinfo = *rinfo; 572 573 TAILQ_INSERT_TAIL(rinfo_list, re, entry); 574 } 575 576 void 577 message_list_clr(struct rinfo_head *rinfo_list) 578 { 579 struct rinfo_entry *re; 580 581 while ((re = TAILQ_FIRST(rinfo_list)) != NULL) { 582 TAILQ_REMOVE(rinfo_list, re, entry); 583 free(re); 584 } 585 } 586 587 void 588 seq_addr_list_clr(struct seq_addr_head *seq_addr_list) 589 { 590 struct seq_addr_entry *sa; 591 592 while ((sa = TAILQ_FIRST(seq_addr_list)) != NULL) { 593 TAILQ_REMOVE(seq_addr_list, sa, entry); 594 free(sa); 595 } 596 } 597 598 void 599 eigrpe_orig_local_route(struct eigrp_iface *ei, struct if_addr *if_addr, 600 int withdraw) 601 { 602 struct rinfo rinfo; 603 604 memset(&rinfo, 0, sizeof(rinfo)); 605 rinfo.af = if_addr->af; 606 rinfo.type = EIGRP_ROUTE_INTERNAL; 607 rinfo.prefix = if_addr->addr; 608 rinfo.prefixlen = if_addr->prefixlen; 609 610 eigrp_applymask(rinfo.af, &rinfo.prefix, &rinfo.prefix, 611 rinfo.prefixlen); 612 613 if (withdraw) 614 rinfo.metric.delay = EIGRP_INFINITE_METRIC; 615 else 616 rinfo.metric.delay = eigrp_composite_delay(ei->delay); 617 rinfo.metric.bandwidth = eigrp_composite_bandwidth(ei->bandwidth); 618 metric_encode_mtu(rinfo.metric.mtu, ei->iface->mtu); 619 rinfo.metric.hop_count = 0; 620 rinfo.metric.reliability = DEFAULT_RELIABILITY; 621 rinfo.metric.load = DEFAULT_LOAD; 622 rinfo.metric.tag = 0; 623 rinfo.metric.flags = 0; 624 625 eigrpe_imsg_compose_rde(IMSG_RECV_UPDATE, ei->self->peerid, 0, 626 &rinfo, sizeof(rinfo)); 627 } 628 629 void 630 eigrpe_iface_ctl(struct ctl_conn *c, unsigned int idx) 631 { 632 struct eigrp *eigrp; 633 struct eigrp_iface *ei; 634 struct ctl_iface *ictl; 635 636 TAILQ_FOREACH(eigrp, &econf->instances, entry) { 637 TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) { 638 if (idx == 0 || idx == ei->iface->ifindex) { 639 ictl = if_to_ctl(ei); 640 imsg_compose_event(&c->iev, 641 IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, 642 ictl, sizeof(struct ctl_iface)); 643 } 644 } 645 } 646 } 647 648 void 649 eigrpe_nbr_ctl(struct ctl_conn *c) 650 { 651 struct eigrp *eigrp; 652 struct nbr *nbr; 653 struct ctl_nbr *nctl; 654 655 TAILQ_FOREACH(eigrp, &econf->instances, entry) { 656 RB_FOREACH(nbr, nbr_addr_head, &eigrp->nbrs) { 657 if (nbr->flags & (F_EIGRP_NBR_PENDING|F_EIGRP_NBR_SELF)) 658 continue; 659 660 nctl = nbr_to_ctl(nbr); 661 imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 662 0, -1, nctl, sizeof(struct ctl_nbr)); 663 } 664 } 665 666 imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); 667 } 668 669 void 670 eigrpe_stats_ctl(struct ctl_conn *c) 671 { 672 struct eigrp *eigrp; 673 struct ctl_stats sctl; 674 675 TAILQ_FOREACH(eigrp, &econf->instances, entry) { 676 sctl.af = eigrp->af; 677 sctl.as = eigrp->as; 678 sctl.stats = eigrp->stats; 679 imsg_compose_event(&c->iev, IMSG_CTL_SHOW_STATS, 0, 680 0, -1, &sctl, sizeof(struct ctl_stats)); 681 } 682 683 imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); 684 } 685