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