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