control.c revision 1.73
1/* $OpenBSD: control.c,v 1.73 2013/03/07 21:26:28 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 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); 37 38int 39control_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 90int 91control_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 101void 102control_shutdown(int fd) 103{ 104 close(fd); 105} 106 107void 108control_cleanup(const char *path) 109{ 110 if (path) 111 unlink(path); 112} 113 114unsigned int 115control_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 149struct ctl_conn * 150control_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 161struct ctl_conn * 162control_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 173int 174control_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 192int 193control_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 case IMSG_CTL_NEIGHBOR_DESTROY: 320 if (imsg.hdr.len == IMSG_HEADER_SIZE + 321 sizeof(struct ctl_neighbor)) { 322 neighbor = imsg.data; 323 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 324 p = getpeerbyaddr(&neighbor->addr); 325 if (p == NULL) 326 p = getpeerbydesc(neighbor->descr); 327 if (p == NULL) { 328 control_result(c, CTL_RES_NOSUCHPEER); 329 break; 330 } 331 switch (imsg.hdr.type) { 332 case IMSG_CTL_NEIGHBOR_UP: 333 bgp_fsm(p, EVNT_START); 334 control_result(c, CTL_RES_OK); 335 break; 336 case IMSG_CTL_NEIGHBOR_DOWN: 337 session_stop(p, ERR_CEASE_ADMIN_DOWN); 338 control_result(c, CTL_RES_OK); 339 break; 340 case IMSG_CTL_NEIGHBOR_CLEAR: 341 if (!p->conf.down) { 342 session_stop(p, 343 ERR_CEASE_ADMIN_RESET); 344 timer_set(p, Timer_IdleHold, 345 SESSION_CLEAR_DELAY); 346 } else { 347 session_stop(p, 348 ERR_CEASE_ADMIN_DOWN); 349 } 350 control_result(c, CTL_RES_OK); 351 break; 352 case IMSG_CTL_NEIGHBOR_RREFRESH: 353 if (session_neighbor_rrefresh(p)) 354 control_result(c, 355 CTL_RES_NOCAP); 356 else 357 control_result(c, CTL_RES_OK); 358 break; 359 case IMSG_CTL_NEIGHBOR_DESTROY: 360 if (!p->template) 361 control_result(c, 362 CTL_RES_BADPEER); 363 else if (p->state != STATE_IDLE) 364 control_result(c, 365 CTL_RES_BADSTATE); 366 else { 367 /* 368 * Mark as deleted, will be 369 * collected on next poll loop. 370 */ 371 p->conf.reconf_action = 372 RECONF_DELETE; 373 control_result(c, CTL_RES_OK); 374 } 375 break; 376 default: 377 fatal("king bula wants more humppa"); 378 } 379 } else 380 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 381 "wrong length"); 382 break; 383 case IMSG_CTL_RELOAD: 384 case IMSG_CTL_SHOW_INTERFACE: 385 case IMSG_CTL_SHOW_FIB_TABLES: 386 c->ibuf.pid = imsg.hdr.pid; 387 imsg_compose_parent(imsg.hdr.type, 0, imsg.hdr.pid, 388 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 389 break; 390 case IMSG_CTL_KROUTE: 391 case IMSG_CTL_KROUTE_ADDR: 392 case IMSG_CTL_SHOW_NEXTHOP: 393 c->ibuf.pid = imsg.hdr.pid; 394 imsg_compose_parent(imsg.hdr.type, imsg.hdr.peerid, 395 imsg.hdr.pid, imsg.data, imsg.hdr.len - 396 IMSG_HEADER_SIZE); 397 break; 398 case IMSG_CTL_SHOW_RIB: 399 case IMSG_CTL_SHOW_RIB_AS: 400 case IMSG_CTL_SHOW_RIB_PREFIX: 401 if (imsg.hdr.len == IMSG_HEADER_SIZE + 402 sizeof(struct ctl_show_rib_request)) { 403 ribreq = imsg.data; 404 neighbor = &ribreq->neighbor; 405 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 406 ribreq->peerid = 0; 407 p = NULL; 408 if (neighbor->addr.aid) { 409 p = getpeerbyaddr(&neighbor->addr); 410 if (p == NULL) { 411 control_result(c, 412 CTL_RES_NOSUCHPEER); 413 break; 414 } 415 ribreq->peerid = p->conf.id; 416 } else if (neighbor->descr[0]) { 417 p = getpeerbydesc(neighbor->descr); 418 if (p == NULL) { 419 control_result(c, 420 CTL_RES_NOSUCHPEER); 421 break; 422 } 423 ribreq->peerid = p->conf.id; 424 } 425 if ((ribreq->flags & F_CTL_ADJ_IN) && p && 426 !p->conf.softreconfig_in) { 427 /* 428 * if no neighbor was specified we 429 * try our best. 430 */ 431 control_result(c, CTL_RES_NOCAP); 432 break; 433 } 434 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 435 && (ribreq->prefix.aid == AID_UNSPEC)) { 436 /* malformed request, must specify af */ 437 control_result(c, CTL_RES_PARSE_ERROR); 438 break; 439 } 440 c->ibuf.pid = imsg.hdr.pid; 441 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 442 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 443 } else 444 log_warnx("got IMSG_CTL_SHOW_RIB with " 445 "wrong length"); 446 break; 447 case IMSG_CTL_SHOW_RIB_MEM: 448 case IMSG_CTL_SHOW_RIB_COMMUNITY: 449 case IMSG_CTL_SHOW_NETWORK: 450 c->ibuf.pid = imsg.hdr.pid; 451 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 452 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 453 break; 454 case IMSG_NETWORK_ADD: 455 case IMSG_NETWORK_ASPATH: 456 case IMSG_NETWORK_ATTR: 457 case IMSG_NETWORK_REMOVE: 458 case IMSG_NETWORK_FLUSH: 459 case IMSG_NETWORK_DONE: 460 case IMSG_FILTER_SET: 461 imsg_compose_rde(imsg.hdr.type, 0, 462 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 463 break; 464 case IMSG_CTL_LOG_VERBOSE: 465 if (imsg.hdr.len != IMSG_HEADER_SIZE + 466 sizeof(verbose)) 467 break; 468 469 /* forward to other processes */ 470 imsg_compose_parent(imsg.hdr.type, 0, imsg.hdr.pid, 471 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 472 imsg_compose_rde(imsg.hdr.type, 0, 473 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 474 475 memcpy(&verbose, imsg.data, sizeof(verbose)); 476 log_verbose(verbose); 477 break; 478 default: 479 break; 480 } 481 imsg_free(&imsg); 482 } 483 484 return (0); 485} 486 487int 488control_imsg_relay(struct imsg *imsg) 489{ 490 struct ctl_conn *c; 491 492 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 493 return (0); 494 495 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 496 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 497} 498 499void 500control_result(struct ctl_conn *c, u_int code) 501{ 502 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 503 &code, sizeof(code)); 504} 505