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