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