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