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