1 |
|
|
/* $OpenBSD: bgpd.c,v 1.191 2017/08/12 16:31:09 florian 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 <syslog.h> |
34 |
|
|
#include <unistd.h> |
35 |
|
|
|
36 |
|
|
#include "bgpd.h" |
37 |
|
|
#include "mrt.h" |
38 |
|
|
#include "session.h" |
39 |
|
|
#include "log.h" |
40 |
|
|
|
41 |
|
|
void sighdlr(int); |
42 |
|
|
__dead void usage(void); |
43 |
|
|
int main(int, char *[]); |
44 |
|
|
pid_t start_child(enum bgpd_process, char *, int, int, int); |
45 |
|
|
int send_filterset(struct imsgbuf *, struct filter_set_head *); |
46 |
|
|
int reconfigure(char *, struct bgpd_config *, struct peer **); |
47 |
|
|
int dispatch_imsg(struct imsgbuf *, int, struct bgpd_config *); |
48 |
|
|
int control_setup(struct bgpd_config *); |
49 |
|
|
int imsg_send_sockets(struct imsgbuf *, struct imsgbuf *); |
50 |
|
|
|
51 |
|
|
int cflags; |
52 |
|
|
volatile sig_atomic_t mrtdump; |
53 |
|
|
volatile sig_atomic_t quit; |
54 |
|
|
volatile sig_atomic_t reconfig; |
55 |
|
|
pid_t reconfpid; |
56 |
|
|
int reconfpending; |
57 |
|
|
struct imsgbuf *ibuf_se; |
58 |
|
|
struct imsgbuf *ibuf_rde; |
59 |
|
|
struct rib_names ribnames = SIMPLEQ_HEAD_INITIALIZER(ribnames); |
60 |
|
|
char *cname; |
61 |
|
|
char *rcname; |
62 |
|
|
|
63 |
|
|
void |
64 |
|
|
sighdlr(int sig) |
65 |
|
|
{ |
66 |
|
|
switch (sig) { |
67 |
|
|
case SIGTERM: |
68 |
|
|
case SIGINT: |
69 |
|
|
quit = 1; |
70 |
|
|
break; |
71 |
|
|
case SIGHUP: |
72 |
|
|
reconfig = 1; |
73 |
|
|
break; |
74 |
|
|
case SIGALRM: |
75 |
|
|
case SIGUSR1: |
76 |
|
|
mrtdump = 1; |
77 |
|
|
break; |
78 |
|
|
} |
79 |
|
|
} |
80 |
|
|
|
81 |
|
|
__dead void |
82 |
|
|
usage(void) |
83 |
|
|
{ |
84 |
|
|
extern char *__progname; |
85 |
|
|
|
86 |
|
|
fprintf(stderr, "usage: %s [-cdnv] [-D macro=value] [-f file]\n", |
87 |
|
|
__progname); |
88 |
|
|
exit(1); |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
#define PFD_PIPE_SESSION 0 |
92 |
|
|
#define PFD_PIPE_ROUTE 1 |
93 |
|
|
#define PFD_SOCK_ROUTE 2 |
94 |
|
|
#define POLL_MAX 3 |
95 |
|
|
#define MAX_TIMEOUT 3600 |
96 |
|
|
|
97 |
|
|
int cmd_opts; |
98 |
|
|
|
99 |
|
|
int |
100 |
|
|
main(int argc, char *argv[]) |
101 |
|
|
{ |
102 |
|
|
struct bgpd_config *conf; |
103 |
|
|
struct peer *peer_l, *p; |
104 |
|
|
struct pollfd pfd[POLL_MAX]; |
105 |
|
|
pid_t io_pid = 0, rde_pid = 0, pid; |
106 |
|
|
char *conffile; |
107 |
|
|
char *saved_argv0; |
108 |
|
|
int debug = 0; |
109 |
|
|
int rflag = 0, sflag = 0; |
110 |
|
|
int rfd = -1; |
111 |
|
|
int ch, timeout, status; |
112 |
|
|
int pipe_m2s[2]; |
113 |
|
|
int pipe_m2r[2]; |
114 |
|
|
|
115 |
|
|
conffile = CONFFILE; |
116 |
|
|
bgpd_process = PROC_MAIN; |
117 |
|
|
|
118 |
|
|
log_init(1, LOG_DAEMON); /* log to stderr until daemonized */ |
119 |
|
|
log_procinit(log_procnames[bgpd_process]); |
120 |
|
|
log_setverbose(1); |
121 |
|
|
|
122 |
|
|
saved_argv0 = argv[0]; |
123 |
|
|
if (saved_argv0 == NULL) |
124 |
|
|
saved_argv0 = "bgpd"; |
125 |
|
|
|
126 |
|
|
conf = new_config(); |
127 |
|
|
peer_l = NULL; |
128 |
|
|
|
129 |
|
|
while ((ch = getopt(argc, argv, "cdD:f:nRSv")) != -1) { |
130 |
|
|
switch (ch) { |
131 |
|
|
case 'c': |
132 |
|
|
cmd_opts |= BGPD_OPT_FORCE_DEMOTE; |
133 |
|
|
break; |
134 |
|
|
case 'd': |
135 |
|
|
debug = 1; |
136 |
|
|
break; |
137 |
|
|
case 'D': |
138 |
|
|
if (cmdline_symset(optarg) < 0) |
139 |
|
|
log_warnx("could not parse macro definition %s", |
140 |
|
|
optarg); |
141 |
|
|
break; |
142 |
|
|
case 'f': |
143 |
|
|
conffile = optarg; |
144 |
|
|
break; |
145 |
|
|
case 'n': |
146 |
|
|
cmd_opts |= BGPD_OPT_NOACTION; |
147 |
|
|
break; |
148 |
|
|
case 'v': |
149 |
|
|
if (cmd_opts & BGPD_OPT_VERBOSE) |
150 |
|
|
cmd_opts |= BGPD_OPT_VERBOSE2; |
151 |
|
|
cmd_opts |= BGPD_OPT_VERBOSE; |
152 |
|
|
break; |
153 |
|
|
case 'R': |
154 |
|
|
rflag = 1; |
155 |
|
|
break; |
156 |
|
|
case 'S': |
157 |
|
|
sflag = 1; |
158 |
|
|
break; |
159 |
|
|
default: |
160 |
|
|
usage(); |
161 |
|
|
/* NOTREACHED */ |
162 |
|
|
} |
163 |
|
|
} |
164 |
|
|
|
165 |
|
|
argc -= optind; |
166 |
|
|
argv += optind; |
167 |
|
|
if (argc > 0 || (sflag && rflag)) |
168 |
|
|
usage(); |
169 |
|
|
|
170 |
|
|
if (cmd_opts & BGPD_OPT_NOACTION) { |
171 |
|
|
if (parse_config(conffile, conf, &peer_l)) |
172 |
|
|
exit(1); |
173 |
|
|
|
174 |
|
|
if (cmd_opts & BGPD_OPT_VERBOSE) |
175 |
|
|
print_config(conf, &ribnames, &conf->networks, peer_l, |
176 |
|
|
conf->filters, conf->mrt, &conf->rdomains); |
177 |
|
|
else |
178 |
|
|
fprintf(stderr, "configuration OK\n"); |
179 |
|
|
exit(0); |
180 |
|
|
} |
181 |
|
|
|
182 |
|
|
if (rflag) |
183 |
|
|
rde_main(debug, cmd_opts & BGPD_OPT_VERBOSE); |
184 |
|
|
else if (sflag) |
185 |
|
|
session_main(debug, cmd_opts & BGPD_OPT_VERBOSE); |
186 |
|
|
|
187 |
|
|
if (geteuid()) |
188 |
|
|
errx(1, "need root privileges"); |
189 |
|
|
|
190 |
|
|
if (getpwnam(BGPD_USER) == NULL) |
191 |
|
|
errx(1, "unknown user %s", BGPD_USER); |
192 |
|
|
|
193 |
|
|
log_init(debug, LOG_DAEMON); |
194 |
|
|
log_setverbose(cmd_opts & BGPD_OPT_VERBOSE); |
195 |
|
|
|
196 |
|
|
if (!debug) |
197 |
|
|
daemon(1, 0); |
198 |
|
|
|
199 |
|
|
log_info("startup"); |
200 |
|
|
|
201 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, |
202 |
|
|
PF_UNSPEC, pipe_m2s) == -1) |
203 |
|
|
fatal("socketpair"); |
204 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, |
205 |
|
|
PF_UNSPEC, pipe_m2r) == -1) |
206 |
|
|
fatal("socketpair"); |
207 |
|
|
|
208 |
|
|
/* fork children */ |
209 |
|
|
rde_pid = start_child(PROC_RDE, saved_argv0, pipe_m2r[1], debug, |
210 |
|
|
cmd_opts & BGPD_OPT_VERBOSE); |
211 |
|
|
io_pid = start_child(PROC_SE, saved_argv0, pipe_m2s[1], debug, |
212 |
|
|
cmd_opts & BGPD_OPT_VERBOSE); |
213 |
|
|
|
214 |
|
|
signal(SIGTERM, sighdlr); |
215 |
|
|
signal(SIGINT, sighdlr); |
216 |
|
|
signal(SIGHUP, sighdlr); |
217 |
|
|
signal(SIGALRM, sighdlr); |
218 |
|
|
signal(SIGUSR1, sighdlr); |
219 |
|
|
signal(SIGPIPE, SIG_IGN); |
220 |
|
|
|
221 |
|
|
if ((ibuf_se = malloc(sizeof(struct imsgbuf))) == NULL || |
222 |
|
|
(ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL) |
223 |
|
|
fatal(NULL); |
224 |
|
|
imsg_init(ibuf_se, pipe_m2s[0]); |
225 |
|
|
imsg_init(ibuf_rde, pipe_m2r[0]); |
226 |
|
|
mrt_init(ibuf_rde, ibuf_se); |
227 |
|
|
if ((rfd = kr_init()) == -1) |
228 |
|
|
quit = 1; |
229 |
|
|
|
230 |
|
|
/* |
231 |
|
|
* rpath, read config file |
232 |
|
|
* cpath, unlink control socket |
233 |
|
|
* fattr, chmod on control socket |
234 |
|
|
* wpath, needed if we are doing mrt dumps |
235 |
|
|
* |
236 |
|
|
* pledge placed here because kr_init() does a setsockopt on the |
237 |
|
|
* routing socket thats not allowed at all. |
238 |
|
|
*/ |
239 |
|
|
#if 0 |
240 |
|
|
/* |
241 |
|
|
* disabled because we do ioctls on /dev/pf and SIOCSIFGATTR |
242 |
|
|
* this needs some redesign of bgpd to be fixed. |
243 |
|
|
*/ |
244 |
|
|
BROKEN if (pledge("stdio rpath wpath cpath fattr unix route recvfd sendfd", |
245 |
|
|
NULL) == -1) |
246 |
|
|
fatal("pledge"); |
247 |
|
|
#endif |
248 |
|
|
|
249 |
|
|
if (imsg_send_sockets(ibuf_se, ibuf_rde)) |
250 |
|
|
fatal("could not establish imsg links"); |
251 |
|
|
quit = reconfigure(conffile, conf, &peer_l); |
252 |
|
|
if (pftable_clear_all() != 0) |
253 |
|
|
quit = 1; |
254 |
|
|
|
255 |
|
|
while (quit == 0) { |
256 |
|
|
bzero(pfd, sizeof(pfd)); |
257 |
|
|
|
258 |
|
|
set_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se); |
259 |
|
|
set_pollfd(&pfd[PFD_PIPE_ROUTE], ibuf_rde); |
260 |
|
|
|
261 |
|
|
pfd[PFD_SOCK_ROUTE].fd = rfd; |
262 |
|
|
pfd[PFD_SOCK_ROUTE].events = POLLIN; |
263 |
|
|
|
264 |
|
|
timeout = mrt_timeout(conf->mrt); |
265 |
|
|
if (timeout > MAX_TIMEOUT) |
266 |
|
|
timeout = MAX_TIMEOUT; |
267 |
|
|
|
268 |
|
|
if (poll(pfd, POLL_MAX, timeout * 1000) == -1) |
269 |
|
|
if (errno != EINTR) { |
270 |
|
|
log_warn("poll error"); |
271 |
|
|
quit = 1; |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
if (handle_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se) == -1) { |
275 |
|
|
log_warnx("main: Lost connection to SE"); |
276 |
|
|
msgbuf_clear(&ibuf_se->w); |
277 |
|
|
free(ibuf_se); |
278 |
|
|
ibuf_se = NULL; |
279 |
|
|
quit = 1; |
280 |
|
|
} else { |
281 |
|
|
if (dispatch_imsg(ibuf_se, PFD_PIPE_SESSION, conf) == |
282 |
|
|
-1) |
283 |
|
|
quit = 1; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
if (handle_pollfd(&pfd[PFD_PIPE_ROUTE], ibuf_rde) == -1) { |
287 |
|
|
log_warnx("main: Lost connection to RDE"); |
288 |
|
|
msgbuf_clear(&ibuf_rde->w); |
289 |
|
|
free(ibuf_rde); |
290 |
|
|
ibuf_rde = NULL; |
291 |
|
|
quit = 1; |
292 |
|
|
} else { |
293 |
|
|
if (dispatch_imsg(ibuf_rde, PFD_PIPE_ROUTE, conf) == |
294 |
|
|
-1) |
295 |
|
|
quit = 1; |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
if (pfd[PFD_SOCK_ROUTE].revents & POLLIN) { |
299 |
|
|
if (kr_dispatch_msg() == -1) |
300 |
|
|
quit = 1; |
301 |
|
|
} |
302 |
|
|
|
303 |
|
|
if (reconfig) { |
304 |
|
|
u_int error; |
305 |
|
|
|
306 |
|
|
reconfig = 0; |
307 |
|
|
switch (reconfigure(conffile, conf, &peer_l)) { |
308 |
|
|
case -1: /* fatal error */ |
309 |
|
|
quit = 1; |
310 |
|
|
break; |
311 |
|
|
case 0: /* all OK */ |
312 |
|
|
error = 0; |
313 |
|
|
break; |
314 |
|
|
case 2: |
315 |
|
|
error = CTL_RES_PENDING; |
316 |
|
|
break; |
317 |
|
|
default: /* parse error */ |
318 |
|
|
error = CTL_RES_PARSE_ERROR; |
319 |
|
|
break; |
320 |
|
|
} |
321 |
|
|
if (reconfpid != 0) { |
322 |
|
|
send_imsg_session(IMSG_CTL_RESULT, reconfpid, |
323 |
|
|
&error, sizeof(error)); |
324 |
|
|
reconfpid = 0; |
325 |
|
|
} |
326 |
|
|
} |
327 |
|
|
|
328 |
|
|
if (mrtdump) { |
329 |
|
|
mrtdump = 0; |
330 |
|
|
mrt_handler(conf->mrt); |
331 |
|
|
} |
332 |
|
|
} |
333 |
|
|
|
334 |
|
|
/* close pipes */ |
335 |
|
|
if (ibuf_se) { |
336 |
|
|
msgbuf_clear(&ibuf_se->w); |
337 |
|
|
close(ibuf_se->fd); |
338 |
|
|
free(ibuf_se); |
339 |
|
|
} |
340 |
|
|
if (ibuf_rde) { |
341 |
|
|
msgbuf_clear(&ibuf_rde->w); |
342 |
|
|
close(ibuf_rde->fd); |
343 |
|
|
free(ibuf_rde); |
344 |
|
|
} |
345 |
|
|
|
346 |
|
|
while ((p = peer_l) != NULL) { |
347 |
|
|
peer_l = p->next; |
348 |
|
|
free(p); |
349 |
|
|
} |
350 |
|
|
|
351 |
|
|
control_cleanup(conf->csock); |
352 |
|
|
control_cleanup(conf->rcsock); |
353 |
|
|
carp_demote_shutdown(); |
354 |
|
|
kr_shutdown(conf->fib_priority); |
355 |
|
|
pftable_clear_all(); |
356 |
|
|
|
357 |
|
|
free_config(conf); |
358 |
|
|
|
359 |
|
|
log_debug("waiting for children to terminate"); |
360 |
|
|
do { |
361 |
|
|
pid = wait(&status); |
362 |
|
|
if (pid == -1) { |
363 |
|
|
if (errno != EINTR && errno != ECHILD) |
364 |
|
|
fatal("wait"); |
365 |
|
|
} else if (WIFSIGNALED(status)) |
366 |
|
|
log_warnx("%s terminated; signal %d", |
367 |
|
|
(pid == rde_pid) ? "route decision engine" : |
368 |
|
|
"session engine", WTERMSIG(status)); |
369 |
|
|
} while (pid != -1 || (pid == -1 && errno == EINTR)); |
370 |
|
|
|
371 |
|
|
free(rcname); |
372 |
|
|
free(cname); |
373 |
|
|
|
374 |
|
|
log_info("terminating"); |
375 |
|
|
return (0); |
376 |
|
|
} |
377 |
|
|
|
378 |
|
|
pid_t |
379 |
|
|
start_child(enum bgpd_process p, char *argv0, int fd, int debug, int verbose) |
380 |
|
|
{ |
381 |
|
|
char *argv[5]; |
382 |
|
|
int argc = 0; |
383 |
|
|
pid_t pid; |
384 |
|
|
|
385 |
|
|
switch (pid = fork()) { |
386 |
|
|
case -1: |
387 |
|
|
fatal("cannot fork"); |
388 |
|
|
case 0: |
389 |
|
|
break; |
390 |
|
|
default: |
391 |
|
|
close(fd); |
392 |
|
|
return (pid); |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
if (dup2(fd, 3) == -1) |
396 |
|
|
fatal("cannot setup imsg fd"); |
397 |
|
|
|
398 |
|
|
argv[argc++] = argv0; |
399 |
|
|
switch (p) { |
400 |
|
|
case PROC_MAIN: |
401 |
|
|
fatalx("Can not start main process"); |
402 |
|
|
case PROC_RDE: |
403 |
|
|
argv[argc++] = "-R"; |
404 |
|
|
break; |
405 |
|
|
case PROC_SE: |
406 |
|
|
argv[argc++] = "-S"; |
407 |
|
|
break; |
408 |
|
|
} |
409 |
|
|
if (debug) |
410 |
|
|
argv[argc++] = "-d"; |
411 |
|
|
if (verbose) |
412 |
|
|
argv[argc++] = "-v"; |
413 |
|
|
argv[argc++] = NULL; |
414 |
|
|
|
415 |
|
|
execvp(argv0, argv); |
416 |
|
|
fatal("execvp"); |
417 |
|
|
} |
418 |
|
|
|
419 |
|
|
int |
420 |
|
|
send_filterset(struct imsgbuf *i, struct filter_set_head *set) |
421 |
|
|
{ |
422 |
|
|
struct filter_set *s; |
423 |
|
|
|
424 |
|
|
TAILQ_FOREACH(s, set, entry) |
425 |
|
|
if (imsg_compose(i, IMSG_FILTER_SET, 0, 0, -1, s, |
426 |
|
|
sizeof(struct filter_set)) == -1) |
427 |
|
|
return (-1); |
428 |
|
|
return (0); |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
int |
432 |
|
|
reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l) |
433 |
|
|
{ |
434 |
|
|
struct peer *p; |
435 |
|
|
struct filter_rule *r; |
436 |
|
|
struct listen_addr *la; |
437 |
|
|
struct rde_rib *rr; |
438 |
|
|
struct rdomain *rd; |
439 |
|
|
|
440 |
|
|
if (reconfpending) { |
441 |
|
|
log_info("previous reload still running"); |
442 |
|
|
return (2); |
443 |
|
|
} |
444 |
|
|
reconfpending = 2; /* one per child */ |
445 |
|
|
|
446 |
|
|
log_info("rereading config"); |
447 |
|
|
if (parse_config(conffile, conf, peer_l)) { |
448 |
|
|
log_warnx("config file %s has errors, not reloading", |
449 |
|
|
conffile); |
450 |
|
|
reconfpending = 0; |
451 |
|
|
return (1); |
452 |
|
|
} |
453 |
|
|
|
454 |
|
|
cflags = conf->flags; |
455 |
|
|
prepare_listeners(conf); |
456 |
|
|
|
457 |
|
|
/* start reconfiguration */ |
458 |
|
|
if (imsg_compose(ibuf_se, IMSG_RECONF_CONF, 0, 0, -1, |
459 |
|
|
conf, sizeof(struct bgpd_config)) == -1) |
460 |
|
|
return (-1); |
461 |
|
|
if (imsg_compose(ibuf_rde, IMSG_RECONF_CONF, 0, 0, -1, |
462 |
|
|
conf, sizeof(struct bgpd_config)) == -1) |
463 |
|
|
return (-1); |
464 |
|
|
|
465 |
|
|
TAILQ_FOREACH(la, conf->listen_addrs, entry) { |
466 |
|
|
if (imsg_compose(ibuf_se, IMSG_RECONF_LISTENER, 0, 0, la->fd, |
467 |
|
|
la, sizeof(struct listen_addr)) == -1) |
468 |
|
|
return (-1); |
469 |
|
|
la->fd = -1; |
470 |
|
|
} |
471 |
|
|
|
472 |
|
|
if (control_setup(conf) == -1) |
473 |
|
|
return (-1); |
474 |
|
|
|
475 |
|
|
/* adjust fib syncing on reload */ |
476 |
|
|
ktable_preload(); |
477 |
|
|
|
478 |
|
|
/* RIBs for the RDE */ |
479 |
|
|
while ((rr = SIMPLEQ_FIRST(&ribnames))) { |
480 |
|
|
SIMPLEQ_REMOVE_HEAD(&ribnames, entry); |
481 |
|
|
if (ktable_update(rr->rtableid, rr->name, NULL, |
482 |
|
|
rr->flags, conf->fib_priority) == -1) { |
483 |
|
|
log_warnx("failed to load rdomain %d", |
484 |
|
|
rr->rtableid); |
485 |
|
|
return (-1); |
486 |
|
|
} |
487 |
|
|
if (imsg_compose(ibuf_rde, IMSG_RECONF_RIB, 0, 0, -1, |
488 |
|
|
rr, sizeof(struct rde_rib)) == -1) |
489 |
|
|
return (-1); |
490 |
|
|
free(rr); |
491 |
|
|
} |
492 |
|
|
|
493 |
|
|
/* send peer list to the SE */ |
494 |
|
|
for (p = *peer_l; p != NULL; p = p->next) { |
495 |
|
|
if (imsg_compose(ibuf_se, IMSG_RECONF_PEER, p->conf.id, 0, -1, |
496 |
|
|
&p->conf, sizeof(struct peer_config)) == -1) |
497 |
|
|
return (-1); |
498 |
|
|
} |
499 |
|
|
|
500 |
|
|
/* networks go via kroute to the RDE */ |
501 |
|
|
if (kr_net_reload(0, &conf->networks)) |
502 |
|
|
return (-1); |
503 |
|
|
|
504 |
|
|
/* filters for the RDE */ |
505 |
|
|
while ((r = TAILQ_FIRST(conf->filters)) != NULL) { |
506 |
|
|
TAILQ_REMOVE(conf->filters, r, entry); |
507 |
|
|
if (imsg_compose(ibuf_rde, IMSG_RECONF_FILTER, 0, 0, -1, |
508 |
|
|
r, sizeof(struct filter_rule)) == -1) |
509 |
|
|
return (-1); |
510 |
|
|
if (send_filterset(ibuf_rde, &r->set) == -1) |
511 |
|
|
return (-1); |
512 |
|
|
filterset_free(&r->set); |
513 |
|
|
free(r); |
514 |
|
|
} |
515 |
|
|
|
516 |
|
|
while ((rd = SIMPLEQ_FIRST(&conf->rdomains)) != NULL) { |
517 |
|
|
SIMPLEQ_REMOVE_HEAD(&conf->rdomains, entry); |
518 |
|
|
if (ktable_update(rd->rtableid, rd->descr, rd->ifmpe, |
519 |
|
|
rd->flags, conf->fib_priority) == -1) { |
520 |
|
|
log_warnx("failed to load rdomain %d", |
521 |
|
|
rd->rtableid); |
522 |
|
|
return (-1); |
523 |
|
|
} |
524 |
|
|
/* networks go via kroute to the RDE */ |
525 |
|
|
if (kr_net_reload(rd->rtableid, &rd->net_l)) |
526 |
|
|
return (-1); |
527 |
|
|
|
528 |
|
|
if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN, 0, 0, -1, |
529 |
|
|
rd, sizeof(*rd)) == -1) |
530 |
|
|
return (-1); |
531 |
|
|
|
532 |
|
|
/* export targets */ |
533 |
|
|
if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_EXPORT, 0, 0, |
534 |
|
|
-1, NULL, 0) == -1) |
535 |
|
|
return (-1); |
536 |
|
|
if (send_filterset(ibuf_rde, &rd->export) == -1) |
537 |
|
|
return (-1); |
538 |
|
|
filterset_free(&rd->export); |
539 |
|
|
|
540 |
|
|
/* import targets */ |
541 |
|
|
if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_IMPORT, 0, 0, |
542 |
|
|
-1, NULL, 0) == -1) |
543 |
|
|
return (-1); |
544 |
|
|
if (send_filterset(ibuf_rde, &rd->import) == -1) |
545 |
|
|
return (-1); |
546 |
|
|
filterset_free(&rd->import); |
547 |
|
|
|
548 |
|
|
if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_DONE, 0, 0, |
549 |
|
|
-1, NULL, 0) == -1) |
550 |
|
|
return (-1); |
551 |
|
|
|
552 |
|
|
free(rd); |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
/* signal the SE first then the RDE to activate the new config */ |
556 |
|
|
if (imsg_compose(ibuf_se, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1) |
557 |
|
|
return (-1); |
558 |
|
|
|
559 |
|
|
/* mrt changes can be sent out of bound */ |
560 |
|
|
mrt_reconfigure(conf->mrt); |
561 |
|
|
return (0); |
562 |
|
|
} |
563 |
|
|
|
564 |
|
|
int |
565 |
|
|
dispatch_imsg(struct imsgbuf *ibuf, int idx, struct bgpd_config *conf) |
566 |
|
|
{ |
567 |
|
|
struct imsg imsg; |
568 |
|
|
ssize_t n; |
569 |
|
|
int rv, verbose; |
570 |
|
|
|
571 |
|
|
rv = 0; |
572 |
|
|
while (ibuf) { |
573 |
|
|
if ((n = imsg_get(ibuf, &imsg)) == -1) |
574 |
|
|
return (-1); |
575 |
|
|
|
576 |
|
|
if (n == 0) |
577 |
|
|
break; |
578 |
|
|
|
579 |
|
|
switch (imsg.hdr.type) { |
580 |
|
|
case IMSG_KROUTE_CHANGE: |
581 |
|
|
if (idx != PFD_PIPE_ROUTE) |
582 |
|
|
log_warnx("route request not from RDE"); |
583 |
|
|
else if (imsg.hdr.len != IMSG_HEADER_SIZE + |
584 |
|
|
sizeof(struct kroute_full)) |
585 |
|
|
log_warnx("wrong imsg len"); |
586 |
|
|
else if (kr_change(imsg.hdr.peerid, imsg.data, |
587 |
|
|
conf->fib_priority)) |
588 |
|
|
rv = -1; |
589 |
|
|
break; |
590 |
|
|
case IMSG_KROUTE_DELETE: |
591 |
|
|
if (idx != PFD_PIPE_ROUTE) |
592 |
|
|
log_warnx("route request not from RDE"); |
593 |
|
|
else if (imsg.hdr.len != IMSG_HEADER_SIZE + |
594 |
|
|
sizeof(struct kroute_full)) |
595 |
|
|
log_warnx("wrong imsg len"); |
596 |
|
|
else if (kr_delete(imsg.hdr.peerid, imsg.data, |
597 |
|
|
conf->fib_priority)) |
598 |
|
|
rv = -1; |
599 |
|
|
break; |
600 |
|
|
case IMSG_NEXTHOP_ADD: |
601 |
|
|
if (idx != PFD_PIPE_ROUTE) |
602 |
|
|
log_warnx("nexthop request not from RDE"); |
603 |
|
|
else if (imsg.hdr.len != IMSG_HEADER_SIZE + |
604 |
|
|
sizeof(struct bgpd_addr)) |
605 |
|
|
log_warnx("wrong imsg len"); |
606 |
|
|
else if (kr_nexthop_add(imsg.hdr.peerid, imsg.data, |
607 |
|
|
conf) == -1) |
608 |
|
|
rv = -1; |
609 |
|
|
break; |
610 |
|
|
case IMSG_NEXTHOP_REMOVE: |
611 |
|
|
if (idx != PFD_PIPE_ROUTE) |
612 |
|
|
log_warnx("nexthop request not from RDE"); |
613 |
|
|
else if (imsg.hdr.len != IMSG_HEADER_SIZE + |
614 |
|
|
sizeof(struct bgpd_addr)) |
615 |
|
|
log_warnx("wrong imsg len"); |
616 |
|
|
else |
617 |
|
|
kr_nexthop_delete(imsg.hdr.peerid, imsg.data, |
618 |
|
|
conf); |
619 |
|
|
break; |
620 |
|
|
case IMSG_PFTABLE_ADD: |
621 |
|
|
if (idx != PFD_PIPE_ROUTE) |
622 |
|
|
log_warnx("pftable request not from RDE"); |
623 |
|
|
else |
624 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + |
625 |
|
|
sizeof(struct pftable_msg)) |
626 |
|
|
log_warnx("wrong imsg len"); |
627 |
|
|
else if (pftable_addr_add(imsg.data) != 0) |
628 |
|
|
rv = -1; |
629 |
|
|
break; |
630 |
|
|
case IMSG_PFTABLE_REMOVE: |
631 |
|
|
if (idx != PFD_PIPE_ROUTE) |
632 |
|
|
log_warnx("pftable request not from RDE"); |
633 |
|
|
else |
634 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + |
635 |
|
|
sizeof(struct pftable_msg)) |
636 |
|
|
log_warnx("wrong imsg len"); |
637 |
|
|
else if (pftable_addr_remove(imsg.data) != 0) |
638 |
|
|
rv = -1; |
639 |
|
|
break; |
640 |
|
|
case IMSG_PFTABLE_COMMIT: |
641 |
|
|
if (idx != PFD_PIPE_ROUTE) |
642 |
|
|
log_warnx("pftable request not from RDE"); |
643 |
|
|
else |
644 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE) |
645 |
|
|
log_warnx("wrong imsg len"); |
646 |
|
|
else if (pftable_commit() != 0) |
647 |
|
|
rv = -1; |
648 |
|
|
break; |
649 |
|
|
case IMSG_CTL_RELOAD: |
650 |
|
|
if (idx != PFD_PIPE_SESSION) |
651 |
|
|
log_warnx("reload request not from SE"); |
652 |
|
|
else { |
653 |
|
|
reconfig = 1; |
654 |
|
|
reconfpid = imsg.hdr.pid; |
655 |
|
|
} |
656 |
|
|
break; |
657 |
|
|
case IMSG_CTL_FIB_COUPLE: |
658 |
|
|
if (idx != PFD_PIPE_SESSION) |
659 |
|
|
log_warnx("couple request not from SE"); |
660 |
|
|
else |
661 |
|
|
kr_fib_couple(imsg.hdr.peerid, |
662 |
|
|
conf->fib_priority); |
663 |
|
|
break; |
664 |
|
|
case IMSG_CTL_FIB_DECOUPLE: |
665 |
|
|
if (idx != PFD_PIPE_SESSION) |
666 |
|
|
log_warnx("decouple request not from SE"); |
667 |
|
|
else |
668 |
|
|
kr_fib_decouple(imsg.hdr.peerid, |
669 |
|
|
conf->fib_priority); |
670 |
|
|
break; |
671 |
|
|
case IMSG_CTL_KROUTE: |
672 |
|
|
case IMSG_CTL_KROUTE_ADDR: |
673 |
|
|
case IMSG_CTL_SHOW_NEXTHOP: |
674 |
|
|
case IMSG_CTL_SHOW_INTERFACE: |
675 |
|
|
case IMSG_CTL_SHOW_FIB_TABLES: |
676 |
|
|
if (idx != PFD_PIPE_SESSION) |
677 |
|
|
log_warnx("kroute request not from SE"); |
678 |
|
|
else |
679 |
|
|
kr_show_route(&imsg); |
680 |
|
|
break; |
681 |
|
|
case IMSG_IFINFO: |
682 |
|
|
if (idx != PFD_PIPE_SESSION) |
683 |
|
|
log_warnx("IFINFO request not from SE"); |
684 |
|
|
else if (imsg.hdr.len != IMSG_HEADER_SIZE + IFNAMSIZ) |
685 |
|
|
log_warnx("IFINFO request with wrong len"); |
686 |
|
|
else |
687 |
|
|
kr_ifinfo(imsg.data); |
688 |
|
|
break; |
689 |
|
|
case IMSG_DEMOTE: |
690 |
|
|
if (idx != PFD_PIPE_SESSION) |
691 |
|
|
log_warnx("demote request not from SE"); |
692 |
|
|
else if (imsg.hdr.len != IMSG_HEADER_SIZE + |
693 |
|
|
sizeof(struct demote_msg)) |
694 |
|
|
log_warnx("DEMOTE request with wrong len"); |
695 |
|
|
else { |
696 |
|
|
struct demote_msg *msg; |
697 |
|
|
|
698 |
|
|
msg = imsg.data; |
699 |
|
|
carp_demote_set(msg->demote_group, msg->level); |
700 |
|
|
} |
701 |
|
|
break; |
702 |
|
|
case IMSG_CTL_LOG_VERBOSE: |
703 |
|
|
/* already checked by SE */ |
704 |
|
|
memcpy(&verbose, imsg.data, sizeof(verbose)); |
705 |
|
|
log_setverbose(verbose); |
706 |
|
|
break; |
707 |
|
|
case IMSG_RECONF_DONE: |
708 |
|
|
if (reconfpending == 0) |
709 |
|
|
log_warnx("unexpected RECONF_DONE received"); |
710 |
|
|
else if (reconfpending == 2) { |
711 |
|
|
imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0, |
712 |
|
|
0, -1, NULL, 0); |
713 |
|
|
|
714 |
|
|
/* finally fix kroute information */ |
715 |
|
|
ktable_postload(conf->fib_priority); |
716 |
|
|
|
717 |
|
|
/* redistribute list needs to be reloaded too */ |
718 |
|
|
kr_reload(); |
719 |
|
|
} |
720 |
|
|
reconfpending--; |
721 |
|
|
break; |
722 |
|
|
default: |
723 |
|
|
break; |
724 |
|
|
} |
725 |
|
|
imsg_free(&imsg); |
726 |
|
|
if (rv != 0) |
727 |
|
|
return (rv); |
728 |
|
|
} |
729 |
|
|
return (0); |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
void |
733 |
|
|
send_nexthop_update(struct kroute_nexthop *msg) |
734 |
|
|
{ |
735 |
|
|
char *gw = NULL; |
736 |
|
|
|
737 |
|
|
if (msg->gateway.aid) |
738 |
|
|
if (asprintf(&gw, ": via %s", |
739 |
|
|
log_addr(&msg->gateway)) == -1) { |
740 |
|
|
log_warn("send_nexthop_update"); |
741 |
|
|
quit = 1; |
742 |
|
|
} |
743 |
|
|
|
744 |
|
|
log_debug("nexthop %s now %s%s%s", log_addr(&msg->nexthop), |
745 |
|
|
msg->valid ? "valid" : "invalid", |
746 |
|
|
msg->connected ? ": directly connected" : "", |
747 |
|
|
msg->gateway.aid ? gw : ""); |
748 |
|
|
|
749 |
|
|
free(gw); |
750 |
|
|
|
751 |
|
|
if (imsg_compose(ibuf_rde, IMSG_NEXTHOP_UPDATE, 0, 0, -1, |
752 |
|
|
msg, sizeof(struct kroute_nexthop)) == -1) |
753 |
|
|
quit = 1; |
754 |
|
|
} |
755 |
|
|
|
756 |
|
|
void |
757 |
|
|
send_imsg_session(int type, pid_t pid, void *data, u_int16_t datalen) |
758 |
|
|
{ |
759 |
|
|
imsg_compose(ibuf_se, type, 0, pid, -1, data, datalen); |
760 |
|
|
} |
761 |
|
|
|
762 |
|
|
int |
763 |
|
|
send_network(int type, struct network_config *net, struct filter_set_head *h) |
764 |
|
|
{ |
765 |
|
|
if (imsg_compose(ibuf_rde, type, 0, 0, -1, net, |
766 |
|
|
sizeof(struct network_config)) == -1) |
767 |
|
|
return (-1); |
768 |
|
|
/* networks that get deleted don't need to send the filter set */ |
769 |
|
|
if (type == IMSG_NETWORK_REMOVE) |
770 |
|
|
return (0); |
771 |
|
|
if (send_filterset(ibuf_rde, h) == -1) |
772 |
|
|
return (-1); |
773 |
|
|
if (imsg_compose(ibuf_rde, IMSG_NETWORK_DONE, 0, 0, -1, NULL, 0) == -1) |
774 |
|
|
return (-1); |
775 |
|
|
|
776 |
|
|
return (0); |
777 |
|
|
} |
778 |
|
|
|
779 |
|
|
int |
780 |
|
|
bgpd_filternexthop(struct kroute *kr, struct kroute6 *kr6) |
781 |
|
|
{ |
782 |
|
|
/* kernel routes are never filtered */ |
783 |
|
|
if (kr && kr->flags & F_KERNEL && kr->prefixlen != 0) |
784 |
|
|
return (0); |
785 |
|
|
if (kr6 && kr6->flags & F_KERNEL && kr6->prefixlen != 0) |
786 |
|
|
return (0); |
787 |
|
|
|
788 |
|
|
if (cflags & BGPD_FLAG_NEXTHOP_BGP) { |
789 |
|
|
if (kr && kr->flags & F_BGPD_INSERTED) |
790 |
|
|
return (0); |
791 |
|
|
if (kr6 && kr6->flags & F_BGPD_INSERTED) |
792 |
|
|
return (0); |
793 |
|
|
} |
794 |
|
|
|
795 |
|
|
if (cflags & BGPD_FLAG_NEXTHOP_DEFAULT) { |
796 |
|
|
if (kr && kr->prefixlen == 0) |
797 |
|
|
return (0); |
798 |
|
|
if (kr6 && kr6->prefixlen == 0) |
799 |
|
|
return (0); |
800 |
|
|
} |
801 |
|
|
|
802 |
|
|
return (1); |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
int |
806 |
|
|
control_setup(struct bgpd_config *conf) |
807 |
|
|
{ |
808 |
|
|
int fd, restricted; |
809 |
|
|
|
810 |
|
|
/* control socket is outside chroot */ |
811 |
|
|
if (!cname || strcmp(cname, conf->csock)) { |
812 |
|
|
if (cname) { |
813 |
|
|
control_cleanup(cname); |
814 |
|
|
free(cname); |
815 |
|
|
} |
816 |
|
|
if ((cname = strdup(conf->csock)) == NULL) |
817 |
|
|
fatal("strdup"); |
818 |
|
|
if ((fd = control_init(0, cname)) == -1) |
819 |
|
|
fatalx("control socket setup failed"); |
820 |
|
|
if (control_listen(fd) == -1) |
821 |
|
|
fatalx("control socket setup failed"); |
822 |
|
|
restricted = 0; |
823 |
|
|
if (imsg_compose(ibuf_se, IMSG_RECONF_CTRL, 0, 0, fd, |
824 |
|
|
&restricted, sizeof(restricted)) == -1) |
825 |
|
|
return (-1); |
826 |
|
|
} |
827 |
|
|
if (!conf->rcsock) { |
828 |
|
|
/* remove restricted socket */ |
829 |
|
|
control_cleanup(rcname); |
830 |
|
|
free(rcname); |
831 |
|
|
rcname = NULL; |
832 |
|
|
} else if (!rcname || strcmp(rcname, conf->rcsock)) { |
833 |
|
|
if (rcname) { |
834 |
|
|
control_cleanup(rcname); |
835 |
|
|
free(rcname); |
836 |
|
|
} |
837 |
|
|
if ((rcname = strdup(conf->rcsock)) == NULL) |
838 |
|
|
fatal("strdup"); |
839 |
|
|
if ((fd = control_init(1, rcname)) == -1) |
840 |
|
|
fatalx("control socket setup failed"); |
841 |
|
|
if (control_listen(fd) == -1) |
842 |
|
|
fatalx("control socket setup failed"); |
843 |
|
|
restricted = 1; |
844 |
|
|
if (imsg_compose(ibuf_se, IMSG_RECONF_CTRL, 0, 0, fd, |
845 |
|
|
&restricted, sizeof(restricted)) == -1) |
846 |
|
|
return (-1); |
847 |
|
|
} |
848 |
|
|
return (0); |
849 |
|
|
} |
850 |
|
|
|
851 |
|
|
void |
852 |
|
|
set_pollfd(struct pollfd *pfd, struct imsgbuf *i) |
853 |
|
|
{ |
854 |
|
|
if (i == NULL || i->fd == -1) { |
855 |
|
|
pfd->fd = -1; |
856 |
|
|
return; |
857 |
|
|
} |
858 |
|
|
pfd->fd = i->fd; |
859 |
|
|
pfd->events = POLLIN; |
860 |
|
|
if (i->w.queued > 0) |
861 |
|
|
pfd->events |= POLLOUT; |
862 |
|
|
} |
863 |
|
|
|
864 |
|
|
int |
865 |
|
|
handle_pollfd(struct pollfd *pfd, struct imsgbuf *i) |
866 |
|
|
{ |
867 |
|
|
ssize_t n; |
868 |
|
|
|
869 |
|
|
if (i == NULL) |
870 |
|
|
return (0); |
871 |
|
|
|
872 |
|
|
if (pfd->revents & POLLOUT) |
873 |
|
|
if (msgbuf_write(&i->w) <= 0 && errno != EAGAIN) { |
874 |
|
|
log_warn("imsg write error"); |
875 |
|
|
close(i->fd); |
876 |
|
|
i->fd = -1; |
877 |
|
|
return (-1); |
878 |
|
|
} |
879 |
|
|
|
880 |
|
|
if (pfd->revents & POLLIN) { |
881 |
|
|
if ((n = imsg_read(i)) == -1 && errno != EAGAIN) { |
882 |
|
|
log_warn("imsg read error"); |
883 |
|
|
close(i->fd); |
884 |
|
|
i->fd = -1; |
885 |
|
|
return (-1); |
886 |
|
|
} |
887 |
|
|
if (n == 0) { |
888 |
|
|
log_warnx("peer closed imsg connection"); |
889 |
|
|
close(i->fd); |
890 |
|
|
i->fd = -1; |
891 |
|
|
return (-1); |
892 |
|
|
} |
893 |
|
|
} |
894 |
|
|
return (0); |
895 |
|
|
} |
896 |
|
|
|
897 |
|
|
int |
898 |
|
|
imsg_send_sockets(struct imsgbuf *se, struct imsgbuf *rde) |
899 |
|
|
{ |
900 |
|
|
int pipe_s2r[2]; |
901 |
|
|
int pipe_s2r_ctl[2]; |
902 |
|
|
|
903 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, |
904 |
|
|
PF_UNSPEC, pipe_s2r) == -1) |
905 |
|
|
return (-1); |
906 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, |
907 |
|
|
PF_UNSPEC, pipe_s2r_ctl) == -1) |
908 |
|
|
return (-1); |
909 |
|
|
|
910 |
|
|
if (imsg_compose(se, IMSG_SOCKET_CONN, 0, 0, pipe_s2r[0], |
911 |
|
|
NULL, 0) == -1) |
912 |
|
|
return (-1); |
913 |
|
|
if (imsg_compose(rde, IMSG_SOCKET_CONN, 0, 0, pipe_s2r[1], |
914 |
|
|
NULL, 0) == -1) |
915 |
|
|
return (-1); |
916 |
|
|
|
917 |
|
|
if (imsg_compose(se, IMSG_SOCKET_CONN_CTL, 0, 0, pipe_s2r_ctl[0], |
918 |
|
|
NULL, 0) == -1) |
919 |
|
|
return (-1); |
920 |
|
|
if (imsg_compose(rde, IMSG_SOCKET_CONN_CTL, 0, 0, pipe_s2r_ctl[1], |
921 |
|
|
NULL, 0) == -1) |
922 |
|
|
return (-1); |
923 |
|
|
|
924 |
|
|
return (0); |
925 |
|
|
} |