control.c revision 1.66
1/*	$OpenBSD: control.c,v 1.66 2009/12/03 19:22:53 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 != EWOULDBLOCK && errno != EINTR)
126			log_warn("control_accept: accept");
127		return (0);
128	}
129
130	session_socket_blockmode(connfd, BM_NONBLOCK);
131
132	if ((ctl_conn = malloc(sizeof(struct ctl_conn))) == NULL) {
133		log_warn("control_accept");
134		close(connfd);
135		return (0);
136	}
137
138	imsg_init(&ctl_conn->ibuf, connfd);
139	ctl_conn->restricted = restricted;
140
141	TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
142
143	return (1);
144}
145
146struct ctl_conn *
147control_connbyfd(int fd)
148{
149	struct ctl_conn	*c;
150
151	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd;
152	    c = TAILQ_NEXT(c, entry))
153		;	/* nothing */
154
155	return (c);
156}
157
158struct ctl_conn *
159control_connbypid(pid_t pid)
160{
161	struct ctl_conn	*c;
162
163	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid;
164	    c = TAILQ_NEXT(c, entry))
165		;	/* nothing */
166
167	return (c);
168}
169
170int
171control_close(int fd)
172{
173	struct ctl_conn	*c;
174
175	if ((c = control_connbyfd(fd)) == NULL) {
176		log_warn("control_close: fd %d: not found", fd);
177		return (0);
178	}
179
180	msgbuf_clear(&c->ibuf.w);
181	TAILQ_REMOVE(&ctl_conns, c, entry);
182
183	close(c->ibuf.fd);
184	free(c);
185
186	return (1);
187}
188
189int
190control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
191{
192	struct imsg		 imsg;
193	struct ctl_conn		*c;
194	ssize_t			 n;
195	int			 verbose;
196	struct peer		*p;
197	struct ctl_neighbor	*neighbor;
198	struct ctl_show_rib_request	*ribreq;
199
200	if ((c = control_connbyfd(pfd->fd)) == NULL) {
201		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
202		return (0);
203	}
204
205	if (pfd->revents & POLLOUT)
206		if (msgbuf_write(&c->ibuf.w) < 0) {
207			*ctl_cnt -= control_close(pfd->fd);
208			return (1);
209		}
210
211	if (!(pfd->revents & POLLIN))
212		return (0);
213
214	if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) {
215		*ctl_cnt -= control_close(pfd->fd);
216		return (1);
217	}
218
219	for (;;) {
220		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
221			*ctl_cnt -= control_close(pfd->fd);
222			return (1);
223		}
224
225		if (n == 0)
226			break;
227
228		if (c->restricted) {
229			switch (imsg.hdr.type) {
230			case IMSG_CTL_SHOW_NEIGHBOR:
231			case IMSG_CTL_SHOW_NEXTHOP:
232			case IMSG_CTL_SHOW_INTERFACE:
233			case IMSG_CTL_SHOW_RIB:
234			case IMSG_CTL_SHOW_RIB_AS:
235			case IMSG_CTL_SHOW_RIB_PREFIX:
236			case IMSG_CTL_SHOW_RIB_MEM:
237			case IMSG_CTL_SHOW_RIB_COMMUNITY:
238			case IMSG_CTL_SHOW_NETWORK:
239			case IMSG_CTL_SHOW_TERSE:
240			case IMSG_CTL_SHOW_TIMER:
241				break;
242			default:
243				/* clear imsg type to prevent processing */
244				imsg.hdr.type = IMSG_NONE;
245				control_result(c, CTL_RES_DENIED);
246				break;
247			}
248		}
249
250		switch (imsg.hdr.type) {
251		case IMSG_NONE:
252			/* message was filtered out, nothing to do */
253			break;
254		case IMSG_CTL_SHOW_NEIGHBOR:
255			c->ibuf.pid = imsg.hdr.pid;
256			if (imsg.hdr.len == IMSG_HEADER_SIZE +
257			    sizeof(struct ctl_neighbor)) {
258				neighbor = imsg.data;
259				p = getpeerbyaddr(&neighbor->addr);
260				if (p == NULL)
261					p = getpeerbydesc(neighbor->descr);
262				if (p == NULL) {
263					control_result(c, CTL_RES_NOSUCHPEER);
264					break;
265				}
266				if (!neighbor->show_timers) {
267					imsg_compose_rde(imsg.hdr.type,
268					    imsg.hdr.pid,
269					    p, sizeof(struct peer));
270					imsg_compose_rde(IMSG_CTL_END,
271					    imsg.hdr.pid, NULL, 0);
272				} else {
273					u_int			 i;
274					time_t			 d;
275					struct ctl_timer	 ct;
276
277					imsg_compose(&c->ibuf,
278					    IMSG_CTL_SHOW_NEIGHBOR,
279					    0, 0, -1, p, sizeof(*p));
280					for (i = 1; i < Timer_Max; i++) {
281						if (!timer_running(p, i, &d))
282							continue;
283						ct.type = i;
284						ct.val = d;
285						imsg_compose(&c->ibuf,
286						    IMSG_CTL_SHOW_TIMER,
287						    0, 0, -1, &ct, sizeof(ct));
288					}
289					imsg_compose(&c->ibuf, IMSG_CTL_END,
290					    0, 0, -1, NULL, 0);
291				}
292			} else {
293				for (p = peers; p != NULL; p = p->next)
294					imsg_compose_rde(imsg.hdr.type,
295					    imsg.hdr.pid,
296					    p, sizeof(struct peer));
297				imsg_compose_rde(IMSG_CTL_END, imsg.hdr.pid,
298					NULL, 0);
299			}
300			break;
301		case IMSG_CTL_SHOW_TERSE:
302			for (p = peers; p != NULL; p = p->next)
303				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
304				    0, 0, -1, p, sizeof(struct peer));
305			imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
306			break;
307		case IMSG_CTL_FIB_COUPLE:
308		case IMSG_CTL_FIB_DECOUPLE:
309			imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
310			break;
311		case IMSG_CTL_NEIGHBOR_UP:
312		case IMSG_CTL_NEIGHBOR_DOWN:
313		case IMSG_CTL_NEIGHBOR_CLEAR:
314		case IMSG_CTL_NEIGHBOR_RREFRESH:
315			if (imsg.hdr.len == IMSG_HEADER_SIZE +
316			    sizeof(struct ctl_neighbor)) {
317				neighbor = imsg.data;
318				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
319				p = getpeerbyaddr(&neighbor->addr);
320				if (p == NULL)
321					p = getpeerbydesc(neighbor->descr);
322				if (p == NULL) {
323					control_result(c, CTL_RES_NOSUCHPEER);
324					break;
325				}
326				switch (imsg.hdr.type) {
327				case IMSG_CTL_NEIGHBOR_UP:
328					bgp_fsm(p, EVNT_START);
329					control_result(c, CTL_RES_OK);
330					break;
331				case IMSG_CTL_NEIGHBOR_DOWN:
332					session_stop(p, ERR_CEASE_ADMIN_DOWN);
333					control_result(c, CTL_RES_OK);
334					break;
335				case IMSG_CTL_NEIGHBOR_CLEAR:
336					if (!p->conf.down) {
337						session_stop(p,
338						    ERR_CEASE_ADMIN_RESET);
339						timer_set(p, Timer_IdleHold,
340						    SESSION_CLEAR_DELAY);
341					} else {
342						session_stop(p,
343						    ERR_CEASE_ADMIN_DOWN);
344					}
345					control_result(c, CTL_RES_OK);
346					break;
347				case IMSG_CTL_NEIGHBOR_RREFRESH:
348					if (session_neighbor_rrefresh(p))
349						control_result(c,
350						    CTL_RES_NOCAP);
351					else
352						control_result(c, CTL_RES_OK);
353					break;
354				default:
355					fatal("king bula wants more humppa");
356				}
357			} else
358				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
359				    "wrong length");
360			break;
361		case IMSG_CTL_RELOAD:
362		case IMSG_CTL_KROUTE:
363		case IMSG_CTL_KROUTE_ADDR:
364		case IMSG_CTL_SHOW_NEXTHOP:
365		case IMSG_CTL_SHOW_INTERFACE:
366			c->ibuf.pid = imsg.hdr.pid;
367			imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
368			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
369			break;
370		case IMSG_CTL_SHOW_RIB:
371		case IMSG_CTL_SHOW_RIB_AS:
372		case IMSG_CTL_SHOW_RIB_PREFIX:
373			if (imsg.hdr.len == IMSG_HEADER_SIZE +
374			    sizeof(struct ctl_show_rib_request)) {
375				ribreq = imsg.data;
376				neighbor = &ribreq->neighbor;
377				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
378				ribreq->peerid = 0;
379				p = NULL;
380				if (neighbor->addr.aid) {
381					p = getpeerbyaddr(&neighbor->addr);
382					if (p == NULL) {
383						control_result(c,
384						    CTL_RES_NOSUCHPEER);
385						break;
386					}
387					ribreq->peerid = p->conf.id;
388				} else if (neighbor->descr[0]) {
389					p = getpeerbydesc(neighbor->descr);
390					if (p == NULL) {
391						control_result(c,
392						    CTL_RES_NOSUCHPEER);
393						break;
394					}
395					ribreq->peerid = p->conf.id;
396				}
397				if ((ribreq->flags & F_CTL_ADJ_IN) && p &&
398				    !p->conf.softreconfig_in) {
399					/*
400					 * if no neighbor was specified we
401					 * try our best.
402					 */
403					control_result(c, CTL_RES_NOCAP);
404					break;
405				}
406				if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
407				    && (ribreq->prefix.aid != AID_INET)
408				    && (ribreq->prefix.aid != AID_INET6)) {
409					/* malformed request, must specify af */
410					control_result(c, CTL_RES_PARSE_ERROR);
411					break;
412				}
413				c->ibuf.pid = imsg.hdr.pid;
414				imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid,
415				    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
416			} else
417				log_warnx("got IMSG_CTL_SHOW_RIB with "
418				    "wrong length");
419			break;
420		case IMSG_CTL_SHOW_RIB_MEM:
421		case IMSG_CTL_SHOW_RIB_COMMUNITY:
422		case IMSG_CTL_SHOW_NETWORK:
423			c->ibuf.pid = imsg.hdr.pid;
424			imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid,
425			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
426			break;
427		case IMSG_NETWORK_ADD:
428		case IMSG_NETWORK_REMOVE:
429		case IMSG_NETWORK_FLUSH:
430		case IMSG_NETWORK_DONE:
431		case IMSG_FILTER_SET:
432			imsg_compose_rde(imsg.hdr.type, 0,
433			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
434			break;
435		case IMSG_CTL_LOG_VERBOSE:
436			if (imsg.hdr.len != IMSG_HEADER_SIZE +
437			    sizeof(verbose))
438				break;
439
440			/* forward to other porcesses */
441			imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
442			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
443			imsg_compose_rde(imsg.hdr.type, 0,
444			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
445
446			memcpy(&verbose, imsg.data, sizeof(verbose));
447			log_verbose(verbose);
448			break;
449		default:
450			break;
451		}
452		imsg_free(&imsg);
453	}
454
455	return (0);
456}
457
458int
459control_imsg_relay(struct imsg *imsg)
460{
461	struct ctl_conn	*c;
462
463	if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
464		return (0);
465
466	return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
467	    imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
468}
469
470void
471control_result(struct ctl_conn *c, u_int code)
472{
473	imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1,
474	    &code, sizeof(code));
475}
476