control.c revision 1.116
1/* $OpenBSD: control.c,v 1.116 2024/01/11 15:46:25 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 32TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns); 33 34#define CONTROL_BACKLOG 5 35 36struct ctl_conn *control_connbyfd(int); 37struct ctl_conn *control_connbypid(pid_t); 38int control_close(struct ctl_conn *); 39void control_result(struct ctl_conn *, u_int); 40ssize_t imsg_read_nofd(struct imsgbuf *); 41 42int 43control_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 68int 69control_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 124int 125control_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 135void 136control_shutdown(int fd) 137{ 138 close(fd); 139} 140 141size_t 142control_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->imsgbuf.fd; 149 pfd[i].events = POLLIN; 150 if (ctl_conn->imsgbuf.w.queued > 0) 151 pfd[i].events |= POLLOUT; 152 i++; 153 } 154 return i; 155} 156 157unsigned int 158control_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->imsgbuf, connfd); 185 ctl_conn->restricted = restricted; 186 187 TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry); 188 189 return (1); 190} 191 192struct ctl_conn * 193control_connbyfd(int fd) 194{ 195 struct ctl_conn *c; 196 197 TAILQ_FOREACH(c, &ctl_conns, entry) { 198 if (c->imsgbuf.fd == fd) 199 break; 200 } 201 202 return (c); 203} 204 205struct ctl_conn * 206control_connbypid(pid_t pid) 207{ 208 struct ctl_conn *c; 209 210 TAILQ_FOREACH(c, &ctl_conns, entry) { 211 if (c->imsgbuf.pid == pid) 212 break; 213 } 214 215 return (c); 216} 217 218int 219control_close(struct ctl_conn *c) 220{ 221 if (c->terminate && c->imsgbuf.pid) 222 imsg_ctl_rde_msg(IMSG_CTL_TERMINATE, 0, c->imsgbuf.pid); 223 224 msgbuf_clear(&c->imsgbuf.w); 225 TAILQ_REMOVE(&ctl_conns, c, entry); 226 227 close(c->imsgbuf.fd); 228 free(c); 229 pauseaccept = 0; 230 return (1); 231} 232 233int 234control_dispatch_msg(struct pollfd *pfd, struct peer_head *peers) 235{ 236 struct imsg imsg; 237 struct ctl_neighbor neighbor; 238 struct ctl_show_rib_request ribreq; 239 struct ctl_conn *c; 240 struct peer *p; 241 ssize_t n; 242 uint32_t type; 243 pid_t pid; 244 int verbose, matched; 245 246 if ((c = control_connbyfd(pfd->fd)) == NULL) { 247 log_warn("control_dispatch_msg: fd %d: not found", pfd->fd); 248 return (0); 249 } 250 251 if (pfd->revents & POLLOUT) { 252 if (msgbuf_write(&c->imsgbuf.w) <= 0 && errno != EAGAIN) 253 return control_close(c); 254 if (c->throttled && c->imsgbuf.w.queued < CTL_MSG_LOW_MARK) { 255 if (imsg_ctl_rde_msg(IMSG_XON, 0, c->imsgbuf.pid) != -1) 256 c->throttled = 0; 257 } 258 } 259 260 if (!(pfd->revents & POLLIN)) 261 return (0); 262 263 if (((n = imsg_read_nofd(&c->imsgbuf)) == -1 && errno != EAGAIN) || 264 n == 0) 265 return control_close(c); 266 267 for (;;) { 268 if ((n = imsg_get(&c->imsgbuf, &imsg)) == -1) 269 return control_close(c); 270 271 if (n == 0) 272 break; 273 274 type = imsg_get_type(&imsg); 275 pid = imsg_get_pid(&imsg); 276 if (c->restricted) { 277 switch (type) { 278 case IMSG_CTL_SHOW_NEIGHBOR: 279 case IMSG_CTL_SHOW_NEXTHOP: 280 case IMSG_CTL_SHOW_INTERFACE: 281 case IMSG_CTL_SHOW_RIB_MEM: 282 case IMSG_CTL_SHOW_TERSE: 283 case IMSG_CTL_SHOW_TIMER: 284 case IMSG_CTL_SHOW_NETWORK: 285 case IMSG_CTL_SHOW_FLOWSPEC: 286 case IMSG_CTL_SHOW_RIB: 287 case IMSG_CTL_SHOW_RIB_PREFIX: 288 case IMSG_CTL_SHOW_SET: 289 case IMSG_CTL_SHOW_RTR: 290 break; 291 default: 292 /* clear imsg type to prevent processing */ 293 type = IMSG_NONE; 294 control_result(c, CTL_RES_DENIED); 295 break; 296 } 297 } 298 299 /* 300 * TODO: this is wrong and shoud work the other way around. 301 * The imsg.hdr.pid is from the remote end and should not 302 * be trusted. 303 */ 304 c->imsgbuf.pid = pid; 305 switch (type) { 306 case IMSG_NONE: 307 /* message was filtered out, nothing to do */ 308 break; 309 case IMSG_CTL_FIB_COUPLE: 310 case IMSG_CTL_FIB_DECOUPLE: 311 imsg_ctl_parent(&imsg); 312 break; 313 case IMSG_CTL_SHOW_TERSE: 314 RB_FOREACH(p, peer_head, peers) 315 imsg_compose(&c->imsgbuf, 316 IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, 317 p, sizeof(struct peer)); 318 imsg_compose(&c->imsgbuf, IMSG_CTL_END, 0, 0, -1, 319 NULL, 0); 320 break; 321 case IMSG_CTL_SHOW_NEIGHBOR: 322 if (imsg_get_data(&imsg, &neighbor, 323 sizeof(neighbor)) == -1) 324 memset(&neighbor, 0, sizeof(neighbor)); 325 326 matched = 0; 327 RB_FOREACH(p, peer_head, peers) { 328 if (!peer_matched(p, &neighbor)) 329 continue; 330 331 matched = 1; 332 if (!neighbor.show_timers) { 333 imsg_ctl_rde_msg(type, 334 p->conf.id, pid); 335 } else { 336 u_int i; 337 time_t d; 338 struct ctl_timer ct; 339 340 imsg_compose(&c->imsgbuf, 341 IMSG_CTL_SHOW_NEIGHBOR, 342 0, 0, -1, p, sizeof(*p)); 343 for (i = 1; i < Timer_Max; i++) { 344 if (!timer_running(&p->timers, 345 i, &d)) 346 continue; 347 ct.type = i; 348 ct.val = d; 349 imsg_compose(&c->imsgbuf, 350 IMSG_CTL_SHOW_TIMER, 351 0, 0, -1, &ct, sizeof(ct)); 352 } 353 } 354 } 355 if (!matched && RB_EMPTY(peers)) { 356 control_result(c, CTL_RES_NOSUCHPEER); 357 } else if (!neighbor.show_timers) { 358 imsg_ctl_rde_msg(IMSG_CTL_END, 0, pid); 359 } else { 360 imsg_compose(&c->imsgbuf, IMSG_CTL_END, 0, 0, 361 -1, NULL, 0); 362 } 363 break; 364 case IMSG_CTL_NEIGHBOR_UP: 365 case IMSG_CTL_NEIGHBOR_DOWN: 366 case IMSG_CTL_NEIGHBOR_CLEAR: 367 case IMSG_CTL_NEIGHBOR_RREFRESH: 368 case IMSG_CTL_NEIGHBOR_DESTROY: 369 if (imsg_get_data(&imsg, &neighbor, 370 sizeof(neighbor)) == -1) { 371 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 372 "wrong length"); 373 break; 374 } 375 376 matched = 0; 377 RB_FOREACH(p, peer_head, peers) { 378 if (!peer_matched(p, &neighbor)) 379 continue; 380 381 matched = 1; 382 383 switch (type) { 384 case IMSG_CTL_NEIGHBOR_UP: 385 bgp_fsm(p, EVNT_START); 386 p->conf.down = 0; 387 p->conf.reason[0] = '\0'; 388 p->IdleHoldTime = 389 INTERVAL_IDLE_HOLD_INITIAL; 390 p->errcnt = 0; 391 control_result(c, CTL_RES_OK); 392 break; 393 case IMSG_CTL_NEIGHBOR_DOWN: 394 neighbor.reason[ 395 sizeof(neighbor.reason) - 1] = '\0'; 396 strlcpy(p->conf.reason, 397 neighbor.reason, 398 sizeof(p->conf.reason)); 399 p->conf.down = 1; 400 session_stop(p, ERR_CEASE_ADMIN_DOWN); 401 control_result(c, CTL_RES_OK); 402 break; 403 case IMSG_CTL_NEIGHBOR_CLEAR: 404 neighbor.reason[ 405 sizeof(neighbor.reason) - 1] = '\0'; 406 strlcpy(p->conf.reason, 407 neighbor.reason, 408 sizeof(p->conf.reason)); 409 p->IdleHoldTime = 410 INTERVAL_IDLE_HOLD_INITIAL; 411 p->errcnt = 0; 412 if (!p->conf.down) { 413 session_stop(p, 414 ERR_CEASE_ADMIN_RESET); 415 timer_set(&p->timers, 416 Timer_IdleHold, 417 SESSION_CLEAR_DELAY); 418 } else { 419 session_stop(p, 420 ERR_CEASE_ADMIN_DOWN); 421 } 422 control_result(c, CTL_RES_OK); 423 break; 424 case IMSG_CTL_NEIGHBOR_RREFRESH: 425 if (session_neighbor_rrefresh(p)) 426 control_result(c, 427 CTL_RES_NOCAP); 428 else 429 control_result(c, CTL_RES_OK); 430 break; 431 case IMSG_CTL_NEIGHBOR_DESTROY: 432 if (!p->template) 433 control_result(c, 434 CTL_RES_BADPEER); 435 else if (p->state != STATE_IDLE) 436 control_result(c, 437 CTL_RES_BADSTATE); 438 else { 439 /* 440 * Mark as deleted, will be 441 * collected on next poll loop. 442 */ 443 p->reconf_action = 444 RECONF_DELETE; 445 control_result(c, CTL_RES_OK); 446 } 447 break; 448 default: 449 fatal("king bula wants more humppa"); 450 } 451 } 452 if (!matched) 453 control_result(c, CTL_RES_NOSUCHPEER); 454 break; 455 case IMSG_CTL_RELOAD: 456 case IMSG_CTL_SHOW_INTERFACE: 457 case IMSG_CTL_SHOW_FIB_TABLES: 458 case IMSG_CTL_SHOW_RTR: 459 imsg_ctl_parent(&imsg); 460 break; 461 case IMSG_CTL_KROUTE: 462 case IMSG_CTL_KROUTE_ADDR: 463 case IMSG_CTL_SHOW_NEXTHOP: 464 imsg_ctl_parent(&imsg); 465 break; 466 case IMSG_CTL_SHOW_RIB: 467 case IMSG_CTL_SHOW_RIB_PREFIX: 468 if (imsg_get_data(&imsg, &ribreq, sizeof(ribreq)) == 469 -1) { 470 log_warnx("got IMSG_CTL_SHOW_RIB with " 471 "wrong length"); 472 break; 473 } 474 475 /* check if at least one neighbor exists */ 476 RB_FOREACH(p, peer_head, peers) 477 if (peer_matched(p, &ribreq.neighbor)) 478 break; 479 if (p == NULL && RB_EMPTY(peers)) { 480 control_result(c, CTL_RES_NOSUCHPEER); 481 break; 482 } 483 484 if (type == IMSG_CTL_SHOW_RIB_PREFIX && 485 ribreq.prefix.aid == AID_UNSPEC) { 486 /* malformed request, must specify af */ 487 control_result(c, CTL_RES_PARSE_ERROR); 488 break; 489 } 490 491 c->terminate = 1; 492 imsg_ctl_rde(&imsg); 493 break; 494 case IMSG_CTL_SHOW_NETWORK: 495 case IMSG_CTL_SHOW_FLOWSPEC: 496 c->terminate = 1; 497 /* FALLTHROUGH */ 498 case IMSG_CTL_SHOW_RIB_MEM: 499 case IMSG_CTL_SHOW_SET: 500 imsg_ctl_rde(&imsg); 501 break; 502 case IMSG_NETWORK_ADD: 503 case IMSG_NETWORK_ASPATH: 504 case IMSG_NETWORK_ATTR: 505 case IMSG_NETWORK_REMOVE: 506 case IMSG_NETWORK_FLUSH: 507 case IMSG_NETWORK_DONE: 508 case IMSG_FLOWSPEC_ADD: 509 case IMSG_FLOWSPEC_REMOVE: 510 case IMSG_FLOWSPEC_DONE: 511 case IMSG_FLOWSPEC_FLUSH: 512 case IMSG_FILTER_SET: 513 imsg_ctl_rde(&imsg); 514 break; 515 case IMSG_CTL_LOG_VERBOSE: 516 if (imsg_get_data(&imsg, &verbose, sizeof(verbose)) == 517 -1) 518 break; 519 520 /* forward to other processes */ 521 imsg_ctl_parent(&imsg); 522 imsg_ctl_rde(&imsg); 523 log_setverbose(verbose); 524 break; 525 default: 526 break; 527 } 528 imsg_free(&imsg); 529 } 530 531 return (0); 532} 533 534int 535control_imsg_relay(struct imsg *imsg, struct peer *p) 536{ 537 struct ctl_conn *c; 538 uint32_t type; 539 pid_t pid; 540 541 type = imsg_get_type(imsg); 542 pid = imsg_get_pid(imsg); 543 544 if ((c = control_connbypid(pid)) == NULL) 545 return (0); 546 547 /* special handling for peers since only the stats are sent from RDE */ 548 if (type == IMSG_CTL_SHOW_NEIGHBOR) { 549 struct rde_peer_stats stats; 550 551 if (p == NULL) { 552 log_warnx("%s: no such peer: id=%u", __func__, 553 imsg_get_id(imsg)); 554 return (0); 555 } 556 if (imsg_get_data(imsg, &stats, sizeof(stats)) == -1) { 557 log_warnx("%s: imsg_get_data", __func__); 558 return (0); 559 } 560 p->stats.prefix_cnt = stats.prefix_cnt; 561 p->stats.prefix_out_cnt = stats.prefix_out_cnt; 562 p->stats.prefix_rcvd_update = stats.prefix_rcvd_update; 563 p->stats.prefix_rcvd_withdraw = stats.prefix_rcvd_withdraw; 564 p->stats.prefix_rcvd_eor = stats.prefix_rcvd_eor; 565 p->stats.prefix_sent_update = stats.prefix_sent_update; 566 p->stats.prefix_sent_withdraw = stats.prefix_sent_withdraw; 567 p->stats.prefix_sent_eor = stats.prefix_sent_eor; 568 p->stats.pending_update = stats.pending_update; 569 p->stats.pending_withdraw = stats.pending_withdraw; 570 571 return imsg_compose(&c->imsgbuf, type, 0, pid, -1, 572 p, sizeof(*p)); 573 } 574 575 /* if command finished no need to send exit message */ 576 if (type == IMSG_CTL_END || type == IMSG_CTL_RESULT) 577 c->terminate = 0; 578 579 if (!c->throttled && c->imsgbuf.w.queued > CTL_MSG_HIGH_MARK) { 580 if (imsg_ctl_rde_msg(IMSG_XOFF, 0, pid) != -1) 581 c->throttled = 1; 582 } 583 584 return (imsg_forward(&c->imsgbuf, imsg)); 585} 586 587void 588control_result(struct ctl_conn *c, u_int code) 589{ 590 imsg_compose(&c->imsgbuf, IMSG_CTL_RESULT, 0, c->imsgbuf.pid, -1, 591 &code, sizeof(code)); 592} 593 594/* This should go into libutil, from smtpd/mproc.c */ 595ssize_t 596imsg_read_nofd(struct imsgbuf *imsgbuf) 597{ 598 ssize_t n; 599 char *buf; 600 size_t len; 601 602 buf = imsgbuf->r.buf + imsgbuf->r.wpos; 603 len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos; 604 605 while ((n = recv(imsgbuf->fd, buf, len, 0)) == -1) { 606 if (errno != EINTR) 607 return (n); 608 } 609 610 imsgbuf->r.wpos += n; 611 return (n); 612} 613