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