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