1 /* $OpenBSD: control.c,v 1.132 2024/12/02 15:03:17 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/stat.h> 21 #include <sys/socket.h> 22 #include <sys/un.h> 23 #include <errno.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "bgpd.h" 29 #include "session.h" 30 #include "log.h" 31 32 TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns); 33 34 #define CONTROL_BACKLOG 5 35 36 struct ctl_conn *control_connbyfd(int); 37 struct ctl_conn *control_connbypid(pid_t); 38 int control_close(struct ctl_conn *); 39 void control_result(struct ctl_conn *, u_int); 40 41 int 42 control_check(char *path) 43 { 44 struct sockaddr_un sa_un; 45 int fd; 46 47 memset(&sa_un, 0, sizeof(sa_un)); 48 sa_un.sun_family = AF_UNIX; 49 strlcpy(sa_un.sun_path, path, sizeof(sa_un.sun_path)); 50 51 if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) { 52 log_warn("%s: socket", __func__); 53 return (-1); 54 } 55 56 if (connect(fd, (struct sockaddr *)&sa_un, sizeof(sa_un)) == 0) { 57 log_warnx("control socket %s already in use", path); 58 close(fd); 59 return (-1); 60 } 61 62 close(fd); 63 64 return (0); 65 } 66 67 int 68 control_init(int restricted, char *path) 69 { 70 struct sockaddr_un sa_un; 71 int fd; 72 mode_t old_umask, mode; 73 74 if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 75 0)) == -1) { 76 log_warn("control_init: socket"); 77 return (-1); 78 } 79 80 memset(&sa_un, 0, sizeof(sa_un)); 81 sa_un.sun_family = AF_UNIX; 82 if (strlcpy(sa_un.sun_path, path, sizeof(sa_un.sun_path)) >= 83 sizeof(sa_un.sun_path)) { 84 log_warn("control_init: socket name too long"); 85 close(fd); 86 return (-1); 87 } 88 89 if (unlink(path) == -1) 90 if (errno != ENOENT) { 91 log_warn("control_init: unlink %s", path); 92 close(fd); 93 return (-1); 94 } 95 96 if (restricted) { 97 old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH); 98 mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 99 } else { 100 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 101 mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP; 102 } 103 104 if (bind(fd, (struct sockaddr *)&sa_un, sizeof(sa_un)) == -1) { 105 log_warn("control_init: bind: %s", path); 106 close(fd); 107 umask(old_umask); 108 return (-1); 109 } 110 111 umask(old_umask); 112 113 if (chmod(path, mode) == -1) { 114 log_warn("control_init: chmod: %s", path); 115 close(fd); 116 unlink(path); 117 return (-1); 118 } 119 120 return (fd); 121 } 122 123 int 124 control_listen(int fd) 125 { 126 if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) { 127 log_warn("control_listen: listen"); 128 return (-1); 129 } 130 131 return (0); 132 } 133 134 void 135 control_shutdown(int fd) 136 { 137 close(fd); 138 } 139 140 size_t 141 control_fill_pfds(struct pollfd *pfd, size_t size) 142 { 143 struct ctl_conn *ctl_conn; 144 size_t i = 0; 145 146 TAILQ_FOREACH(ctl_conn, &ctl_conns, entry) { 147 pfd[i].fd = ctl_conn->imsgbuf.fd; 148 pfd[i].events = POLLIN; 149 if (imsgbuf_queuelen(&ctl_conn->imsgbuf) > 0) 150 pfd[i].events |= POLLOUT; 151 i++; 152 } 153 return i; 154 } 155 156 unsigned int 157 control_accept(int listenfd, int restricted) 158 { 159 int connfd; 160 socklen_t len; 161 struct sockaddr_un sa_un; 162 struct ctl_conn *ctl_conn; 163 164 len = sizeof(sa_un); 165 if ((connfd = accept4(listenfd, 166 (struct sockaddr *)&sa_un, &len, 167 SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) { 168 if (errno == ENFILE || errno == EMFILE) { 169 pauseaccept = getmonotime(); 170 return (0); 171 } else if (errno != EWOULDBLOCK && errno != EINTR && 172 errno != ECONNABORTED) 173 log_warn("control_accept: accept"); 174 return (0); 175 } 176 177 if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) { 178 log_warn("control_accept"); 179 close(connfd); 180 return (0); 181 } 182 183 if (imsgbuf_init(&ctl_conn->imsgbuf, connfd) == -1 || 184 imsgbuf_set_maxsize(&ctl_conn->imsgbuf, MAX_BGPD_IMSGSIZE) == -1) { 185 log_warn("control_accept"); 186 imsgbuf_clear(&ctl_conn->imsgbuf); 187 close(connfd); 188 free(ctl_conn); 189 return (0); 190 } 191 ctl_conn->restricted = restricted; 192 193 TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry); 194 195 return (1); 196 } 197 198 struct ctl_conn * 199 control_connbyfd(int fd) 200 { 201 struct ctl_conn *c; 202 203 TAILQ_FOREACH(c, &ctl_conns, entry) { 204 if (c->imsgbuf.fd == fd) 205 break; 206 } 207 208 return (c); 209 } 210 211 struct ctl_conn * 212 control_connbypid(pid_t pid) 213 { 214 struct ctl_conn *c; 215 216 TAILQ_FOREACH(c, &ctl_conns, entry) { 217 if (c->imsgbuf.pid == pid) 218 break; 219 } 220 221 return (c); 222 } 223 224 int 225 control_close(struct ctl_conn *c) 226 { 227 if (c->terminate && c->imsgbuf.pid) 228 imsg_ctl_rde_msg(IMSG_CTL_TERMINATE, 0, c->imsgbuf.pid); 229 230 imsgbuf_clear(&c->imsgbuf); 231 TAILQ_REMOVE(&ctl_conns, c, entry); 232 233 close(c->imsgbuf.fd); 234 free(c); 235 pauseaccept = 0; 236 return (1); 237 } 238 239 int 240 control_dispatch_msg(struct pollfd *pfd, struct peer_head *peers) 241 { 242 struct imsg imsg; 243 struct ctl_neighbor neighbor; 244 struct ctl_show_rib_request ribreq; 245 struct ctl_conn *c; 246 struct peer *p; 247 ssize_t n; 248 uint32_t type; 249 pid_t pid; 250 int verbose, matched; 251 252 if ((c = control_connbyfd(pfd->fd)) == NULL) { 253 log_warn("control_dispatch_msg: fd %d: not found", pfd->fd); 254 return (0); 255 } 256 257 if (pfd->revents & POLLOUT) { 258 if (imsgbuf_write(&c->imsgbuf) == -1) 259 return control_close(c); 260 if (c->throttled && 261 imsgbuf_queuelen(&c->imsgbuf) < CTL_MSG_LOW_MARK) { 262 if (imsg_ctl_rde_msg(IMSG_XON, 0, c->imsgbuf.pid) != -1) 263 c->throttled = 0; 264 } 265 } 266 267 if (!(pfd->revents & POLLIN)) 268 return (0); 269 270 if (imsgbuf_read(&c->imsgbuf) != 1) 271 return control_close(c); 272 273 for (;;) { 274 if ((n = imsg_get(&c->imsgbuf, &imsg)) == -1) 275 return control_close(c); 276 277 if (n == 0) 278 break; 279 280 type = imsg_get_type(&imsg); 281 pid = imsg_get_pid(&imsg); 282 if (c->restricted) { 283 switch (type) { 284 case IMSG_CTL_SHOW_NEIGHBOR: 285 case IMSG_CTL_SHOW_NEXTHOP: 286 case IMSG_CTL_SHOW_INTERFACE: 287 case IMSG_CTL_SHOW_RIB_MEM: 288 case IMSG_CTL_SHOW_TERSE: 289 case IMSG_CTL_SHOW_TIMER: 290 case IMSG_CTL_SHOW_NETWORK: 291 case IMSG_CTL_SHOW_FLOWSPEC: 292 case IMSG_CTL_SHOW_RIB: 293 case IMSG_CTL_SHOW_RIB_PREFIX: 294 case IMSG_CTL_SHOW_SET: 295 case IMSG_CTL_SHOW_RTR: 296 break; 297 default: 298 /* clear imsg type to prevent processing */ 299 type = IMSG_NONE; 300 control_result(c, CTL_RES_DENIED); 301 break; 302 } 303 } 304 305 /* 306 * TODO: this is wrong and shoud work the other way around. 307 * The imsg.hdr.pid is from the remote end and should not 308 * be trusted. 309 */ 310 c->imsgbuf.pid = pid; 311 switch (type) { 312 case IMSG_NONE: 313 /* message was filtered out, nothing to do */ 314 break; 315 case IMSG_CTL_FIB_COUPLE: 316 case IMSG_CTL_FIB_DECOUPLE: 317 imsg_ctl_parent(&imsg); 318 break; 319 case IMSG_CTL_SHOW_TERSE: 320 RB_FOREACH(p, peer_head, peers) 321 imsg_compose(&c->imsgbuf, 322 IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, 323 p, sizeof(struct peer)); 324 imsg_compose(&c->imsgbuf, IMSG_CTL_END, 0, 0, -1, 325 NULL, 0); 326 break; 327 case IMSG_CTL_SHOW_NEIGHBOR: 328 if (imsg_get_data(&imsg, &neighbor, 329 sizeof(neighbor)) == -1) 330 memset(&neighbor, 0, sizeof(neighbor)); 331 332 matched = 0; 333 RB_FOREACH(p, peer_head, peers) { 334 if (!peer_matched(p, &neighbor)) 335 continue; 336 337 matched = 1; 338 if (!neighbor.show_timers) { 339 imsg_ctl_rde_msg(type, 340 p->conf.id, pid); 341 } else { 342 u_int i; 343 time_t d; 344 struct ctl_timer ct; 345 346 imsg_compose(&c->imsgbuf, 347 IMSG_CTL_SHOW_NEIGHBOR, 348 0, 0, -1, p, sizeof(*p)); 349 for (i = 1; i < Timer_Max; i++) { 350 if (!timer_running(&p->timers, 351 i, &d)) 352 continue; 353 ct.type = i; 354 ct.val = d; 355 imsg_compose(&c->imsgbuf, 356 IMSG_CTL_SHOW_TIMER, 357 0, 0, -1, &ct, sizeof(ct)); 358 } 359 } 360 } 361 if (!matched && !RB_EMPTY(peers)) { 362 control_result(c, CTL_RES_NOSUCHPEER); 363 } else if (!neighbor.show_timers) { 364 imsg_ctl_rde_msg(IMSG_CTL_END, 0, pid); 365 } else { 366 imsg_compose(&c->imsgbuf, IMSG_CTL_END, 0, 0, 367 -1, NULL, 0); 368 } 369 break; 370 case IMSG_CTL_NEIGHBOR_UP: 371 case IMSG_CTL_NEIGHBOR_DOWN: 372 case IMSG_CTL_NEIGHBOR_CLEAR: 373 case IMSG_CTL_NEIGHBOR_RREFRESH: 374 case IMSG_CTL_NEIGHBOR_DESTROY: 375 if (imsg_get_data(&imsg, &neighbor, 376 sizeof(neighbor)) == -1) { 377 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 378 "wrong length"); 379 break; 380 } 381 382 matched = 0; 383 RB_FOREACH(p, peer_head, peers) { 384 if (!peer_matched(p, &neighbor)) 385 continue; 386 387 matched = 1; 388 389 switch (type) { 390 case IMSG_CTL_NEIGHBOR_UP: 391 bgp_fsm(p, EVNT_START, NULL); 392 p->conf.down = 0; 393 p->conf.reason[0] = '\0'; 394 p->IdleHoldTime = 395 INTERVAL_IDLE_HOLD_INITIAL; 396 p->errcnt = 0; 397 control_result(c, CTL_RES_OK); 398 break; 399 case IMSG_CTL_NEIGHBOR_DOWN: 400 neighbor.reason[ 401 sizeof(neighbor.reason) - 1] = '\0'; 402 p->conf.down = 1; 403 session_stop(p, ERR_CEASE_ADMIN_DOWN, 404 neighbor.reason); 405 control_result(c, CTL_RES_OK); 406 break; 407 case IMSG_CTL_NEIGHBOR_CLEAR: 408 neighbor.reason[ 409 sizeof(neighbor.reason) - 1] = '\0'; 410 p->IdleHoldTime = 411 INTERVAL_IDLE_HOLD_INITIAL; 412 p->errcnt = 0; 413 if (!p->conf.down) { 414 session_stop(p, 415 ERR_CEASE_ADMIN_RESET, 416 neighbor.reason); 417 timer_set(&p->timers, 418 Timer_IdleHold, 419 SESSION_CLEAR_DELAY); 420 } else { 421 session_stop(p, 422 ERR_CEASE_ADMIN_DOWN, 423 neighbor.reason); 424 } 425 control_result(c, CTL_RES_OK); 426 break; 427 case IMSG_CTL_NEIGHBOR_RREFRESH: 428 if (session_neighbor_rrefresh(p)) 429 control_result(c, 430 CTL_RES_NOCAP); 431 else 432 control_result(c, CTL_RES_OK); 433 break; 434 case IMSG_CTL_NEIGHBOR_DESTROY: 435 if (!p->template) 436 control_result(c, 437 CTL_RES_BADPEER); 438 else if (p->state != STATE_IDLE) 439 control_result(c, 440 CTL_RES_BADSTATE); 441 else { 442 /* 443 * Mark as deleted, will be 444 * collected on next poll loop. 445 */ 446 p->reconf_action = 447 RECONF_DELETE; 448 control_result(c, CTL_RES_OK); 449 } 450 break; 451 default: 452 fatal("king bula wants more humppa"); 453 } 454 } 455 if (!matched) 456 control_result(c, CTL_RES_NOSUCHPEER); 457 break; 458 case IMSG_CTL_RELOAD: 459 case IMSG_CTL_SHOW_INTERFACE: 460 case IMSG_CTL_SHOW_FIB_TABLES: 461 case IMSG_CTL_SHOW_RTR: 462 imsg_ctl_parent(&imsg); 463 break; 464 case IMSG_CTL_KROUTE: 465 case IMSG_CTL_KROUTE_ADDR: 466 case IMSG_CTL_SHOW_NEXTHOP: 467 imsg_ctl_parent(&imsg); 468 break; 469 case IMSG_CTL_SHOW_RIB: 470 case IMSG_CTL_SHOW_RIB_PREFIX: 471 if (imsg_get_data(&imsg, &ribreq, sizeof(ribreq)) == 472 -1) { 473 log_warnx("got IMSG_CTL_SHOW_RIB with " 474 "wrong length"); 475 break; 476 } 477 478 /* check if at least one neighbor exists */ 479 RB_FOREACH(p, peer_head, peers) 480 if (peer_matched(p, &ribreq.neighbor)) 481 break; 482 if (p == NULL && !RB_EMPTY(peers)) { 483 control_result(c, CTL_RES_NOSUCHPEER); 484 break; 485 } 486 487 if (type == IMSG_CTL_SHOW_RIB_PREFIX && 488 ribreq.prefix.aid == AID_UNSPEC) { 489 /* malformed request, must specify af */ 490 control_result(c, CTL_RES_PARSE_ERROR); 491 break; 492 } 493 494 c->terminate = 1; 495 imsg_ctl_rde(&imsg); 496 break; 497 case IMSG_CTL_SHOW_NETWORK: 498 case IMSG_CTL_SHOW_FLOWSPEC: 499 c->terminate = 1; 500 /* FALLTHROUGH */ 501 case IMSG_CTL_SHOW_RIB_MEM: 502 case IMSG_CTL_SHOW_SET: 503 imsg_ctl_rde(&imsg); 504 break; 505 case IMSG_NETWORK_ADD: 506 case IMSG_NETWORK_ASPATH: 507 case IMSG_NETWORK_ATTR: 508 case IMSG_NETWORK_REMOVE: 509 case IMSG_NETWORK_FLUSH: 510 case IMSG_NETWORK_DONE: 511 case IMSG_FLOWSPEC_ADD: 512 case IMSG_FLOWSPEC_REMOVE: 513 case IMSG_FLOWSPEC_DONE: 514 case IMSG_FLOWSPEC_FLUSH: 515 case IMSG_FILTER_SET: 516 imsg_ctl_rde(&imsg); 517 break; 518 case IMSG_CTL_LOG_VERBOSE: 519 if (imsg_get_data(&imsg, &verbose, sizeof(verbose)) == 520 -1) 521 break; 522 523 /* forward to other processes */ 524 imsg_ctl_parent(&imsg); 525 imsg_ctl_rde(&imsg); 526 log_setverbose(verbose); 527 break; 528 default: 529 break; 530 } 531 imsg_free(&imsg); 532 } 533 534 return (0); 535 } 536 537 int 538 control_imsg_relay(struct imsg *imsg, struct peer *p) 539 { 540 struct ctl_conn *c; 541 uint32_t type; 542 pid_t pid; 543 544 type = imsg_get_type(imsg); 545 pid = imsg_get_pid(imsg); 546 547 if ((c = control_connbypid(pid)) == NULL) 548 return (0); 549 550 /* special handling for peers since only the stats are sent from RDE */ 551 if (type == IMSG_CTL_SHOW_NEIGHBOR) { 552 struct rde_peer_stats stats; 553 struct peer peer; 554 555 if (p == NULL) { 556 log_warnx("%s: no such peer: id=%u", __func__, 557 imsg_get_id(imsg)); 558 return (0); 559 } 560 if (imsg_get_data(imsg, &stats, sizeof(stats)) == -1) { 561 log_warnx("%s: imsg_get_data", __func__); 562 return (0); 563 } 564 peer = *p; 565 explicit_bzero(&peer.auth_conf, sizeof(peer.auth_conf)); 566 peer.auth_conf.method = p->auth_conf.method; 567 peer.stats.prefix_cnt = stats.prefix_cnt; 568 peer.stats.prefix_out_cnt = stats.prefix_out_cnt; 569 peer.stats.prefix_rcvd_update = stats.prefix_rcvd_update; 570 peer.stats.prefix_rcvd_withdraw = stats.prefix_rcvd_withdraw; 571 peer.stats.prefix_rcvd_eor = stats.prefix_rcvd_eor; 572 peer.stats.prefix_sent_update = stats.prefix_sent_update; 573 peer.stats.prefix_sent_withdraw = stats.prefix_sent_withdraw; 574 peer.stats.prefix_sent_eor = stats.prefix_sent_eor; 575 peer.stats.pending_update = stats.pending_update; 576 peer.stats.pending_withdraw = stats.pending_withdraw; 577 peer.stats.msg_queue_len = msgbuf_queuelen(p->wbuf); 578 579 return imsg_compose(&c->imsgbuf, type, 0, pid, -1, 580 &peer, sizeof(peer)); 581 } 582 583 /* if command finished no need to send exit message */ 584 if (type == IMSG_CTL_END || type == IMSG_CTL_RESULT) 585 c->terminate = 0; 586 587 if (!c->throttled && 588 imsgbuf_queuelen(&c->imsgbuf) > CTL_MSG_HIGH_MARK) { 589 if (imsg_ctl_rde_msg(IMSG_XOFF, 0, pid) != -1) 590 c->throttled = 1; 591 } 592 593 return (imsg_forward(&c->imsgbuf, imsg)); 594 } 595 596 void 597 control_result(struct ctl_conn *c, u_int code) 598 { 599 imsg_compose(&c->imsgbuf, IMSG_CTL_RESULT, 0, c->imsgbuf.pid, -1, 600 &code, sizeof(code)); 601 } 602