1 /* $OpenBSD: control.c,v 1.96 2019/03/31 16:57:38 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 #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, 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 TAILQ_FOREACH(p, peers, entry) 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 TAILQ_FOREACH(p, peers, entry) { 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 && TAILQ_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 TAILQ_FOREACH(p, peers, entry) { 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.shutcomm[0] = '\0'; 379 control_result(c, CTL_RES_OK); 380 break; 381 case IMSG_CTL_NEIGHBOR_DOWN: 382 p->conf.down = 1; 383 strlcpy(p->conf.shutcomm, 384 neighbor->shutcomm, 385 sizeof(neighbor->shutcomm)); 386 session_stop(p, ERR_CEASE_ADMIN_DOWN); 387 control_result(c, CTL_RES_OK); 388 break; 389 case IMSG_CTL_NEIGHBOR_CLEAR: 390 strlcpy(p->conf.shutcomm, 391 neighbor->shutcomm, 392 sizeof(neighbor->shutcomm)); 393 if (!p->conf.down) { 394 session_stop(p, 395 ERR_CEASE_ADMIN_RESET); 396 timer_set(p, Timer_IdleHold, 397 SESSION_CLEAR_DELAY); 398 } else { 399 session_stop(p, 400 ERR_CEASE_ADMIN_DOWN); 401 } 402 control_result(c, CTL_RES_OK); 403 break; 404 case IMSG_CTL_NEIGHBOR_RREFRESH: 405 if (session_neighbor_rrefresh(p)) 406 control_result(c, 407 CTL_RES_NOCAP); 408 else 409 control_result(c, CTL_RES_OK); 410 break; 411 case IMSG_CTL_NEIGHBOR_DESTROY: 412 if (!p->template) 413 control_result(c, 414 CTL_RES_BADPEER); 415 else if (p->state != STATE_IDLE) 416 control_result(c, 417 CTL_RES_BADSTATE); 418 else { 419 /* 420 * Mark as deleted, will be 421 * collected on next poll loop. 422 */ 423 p->reconf_action = 424 RECONF_DELETE; 425 control_result(c, CTL_RES_OK); 426 } 427 break; 428 default: 429 fatal("king bula wants more humppa"); 430 } 431 } 432 if (!matched) 433 control_result(c, CTL_RES_NOSUCHPEER); 434 break; 435 case IMSG_CTL_RELOAD: 436 case IMSG_CTL_SHOW_INTERFACE: 437 case IMSG_CTL_SHOW_FIB_TABLES: 438 c->ibuf.pid = imsg.hdr.pid; 439 imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid, 440 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 441 break; 442 case IMSG_CTL_KROUTE: 443 case IMSG_CTL_KROUTE_ADDR: 444 case IMSG_CTL_SHOW_NEXTHOP: 445 c->ibuf.pid = imsg.hdr.pid; 446 imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid, 447 imsg.hdr.pid, imsg.data, imsg.hdr.len - 448 IMSG_HEADER_SIZE); 449 break; 450 case IMSG_CTL_SHOW_RIB: 451 case IMSG_CTL_SHOW_RIB_PREFIX: 452 if (imsg.hdr.len != IMSG_HEADER_SIZE + 453 sizeof(struct ctl_show_rib_request)) { 454 log_warnx("got IMSG_CTL_SHOW_RIB with " 455 "wrong length"); 456 break; 457 } 458 459 ribreq = imsg.data; 460 neighbor = &ribreq->neighbor; 461 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 462 463 /* check if at least one neighbor exists */ 464 TAILQ_FOREACH(p, peers, entry) 465 if (peer_matched(p, neighbor)) 466 break; 467 if (p == NULL && TAILQ_EMPTY(peers)) { 468 control_result(c, CTL_RES_NOSUCHPEER); 469 break; 470 } 471 472 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 473 && (ribreq->prefix.aid == AID_UNSPEC)) { 474 /* malformed request, must specify af */ 475 control_result(c, CTL_RES_PARSE_ERROR); 476 break; 477 } 478 479 c->ibuf.pid = imsg.hdr.pid; 480 c->terminate = 1; 481 482 imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, 483 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 484 break; 485 case IMSG_CTL_SHOW_NETWORK: 486 c->terminate = 1; 487 /* FALLTHROUGH */ 488 case IMSG_CTL_SHOW_RIB_MEM: 489 c->ibuf.pid = imsg.hdr.pid; 490 imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, 491 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 492 break; 493 case IMSG_NETWORK_ADD: 494 case IMSG_NETWORK_ASPATH: 495 case IMSG_NETWORK_ATTR: 496 case IMSG_NETWORK_REMOVE: 497 case IMSG_NETWORK_FLUSH: 498 case IMSG_NETWORK_DONE: 499 case IMSG_FILTER_SET: 500 imsg_ctl_rde(imsg.hdr.type, 0, 501 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 502 break; 503 case IMSG_CTL_LOG_VERBOSE: 504 if (imsg.hdr.len != IMSG_HEADER_SIZE + 505 sizeof(verbose)) 506 break; 507 508 /* forward to other processes */ 509 imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid, 510 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 511 imsg_ctl_rde(imsg.hdr.type, 0, 512 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 513 514 memcpy(&verbose, imsg.data, sizeof(verbose)); 515 log_setverbose(verbose); 516 break; 517 default: 518 break; 519 } 520 imsg_free(&imsg); 521 } 522 523 return (0); 524 } 525 526 int 527 control_imsg_relay(struct imsg *imsg) 528 { 529 struct ctl_conn *c; 530 531 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 532 return (0); 533 534 /* if command finished no need to send exit message */ 535 if (imsg->hdr.type == IMSG_CTL_END || imsg->hdr.type == IMSG_CTL_RESULT) 536 c->terminate = 0; 537 538 if (!c->throttled && c->ibuf.w.queued > CTL_MSG_HIGH_MARK) { 539 if (imsg_ctl_rde(IMSG_XOFF, imsg->hdr.pid, NULL, 0) != -1) 540 c->throttled = 1; 541 } 542 543 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 544 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 545 } 546 547 void 548 control_result(struct ctl_conn *c, u_int code) 549 { 550 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 551 &code, sizeof(code)); 552 } 553 554 /* This should go into libutil, from smtpd/mproc.c */ 555 ssize_t 556 imsg_read_nofd(struct imsgbuf *ibuf) 557 { 558 ssize_t n; 559 char *buf; 560 size_t len; 561 562 buf = ibuf->r.buf + ibuf->r.wpos; 563 len = sizeof(ibuf->r.buf) - ibuf->r.wpos; 564 565 while ((n = recv(ibuf->fd, buf, len, 0)) == -1) { 566 if (errno != EINTR) 567 return (n); 568 } 569 570 ibuf->r.wpos += n; 571 return (n); 572 } 573