bgpd.c revision 1.116
1/* $OpenBSD: bgpd.c,v 1.116 2005/03/30 11:23:15 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 free(rules_l); 158 exit(1); 159 } 160 161 if (conf.opts & BGPD_OPT_NOACTION) { 162 if (conf.opts & BGPD_OPT_VERBOSE) 163 print_config(&conf, &net_l, peer_l, rules_l, &mrt_l); 164 else 165 fprintf(stderr, "configuration OK\n"); 166 exit(0); 167 } 168 169 if (geteuid()) 170 errx(1, "need root privileges"); 171 172 if (getpwnam(BGPD_USER) == NULL) 173 errx(1, "unknown user %s", BGPD_USER); 174 endpwent(); 175 176 log_init(debug); 177 178 if (!debug) 179 daemon(1, 0); 180 181 log_info("startup"); 182 183 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_m2s) == -1) 184 fatal("socketpair"); 185 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_m2r) == -1) 186 fatal("socketpair"); 187 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_s2r) == -1) 188 fatal("socketpair"); 189 session_socket_blockmode(pipe_m2s[0], BM_NONBLOCK); 190 session_socket_blockmode(pipe_m2s[1], BM_NONBLOCK); 191 session_socket_blockmode(pipe_m2r[0], BM_NONBLOCK); 192 session_socket_blockmode(pipe_m2r[1], BM_NONBLOCK); 193 session_socket_blockmode(pipe_s2r[0], BM_NONBLOCK); 194 session_socket_blockmode(pipe_s2r[1], BM_NONBLOCK); 195 196 prepare_listeners(&conf); 197 198 /* fork children */ 199 rde_pid = rde_main(&conf, peer_l, &net_l, rules_l, &mrt_l, 200 pipe_m2r, pipe_s2r, pipe_m2s); 201 io_pid = session_main(&conf, peer_l, &net_l, rules_l, &mrt_l, 202 pipe_m2s, pipe_s2r, pipe_m2r); 203 204 setproctitle("parent"); 205 206 signal(SIGTERM, sighdlr); 207 signal(SIGINT, sighdlr); 208 signal(SIGCHLD, sighdlr); 209 signal(SIGHUP, sighdlr); 210 signal(SIGALRM, sighdlr); 211 signal(SIGUSR1, sighdlr); 212 213 close(pipe_m2s[1]); 214 close(pipe_m2r[1]); 215 close(pipe_s2r[0]); 216 close(pipe_s2r[1]); 217 218 if ((ibuf_se = malloc(sizeof(struct imsgbuf))) == NULL || 219 (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL) 220 fatal(NULL); 221 imsg_init(ibuf_se, pipe_m2s[0]); 222 imsg_init(ibuf_rde, pipe_m2r[0]); 223 mrt_init(ibuf_rde, ibuf_se); 224 if ((rfd = kr_init(!(conf.flags & BGPD_FLAG_NO_FIB_UPDATE))) == -1) 225 quit = 1; 226 if (pftable_clear_all() != 0) 227 quit = 1; 228 229 while ((net = TAILQ_FIRST(&net_l)) != NULL) { 230 TAILQ_REMOVE(&net_l, net, entry); 231 free(net); 232 } 233 234 while ((r = TAILQ_FIRST(rules_l)) != NULL) { 235 TAILQ_REMOVE(rules_l, r, entry); 236 free(r); 237 } 238 TAILQ_FOREACH(la, conf.listen_addrs, entry) { 239 close(la->fd); 240 la->fd = -1; 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 while ((la = TAILQ_FIRST(conf.listen_addrs)) != NULL) { 338 TAILQ_REMOVE(conf.listen_addrs, la, entry); 339 close(la->fd); 340 free(la); 341 } 342 343 free(rules_l); 344 control_cleanup(); 345 kr_shutdown(); 346 pftable_clear_all(); 347 free(conf.listen_addrs); 348 349 do { 350 if ((pid = wait(NULL)) == -1 && 351 errno != EINTR && errno != ECHILD) 352 fatal("wait"); 353 } while (pid != -1 || (pid == -1 && errno == EINTR)); 354 355 msgbuf_clear(&ibuf_se->w); 356 free(ibuf_se); 357 msgbuf_clear(&ibuf_rde->w); 358 free(ibuf_rde); 359 360 log_info("Terminating"); 361 return (0); 362} 363 364int 365check_child(pid_t pid, const char *pname) 366{ 367 int status; 368 369 if (waitpid(pid, &status, WNOHANG) > 0) { 370 if (WIFEXITED(status)) { 371 log_warnx("Lost child: %s exited", pname); 372 return (1); 373 } 374 if (WIFSIGNALED(status)) { 375 log_warnx("Lost child: %s terminated; signal %d", 376 pname, WTERMSIG(status)); 377 return (1); 378 } 379 } 380 381 return (0); 382} 383 384int 385send_filterset(struct imsgbuf *i, struct filter_set_head *set, int id, int f) 386{ 387 struct filter_set *s; 388 389 for (s = SIMPLEQ_FIRST(set); s != NULL; ) { 390 if (imsg_compose(i, IMSG_FILTER_SET, id, 0, -1, s, 391 sizeof(struct filter_set)) == -1) 392 return (-1); 393 if (f) { 394 SIMPLEQ_REMOVE_HEAD(set, entry); 395 free(s); 396 s = SIMPLEQ_FIRST(set); 397 } else 398 s = SIMPLEQ_NEXT(s, entry); 399 } 400 return (0); 401} 402 403int 404reconfigure(char *conffile, struct bgpd_config *conf, struct mrt_head *mrt_l, 405 struct peer **peer_l, struct filter_head *rules_l) 406{ 407 struct network_head net_l; 408 struct network *n; 409 struct peer *p; 410 struct filter_rule *r; 411 struct listen_addr *la; 412 413 if (parse_config(conffile, conf, mrt_l, peer_l, &net_l, rules_l)) { 414 log_warnx("config file %s has errors, not reloading", 415 conffile); 416 return (-1); 417 } 418 419 prepare_listeners(conf); 420 421 if (imsg_compose(ibuf_se, IMSG_RECONF_CONF, 0, 0, -1, 422 conf, sizeof(struct bgpd_config)) == -1) 423 return (-1); 424 if (imsg_compose(ibuf_rde, IMSG_RECONF_CONF, 0, 0, -1, 425 conf, sizeof(struct bgpd_config)) == -1) 426 return (-1); 427 for (p = *peer_l; p != NULL; p = p->next) { 428 if (imsg_compose(ibuf_se, IMSG_RECONF_PEER, p->conf.id, 0, -1, 429 &p->conf, sizeof(struct peer_config)) == -1) 430 return (-1); 431 if (send_filterset(ibuf_se, &p->conf.attrset, 432 p->conf.id, 0) == -1) 433 return (-1); 434 } 435 while ((n = TAILQ_FIRST(&net_l)) != NULL) { 436 if (imsg_compose(ibuf_rde, IMSG_NETWORK_ADD, 0, 0, -1, 437 &n->net, sizeof(struct network_config)) == -1) 438 return (-1); 439 if (send_filterset(ibuf_rde, &n->net.attrset, 0, 1) == -1) 440 return (-1); 441 if (imsg_compose(ibuf_rde, IMSG_NETWORK_DONE, 0, 0, -1, 442 NULL, 0) == -1) 443 return (-1); 444 TAILQ_REMOVE(&net_l, n, entry); 445 free(n); 446 } 447 while ((r = TAILQ_FIRST(rules_l)) != NULL) { 448 if (imsg_compose(ibuf_rde, IMSG_RECONF_FILTER, 0, 0, -1, 449 r, sizeof(struct filter_rule)) == -1) 450 return (-1); 451 if (send_filterset(ibuf_rde, &r->set, 0, 1) == -1) 452 return (-1); 453 TAILQ_REMOVE(rules_l, r, entry); 454 free(r); 455 } 456 TAILQ_FOREACH(la, conf->listen_addrs, entry) { 457 if (imsg_compose(ibuf_se, IMSG_RECONF_LISTENER, 0, 0, la->fd, 458 la, sizeof(struct listen_addr)) == -1) 459 return (-1); 460 la->fd = -1; 461 } 462 463 if (imsg_compose(ibuf_se, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1 || 464 imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1) 465 return (-1); 466 467 /* mrt changes can be sent out of bound */ 468 mrt_reconfigure(mrt_l); 469 return (0); 470} 471 472int 473dispatch_imsg(struct imsgbuf *ibuf, int idx) 474{ 475 struct imsg imsg; 476 int n; 477 int rv; 478 479 if ((n = imsg_read(ibuf)) == -1) 480 return (-1); 481 482 if (n == 0) { /* connection closed */ 483 log_warnx("dispatch_imsg in main: pipe closed"); 484 return (-1); 485 } 486 487 rv = 0; 488 for (;;) { 489 if ((n = imsg_get(ibuf, &imsg)) == -1) 490 return (-1); 491 492 if (n == 0) 493 break; 494 495 switch (imsg.hdr.type) { 496 case IMSG_KROUTE_CHANGE: 497 if (idx != PFD_PIPE_ROUTE) 498 log_warnx("route request not from RDE"); 499 else if (kr_change(imsg.data)) 500 rv = -1; 501 break; 502 case IMSG_KROUTE_DELETE: 503 if (idx != PFD_PIPE_ROUTE) 504 log_warnx("route request not from RDE"); 505 else if (kr_delete(imsg.data)) 506 rv = -1; 507 break; 508 case IMSG_NEXTHOP_ADD: 509 if (idx != PFD_PIPE_ROUTE) 510 log_warnx("nexthop request not from RDE"); 511 else 512 if (imsg.hdr.len != IMSG_HEADER_SIZE + 513 sizeof(struct bgpd_addr)) 514 log_warnx("wrong imsg len"); 515 else if (kr_nexthop_add(imsg.data) == -1) 516 rv = -1; 517 break; 518 case IMSG_NEXTHOP_REMOVE: 519 if (idx != PFD_PIPE_ROUTE) 520 log_warnx("nexthop request not from RDE"); 521 else 522 if (imsg.hdr.len != IMSG_HEADER_SIZE + 523 sizeof(struct bgpd_addr)) 524 log_warnx("wrong imsg len"); 525 else 526 kr_nexthop_delete(imsg.data); 527 break; 528 case IMSG_PFTABLE_ADD: 529 if (idx != PFD_PIPE_ROUTE) 530 log_warnx("pftable request not from RDE"); 531 else 532 if (imsg.hdr.len != IMSG_HEADER_SIZE + 533 sizeof(struct pftable_msg)) 534 log_warnx("wrong imsg len"); 535 else if (pftable_addr_add(imsg.data) != 0) 536 rv = -1; 537 break; 538 case IMSG_PFTABLE_REMOVE: 539 if (idx != PFD_PIPE_ROUTE) 540 log_warnx("pftable request not from RDE"); 541 else 542 if (imsg.hdr.len != IMSG_HEADER_SIZE + 543 sizeof(struct pftable_msg)) 544 log_warnx("wrong imsg len"); 545 else if (pftable_addr_remove(imsg.data) != 0) 546 rv = -1; 547 break; 548 case IMSG_PFTABLE_COMMIT: 549 if (idx != PFD_PIPE_ROUTE) 550 log_warnx("pftable request not from RDE"); 551 else 552 if (imsg.hdr.len != IMSG_HEADER_SIZE) 553 log_warnx("wrong imsg len"); 554 else if (pftable_commit() != 0) 555 rv = -1; 556 break; 557 case IMSG_CTL_RELOAD: 558 if (idx != PFD_PIPE_SESSION) 559 log_warnx("reload request not from SE"); 560 else 561 reconfig = 1; 562 break; 563 case IMSG_CTL_FIB_COUPLE: 564 if (idx != PFD_PIPE_SESSION) 565 log_warnx("couple request not from SE"); 566 else 567 kr_fib_couple(); 568 break; 569 case IMSG_CTL_FIB_DECOUPLE: 570 if (idx != PFD_PIPE_SESSION) 571 log_warnx("decouple request not from SE"); 572 else 573 kr_fib_decouple(); 574 break; 575 case IMSG_CTL_KROUTE: 576 case IMSG_CTL_KROUTE_ADDR: 577 case IMSG_CTL_SHOW_NEXTHOP: 578 case IMSG_CTL_SHOW_INTERFACE: 579 if (idx != PFD_PIPE_SESSION) 580 log_warnx("kroute request not from SE"); 581 else 582 kr_show_route(&imsg); 583 break; 584 case IMSG_IFINFO: 585 if (idx != PFD_PIPE_SESSION) 586 log_warnx("IFINFO request not from SE"); 587 else if (imsg.hdr.len != IMSG_HEADER_SIZE + IFNAMSIZ) 588 log_warnx("IFINFO request with wrong len"); 589 else 590 kr_ifinfo(imsg.data); 591 break; 592 default: 593 break; 594 } 595 imsg_free(&imsg); 596 if (rv != 0) 597 return (rv); 598 } 599 return (0); 600} 601 602void 603send_nexthop_update(struct kroute_nexthop *msg) 604{ 605 char *gw = NULL; 606 607 if (msg->gateway.af) 608 if (asprintf(&gw, ": via %s", 609 log_addr(&msg->gateway)) == -1) { 610 log_warn("send_nexthop_update"); 611 quit = 1; 612 } 613 614 log_info("nexthop %s now %s%s%s", log_addr(&msg->nexthop), 615 msg->valid ? "valid" : "invalid", 616 msg->connected ? ": directly connected" : "", 617 msg->gateway.af ? gw : ""); 618 619 free(gw); 620 621 if (imsg_compose(ibuf_rde, IMSG_NEXTHOP_UPDATE, 0, 0, -1, 622 msg, sizeof(struct kroute_nexthop)) == -1) 623 quit = 1; 624} 625 626void 627send_imsg_session(int type, pid_t pid, void *data, u_int16_t datalen) 628{ 629 imsg_compose(ibuf_se, type, 0, pid, -1, data, datalen); 630} 631