control.c revision 1.115
1/*	$OpenBSD: control.c,v 1.115 2024/01/10 11:08: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->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
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->ibuf, 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->ibuf.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->ibuf.pid == pid)
212			break;
213	}
214
215	return (c);
216}
217
218int
219control_close(struct ctl_conn *c)
220{
221	if (c->terminate && c->ibuf.pid)
222		imsg_ctl_rde_msg(IMSG_CTL_TERMINATE, 0, c->ibuf.pid);
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
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->ibuf.w) <= 0 && errno != EAGAIN)
253			return control_close(c);
254		if (c->throttled && c->ibuf.w.queued < CTL_MSG_LOW_MARK) {
255			if (imsg_ctl_rde_msg(IMSG_XON, 0, c->ibuf.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->ibuf)) == -1 && errno != EAGAIN) ||
264	    n == 0)
265		return control_close(c);
266
267	for (;;) {
268		if ((n = imsg_get(&c->ibuf, &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->ibuf.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->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
316				    0, 0, -1, p, sizeof(struct peer));
317			imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
318			break;
319		case IMSG_CTL_SHOW_NEIGHBOR:
320			if (imsg_get_data(&imsg, &neighbor,
321			    sizeof(neighbor)) == -1)
322				memset(&neighbor, 0, sizeof(neighbor));
323
324			matched = 0;
325			RB_FOREACH(p, peer_head, peers) {
326				if (!peer_matched(p, &neighbor))
327					continue;
328
329				matched = 1;
330				if (!neighbor.show_timers) {
331					imsg_ctl_rde_msg(type,
332					    p->conf.id, pid);
333				} else {
334					u_int			 i;
335					time_t			 d;
336					struct ctl_timer	 ct;
337
338					imsg_compose(&c->ibuf,
339					    IMSG_CTL_SHOW_NEIGHBOR,
340					    0, 0, -1, p, sizeof(*p));
341					for (i = 1; i < Timer_Max; i++) {
342						if (!timer_running(&p->timers,
343						    i, &d))
344							continue;
345						ct.type = i;
346						ct.val = d;
347						imsg_compose(&c->ibuf,
348						    IMSG_CTL_SHOW_TIMER,
349						    0, 0, -1, &ct, sizeof(ct));
350					}
351				}
352			}
353			if (!matched && RB_EMPTY(peers)) {
354				control_result(c, CTL_RES_NOSUCHPEER);
355			} else if (!neighbor.show_timers) {
356				imsg_ctl_rde_msg(IMSG_CTL_END, 0, pid);
357			} else {
358				imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1,
359				    NULL, 0);
360			}
361			break;
362		case IMSG_CTL_NEIGHBOR_UP:
363		case IMSG_CTL_NEIGHBOR_DOWN:
364		case IMSG_CTL_NEIGHBOR_CLEAR:
365		case IMSG_CTL_NEIGHBOR_RREFRESH:
366		case IMSG_CTL_NEIGHBOR_DESTROY:
367			if (imsg_get_data(&imsg, &neighbor,
368			    sizeof(neighbor)) == -1) {
369				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
370				    "wrong length");
371				break;
372			}
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 (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					neighbor.reason[
393					    sizeof(neighbor.reason) - 1] = '\0';
394					strlcpy(p->conf.reason,
395					    neighbor.reason,
396					    sizeof(p->conf.reason));
397					p->conf.down = 1;
398					session_stop(p, ERR_CEASE_ADMIN_DOWN);
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					strlcpy(p->conf.reason,
405					    neighbor.reason,
406					    sizeof(p->conf.reason));
407					p->IdleHoldTime =
408					    INTERVAL_IDLE_HOLD_INITIAL;
409					p->errcnt = 0;
410					if (!p->conf.down) {
411						session_stop(p,
412						    ERR_CEASE_ADMIN_RESET);
413						timer_set(&p->timers,
414						    Timer_IdleHold,
415						    SESSION_CLEAR_DELAY);
416					} else {
417						session_stop(p,
418						    ERR_CEASE_ADMIN_DOWN);
419					}
420					control_result(c, CTL_RES_OK);
421					break;
422				case IMSG_CTL_NEIGHBOR_RREFRESH:
423					if (session_neighbor_rrefresh(p))
424						control_result(c,
425						    CTL_RES_NOCAP);
426					else
427						control_result(c, CTL_RES_OK);
428					break;
429				case IMSG_CTL_NEIGHBOR_DESTROY:
430					if (!p->template)
431						control_result(c,
432						    CTL_RES_BADPEER);
433					else if (p->state != STATE_IDLE)
434						control_result(c,
435						    CTL_RES_BADSTATE);
436					else {
437						/*
438						 * Mark as deleted, will be
439						 * collected on next poll loop.
440						 */
441						p->reconf_action =
442						    RECONF_DELETE;
443						control_result(c, CTL_RES_OK);
444					}
445					break;
446				default:
447					fatal("king bula wants more humppa");
448				}
449			}
450			if (!matched)
451				control_result(c, CTL_RES_NOSUCHPEER);
452			break;
453		case IMSG_CTL_RELOAD:
454		case IMSG_CTL_SHOW_INTERFACE:
455		case IMSG_CTL_SHOW_FIB_TABLES:
456		case IMSG_CTL_SHOW_RTR:
457			imsg_ctl_parent(&imsg);
458			break;
459		case IMSG_CTL_KROUTE:
460		case IMSG_CTL_KROUTE_ADDR:
461		case IMSG_CTL_SHOW_NEXTHOP:
462			imsg_ctl_parent(&imsg);
463			break;
464		case IMSG_CTL_SHOW_RIB:
465		case IMSG_CTL_SHOW_RIB_PREFIX:
466			if (imsg_get_data(&imsg, &ribreq, sizeof(ribreq)) ==
467			    -1) {
468				log_warnx("got IMSG_CTL_SHOW_RIB with "
469				    "wrong length");
470				break;
471			}
472
473			/* check if at least one neighbor exists */
474			RB_FOREACH(p, peer_head, peers)
475				if (peer_matched(p, &ribreq.neighbor))
476					break;
477			if (p == NULL && RB_EMPTY(peers)) {
478				control_result(c, CTL_RES_NOSUCHPEER);
479				break;
480			}
481
482			if (type == IMSG_CTL_SHOW_RIB_PREFIX &&
483			    ribreq.prefix.aid == AID_UNSPEC) {
484				/* malformed request, must specify af */
485				control_result(c, CTL_RES_PARSE_ERROR);
486				break;
487			}
488
489			c->terminate = 1;
490			imsg_ctl_rde(&imsg);
491			break;
492		case IMSG_CTL_SHOW_NETWORK:
493		case IMSG_CTL_SHOW_FLOWSPEC:
494			c->terminate = 1;
495			/* FALLTHROUGH */
496		case IMSG_CTL_SHOW_RIB_MEM:
497		case IMSG_CTL_SHOW_SET:
498			imsg_ctl_rde(&imsg);
499			break;
500		case IMSG_NETWORK_ADD:
501		case IMSG_NETWORK_ASPATH:
502		case IMSG_NETWORK_ATTR:
503		case IMSG_NETWORK_REMOVE:
504		case IMSG_NETWORK_FLUSH:
505		case IMSG_NETWORK_DONE:
506		case IMSG_FLOWSPEC_ADD:
507		case IMSG_FLOWSPEC_REMOVE:
508		case IMSG_FLOWSPEC_DONE:
509		case IMSG_FLOWSPEC_FLUSH:
510		case IMSG_FILTER_SET:
511			imsg_ctl_rde(&imsg);
512			break;
513		case IMSG_CTL_LOG_VERBOSE:
514			if (imsg_get_data(&imsg, &verbose, sizeof(verbose)) ==
515			    -1)
516				break;
517
518			/* forward to other processes */
519			imsg_ctl_parent(&imsg);
520			imsg_ctl_rde(&imsg);
521			log_setverbose(verbose);
522			break;
523		default:
524			break;
525		}
526		imsg_free(&imsg);
527	}
528
529	return (0);
530}
531
532int
533control_imsg_relay(struct imsg *imsg, struct peer *p)
534{
535	struct ctl_conn	*c;
536	uint32_t type;
537	pid_t pid;
538
539	type = imsg_get_type(imsg);
540	pid = imsg_get_pid(imsg);
541
542	if ((c = control_connbypid(pid)) == NULL)
543		return (0);
544
545	/* special handling for peers since only the stats are sent from RDE */
546	if (type == IMSG_CTL_SHOW_NEIGHBOR) {
547		struct rde_peer_stats stats;
548
549		if (p == NULL) {
550			log_warnx("%s: no such peer: id=%u", __func__,
551			    imsg_get_id(imsg));
552			return (0);
553		}
554		if (imsg_get_data(imsg, &stats, sizeof(stats)) == -1) {
555			log_warnx("%s: imsg_get_data", __func__);
556			return (0);
557		}
558		p->stats.prefix_cnt = stats.prefix_cnt;
559		p->stats.prefix_out_cnt = stats.prefix_out_cnt;
560		p->stats.prefix_rcvd_update = stats.prefix_rcvd_update;
561		p->stats.prefix_rcvd_withdraw = stats.prefix_rcvd_withdraw;
562		p->stats.prefix_rcvd_eor = stats.prefix_rcvd_eor;
563		p->stats.prefix_sent_update = stats.prefix_sent_update;
564		p->stats.prefix_sent_withdraw = stats.prefix_sent_withdraw;
565		p->stats.prefix_sent_eor = stats.prefix_sent_eor;
566		p->stats.pending_update = stats.pending_update;
567		p->stats.pending_withdraw = stats.pending_withdraw;
568
569		return imsg_compose(&c->ibuf, type, 0, pid, -1, 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->ibuf.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->ibuf, imsg));
582}
583
584void
585control_result(struct ctl_conn *c, u_int code)
586{
587	imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.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