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