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