1 /* $OpenBSD: control.c,v 1.62 2009/09/02 08:06:42 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 int n; 195 struct peer *p; 196 struct ctl_neighbor *neighbor; 197 struct ctl_show_rib_request *ribreq; 198 199 if ((c = control_connbyfd(pfd->fd)) == NULL) { 200 log_warn("control_dispatch_msg: fd %d: not found", pfd->fd); 201 return (0); 202 } 203 204 if (pfd->revents & POLLOUT) 205 if (msgbuf_write(&c->ibuf.w) < 0) { 206 *ctl_cnt -= control_close(pfd->fd); 207 return (1); 208 } 209 210 if (!(pfd->revents & POLLIN)) 211 return (0); 212 213 if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) { 214 *ctl_cnt -= control_close(pfd->fd); 215 return (1); 216 } 217 218 for (;;) { 219 if ((n = imsg_get(&c->ibuf, &imsg)) == -1) { 220 *ctl_cnt -= control_close(pfd->fd); 221 return (1); 222 } 223 224 if (n == 0) 225 break; 226 227 if (c->restricted) { 228 switch (imsg.hdr.type) { 229 case IMSG_CTL_SHOW_NEIGHBOR: 230 case IMSG_CTL_SHOW_NEXTHOP: 231 case IMSG_CTL_SHOW_INTERFACE: 232 case IMSG_CTL_SHOW_RIB: 233 case IMSG_CTL_SHOW_RIB_AS: 234 case IMSG_CTL_SHOW_RIB_PREFIX: 235 case IMSG_CTL_SHOW_RIB_MEM: 236 case IMSG_CTL_SHOW_RIB_COMMUNITY: 237 case IMSG_CTL_SHOW_NETWORK: 238 case IMSG_CTL_SHOW_TERSE: 239 case IMSG_CTL_SHOW_TIMER: 240 break; 241 default: 242 /* clear imsg type to prevent processing */ 243 imsg.hdr.type = IMSG_NONE; 244 control_result(c, CTL_RES_DENIED); 245 break; 246 } 247 } 248 249 switch (imsg.hdr.type) { 250 case IMSG_NONE: 251 /* message was filtered out, nothing to do */ 252 break; 253 case IMSG_CTL_SHOW_NEIGHBOR: 254 c->ibuf.pid = imsg.hdr.pid; 255 if (imsg.hdr.len == IMSG_HEADER_SIZE + 256 sizeof(struct ctl_neighbor)) { 257 neighbor = imsg.data; 258 p = getpeerbyaddr(&neighbor->addr); 259 if (p == NULL) 260 p = getpeerbydesc(neighbor->descr); 261 if (p == NULL) { 262 control_result(c, CTL_RES_NOSUCHPEER); 263 break; 264 } 265 if (!neighbor->show_timers) { 266 imsg_compose_rde(imsg.hdr.type, 267 imsg.hdr.pid, 268 p, sizeof(struct peer)); 269 imsg_compose_rde(IMSG_CTL_END, 270 imsg.hdr.pid, NULL, 0); 271 } else { 272 u_int i; 273 time_t d; 274 struct ctl_timer ct; 275 276 imsg_compose(&c->ibuf, 277 IMSG_CTL_SHOW_NEIGHBOR, 278 0, 0, -1, p, sizeof(*p)); 279 for (i = 1; i < Timer_Max; i++) { 280 if (!timer_running(p, i, &d)) 281 continue; 282 ct.type = i; 283 ct.val = d; 284 imsg_compose(&c->ibuf, 285 IMSG_CTL_SHOW_TIMER, 286 0, 0, -1, &ct, sizeof(ct)); 287 } 288 imsg_compose(&c->ibuf, IMSG_CTL_END, 289 0, 0, -1, NULL, 0); 290 } 291 } else { 292 for (p = peers; p != NULL; p = p->next) 293 imsg_compose_rde(imsg.hdr.type, 294 imsg.hdr.pid, 295 p, sizeof(struct peer)); 296 imsg_compose_rde(IMSG_CTL_END, imsg.hdr.pid, 297 NULL, 0); 298 } 299 break; 300 case IMSG_CTL_SHOW_TERSE: 301 for (p = peers; p != NULL; p = p->next) 302 imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR, 303 0, 0, -1, p, sizeof(struct peer)); 304 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 305 break; 306 case IMSG_CTL_FIB_COUPLE: 307 case IMSG_CTL_FIB_DECOUPLE: 308 imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); 309 break; 310 case IMSG_CTL_NEIGHBOR_UP: 311 case IMSG_CTL_NEIGHBOR_DOWN: 312 case IMSG_CTL_NEIGHBOR_CLEAR: 313 case IMSG_CTL_NEIGHBOR_RREFRESH: 314 if (imsg.hdr.len == IMSG_HEADER_SIZE + 315 sizeof(struct ctl_neighbor)) { 316 neighbor = imsg.data; 317 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 318 p = getpeerbyaddr(&neighbor->addr); 319 if (p == NULL) 320 p = getpeerbydesc(neighbor->descr); 321 if (p == NULL) { 322 control_result(c, CTL_RES_NOSUCHPEER); 323 break; 324 } 325 switch (imsg.hdr.type) { 326 case IMSG_CTL_NEIGHBOR_UP: 327 bgp_fsm(p, EVNT_START); 328 control_result(c, CTL_RES_OK); 329 break; 330 case IMSG_CTL_NEIGHBOR_DOWN: 331 session_stop(p, ERR_CEASE_ADMIN_DOWN); 332 control_result(c, CTL_RES_OK); 333 break; 334 case IMSG_CTL_NEIGHBOR_CLEAR: 335 session_stop(p, ERR_CEASE_ADMIN_RESET); 336 timer_set(p, Timer_IdleHold, 337 SESSION_CLEAR_DELAY); 338 control_result(c, CTL_RES_OK); 339 break; 340 case IMSG_CTL_NEIGHBOR_RREFRESH: 341 if (session_neighbor_rrefresh(p)) 342 control_result(c, 343 CTL_RES_NOCAP); 344 else 345 control_result(c, CTL_RES_OK); 346 break; 347 default: 348 fatal("king bula wants more humppa"); 349 } 350 } else 351 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 352 "wrong length"); 353 break; 354 case IMSG_CTL_RELOAD: 355 case IMSG_CTL_KROUTE: 356 case IMSG_CTL_KROUTE_ADDR: 357 case IMSG_CTL_SHOW_NEXTHOP: 358 case IMSG_CTL_SHOW_INTERFACE: 359 c->ibuf.pid = imsg.hdr.pid; 360 imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, 361 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 362 break; 363 case IMSG_CTL_SHOW_RIB: 364 case IMSG_CTL_SHOW_RIB_AS: 365 case IMSG_CTL_SHOW_RIB_PREFIX: 366 if (imsg.hdr.len == IMSG_HEADER_SIZE + 367 sizeof(struct ctl_show_rib_request)) { 368 ribreq = imsg.data; 369 neighbor = &ribreq->neighbor; 370 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 371 ribreq->peerid = 0; 372 p = NULL; 373 if (neighbor->addr.af) { 374 p = getpeerbyaddr(&neighbor->addr); 375 if (p == NULL) { 376 control_result(c, 377 CTL_RES_NOSUCHPEER); 378 break; 379 } 380 ribreq->peerid = p->conf.id; 381 } else if (neighbor->descr[0]) { 382 p = getpeerbydesc(neighbor->descr); 383 if (p == NULL) { 384 control_result(c, 385 CTL_RES_NOSUCHPEER); 386 break; 387 } 388 ribreq->peerid = p->conf.id; 389 } 390 if ((ribreq->flags & F_CTL_ADJ_IN) && p && 391 !p->conf.softreconfig_in) { 392 /* 393 * if no neighbor was specified we 394 * try our best. 395 */ 396 control_result(c, CTL_RES_NOCAP); 397 break; 398 } 399 if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX) 400 && (ribreq->prefix.af != AF_INET) 401 && (ribreq->prefix.af != AF_INET6)) { 402 /* malformed request, must specify af */ 403 control_result(c, CTL_RES_PARSE_ERROR); 404 break; 405 } 406 c->ibuf.pid = imsg.hdr.pid; 407 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 408 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 409 } else 410 log_warnx("got IMSG_CTL_SHOW_RIB with " 411 "wrong length"); 412 break; 413 case IMSG_CTL_SHOW_RIB_MEM: 414 case IMSG_CTL_SHOW_RIB_COMMUNITY: 415 case IMSG_CTL_SHOW_NETWORK: 416 c->ibuf.pid = imsg.hdr.pid; 417 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 418 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 419 break; 420 case IMSG_NETWORK_ADD: 421 case IMSG_NETWORK_REMOVE: 422 case IMSG_NETWORK_FLUSH: 423 case IMSG_NETWORK_DONE: 424 case IMSG_FILTER_SET: 425 imsg_compose_rde(imsg.hdr.type, 0, 426 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 427 break; 428 default: 429 break; 430 } 431 imsg_free(&imsg); 432 } 433 434 return (0); 435 } 436 437 int 438 control_imsg_relay(struct imsg *imsg) 439 { 440 struct ctl_conn *c; 441 442 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 443 return (0); 444 445 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 446 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 447 } 448 449 void 450 control_result(struct ctl_conn *c, u_int code) 451 { 452 imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1, 453 &code, sizeof(code)); 454 } 455