control.c revision 1.38
1/*	$OpenBSD: control.c,v 1.38 2004/09/16 17:36:29 henning 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
33struct {
34	int	fd;
35} control_state;
36
37struct ctl_conn	*control_connbyfd(int);
38struct ctl_conn	*control_connbypid(pid_t);
39int		 control_close(int);
40
41int
42control_init(void)
43{
44	struct sockaddr_un	 sun;
45	int			 fd;
46	mode_t			 old_umask;
47
48	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
49		log_warn("control_init: socket");
50		return (-1);
51	}
52
53	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
54	bzero(&sun, sizeof(sun));
55	sun.sun_family = AF_UNIX;
56	strlcpy(sun.sun_path, SOCKET_NAME, sizeof(sun.sun_path));
57
58	if (unlink(SOCKET_NAME) == -1)
59		if (errno != ENOENT) {
60			log_warn("unlink %s", SOCKET_NAME);
61			close(fd);
62			return (-1);
63		}
64
65	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
66		log_warn("control_init: bind: %s", SOCKET_NAME);
67		close(fd);
68		return (-1);
69	}
70
71	if (chmod(SOCKET_NAME, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
72		log_warn("control_init chmod");
73		close(fd);
74		return (-1);
75	}
76
77	umask(old_umask);
78
79	session_socket_blockmode(fd, BM_NONBLOCK);
80	control_state.fd = fd;
81
82	return (fd);
83}
84
85int
86control_listen(void)
87{
88	if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
89		log_warn("control_listen: listen");
90		return (-1);
91	}
92
93	return (control_state.fd);
94}
95
96void
97control_shutdown(void)
98{
99	close(control_state.fd);
100}
101
102void
103control_cleanup(void)
104{
105	unlink(SOCKET_NAME);
106}
107
108int
109control_accept(int listenfd)
110{
111	int			 connfd;
112	socklen_t		 len;
113	struct sockaddr_un	 sun;
114	struct ctl_conn		*ctl_conn;
115
116	len = sizeof(sun);
117	if ((connfd = accept(listenfd,
118	    (struct sockaddr *)&sun, &len)) == -1) {
119		if (errno != EWOULDBLOCK && errno != EINTR)
120			log_warn("session_control_accept");
121		return (0);
122	}
123
124	session_socket_blockmode(connfd, BM_NONBLOCK);
125
126	if ((ctl_conn = malloc(sizeof(struct ctl_conn))) == NULL) {
127		log_warn("session_control_accept");
128		return (0);
129	}
130
131	imsg_init(&ctl_conn->ibuf, connfd);
132
133	TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
134
135	return (1);
136}
137
138struct ctl_conn *
139control_connbyfd(int fd)
140{
141	struct ctl_conn	*c;
142
143	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd;
144	    c = TAILQ_NEXT(c, entry))
145		;	/* nothing */
146
147	return (c);
148}
149
150struct ctl_conn *
151control_connbypid(pid_t pid)
152{
153	struct ctl_conn	*c;
154
155	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid;
156	    c = TAILQ_NEXT(c, entry))
157		;	/* nothing */
158
159	return (c);
160}
161
162int
163control_close(int fd)
164{
165	struct ctl_conn	*c;
166
167	if ((c = control_connbyfd(fd)) == NULL) {
168		log_warn("control_close: fd %d: not found", fd);
169		return (0);
170	}
171
172	msgbuf_clear(&c->ibuf.w);
173	TAILQ_REMOVE(&ctl_conns, c, entry);
174
175	close(c->ibuf.fd);
176	free(c);
177
178	return (1);
179}
180
181int
182control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
183{
184	struct imsg		 imsg;
185	struct ctl_conn		*c;
186	int			 n;
187	struct peer		*p;
188	struct bgpd_addr	*addr;
189
190	if ((c = control_connbyfd(pfd->fd)) == NULL) {
191		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
192		return (0);
193	}
194
195	if (pfd->revents & POLLOUT)
196		if (msgbuf_write(&c->ibuf.w) < 0) {
197			*ctl_cnt -= control_close(pfd->fd);
198			return (1);
199		}
200
201	if (!(pfd->revents & POLLIN))
202		return (0);
203
204	if (imsg_read(&c->ibuf) <= 0) {
205		*ctl_cnt -= control_close(pfd->fd);
206		return (1);
207	}
208
209	for (;;) {
210		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
211			*ctl_cnt -= control_close(pfd->fd);
212			return (1);
213		}
214
215		if (n == 0)
216			break;
217
218		switch (imsg.hdr.type) {
219		case IMSG_CTL_SHOW_NEIGHBOR:
220			c->ibuf.pid = imsg.hdr.pid;
221			if (imsg.hdr.len == IMSG_HEADER_SIZE +
222			    sizeof(struct bgpd_addr)) {
223				addr = imsg.data;
224				p = getpeerbyaddr(addr);
225				if (p != NULL)
226					imsg_compose_rde(imsg.hdr.type,
227					    imsg.hdr.pid,
228					    p, sizeof(struct peer));
229			} else
230				for (p = peers; p != NULL; p = p->next)
231					imsg_compose_rde(imsg.hdr.type,
232					    imsg.hdr.pid,
233					    p, sizeof(struct peer));
234			imsg_compose_rde(IMSG_CTL_END, imsg.hdr.pid, NULL, 0);
235			break;
236		case IMSG_CTL_RELOAD:
237		case IMSG_CTL_FIB_COUPLE:
238		case IMSG_CTL_FIB_DECOUPLE:
239			imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
240			break;
241		case IMSG_CTL_NEIGHBOR_UP:
242		case IMSG_CTL_NEIGHBOR_DOWN:
243		case IMSG_CTL_NEIGHBOR_CLEAR:
244			if (imsg.hdr.len == IMSG_HEADER_SIZE +
245			    sizeof(struct bgpd_addr)) {
246				addr = imsg.data;
247				p = getpeerbyaddr(addr);
248				if (p == NULL) {
249					log_warnx("IMSG_CTL_NEIGHBOR_ "
250					    "with unknown neighbor");
251					break;
252				}
253				switch (imsg.hdr.type) {
254				case IMSG_CTL_NEIGHBOR_UP:
255					bgp_fsm(p, EVNT_START);
256					break;
257				case IMSG_CTL_NEIGHBOR_DOWN:
258					bgp_fsm(p, EVNT_STOP);
259					break;
260				case IMSG_CTL_NEIGHBOR_CLEAR:
261					bgp_fsm(p, EVNT_STOP);
262					bgp_fsm(p, EVNT_START);
263					break;
264				default:
265					fatal("king bula wants more humppa");
266				}
267			} else
268				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
269				    "wrong length");
270			break;
271		case IMSG_CTL_KROUTE:
272		case IMSG_CTL_KROUTE_ADDR:
273		case IMSG_CTL_SHOW_NEXTHOP:
274		case IMSG_CTL_SHOW_INTERFACE:
275			c->ibuf.pid = imsg.hdr.pid;
276			imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
277			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
278			break;
279		case IMSG_CTL_SHOW_RIB:
280		case IMSG_CTL_SHOW_RIB_AS:
281		case IMSG_CTL_SHOW_RIB_PREFIX:
282		case IMSG_CTL_SHOW_NETWORK:
283			c->ibuf.pid = imsg.hdr.pid;
284			imsg_compose_rde(imsg.hdr.type, imsg.hdr.pid,
285			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
286			break;
287		case IMSG_NETWORK_ADD:
288		case IMSG_NETWORK_REMOVE:
289		case IMSG_NETWORK_FLUSH:
290			imsg_compose_rde(imsg.hdr.type, 0,
291			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
292			break;
293		default:
294			break;
295		}
296		imsg_free(&imsg);
297	}
298
299	return (0);
300}
301
302int
303control_imsg_relay(struct imsg *imsg)
304{
305	struct ctl_conn	*c;
306
307	if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
308		return (0);
309
310	return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
311	    imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
312}
313