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