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