xref: /openbsd-src/usr.sbin/bgpd/control.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
1 /*	$OpenBSD: control.c,v 1.63 2009/11/02 20:38:15 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 
33 struct ctl_conn	*control_connbyfd(int);
34 struct ctl_conn	*control_connbypid(pid_t);
35 int		 control_close(int);
36 void		 control_result(struct ctl_conn *, u_int);
37 
38 int
39 control_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("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 
90 int
91 control_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 
101 void
102 control_shutdown(int fd)
103 {
104 	close(fd);
105 }
106 
107 void
108 control_cleanup(const char *path)
109 {
110 	if (path)
111 		unlink(path);
112 }
113 
114 unsigned int
115 control_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("session_control_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("session_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 
146 struct ctl_conn *
147 control_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 
158 struct ctl_conn *
159 control_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 
170 int
171 control_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 
189 int
190 control_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 					session_stop(p, ERR_CEASE_ADMIN_RESET);
337 					timer_set(p, Timer_IdleHold,
338 					    SESSION_CLEAR_DELAY);
339 					control_result(c, CTL_RES_OK);
340 					break;
341 				case IMSG_CTL_NEIGHBOR_RREFRESH:
342 					if (session_neighbor_rrefresh(p))
343 						control_result(c,
344 						    CTL_RES_NOCAP);
345 					else
346 						control_result(c, CTL_RES_OK);
347 					break;
348 				default:
349 					fatal("king bula wants more humppa");
350 				}
351 			} else
352 				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
353 				    "wrong length");
354 			break;
355 		case IMSG_CTL_RELOAD:
356 		case IMSG_CTL_KROUTE:
357 		case IMSG_CTL_KROUTE_ADDR:
358 		case IMSG_CTL_SHOW_NEXTHOP:
359 		case IMSG_CTL_SHOW_INTERFACE:
360 			c->ibuf.pid = imsg.hdr.pid;
361 			imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
362 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
363 			break;
364 		case IMSG_CTL_SHOW_RIB:
365 		case IMSG_CTL_SHOW_RIB_AS:
366 		case IMSG_CTL_SHOW_RIB_PREFIX:
367 			if (imsg.hdr.len == IMSG_HEADER_SIZE +
368 			    sizeof(struct ctl_show_rib_request)) {
369 				ribreq = imsg.data;
370 				neighbor = &ribreq->neighbor;
371 				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
372 				ribreq->peerid = 0;
373 				p = NULL;
374 				if (neighbor->addr.af) {
375 					p = getpeerbyaddr(&neighbor->addr);
376 					if (p == NULL) {
377 						control_result(c,
378 						    CTL_RES_NOSUCHPEER);
379 						break;
380 					}
381 					ribreq->peerid = p->conf.id;
382 				} else if (neighbor->descr[0]) {
383 					p = getpeerbydesc(neighbor->descr);
384 					if (p == NULL) {
385 						control_result(c,
386 						    CTL_RES_NOSUCHPEER);
387 						break;
388 					}
389 					ribreq->peerid = p->conf.id;
390 				}
391 				if ((ribreq->flags & F_CTL_ADJ_IN) && p &&
392 				    !p->conf.softreconfig_in) {
393 					/*
394 					 * if no neighbor was specified we
395 					 * try our best.
396 					 */
397 					control_result(c, CTL_RES_NOCAP);
398 					break;
399 				}
400 				if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
401 				    && (ribreq->prefix.af != AF_INET)
402 				    && (ribreq->prefix.af != AF_INET6)) {
403 					/* malformed request, must specify af */
404 					control_result(c, CTL_RES_PARSE_ERROR);
405 					break;
406 				}
407 				c->ibuf.pid = imsg.hdr.pid;
408 				imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid,
409 				    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
410 			} else
411 				log_warnx("got IMSG_CTL_SHOW_RIB with "
412 				    "wrong length");
413 			break;
414 		case IMSG_CTL_SHOW_RIB_MEM:
415 		case IMSG_CTL_SHOW_RIB_COMMUNITY:
416 		case IMSG_CTL_SHOW_NETWORK:
417 			c->ibuf.pid = imsg.hdr.pid;
418 			imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid,
419 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
420 			break;
421 		case IMSG_NETWORK_ADD:
422 		case IMSG_NETWORK_REMOVE:
423 		case IMSG_NETWORK_FLUSH:
424 		case IMSG_NETWORK_DONE:
425 		case IMSG_FILTER_SET:
426 			imsg_compose_rde(imsg.hdr.type, 0,
427 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
428 			break;
429 		case IMSG_CTL_LOG_VERBOSE:
430 			if (imsg.hdr.len != IMSG_HEADER_SIZE +
431 			    sizeof(verbose))
432 				break;
433 
434 			/* forward to other porcesses */
435 			imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
436 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
437 			imsg_compose_rde(imsg.hdr.type, 0,
438 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
439 
440 			memcpy(&verbose, imsg.data, sizeof(verbose));
441 			log_verbose(verbose);
442 			break;
443 		default:
444 			break;
445 		}
446 		imsg_free(&imsg);
447 	}
448 
449 	return (0);
450 }
451 
452 int
453 control_imsg_relay(struct imsg *imsg)
454 {
455 	struct ctl_conn	*c;
456 
457 	if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
458 		return (0);
459 
460 	return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
461 	    imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
462 }
463 
464 void
465 control_result(struct ctl_conn *c, u_int code)
466 {
467 	imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1,
468 	    &code, sizeof(code));
469 }
470