1 /* $OpenBSD: control.c,v 1.63 2009/11/02 20:38:15 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("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("session_control_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("session_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, 0, NULL, 0); 310 break; 311 case IMSG_CTL_NEIGHBOR_UP: 312 case IMSG_CTL_NEIGHBOR_DOWN: 313 case IMSG_CTL_NEIGHBOR_CLEAR: 314 case IMSG_CTL_NEIGHBOR_RREFRESH: 315 if (imsg.hdr.len == IMSG_HEADER_SIZE + 316 sizeof(struct ctl_neighbor)) { 317 neighbor = imsg.data; 318 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 319 p = getpeerbyaddr(&neighbor->addr); 320 if (p == NULL) 321 p = getpeerbydesc(neighbor->descr); 322 if (p == NULL) { 323 control_result(c, CTL_RES_NOSUCHPEER); 324 break; 325 } 326 switch (imsg.hdr.type) { 327 case IMSG_CTL_NEIGHBOR_UP: 328 bgp_fsm(p, EVNT_START); 329 control_result(c, CTL_RES_OK); 330 break; 331 case IMSG_CTL_NEIGHBOR_DOWN: 332 session_stop(p, ERR_CEASE_ADMIN_DOWN); 333 control_result(c, CTL_RES_OK); 334 break; 335 case IMSG_CTL_NEIGHBOR_CLEAR: 336 session_stop(p, ERR_CEASE_ADMIN_RESET); 337 timer_set(p, Timer_IdleHold, 338 SESSION_CLEAR_DELAY); 339 control_result(c, CTL_RES_OK); 340 break; 341 case IMSG_CTL_NEIGHBOR_RREFRESH: 342 if (session_neighbor_rrefresh(p)) 343 control_result(c, 344 CTL_RES_NOCAP); 345 else 346 control_result(c, CTL_RES_OK); 347 break; 348 default: 349 fatal("king bula wants more humppa"); 350 } 351 } else 352 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 353 "wrong length"); 354 break; 355 case IMSG_CTL_RELOAD: 356 case IMSG_CTL_KROUTE: 357 case IMSG_CTL_KROUTE_ADDR: 358 case IMSG_CTL_SHOW_NEXTHOP: 359 case IMSG_CTL_SHOW_INTERFACE: 360 c->ibuf.pid = imsg.hdr.pid; 361 imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, 362 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 363 break; 364 case IMSG_CTL_SHOW_RIB: 365 case IMSG_CTL_SHOW_RIB_AS: 366 case IMSG_CTL_SHOW_RIB_PREFIX: 367 if (imsg.hdr.len == IMSG_HEADER_SIZE + 368 sizeof(struct ctl_show_rib_request)) { 369 ribreq = imsg.data; 370 neighbor = &ribreq->neighbor; 371 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 372 ribreq->peerid = 0; 373 p = NULL; 374 if (neighbor->addr.af) { 375 p = getpeerbyaddr(&neighbor->addr); 376 if (p == NULL) { 377 control_result(c, 378 CTL_RES_NOSUCHPEER); 379 break; 380 } 381 ribreq->peerid = p->conf.id; 382 } else if (neighbor->descr[0]) { 383 p = getpeerbydesc(neighbor->descr); 384 if (p == NULL) { 385 control_result(c, 386 CTL_RES_NOSUCHPEER); 387 break; 388 } 389 ribreq->peerid = p->conf.id; 390 } 391 if ((ribreq->flags & F_CTL_ADJ_IN) && p && 392 !p->conf.softreconfig_in) { 393 /* 394 * if no neighbor was specified we 395 * try our best. 396 */ 397 control_result(c, CTL_RES_NOCAP); 398 break; 399 } 400 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 401 && (ribreq->prefix.af != AF_INET) 402 && (ribreq->prefix.af != AF_INET6)) { 403 /* malformed request, must specify af */ 404 control_result(c, CTL_RES_PARSE_ERROR); 405 break; 406 } 407 c->ibuf.pid = imsg.hdr.pid; 408 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 409 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 410 } else 411 log_warnx("got IMSG_CTL_SHOW_RIB with " 412 "wrong length"); 413 break; 414 case IMSG_CTL_SHOW_RIB_MEM: 415 case IMSG_CTL_SHOW_RIB_COMMUNITY: 416 case IMSG_CTL_SHOW_NETWORK: 417 c->ibuf.pid = imsg.hdr.pid; 418 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 419 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 420 break; 421 case IMSG_NETWORK_ADD: 422 case IMSG_NETWORK_REMOVE: 423 case IMSG_NETWORK_FLUSH: 424 case IMSG_NETWORK_DONE: 425 case IMSG_FILTER_SET: 426 imsg_compose_rde(imsg.hdr.type, 0, 427 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 428 break; 429 case IMSG_CTL_LOG_VERBOSE: 430 if (imsg.hdr.len != IMSG_HEADER_SIZE + 431 sizeof(verbose)) 432 break; 433 434 /* forward to other porcesses */ 435 imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, 436 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 437 imsg_compose_rde(imsg.hdr.type, 0, 438 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 439 440 memcpy(&verbose, imsg.data, sizeof(verbose)); 441 log_verbose(verbose); 442 break; 443 default: 444 break; 445 } 446 imsg_free(&imsg); 447 } 448 449 return (0); 450 } 451 452 int 453 control_imsg_relay(struct imsg *imsg) 454 { 455 struct ctl_conn *c; 456 457 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 458 return (0); 459 460 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 461 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 462 } 463 464 void 465 control_result(struct ctl_conn *c, u_int code) 466 { 467 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 468 &code, sizeof(code)); 469 } 470