1 /* $OpenBSD: control.c,v 1.72 2012/05/27 18:52:07 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 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 log_warn("control_accept: accept"); 130 return (0); 131 } 132 133 session_socket_blockmode(connfd, BM_NONBLOCK); 134 135 if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) { 136 log_warn("control_accept"); 137 close(connfd); 138 return (0); 139 } 140 141 imsg_init(&ctl_conn->ibuf, connfd); 142 ctl_conn->restricted = restricted; 143 144 TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry); 145 146 return (1); 147 } 148 149 struct ctl_conn * 150 control_connbyfd(int fd) 151 { 152 struct ctl_conn *c; 153 154 for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd; 155 c = TAILQ_NEXT(c, entry)) 156 ; /* nothing */ 157 158 return (c); 159 } 160 161 struct ctl_conn * 162 control_connbypid(pid_t pid) 163 { 164 struct ctl_conn *c; 165 166 for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid; 167 c = TAILQ_NEXT(c, entry)) 168 ; /* nothing */ 169 170 return (c); 171 } 172 173 int 174 control_close(int fd) 175 { 176 struct ctl_conn *c; 177 178 if ((c = control_connbyfd(fd)) == NULL) { 179 log_warn("control_close: fd %d: not found", fd); 180 return (0); 181 } 182 183 msgbuf_clear(&c->ibuf.w); 184 TAILQ_REMOVE(&ctl_conns, c, entry); 185 186 close(c->ibuf.fd); 187 free(c); 188 pauseaccept = 0; 189 return (1); 190 } 191 192 int 193 control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt) 194 { 195 struct imsg imsg; 196 struct ctl_conn *c; 197 ssize_t n; 198 int verbose; 199 struct peer *p; 200 struct ctl_neighbor *neighbor; 201 struct ctl_show_rib_request *ribreq; 202 203 if ((c = control_connbyfd(pfd->fd)) == NULL) { 204 log_warn("control_dispatch_msg: fd %d: not found", pfd->fd); 205 return (0); 206 } 207 208 if (pfd->revents & POLLOUT) 209 if (msgbuf_write(&c->ibuf.w) < 0) { 210 *ctl_cnt -= control_close(pfd->fd); 211 return (1); 212 } 213 214 if (!(pfd->revents & POLLIN)) 215 return (0); 216 217 if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) { 218 *ctl_cnt -= control_close(pfd->fd); 219 return (1); 220 } 221 222 for (;;) { 223 if ((n = imsg_get(&c->ibuf, &imsg)) == -1) { 224 *ctl_cnt -= control_close(pfd->fd); 225 return (1); 226 } 227 228 if (n == 0) 229 break; 230 231 if (c->restricted) { 232 switch (imsg.hdr.type) { 233 case IMSG_CTL_SHOW_NEIGHBOR: 234 case IMSG_CTL_SHOW_NEXTHOP: 235 case IMSG_CTL_SHOW_INTERFACE: 236 case IMSG_CTL_SHOW_RIB: 237 case IMSG_CTL_SHOW_RIB_AS: 238 case IMSG_CTL_SHOW_RIB_PREFIX: 239 case IMSG_CTL_SHOW_RIB_MEM: 240 case IMSG_CTL_SHOW_RIB_COMMUNITY: 241 case IMSG_CTL_SHOW_NETWORK: 242 case IMSG_CTL_SHOW_TERSE: 243 case IMSG_CTL_SHOW_TIMER: 244 break; 245 default: 246 /* clear imsg type to prevent processing */ 247 imsg.hdr.type = IMSG_NONE; 248 control_result(c, CTL_RES_DENIED); 249 break; 250 } 251 } 252 253 switch (imsg.hdr.type) { 254 case IMSG_NONE: 255 /* message was filtered out, nothing to do */ 256 break; 257 case IMSG_CTL_SHOW_NEIGHBOR: 258 c->ibuf.pid = imsg.hdr.pid; 259 if (imsg.hdr.len == IMSG_HEADER_SIZE + 260 sizeof(struct ctl_neighbor)) { 261 neighbor = imsg.data; 262 p = getpeerbyaddr(&neighbor->addr); 263 if (p == NULL) 264 p = getpeerbydesc(neighbor->descr); 265 if (p == NULL) { 266 control_result(c, CTL_RES_NOSUCHPEER); 267 break; 268 } 269 if (!neighbor->show_timers) { 270 imsg_compose_rde(imsg.hdr.type, 271 imsg.hdr.pid, 272 p, sizeof(struct peer)); 273 imsg_compose_rde(IMSG_CTL_END, 274 imsg.hdr.pid, NULL, 0); 275 } else { 276 u_int i; 277 time_t d; 278 struct ctl_timer ct; 279 280 imsg_compose(&c->ibuf, 281 IMSG_CTL_SHOW_NEIGHBOR, 282 0, 0, -1, p, sizeof(*p)); 283 for (i = 1; i < Timer_Max; i++) { 284 if (!timer_running(p, i, &d)) 285 continue; 286 ct.type = i; 287 ct.val = d; 288 imsg_compose(&c->ibuf, 289 IMSG_CTL_SHOW_TIMER, 290 0, 0, -1, &ct, sizeof(ct)); 291 } 292 imsg_compose(&c->ibuf, IMSG_CTL_END, 293 0, 0, -1, NULL, 0); 294 } 295 } else { 296 for (p = peers; p != NULL; p = p->next) 297 imsg_compose_rde(imsg.hdr.type, 298 imsg.hdr.pid, 299 p, sizeof(struct peer)); 300 imsg_compose_rde(IMSG_CTL_END, imsg.hdr.pid, 301 NULL, 0); 302 } 303 break; 304 case IMSG_CTL_SHOW_TERSE: 305 for (p = peers; p != NULL; p = p->next) 306 imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR, 307 0, 0, -1, p, sizeof(struct peer)); 308 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 309 break; 310 case IMSG_CTL_FIB_COUPLE: 311 case IMSG_CTL_FIB_DECOUPLE: 312 imsg_compose_parent(imsg.hdr.type, imsg.hdr.peerid, 313 0, NULL, 0); 314 break; 315 case IMSG_CTL_NEIGHBOR_UP: 316 case IMSG_CTL_NEIGHBOR_DOWN: 317 case IMSG_CTL_NEIGHBOR_CLEAR: 318 case IMSG_CTL_NEIGHBOR_RREFRESH: 319 if (imsg.hdr.len == IMSG_HEADER_SIZE + 320 sizeof(struct ctl_neighbor)) { 321 neighbor = imsg.data; 322 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 323 p = getpeerbyaddr(&neighbor->addr); 324 if (p == NULL) 325 p = getpeerbydesc(neighbor->descr); 326 if (p == NULL) { 327 control_result(c, CTL_RES_NOSUCHPEER); 328 break; 329 } 330 switch (imsg.hdr.type) { 331 case IMSG_CTL_NEIGHBOR_UP: 332 bgp_fsm(p, EVNT_START); 333 control_result(c, CTL_RES_OK); 334 break; 335 case IMSG_CTL_NEIGHBOR_DOWN: 336 session_stop(p, ERR_CEASE_ADMIN_DOWN); 337 control_result(c, CTL_RES_OK); 338 break; 339 case IMSG_CTL_NEIGHBOR_CLEAR: 340 if (!p->conf.down) { 341 session_stop(p, 342 ERR_CEASE_ADMIN_RESET); 343 timer_set(p, Timer_IdleHold, 344 SESSION_CLEAR_DELAY); 345 } else { 346 session_stop(p, 347 ERR_CEASE_ADMIN_DOWN); 348 } 349 control_result(c, CTL_RES_OK); 350 break; 351 case IMSG_CTL_NEIGHBOR_RREFRESH: 352 if (session_neighbor_rrefresh(p)) 353 control_result(c, 354 CTL_RES_NOCAP); 355 else 356 control_result(c, CTL_RES_OK); 357 break; 358 default: 359 fatal("king bula wants more humppa"); 360 } 361 } else 362 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 363 "wrong length"); 364 break; 365 case IMSG_CTL_RELOAD: 366 case IMSG_CTL_SHOW_INTERFACE: 367 case IMSG_CTL_SHOW_FIB_TABLES: 368 c->ibuf.pid = imsg.hdr.pid; 369 imsg_compose_parent(imsg.hdr.type, 0, imsg.hdr.pid, 370 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 371 break; 372 case IMSG_CTL_KROUTE: 373 case IMSG_CTL_KROUTE_ADDR: 374 case IMSG_CTL_SHOW_NEXTHOP: 375 c->ibuf.pid = imsg.hdr.pid; 376 imsg_compose_parent(imsg.hdr.type, imsg.hdr.peerid, 377 imsg.hdr.pid, imsg.data, imsg.hdr.len - 378 IMSG_HEADER_SIZE); 379 break; 380 case IMSG_CTL_SHOW_RIB: 381 case IMSG_CTL_SHOW_RIB_AS: 382 case IMSG_CTL_SHOW_RIB_PREFIX: 383 if (imsg.hdr.len == IMSG_HEADER_SIZE + 384 sizeof(struct ctl_show_rib_request)) { 385 ribreq = imsg.data; 386 neighbor = &ribreq->neighbor; 387 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 388 ribreq->peerid = 0; 389 p = NULL; 390 if (neighbor->addr.aid) { 391 p = getpeerbyaddr(&neighbor->addr); 392 if (p == NULL) { 393 control_result(c, 394 CTL_RES_NOSUCHPEER); 395 break; 396 } 397 ribreq->peerid = p->conf.id; 398 } else if (neighbor->descr[0]) { 399 p = getpeerbydesc(neighbor->descr); 400 if (p == NULL) { 401 control_result(c, 402 CTL_RES_NOSUCHPEER); 403 break; 404 } 405 ribreq->peerid = p->conf.id; 406 } 407 if ((ribreq->flags & F_CTL_ADJ_IN) && p && 408 !p->conf.softreconfig_in) { 409 /* 410 * if no neighbor was specified we 411 * try our best. 412 */ 413 control_result(c, CTL_RES_NOCAP); 414 break; 415 } 416 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 417 && (ribreq->prefix.aid == AID_UNSPEC)) { 418 /* malformed request, must specify af */ 419 control_result(c, CTL_RES_PARSE_ERROR); 420 break; 421 } 422 c->ibuf.pid = imsg.hdr.pid; 423 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 424 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 425 } else 426 log_warnx("got IMSG_CTL_SHOW_RIB with " 427 "wrong length"); 428 break; 429 case IMSG_CTL_SHOW_RIB_MEM: 430 case IMSG_CTL_SHOW_RIB_COMMUNITY: 431 case IMSG_CTL_SHOW_NETWORK: 432 c->ibuf.pid = imsg.hdr.pid; 433 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 434 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 435 break; 436 case IMSG_NETWORK_ADD: 437 case IMSG_NETWORK_ASPATH: 438 case IMSG_NETWORK_ATTR: 439 case IMSG_NETWORK_REMOVE: 440 case IMSG_NETWORK_FLUSH: 441 case IMSG_NETWORK_DONE: 442 case IMSG_FILTER_SET: 443 imsg_compose_rde(imsg.hdr.type, 0, 444 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 445 break; 446 case IMSG_CTL_LOG_VERBOSE: 447 if (imsg.hdr.len != IMSG_HEADER_SIZE + 448 sizeof(verbose)) 449 break; 450 451 /* forward to other processes */ 452 imsg_compose_parent(imsg.hdr.type, 0, imsg.hdr.pid, 453 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 454 imsg_compose_rde(imsg.hdr.type, 0, 455 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 456 457 memcpy(&verbose, imsg.data, sizeof(verbose)); 458 log_verbose(verbose); 459 break; 460 default: 461 break; 462 } 463 imsg_free(&imsg); 464 } 465 466 return (0); 467 } 468 469 int 470 control_imsg_relay(struct imsg *imsg) 471 { 472 struct ctl_conn *c; 473 474 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 475 return (0); 476 477 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 478 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 479 } 480 481 void 482 control_result(struct ctl_conn *c, u_int code) 483 { 484 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 485 &code, sizeof(code)); 486 } 487