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