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