xref: /openbsd-src/usr.sbin/bgpd/control.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: control.c,v 1.82 2015/12/05 18:28:04 benno 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 ssize_t		 imsg_read_nofd(struct imsgbuf *);
38 
39 int
40 control_init(int restricted, char *path)
41 {
42 	struct sockaddr_un	 sun;
43 	int			 fd;
44 	mode_t			 old_umask, mode;
45 
46 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
47 	     0)) == -1) {
48 		log_warn("control_init: socket");
49 		return (-1);
50 	}
51 
52 	bzero(&sun, sizeof(sun));
53 	sun.sun_family = AF_UNIX;
54 	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
55 	    sizeof(sun.sun_path)) {
56 		log_warn("control_init: socket name too long");
57 		close(fd);
58 		return (-1);
59 	}
60 
61 	if (unlink(path) == -1)
62 		if (errno != ENOENT) {
63 			log_warn("control_init: unlink %s", path);
64 			close(fd);
65 			return (-1);
66 		}
67 
68 	if (restricted) {
69 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
70 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
71 	} else {
72 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
73 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
74 	}
75 
76 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
77 		log_warn("control_init: bind: %s", path);
78 		close(fd);
79 		umask(old_umask);
80 		return (-1);
81 	}
82 
83 	umask(old_umask);
84 
85 	if (chmod(path, mode) == -1) {
86 		log_warn("control_init: chmod: %s", path);
87 		close(fd);
88 		unlink(path);
89 		return (-1);
90 	}
91 
92 	return (fd);
93 }
94 
95 int
96 control_listen(int fd)
97 {
98 	if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) {
99 		log_warn("control_listen: listen");
100 		return (-1);
101 	}
102 
103 	return (0);
104 }
105 
106 void
107 control_shutdown(int fd)
108 {
109 	close(fd);
110 }
111 
112 void
113 control_cleanup(const char *path)
114 {
115 	if (path)
116 		unlink(path);
117 }
118 
119 unsigned int
120 control_accept(int listenfd, int restricted)
121 {
122 	int			 connfd;
123 	socklen_t		 len;
124 	struct sockaddr_un	 sun;
125 	struct ctl_conn		*ctl_conn;
126 
127 	len = sizeof(sun);
128 	if ((connfd = accept4(listenfd,
129 	    (struct sockaddr *)&sun, &len,
130 	    SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) {
131 		if (errno == ENFILE || errno == EMFILE) {
132 			pauseaccept = getmonotime();
133 			return (0);
134 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
135 		    errno != ECONNABORTED)
136 			log_warn("control_accept: accept");
137 		return (0);
138 	}
139 
140 	if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
141 		log_warn("control_accept");
142 		close(connfd);
143 		return (0);
144 	}
145 
146 	imsg_init(&ctl_conn->ibuf, connfd);
147 	ctl_conn->restricted = restricted;
148 
149 	TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
150 
151 	return (1);
152 }
153 
154 struct ctl_conn *
155 control_connbyfd(int fd)
156 {
157 	struct ctl_conn	*c;
158 
159 	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd;
160 	    c = TAILQ_NEXT(c, entry))
161 		;	/* nothing */
162 
163 	return (c);
164 }
165 
166 struct ctl_conn *
167 control_connbypid(pid_t pid)
168 {
169 	struct ctl_conn	*c;
170 
171 	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid;
172 	    c = TAILQ_NEXT(c, entry))
173 		;	/* nothing */
174 
175 	return (c);
176 }
177 
178 int
179 control_close(int fd)
180 {
181 	struct ctl_conn	*c;
182 
183 	if ((c = control_connbyfd(fd)) == NULL) {
184 		log_warn("control_close: fd %d: not found", fd);
185 		return (0);
186 	}
187 
188 	msgbuf_clear(&c->ibuf.w);
189 	TAILQ_REMOVE(&ctl_conns, c, entry);
190 
191 	close(c->ibuf.fd);
192 	free(c);
193 	pauseaccept = 0;
194 	return (1);
195 }
196 
197 int
198 control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
199 {
200 	struct imsg		 imsg;
201 	struct ctl_conn		*c;
202 	ssize_t			 n;
203 	int			 verbose;
204 	struct peer		*p;
205 	struct ctl_neighbor	*neighbor;
206 	struct ctl_show_rib_request	*ribreq;
207 
208 	if ((c = control_connbyfd(pfd->fd)) == NULL) {
209 		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
210 		return (0);
211 	}
212 
213 	if (pfd->revents & POLLOUT)
214 		if (msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) {
215 			*ctl_cnt -= control_close(pfd->fd);
216 			return (1);
217 		}
218 
219 	if (!(pfd->revents & POLLIN))
220 		return (0);
221 
222 	if (((n = imsg_read_nofd(&c->ibuf)) == -1 && errno != EAGAIN) ||
223 	    n == 0) {
224 		*ctl_cnt -= control_close(pfd->fd);
225 		return (1);
226 	}
227 
228 	for (;;) {
229 		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
230 			*ctl_cnt -= control_close(pfd->fd);
231 			return (1);
232 		}
233 
234 		if (n == 0)
235 			break;
236 
237 		if (c->restricted) {
238 			switch (imsg.hdr.type) {
239 			case IMSG_CTL_SHOW_NEIGHBOR:
240 			case IMSG_CTL_SHOW_NEXTHOP:
241 			case IMSG_CTL_SHOW_INTERFACE:
242 			case IMSG_CTL_SHOW_RIB:
243 			case IMSG_CTL_SHOW_RIB_AS:
244 			case IMSG_CTL_SHOW_RIB_PREFIX:
245 			case IMSG_CTL_SHOW_RIB_MEM:
246 			case IMSG_CTL_SHOW_RIB_COMMUNITY:
247 			case IMSG_CTL_SHOW_NETWORK:
248 			case IMSG_CTL_SHOW_TERSE:
249 			case IMSG_CTL_SHOW_TIMER:
250 				break;
251 			default:
252 				/* clear imsg type to prevent processing */
253 				imsg.hdr.type = IMSG_NONE;
254 				control_result(c, CTL_RES_DENIED);
255 				break;
256 			}
257 		}
258 
259 		switch (imsg.hdr.type) {
260 		case IMSG_NONE:
261 			/* message was filtered out, nothing to do */
262 			break;
263 		case IMSG_CTL_SHOW_NEIGHBOR:
264 			c->ibuf.pid = imsg.hdr.pid;
265 			if (imsg.hdr.len == IMSG_HEADER_SIZE +
266 			    sizeof(struct ctl_neighbor)) {
267 				neighbor = imsg.data;
268 				p = getpeerbyaddr(&neighbor->addr);
269 				if (p == NULL)
270 					p = getpeerbydesc(neighbor->descr);
271 				if (p == NULL) {
272 					control_result(c, CTL_RES_NOSUCHPEER);
273 					break;
274 				}
275 				if (!neighbor->show_timers) {
276 					imsg_ctl_rde(imsg.hdr.type,
277 					    imsg.hdr.pid,
278 					    p, sizeof(struct peer));
279 					imsg_ctl_rde(IMSG_CTL_END,
280 					    imsg.hdr.pid, NULL, 0);
281 				} else {
282 					u_int			 i;
283 					time_t			 d;
284 					struct ctl_timer	 ct;
285 
286 					imsg_compose(&c->ibuf,
287 					    IMSG_CTL_SHOW_NEIGHBOR,
288 					    0, 0, -1, p, sizeof(*p));
289 					for (i = 1; i < Timer_Max; i++) {
290 						if (!timer_running(p, i, &d))
291 							continue;
292 						ct.type = i;
293 						ct.val = d;
294 						imsg_compose(&c->ibuf,
295 						    IMSG_CTL_SHOW_TIMER,
296 						    0, 0, -1, &ct, sizeof(ct));
297 					}
298 					imsg_compose(&c->ibuf, IMSG_CTL_END,
299 					    0, 0, -1, NULL, 0);
300 				}
301 			} else {
302 				for (p = peers; p != NULL; p = p->next)
303 					imsg_ctl_rde(imsg.hdr.type,
304 					    imsg.hdr.pid,
305 					    p, sizeof(struct peer));
306 				imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid,
307 					NULL, 0);
308 			}
309 			break;
310 		case IMSG_CTL_SHOW_TERSE:
311 			for (p = peers; p != NULL; p = p->next)
312 				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
313 				    0, 0, -1, p, sizeof(struct peer));
314 			imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
315 			break;
316 		case IMSG_CTL_FIB_COUPLE:
317 		case IMSG_CTL_FIB_DECOUPLE:
318 			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
319 			    0, NULL, 0);
320 			break;
321 		case IMSG_CTL_NEIGHBOR_UP:
322 		case IMSG_CTL_NEIGHBOR_DOWN:
323 		case IMSG_CTL_NEIGHBOR_CLEAR:
324 		case IMSG_CTL_NEIGHBOR_RREFRESH:
325 		case IMSG_CTL_NEIGHBOR_DESTROY:
326 			if (imsg.hdr.len == IMSG_HEADER_SIZE +
327 			    sizeof(struct ctl_neighbor)) {
328 				neighbor = imsg.data;
329 				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
330 				p = getpeerbyaddr(&neighbor->addr);
331 				if (p == NULL)
332 					p = getpeerbydesc(neighbor->descr);
333 				if (p == NULL) {
334 					control_result(c, CTL_RES_NOSUCHPEER);
335 					break;
336 				}
337 				switch (imsg.hdr.type) {
338 				case IMSG_CTL_NEIGHBOR_UP:
339 					bgp_fsm(p, EVNT_START);
340 					control_result(c, CTL_RES_OK);
341 					break;
342 				case IMSG_CTL_NEIGHBOR_DOWN:
343 					session_stop(p, ERR_CEASE_ADMIN_DOWN);
344 					control_result(c, CTL_RES_OK);
345 					break;
346 				case IMSG_CTL_NEIGHBOR_CLEAR:
347 					if (!p->conf.down) {
348 						session_stop(p,
349 						    ERR_CEASE_ADMIN_RESET);
350 						timer_set(p, Timer_IdleHold,
351 						    SESSION_CLEAR_DELAY);
352 					} else {
353 						session_stop(p,
354 						    ERR_CEASE_ADMIN_DOWN);
355 					}
356 					control_result(c, CTL_RES_OK);
357 					break;
358 				case IMSG_CTL_NEIGHBOR_RREFRESH:
359 					if (session_neighbor_rrefresh(p))
360 						control_result(c,
361 						    CTL_RES_NOCAP);
362 					else
363 						control_result(c, CTL_RES_OK);
364 					break;
365 				case IMSG_CTL_NEIGHBOR_DESTROY:
366 					if (!p->template)
367 						control_result(c,
368 						    CTL_RES_BADPEER);
369 					else if (p->state != STATE_IDLE)
370 						control_result(c,
371 						    CTL_RES_BADSTATE);
372 					else {
373 						/*
374 						 * Mark as deleted, will be
375 						 * collected on next poll loop.
376 						 */
377 						p->conf.reconf_action =
378 						    RECONF_DELETE;
379 						control_result(c, CTL_RES_OK);
380 					}
381 					break;
382 				default:
383 					fatal("king bula wants more humppa");
384 				}
385 			} else
386 				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
387 				    "wrong length");
388 			break;
389 		case IMSG_CTL_RELOAD:
390 		case IMSG_CTL_SHOW_INTERFACE:
391 		case IMSG_CTL_SHOW_FIB_TABLES:
392 			c->ibuf.pid = imsg.hdr.pid;
393 			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
394 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
395 			break;
396 		case IMSG_CTL_KROUTE:
397 		case IMSG_CTL_KROUTE_ADDR:
398 		case IMSG_CTL_SHOW_NEXTHOP:
399 			c->ibuf.pid = imsg.hdr.pid;
400 			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
401 			    imsg.hdr.pid, imsg.data, imsg.hdr.len -
402 			    IMSG_HEADER_SIZE);
403 			break;
404 		case IMSG_CTL_SHOW_RIB:
405 		case IMSG_CTL_SHOW_RIB_AS:
406 		case IMSG_CTL_SHOW_RIB_PREFIX:
407 			if (imsg.hdr.len == IMSG_HEADER_SIZE +
408 			    sizeof(struct ctl_show_rib_request)) {
409 				ribreq = imsg.data;
410 				neighbor = &ribreq->neighbor;
411 				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
412 				ribreq->peerid = 0;
413 				p = NULL;
414 				if (neighbor->addr.aid) {
415 					p = getpeerbyaddr(&neighbor->addr);
416 					if (p == NULL) {
417 						control_result(c,
418 						    CTL_RES_NOSUCHPEER);
419 						break;
420 					}
421 					ribreq->peerid = p->conf.id;
422 				} else if (neighbor->descr[0]) {
423 					p = getpeerbydesc(neighbor->descr);
424 					if (p == NULL) {
425 						control_result(c,
426 						    CTL_RES_NOSUCHPEER);
427 						break;
428 					}
429 					ribreq->peerid = p->conf.id;
430 				}
431 				if ((ribreq->flags &
432 				     (F_CTL_ADJ_OUT | F_CTL_ADJ_IN)) && !p) {
433 					/*
434 					 * both in and out tables are only
435 					 * meaningful if used on a single
436 					 * peer.
437 					 */
438 					control_result(c, CTL_RES_NOSUCHPEER);
439 					break;
440 				}
441 				if ((ribreq->flags & F_CTL_ADJ_IN) && p &&
442 				    !p->conf.softreconfig_in) {
443 					/*
444 					 * without softreconfig_in we do not
445 					 * have an Adj-RIB-In table
446 					 */
447 					control_result(c, CTL_RES_NOCAP);
448 					break;
449 				}
450 				if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
451 				    && (ribreq->prefix.aid == AID_UNSPEC)) {
452 					/* malformed request, must specify af */
453 					control_result(c, CTL_RES_PARSE_ERROR);
454 					break;
455 				}
456 				c->ibuf.pid = imsg.hdr.pid;
457 				imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
458 				    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
459 			} else
460 				log_warnx("got IMSG_CTL_SHOW_RIB with "
461 				    "wrong length");
462 			break;
463 		case IMSG_CTL_SHOW_RIB_MEM:
464 		case IMSG_CTL_SHOW_RIB_COMMUNITY:
465 		case IMSG_CTL_SHOW_NETWORK:
466 			c->ibuf.pid = imsg.hdr.pid;
467 			imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
468 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
469 			break;
470 		case IMSG_NETWORK_ADD:
471 		case IMSG_NETWORK_ASPATH:
472 		case IMSG_NETWORK_ATTR:
473 		case IMSG_NETWORK_REMOVE:
474 		case IMSG_NETWORK_FLUSH:
475 		case IMSG_NETWORK_DONE:
476 		case IMSG_FILTER_SET:
477 			imsg_ctl_rde(imsg.hdr.type, 0,
478 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
479 			break;
480 		case IMSG_CTL_LOG_VERBOSE:
481 			if (imsg.hdr.len != IMSG_HEADER_SIZE +
482 			    sizeof(verbose))
483 				break;
484 
485 			/* forward to other processes */
486 			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
487 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
488 			imsg_ctl_rde(imsg.hdr.type, 0,
489 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
490 
491 			memcpy(&verbose, imsg.data, sizeof(verbose));
492 			log_verbose(verbose);
493 			break;
494 		default:
495 			break;
496 		}
497 		imsg_free(&imsg);
498 	}
499 
500 	return (0);
501 }
502 
503 int
504 control_imsg_relay(struct imsg *imsg)
505 {
506 	struct ctl_conn	*c;
507 
508 	if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
509 		return (0);
510 
511 	return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
512 	    imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
513 }
514 
515 void
516 control_result(struct ctl_conn *c, u_int code)
517 {
518 	imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1,
519 	    &code, sizeof(code));
520 }
521 
522 /* This should go into libutil, from smtpd/mproc.c */
523 ssize_t
524 imsg_read_nofd(struct imsgbuf *ibuf)
525 {
526 	ssize_t	 n;
527 	char	*buf;
528 	size_t	 len;
529 
530 	buf = ibuf->r.buf + ibuf->r.wpos;
531 	len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
532 
533 	while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
534 		if (errno != EINTR)
535 			return (n);
536 	}
537 
538 	ibuf->r.wpos += n;
539 	return (n);
540 }
541