control.c revision 1.66
1/* $OpenBSD: control.c,v 1.66 2009/12/03 19:22:53 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 != 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 146struct ctl_conn * 147control_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 158struct ctl_conn * 159control_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 170int 171control_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 189int 190control_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 if (!p->conf.down) { 337 session_stop(p, 338 ERR_CEASE_ADMIN_RESET); 339 timer_set(p, Timer_IdleHold, 340 SESSION_CLEAR_DELAY); 341 } else { 342 session_stop(p, 343 ERR_CEASE_ADMIN_DOWN); 344 } 345 control_result(c, CTL_RES_OK); 346 break; 347 case IMSG_CTL_NEIGHBOR_RREFRESH: 348 if (session_neighbor_rrefresh(p)) 349 control_result(c, 350 CTL_RES_NOCAP); 351 else 352 control_result(c, CTL_RES_OK); 353 break; 354 default: 355 fatal("king bula wants more humppa"); 356 } 357 } else 358 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 359 "wrong length"); 360 break; 361 case IMSG_CTL_RELOAD: 362 case IMSG_CTL_KROUTE: 363 case IMSG_CTL_KROUTE_ADDR: 364 case IMSG_CTL_SHOW_NEXTHOP: 365 case IMSG_CTL_SHOW_INTERFACE: 366 c->ibuf.pid = imsg.hdr.pid; 367 imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, 368 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 369 break; 370 case IMSG_CTL_SHOW_RIB: 371 case IMSG_CTL_SHOW_RIB_AS: 372 case IMSG_CTL_SHOW_RIB_PREFIX: 373 if (imsg.hdr.len == IMSG_HEADER_SIZE + 374 sizeof(struct ctl_show_rib_request)) { 375 ribreq = imsg.data; 376 neighbor = &ribreq->neighbor; 377 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 378 ribreq->peerid = 0; 379 p = NULL; 380 if (neighbor->addr.aid) { 381 p = getpeerbyaddr(&neighbor->addr); 382 if (p == NULL) { 383 control_result(c, 384 CTL_RES_NOSUCHPEER); 385 break; 386 } 387 ribreq->peerid = p->conf.id; 388 } else if (neighbor->descr[0]) { 389 p = getpeerbydesc(neighbor->descr); 390 if (p == NULL) { 391 control_result(c, 392 CTL_RES_NOSUCHPEER); 393 break; 394 } 395 ribreq->peerid = p->conf.id; 396 } 397 if ((ribreq->flags & F_CTL_ADJ_IN) && p && 398 !p->conf.softreconfig_in) { 399 /* 400 * if no neighbor was specified we 401 * try our best. 402 */ 403 control_result(c, CTL_RES_NOCAP); 404 break; 405 } 406 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 407 && (ribreq->prefix.aid != AID_INET) 408 && (ribreq->prefix.aid != AID_INET6)) { 409 /* malformed request, must specify af */ 410 control_result(c, CTL_RES_PARSE_ERROR); 411 break; 412 } 413 c->ibuf.pid = imsg.hdr.pid; 414 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 415 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 416 } else 417 log_warnx("got IMSG_CTL_SHOW_RIB with " 418 "wrong length"); 419 break; 420 case IMSG_CTL_SHOW_RIB_MEM: 421 case IMSG_CTL_SHOW_RIB_COMMUNITY: 422 case IMSG_CTL_SHOW_NETWORK: 423 c->ibuf.pid = imsg.hdr.pid; 424 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 425 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 426 break; 427 case IMSG_NETWORK_ADD: 428 case IMSG_NETWORK_REMOVE: 429 case IMSG_NETWORK_FLUSH: 430 case IMSG_NETWORK_DONE: 431 case IMSG_FILTER_SET: 432 imsg_compose_rde(imsg.hdr.type, 0, 433 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 434 break; 435 case IMSG_CTL_LOG_VERBOSE: 436 if (imsg.hdr.len != IMSG_HEADER_SIZE + 437 sizeof(verbose)) 438 break; 439 440 /* forward to other porcesses */ 441 imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, 442 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 443 imsg_compose_rde(imsg.hdr.type, 0, 444 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 445 446 memcpy(&verbose, imsg.data, sizeof(verbose)); 447 log_verbose(verbose); 448 break; 449 default: 450 break; 451 } 452 imsg_free(&imsg); 453 } 454 455 return (0); 456} 457 458int 459control_imsg_relay(struct imsg *imsg) 460{ 461 struct ctl_conn *c; 462 463 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 464 return (0); 465 466 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 467 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 468} 469 470void 471control_result(struct ctl_conn *c, u_int code) 472{ 473 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 474 &code, sizeof(code)); 475} 476