xref: /openbsd-src/usr.sbin/bgpd/control.c (revision 505ee9ea3b177e2387d907a91ca7da069f3f14d8)
1 /*	$OpenBSD: control.c,v 1.100 2020/05/10 13:38:46 deraadt 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 #include "log.h"
31 
32 #define	CONTROL_BACKLOG	5
33 
34 struct ctl_conn	*control_connbyfd(int);
35 struct ctl_conn	*control_connbypid(pid_t);
36 int		 control_close(int);
37 void		 control_result(struct ctl_conn *, u_int);
38 ssize_t		 imsg_read_nofd(struct imsgbuf *);
39 
40 int
41 control_check(char *path)
42 {
43 	struct sockaddr_un	 sun;
44 	int			 fd;
45 
46 	bzero(&sun, sizeof(sun));
47 	sun.sun_family = AF_UNIX;
48 	strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
49 
50 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
51 		log_warn("%s: socket", __func__);
52 		return (-1);
53 	}
54 
55 	if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
56 		log_warnx("control socket %s already in use", path);
57 		close(fd);
58 		return (-1);
59 	}
60 
61 	close(fd);
62 
63 	return (0);
64 }
65 
66 int
67 control_init(int restricted, char *path)
68 {
69 	struct sockaddr_un	 sun;
70 	int			 fd;
71 	mode_t			 old_umask, mode;
72 
73 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
74 	     0)) == -1) {
75 		log_warn("control_init: socket");
76 		return (-1);
77 	}
78 
79 	bzero(&sun, sizeof(sun));
80 	sun.sun_family = AF_UNIX;
81 	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
82 	    sizeof(sun.sun_path)) {
83 		log_warn("control_init: socket name too long");
84 		close(fd);
85 		return (-1);
86 	}
87 
88 	if (unlink(path) == -1)
89 		if (errno != ENOENT) {
90 			log_warn("control_init: unlink %s", path);
91 			close(fd);
92 			return (-1);
93 		}
94 
95 	if (restricted) {
96 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
97 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
98 	} else {
99 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
100 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
101 	}
102 
103 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
104 		log_warn("control_init: bind: %s", path);
105 		close(fd);
106 		umask(old_umask);
107 		return (-1);
108 	}
109 
110 	umask(old_umask);
111 
112 	if (chmod(path, mode) == -1) {
113 		log_warn("control_init: chmod: %s", path);
114 		close(fd);
115 		unlink(path);
116 		return (-1);
117 	}
118 
119 	return (fd);
120 }
121 
122 int
123 control_listen(int fd)
124 {
125 	if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) {
126 		log_warn("control_listen: listen");
127 		return (-1);
128 	}
129 
130 	return (0);
131 }
132 
133 void
134 control_shutdown(int fd)
135 {
136 	close(fd);
137 }
138 
139 unsigned int
140 control_accept(int listenfd, int restricted)
141 {
142 	int			 connfd;
143 	socklen_t		 len;
144 	struct sockaddr_un	 sun;
145 	struct ctl_conn		*ctl_conn;
146 
147 	len = sizeof(sun);
148 	if ((connfd = accept4(listenfd,
149 	    (struct sockaddr *)&sun, &len,
150 	    SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) {
151 		if (errno == ENFILE || errno == EMFILE) {
152 			pauseaccept = getmonotime();
153 			return (0);
154 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
155 		    errno != ECONNABORTED)
156 			log_warn("control_accept: accept");
157 		return (0);
158 	}
159 
160 	if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
161 		log_warn("control_accept");
162 		close(connfd);
163 		return (0);
164 	}
165 
166 	imsg_init(&ctl_conn->ibuf, connfd);
167 	ctl_conn->restricted = restricted;
168 
169 	TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
170 
171 	return (1);
172 }
173 
174 struct ctl_conn *
175 control_connbyfd(int fd)
176 {
177 	struct ctl_conn	*c;
178 
179 	TAILQ_FOREACH(c, &ctl_conns, entry) {
180 		if (c->ibuf.fd == fd)
181 			break;
182 	}
183 
184 	return (c);
185 }
186 
187 struct ctl_conn *
188 control_connbypid(pid_t pid)
189 {
190 	struct ctl_conn	*c;
191 
192 	TAILQ_FOREACH(c, &ctl_conns, entry) {
193 		if (c->ibuf.pid == pid)
194 			break;
195 	}
196 
197 	return (c);
198 }
199 
200 int
201 control_close(int fd)
202 {
203 	struct ctl_conn	*c;
204 
205 	if ((c = control_connbyfd(fd)) == NULL) {
206 		log_warn("control_close: fd %d: not found", fd);
207 		return (0);
208 	}
209 
210 	if (c->terminate && c->ibuf.pid)
211 		imsg_ctl_rde(IMSG_CTL_TERMINATE, c->ibuf.pid, NULL, 0);
212 
213 	msgbuf_clear(&c->ibuf.w);
214 	TAILQ_REMOVE(&ctl_conns, c, entry);
215 
216 	close(c->ibuf.fd);
217 	free(c);
218 	pauseaccept = 0;
219 	return (1);
220 }
221 
222 int
223 control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt,
224     struct peer_head *peers)
225 {
226 	struct imsg		 imsg;
227 	struct ctl_conn		*c;
228 	ssize_t			 n;
229 	int			 verbose, matched;
230 	struct peer		*p;
231 	struct ctl_neighbor	*neighbor;
232 	struct ctl_show_rib_request	*ribreq;
233 
234 	if ((c = control_connbyfd(pfd->fd)) == NULL) {
235 		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
236 		return (0);
237 	}
238 
239 	if (pfd->revents & POLLOUT) {
240 		if (msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) {
241 			*ctl_cnt -= control_close(pfd->fd);
242 			return (1);
243 		}
244 		if (c->throttled && c->ibuf.w.queued < CTL_MSG_LOW_MARK) {
245 			if (imsg_ctl_rde(IMSG_XON, c->ibuf.pid, NULL, 0) != -1)
246 				c->throttled = 0;
247 		}
248 	}
249 
250 	if (!(pfd->revents & POLLIN))
251 		return (0);
252 
253 	if (((n = imsg_read_nofd(&c->ibuf)) == -1 && errno != EAGAIN) ||
254 	    n == 0) {
255 		*ctl_cnt -= control_close(pfd->fd);
256 		return (1);
257 	}
258 
259 	for (;;) {
260 		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
261 			*ctl_cnt -= control_close(pfd->fd);
262 			return (1);
263 		}
264 
265 		if (n == 0)
266 			break;
267 
268 		if (c->restricted) {
269 			switch (imsg.hdr.type) {
270 			case IMSG_CTL_SHOW_NEIGHBOR:
271 			case IMSG_CTL_SHOW_NEXTHOP:
272 			case IMSG_CTL_SHOW_INTERFACE:
273 			case IMSG_CTL_SHOW_RIB_MEM:
274 			case IMSG_CTL_SHOW_TERSE:
275 			case IMSG_CTL_SHOW_TIMER:
276 			case IMSG_CTL_SHOW_NETWORK:
277 			case IMSG_CTL_SHOW_RIB:
278 			case IMSG_CTL_SHOW_RIB_PREFIX:
279 				break;
280 			default:
281 				/* clear imsg type to prevent processing */
282 				imsg.hdr.type = IMSG_NONE;
283 				control_result(c, CTL_RES_DENIED);
284 				break;
285 			}
286 		}
287 
288 		switch (imsg.hdr.type) {
289 		case IMSG_NONE:
290 			/* message was filtered out, nothing to do */
291 			break;
292 		case IMSG_CTL_FIB_COUPLE:
293 		case IMSG_CTL_FIB_DECOUPLE:
294 			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
295 			    0, NULL, 0);
296 			break;
297 		case IMSG_CTL_SHOW_TERSE:
298 			RB_FOREACH(p, peer_head, peers)
299 				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
300 				    0, 0, -1, p, sizeof(struct peer));
301 			imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
302 			break;
303 		case IMSG_CTL_SHOW_NEIGHBOR:
304 			c->ibuf.pid = imsg.hdr.pid;
305 
306 			if (imsg.hdr.len == IMSG_HEADER_SIZE +
307 			    sizeof(struct ctl_neighbor)) {
308 				neighbor = imsg.data;
309 				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
310 			} else {
311 				neighbor = NULL;
312 			}
313 			matched = 0;
314 			RB_FOREACH(p, peer_head, peers) {
315 				if (!peer_matched(p, neighbor))
316 					continue;
317 
318 				matched = 1;
319 				if (!neighbor || !neighbor->show_timers) {
320 					imsg_ctl_rde(imsg.hdr.type,
321 					    imsg.hdr.pid,
322 					    p, sizeof(struct peer));
323 				} else {
324 					u_int			 i;
325 					time_t			 d;
326 					struct ctl_timer	 ct;
327 
328 					imsg_compose(&c->ibuf,
329 					    IMSG_CTL_SHOW_NEIGHBOR,
330 					    0, 0, -1, p, sizeof(*p));
331 					for (i = 1; i < Timer_Max; i++) {
332 						if (!timer_running(p, i, &d))
333 							continue;
334 						ct.type = i;
335 						ct.val = d;
336 						imsg_compose(&c->ibuf,
337 						    IMSG_CTL_SHOW_TIMER,
338 						    0, 0, -1, &ct, sizeof(ct));
339 					}
340 				}
341 			}
342 			if (!matched && RB_EMPTY(peers)) {
343 				control_result(c, CTL_RES_NOSUCHPEER);
344 			} else if (!neighbor || !neighbor->show_timers) {
345 				imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid,
346 				    NULL, 0);
347 			} else {
348 				imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1,
349 				    NULL, 0);
350 			}
351 			break;
352 		case IMSG_CTL_NEIGHBOR_UP:
353 		case IMSG_CTL_NEIGHBOR_DOWN:
354 		case IMSG_CTL_NEIGHBOR_CLEAR:
355 		case IMSG_CTL_NEIGHBOR_RREFRESH:
356 		case IMSG_CTL_NEIGHBOR_DESTROY:
357 			if (imsg.hdr.len != IMSG_HEADER_SIZE +
358 			    sizeof(struct ctl_neighbor)) {
359 				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
360 				    "wrong length");
361 				break;
362 			}
363 
364 			neighbor = imsg.data;
365 			neighbor->descr[PEER_DESCR_LEN - 1] = 0;
366 
367 			matched = 0;
368 			RB_FOREACH(p, peer_head, peers) {
369 				if (!peer_matched(p, neighbor))
370 					continue;
371 
372 				matched = 1;
373 
374 				switch (imsg.hdr.type) {
375 				case IMSG_CTL_NEIGHBOR_UP:
376 					bgp_fsm(p, EVNT_START);
377 					p->conf.down = 0;
378 					p->conf.reason[0] = '\0';
379 					p->IdleHoldTime =
380 					    INTERVAL_IDLE_HOLD_INITIAL;
381 					p->errcnt = 0;
382 					control_result(c, CTL_RES_OK);
383 					break;
384 				case IMSG_CTL_NEIGHBOR_DOWN:
385 					p->conf.down = 1;
386 					strlcpy(p->conf.reason,
387 					    neighbor->reason,
388 					    sizeof(neighbor->reason));
389 					session_stop(p, ERR_CEASE_ADMIN_DOWN);
390 					control_result(c, CTL_RES_OK);
391 					break;
392 				case IMSG_CTL_NEIGHBOR_CLEAR:
393 					strlcpy(p->conf.reason,
394 					    neighbor->reason,
395 					    sizeof(neighbor->reason));
396 					p->IdleHoldTime =
397 					    INTERVAL_IDLE_HOLD_INITIAL;
398 					p->errcnt = 0;
399 					if (!p->conf.down) {
400 						session_stop(p,
401 						    ERR_CEASE_ADMIN_RESET);
402 						timer_set(p, Timer_IdleHold,
403 						    SESSION_CLEAR_DELAY);
404 					} else {
405 						session_stop(p,
406 						    ERR_CEASE_ADMIN_DOWN);
407 					}
408 					control_result(c, CTL_RES_OK);
409 					break;
410 				case IMSG_CTL_NEIGHBOR_RREFRESH:
411 					if (session_neighbor_rrefresh(p))
412 						control_result(c,
413 						    CTL_RES_NOCAP);
414 					else
415 						control_result(c, CTL_RES_OK);
416 					break;
417 				case IMSG_CTL_NEIGHBOR_DESTROY:
418 					if (!p->template)
419 						control_result(c,
420 						    CTL_RES_BADPEER);
421 					else if (p->state != STATE_IDLE)
422 						control_result(c,
423 						    CTL_RES_BADSTATE);
424 					else {
425 						/*
426 						 * Mark as deleted, will be
427 						 * collected on next poll loop.
428 						 */
429 						p->reconf_action =
430 						    RECONF_DELETE;
431 						control_result(c, CTL_RES_OK);
432 					}
433 					break;
434 				default:
435 					fatal("king bula wants more humppa");
436 				}
437 			}
438 			if (!matched)
439 				control_result(c, CTL_RES_NOSUCHPEER);
440 			break;
441 		case IMSG_CTL_RELOAD:
442 		case IMSG_CTL_SHOW_INTERFACE:
443 		case IMSG_CTL_SHOW_FIB_TABLES:
444 			c->ibuf.pid = imsg.hdr.pid;
445 			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
446 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
447 			break;
448 		case IMSG_CTL_KROUTE:
449 		case IMSG_CTL_KROUTE_ADDR:
450 		case IMSG_CTL_SHOW_NEXTHOP:
451 			c->ibuf.pid = imsg.hdr.pid;
452 			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
453 			    imsg.hdr.pid, imsg.data, imsg.hdr.len -
454 			    IMSG_HEADER_SIZE);
455 			break;
456 		case IMSG_CTL_SHOW_RIB:
457 		case IMSG_CTL_SHOW_RIB_PREFIX:
458 			if (imsg.hdr.len != IMSG_HEADER_SIZE +
459 			    sizeof(struct ctl_show_rib_request)) {
460 				log_warnx("got IMSG_CTL_SHOW_RIB with "
461 				    "wrong length");
462 				break;
463 			}
464 
465 			ribreq = imsg.data;
466 			neighbor = &ribreq->neighbor;
467 			neighbor->descr[PEER_DESCR_LEN - 1] = 0;
468 
469 			/* check if at least one neighbor exists */
470 			RB_FOREACH(p, peer_head, peers)
471 				if (peer_matched(p, neighbor))
472 					break;
473 			if (p == NULL && RB_EMPTY(peers)) {
474 				control_result(c, CTL_RES_NOSUCHPEER);
475 				break;
476 			}
477 
478 			if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
479 			    && (ribreq->prefix.aid == AID_UNSPEC)) {
480 				/* malformed request, must specify af */
481 				control_result(c, CTL_RES_PARSE_ERROR);
482 				break;
483 			}
484 
485 			c->ibuf.pid = imsg.hdr.pid;
486 			c->terminate = 1;
487 
488 			imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
489 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
490 			break;
491 		case IMSG_CTL_SHOW_NETWORK:
492 			c->terminate = 1;
493 			/* FALLTHROUGH */
494 		case IMSG_CTL_SHOW_RIB_MEM:
495 			c->ibuf.pid = imsg.hdr.pid;
496 			imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
497 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
498 			break;
499 		case IMSG_NETWORK_ADD:
500 		case IMSG_NETWORK_ASPATH:
501 		case IMSG_NETWORK_ATTR:
502 		case IMSG_NETWORK_REMOVE:
503 		case IMSG_NETWORK_FLUSH:
504 		case IMSG_NETWORK_DONE:
505 		case IMSG_FILTER_SET:
506 			imsg_ctl_rde(imsg.hdr.type, 0,
507 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
508 			break;
509 		case IMSG_CTL_LOG_VERBOSE:
510 			if (imsg.hdr.len != IMSG_HEADER_SIZE +
511 			    sizeof(verbose))
512 				break;
513 
514 			/* forward to other processes */
515 			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
516 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
517 			imsg_ctl_rde(imsg.hdr.type, 0,
518 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
519 
520 			memcpy(&verbose, imsg.data, sizeof(verbose));
521 			log_setverbose(verbose);
522 			break;
523 		default:
524 			break;
525 		}
526 		imsg_free(&imsg);
527 	}
528 
529 	return (0);
530 }
531 
532 int
533 control_imsg_relay(struct imsg *imsg)
534 {
535 	struct ctl_conn	*c;
536 
537 	if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
538 		return (0);
539 
540 	/* if command finished no need to send exit message */
541 	if (imsg->hdr.type == IMSG_CTL_END || imsg->hdr.type == IMSG_CTL_RESULT)
542 		c->terminate = 0;
543 
544 	if (!c->throttled && c->ibuf.w.queued > CTL_MSG_HIGH_MARK) {
545 		if (imsg_ctl_rde(IMSG_XOFF, imsg->hdr.pid, NULL, 0) != -1)
546 			c->throttled = 1;
547 	}
548 
549 	return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
550 	    imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
551 }
552 
553 void
554 control_result(struct ctl_conn *c, u_int code)
555 {
556 	imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1,
557 	    &code, sizeof(code));
558 }
559 
560 /* This should go into libutil, from smtpd/mproc.c */
561 ssize_t
562 imsg_read_nofd(struct imsgbuf *ibuf)
563 {
564 	ssize_t	 n;
565 	char	*buf;
566 	size_t	 len;
567 
568 	buf = ibuf->r.buf + ibuf->r.wpos;
569 	len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
570 
571 	while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
572 		if (errno != EINTR)
573 			return (n);
574 	}
575 
576 	ibuf->r.wpos += n;
577 	return (n);
578 }
579