control.c revision 1.41
1/* $OpenBSD: control.c,v 1.41 2004/12/23 17:55:58 henning 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 { 34 int fd; 35} control_state; 36 37struct ctl_conn *control_connbyfd(int); 38struct ctl_conn *control_connbypid(pid_t); 39int control_close(int); 40 41int 42control_init(void) 43{ 44 struct sockaddr_un sun; 45 int fd; 46 mode_t old_umask; 47 48 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 49 log_warn("control_init: socket"); 50 return (-1); 51 } 52 53 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 54 bzero(&sun, sizeof(sun)); 55 sun.sun_family = AF_UNIX; 56 strlcpy(sun.sun_path, SOCKET_NAME, sizeof(sun.sun_path)); 57 58 if (unlink(SOCKET_NAME) == -1) 59 if (errno != ENOENT) { 60 log_warn("unlink %s", SOCKET_NAME); 61 close(fd); 62 return (-1); 63 } 64 65 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 66 log_warn("control_init: bind: %s", SOCKET_NAME); 67 close(fd); 68 return (-1); 69 } 70 71 if (chmod(SOCKET_NAME, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { 72 log_warn("control_init chmod"); 73 close(fd); 74 return (-1); 75 } 76 77 umask(old_umask); 78 79 session_socket_blockmode(fd, BM_NONBLOCK); 80 control_state.fd = fd; 81 82 return (fd); 83} 84 85int 86control_listen(void) 87{ 88 if (listen(control_state.fd, CONTROL_BACKLOG) == -1) { 89 log_warn("control_listen: listen"); 90 return (-1); 91 } 92 93 return (control_state.fd); 94} 95 96void 97control_shutdown(void) 98{ 99 close(control_state.fd); 100} 101 102void 103control_cleanup(void) 104{ 105 unlink(SOCKET_NAME); 106} 107 108int 109control_accept(int listenfd) 110{ 111 int connfd; 112 socklen_t len; 113 struct sockaddr_un sun; 114 struct ctl_conn *ctl_conn; 115 116 len = sizeof(sun); 117 if ((connfd = accept(listenfd, 118 (struct sockaddr *)&sun, &len)) == -1) { 119 if (errno != EWOULDBLOCK && errno != EINTR) 120 log_warn("session_control_accept"); 121 return (0); 122 } 123 124 session_socket_blockmode(connfd, BM_NONBLOCK); 125 126 if ((ctl_conn = malloc(sizeof(struct ctl_conn))) == NULL) { 127 log_warn("session_control_accept"); 128 return (0); 129 } 130 131 imsg_init(&ctl_conn->ibuf, connfd); 132 133 TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry); 134 135 return (1); 136} 137 138struct ctl_conn * 139control_connbyfd(int fd) 140{ 141 struct ctl_conn *c; 142 143 for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd; 144 c = TAILQ_NEXT(c, entry)) 145 ; /* nothing */ 146 147 return (c); 148} 149 150struct ctl_conn * 151control_connbypid(pid_t pid) 152{ 153 struct ctl_conn *c; 154 155 for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid; 156 c = TAILQ_NEXT(c, entry)) 157 ; /* nothing */ 158 159 return (c); 160} 161 162int 163control_close(int fd) 164{ 165 struct ctl_conn *c; 166 167 if ((c = control_connbyfd(fd)) == NULL) { 168 log_warn("control_close: fd %d: not found", fd); 169 return (0); 170 } 171 172 msgbuf_clear(&c->ibuf.w); 173 TAILQ_REMOVE(&ctl_conns, c, entry); 174 175 close(c->ibuf.fd); 176 free(c); 177 178 return (1); 179} 180 181int 182control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt) 183{ 184 struct imsg imsg; 185 struct ctl_conn *c; 186 int n; 187 struct peer *p; 188 struct ctl_neighbor *neighbor; 189 190 if ((c = control_connbyfd(pfd->fd)) == NULL) { 191 log_warn("control_dispatch_msg: fd %d: not found", pfd->fd); 192 return (0); 193 } 194 195 if (pfd->revents & POLLOUT) 196 if (msgbuf_write(&c->ibuf.w) < 0) { 197 *ctl_cnt -= control_close(pfd->fd); 198 return (1); 199 } 200 201 if (!(pfd->revents & POLLIN)) 202 return (0); 203 204 if (imsg_read(&c->ibuf) <= 0) { 205 *ctl_cnt -= control_close(pfd->fd); 206 return (1); 207 } 208 209 for (;;) { 210 if ((n = imsg_get(&c->ibuf, &imsg)) == -1) { 211 *ctl_cnt -= control_close(pfd->fd); 212 return (1); 213 } 214 215 if (n == 0) 216 break; 217 218 switch (imsg.hdr.type) { 219 case IMSG_CTL_SHOW_NEIGHBOR: 220 c->ibuf.pid = imsg.hdr.pid; 221 if (imsg.hdr.len == IMSG_HEADER_SIZE + 222 sizeof(struct ctl_neighbor)) { 223 neighbor = imsg.data; 224 p = getpeerbyaddr(&neighbor->addr); 225 if (p == NULL) 226 p = getpeerbydesc(neighbor->descr); 227 if (p != NULL) 228 imsg_compose_rde(imsg.hdr.type, 229 imsg.hdr.pid, 230 p, sizeof(struct peer)); 231 } else 232 for (p = peers; p != NULL; p = p->next) 233 imsg_compose_rde(imsg.hdr.type, 234 imsg.hdr.pid, 235 p, sizeof(struct peer)); 236 imsg_compose_rde(IMSG_CTL_END, imsg.hdr.pid, NULL, 0); 237 break; 238 case IMSG_CTL_RELOAD: 239 case IMSG_CTL_FIB_COUPLE: 240 case IMSG_CTL_FIB_DECOUPLE: 241 imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); 242 break; 243 case IMSG_CTL_NEIGHBOR_UP: 244 case IMSG_CTL_NEIGHBOR_DOWN: 245 case IMSG_CTL_NEIGHBOR_CLEAR: 246 if (imsg.hdr.len == IMSG_HEADER_SIZE + 247 sizeof(struct ctl_neighbor)) { 248 neighbor = imsg.data; 249 neighbor->descr[PEER_DESCR_LEN - 1] = 0; 250 p = getpeerbyaddr(&neighbor->addr); 251 if (p == NULL) 252 p = getpeerbydesc(neighbor->descr); 253 if (p == NULL) { 254 log_warnx("IMSG_CTL_NEIGHBOR_ " 255 "with unknown neighbor"); 256 break; 257 } 258 switch (imsg.hdr.type) { 259 case IMSG_CTL_NEIGHBOR_UP: 260 bgp_fsm(p, EVNT_START); 261 break; 262 case IMSG_CTL_NEIGHBOR_DOWN: 263 bgp_fsm(p, EVNT_STOP); 264 break; 265 case IMSG_CTL_NEIGHBOR_CLEAR: 266 bgp_fsm(p, EVNT_STOP); 267 bgp_fsm(p, EVNT_START); 268 break; 269 default: 270 fatal("king bula wants more humppa"); 271 } 272 } else 273 log_warnx("got IMSG_CTL_NEIGHBOR_ with " 274 "wrong length"); 275 break; 276 case IMSG_CTL_KROUTE: 277 case IMSG_CTL_KROUTE_ADDR: 278 case IMSG_CTL_SHOW_NEXTHOP: 279 case IMSG_CTL_SHOW_INTERFACE: 280 c->ibuf.pid = imsg.hdr.pid; 281 imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, 282 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 283 break; 284 case IMSG_CTL_SHOW_RIB: 285 case IMSG_CTL_SHOW_RIB_AS: 286 case IMSG_CTL_SHOW_RIB_PREFIX: 287 case IMSG_CTL_SHOW_NETWORK: 288 c->ibuf.pid = imsg.hdr.pid; 289 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 290 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 291 break; 292 case IMSG_NETWORK_ADD: 293 case IMSG_NETWORK_REMOVE: 294 case IMSG_NETWORK_FLUSH: 295 case IMSG_NETWORK_DONE: 296 case IMSG_FILTER_SET: 297 imsg_compose_rde(imsg.hdr.type, 0, 298 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 299 break; 300 default: 301 break; 302 } 303 imsg_free(&imsg); 304 } 305 306 return (0); 307} 308 309int 310control_imsg_relay(struct imsg *imsg) 311{ 312 struct ctl_conn *c; 313 314 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 315 return (0); 316 317 return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, 318 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 319} 320