bgpd.c revision 1.111
1/*	$OpenBSD: bgpd.c,v 1.111 2004/11/23 13:07:01 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/socket.h>
21#include <sys/wait.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <err.h>
25#include <errno.h>
26#include <fcntl.h>
27#include <poll.h>
28#include <pwd.h>
29#include <signal.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34
35#include "mrt.h"
36#include "bgpd.h"
37#include "session.h"
38
39void	sighdlr(int);
40void	usage(void);
41int	main(int, char *[]);
42int	check_child(pid_t, const char *);
43int	send_filterset(struct imsgbuf *, struct filter_set_head *, int, int);
44int	reconfigure(char *, struct bgpd_config *, struct mrt_head *,
45	    struct peer **, struct filter_head *);
46int	dispatch_imsg(struct imsgbuf *, int);
47
48int			 rfd = -1;
49volatile sig_atomic_t	 mrtdump = 0;
50volatile sig_atomic_t	 quit = 0;
51volatile sig_atomic_t	 reconfig = 0;
52volatile sig_atomic_t	 sigchld = 0;
53struct imsgbuf		*ibuf_se;
54struct imsgbuf		*ibuf_rde;
55
56void
57sighdlr(int sig)
58{
59	switch (sig) {
60	case SIGTERM:
61	case SIGINT:
62		quit = 1;
63		break;
64	case SIGCHLD:
65		sigchld = 1;
66		break;
67	case SIGHUP:
68		reconfig = 1;
69		break;
70	case SIGALRM:
71	case SIGUSR1:
72		mrtdump = 1;
73		break;
74	}
75}
76
77void
78usage(void)
79{
80	extern char *__progname;
81
82	fprintf(stderr, "usage: %s [-dnv] ", __progname);
83	fprintf(stderr, "[-D macro=value] [-f file]\n");
84	exit(1);
85}
86
87#define PFD_PIPE_SESSION	0
88#define PFD_PIPE_ROUTE		1
89#define PFD_SOCK_ROUTE		2
90#define POLL_MAX		3
91#define MAX_TIMEOUT		3600
92
93int
94main(int argc, char *argv[])
95{
96	struct bgpd_config	 conf;
97	struct peer		*peer_l, *p;
98	struct mrt_head		 mrt_l;
99	struct network_head	 net_l;
100	struct filter_head	*rules_l;
101	struct network		*net;
102	struct filter_rule	*r;
103	struct mrt		*m;
104	struct listen_addr	*la;
105	struct pollfd		 pfd[POLL_MAX];
106	pid_t			 io_pid = 0, rde_pid = 0, pid;
107	char			*conffile;
108	int			 debug = 0;
109	int			 ch, nfds, timeout;
110	int			 pipe_m2s[2];
111	int			 pipe_m2r[2];
112	int			 pipe_s2r[2];
113
114	conffile = CONFFILE;
115	bgpd_process = PROC_MAIN;
116
117	log_init(1);		/* log to stderr until daemonized */
118
119	if ((rules_l = calloc(1, sizeof(struct filter_head))) == NULL)
120		err(1, NULL);
121
122	bzero(&conf, sizeof(conf));
123	LIST_INIT(&mrt_l);
124	TAILQ_INIT(&net_l);
125	TAILQ_INIT(rules_l);
126	peer_l = NULL;
127
128	while ((ch = getopt(argc, argv, "dD:f:nv")) != -1) {
129		switch (ch) {
130		case 'd':
131			debug = 1;
132			break;
133		case 'D':
134			if (cmdline_symset(optarg) < 0)
135				log_warnx("could not parse macro definition %s",
136				    optarg);
137			break;
138		case 'f':
139			conffile = optarg;
140			break;
141		case 'n':
142			conf.opts |= BGPD_OPT_NOACTION;
143			break;
144		case 'v':
145			if (conf.opts & BGPD_OPT_VERBOSE)
146				conf.opts |= BGPD_OPT_VERBOSE2;
147			conf.opts |= BGPD_OPT_VERBOSE;
148			break;
149		default:
150			usage();
151			/* NOTREACHED */
152		}
153	}
154
155	if (parse_config(conffile, &conf, &mrt_l, &peer_l, &net_l, rules_l))
156		exit(1);
157
158	if (conf.opts & BGPD_OPT_NOACTION) {
159		if (conf.opts & BGPD_OPT_VERBOSE)
160			print_config(&conf, &net_l, peer_l, rules_l, &mrt_l);
161		else
162			fprintf(stderr, "configuration OK\n");
163		exit(0);
164	}
165
166	if (geteuid())
167		errx(1, "need root privileges");
168
169	if (getpwnam(BGPD_USER) == NULL)
170		errx(1, "unknown user %s", BGPD_USER);
171	endpwent();
172
173	log_init(debug);
174
175	if (!debug)
176		daemon(1, 0);
177
178	log_info("startup");
179
180	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_m2s) == -1)
181		fatal("socketpair");
182	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_m2r) == -1)
183		fatal("socketpair");
184	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_s2r) == -1)
185		fatal("socketpair");
186	session_socket_blockmode(pipe_m2s[0], BM_NONBLOCK);
187	session_socket_blockmode(pipe_m2s[1], BM_NONBLOCK);
188	session_socket_blockmode(pipe_m2r[0], BM_NONBLOCK);
189	session_socket_blockmode(pipe_m2r[1], BM_NONBLOCK);
190	session_socket_blockmode(pipe_s2r[0], BM_NONBLOCK);
191	session_socket_blockmode(pipe_s2r[1], BM_NONBLOCK);
192
193	prepare_listeners(&conf);
194
195	/* fork children */
196	rde_pid = rde_main(&conf, peer_l, &net_l, rules_l, &mrt_l,
197	    pipe_m2r, pipe_s2r, pipe_m2s);
198	io_pid = session_main(&conf, peer_l, &net_l, rules_l, &mrt_l,
199	    pipe_m2s, pipe_s2r, pipe_m2r);
200
201	setproctitle("parent");
202
203	signal(SIGTERM, sighdlr);
204	signal(SIGINT, sighdlr);
205	signal(SIGCHLD, sighdlr);
206	signal(SIGHUP, sighdlr);
207	signal(SIGALRM, sighdlr);
208	signal(SIGUSR1, sighdlr);
209
210	close(pipe_m2s[1]);
211	close(pipe_m2r[1]);
212	close(pipe_s2r[0]);
213	close(pipe_s2r[1]);
214
215	if ((ibuf_se = malloc(sizeof(struct imsgbuf))) == NULL ||
216	    (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL)
217		fatal(NULL);
218	imsg_init(ibuf_se, pipe_m2s[0]);
219	imsg_init(ibuf_rde, pipe_m2r[0]);
220	mrt_init(ibuf_rde, ibuf_se);
221	if ((rfd = kr_init(!(conf.flags & BGPD_FLAG_NO_FIB_UPDATE))) == -1)
222		quit = 1;
223	if (pftable_clear_all() != 0)
224		quit = 1;
225
226	while ((net = TAILQ_FIRST(&net_l)) != NULL) {
227		TAILQ_REMOVE(&net_l, net, entry);
228		free(net);
229	}
230
231	while ((r = TAILQ_FIRST(rules_l)) != NULL) {
232		TAILQ_REMOVE(rules_l, r, entry);
233		free(r);
234	}
235
236	while ((la = TAILQ_FIRST(conf.listen_addrs)) != NULL) {
237		TAILQ_REMOVE(conf.listen_addrs, la, entry);
238		close(la->fd);
239		free(la);
240	}
241
242	mrt_reconfigure(&mrt_l);
243
244	while (quit == 0) {
245		pfd[PFD_PIPE_SESSION].fd = ibuf_se->fd;
246		pfd[PFD_PIPE_SESSION].events = POLLIN;
247		if (ibuf_se->w.queued)
248			pfd[PFD_PIPE_SESSION].events |= POLLOUT;
249		pfd[PFD_PIPE_ROUTE].fd = ibuf_rde->fd;
250		pfd[PFD_PIPE_ROUTE].events = POLLIN;
251		if (ibuf_rde->w.queued)
252			pfd[PFD_PIPE_ROUTE].events |= POLLOUT;
253		pfd[PFD_SOCK_ROUTE].fd = rfd;
254		pfd[PFD_SOCK_ROUTE].events = POLLIN;
255
256		timeout = mrt_timeout(&mrt_l);
257		if (timeout > MAX_TIMEOUT)
258			timeout = MAX_TIMEOUT;
259
260		if ((nfds = poll(pfd, POLL_MAX, timeout * 1000)) == -1)
261			if (errno != EINTR) {
262				log_warn("poll error");
263				quit = 1;
264			}
265
266		if (nfds > 0 && (pfd[PFD_PIPE_SESSION].revents & POLLOUT))
267			if (msgbuf_write(&ibuf_se->w) < 0) {
268				log_warn("pipe write error (to SE)");
269				quit = 1;
270			}
271
272		if (nfds > 0 && (pfd[PFD_PIPE_ROUTE].revents & POLLOUT))
273			if (msgbuf_write(&ibuf_rde->w) < 0) {
274				log_warn("pipe write error (to RDE)");
275				quit = 1;
276			}
277
278		if (nfds > 0 && pfd[PFD_PIPE_SESSION].revents & POLLIN) {
279			nfds--;
280			if (dispatch_imsg(ibuf_se, PFD_PIPE_SESSION) == -1)
281				quit = 1;
282		}
283
284		if (nfds > 0 && pfd[PFD_PIPE_ROUTE].revents & POLLIN) {
285			nfds--;
286			if (dispatch_imsg(ibuf_rde, PFD_PIPE_ROUTE) == -1)
287				quit = 1;
288		}
289
290		if (nfds > 0 && pfd[PFD_SOCK_ROUTE].revents & POLLIN) {
291			nfds--;
292			if (kr_dispatch_msg() == -1)
293				quit = 1;
294		}
295
296		if (reconfig) {
297			reconfig = 0;
298			log_info("rereading config");
299			reconfigure(conffile, &conf, &mrt_l, &peer_l, rules_l);
300		}
301
302		if (sigchld) {
303			sigchld = 0;
304			if (check_child(io_pid, "session engine")) {
305				quit = 1;
306				io_pid = 0;
307			}
308			if (check_child(rde_pid, "route decision engine")) {
309				quit = 1;
310				rde_pid = 0;
311			}
312		}
313
314		if (mrtdump == 1) {
315			mrtdump = 0;
316			mrt_handler(&mrt_l);
317		}
318	}
319
320	signal(SIGCHLD, SIG_IGN);
321
322	if (io_pid)
323		kill(io_pid, SIGTERM);
324
325	if (rde_pid)
326		kill(rde_pid, SIGTERM);
327
328	while ((p = peer_l) != NULL) {
329		peer_l = p->next;
330		free(p);
331	}
332	while ((m = LIST_FIRST(&mrt_l)) != NULL) {
333		LIST_REMOVE(m, entry);
334		free(m);
335	}
336
337	free(rules_l);
338	control_cleanup();
339	kr_shutdown();
340	pftable_clear_all();
341	free(conf.listen_addrs);
342
343	do {
344		if ((pid = wait(NULL)) == -1 &&
345		    errno != EINTR && errno != ECHILD)
346			fatal("wait");
347	} while (pid != -1 || (pid == -1 && errno == EINTR));
348
349	msgbuf_clear(&ibuf_se->w);
350	free(ibuf_se);
351	msgbuf_clear(&ibuf_rde->w);
352	free(ibuf_rde);
353
354	log_info("Terminating");
355	return (0);
356}
357
358int
359check_child(pid_t pid, const char *pname)
360{
361	int	status;
362
363	if (waitpid(pid, &status, WNOHANG) > 0) {
364		if (WIFEXITED(status)) {
365			log_warnx("Lost child: %s exited", pname);
366			return (1);
367		}
368		if (WIFSIGNALED(status)) {
369			log_warnx("Lost child: %s terminated; signal %d",
370			    pname, WTERMSIG(status));
371			return (1);
372		}
373	}
374
375	return (0);
376}
377
378int
379send_filterset(struct imsgbuf *i, struct filter_set_head *set, int id, int f)
380{
381	struct filter_set	*s;
382
383	for (s = SIMPLEQ_FIRST(set); s != NULL; ) {
384		if (imsg_compose(i, IMSG_FILTER_SET, id, 0, -1, s,
385		    sizeof(struct filter_set)) == -1)
386			return (-1);
387		if (f) {
388			SIMPLEQ_REMOVE_HEAD(set, entry);
389			free(s);
390			s = SIMPLEQ_FIRST(set);
391		} else
392			s = SIMPLEQ_NEXT(s, entry);
393	}
394	return (0);
395}
396
397int
398reconfigure(char *conffile, struct bgpd_config *conf, struct mrt_head *mrt_l,
399    struct peer **peer_l, struct filter_head *rules_l)
400{
401	struct network_head	 net_l;
402	struct network		*n;
403	struct peer		*p;
404	struct filter_rule	*r;
405	struct listen_addr	*la;
406
407	if (parse_config(conffile, conf, mrt_l, peer_l, &net_l, rules_l)) {
408		log_warnx("config file %s has errors, not reloading",
409		    conffile);
410		return (-1);
411	}
412
413	prepare_listeners(conf);
414
415	if (imsg_compose(ibuf_se, IMSG_RECONF_CONF, 0, 0, -1,
416	    conf, sizeof(struct bgpd_config)) == -1)
417		return (-1);
418	if (imsg_compose(ibuf_rde, IMSG_RECONF_CONF, 0, 0, -1,
419	    conf, sizeof(struct bgpd_config)) == -1)
420		return (-1);
421	for (p = *peer_l; p != NULL; p = p->next) {
422		if (imsg_compose(ibuf_se, IMSG_RECONF_PEER, p->conf.id, 0, -1,
423		    &p->conf, sizeof(struct peer_config)) == -1)
424			return (-1);
425		if (send_filterset(ibuf_se, &p->conf.attrset,
426		    p->conf.id, 0) == -1)
427			return (-1);
428	}
429	while ((n = TAILQ_FIRST(&net_l)) != NULL) {
430		if (imsg_compose(ibuf_rde, IMSG_NETWORK_ADD, 0, 0, -1,
431		    &n->net, sizeof(struct network_config)) == -1)
432			return (-1);
433		if (send_filterset(ibuf_rde, &n->net.attrset, 0, 1) == -1)
434			return (-1);
435		TAILQ_REMOVE(&net_l, n, entry);
436		free(n);
437	}
438	while ((r = TAILQ_FIRST(rules_l)) != NULL) {
439		if (imsg_compose(ibuf_rde, IMSG_RECONF_FILTER, 0, 0, -1,
440		    r, sizeof(struct filter_rule)) == -1)
441			return (-1);
442		if (send_filterset(ibuf_rde, &r->set, 0, 1) == -1)
443			return (-1);
444		TAILQ_REMOVE(rules_l, r, entry);
445		free(r);
446	}
447	while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) {
448		if (imsg_compose(ibuf_se, IMSG_RECONF_LISTENER, 0, 0, la->fd,
449		    la, sizeof(struct listen_addr)) == -1)
450			return (-1);
451		TAILQ_REMOVE(conf->listen_addrs, la, entry);
452		free(la);
453	}
454	free(conf->listen_addrs);
455	conf->listen_addrs = NULL;
456
457	if (imsg_compose(ibuf_se, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1 ||
458	    imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1)
459		return (-1);
460
461	/* mrt changes can be sent out of bound */
462	mrt_reconfigure(mrt_l);
463	return (0);
464}
465
466int
467dispatch_imsg(struct imsgbuf *ibuf, int idx)
468{
469	struct imsg		 imsg;
470	int			 n;
471
472	if ((n = imsg_read(ibuf)) == -1)
473		return (-1);
474
475	if (n == 0) {	/* connection closed */
476		log_warnx("dispatch_imsg in main: pipe closed");
477		return (-1);
478	}
479
480	for (;;) {
481		if ((n = imsg_get(ibuf, &imsg)) == -1)
482			return (-1);
483
484		if (n == 0)
485			break;
486
487		switch (imsg.hdr.type) {
488		case IMSG_KROUTE_CHANGE:
489			if (idx != PFD_PIPE_ROUTE)
490				log_warnx("route request not from RDE");
491			else if (kr_change(imsg.data))
492				return (-1);
493			break;
494		case IMSG_KROUTE_DELETE:
495			if (idx != PFD_PIPE_ROUTE)
496				log_warnx("route request not from RDE");
497			else if (kr_delete(imsg.data))
498				return (-1);
499			break;
500		case IMSG_NEXTHOP_ADD:
501			if (idx != PFD_PIPE_ROUTE)
502				log_warnx("nexthop request not from RDE");
503			else
504				if (imsg.hdr.len != IMSG_HEADER_SIZE +
505				    sizeof(struct bgpd_addr))
506					log_warnx("wrong imsg len");
507				else if (kr_nexthop_add(imsg.data) == -1)
508					return (-1);
509			break;
510		case IMSG_NEXTHOP_REMOVE:
511			if (idx != PFD_PIPE_ROUTE)
512				log_warnx("nexthop request not from RDE");
513			else
514				if (imsg.hdr.len != IMSG_HEADER_SIZE +
515				    sizeof(struct bgpd_addr))
516					log_warnx("wrong imsg len");
517				else
518					kr_nexthop_delete(imsg.data);
519			break;
520		case IMSG_PFTABLE_ADD:
521			if (idx != PFD_PIPE_ROUTE)
522				log_warnx("pftable request not from RDE");
523			else
524				if (imsg.hdr.len != IMSG_HEADER_SIZE +
525				    sizeof(struct pftable_msg))
526					log_warnx("wrong imsg len");
527				else if (pftable_addr_add(imsg.data) != 0)
528					return (-1);
529			break;
530		case IMSG_PFTABLE_REMOVE:
531			if (idx != PFD_PIPE_ROUTE)
532				log_warnx("pftable request not from RDE");
533			else
534				if (imsg.hdr.len != IMSG_HEADER_SIZE +
535				    sizeof(struct pftable_msg))
536					log_warnx("wrong imsg len");
537				else if (pftable_addr_remove(imsg.data) != 0)
538					return (-1);
539			break;
540		case IMSG_PFTABLE_COMMIT:
541			if (idx != PFD_PIPE_ROUTE)
542				log_warnx("pftable request not from RDE");
543			else
544				if (imsg.hdr.len != IMSG_HEADER_SIZE)
545					log_warnx("wrong imsg len");
546				else if (pftable_commit() != 0)
547					return (-1);
548			break;
549		case IMSG_CTL_RELOAD:
550			if (idx != PFD_PIPE_SESSION)
551				log_warnx("reload request not from SE");
552			else
553				reconfig = 1;
554			break;
555		case IMSG_CTL_FIB_COUPLE:
556			if (idx != PFD_PIPE_SESSION)
557				log_warnx("couple request not from SE");
558			else
559				kr_fib_couple();
560			break;
561		case IMSG_CTL_FIB_DECOUPLE:
562			if (idx != PFD_PIPE_SESSION)
563				log_warnx("decouple request not from SE");
564			else
565				kr_fib_decouple();
566			break;
567		case IMSG_CTL_KROUTE:
568		case IMSG_CTL_KROUTE_ADDR:
569		case IMSG_CTL_SHOW_NEXTHOP:
570		case IMSG_CTL_SHOW_INTERFACE:
571			if (idx != PFD_PIPE_SESSION)
572				log_warnx("kroute request not from SE");
573			else
574				kr_show_route(&imsg);
575			break;
576		case IMSG_IFINFO:
577			if (idx != PFD_PIPE_SESSION)
578				log_warnx("IFINFO request not from SE");
579			else if (imsg.hdr.len != IMSG_HEADER_SIZE + IFNAMSIZ)
580				log_warnx("IFINFO request with wrong len");
581			else
582				kr_ifinfo(imsg.data);
583			break;
584		default:
585			break;
586		}
587		imsg_free(&imsg);
588	}
589	return (0);
590}
591
592void
593send_nexthop_update(struct kroute_nexthop *msg)
594{
595	char	*gw = NULL;
596
597	if (msg->gateway.af)
598		if (asprintf(&gw, ": via %s",
599		    log_addr(&msg->gateway)) == -1) {
600			log_warn("send_nexthop_update");
601			quit = 1;
602		}
603
604	log_info("nexthop %s now %s%s%s", log_addr(&msg->nexthop),
605	    msg->valid ? "valid" : "invalid",
606	    msg->connected ? ": directly connected" : "",
607	    msg->gateway.af ? gw : "");
608
609	free(gw);
610
611	if (imsg_compose(ibuf_rde, IMSG_NEXTHOP_UPDATE, 0, 0, -1,
612	    msg, sizeof(struct kroute_nexthop)) == -1)
613		quit = 1;
614}
615
616void
617send_imsg_session(int type, pid_t pid, void *data, u_int16_t datalen)
618{
619	imsg_compose(ibuf_se, type, 0, pid, -1, data, datalen);
620}
621