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
32TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
33
34#define	CONTROL_BACKLOG	5
35
36struct ctl_conn	*control_connbyfd(int);
37struct ctl_conn	*control_connbypid(pid_t);
38int		 control_close(struct ctl_conn *);
39void		 control_result(struct ctl_conn *, u_int);
40ssize_t		 imsg_read_nofd(struct imsgbuf *);
41
42int
43control_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
68int
69control_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
124int
125control_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
135void
136control_shutdown(int fd)
137{
138	close(fd);
139}
140
141size_t
142control_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
157unsigned int
158control_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
192struct ctl_conn *
193control_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
205struct ctl_conn *
206control_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
218int
219control_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
233int
234control_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
531int
532control_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
584void
585control_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 */
592ssize_t
593imsg_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