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