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