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