control.c revision 1.103
1/* $OpenBSD: control.c,v 1.103 2020/12/30 07:29:56 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 sun; 46 int fd; 47 48 bzero(&sun, sizeof(sun)); 49 sun.sun_family = AF_UNIX; 50 strlcpy(sun.sun_path, path, sizeof(sun.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 *)&sun, sizeof(sun)) == 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 sun; 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 bzero(&sun, sizeof(sun)); 82 sun.sun_family = AF_UNIX; 83 if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >= 84 sizeof(sun.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 *)&sun, sizeof(sun)) == -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->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 157unsigned int 158control_accept(int listenfd, int restricted) 159{ 160 int connfd; 161 socklen_t len; 162 struct sockaddr_un sun; 163 struct ctl_conn *ctl_conn; 164 165 len = sizeof(sun); 166 if ((connfd = accept4(listenfd, 167 (struct sockaddr *)&sun, &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 192struct ctl_conn * 193control_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 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->ibuf.pid == pid) 212 break; 213 } 214 215 return (c); 216} 217 218int 219control_close(struct ctl_conn *c) 220{ 221 if (c->terminate && c->ibuf.pid) 222 imsg_ctl_rde(IMSG_CTL_TERMINATE, 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 233int 234control_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, c->ibuf.pid, NULL, 0) != -1) 254 c->throttled = 0; 255 } 256 } 257 258 if (!(pfd->revents & POLLIN)) 259 return (0); 260 261 if (((n = imsg_read_nofd(&c->ibuf)) == -1 && errno != EAGAIN) || 262 n == 0) 263 return control_close(c); 264 265 for (;;) { 266 if ((n = imsg_get(&c->ibuf, &imsg)) == -1) 267 return control_close(c); 268 269 if (n == 0) 270 break; 271 272 if (c->restricted) { 273 switch (imsg.hdr.type) { 274 case IMSG_CTL_SHOW_NEIGHBOR: 275 case IMSG_CTL_SHOW_NEXTHOP: 276 case IMSG_CTL_SHOW_INTERFACE: 277 case IMSG_CTL_SHOW_RIB_MEM: 278 case IMSG_CTL_SHOW_TERSE: 279 case IMSG_CTL_SHOW_TIMER: 280 case IMSG_CTL_SHOW_NETWORK: 281 case IMSG_CTL_SHOW_RIB: 282 case IMSG_CTL_SHOW_RIB_PREFIX: 283 case IMSG_CTL_SHOW_SET: 284 break; 285 default: 286 /* clear imsg type to prevent processing */ 287 imsg.hdr.type = IMSG_NONE; 288 control_result(c, CTL_RES_DENIED); 289 break; 290 } 291 } 292 293 switch (imsg.hdr.type) { 294 case IMSG_NONE: 295 /* message was filtered out, nothing to do */ 296 break; 297 case IMSG_CTL_FIB_COUPLE: 298 case IMSG_CTL_FIB_DECOUPLE: 299 imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid, 300 0, NULL, 0); 301 break; 302 case IMSG_CTL_SHOW_TERSE: 303 RB_FOREACH(p, peer_head, peers) 304 imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR, 305 0, 0, -1, p, sizeof(struct peer)); 306 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 307 break; 308 case IMSG_CTL_SHOW_NEIGHBOR: 309 c->ibuf.pid = imsg.hdr.pid; 310 311 if (imsg.hdr.len == IMSG_HEADER_SIZE + 312 sizeof(struct ctl_neighbor)) { 313 neighbor = imsg.data; 314 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 315 } else { 316 neighbor = NULL; 317 } 318 matched = 0; 319 RB_FOREACH(p, peer_head, peers) { 320 if (!peer_matched(p, neighbor)) 321 continue; 322 323 matched = 1; 324 if (!neighbor || !neighbor->show_timers) { 325 imsg_ctl_rde(imsg.hdr.type, 326 imsg.hdr.pid, 327 p, sizeof(struct peer)); 328 } else { 329 u_int i; 330 time_t d; 331 struct ctl_timer ct; 332 333 imsg_compose(&c->ibuf, 334 IMSG_CTL_SHOW_NEIGHBOR, 335 0, 0, -1, p, sizeof(*p)); 336 for (i = 1; i < Timer_Max; i++) { 337 if (!timer_running(&p->timers, 338 i, &d)) 339 continue; 340 ct.type = i; 341 ct.val = d; 342 imsg_compose(&c->ibuf, 343 IMSG_CTL_SHOW_TIMER, 344 0, 0, -1, &ct, sizeof(ct)); 345 } 346 } 347 } 348 if (!matched && RB_EMPTY(peers)) { 349 control_result(c, CTL_RES_NOSUCHPEER); 350 } else if (!neighbor || !neighbor->show_timers) { 351 imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid, 352 NULL, 0); 353 } else { 354 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, 355 NULL, 0); 356 } 357 break; 358 case IMSG_CTL_NEIGHBOR_UP: 359 case IMSG_CTL_NEIGHBOR_DOWN: 360 case IMSG_CTL_NEIGHBOR_CLEAR: 361 case IMSG_CTL_NEIGHBOR_RREFRESH: 362 case IMSG_CTL_NEIGHBOR_DESTROY: 363 if (imsg.hdr.len != IMSG_HEADER_SIZE + 364 sizeof(struct ctl_neighbor)) { 365 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 366 "wrong length"); 367 break; 368 } 369 370 neighbor = imsg.data; 371 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 372 373 matched = 0; 374 RB_FOREACH(p, peer_head, peers) { 375 if (!peer_matched(p, neighbor)) 376 continue; 377 378 matched = 1; 379 380 switch (imsg.hdr.type) { 381 case IMSG_CTL_NEIGHBOR_UP: 382 bgp_fsm(p, EVNT_START); 383 p->conf.down = 0; 384 p->conf.reason[0] = '\0'; 385 p->IdleHoldTime = 386 INTERVAL_IDLE_HOLD_INITIAL; 387 p->errcnt = 0; 388 control_result(c, CTL_RES_OK); 389 break; 390 case IMSG_CTL_NEIGHBOR_DOWN: 391 p->conf.down = 1; 392 strlcpy(p->conf.reason, 393 neighbor->reason, 394 sizeof(neighbor->reason)); 395 session_stop(p, ERR_CEASE_ADMIN_DOWN); 396 control_result(c, CTL_RES_OK); 397 break; 398 case IMSG_CTL_NEIGHBOR_CLEAR: 399 strlcpy(p->conf.reason, 400 neighbor->reason, 401 sizeof(neighbor->reason)); 402 p->IdleHoldTime = 403 INTERVAL_IDLE_HOLD_INITIAL; 404 p->errcnt = 0; 405 if (!p->conf.down) { 406 session_stop(p, 407 ERR_CEASE_ADMIN_RESET); 408 timer_set(&p->timers, 409 Timer_IdleHold, 410 SESSION_CLEAR_DELAY); 411 } else { 412 session_stop(p, 413 ERR_CEASE_ADMIN_DOWN); 414 } 415 control_result(c, CTL_RES_OK); 416 break; 417 case IMSG_CTL_NEIGHBOR_RREFRESH: 418 if (session_neighbor_rrefresh(p)) 419 control_result(c, 420 CTL_RES_NOCAP); 421 else 422 control_result(c, CTL_RES_OK); 423 break; 424 case IMSG_CTL_NEIGHBOR_DESTROY: 425 if (!p->template) 426 control_result(c, 427 CTL_RES_BADPEER); 428 else if (p->state != STATE_IDLE) 429 control_result(c, 430 CTL_RES_BADSTATE); 431 else { 432 /* 433 * Mark as deleted, will be 434 * collected on next poll loop. 435 */ 436 p->reconf_action = 437 RECONF_DELETE; 438 control_result(c, CTL_RES_OK); 439 } 440 break; 441 default: 442 fatal("king bula wants more humppa"); 443 } 444 } 445 if (!matched) 446 control_result(c, CTL_RES_NOSUCHPEER); 447 break; 448 case IMSG_CTL_RELOAD: 449 case IMSG_CTL_SHOW_INTERFACE: 450 case IMSG_CTL_SHOW_FIB_TABLES: 451 c->ibuf.pid = imsg.hdr.pid; 452 imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid, 453 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 454 break; 455 case IMSG_CTL_KROUTE: 456 case IMSG_CTL_KROUTE_ADDR: 457 case IMSG_CTL_SHOW_NEXTHOP: 458 c->ibuf.pid = imsg.hdr.pid; 459 imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid, 460 imsg.hdr.pid, imsg.data, imsg.hdr.len - 461 IMSG_HEADER_SIZE); 462 break; 463 case IMSG_CTL_SHOW_RIB: 464 case IMSG_CTL_SHOW_RIB_PREFIX: 465 if (imsg.hdr.len != IMSG_HEADER_SIZE + 466 sizeof(struct ctl_show_rib_request)) { 467 log_warnx("got IMSG_CTL_SHOW_RIB with " 468 "wrong length"); 469 break; 470 } 471 472 ribreq = imsg.data; 473 neighbor = &ribreq->neighbor; 474 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 475 476 /* check if at least one neighbor exists */ 477 RB_FOREACH(p, peer_head, peers) 478 if (peer_matched(p, neighbor)) 479 break; 480 if (p == NULL && RB_EMPTY(peers)) { 481 control_result(c, CTL_RES_NOSUCHPEER); 482 break; 483 } 484 485 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 486 && (ribreq->prefix.aid == AID_UNSPEC)) { 487 /* malformed request, must specify af */ 488 control_result(c, CTL_RES_PARSE_ERROR); 489 break; 490 } 491 492 c->ibuf.pid = imsg.hdr.pid; 493 c->terminate = 1; 494 495 imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, 496 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 497 break; 498 case IMSG_CTL_SHOW_NETWORK: 499 c->terminate = 1; 500 /* FALLTHROUGH */ 501 case IMSG_CTL_SHOW_RIB_MEM: 502 case IMSG_CTL_SHOW_SET: 503 c->ibuf.pid = imsg.hdr.pid; 504 imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, 505 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 506 break; 507 case IMSG_NETWORK_ADD: 508 case IMSG_NETWORK_ASPATH: 509 case IMSG_NETWORK_ATTR: 510 case IMSG_NETWORK_REMOVE: 511 case IMSG_NETWORK_FLUSH: 512 case IMSG_NETWORK_DONE: 513 case IMSG_FILTER_SET: 514 imsg_ctl_rde(imsg.hdr.type, 0, 515 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 516 break; 517 case IMSG_CTL_LOG_VERBOSE: 518 if (imsg.hdr.len != IMSG_HEADER_SIZE + 519 sizeof(verbose)) 520 break; 521 522 /* forward to other processes */ 523 imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid, 524 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 525 imsg_ctl_rde(imsg.hdr.type, 0, 526 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 527 528 memcpy(&verbose, imsg.data, sizeof(verbose)); 529 log_setverbose(verbose); 530 break; 531 default: 532 break; 533 } 534 imsg_free(&imsg); 535 } 536 537 return (0); 538} 539 540int 541control_imsg_relay(struct imsg *imsg) 542{ 543 struct ctl_conn *c; 544 545 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 546 return (0); 547 548 /* if command finished no need to send exit message */ 549 if (imsg->hdr.type == IMSG_CTL_END || imsg->hdr.type == IMSG_CTL_RESULT) 550 c->terminate = 0; 551 552 if (!c->throttled && c->ibuf.w.queued > CTL_MSG_HIGH_MARK) { 553 if (imsg_ctl_rde(IMSG_XOFF, imsg->hdr.pid, NULL, 0) != -1) 554 c->throttled = 1; 555 } 556 557 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 558 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 559} 560 561void 562control_result(struct ctl_conn *c, u_int code) 563{ 564 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 565 &code, sizeof(code)); 566} 567 568/* This should go into libutil, from smtpd/mproc.c */ 569ssize_t 570imsg_read_nofd(struct imsgbuf *ibuf) 571{ 572 ssize_t n; 573 char *buf; 574 size_t len; 575 576 buf = ibuf->r.buf + ibuf->r.wpos; 577 len = sizeof(ibuf->r.buf) - ibuf->r.wpos; 578 579 while ((n = recv(ibuf->fd, buf, len, 0)) == -1) { 580 if (errno != EINTR) 581 return (n); 582 } 583 584 ibuf->r.wpos += n; 585 return (n); 586} 587