xref: /openbsd-src/usr.sbin/bgpd/control.c (revision 6a13ef69787db04ae501a22e92fa10865b44fd7c)
1 /*	$OpenBSD: control.c,v 1.85 2017/01/13 18:59:12 phessler 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 ssize_t		 imsg_read_nofd(struct imsgbuf *);
38 
39 int
40 control_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 
95 int
96 control_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 
106 void
107 control_shutdown(int fd)
108 {
109 	close(fd);
110 }
111 
112 void
113 control_cleanup(const char *path)
114 {
115 	if (path)
116 		unlink(path);
117 }
118 
119 unsigned int
120 control_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 
154 struct ctl_conn *
155 control_connbyfd(int fd)
156 {
157 	struct ctl_conn	*c;
158 
159 	TAILQ_FOREACH(c, &ctl_conns, entry) {
160 		if (c->ibuf.fd == fd)
161 			break;
162 	}
163 
164 	return (c);
165 }
166 
167 struct ctl_conn *
168 control_connbypid(pid_t pid)
169 {
170 	struct ctl_conn	*c;
171 
172 	TAILQ_FOREACH(c, &ctl_conns, entry) {
173 		if (c->ibuf.pid == pid)
174 			break;
175 	}
176 
177 	return (c);
178 }
179 
180 int
181 control_close(int fd)
182 {
183 	struct ctl_conn	*c;
184 
185 	if ((c = control_connbyfd(fd)) == NULL) {
186 		log_warn("control_close: fd %d: not found", fd);
187 		return (0);
188 	}
189 
190 	msgbuf_clear(&c->ibuf.w);
191 	TAILQ_REMOVE(&ctl_conns, c, entry);
192 
193 	close(c->ibuf.fd);
194 	free(c);
195 	pauseaccept = 0;
196 	return (1);
197 }
198 
199 int
200 control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
201 {
202 	struct imsg		 imsg;
203 	struct ctl_conn		*c;
204 	ssize_t			 n;
205 	int			 verbose;
206 	struct peer		*p;
207 	struct ctl_neighbor	*neighbor;
208 	struct ctl_show_rib_request	*ribreq;
209 
210 	if ((c = control_connbyfd(pfd->fd)) == NULL) {
211 		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
212 		return (0);
213 	}
214 
215 	if (pfd->revents & POLLOUT)
216 		if (msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) {
217 			*ctl_cnt -= control_close(pfd->fd);
218 			return (1);
219 		}
220 
221 	if (!(pfd->revents & POLLIN))
222 		return (0);
223 
224 	if (((n = imsg_read_nofd(&c->ibuf)) == -1 && errno != EAGAIN) ||
225 	    n == 0) {
226 		*ctl_cnt -= control_close(pfd->fd);
227 		return (1);
228 	}
229 
230 	for (;;) {
231 		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
232 			*ctl_cnt -= control_close(pfd->fd);
233 			return (1);
234 		}
235 
236 		if (n == 0)
237 			break;
238 
239 		if (c->restricted) {
240 			switch (imsg.hdr.type) {
241 			case IMSG_CTL_SHOW_NEIGHBOR:
242 			case IMSG_CTL_SHOW_NEXTHOP:
243 			case IMSG_CTL_SHOW_INTERFACE:
244 			case IMSG_CTL_SHOW_RIB:
245 			case IMSG_CTL_SHOW_RIB_AS:
246 			case IMSG_CTL_SHOW_RIB_PREFIX:
247 			case IMSG_CTL_SHOW_RIB_MEM:
248 			case IMSG_CTL_SHOW_RIB_COMMUNITY:
249 			case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY:
250 			case IMSG_CTL_SHOW_NETWORK:
251 			case IMSG_CTL_SHOW_TERSE:
252 			case IMSG_CTL_SHOW_TIMER:
253 				break;
254 			default:
255 				/* clear imsg type to prevent processing */
256 				imsg.hdr.type = IMSG_NONE;
257 				control_result(c, CTL_RES_DENIED);
258 				break;
259 			}
260 		}
261 
262 		switch (imsg.hdr.type) {
263 		case IMSG_NONE:
264 			/* message was filtered out, nothing to do */
265 			break;
266 		case IMSG_CTL_SHOW_NEIGHBOR:
267 			c->ibuf.pid = imsg.hdr.pid;
268 			if (imsg.hdr.len == IMSG_HEADER_SIZE +
269 			    sizeof(struct ctl_neighbor)) {
270 				neighbor = imsg.data;
271 				p = getpeerbyaddr(&neighbor->addr);
272 				if (p == NULL)
273 					p = getpeerbydesc(neighbor->descr);
274 				if (p == NULL) {
275 					control_result(c, CTL_RES_NOSUCHPEER);
276 					break;
277 				}
278 				if (!neighbor->show_timers) {
279 					imsg_ctl_rde(imsg.hdr.type,
280 					    imsg.hdr.pid,
281 					    p, sizeof(struct peer));
282 					imsg_ctl_rde(IMSG_CTL_END,
283 					    imsg.hdr.pid, NULL, 0);
284 				} else {
285 					u_int			 i;
286 					time_t			 d;
287 					struct ctl_timer	 ct;
288 
289 					imsg_compose(&c->ibuf,
290 					    IMSG_CTL_SHOW_NEIGHBOR,
291 					    0, 0, -1, p, sizeof(*p));
292 					for (i = 1; i < Timer_Max; i++) {
293 						if (!timer_running(p, i, &d))
294 							continue;
295 						ct.type = i;
296 						ct.val = d;
297 						imsg_compose(&c->ibuf,
298 						    IMSG_CTL_SHOW_TIMER,
299 						    0, 0, -1, &ct, sizeof(ct));
300 					}
301 					imsg_compose(&c->ibuf, IMSG_CTL_END,
302 					    0, 0, -1, NULL, 0);
303 				}
304 			} else {
305 				for (p = peers; p != NULL; p = p->next)
306 					imsg_ctl_rde(imsg.hdr.type,
307 					    imsg.hdr.pid,
308 					    p, sizeof(struct peer));
309 				imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid,
310 					NULL, 0);
311 			}
312 			break;
313 		case IMSG_CTL_SHOW_TERSE:
314 			for (p = peers; p != NULL; p = p->next)
315 				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
316 				    0, 0, -1, p, sizeof(struct peer));
317 			imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
318 			break;
319 		case IMSG_CTL_FIB_COUPLE:
320 		case IMSG_CTL_FIB_DECOUPLE:
321 			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
322 			    0, NULL, 0);
323 			break;
324 		case IMSG_CTL_NEIGHBOR_UP:
325 		case IMSG_CTL_NEIGHBOR_DOWN:
326 		case IMSG_CTL_NEIGHBOR_CLEAR:
327 		case IMSG_CTL_NEIGHBOR_RREFRESH:
328 		case IMSG_CTL_NEIGHBOR_DESTROY:
329 			if (imsg.hdr.len == IMSG_HEADER_SIZE +
330 			    sizeof(struct ctl_neighbor)) {
331 				neighbor = imsg.data;
332 				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
333 				p = getpeerbyaddr(&neighbor->addr);
334 				if (p == NULL)
335 					p = getpeerbydesc(neighbor->descr);
336 				if (p == NULL) {
337 					control_result(c, CTL_RES_NOSUCHPEER);
338 					break;
339 				}
340 				switch (imsg.hdr.type) {
341 				case IMSG_CTL_NEIGHBOR_UP:
342 					bgp_fsm(p, EVNT_START);
343 					p->conf.down = 0;
344 					p->conf.shutcomm[0] = '\0';
345 					control_result(c, CTL_RES_OK);
346 					break;
347 				case IMSG_CTL_NEIGHBOR_DOWN:
348 					p->conf.down = 1;
349 					strlcpy(p->conf.shutcomm,
350 					    neighbor->shutcomm,
351 					    sizeof(neighbor->shutcomm));
352 					session_stop(p, ERR_CEASE_ADMIN_DOWN);
353 					control_result(c, CTL_RES_OK);
354 					break;
355 				case IMSG_CTL_NEIGHBOR_CLEAR:
356 					if (!p->conf.down) {
357 						session_stop(p,
358 						    ERR_CEASE_ADMIN_RESET);
359 						timer_set(p, Timer_IdleHold,
360 						    SESSION_CLEAR_DELAY);
361 					} else {
362 						session_stop(p,
363 						    ERR_CEASE_ADMIN_DOWN);
364 					}
365 					control_result(c, CTL_RES_OK);
366 					break;
367 				case IMSG_CTL_NEIGHBOR_RREFRESH:
368 					if (session_neighbor_rrefresh(p))
369 						control_result(c,
370 						    CTL_RES_NOCAP);
371 					else
372 						control_result(c, CTL_RES_OK);
373 					break;
374 				case IMSG_CTL_NEIGHBOR_DESTROY:
375 					if (!p->template)
376 						control_result(c,
377 						    CTL_RES_BADPEER);
378 					else if (p->state != STATE_IDLE)
379 						control_result(c,
380 						    CTL_RES_BADSTATE);
381 					else {
382 						/*
383 						 * Mark as deleted, will be
384 						 * collected on next poll loop.
385 						 */
386 						p->conf.reconf_action =
387 						    RECONF_DELETE;
388 						control_result(c, CTL_RES_OK);
389 					}
390 					break;
391 				default:
392 					fatal("king bula wants more humppa");
393 				}
394 			} else
395 				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
396 				    "wrong length");
397 			break;
398 		case IMSG_CTL_RELOAD:
399 		case IMSG_CTL_SHOW_INTERFACE:
400 		case IMSG_CTL_SHOW_FIB_TABLES:
401 			c->ibuf.pid = imsg.hdr.pid;
402 			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
403 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
404 			break;
405 		case IMSG_CTL_KROUTE:
406 		case IMSG_CTL_KROUTE_ADDR:
407 		case IMSG_CTL_SHOW_NEXTHOP:
408 			c->ibuf.pid = imsg.hdr.pid;
409 			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
410 			    imsg.hdr.pid, imsg.data, imsg.hdr.len -
411 			    IMSG_HEADER_SIZE);
412 			break;
413 		case IMSG_CTL_SHOW_RIB:
414 		case IMSG_CTL_SHOW_RIB_AS:
415 		case IMSG_CTL_SHOW_RIB_PREFIX:
416 			if (imsg.hdr.len == IMSG_HEADER_SIZE +
417 			    sizeof(struct ctl_show_rib_request)) {
418 				ribreq = imsg.data;
419 				neighbor = &ribreq->neighbor;
420 				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
421 				ribreq->peerid = 0;
422 				p = NULL;
423 				if (neighbor->addr.aid) {
424 					p = getpeerbyaddr(&neighbor->addr);
425 					if (p == NULL) {
426 						control_result(c,
427 						    CTL_RES_NOSUCHPEER);
428 						break;
429 					}
430 					ribreq->peerid = p->conf.id;
431 				} else if (neighbor->descr[0]) {
432 					p = getpeerbydesc(neighbor->descr);
433 					if (p == NULL) {
434 						control_result(c,
435 						    CTL_RES_NOSUCHPEER);
436 						break;
437 					}
438 					ribreq->peerid = p->conf.id;
439 				}
440 				if ((ribreq->flags &
441 				     (F_CTL_ADJ_OUT | F_CTL_ADJ_IN)) && !p) {
442 					/*
443 					 * both in and out tables are only
444 					 * meaningful if used on a single
445 					 * peer.
446 					 */
447 					control_result(c, CTL_RES_NOSUCHPEER);
448 					break;
449 				}
450 				if ((ribreq->flags & F_CTL_ADJ_IN) && p &&
451 				    !p->conf.softreconfig_in) {
452 					/*
453 					 * without softreconfig_in we do not
454 					 * have an Adj-RIB-In table
455 					 */
456 					control_result(c, CTL_RES_NOCAP);
457 					break;
458 				}
459 				if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
460 				    && (ribreq->prefix.aid == AID_UNSPEC)) {
461 					/* malformed request, must specify af */
462 					control_result(c, CTL_RES_PARSE_ERROR);
463 					break;
464 				}
465 				c->ibuf.pid = imsg.hdr.pid;
466 				imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
467 				    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
468 			} else
469 				log_warnx("got IMSG_CTL_SHOW_RIB with "
470 				    "wrong length");
471 			break;
472 		case IMSG_CTL_SHOW_RIB_MEM:
473 		case IMSG_CTL_SHOW_RIB_COMMUNITY:
474 		case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY:
475 		case IMSG_CTL_SHOW_NETWORK:
476 			c->ibuf.pid = imsg.hdr.pid;
477 			imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
478 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
479 			break;
480 		case IMSG_NETWORK_ADD:
481 		case IMSG_NETWORK_ASPATH:
482 		case IMSG_NETWORK_ATTR:
483 		case IMSG_NETWORK_REMOVE:
484 		case IMSG_NETWORK_FLUSH:
485 		case IMSG_NETWORK_DONE:
486 		case IMSG_FILTER_SET:
487 			imsg_ctl_rde(imsg.hdr.type, 0,
488 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
489 			break;
490 		case IMSG_CTL_LOG_VERBOSE:
491 			if (imsg.hdr.len != IMSG_HEADER_SIZE +
492 			    sizeof(verbose))
493 				break;
494 
495 			/* forward to other processes */
496 			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
497 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
498 			imsg_ctl_rde(imsg.hdr.type, 0,
499 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
500 
501 			memcpy(&verbose, imsg.data, sizeof(verbose));
502 			log_verbose(verbose);
503 			break;
504 		default:
505 			break;
506 		}
507 		imsg_free(&imsg);
508 	}
509 
510 	return (0);
511 }
512 
513 int
514 control_imsg_relay(struct imsg *imsg)
515 {
516 	struct ctl_conn	*c;
517 
518 	if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
519 		return (0);
520 
521 	return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
522 	    imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
523 }
524 
525 void
526 control_result(struct ctl_conn *c, u_int code)
527 {
528 	imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1,
529 	    &code, sizeof(code));
530 }
531 
532 /* This should go into libutil, from smtpd/mproc.c */
533 ssize_t
534 imsg_read_nofd(struct imsgbuf *ibuf)
535 {
536 	ssize_t	 n;
537 	char	*buf;
538 	size_t	 len;
539 
540 	buf = ibuf->r.buf + ibuf->r.wpos;
541 	len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
542 
543 	while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
544 		if (errno != EINTR)
545 			return (n);
546 	}
547 
548 	ibuf->r.wpos += n;
549 	return (n);
550 }
551