bgpd.c revision 1.113
1/* $OpenBSD: bgpd.c,v 1.113 2005/02/09 10:56:28 henning Exp $ */ 2 3/* 4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/socket.h> 21#include <sys/wait.h> 22#include <netinet/in.h> 23#include <arpa/inet.h> 24#include <err.h> 25#include <errno.h> 26#include <fcntl.h> 27#include <poll.h> 28#include <pwd.h> 29#include <signal.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <unistd.h> 34 35#include "mrt.h" 36#include "bgpd.h" 37#include "session.h" 38 39void sighdlr(int); 40__dead void usage(void); 41int main(int, char *[]); 42int check_child(pid_t, const char *); 43int send_filterset(struct imsgbuf *, struct filter_set_head *, 44 int, int); 45int reconfigure(char *, struct bgpd_config *, struct mrt_head *, 46 struct peer **, struct filter_head *); 47int dispatch_imsg(struct imsgbuf *, int); 48 49int rfd = -1; 50volatile sig_atomic_t mrtdump = 0; 51volatile sig_atomic_t quit = 0; 52volatile sig_atomic_t reconfig = 0; 53volatile sig_atomic_t sigchld = 0; 54struct imsgbuf *ibuf_se; 55struct imsgbuf *ibuf_rde; 56 57void 58sighdlr(int sig) 59{ 60 switch (sig) { 61 case SIGTERM: 62 case SIGINT: 63 quit = 1; 64 break; 65 case SIGCHLD: 66 sigchld = 1; 67 break; 68 case SIGHUP: 69 reconfig = 1; 70 break; 71 case SIGALRM: 72 case SIGUSR1: 73 mrtdump = 1; 74 break; 75 } 76} 77 78__dead void 79usage(void) 80{ 81 extern char *__progname; 82 83 fprintf(stderr, "usage: %s [-dnv] ", __progname); 84 fprintf(stderr, "[-D macro=value] [-f file]\n"); 85 exit(1); 86} 87 88#define PFD_PIPE_SESSION 0 89#define PFD_PIPE_ROUTE 1 90#define PFD_SOCK_ROUTE 2 91#define POLL_MAX 3 92#define MAX_TIMEOUT 3600 93 94int 95main(int argc, char *argv[]) 96{ 97 struct bgpd_config conf; 98 struct peer *peer_l, *p; 99 struct mrt_head mrt_l; 100 struct network_head net_l; 101 struct filter_head *rules_l; 102 struct network *net; 103 struct filter_rule *r; 104 struct mrt *m; 105 struct listen_addr *la; 106 struct pollfd pfd[POLL_MAX]; 107 pid_t io_pid = 0, rde_pid = 0, pid; 108 char *conffile; 109 int debug = 0; 110 int ch, nfds, timeout; 111 int pipe_m2s[2]; 112 int pipe_m2r[2]; 113 int pipe_s2r[2]; 114 115 conffile = CONFFILE; 116 bgpd_process = PROC_MAIN; 117 118 log_init(1); /* log to stderr until daemonized */ 119 120 if ((rules_l = calloc(1, sizeof(struct filter_head))) == NULL) 121 err(1, NULL); 122 123 bzero(&conf, sizeof(conf)); 124 LIST_INIT(&mrt_l); 125 TAILQ_INIT(&net_l); 126 TAILQ_INIT(rules_l); 127 peer_l = NULL; 128 129 while ((ch = getopt(argc, argv, "dD:f:nv")) != -1) { 130 switch (ch) { 131 case 'd': 132 debug = 1; 133 break; 134 case 'D': 135 if (cmdline_symset(optarg) < 0) 136 log_warnx("could not parse macro definition %s", 137 optarg); 138 break; 139 case 'f': 140 conffile = optarg; 141 break; 142 case 'n': 143 conf.opts |= BGPD_OPT_NOACTION; 144 break; 145 case 'v': 146 if (conf.opts & BGPD_OPT_VERBOSE) 147 conf.opts |= BGPD_OPT_VERBOSE2; 148 conf.opts |= BGPD_OPT_VERBOSE; 149 break; 150 default: 151 usage(); 152 /* NOTREACHED */ 153 } 154 } 155 156 if (parse_config(conffile, &conf, &mrt_l, &peer_l, &net_l, rules_l)) 157 exit(1); 158 159 if (conf.opts & BGPD_OPT_NOACTION) { 160 if (conf.opts & BGPD_OPT_VERBOSE) 161 print_config(&conf, &net_l, peer_l, rules_l, &mrt_l); 162 else 163 fprintf(stderr, "configuration OK\n"); 164 exit(0); 165 } 166 167 if (geteuid()) 168 errx(1, "need root privileges"); 169 170 if (getpwnam(BGPD_USER) == NULL) 171 errx(1, "unknown user %s", BGPD_USER); 172 endpwent(); 173 174 log_init(debug); 175 176 if (!debug) 177 daemon(1, 0); 178 179 log_info("startup"); 180 181 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_m2s) == -1) 182 fatal("socketpair"); 183 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_m2r) == -1) 184 fatal("socketpair"); 185 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_s2r) == -1) 186 fatal("socketpair"); 187 session_socket_blockmode(pipe_m2s[0], BM_NONBLOCK); 188 session_socket_blockmode(pipe_m2s[1], BM_NONBLOCK); 189 session_socket_blockmode(pipe_m2r[0], BM_NONBLOCK); 190 session_socket_blockmode(pipe_m2r[1], BM_NONBLOCK); 191 session_socket_blockmode(pipe_s2r[0], BM_NONBLOCK); 192 session_socket_blockmode(pipe_s2r[1], BM_NONBLOCK); 193 194 prepare_listeners(&conf); 195 196 /* fork children */ 197 rde_pid = rde_main(&conf, peer_l, &net_l, rules_l, &mrt_l, 198 pipe_m2r, pipe_s2r, pipe_m2s); 199 io_pid = session_main(&conf, peer_l, &net_l, rules_l, &mrt_l, 200 pipe_m2s, pipe_s2r, pipe_m2r); 201 202 setproctitle("parent"); 203 204 signal(SIGTERM, sighdlr); 205 signal(SIGINT, sighdlr); 206 signal(SIGCHLD, sighdlr); 207 signal(SIGHUP, sighdlr); 208 signal(SIGALRM, sighdlr); 209 signal(SIGUSR1, sighdlr); 210 211 close(pipe_m2s[1]); 212 close(pipe_m2r[1]); 213 close(pipe_s2r[0]); 214 close(pipe_s2r[1]); 215 216 if ((ibuf_se = malloc(sizeof(struct imsgbuf))) == NULL || 217 (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL) 218 fatal(NULL); 219 imsg_init(ibuf_se, pipe_m2s[0]); 220 imsg_init(ibuf_rde, pipe_m2r[0]); 221 mrt_init(ibuf_rde, ibuf_se); 222 if ((rfd = kr_init(!(conf.flags & BGPD_FLAG_NO_FIB_UPDATE))) == -1) 223 quit = 1; 224 if (pftable_clear_all() != 0) 225 quit = 1; 226 227 while ((net = TAILQ_FIRST(&net_l)) != NULL) { 228 TAILQ_REMOVE(&net_l, net, entry); 229 free(net); 230 } 231 232 while ((r = TAILQ_FIRST(rules_l)) != NULL) { 233 TAILQ_REMOVE(rules_l, r, entry); 234 free(r); 235 } 236 237 while ((la = TAILQ_FIRST(conf.listen_addrs)) != NULL) { 238 TAILQ_REMOVE(conf.listen_addrs, la, entry); 239 close(la->fd); 240 free(la); 241 } 242 243 mrt_reconfigure(&mrt_l); 244 245 while (quit == 0) { 246 pfd[PFD_PIPE_SESSION].fd = ibuf_se->fd; 247 pfd[PFD_PIPE_SESSION].events = POLLIN; 248 if (ibuf_se->w.queued) 249 pfd[PFD_PIPE_SESSION].events |= POLLOUT; 250 pfd[PFD_PIPE_ROUTE].fd = ibuf_rde->fd; 251 pfd[PFD_PIPE_ROUTE].events = POLLIN; 252 if (ibuf_rde->w.queued) 253 pfd[PFD_PIPE_ROUTE].events |= POLLOUT; 254 pfd[PFD_SOCK_ROUTE].fd = rfd; 255 pfd[PFD_SOCK_ROUTE].events = POLLIN; 256 257 timeout = mrt_timeout(&mrt_l); 258 if (timeout > MAX_TIMEOUT) 259 timeout = MAX_TIMEOUT; 260 261 if ((nfds = poll(pfd, POLL_MAX, timeout * 1000)) == -1) 262 if (errno != EINTR) { 263 log_warn("poll error"); 264 quit = 1; 265 } 266 267 if (nfds > 0 && (pfd[PFD_PIPE_SESSION].revents & POLLOUT)) 268 if (msgbuf_write(&ibuf_se->w) < 0) { 269 log_warn("pipe write error (to SE)"); 270 quit = 1; 271 } 272 273 if (nfds > 0 && (pfd[PFD_PIPE_ROUTE].revents & POLLOUT)) 274 if (msgbuf_write(&ibuf_rde->w) < 0) { 275 log_warn("pipe write error (to RDE)"); 276 quit = 1; 277 } 278 279 if (nfds > 0 && pfd[PFD_PIPE_SESSION].revents & POLLIN) { 280 nfds--; 281 if (dispatch_imsg(ibuf_se, PFD_PIPE_SESSION) == -1) 282 quit = 1; 283 } 284 285 if (nfds > 0 && pfd[PFD_PIPE_ROUTE].revents & POLLIN) { 286 nfds--; 287 if (dispatch_imsg(ibuf_rde, PFD_PIPE_ROUTE) == -1) 288 quit = 1; 289 } 290 291 if (nfds > 0 && pfd[PFD_SOCK_ROUTE].revents & POLLIN) { 292 nfds--; 293 if (kr_dispatch_msg() == -1) 294 quit = 1; 295 } 296 297 if (reconfig) { 298 reconfig = 0; 299 log_info("rereading config"); 300 reconfigure(conffile, &conf, &mrt_l, &peer_l, rules_l); 301 } 302 303 if (sigchld) { 304 sigchld = 0; 305 if (check_child(io_pid, "session engine")) { 306 quit = 1; 307 io_pid = 0; 308 } 309 if (check_child(rde_pid, "route decision engine")) { 310 quit = 1; 311 rde_pid = 0; 312 } 313 } 314 315 if (mrtdump == 1) { 316 mrtdump = 0; 317 mrt_handler(&mrt_l); 318 } 319 } 320 321 signal(SIGCHLD, SIG_IGN); 322 323 if (io_pid) 324 kill(io_pid, SIGTERM); 325 326 if (rde_pid) 327 kill(rde_pid, SIGTERM); 328 329 while ((p = peer_l) != NULL) { 330 peer_l = p->next; 331 free(p); 332 } 333 while ((m = LIST_FIRST(&mrt_l)) != NULL) { 334 LIST_REMOVE(m, entry); 335 free(m); 336 } 337 338 free(rules_l); 339 control_cleanup(); 340 kr_shutdown(); 341 pftable_clear_all(); 342 free(conf.listen_addrs); 343 344 do { 345 if ((pid = wait(NULL)) == -1 && 346 errno != EINTR && errno != ECHILD) 347 fatal("wait"); 348 } while (pid != -1 || (pid == -1 && errno == EINTR)); 349 350 msgbuf_clear(&ibuf_se->w); 351 free(ibuf_se); 352 msgbuf_clear(&ibuf_rde->w); 353 free(ibuf_rde); 354 355 log_info("Terminating"); 356 return (0); 357} 358 359int 360check_child(pid_t pid, const char *pname) 361{ 362 int status; 363 364 if (waitpid(pid, &status, WNOHANG) > 0) { 365 if (WIFEXITED(status)) { 366 log_warnx("Lost child: %s exited", pname); 367 return (1); 368 } 369 if (WIFSIGNALED(status)) { 370 log_warnx("Lost child: %s terminated; signal %d", 371 pname, WTERMSIG(status)); 372 return (1); 373 } 374 } 375 376 return (0); 377} 378 379int 380send_filterset(struct imsgbuf *i, struct filter_set_head *set, int id, int f) 381{ 382 struct filter_set *s; 383 384 for (s = SIMPLEQ_FIRST(set); s != NULL; ) { 385 if (imsg_compose(i, IMSG_FILTER_SET, id, 0, -1, s, 386 sizeof(struct filter_set)) == -1) 387 return (-1); 388 if (f) { 389 SIMPLEQ_REMOVE_HEAD(set, entry); 390 free(s); 391 s = SIMPLEQ_FIRST(set); 392 } else 393 s = SIMPLEQ_NEXT(s, entry); 394 } 395 return (0); 396} 397 398int 399reconfigure(char *conffile, struct bgpd_config *conf, struct mrt_head *mrt_l, 400 struct peer **peer_l, struct filter_head *rules_l) 401{ 402 struct network_head net_l; 403 struct network *n; 404 struct peer *p; 405 struct filter_rule *r; 406 struct listen_addr *la; 407 408 if (parse_config(conffile, conf, mrt_l, peer_l, &net_l, rules_l)) { 409 log_warnx("config file %s has errors, not reloading", 410 conffile); 411 return (-1); 412 } 413 414 prepare_listeners(conf); 415 416 if (imsg_compose(ibuf_se, IMSG_RECONF_CONF, 0, 0, -1, 417 conf, sizeof(struct bgpd_config)) == -1) 418 return (-1); 419 if (imsg_compose(ibuf_rde, IMSG_RECONF_CONF, 0, 0, -1, 420 conf, sizeof(struct bgpd_config)) == -1) 421 return (-1); 422 for (p = *peer_l; p != NULL; p = p->next) { 423 if (imsg_compose(ibuf_se, IMSG_RECONF_PEER, p->conf.id, 0, -1, 424 &p->conf, sizeof(struct peer_config)) == -1) 425 return (-1); 426 if (send_filterset(ibuf_se, &p->conf.attrset, 427 p->conf.id, 0) == -1) 428 return (-1); 429 } 430 while ((n = TAILQ_FIRST(&net_l)) != NULL) { 431 if (imsg_compose(ibuf_rde, IMSG_NETWORK_ADD, 0, 0, -1, 432 &n->net, sizeof(struct network_config)) == -1) 433 return (-1); 434 if (send_filterset(ibuf_rde, &n->net.attrset, 0, 1) == -1) 435 return (-1); 436 if (imsg_compose(ibuf_rde, IMSG_NETWORK_DONE, 0, 0, -1, 437 NULL, 0) == -1) 438 return (-1); 439 TAILQ_REMOVE(&net_l, n, entry); 440 free(n); 441 } 442 while ((r = TAILQ_FIRST(rules_l)) != NULL) { 443 if (imsg_compose(ibuf_rde, IMSG_RECONF_FILTER, 0, 0, -1, 444 r, sizeof(struct filter_rule)) == -1) 445 return (-1); 446 if (send_filterset(ibuf_rde, &r->set, 0, 1) == -1) 447 return (-1); 448 TAILQ_REMOVE(rules_l, r, entry); 449 free(r); 450 } 451 while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) { 452 if (imsg_compose(ibuf_se, IMSG_RECONF_LISTENER, 0, 0, la->fd, 453 la, sizeof(struct listen_addr)) == -1) 454 return (-1); 455 TAILQ_REMOVE(conf->listen_addrs, la, entry); 456 free(la); 457 } 458 free(conf->listen_addrs); 459 conf->listen_addrs = NULL; 460 461 if (imsg_compose(ibuf_se, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1 || 462 imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1) 463 return (-1); 464 465 /* mrt changes can be sent out of bound */ 466 mrt_reconfigure(mrt_l); 467 return (0); 468} 469 470int 471dispatch_imsg(struct imsgbuf *ibuf, int idx) 472{ 473 struct imsg imsg; 474 int n; 475 476 if ((n = imsg_read(ibuf)) == -1) 477 return (-1); 478 479 if (n == 0) { /* connection closed */ 480 log_warnx("dispatch_imsg in main: pipe closed"); 481 return (-1); 482 } 483 484 for (;;) { 485 if ((n = imsg_get(ibuf, &imsg)) == -1) 486 return (-1); 487 488 if (n == 0) 489 break; 490 491 switch (imsg.hdr.type) { 492 case IMSG_KROUTE_CHANGE: 493 if (idx != PFD_PIPE_ROUTE) 494 log_warnx("route request not from RDE"); 495 else if (kr_change(imsg.data)) 496 return (-1); 497 break; 498 case IMSG_KROUTE_DELETE: 499 if (idx != PFD_PIPE_ROUTE) 500 log_warnx("route request not from RDE"); 501 else if (kr_delete(imsg.data)) 502 return (-1); 503 break; 504 case IMSG_NEXTHOP_ADD: 505 if (idx != PFD_PIPE_ROUTE) 506 log_warnx("nexthop request not from RDE"); 507 else 508 if (imsg.hdr.len != IMSG_HEADER_SIZE + 509 sizeof(struct bgpd_addr)) 510 log_warnx("wrong imsg len"); 511 else if (kr_nexthop_add(imsg.data) == -1) 512 return (-1); 513 break; 514 case IMSG_NEXTHOP_REMOVE: 515 if (idx != PFD_PIPE_ROUTE) 516 log_warnx("nexthop request not from RDE"); 517 else 518 if (imsg.hdr.len != IMSG_HEADER_SIZE + 519 sizeof(struct bgpd_addr)) 520 log_warnx("wrong imsg len"); 521 else 522 kr_nexthop_delete(imsg.data); 523 break; 524 case IMSG_PFTABLE_ADD: 525 if (idx != PFD_PIPE_ROUTE) 526 log_warnx("pftable request not from RDE"); 527 else 528 if (imsg.hdr.len != IMSG_HEADER_SIZE + 529 sizeof(struct pftable_msg)) 530 log_warnx("wrong imsg len"); 531 else if (pftable_addr_add(imsg.data) != 0) 532 return (-1); 533 break; 534 case IMSG_PFTABLE_REMOVE: 535 if (idx != PFD_PIPE_ROUTE) 536 log_warnx("pftable request not from RDE"); 537 else 538 if (imsg.hdr.len != IMSG_HEADER_SIZE + 539 sizeof(struct pftable_msg)) 540 log_warnx("wrong imsg len"); 541 else if (pftable_addr_remove(imsg.data) != 0) 542 return (-1); 543 break; 544 case IMSG_PFTABLE_COMMIT: 545 if (idx != PFD_PIPE_ROUTE) 546 log_warnx("pftable request not from RDE"); 547 else 548 if (imsg.hdr.len != IMSG_HEADER_SIZE) 549 log_warnx("wrong imsg len"); 550 else if (pftable_commit() != 0) 551 return (-1); 552 break; 553 case IMSG_CTL_RELOAD: 554 if (idx != PFD_PIPE_SESSION) 555 log_warnx("reload request not from SE"); 556 else 557 reconfig = 1; 558 break; 559 case IMSG_CTL_FIB_COUPLE: 560 if (idx != PFD_PIPE_SESSION) 561 log_warnx("couple request not from SE"); 562 else 563 kr_fib_couple(); 564 break; 565 case IMSG_CTL_FIB_DECOUPLE: 566 if (idx != PFD_PIPE_SESSION) 567 log_warnx("decouple request not from SE"); 568 else 569 kr_fib_decouple(); 570 break; 571 case IMSG_CTL_KROUTE: 572 case IMSG_CTL_KROUTE_ADDR: 573 case IMSG_CTL_SHOW_NEXTHOP: 574 case IMSG_CTL_SHOW_INTERFACE: 575 if (idx != PFD_PIPE_SESSION) 576 log_warnx("kroute request not from SE"); 577 else 578 kr_show_route(&imsg); 579 break; 580 case IMSG_IFINFO: 581 if (idx != PFD_PIPE_SESSION) 582 log_warnx("IFINFO request not from SE"); 583 else if (imsg.hdr.len != IMSG_HEADER_SIZE + IFNAMSIZ) 584 log_warnx("IFINFO request with wrong len"); 585 else 586 kr_ifinfo(imsg.data); 587 break; 588 default: 589 break; 590 } 591 imsg_free(&imsg); 592 } 593 return (0); 594} 595 596void 597send_nexthop_update(struct kroute_nexthop *msg) 598{ 599 char *gw = NULL; 600 601 if (msg->gateway.af) 602 if (asprintf(&gw, ": via %s", 603 log_addr(&msg->gateway)) == -1) { 604 log_warn("send_nexthop_update"); 605 quit = 1; 606 } 607 608 log_info("nexthop %s now %s%s%s", log_addr(&msg->nexthop), 609 msg->valid ? "valid" : "invalid", 610 msg->connected ? ": directly connected" : "", 611 msg->gateway.af ? gw : ""); 612 613 free(gw); 614 615 if (imsg_compose(ibuf_rde, IMSG_NEXTHOP_UPDATE, 0, 0, -1, 616 msg, sizeof(struct kroute_nexthop)) == -1) 617 quit = 1; 618} 619 620void 621send_imsg_session(int type, pid_t pid, void *data, u_int16_t datalen) 622{ 623 imsg_compose(ibuf_se, type, 0, pid, -1, data, datalen); 624} 625