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