control.c revision 1.83
1/* $OpenBSD: control.c,v 1.83 2016/10/14 16:05:35 phessler 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_RIB_LARGECOMMUNITY: 248 case IMSG_CTL_SHOW_NETWORK: 249 case IMSG_CTL_SHOW_TERSE: 250 case IMSG_CTL_SHOW_TIMER: 251 break; 252 default: 253 /* clear imsg type to prevent processing */ 254 imsg.hdr.type = IMSG_NONE; 255 control_result(c, CTL_RES_DENIED); 256 break; 257 } 258 } 259 260 switch (imsg.hdr.type) { 261 case IMSG_NONE: 262 /* message was filtered out, nothing to do */ 263 break; 264 case IMSG_CTL_SHOW_NEIGHBOR: 265 c->ibuf.pid = imsg.hdr.pid; 266 if (imsg.hdr.len == IMSG_HEADER_SIZE + 267 sizeof(struct ctl_neighbor)) { 268 neighbor = imsg.data; 269 p = getpeerbyaddr(&neighbor->addr); 270 if (p == NULL) 271 p = getpeerbydesc(neighbor->descr); 272 if (p == NULL) { 273 control_result(c, CTL_RES_NOSUCHPEER); 274 break; 275 } 276 if (!neighbor->show_timers) { 277 imsg_ctl_rde(imsg.hdr.type, 278 imsg.hdr.pid, 279 p, sizeof(struct peer)); 280 imsg_ctl_rde(IMSG_CTL_END, 281 imsg.hdr.pid, NULL, 0); 282 } else { 283 u_int i; 284 time_t d; 285 struct ctl_timer ct; 286 287 imsg_compose(&c->ibuf, 288 IMSG_CTL_SHOW_NEIGHBOR, 289 0, 0, -1, p, sizeof(*p)); 290 for (i = 1; i < Timer_Max; i++) { 291 if (!timer_running(p, i, &d)) 292 continue; 293 ct.type = i; 294 ct.val = d; 295 imsg_compose(&c->ibuf, 296 IMSG_CTL_SHOW_TIMER, 297 0, 0, -1, &ct, sizeof(ct)); 298 } 299 imsg_compose(&c->ibuf, IMSG_CTL_END, 300 0, 0, -1, NULL, 0); 301 } 302 } else { 303 for (p = peers; p != NULL; p = p->next) 304 imsg_ctl_rde(imsg.hdr.type, 305 imsg.hdr.pid, 306 p, sizeof(struct peer)); 307 imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid, 308 NULL, 0); 309 } 310 break; 311 case IMSG_CTL_SHOW_TERSE: 312 for (p = peers; p != NULL; p = p->next) 313 imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR, 314 0, 0, -1, p, sizeof(struct peer)); 315 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 316 break; 317 case IMSG_CTL_FIB_COUPLE: 318 case IMSG_CTL_FIB_DECOUPLE: 319 imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid, 320 0, NULL, 0); 321 break; 322 case IMSG_CTL_NEIGHBOR_UP: 323 case IMSG_CTL_NEIGHBOR_DOWN: 324 case IMSG_CTL_NEIGHBOR_CLEAR: 325 case IMSG_CTL_NEIGHBOR_RREFRESH: 326 case IMSG_CTL_NEIGHBOR_DESTROY: 327 if (imsg.hdr.len == IMSG_HEADER_SIZE + 328 sizeof(struct ctl_neighbor)) { 329 neighbor = imsg.data; 330 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 331 p = getpeerbyaddr(&neighbor->addr); 332 if (p == NULL) 333 p = getpeerbydesc(neighbor->descr); 334 if (p == NULL) { 335 control_result(c, CTL_RES_NOSUCHPEER); 336 break; 337 } 338 switch (imsg.hdr.type) { 339 case IMSG_CTL_NEIGHBOR_UP: 340 bgp_fsm(p, EVNT_START); 341 control_result(c, CTL_RES_OK); 342 break; 343 case IMSG_CTL_NEIGHBOR_DOWN: 344 session_stop(p, ERR_CEASE_ADMIN_DOWN); 345 control_result(c, CTL_RES_OK); 346 break; 347 case IMSG_CTL_NEIGHBOR_CLEAR: 348 if (!p->conf.down) { 349 session_stop(p, 350 ERR_CEASE_ADMIN_RESET); 351 timer_set(p, Timer_IdleHold, 352 SESSION_CLEAR_DELAY); 353 } else { 354 session_stop(p, 355 ERR_CEASE_ADMIN_DOWN); 356 } 357 control_result(c, CTL_RES_OK); 358 break; 359 case IMSG_CTL_NEIGHBOR_RREFRESH: 360 if (session_neighbor_rrefresh(p)) 361 control_result(c, 362 CTL_RES_NOCAP); 363 else 364 control_result(c, CTL_RES_OK); 365 break; 366 case IMSG_CTL_NEIGHBOR_DESTROY: 367 if (!p->template) 368 control_result(c, 369 CTL_RES_BADPEER); 370 else if (p->state != STATE_IDLE) 371 control_result(c, 372 CTL_RES_BADSTATE); 373 else { 374 /* 375 * Mark as deleted, will be 376 * collected on next poll loop. 377 */ 378 p->conf.reconf_action = 379 RECONF_DELETE; 380 control_result(c, CTL_RES_OK); 381 } 382 break; 383 default: 384 fatal("king bula wants more humppa"); 385 } 386 } else 387 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 388 "wrong length"); 389 break; 390 case IMSG_CTL_RELOAD: 391 case IMSG_CTL_SHOW_INTERFACE: 392 case IMSG_CTL_SHOW_FIB_TABLES: 393 c->ibuf.pid = imsg.hdr.pid; 394 imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid, 395 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 396 break; 397 case IMSG_CTL_KROUTE: 398 case IMSG_CTL_KROUTE_ADDR: 399 case IMSG_CTL_SHOW_NEXTHOP: 400 c->ibuf.pid = imsg.hdr.pid; 401 imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid, 402 imsg.hdr.pid, imsg.data, imsg.hdr.len - 403 IMSG_HEADER_SIZE); 404 break; 405 case IMSG_CTL_SHOW_RIB: 406 case IMSG_CTL_SHOW_RIB_AS: 407 case IMSG_CTL_SHOW_RIB_PREFIX: 408 if (imsg.hdr.len == IMSG_HEADER_SIZE + 409 sizeof(struct ctl_show_rib_request)) { 410 ribreq = imsg.data; 411 neighbor = &ribreq->neighbor; 412 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 413 ribreq->peerid = 0; 414 p = NULL; 415 if (neighbor->addr.aid) { 416 p = getpeerbyaddr(&neighbor->addr); 417 if (p == NULL) { 418 control_result(c, 419 CTL_RES_NOSUCHPEER); 420 break; 421 } 422 ribreq->peerid = p->conf.id; 423 } else if (neighbor->descr[0]) { 424 p = getpeerbydesc(neighbor->descr); 425 if (p == NULL) { 426 control_result(c, 427 CTL_RES_NOSUCHPEER); 428 break; 429 } 430 ribreq->peerid = p->conf.id; 431 } 432 if ((ribreq->flags & 433 (F_CTL_ADJ_OUT | F_CTL_ADJ_IN)) && !p) { 434 /* 435 * both in and out tables are only 436 * meaningful if used on a single 437 * peer. 438 */ 439 control_result(c, CTL_RES_NOSUCHPEER); 440 break; 441 } 442 if ((ribreq->flags & F_CTL_ADJ_IN) && p && 443 !p->conf.softreconfig_in) { 444 /* 445 * without softreconfig_in we do not 446 * have an Adj-RIB-In table 447 */ 448 control_result(c, CTL_RES_NOCAP); 449 break; 450 } 451 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 452 && (ribreq->prefix.aid == AID_UNSPEC)) { 453 /* malformed request, must specify af */ 454 control_result(c, CTL_RES_PARSE_ERROR); 455 break; 456 } 457 c->ibuf.pid = imsg.hdr.pid; 458 imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, 459 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 460 } else 461 log_warnx("got IMSG_CTL_SHOW_RIB with " 462 "wrong length"); 463 break; 464 case IMSG_CTL_SHOW_RIB_MEM: 465 case IMSG_CTL_SHOW_RIB_COMMUNITY: 466 case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: 467 case IMSG_CTL_SHOW_NETWORK: 468 c->ibuf.pid = imsg.hdr.pid; 469 imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, 470 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 471 break; 472 case IMSG_NETWORK_ADD: 473 case IMSG_NETWORK_ASPATH: 474 case IMSG_NETWORK_ATTR: 475 case IMSG_NETWORK_REMOVE: 476 case IMSG_NETWORK_FLUSH: 477 case IMSG_NETWORK_DONE: 478 case IMSG_FILTER_SET: 479 imsg_ctl_rde(imsg.hdr.type, 0, 480 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 481 break; 482 case IMSG_CTL_LOG_VERBOSE: 483 if (imsg.hdr.len != IMSG_HEADER_SIZE + 484 sizeof(verbose)) 485 break; 486 487 /* forward to other processes */ 488 imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid, 489 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 490 imsg_ctl_rde(imsg.hdr.type, 0, 491 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 492 493 memcpy(&verbose, imsg.data, sizeof(verbose)); 494 log_verbose(verbose); 495 break; 496 default: 497 break; 498 } 499 imsg_free(&imsg); 500 } 501 502 return (0); 503} 504 505int 506control_imsg_relay(struct imsg *imsg) 507{ 508 struct ctl_conn *c; 509 510 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 511 return (0); 512 513 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 514 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 515} 516 517void 518control_result(struct ctl_conn *c, u_int code) 519{ 520 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 521 &code, sizeof(code)); 522} 523 524/* This should go into libutil, from smtpd/mproc.c */ 525ssize_t 526imsg_read_nofd(struct imsgbuf *ibuf) 527{ 528 ssize_t n; 529 char *buf; 530 size_t len; 531 532 buf = ibuf->r.buf + ibuf->r.wpos; 533 len = sizeof(ibuf->r.buf) - ibuf->r.wpos; 534 535 while ((n = recv(ibuf->fd, buf, len, 0)) == -1) { 536 if (errno != EINTR) 537 return (n); 538 } 539 540 ibuf->r.wpos += n; 541 return (n); 542} 543