control.c revision 1.95
1/* $OpenBSD: control.c,v 1.95 2019/02/12 13:30:39 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 34struct ctl_conn *control_connbyfd(int); 35struct ctl_conn *control_connbypid(pid_t); 36int control_close(int); 37void control_result(struct ctl_conn *, u_int); 38ssize_t imsg_read_nofd(struct imsgbuf *); 39 40int 41control_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 66int 67control_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 122int 123control_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 133void 134control_shutdown(int fd) 135{ 136 close(fd); 137} 138 139unsigned int 140control_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 174struct ctl_conn * 175control_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 187struct ctl_conn * 188control_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 200int 201control_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 222int 223control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt) 224{ 225 struct imsg imsg; 226 struct ctl_conn *c; 227 ssize_t n; 228 int verbose, matched; 229 struct peer *p; 230 struct ctl_neighbor *neighbor; 231 struct ctl_show_rib_request *ribreq; 232 233 if ((c = control_connbyfd(pfd->fd)) == NULL) { 234 log_warn("control_dispatch_msg: fd %d: not found", pfd->fd); 235 return (0); 236 } 237 238 if (pfd->revents & POLLOUT) { 239 if (msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) { 240 *ctl_cnt -= control_close(pfd->fd); 241 return (1); 242 } 243 if (c->throttled && c->ibuf.w.queued < CTL_MSG_LOW_MARK) { 244 if (imsg_ctl_rde(IMSG_XON, c->ibuf.pid, NULL, 0) != -1) 245 c->throttled = 0; 246 } 247 } 248 249 if (!(pfd->revents & POLLIN)) 250 return (0); 251 252 if (((n = imsg_read_nofd(&c->ibuf)) == -1 && errno != EAGAIN) || 253 n == 0) { 254 *ctl_cnt -= control_close(pfd->fd); 255 return (1); 256 } 257 258 for (;;) { 259 if ((n = imsg_get(&c->ibuf, &imsg)) == -1) { 260 *ctl_cnt -= control_close(pfd->fd); 261 return (1); 262 } 263 264 if (n == 0) 265 break; 266 267 if (c->restricted) { 268 switch (imsg.hdr.type) { 269 case IMSG_CTL_SHOW_NEIGHBOR: 270 case IMSG_CTL_SHOW_NEXTHOP: 271 case IMSG_CTL_SHOW_INTERFACE: 272 case IMSG_CTL_SHOW_RIB_MEM: 273 case IMSG_CTL_SHOW_TERSE: 274 case IMSG_CTL_SHOW_TIMER: 275 case IMSG_CTL_SHOW_NETWORK: 276 case IMSG_CTL_SHOW_RIB: 277 case IMSG_CTL_SHOW_RIB_PREFIX: 278 break; 279 default: 280 /* clear imsg type to prevent processing */ 281 imsg.hdr.type = IMSG_NONE; 282 control_result(c, CTL_RES_DENIED); 283 break; 284 } 285 } 286 287 switch (imsg.hdr.type) { 288 case IMSG_NONE: 289 /* message was filtered out, nothing to do */ 290 break; 291 case IMSG_CTL_FIB_COUPLE: 292 case IMSG_CTL_FIB_DECOUPLE: 293 imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid, 294 0, NULL, 0); 295 break; 296 case IMSG_CTL_SHOW_TERSE: 297 for (p = peers; p != NULL; p = p->next) 298 imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR, 299 0, 0, -1, p, sizeof(struct peer)); 300 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 301 break; 302 case IMSG_CTL_SHOW_NEIGHBOR: 303 c->ibuf.pid = imsg.hdr.pid; 304 305 if (imsg.hdr.len == IMSG_HEADER_SIZE + 306 sizeof(struct ctl_neighbor)) { 307 neighbor = imsg.data; 308 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 309 } else { 310 neighbor = NULL; 311 } 312 for (matched = 0, p = peers; p != NULL; p = p->next) { 313 if (!peer_matched(p, neighbor)) 314 continue; 315 316 matched = 1; 317 if (!neighbor || !neighbor->show_timers) { 318 imsg_ctl_rde(imsg.hdr.type, 319 imsg.hdr.pid, 320 p, sizeof(struct peer)); 321 } else { 322 u_int i; 323 time_t d; 324 struct ctl_timer ct; 325 326 imsg_compose(&c->ibuf, 327 IMSG_CTL_SHOW_NEIGHBOR, 328 0, 0, -1, p, sizeof(*p)); 329 for (i = 1; i < Timer_Max; i++) { 330 if (!timer_running(p, i, &d)) 331 continue; 332 ct.type = i; 333 ct.val = d; 334 imsg_compose(&c->ibuf, 335 IMSG_CTL_SHOW_TIMER, 336 0, 0, -1, &ct, sizeof(ct)); 337 } 338 } 339 } 340 if (!matched && peers != NULL) { 341 control_result(c, CTL_RES_NOSUCHPEER); 342 } else if (!neighbor || !neighbor->show_timers) { 343 imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid, 344 NULL, 0); 345 } else { 346 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, 347 NULL, 0); 348 } 349 break; 350 case IMSG_CTL_NEIGHBOR_UP: 351 case IMSG_CTL_NEIGHBOR_DOWN: 352 case IMSG_CTL_NEIGHBOR_CLEAR: 353 case IMSG_CTL_NEIGHBOR_RREFRESH: 354 case IMSG_CTL_NEIGHBOR_DESTROY: 355 if (imsg.hdr.len != IMSG_HEADER_SIZE + 356 sizeof(struct ctl_neighbor)) { 357 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 358 "wrong length"); 359 break; 360 } 361 362 neighbor = imsg.data; 363 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 364 365 for (matched = 0, p = peers; p != NULL; p = p->next) { 366 if (!peer_matched(p, neighbor)) 367 continue; 368 369 matched = 1; 370 371 switch (imsg.hdr.type) { 372 case IMSG_CTL_NEIGHBOR_UP: 373 bgp_fsm(p, EVNT_START); 374 p->conf.down = 0; 375 p->conf.shutcomm[0] = '\0'; 376 control_result(c, CTL_RES_OK); 377 break; 378 case IMSG_CTL_NEIGHBOR_DOWN: 379 p->conf.down = 1; 380 strlcpy(p->conf.shutcomm, 381 neighbor->shutcomm, 382 sizeof(neighbor->shutcomm)); 383 session_stop(p, ERR_CEASE_ADMIN_DOWN); 384 control_result(c, CTL_RES_OK); 385 break; 386 case IMSG_CTL_NEIGHBOR_CLEAR: 387 strlcpy(p->conf.shutcomm, 388 neighbor->shutcomm, 389 sizeof(neighbor->shutcomm)); 390 if (!p->conf.down) { 391 session_stop(p, 392 ERR_CEASE_ADMIN_RESET); 393 timer_set(p, Timer_IdleHold, 394 SESSION_CLEAR_DELAY); 395 } else { 396 session_stop(p, 397 ERR_CEASE_ADMIN_DOWN); 398 } 399 control_result(c, CTL_RES_OK); 400 break; 401 case IMSG_CTL_NEIGHBOR_RREFRESH: 402 if (session_neighbor_rrefresh(p)) 403 control_result(c, 404 CTL_RES_NOCAP); 405 else 406 control_result(c, CTL_RES_OK); 407 break; 408 case IMSG_CTL_NEIGHBOR_DESTROY: 409 if (!p->template) 410 control_result(c, 411 CTL_RES_BADPEER); 412 else if (p->state != STATE_IDLE) 413 control_result(c, 414 CTL_RES_BADSTATE); 415 else { 416 /* 417 * Mark as deleted, will be 418 * collected on next poll loop. 419 */ 420 p->conf.reconf_action = 421 RECONF_DELETE; 422 control_result(c, CTL_RES_OK); 423 } 424 break; 425 default: 426 fatal("king bula wants more humppa"); 427 } 428 } 429 if (!matched) 430 control_result(c, CTL_RES_NOSUCHPEER); 431 break; 432 case IMSG_CTL_RELOAD: 433 case IMSG_CTL_SHOW_INTERFACE: 434 case IMSG_CTL_SHOW_FIB_TABLES: 435 c->ibuf.pid = imsg.hdr.pid; 436 imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid, 437 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 438 break; 439 case IMSG_CTL_KROUTE: 440 case IMSG_CTL_KROUTE_ADDR: 441 case IMSG_CTL_SHOW_NEXTHOP: 442 c->ibuf.pid = imsg.hdr.pid; 443 imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid, 444 imsg.hdr.pid, imsg.data, imsg.hdr.len - 445 IMSG_HEADER_SIZE); 446 break; 447 case IMSG_CTL_SHOW_RIB: 448 case IMSG_CTL_SHOW_RIB_PREFIX: 449 if (imsg.hdr.len != IMSG_HEADER_SIZE + 450 sizeof(struct ctl_show_rib_request)) { 451 log_warnx("got IMSG_CTL_SHOW_RIB with " 452 "wrong length"); 453 break; 454 } 455 456 ribreq = imsg.data; 457 neighbor = &ribreq->neighbor; 458 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 459 460 /* check if at least one neighbor exists */ 461 for (p = peers; p != NULL; p = p->next) 462 if (peer_matched(p, neighbor)) 463 break; 464 if (p == NULL && peers != NULL) { 465 control_result(c, CTL_RES_NOSUCHPEER); 466 break; 467 } 468 469 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 470 && (ribreq->prefix.aid == AID_UNSPEC)) { 471 /* malformed request, must specify af */ 472 control_result(c, CTL_RES_PARSE_ERROR); 473 break; 474 } 475 476 c->ibuf.pid = imsg.hdr.pid; 477 c->terminate = 1; 478 479 imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, 480 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 481 break; 482 case IMSG_CTL_SHOW_NETWORK: 483 c->terminate = 1; 484 /* FALLTHROUGH */ 485 case IMSG_CTL_SHOW_RIB_MEM: 486 c->ibuf.pid = imsg.hdr.pid; 487 imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, 488 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 489 break; 490 case IMSG_NETWORK_ADD: 491 case IMSG_NETWORK_ASPATH: 492 case IMSG_NETWORK_ATTR: 493 case IMSG_NETWORK_REMOVE: 494 case IMSG_NETWORK_FLUSH: 495 case IMSG_NETWORK_DONE: 496 case IMSG_FILTER_SET: 497 imsg_ctl_rde(imsg.hdr.type, 0, 498 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 499 break; 500 case IMSG_CTL_LOG_VERBOSE: 501 if (imsg.hdr.len != IMSG_HEADER_SIZE + 502 sizeof(verbose)) 503 break; 504 505 /* forward to other processes */ 506 imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid, 507 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 508 imsg_ctl_rde(imsg.hdr.type, 0, 509 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 510 511 memcpy(&verbose, imsg.data, sizeof(verbose)); 512 log_setverbose(verbose); 513 break; 514 default: 515 break; 516 } 517 imsg_free(&imsg); 518 } 519 520 return (0); 521} 522 523int 524control_imsg_relay(struct imsg *imsg) 525{ 526 struct ctl_conn *c; 527 528 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 529 return (0); 530 531 /* if command finished no need to send exit message */ 532 if (imsg->hdr.type == IMSG_CTL_END || imsg->hdr.type == IMSG_CTL_RESULT) 533 c->terminate = 0; 534 535 if (!c->throttled && c->ibuf.w.queued > CTL_MSG_HIGH_MARK) { 536 if (imsg_ctl_rde(IMSG_XOFF, imsg->hdr.pid, NULL, 0) != -1) 537 c->throttled = 1; 538 } 539 540 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 541 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 542} 543 544void 545control_result(struct ctl_conn *c, u_int code) 546{ 547 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 548 &code, sizeof(code)); 549} 550 551/* This should go into libutil, from smtpd/mproc.c */ 552ssize_t 553imsg_read_nofd(struct imsgbuf *ibuf) 554{ 555 ssize_t n; 556 char *buf; 557 size_t len; 558 559 buf = ibuf->r.buf + ibuf->r.wpos; 560 len = sizeof(ibuf->r.buf) - ibuf->r.wpos; 561 562 while ((n = recv(ibuf->fd, buf, len, 0)) == -1) { 563 if (errno != EINTR) 564 return (n); 565 } 566 567 ibuf->r.wpos += n; 568 return (n); 569} 570