control.c revision 1.33
1/* $OpenBSD: control.c,v 1.33 2004/06/20 18:35:12 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 bgpd_addr *addr; 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 if (imsg.hdr.len == IMSG_HEADER_SIZE + 221 sizeof(struct bgpd_addr)) { 222 addr = imsg.data; 223 p = getpeerbyaddr(addr); 224 if (p != NULL) 225 imsg_compose(&c->ibuf, 226 IMSG_CTL_SHOW_NEIGHBOR, 227 0, p, sizeof(struct peer)); 228 } else 229 for (p = peers; p != NULL; p = p->next) 230 imsg_compose(&c->ibuf, 231 IMSG_CTL_SHOW_NEIGHBOR, 232 0, p, sizeof(struct peer)); 233 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, NULL, 0); 234 break; 235 case IMSG_CTL_RELOAD: 236 case IMSG_CTL_FIB_COUPLE: 237 case IMSG_CTL_FIB_DECOUPLE: 238 imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); 239 break; 240 case IMSG_CTL_NEIGHBOR_UP: 241 if (imsg.hdr.len == IMSG_HEADER_SIZE + 242 sizeof(struct bgpd_addr)) { 243 addr = imsg.data; 244 p = getpeerbyaddr(addr); 245 if (p != NULL) 246 bgp_fsm(p, EVNT_START); 247 else 248 log_warnx("IMSG_CTL_NEIGHBOR_UP " 249 "with unknown neighbor"); 250 } else 251 log_warnx("got IMSG_CTL_NEIGHBOR_UP with " 252 "wrong length"); 253 break; 254 case IMSG_CTL_NEIGHBOR_DOWN: 255 if (imsg.hdr.len == IMSG_HEADER_SIZE + 256 sizeof(struct bgpd_addr)) { 257 addr = imsg.data; 258 p = getpeerbyaddr(addr); 259 if (p != NULL) 260 bgp_fsm(p, EVNT_STOP); 261 else 262 log_warnx("IMSG_CTL_NEIGHBOR_DOWN" 263 " with unknown neighbor"); 264 } else 265 log_warnx("got IMSG_CTL_NEIGHBOR_DOWN " 266 "with wrong length"); 267 break; 268 case IMSG_CTL_KROUTE: 269 case IMSG_CTL_KROUTE_ADDR: 270 case IMSG_CTL_SHOW_NEXTHOP: 271 case IMSG_CTL_SHOW_INTERFACE: 272 c->ibuf.pid = imsg.hdr.pid; 273 imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, 274 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 275 break; 276 case IMSG_CTL_SHOW_RIB: 277 case IMSG_CTL_SHOW_RIB_AS: 278 case IMSG_CTL_SHOW_RIB_PREFIX: 279 case IMSG_CTL_SHOW_NETWORK: 280 c->ibuf.pid = imsg.hdr.pid; 281 imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid, 282 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 283 break; 284 case IMSG_NETWORK_ADD: 285 case IMSG_NETWORK_REMOVE: 286 case IMSG_NETWORK_FLUSH: 287 imsg_compose_rde(imsg.hdr.type, 0, 288 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); 289 break; 290 default: 291 break; 292 } 293 imsg_free(&imsg); 294 } 295 296 return (0); 297} 298 299int 300control_imsg_relay(struct imsg *imsg) 301{ 302 struct ctl_conn *c; 303 304 if ((c = control_connbypid(imsg->hdr.pid)) == NULL) 305 return (0); 306 307 return (imsg_compose_pid(&c->ibuf, imsg->hdr.type, imsg->hdr.pid, 308 imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 309} 310