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