control.c revision 1.81
1/*	$OpenBSD: control.c,v 1.81 2015/12/05 13:10:32 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
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_NETWORK:
248			case IMSG_CTL_SHOW_TERSE:
249			case IMSG_CTL_SHOW_TIMER:
250				break;
251			default:
252				/* clear imsg type to prevent processing */
253				imsg.hdr.type = IMSG_NONE;
254				control_result(c, CTL_RES_DENIED);
255				break;
256			}
257		}
258
259		switch (imsg.hdr.type) {
260		case IMSG_NONE:
261			/* message was filtered out, nothing to do */
262			break;
263		case IMSG_CTL_SHOW_NEIGHBOR:
264			c->ibuf.pid = imsg.hdr.pid;
265			if (imsg.hdr.len == IMSG_HEADER_SIZE +
266			    sizeof(struct ctl_neighbor)) {
267				neighbor = imsg.data;
268				p = getpeerbyaddr(&neighbor->addr);
269				if (p == NULL)
270					p = getpeerbydesc(neighbor->descr);
271				if (p == NULL) {
272					control_result(c, CTL_RES_NOSUCHPEER);
273					break;
274				}
275				if (!neighbor->show_timers) {
276					imsg_ctl_rde(imsg.hdr.type,
277					    imsg.hdr.pid,
278					    p, sizeof(struct peer));
279					imsg_ctl_rde(IMSG_CTL_END,
280					    imsg.hdr.pid, NULL, 0);
281				} else {
282					u_int			 i;
283					time_t			 d;
284					struct ctl_timer	 ct;
285
286					imsg_compose(&c->ibuf,
287					    IMSG_CTL_SHOW_NEIGHBOR,
288					    0, 0, -1, p, sizeof(*p));
289					for (i = 1; i < Timer_Max; i++) {
290						if (!timer_running(p, i, &d))
291							continue;
292						ct.type = i;
293						ct.val = d;
294						imsg_compose(&c->ibuf,
295						    IMSG_CTL_SHOW_TIMER,
296						    0, 0, -1, &ct, sizeof(ct));
297					}
298					imsg_compose(&c->ibuf, IMSG_CTL_END,
299					    0, 0, -1, NULL, 0);
300				}
301			} else {
302				for (p = peers; p != NULL; p = p->next)
303					imsg_ctl_rde(imsg.hdr.type,
304					    imsg.hdr.pid,
305					    p, sizeof(struct peer));
306				imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid,
307					NULL, 0);
308			}
309			break;
310		case IMSG_CTL_SHOW_TERSE:
311			for (p = peers; p != NULL; p = p->next)
312				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
313				    0, 0, -1, p, sizeof(struct peer));
314			imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
315			break;
316		case IMSG_CTL_FIB_COUPLE:
317		case IMSG_CTL_FIB_DECOUPLE:
318			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
319			    0, NULL, 0);
320			break;
321		case IMSG_CTL_NEIGHBOR_UP:
322		case IMSG_CTL_NEIGHBOR_DOWN:
323		case IMSG_CTL_NEIGHBOR_CLEAR:
324		case IMSG_CTL_NEIGHBOR_RREFRESH:
325		case IMSG_CTL_NEIGHBOR_DESTROY:
326			if (imsg.hdr.len == IMSG_HEADER_SIZE +
327			    sizeof(struct ctl_neighbor)) {
328				neighbor = imsg.data;
329				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
330				p = getpeerbyaddr(&neighbor->addr);
331				if (p == NULL)
332					p = getpeerbydesc(neighbor->descr);
333				if (p == NULL) {
334					control_result(c, CTL_RES_NOSUCHPEER);
335					break;
336				}
337				switch (imsg.hdr.type) {
338				case IMSG_CTL_NEIGHBOR_UP:
339					bgp_fsm(p, EVNT_START);
340					control_result(c, CTL_RES_OK);
341					break;
342				case IMSG_CTL_NEIGHBOR_DOWN:
343					session_stop(p, ERR_CEASE_ADMIN_DOWN);
344					control_result(c, CTL_RES_OK);
345					break;
346				case IMSG_CTL_NEIGHBOR_CLEAR:
347					if (!p->conf.down) {
348						session_stop(p,
349						    ERR_CEASE_ADMIN_RESET);
350						timer_set(p, Timer_IdleHold,
351						    SESSION_CLEAR_DELAY);
352					} else {
353						session_stop(p,
354						    ERR_CEASE_ADMIN_DOWN);
355					}
356					control_result(c, CTL_RES_OK);
357					break;
358				case IMSG_CTL_NEIGHBOR_RREFRESH:
359					if (session_neighbor_rrefresh(p))
360						control_result(c,
361						    CTL_RES_NOCAP);
362					else
363						control_result(c, CTL_RES_OK);
364					break;
365				case IMSG_CTL_NEIGHBOR_DESTROY:
366					if (!p->template)
367						control_result(c,
368						    CTL_RES_BADPEER);
369					else if (p->state != STATE_IDLE)
370						control_result(c,
371						    CTL_RES_BADSTATE);
372					else {
373						/*
374					 	 * Mark as deleted, will be
375						 * collected on next poll loop.
376						 */
377						p->conf.reconf_action =
378						    RECONF_DELETE;
379						control_result(c, CTL_RES_OK);
380					}
381					break;
382				default:
383					fatal("king bula wants more humppa");
384				}
385			} else
386				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
387				    "wrong length");
388			break;
389		case IMSG_CTL_RELOAD:
390		case IMSG_CTL_SHOW_INTERFACE:
391		case IMSG_CTL_SHOW_FIB_TABLES:
392			c->ibuf.pid = imsg.hdr.pid;
393			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
394			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
395			break;
396		case IMSG_CTL_KROUTE:
397		case IMSG_CTL_KROUTE_ADDR:
398		case IMSG_CTL_SHOW_NEXTHOP:
399			c->ibuf.pid = imsg.hdr.pid;
400			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
401			    imsg.hdr.pid, imsg.data, imsg.hdr.len -
402			    IMSG_HEADER_SIZE);
403			break;
404		case IMSG_CTL_SHOW_RIB:
405		case IMSG_CTL_SHOW_RIB_AS:
406		case IMSG_CTL_SHOW_RIB_PREFIX:
407			if (imsg.hdr.len == IMSG_HEADER_SIZE +
408			    sizeof(struct ctl_show_rib_request)) {
409				ribreq = imsg.data;
410				neighbor = &ribreq->neighbor;
411				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
412				ribreq->peerid = 0;
413				p = NULL;
414				if (neighbor->addr.aid) {
415					p = getpeerbyaddr(&neighbor->addr);
416					if (p == NULL) {
417						control_result(c,
418						    CTL_RES_NOSUCHPEER);
419						break;
420					}
421					ribreq->peerid = p->conf.id;
422				} else if (neighbor->descr[0]) {
423					p = getpeerbydesc(neighbor->descr);
424					if (p == NULL) {
425						control_result(c,
426						    CTL_RES_NOSUCHPEER);
427						break;
428					}
429					ribreq->peerid = p->conf.id;
430				}
431				if ((ribreq->flags &
432				     (F_CTL_ADJ_OUT | F_CTL_ADJ_IN)) && !p) {
433					/*
434					 * both in and out tables are only
435					 * meaningful if used on a single
436                                         * peer.
437					 */
438					control_result(c, CTL_RES_NOSUCHPEER);
439					break;
440				}
441				if ((ribreq->flags & F_CTL_ADJ_IN) && p &&
442				    !p->conf.softreconfig_in) {
443					/*
444					 * without softreconfig_in we do not
445					 * have an Adj-RIB-In table
446					 */
447					control_result(c, CTL_RES_NOCAP);
448					break;
449				}
450				if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
451				    && (ribreq->prefix.aid == AID_UNSPEC)) {
452					/* malformed request, must specify af */
453					control_result(c, CTL_RES_PARSE_ERROR);
454					break;
455				}
456				c->ibuf.pid = imsg.hdr.pid;
457				imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
458				    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
459			} else
460				log_warnx("got IMSG_CTL_SHOW_RIB with "
461				    "wrong length");
462			break;
463		case IMSG_CTL_SHOW_RIB_MEM:
464		case IMSG_CTL_SHOW_RIB_COMMUNITY:
465		case IMSG_CTL_SHOW_NETWORK:
466			c->ibuf.pid = imsg.hdr.pid;
467			imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
468			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
469			break;
470		case IMSG_NETWORK_ADD:
471		case IMSG_NETWORK_ASPATH:
472		case IMSG_NETWORK_ATTR:
473		case IMSG_NETWORK_REMOVE:
474		case IMSG_NETWORK_FLUSH:
475		case IMSG_NETWORK_DONE:
476		case IMSG_FILTER_SET:
477			imsg_ctl_rde(imsg.hdr.type, 0,
478			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
479			break;
480		case IMSG_CTL_LOG_VERBOSE:
481			if (imsg.hdr.len != IMSG_HEADER_SIZE +
482			    sizeof(verbose))
483				break;
484
485			/* forward to other processes */
486			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
487			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
488			imsg_ctl_rde(imsg.hdr.type, 0,
489			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
490
491			memcpy(&verbose, imsg.data, sizeof(verbose));
492			log_verbose(verbose);
493			break;
494		default:
495			break;
496		}
497		imsg_free(&imsg);
498	}
499
500	return (0);
501}
502
503int
504control_imsg_relay(struct imsg *imsg)
505{
506	struct ctl_conn	*c;
507
508	if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
509		return (0);
510
511	return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
512	    imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
513}
514
515void
516control_result(struct ctl_conn *c, u_int code)
517{
518	imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1,
519	    &code, sizeof(code));
520}
521
522/* This should go into libutil, from smtpd/mproc.c */
523ssize_t
524imsg_read_nofd(struct imsgbuf *ibuf)
525{
526	ssize_t	 n;
527	char	*buf;
528	size_t	 len;
529
530	buf = ibuf->r.buf + ibuf->r.wpos;
531	len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
532
533	while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
534		if (errno != EINTR)
535			return (n);
536	}
537
538        ibuf->r.wpos += n;
539        return (n);
540}
541