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