xref: /openbsd-src/usr.sbin/bgpd/control.c (revision 04e124822cc9826027dbc6fe699898dd3923cbd7)
1*04e12482Sclaudio /*	$OpenBSD: control.c,v 1.132 2024/12/02 15:03:17 claudio Exp $ */
227157683Shenning 
327157683Shenning /*
4050527e1Shenning  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
527157683Shenning  *
627157683Shenning  * Permission to use, copy, modify, and distribute this software for any
727157683Shenning  * purpose with or without fee is hereby granted, provided that the above
827157683Shenning  * copyright notice and this permission notice appear in all copies.
927157683Shenning  *
1027157683Shenning  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1127157683Shenning  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1227157683Shenning  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1327157683Shenning  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1427157683Shenning  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1527157683Shenning  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1627157683Shenning  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1727157683Shenning  */
1827157683Shenning 
1927157683Shenning #include <sys/types.h>
20322c71cfShenning #include <sys/stat.h>
2127157683Shenning #include <sys/socket.h>
2227157683Shenning #include <sys/un.h>
2327157683Shenning #include <errno.h>
2427157683Shenning #include <stdlib.h>
2527157683Shenning #include <string.h>
2627157683Shenning #include <unistd.h>
2727157683Shenning 
2827157683Shenning #include "bgpd.h"
2927157683Shenning #include "session.h"
305e3f6f95Sbenno #include "log.h"
3127157683Shenning 
3276e39a7cSclaudio TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
3376e39a7cSclaudio 
3427157683Shenning #define	CONTROL_BACKLOG	5
3527157683Shenning 
3627157683Shenning struct ctl_conn	*control_connbyfd(int);
37f4ecf7d5Shenning struct ctl_conn	*control_connbypid(pid_t);
3876e39a7cSclaudio int		 control_close(struct ctl_conn *);
39f58eb34cShenning void		 control_result(struct ctl_conn *, u_int);
4027157683Shenning 
4127157683Shenning int
421adf6159Sremi control_check(char *path)
431adf6159Sremi {
44db04565aSclaudio 	struct sockaddr_un	 sa_un;
451adf6159Sremi 	int			 fd;
461adf6159Sremi 
47eafe309eSclaudio 	memset(&sa_un, 0, sizeof(sa_un));
48db04565aSclaudio 	sa_un.sun_family = AF_UNIX;
49db04565aSclaudio 	strlcpy(sa_un.sun_path, path, sizeof(sa_un.sun_path));
501adf6159Sremi 
513ac77e71Sclaudio 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
521adf6159Sremi 		log_warn("%s: socket", __func__);
531adf6159Sremi 		return (-1);
541adf6159Sremi 	}
551adf6159Sremi 
56db04565aSclaudio 	if (connect(fd, (struct sockaddr *)&sa_un, sizeof(sa_un)) == 0) {
571adf6159Sremi 		log_warnx("control socket %s already in use", path);
581adf6159Sremi 		close(fd);
591adf6159Sremi 		return (-1);
601adf6159Sremi 	}
611adf6159Sremi 
621adf6159Sremi 	close(fd);
631adf6159Sremi 
641adf6159Sremi 	return (0);
651adf6159Sremi }
661adf6159Sremi 
671adf6159Sremi int
6841928d45Shenning control_init(int restricted, char *path)
6927157683Shenning {
70db04565aSclaudio 	struct sockaddr_un	 sa_un;
7127157683Shenning 	int			 fd;
7241928d45Shenning 	mode_t			 old_umask, mode;
7327157683Shenning 
745ba20998Sclaudio 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
755ba20998Sclaudio 	    0)) == -1) {
768013a801Shenning 		log_warn("control_init: socket");
7727157683Shenning 		return (-1);
7827157683Shenning 	}
7927157683Shenning 
80eafe309eSclaudio 	memset(&sa_un, 0, sizeof(sa_un));
81db04565aSclaudio 	sa_un.sun_family = AF_UNIX;
82db04565aSclaudio 	if (strlcpy(sa_un.sun_path, path, sizeof(sa_un.sun_path)) >=
83db04565aSclaudio 	    sizeof(sa_un.sun_path)) {
84339bb5e0Sbenno 		log_warn("control_init: socket name too long");
85339bb5e0Sbenno 		close(fd);
86339bb5e0Sbenno 		return (-1);
87339bb5e0Sbenno 	}
88322c71cfShenning 
8941928d45Shenning 	if (unlink(path) == -1)
90322c71cfShenning 		if (errno != ENOENT) {
91e7464dffSmk 			log_warn("control_init: unlink %s", path);
92efc32651Shenning 			close(fd);
93322c71cfShenning 			return (-1);
94322c71cfShenning 		}
95322c71cfShenning 
9641928d45Shenning 	if (restricted) {
9741928d45Shenning 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
9841928d45Shenning 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
9941928d45Shenning 	} else {
100126a8a1bSderaadt 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
10141928d45Shenning 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
10241928d45Shenning 	}
10341928d45Shenning 
104db04565aSclaudio 	if (bind(fd, (struct sockaddr *)&sa_un, sizeof(sa_un)) == -1) {
10541928d45Shenning 		log_warn("control_init: bind: %s", path);
106efc32651Shenning 		close(fd);
107126a8a1bSderaadt 		umask(old_umask);
10827157683Shenning 		return (-1);
10927157683Shenning 	}
11027157683Shenning 
111126a8a1bSderaadt 	umask(old_umask);
112126a8a1bSderaadt 
11341928d45Shenning 	if (chmod(path, mode) == -1) {
11441928d45Shenning 		log_warn("control_init: chmod: %s", path);
115efc32651Shenning 		close(fd);
11641928d45Shenning 		unlink(path);
117322c71cfShenning 		return (-1);
118322c71cfShenning 	}
119322c71cfShenning 
12027157683Shenning 	return (fd);
12127157683Shenning }
12227157683Shenning 
12327157683Shenning int
12441928d45Shenning control_listen(int fd)
12527157683Shenning {
12641928d45Shenning 	if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) {
1278013a801Shenning 		log_warn("control_listen: listen");
12827157683Shenning 		return (-1);
12927157683Shenning 	}
13027157683Shenning 
13141928d45Shenning 	return (0);
13227157683Shenning }
13327157683Shenning 
13427157683Shenning void
13541928d45Shenning control_shutdown(int fd)
13627157683Shenning {
13741928d45Shenning 	close(fd);
13827157683Shenning }
13927157683Shenning 
14076e39a7cSclaudio size_t
14176e39a7cSclaudio control_fill_pfds(struct pollfd *pfd, size_t size)
14276e39a7cSclaudio {
14376e39a7cSclaudio 	struct ctl_conn	*ctl_conn;
14476e39a7cSclaudio 	size_t i = 0;
14576e39a7cSclaudio 
14676e39a7cSclaudio 	TAILQ_FOREACH(ctl_conn, &ctl_conns, entry) {
147c8f6e08eSclaudio 		pfd[i].fd = ctl_conn->imsgbuf.fd;
14876e39a7cSclaudio 		pfd[i].events = POLLIN;
14931be28caSclaudio 		if (imsgbuf_queuelen(&ctl_conn->imsgbuf) > 0)
15076e39a7cSclaudio 			pfd[i].events |= POLLOUT;
15176e39a7cSclaudio 		i++;
15276e39a7cSclaudio 	}
15376e39a7cSclaudio 	return i;
15476e39a7cSclaudio }
15576e39a7cSclaudio 
15699bf90ffSclaudio unsigned int
15741928d45Shenning control_accept(int listenfd, int restricted)
15827157683Shenning {
15927157683Shenning 	int			 connfd;
16027157683Shenning 	socklen_t		 len;
161db04565aSclaudio 	struct sockaddr_un	 sa_un;
16227157683Shenning 	struct ctl_conn		*ctl_conn;
16327157683Shenning 
164db04565aSclaudio 	len = sizeof(sa_un);
1655ba20998Sclaudio 	if ((connfd = accept4(listenfd,
166db04565aSclaudio 	    (struct sockaddr *)&sa_un, &len,
1675ba20998Sclaudio 	    SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) {
16878c7f42bSclaudio 		if (errno == ENFILE || errno == EMFILE) {
16978c7f42bSclaudio 			pauseaccept = getmonotime();
17078c7f42bSclaudio 			return (0);
17162e3c252Sderaadt 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
17262e3c252Sderaadt 		    errno != ECONNABORTED)
173e7464dffSmk 			log_warn("control_accept: accept");
174edf7a95aShenning 		return (0);
17527157683Shenning 	}
17627157683Shenning 
177d923c008Shenning 	if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
178e7464dffSmk 		log_warn("control_accept");
179ac50b3daShenning 		close(connfd);
180edf7a95aShenning 		return (0);
18127157683Shenning 	}
18227157683Shenning 
183*04e12482Sclaudio 	if (imsgbuf_init(&ctl_conn->imsgbuf, connfd) == -1 ||
184*04e12482Sclaudio 	    imsgbuf_set_maxsize(&ctl_conn->imsgbuf, MAX_BGPD_IMSGSIZE) == -1) {
185f1b790a5Sclaudio 		log_warn("control_accept");
186*04e12482Sclaudio 		imsgbuf_clear(&ctl_conn->imsgbuf);
187f1b790a5Sclaudio 		close(connfd);
188f1b790a5Sclaudio 		free(ctl_conn);
189f1b790a5Sclaudio 		return (0);
190f1b790a5Sclaudio 	}
19141928d45Shenning 	ctl_conn->restricted = restricted;
19227157683Shenning 
193435f34f3Shenning 	TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
194edf7a95aShenning 
195edf7a95aShenning 	return (1);
19627157683Shenning }
19727157683Shenning 
19827157683Shenning struct ctl_conn *
19927157683Shenning control_connbyfd(int fd)
20027157683Shenning {
20127157683Shenning 	struct ctl_conn	*c;
20227157683Shenning 
203c39b7208Skrw 	TAILQ_FOREACH(c, &ctl_conns, entry) {
204c8f6e08eSclaudio 		if (c->imsgbuf.fd == fd)
205c39b7208Skrw 			break;
206c39b7208Skrw 	}
20727157683Shenning 
20827157683Shenning 	return (c);
20927157683Shenning }
21027157683Shenning 
211f4ecf7d5Shenning struct ctl_conn *
212f4ecf7d5Shenning control_connbypid(pid_t pid)
213f4ecf7d5Shenning {
214f4ecf7d5Shenning 	struct ctl_conn	*c;
215f4ecf7d5Shenning 
216c39b7208Skrw 	TAILQ_FOREACH(c, &ctl_conns, entry) {
217c8f6e08eSclaudio 		if (c->imsgbuf.pid == pid)
218c39b7208Skrw 			break;
219c39b7208Skrw 	}
220f4ecf7d5Shenning 
221f4ecf7d5Shenning 	return (c);
222f4ecf7d5Shenning }
223f4ecf7d5Shenning 
224edf7a95aShenning int
22576e39a7cSclaudio control_close(struct ctl_conn *c)
22627157683Shenning {
227c8f6e08eSclaudio 	if (c->terminate && c->imsgbuf.pid)
228c8f6e08eSclaudio 		imsg_ctl_rde_msg(IMSG_CTL_TERMINATE, 0, c->imsgbuf.pid);
229fb02cdf0Sclaudio 
2309cbf9e90Sclaudio 	imsgbuf_clear(&c->imsgbuf);
231435f34f3Shenning 	TAILQ_REMOVE(&ctl_conns, c, entry);
23227157683Shenning 
233c8f6e08eSclaudio 	close(c->imsgbuf.fd);
23427157683Shenning 	free(c);
23578c7f42bSclaudio 	pauseaccept = 0;
236edf7a95aShenning 	return (1);
23727157683Shenning }
23827157683Shenning 
23927157683Shenning int
24076e39a7cSclaudio control_dispatch_msg(struct pollfd *pfd, struct peer_head *peers)
24127157683Shenning {
24227157683Shenning 	struct imsg		 imsg;
2430b920bb9Sclaudio 	struct ctl_neighbor	 neighbor;
2440b920bb9Sclaudio 	struct ctl_show_rib_request	ribreq;
24527157683Shenning 	struct ctl_conn		*c;
24627157683Shenning 	struct peer		*p;
2470b920bb9Sclaudio 	ssize_t			 n;
2480b920bb9Sclaudio 	uint32_t		 type;
2490b920bb9Sclaudio 	pid_t			 pid;
2500b920bb9Sclaudio 	int			 verbose, matched;
25127157683Shenning 
25227157683Shenning 	if ((c = control_connbyfd(pfd->fd)) == NULL) {
2538013a801Shenning 		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
25427157683Shenning 		return (0);
25527157683Shenning 	}
25627157683Shenning 
257383bcad8Sclaudio 	if (pfd->revents & POLLOUT) {
258dd7efffeSclaudio 		if (imsgbuf_write(&c->imsgbuf) == -1)
25976e39a7cSclaudio 			return control_close(c);
26031be28caSclaudio 		if (c->throttled &&
26131be28caSclaudio 		    imsgbuf_queuelen(&c->imsgbuf) < CTL_MSG_LOW_MARK) {
262c8f6e08eSclaudio 			if (imsg_ctl_rde_msg(IMSG_XON, 0, c->imsgbuf.pid) != -1)
263383bcad8Sclaudio 				c->throttled = 0;
264383bcad8Sclaudio 		}
265383bcad8Sclaudio 	}
2660d8c7131Shenning 
2670d8c7131Shenning 	if (!(pfd->revents & POLLIN))
2680d8c7131Shenning 		return (0);
2690d8c7131Shenning 
27098435822Sclaudio 	if (imsgbuf_read(&c->imsgbuf) != 1)
27176e39a7cSclaudio 		return control_close(c);
27227157683Shenning 
27327157683Shenning 	for (;;) {
274c8f6e08eSclaudio 		if ((n = imsg_get(&c->imsgbuf, &imsg)) == -1)
27576e39a7cSclaudio 			return control_close(c);
27627157683Shenning 
27727157683Shenning 		if (n == 0)
27827157683Shenning 			break;
27927157683Shenning 
2800b920bb9Sclaudio 		type = imsg_get_type(&imsg);
2810b920bb9Sclaudio 		pid = imsg_get_pid(&imsg);
28241928d45Shenning 		if (c->restricted) {
2830b920bb9Sclaudio 			switch (type) {
28427157683Shenning 			case IMSG_CTL_SHOW_NEIGHBOR:
28541928d45Shenning 			case IMSG_CTL_SHOW_NEXTHOP:
28641928d45Shenning 			case IMSG_CTL_SHOW_INTERFACE:
28741928d45Shenning 			case IMSG_CTL_SHOW_RIB_MEM:
28816fcf8d2Shenning 			case IMSG_CTL_SHOW_TERSE:
2896251e4e2Shenning 			case IMSG_CTL_SHOW_TIMER:
290fb02cdf0Sclaudio 			case IMSG_CTL_SHOW_NETWORK:
291f7b38103Sclaudio 			case IMSG_CTL_SHOW_FLOWSPEC:
292fb02cdf0Sclaudio 			case IMSG_CTL_SHOW_RIB:
293fb02cdf0Sclaudio 			case IMSG_CTL_SHOW_RIB_PREFIX:
2947ff9bf35Sclaudio 			case IMSG_CTL_SHOW_SET:
2959647b4ffSclaudio 			case IMSG_CTL_SHOW_RTR:
29641928d45Shenning 				break;
29741928d45Shenning 			default:
29841928d45Shenning 				/* clear imsg type to prevent processing */
2990b920bb9Sclaudio 				type = IMSG_NONE;
30041928d45Shenning 				control_result(c, CTL_RES_DENIED);
30141928d45Shenning 				break;
30241928d45Shenning 			}
30341928d45Shenning 		}
30441928d45Shenning 
3050b920bb9Sclaudio 		/*
3060b920bb9Sclaudio 		 * TODO: this is wrong and shoud work the other way around.
3070b920bb9Sclaudio 		 * The imsg.hdr.pid is from the remote end and should not
3080b920bb9Sclaudio 		 * be trusted.
3090b920bb9Sclaudio 		 */
310c8f6e08eSclaudio 		c->imsgbuf.pid = pid;
3110b920bb9Sclaudio 		switch (type) {
31241928d45Shenning 		case IMSG_NONE:
31341928d45Shenning 			/* message was filtered out, nothing to do */
31441928d45Shenning 			break;
315514180acSclaudio 		case IMSG_CTL_FIB_COUPLE:
316514180acSclaudio 		case IMSG_CTL_FIB_DECOUPLE:
3170b920bb9Sclaudio 			imsg_ctl_parent(&imsg);
318514180acSclaudio 			break;
319514180acSclaudio 		case IMSG_CTL_SHOW_TERSE:
3207876190cSclaudio 			RB_FOREACH(p, peer_head, peers)
321c8f6e08eSclaudio 				imsg_compose(&c->imsgbuf,
322c8f6e08eSclaudio 				    IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1,
323c8f6e08eSclaudio 				    p, sizeof(struct peer));
324c8f6e08eSclaudio 			imsg_compose(&c->imsgbuf, IMSG_CTL_END, 0, 0, -1,
325c8f6e08eSclaudio 			    NULL, 0);
326514180acSclaudio 			break;
32741928d45Shenning 		case IMSG_CTL_SHOW_NEIGHBOR:
3280b920bb9Sclaudio 			if (imsg_get_data(&imsg, &neighbor,
3290b920bb9Sclaudio 			    sizeof(neighbor)) == -1)
3300b920bb9Sclaudio 				memset(&neighbor, 0, sizeof(neighbor));
331514180acSclaudio 
33282fc6237Sclaudio 			matched = 0;
3337876190cSclaudio 			RB_FOREACH(p, peer_head, peers) {
3340b920bb9Sclaudio 				if (!peer_matched(p, &neighbor))
335514180acSclaudio 					continue;
336514180acSclaudio 
337514180acSclaudio 				matched = 1;
3380b920bb9Sclaudio 				if (!neighbor.show_timers) {
3390b920bb9Sclaudio 					imsg_ctl_rde_msg(type,
3400b920bb9Sclaudio 					    p->conf.id, pid);
3415641005aShenning 				} else {
3420d594c94Shenning 					u_int			 i;
3430d594c94Shenning 					time_t			 d;
3440d594c94Shenning 					struct ctl_timer	 ct;
3450d594c94Shenning 
346c8f6e08eSclaudio 					imsg_compose(&c->imsgbuf,
3470d594c94Shenning 					    IMSG_CTL_SHOW_NEIGHBOR,
3480d594c94Shenning 					    0, 0, -1, p, sizeof(*p));
3490d594c94Shenning 					for (i = 1; i < Timer_Max; i++) {
35082fcfa8bSclaudio 						if (!timer_running(&p->timers,
35182fcfa8bSclaudio 						    i, &d))
3520d594c94Shenning 							continue;
3530d594c94Shenning 						ct.type = i;
3540d594c94Shenning 						ct.val = d;
355c8f6e08eSclaudio 						imsg_compose(&c->imsgbuf,
3560d594c94Shenning 						    IMSG_CTL_SHOW_TIMER,
3570d594c94Shenning 						    0, 0, -1, &ct, sizeof(ct));
3580d594c94Shenning 					}
3590d594c94Shenning 				}
360514180acSclaudio 			}
361baad1f5dSclaudio 			if (!matched && !RB_EMPTY(peers)) {
362514180acSclaudio 				control_result(c, CTL_RES_NOSUCHPEER);
3630b920bb9Sclaudio 			} else if (!neighbor.show_timers) {
3640b920bb9Sclaudio 				imsg_ctl_rde_msg(IMSG_CTL_END, 0, pid);
365514180acSclaudio 			} else {
366c8f6e08eSclaudio 				imsg_compose(&c->imsgbuf, IMSG_CTL_END, 0, 0,
367c8f6e08eSclaudio 				    -1, NULL, 0);
3680d594c94Shenning 			}
36927157683Shenning 			break;
37041e05cf3Shenning 		case IMSG_CTL_NEIGHBOR_UP:
37141e05cf3Shenning 		case IMSG_CTL_NEIGHBOR_DOWN:
37292b49885Shenning 		case IMSG_CTL_NEIGHBOR_CLEAR:
373b51efe11Shenning 		case IMSG_CTL_NEIGHBOR_RREFRESH:
37417098ec8Sclaudio 		case IMSG_CTL_NEIGHBOR_DESTROY:
3750b920bb9Sclaudio 			if (imsg_get_data(&imsg, &neighbor,
3760b920bb9Sclaudio 			    sizeof(neighbor)) == -1) {
377514180acSclaudio 				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
378514180acSclaudio 				    "wrong length");
3790bdee4bbShenning 				break;
3800bdee4bbShenning 			}
381514180acSclaudio 
38282fc6237Sclaudio 			matched = 0;
3837876190cSclaudio 			RB_FOREACH(p, peer_head, peers) {
3840b920bb9Sclaudio 				if (!peer_matched(p, &neighbor))
385514180acSclaudio 					continue;
386514180acSclaudio 
387514180acSclaudio 				matched = 1;
388514180acSclaudio 
3890b920bb9Sclaudio 				switch (type) {
3900bdee4bbShenning 				case IMSG_CTL_NEIGHBOR_UP:
391c9c4d4e4Sclaudio 					bgp_fsm(p, EVNT_START, NULL);
3920561b344Sphessler 					p->conf.down = 0;
393a78f83ceSderaadt 					p->conf.reason[0] = '\0';
3940239f306Sclaudio 					p->IdleHoldTime =
3950239f306Sclaudio 					    INTERVAL_IDLE_HOLD_INITIAL;
3960239f306Sclaudio 					p->errcnt = 0;
397b51efe11Shenning 					control_result(c, CTL_RES_OK);
3980bdee4bbShenning 					break;
3990bdee4bbShenning 				case IMSG_CTL_NEIGHBOR_DOWN:
4000b920bb9Sclaudio 					neighbor.reason[
4010b920bb9Sclaudio 					    sizeof(neighbor.reason) - 1] = '\0';
4029af396adSclaudio 					p->conf.down = 1;
4033a82eff3Sclaudio 					session_stop(p, ERR_CEASE_ADMIN_DOWN,
4043a82eff3Sclaudio 					    neighbor.reason);
405b51efe11Shenning 					control_result(c, CTL_RES_OK);
4060bdee4bbShenning 					break;
40792b49885Shenning 				case IMSG_CTL_NEIGHBOR_CLEAR:
4080b920bb9Sclaudio 					neighbor.reason[
4090b920bb9Sclaudio 					    sizeof(neighbor.reason) - 1] = '\0';
4100239f306Sclaudio 					p->IdleHoldTime =
4110239f306Sclaudio 					    INTERVAL_IDLE_HOLD_INITIAL;
4120239f306Sclaudio 					p->errcnt = 0;
413ad45d28fSclaudio 					if (!p->conf.down) {
414ad45d28fSclaudio 						session_stop(p,
4153a82eff3Sclaudio 						    ERR_CEASE_ADMIN_RESET,
4163a82eff3Sclaudio 						    neighbor.reason);
41782fcfa8bSclaudio 						timer_set(&p->timers,
41882fcfa8bSclaudio 						    Timer_IdleHold,
4191b5a1903Shenning 						    SESSION_CLEAR_DELAY);
420ad45d28fSclaudio 					} else {
421ad45d28fSclaudio 						session_stop(p,
4223a82eff3Sclaudio 						    ERR_CEASE_ADMIN_DOWN,
4233a82eff3Sclaudio 						    neighbor.reason);
424ad45d28fSclaudio 					}
425b51efe11Shenning 					control_result(c, CTL_RES_OK);
426b51efe11Shenning 					break;
427b51efe11Shenning 				case IMSG_CTL_NEIGHBOR_RREFRESH:
428b51efe11Shenning 					if (session_neighbor_rrefresh(p))
429b51efe11Shenning 						control_result(c,
430b51efe11Shenning 						    CTL_RES_NOCAP);
431b51efe11Shenning 					else
432b51efe11Shenning 						control_result(c, CTL_RES_OK);
43392b49885Shenning 					break;
43417098ec8Sclaudio 				case IMSG_CTL_NEIGHBOR_DESTROY:
43517098ec8Sclaudio 					if (!p->template)
43617098ec8Sclaudio 						control_result(c,
43717098ec8Sclaudio 						    CTL_RES_BADPEER);
43817098ec8Sclaudio 					else if (p->state != STATE_IDLE)
43917098ec8Sclaudio 						control_result(c,
44017098ec8Sclaudio 						    CTL_RES_BADSTATE);
44117098ec8Sclaudio 					else {
44217098ec8Sclaudio 						/*
44317098ec8Sclaudio 						 * Mark as deleted, will be
44417098ec8Sclaudio 						 * collected on next poll loop.
44517098ec8Sclaudio 						 */
44682fc6237Sclaudio 						p->reconf_action =
44717098ec8Sclaudio 						    RECONF_DELETE;
44817098ec8Sclaudio 						control_result(c, CTL_RES_OK);
44917098ec8Sclaudio 					}
45017098ec8Sclaudio 					break;
4510bdee4bbShenning 				default:
4520bdee4bbShenning 					fatal("king bula wants more humppa");
4530bdee4bbShenning 				}
454514180acSclaudio 			}
455514180acSclaudio 			if (!matched)
456514180acSclaudio 				control_result(c, CTL_RES_NOSUCHPEER);
45741e05cf3Shenning 			break;
4581721f25bSclaudio 		case IMSG_CTL_RELOAD:
459459fcfe4Sclaudio 		case IMSG_CTL_SHOW_INTERFACE:
460459fcfe4Sclaudio 		case IMSG_CTL_SHOW_FIB_TABLES:
461bd9df44eSclaudio 		case IMSG_CTL_SHOW_RTR:
4620b920bb9Sclaudio 			imsg_ctl_parent(&imsg);
463459fcfe4Sclaudio 			break;
464f4ecf7d5Shenning 		case IMSG_CTL_KROUTE:
46552ef641bShenning 		case IMSG_CTL_KROUTE_ADDR:
466e949080fShenning 		case IMSG_CTL_SHOW_NEXTHOP:
4670b920bb9Sclaudio 			imsg_ctl_parent(&imsg);
468f4ecf7d5Shenning 			break;
469e37c0eabSclaudio 		case IMSG_CTL_SHOW_RIB:
470ad926354Sclaudio 		case IMSG_CTL_SHOW_RIB_PREFIX:
4710b920bb9Sclaudio 			if (imsg_get_data(&imsg, &ribreq, sizeof(ribreq)) ==
4720b920bb9Sclaudio 			    -1) {
473514180acSclaudio 				log_warnx("got IMSG_CTL_SHOW_RIB with "
474514180acSclaudio 				    "wrong length");
475514180acSclaudio 				break;
476514180acSclaudio 			}
477514180acSclaudio 
478514180acSclaudio 			/* check if at least one neighbor exists */
4797876190cSclaudio 			RB_FOREACH(p, peer_head, peers)
4800b920bb9Sclaudio 				if (peer_matched(p, &ribreq.neighbor))
481dae6dd81Sclaudio 					break;
482baad1f5dSclaudio 			if (p == NULL && !RB_EMPTY(peers)) {
4832b5ebfecSbenno 				control_result(c, CTL_RES_NOSUCHPEER);
4842b5ebfecSbenno 				break;
4852b5ebfecSbenno 			}
486514180acSclaudio 
4870b920bb9Sclaudio 			if (type == IMSG_CTL_SHOW_RIB_PREFIX &&
4880b920bb9Sclaudio 			    ribreq.prefix.aid == AID_UNSPEC) {
4892b2440b0Ssthen 				/* malformed request, must specify af */
4902b2440b0Ssthen 				control_result(c, CTL_RES_PARSE_ERROR);
4912b2440b0Ssthen 				break;
4922b2440b0Ssthen 			}
493514180acSclaudio 
494fb02cdf0Sclaudio 			c->terminate = 1;
4950b920bb9Sclaudio 			imsg_ctl_rde(&imsg);
496dae6dd81Sclaudio 			break;
49797f08015Sclaudio 		case IMSG_CTL_SHOW_NETWORK:
498f7b38103Sclaudio 		case IMSG_CTL_SHOW_FLOWSPEC:
499fb02cdf0Sclaudio 			c->terminate = 1;
500fb02cdf0Sclaudio 			/* FALLTHROUGH */
501fb02cdf0Sclaudio 		case IMSG_CTL_SHOW_RIB_MEM:
5027ff9bf35Sclaudio 		case IMSG_CTL_SHOW_SET:
5030b920bb9Sclaudio 			imsg_ctl_rde(&imsg);
504e37c0eabSclaudio 			break;
50597f08015Sclaudio 		case IMSG_NETWORK_ADD:
506abf4b724Sclaudio 		case IMSG_NETWORK_ASPATH:
507abf4b724Sclaudio 		case IMSG_NETWORK_ATTR:
50897f08015Sclaudio 		case IMSG_NETWORK_REMOVE:
50997f08015Sclaudio 		case IMSG_NETWORK_FLUSH:
51098d902b5Sclaudio 		case IMSG_NETWORK_DONE:
511f7b38103Sclaudio 		case IMSG_FLOWSPEC_ADD:
512f7b38103Sclaudio 		case IMSG_FLOWSPEC_REMOVE:
513f7b38103Sclaudio 		case IMSG_FLOWSPEC_DONE:
514f7b38103Sclaudio 		case IMSG_FLOWSPEC_FLUSH:
51598d902b5Sclaudio 		case IMSG_FILTER_SET:
5160b920bb9Sclaudio 			imsg_ctl_rde(&imsg);
51797f08015Sclaudio 			break;
518d6b35cfbSclaudio 		case IMSG_CTL_LOG_VERBOSE:
5190b920bb9Sclaudio 			if (imsg_get_data(&imsg, &verbose, sizeof(verbose)) ==
5200b920bb9Sclaudio 			    -1)
521d6b35cfbSclaudio 				break;
522d6b35cfbSclaudio 
523307aca1bSjsg 			/* forward to other processes */
5240b920bb9Sclaudio 			imsg_ctl_parent(&imsg);
5250b920bb9Sclaudio 			imsg_ctl_rde(&imsg);
5265e3f6f95Sbenno 			log_setverbose(verbose);
527d6b35cfbSclaudio 			break;
52827157683Shenning 		default:
52927157683Shenning 			break;
53027157683Shenning 		}
53127157683Shenning 		imsg_free(&imsg);
53227157683Shenning 	}
53327157683Shenning 
53427157683Shenning 	return (0);
53527157683Shenning }
536f4ecf7d5Shenning 
537f4ecf7d5Shenning int
53882625ff8Sclaudio control_imsg_relay(struct imsg *imsg, struct peer *p)
539f4ecf7d5Shenning {
540f4ecf7d5Shenning 	struct ctl_conn	*c;
5410b920bb9Sclaudio 	uint32_t type;
5420b920bb9Sclaudio 	pid_t pid;
543f4ecf7d5Shenning 
5440b920bb9Sclaudio 	type = imsg_get_type(imsg);
5450b920bb9Sclaudio 	pid = imsg_get_pid(imsg);
5460b920bb9Sclaudio 
5470b920bb9Sclaudio 	if ((c = control_connbypid(pid)) == NULL)
548f4ecf7d5Shenning 		return (0);
549f4ecf7d5Shenning 
55082625ff8Sclaudio 	/* special handling for peers since only the stats are sent from RDE */
5510b920bb9Sclaudio 	if (type == IMSG_CTL_SHOW_NEIGHBOR) {
55282625ff8Sclaudio 		struct rde_peer_stats stats;
553d7629114Sclaudio 		struct peer peer;
55482625ff8Sclaudio 
55582625ff8Sclaudio 		if (p == NULL) {
5560b920bb9Sclaudio 			log_warnx("%s: no such peer: id=%u", __func__,
5570b920bb9Sclaudio 			    imsg_get_id(imsg));
55882625ff8Sclaudio 			return (0);
55982625ff8Sclaudio 		}
5600b920bb9Sclaudio 		if (imsg_get_data(imsg, &stats, sizeof(stats)) == -1) {
5610b920bb9Sclaudio 			log_warnx("%s: imsg_get_data", __func__);
5620b920bb9Sclaudio 			return (0);
5630b920bb9Sclaudio 		}
564d7629114Sclaudio 		peer = *p;
565d7629114Sclaudio 		explicit_bzero(&peer.auth_conf, sizeof(peer.auth_conf));
56663c2b496Sclaudio 		peer.auth_conf.method = p->auth_conf.method;
567d7629114Sclaudio 		peer.stats.prefix_cnt = stats.prefix_cnt;
568d7629114Sclaudio 		peer.stats.prefix_out_cnt = stats.prefix_out_cnt;
569d7629114Sclaudio 		peer.stats.prefix_rcvd_update = stats.prefix_rcvd_update;
570d7629114Sclaudio 		peer.stats.prefix_rcvd_withdraw = stats.prefix_rcvd_withdraw;
571d7629114Sclaudio 		peer.stats.prefix_rcvd_eor = stats.prefix_rcvd_eor;
572d7629114Sclaudio 		peer.stats.prefix_sent_update = stats.prefix_sent_update;
573d7629114Sclaudio 		peer.stats.prefix_sent_withdraw = stats.prefix_sent_withdraw;
574d7629114Sclaudio 		peer.stats.prefix_sent_eor = stats.prefix_sent_eor;
575d7629114Sclaudio 		peer.stats.pending_update = stats.pending_update;
576d7629114Sclaudio 		peer.stats.pending_withdraw = stats.pending_withdraw;
57705453d67Sclaudio 		peer.stats.msg_queue_len = msgbuf_queuelen(p->wbuf);
57882625ff8Sclaudio 
579c8f6e08eSclaudio 		return imsg_compose(&c->imsgbuf, type, 0, pid, -1,
580d7629114Sclaudio 		    &peer, sizeof(peer));
58182625ff8Sclaudio 	}
58282625ff8Sclaudio 
583fb02cdf0Sclaudio 	/* if command finished no need to send exit message */
5840b920bb9Sclaudio 	if (type == IMSG_CTL_END || type == IMSG_CTL_RESULT)
585fb02cdf0Sclaudio 		c->terminate = 0;
586fb02cdf0Sclaudio 
58731be28caSclaudio 	if (!c->throttled &&
58831be28caSclaudio 	    imsgbuf_queuelen(&c->imsgbuf) > CTL_MSG_HIGH_MARK) {
5890b920bb9Sclaudio 		if (imsg_ctl_rde_msg(IMSG_XOFF, 0, pid) != -1)
590383bcad8Sclaudio 			c->throttled = 1;
591383bcad8Sclaudio 	}
592383bcad8Sclaudio 
593c8f6e08eSclaudio 	return (imsg_forward(&c->imsgbuf, imsg));
594f4ecf7d5Shenning }
595f58eb34cShenning 
596f58eb34cShenning void
597f58eb34cShenning control_result(struct ctl_conn *c, u_int code)
598f58eb34cShenning {
599c8f6e08eSclaudio 	imsg_compose(&c->imsgbuf, IMSG_CTL_RESULT, 0, c->imsgbuf.pid, -1,
600f58eb34cShenning 	    &code, sizeof(code));
601f58eb34cShenning }
602