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