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