control.c revision 1.116
1/*	$OpenBSD: control.c,v 1.116 2024/01/11 15:46:25 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					strlcpy(p->conf.reason,
397					    neighbor.reason,
398					    sizeof(p->conf.reason));
399					p->conf.down = 1;
400					session_stop(p, ERR_CEASE_ADMIN_DOWN);
401					control_result(c, CTL_RES_OK);
402					break;
403				case IMSG_CTL_NEIGHBOR_CLEAR:
404					neighbor.reason[
405					    sizeof(neighbor.reason) - 1] = '\0';
406					strlcpy(p->conf.reason,
407					    neighbor.reason,
408					    sizeof(p->conf.reason));
409					p->IdleHoldTime =
410					    INTERVAL_IDLE_HOLD_INITIAL;
411					p->errcnt = 0;
412					if (!p->conf.down) {
413						session_stop(p,
414						    ERR_CEASE_ADMIN_RESET);
415						timer_set(&p->timers,
416						    Timer_IdleHold,
417						    SESSION_CLEAR_DELAY);
418					} else {
419						session_stop(p,
420						    ERR_CEASE_ADMIN_DOWN);
421					}
422					control_result(c, CTL_RES_OK);
423					break;
424				case IMSG_CTL_NEIGHBOR_RREFRESH:
425					if (session_neighbor_rrefresh(p))
426						control_result(c,
427						    CTL_RES_NOCAP);
428					else
429						control_result(c, CTL_RES_OK);
430					break;
431				case IMSG_CTL_NEIGHBOR_DESTROY:
432					if (!p->template)
433						control_result(c,
434						    CTL_RES_BADPEER);
435					else if (p->state != STATE_IDLE)
436						control_result(c,
437						    CTL_RES_BADSTATE);
438					else {
439						/*
440						 * Mark as deleted, will be
441						 * collected on next poll loop.
442						 */
443						p->reconf_action =
444						    RECONF_DELETE;
445						control_result(c, CTL_RES_OK);
446					}
447					break;
448				default:
449					fatal("king bula wants more humppa");
450				}
451			}
452			if (!matched)
453				control_result(c, CTL_RES_NOSUCHPEER);
454			break;
455		case IMSG_CTL_RELOAD:
456		case IMSG_CTL_SHOW_INTERFACE:
457		case IMSG_CTL_SHOW_FIB_TABLES:
458		case IMSG_CTL_SHOW_RTR:
459			imsg_ctl_parent(&imsg);
460			break;
461		case IMSG_CTL_KROUTE:
462		case IMSG_CTL_KROUTE_ADDR:
463		case IMSG_CTL_SHOW_NEXTHOP:
464			imsg_ctl_parent(&imsg);
465			break;
466		case IMSG_CTL_SHOW_RIB:
467		case IMSG_CTL_SHOW_RIB_PREFIX:
468			if (imsg_get_data(&imsg, &ribreq, sizeof(ribreq)) ==
469			    -1) {
470				log_warnx("got IMSG_CTL_SHOW_RIB with "
471				    "wrong length");
472				break;
473			}
474
475			/* check if at least one neighbor exists */
476			RB_FOREACH(p, peer_head, peers)
477				if (peer_matched(p, &ribreq.neighbor))
478					break;
479			if (p == NULL && RB_EMPTY(peers)) {
480				control_result(c, CTL_RES_NOSUCHPEER);
481				break;
482			}
483
484			if (type == IMSG_CTL_SHOW_RIB_PREFIX &&
485			    ribreq.prefix.aid == AID_UNSPEC) {
486				/* malformed request, must specify af */
487				control_result(c, CTL_RES_PARSE_ERROR);
488				break;
489			}
490
491			c->terminate = 1;
492			imsg_ctl_rde(&imsg);
493			break;
494		case IMSG_CTL_SHOW_NETWORK:
495		case IMSG_CTL_SHOW_FLOWSPEC:
496			c->terminate = 1;
497			/* FALLTHROUGH */
498		case IMSG_CTL_SHOW_RIB_MEM:
499		case IMSG_CTL_SHOW_SET:
500			imsg_ctl_rde(&imsg);
501			break;
502		case IMSG_NETWORK_ADD:
503		case IMSG_NETWORK_ASPATH:
504		case IMSG_NETWORK_ATTR:
505		case IMSG_NETWORK_REMOVE:
506		case IMSG_NETWORK_FLUSH:
507		case IMSG_NETWORK_DONE:
508		case IMSG_FLOWSPEC_ADD:
509		case IMSG_FLOWSPEC_REMOVE:
510		case IMSG_FLOWSPEC_DONE:
511		case IMSG_FLOWSPEC_FLUSH:
512		case IMSG_FILTER_SET:
513			imsg_ctl_rde(&imsg);
514			break;
515		case IMSG_CTL_LOG_VERBOSE:
516			if (imsg_get_data(&imsg, &verbose, sizeof(verbose)) ==
517			    -1)
518				break;
519
520			/* forward to other processes */
521			imsg_ctl_parent(&imsg);
522			imsg_ctl_rde(&imsg);
523			log_setverbose(verbose);
524			break;
525		default:
526			break;
527		}
528		imsg_free(&imsg);
529	}
530
531	return (0);
532}
533
534int
535control_imsg_relay(struct imsg *imsg, struct peer *p)
536{
537	struct ctl_conn	*c;
538	uint32_t type;
539	pid_t pid;
540
541	type = imsg_get_type(imsg);
542	pid = imsg_get_pid(imsg);
543
544	if ((c = control_connbypid(pid)) == NULL)
545		return (0);
546
547	/* special handling for peers since only the stats are sent from RDE */
548	if (type == IMSG_CTL_SHOW_NEIGHBOR) {
549		struct rde_peer_stats stats;
550
551		if (p == NULL) {
552			log_warnx("%s: no such peer: id=%u", __func__,
553			    imsg_get_id(imsg));
554			return (0);
555		}
556		if (imsg_get_data(imsg, &stats, sizeof(stats)) == -1) {
557			log_warnx("%s: imsg_get_data", __func__);
558			return (0);
559		}
560		p->stats.prefix_cnt = stats.prefix_cnt;
561		p->stats.prefix_out_cnt = stats.prefix_out_cnt;
562		p->stats.prefix_rcvd_update = stats.prefix_rcvd_update;
563		p->stats.prefix_rcvd_withdraw = stats.prefix_rcvd_withdraw;
564		p->stats.prefix_rcvd_eor = stats.prefix_rcvd_eor;
565		p->stats.prefix_sent_update = stats.prefix_sent_update;
566		p->stats.prefix_sent_withdraw = stats.prefix_sent_withdraw;
567		p->stats.prefix_sent_eor = stats.prefix_sent_eor;
568		p->stats.pending_update = stats.pending_update;
569		p->stats.pending_withdraw = stats.pending_withdraw;
570
571		return imsg_compose(&c->imsgbuf, type, 0, pid, -1,
572		    p, sizeof(*p));
573	}
574
575	/* if command finished no need to send exit message */
576	if (type == IMSG_CTL_END || type == IMSG_CTL_RESULT)
577		c->terminate = 0;
578
579	if (!c->throttled && c->imsgbuf.w.queued > CTL_MSG_HIGH_MARK) {
580		if (imsg_ctl_rde_msg(IMSG_XOFF, 0, pid) != -1)
581			c->throttled = 1;
582	}
583
584	return (imsg_forward(&c->imsgbuf, imsg));
585}
586
587void
588control_result(struct ctl_conn *c, u_int code)
589{
590	imsg_compose(&c->imsgbuf, IMSG_CTL_RESULT, 0, c->imsgbuf.pid, -1,
591	    &code, sizeof(code));
592}
593
594/* This should go into libutil, from smtpd/mproc.c */
595ssize_t
596imsg_read_nofd(struct imsgbuf *imsgbuf)
597{
598	ssize_t	 n;
599	char	*buf;
600	size_t	 len;
601
602	buf = imsgbuf->r.buf + imsgbuf->r.wpos;
603	len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos;
604
605	while ((n = recv(imsgbuf->fd, buf, len, 0)) == -1) {
606		if (errno != EINTR)
607			return (n);
608	}
609
610	imsgbuf->r.wpos += n;
611	return (n);
612}
613