xref: /openbsd/usr.sbin/bgpd/control.c (revision 9e6efb0a)
1 /*	$OpenBSD: control.c,v 1.117 2024/04/22 09:36:04 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	 sa_un;
46 	int			 fd;
47 
48 	memset(&sa_un, 0, sizeof(sa_un));
49 	sa_un.sun_family = AF_UNIX;
50 	strlcpy(sa_un.sun_path, path, sizeof(sa_un.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 *)&sa_un, sizeof(sa_un)) == 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	 sa_un;
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 	memset(&sa_un, 0, sizeof(sa_un));
82 	sa_un.sun_family = AF_UNIX;
83 	if (strlcpy(sa_un.sun_path, path, sizeof(sa_un.sun_path)) >=
84 	    sizeof(sa_un.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 *)&sa_un, sizeof(sa_un)) == -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->imsgbuf.fd;
149 		pfd[i].events = POLLIN;
150 		if (ctl_conn->imsgbuf.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	 sa_un;
163 	struct ctl_conn		*ctl_conn;
164 
165 	len = sizeof(sa_un);
166 	if ((connfd = accept4(listenfd,
167 	    (struct sockaddr *)&sa_un, &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->imsgbuf, 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->imsgbuf.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->imsgbuf.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->imsgbuf.pid)
222 		imsg_ctl_rde_msg(IMSG_CTL_TERMINATE, 0, c->imsgbuf.pid);
223 
224 	msgbuf_clear(&c->imsgbuf.w);
225 	TAILQ_REMOVE(&ctl_conns, c, entry);
226 
227 	close(c->imsgbuf.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_neighbor	 neighbor;
238 	struct ctl_show_rib_request	ribreq;
239 	struct ctl_conn		*c;
240 	struct peer		*p;
241 	ssize_t			 n;
242 	uint32_t		 type;
243 	pid_t			 pid;
244 	int			 verbose, matched;
245 
246 	if ((c = control_connbyfd(pfd->fd)) == NULL) {
247 		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
248 		return (0);
249 	}
250 
251 	if (pfd->revents & POLLOUT) {
252 		if (msgbuf_write(&c->imsgbuf.w) <= 0 && errno != EAGAIN)
253 			return control_close(c);
254 		if (c->throttled && c->imsgbuf.w.queued < CTL_MSG_LOW_MARK) {
255 			if (imsg_ctl_rde_msg(IMSG_XON, 0, c->imsgbuf.pid) != -1)
256 				c->throttled = 0;
257 		}
258 	}
259 
260 	if (!(pfd->revents & POLLIN))
261 		return (0);
262 
263 	if (((n = imsg_read_nofd(&c->imsgbuf)) == -1 && errno != EAGAIN) ||
264 	    n == 0)
265 		return control_close(c);
266 
267 	for (;;) {
268 		if ((n = imsg_get(&c->imsgbuf, &imsg)) == -1)
269 			return control_close(c);
270 
271 		if (n == 0)
272 			break;
273 
274 		type = imsg_get_type(&imsg);
275 		pid = imsg_get_pid(&imsg);
276 		if (c->restricted) {
277 			switch (type) {
278 			case IMSG_CTL_SHOW_NEIGHBOR:
279 			case IMSG_CTL_SHOW_NEXTHOP:
280 			case IMSG_CTL_SHOW_INTERFACE:
281 			case IMSG_CTL_SHOW_RIB_MEM:
282 			case IMSG_CTL_SHOW_TERSE:
283 			case IMSG_CTL_SHOW_TIMER:
284 			case IMSG_CTL_SHOW_NETWORK:
285 			case IMSG_CTL_SHOW_FLOWSPEC:
286 			case IMSG_CTL_SHOW_RIB:
287 			case IMSG_CTL_SHOW_RIB_PREFIX:
288 			case IMSG_CTL_SHOW_SET:
289 			case IMSG_CTL_SHOW_RTR:
290 				break;
291 			default:
292 				/* clear imsg type to prevent processing */
293 				type = IMSG_NONE;
294 				control_result(c, CTL_RES_DENIED);
295 				break;
296 			}
297 		}
298 
299 		/*
300 		 * TODO: this is wrong and shoud work the other way around.
301 		 * The imsg.hdr.pid is from the remote end and should not
302 		 * be trusted.
303 		 */
304 		c->imsgbuf.pid = pid;
305 		switch (type) {
306 		case IMSG_NONE:
307 			/* message was filtered out, nothing to do */
308 			break;
309 		case IMSG_CTL_FIB_COUPLE:
310 		case IMSG_CTL_FIB_DECOUPLE:
311 			imsg_ctl_parent(&imsg);
312 			break;
313 		case IMSG_CTL_SHOW_TERSE:
314 			RB_FOREACH(p, peer_head, peers)
315 				imsg_compose(&c->imsgbuf,
316 				    IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1,
317 				    p, sizeof(struct peer));
318 			imsg_compose(&c->imsgbuf, IMSG_CTL_END, 0, 0, -1,
319 			    NULL, 0);
320 			break;
321 		case IMSG_CTL_SHOW_NEIGHBOR:
322 			if (imsg_get_data(&imsg, &neighbor,
323 			    sizeof(neighbor)) == -1)
324 				memset(&neighbor, 0, sizeof(neighbor));
325 
326 			matched = 0;
327 			RB_FOREACH(p, peer_head, peers) {
328 				if (!peer_matched(p, &neighbor))
329 					continue;
330 
331 				matched = 1;
332 				if (!neighbor.show_timers) {
333 					imsg_ctl_rde_msg(type,
334 					    p->conf.id, pid);
335 				} else {
336 					u_int			 i;
337 					time_t			 d;
338 					struct ctl_timer	 ct;
339 
340 					imsg_compose(&c->imsgbuf,
341 					    IMSG_CTL_SHOW_NEIGHBOR,
342 					    0, 0, -1, p, sizeof(*p));
343 					for (i = 1; i < Timer_Max; i++) {
344 						if (!timer_running(&p->timers,
345 						    i, &d))
346 							continue;
347 						ct.type = i;
348 						ct.val = d;
349 						imsg_compose(&c->imsgbuf,
350 						    IMSG_CTL_SHOW_TIMER,
351 						    0, 0, -1, &ct, sizeof(ct));
352 					}
353 				}
354 			}
355 			if (!matched && RB_EMPTY(peers)) {
356 				control_result(c, CTL_RES_NOSUCHPEER);
357 			} else if (!neighbor.show_timers) {
358 				imsg_ctl_rde_msg(IMSG_CTL_END, 0, pid);
359 			} else {
360 				imsg_compose(&c->imsgbuf, IMSG_CTL_END, 0, 0,
361 				    -1, NULL, 0);
362 			}
363 			break;
364 		case IMSG_CTL_NEIGHBOR_UP:
365 		case IMSG_CTL_NEIGHBOR_DOWN:
366 		case IMSG_CTL_NEIGHBOR_CLEAR:
367 		case IMSG_CTL_NEIGHBOR_RREFRESH:
368 		case IMSG_CTL_NEIGHBOR_DESTROY:
369 			if (imsg_get_data(&imsg, &neighbor,
370 			    sizeof(neighbor)) == -1) {
371 				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
372 				    "wrong length");
373 				break;
374 			}
375 
376 			matched = 0;
377 			RB_FOREACH(p, peer_head, peers) {
378 				if (!peer_matched(p, &neighbor))
379 					continue;
380 
381 				matched = 1;
382 
383 				switch (type) {
384 				case IMSG_CTL_NEIGHBOR_UP:
385 					bgp_fsm(p, EVNT_START);
386 					p->conf.down = 0;
387 					p->conf.reason[0] = '\0';
388 					p->IdleHoldTime =
389 					    INTERVAL_IDLE_HOLD_INITIAL;
390 					p->errcnt = 0;
391 					control_result(c, CTL_RES_OK);
392 					break;
393 				case IMSG_CTL_NEIGHBOR_DOWN:
394 					neighbor.reason[
395 					    sizeof(neighbor.reason) - 1] = '\0';
396 					p->conf.down = 1;
397 					session_stop(p, ERR_CEASE_ADMIN_DOWN,
398 					    neighbor.reason);
399 					control_result(c, CTL_RES_OK);
400 					break;
401 				case IMSG_CTL_NEIGHBOR_CLEAR:
402 					neighbor.reason[
403 					    sizeof(neighbor.reason) - 1] = '\0';
404 					p->IdleHoldTime =
405 					    INTERVAL_IDLE_HOLD_INITIAL;
406 					p->errcnt = 0;
407 					if (!p->conf.down) {
408 						session_stop(p,
409 						    ERR_CEASE_ADMIN_RESET,
410 						    neighbor.reason);
411 						timer_set(&p->timers,
412 						    Timer_IdleHold,
413 						    SESSION_CLEAR_DELAY);
414 					} else {
415 						session_stop(p,
416 						    ERR_CEASE_ADMIN_DOWN,
417 						    neighbor.reason);
418 					}
419 					control_result(c, CTL_RES_OK);
420 					break;
421 				case IMSG_CTL_NEIGHBOR_RREFRESH:
422 					if (session_neighbor_rrefresh(p))
423 						control_result(c,
424 						    CTL_RES_NOCAP);
425 					else
426 						control_result(c, CTL_RES_OK);
427 					break;
428 				case IMSG_CTL_NEIGHBOR_DESTROY:
429 					if (!p->template)
430 						control_result(c,
431 						    CTL_RES_BADPEER);
432 					else if (p->state != STATE_IDLE)
433 						control_result(c,
434 						    CTL_RES_BADSTATE);
435 					else {
436 						/*
437 						 * Mark as deleted, will be
438 						 * collected on next poll loop.
439 						 */
440 						p->reconf_action =
441 						    RECONF_DELETE;
442 						control_result(c, CTL_RES_OK);
443 					}
444 					break;
445 				default:
446 					fatal("king bula wants more humppa");
447 				}
448 			}
449 			if (!matched)
450 				control_result(c, CTL_RES_NOSUCHPEER);
451 			break;
452 		case IMSG_CTL_RELOAD:
453 		case IMSG_CTL_SHOW_INTERFACE:
454 		case IMSG_CTL_SHOW_FIB_TABLES:
455 		case IMSG_CTL_SHOW_RTR:
456 			imsg_ctl_parent(&imsg);
457 			break;
458 		case IMSG_CTL_KROUTE:
459 		case IMSG_CTL_KROUTE_ADDR:
460 		case IMSG_CTL_SHOW_NEXTHOP:
461 			imsg_ctl_parent(&imsg);
462 			break;
463 		case IMSG_CTL_SHOW_RIB:
464 		case IMSG_CTL_SHOW_RIB_PREFIX:
465 			if (imsg_get_data(&imsg, &ribreq, sizeof(ribreq)) ==
466 			    -1) {
467 				log_warnx("got IMSG_CTL_SHOW_RIB with "
468 				    "wrong length");
469 				break;
470 			}
471 
472 			/* check if at least one neighbor exists */
473 			RB_FOREACH(p, peer_head, peers)
474 				if (peer_matched(p, &ribreq.neighbor))
475 					break;
476 			if (p == NULL && RB_EMPTY(peers)) {
477 				control_result(c, CTL_RES_NOSUCHPEER);
478 				break;
479 			}
480 
481 			if (type == IMSG_CTL_SHOW_RIB_PREFIX &&
482 			    ribreq.prefix.aid == AID_UNSPEC) {
483 				/* malformed request, must specify af */
484 				control_result(c, CTL_RES_PARSE_ERROR);
485 				break;
486 			}
487 
488 			c->terminate = 1;
489 			imsg_ctl_rde(&imsg);
490 			break;
491 		case IMSG_CTL_SHOW_NETWORK:
492 		case IMSG_CTL_SHOW_FLOWSPEC:
493 			c->terminate = 1;
494 			/* FALLTHROUGH */
495 		case IMSG_CTL_SHOW_RIB_MEM:
496 		case IMSG_CTL_SHOW_SET:
497 			imsg_ctl_rde(&imsg);
498 			break;
499 		case IMSG_NETWORK_ADD:
500 		case IMSG_NETWORK_ASPATH:
501 		case IMSG_NETWORK_ATTR:
502 		case IMSG_NETWORK_REMOVE:
503 		case IMSG_NETWORK_FLUSH:
504 		case IMSG_NETWORK_DONE:
505 		case IMSG_FLOWSPEC_ADD:
506 		case IMSG_FLOWSPEC_REMOVE:
507 		case IMSG_FLOWSPEC_DONE:
508 		case IMSG_FLOWSPEC_FLUSH:
509 		case IMSG_FILTER_SET:
510 			imsg_ctl_rde(&imsg);
511 			break;
512 		case IMSG_CTL_LOG_VERBOSE:
513 			if (imsg_get_data(&imsg, &verbose, sizeof(verbose)) ==
514 			    -1)
515 				break;
516 
517 			/* forward to other processes */
518 			imsg_ctl_parent(&imsg);
519 			imsg_ctl_rde(&imsg);
520 			log_setverbose(verbose);
521 			break;
522 		default:
523 			break;
524 		}
525 		imsg_free(&imsg);
526 	}
527 
528 	return (0);
529 }
530 
531 int
532 control_imsg_relay(struct imsg *imsg, struct peer *p)
533 {
534 	struct ctl_conn	*c;
535 	uint32_t type;
536 	pid_t pid;
537 
538 	type = imsg_get_type(imsg);
539 	pid = imsg_get_pid(imsg);
540 
541 	if ((c = control_connbypid(pid)) == NULL)
542 		return (0);
543 
544 	/* special handling for peers since only the stats are sent from RDE */
545 	if (type == IMSG_CTL_SHOW_NEIGHBOR) {
546 		struct rde_peer_stats stats;
547 
548 		if (p == NULL) {
549 			log_warnx("%s: no such peer: id=%u", __func__,
550 			    imsg_get_id(imsg));
551 			return (0);
552 		}
553 		if (imsg_get_data(imsg, &stats, sizeof(stats)) == -1) {
554 			log_warnx("%s: imsg_get_data", __func__);
555 			return (0);
556 		}
557 		p->stats.prefix_cnt = stats.prefix_cnt;
558 		p->stats.prefix_out_cnt = stats.prefix_out_cnt;
559 		p->stats.prefix_rcvd_update = stats.prefix_rcvd_update;
560 		p->stats.prefix_rcvd_withdraw = stats.prefix_rcvd_withdraw;
561 		p->stats.prefix_rcvd_eor = stats.prefix_rcvd_eor;
562 		p->stats.prefix_sent_update = stats.prefix_sent_update;
563 		p->stats.prefix_sent_withdraw = stats.prefix_sent_withdraw;
564 		p->stats.prefix_sent_eor = stats.prefix_sent_eor;
565 		p->stats.pending_update = stats.pending_update;
566 		p->stats.pending_withdraw = stats.pending_withdraw;
567 
568 		return imsg_compose(&c->imsgbuf, type, 0, pid, -1,
569 		    p, sizeof(*p));
570 	}
571 
572 	/* if command finished no need to send exit message */
573 	if (type == IMSG_CTL_END || type == IMSG_CTL_RESULT)
574 		c->terminate = 0;
575 
576 	if (!c->throttled && c->imsgbuf.w.queued > CTL_MSG_HIGH_MARK) {
577 		if (imsg_ctl_rde_msg(IMSG_XOFF, 0, pid) != -1)
578 			c->throttled = 1;
579 	}
580 
581 	return (imsg_forward(&c->imsgbuf, imsg));
582 }
583 
584 void
585 control_result(struct ctl_conn *c, u_int code)
586 {
587 	imsg_compose(&c->imsgbuf, IMSG_CTL_RESULT, 0, c->imsgbuf.pid, -1,
588 	    &code, sizeof(code));
589 }
590 
591 /* This should go into libutil, from smtpd/mproc.c */
592 ssize_t
593 imsg_read_nofd(struct imsgbuf *imsgbuf)
594 {
595 	ssize_t	 n;
596 	char	*buf;
597 	size_t	 len;
598 
599 	buf = imsgbuf->r.buf + imsgbuf->r.wpos;
600 	len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos;
601 
602 	while ((n = recv(imsgbuf->fd, buf, len, 0)) == -1) {
603 		if (errno != EINTR)
604 			return (n);
605 	}
606 
607 	imsgbuf->r.wpos += n;
608 	return (n);
609 }
610