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