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