]> git.ipfire.org Git - thirdparty/bird.git/blob - sysdep/unix/main.c
The generalized TTL security mechanism (RFC 5082) support.
[thirdparty/bird.git] / sysdep / unix / main.c
1 /*
2 * BIRD Internet Routing Daemon -- Unix Entry Point
3 *
4 * (c) 1998--2000 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
9 #undef LOCAL_DEBUG
10
11 #define _GNU_SOURCE 1
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <signal.h>
18 #include <pwd.h>
19 #include <grp.h>
20 #include <sys/stat.h>
21
22 #include "nest/bird.h"
23 #include "lib/lists.h"
24 #include "lib/resource.h"
25 #include "lib/socket.h"
26 #include "lib/event.h"
27 #include "lib/string.h"
28 #include "nest/route.h"
29 #include "nest/protocol.h"
30 #include "nest/iface.h"
31 #include "nest/cli.h"
32 #include "nest/locks.h"
33 #include "conf/conf.h"
34 #include "filter/filter.h"
35
36 #include "unix.h"
37 #include "krt.h"
38
39 /*
40 * Debugging
41 */
42
43 #ifdef DEBUGGING
44 static int debug_flag = 1;
45 #else
46 static int debug_flag = 0;
47 #endif
48
49 void
50 async_dump(void)
51 {
52 debug("INTERNAL STATE DUMP\n\n");
53
54 rdump(&root_pool);
55 sk_dump_all();
56 tm_dump_all();
57 if_dump_all();
58 neigh_dump_all();
59 rta_dump_all();
60 rt_dump_all();
61 protos_dump_all();
62
63 debug("\n");
64 }
65
66 /*
67 * Dropping privileges
68 */
69
70 #ifdef CONFIG_RESTRICTED_PRIVILEGES
71 #include "lib/syspriv.h"
72 #else
73
74 static inline void
75 drop_uid(uid_t uid)
76 {
77 die("Cannot change user on this platform");
78 }
79
80 #endif
81
82 static inline void
83 drop_gid(gid_t gid)
84 {
85 if (setgid(gid) < 0)
86 die("setgid: %m");
87 }
88
89 /*
90 * Reading the Configuration
91 */
92
93 #ifdef PATH_IPROUTE_DIR
94
95 static inline void
96 add_num_const(char *name, int val)
97 {
98 struct symbol *s = cf_find_symbol(name);
99 s->class = SYM_NUMBER;
100 s->def = NULL;
101 s->aux = val;
102 }
103
104 /* the code of read_iproute_table() is based on
105 rtnl_tab_initialize() from iproute2 package */
106 static void
107 read_iproute_table(char *file, char *prefix, int max)
108 {
109 char buf[512], namebuf[512];
110 char *name;
111 int val;
112 FILE *fp;
113
114 strcpy(namebuf, prefix);
115 name = namebuf + strlen(prefix);
116
117 fp = fopen(file, "r");
118 if (!fp)
119 return;
120
121 while (fgets(buf, sizeof(buf), fp))
122 {
123 char *p = buf;
124
125 while (*p == ' ' || *p == '\t')
126 p++;
127
128 if (*p == '#' || *p == '\n' || *p == 0)
129 continue;
130
131 if (sscanf(p, "0x%x %s\n", &val, name) != 2 &&
132 sscanf(p, "0x%x %s #", &val, name) != 2 &&
133 sscanf(p, "%d %s\n", &val, name) != 2 &&
134 sscanf(p, "%d %s #", &val, name) != 2)
135 continue;
136
137 if (val < 0 || val > max)
138 continue;
139
140 for(p = name; *p; p++)
141 if ((*p < 'a' || *p > 'z') && (*p < '0' || *p > '9') && (*p != '_'))
142 *p = '_';
143
144 add_num_const(namebuf, val);
145 }
146
147 fclose(fp);
148 }
149
150 #endif // PATH_IPROUTE_DIR
151
152
153 static int conf_fd;
154 static char *config_name = PATH_CONFIG;
155
156 static int
157 cf_read(byte *dest, unsigned int len)
158 {
159 int l = read(conf_fd, dest, len);
160 if (l < 0)
161 cf_error("Read error");
162 return l;
163 }
164
165 void
166 sysdep_preconfig(struct config *c)
167 {
168 init_list(&c->logfiles);
169
170 #ifdef PATH_IPROUTE_DIR
171 // read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256);
172 read_iproute_table(PATH_IPROUTE_DIR "/rt_realms", "ipr_", 256);
173 read_iproute_table(PATH_IPROUTE_DIR "/rt_scopes", "ips_", 256);
174 read_iproute_table(PATH_IPROUTE_DIR "/rt_tables", "ipt_", 256);
175 #endif
176 }
177
178 int
179 sysdep_commit(struct config *new, struct config *old UNUSED)
180 {
181 log_switch(debug_flag, &new->logfiles, new->syslog_name);
182 return 0;
183 }
184
185 static int
186 unix_read_config(struct config **cp, char *name)
187 {
188 struct config *conf = config_alloc(name);
189 int ret;
190
191 *cp = conf;
192 conf_fd = open(name, O_RDONLY);
193 if (conf_fd < 0)
194 return 0;
195 cf_read_hook = cf_read;
196 ret = config_parse(conf);
197 close(conf_fd);
198 return ret;
199 }
200
201 static void
202 read_config(void)
203 {
204 struct config *conf;
205
206 if (!unix_read_config(&conf, config_name))
207 {
208 if (conf->err_msg)
209 die("%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
210 else
211 die("Unable to open configuration file %s: %m", config_name);
212 }
213 config_commit(conf, RECONFIG_HARD);
214 }
215
216 void
217 async_config(void)
218 {
219 struct config *conf;
220
221 log(L_INFO "Reconfiguration requested by SIGHUP");
222 if (!unix_read_config(&conf, config_name))
223 {
224 if (conf->err_msg)
225 log(L_ERR "%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
226 else
227 log(L_ERR "Unable to open configuration file %s: %m", config_name);
228 config_free(conf);
229 }
230 else
231 config_commit(conf, RECONFIG_HARD);
232 }
233
234 void
235 cmd_reconfig(char *name, int type)
236 {
237 struct config *conf;
238
239 if (cli_access_restricted())
240 return;
241
242 if (!name)
243 name = config_name;
244 cli_msg(-2, "Reading configuration from %s", name);
245 if (!unix_read_config(&conf, name))
246 {
247 if (conf->err_msg)
248 cli_msg(8002, "%s, line %d: %s", name, conf->err_lino, conf->err_msg);
249 else
250 cli_msg(8002, "%s: %m", name);
251 config_free(conf);
252 }
253 else
254 {
255 switch (config_commit(conf, type))
256 {
257 case CONF_DONE:
258 cli_msg(3, "Reconfigured.");
259 break;
260 case CONF_PROGRESS:
261 cli_msg(4, "Reconfiguration in progress.");
262 break;
263 case CONF_SHUTDOWN:
264 cli_msg(6, "Reconfiguration ignored, shutting down.");
265 break;
266 default:
267 cli_msg(5, "Reconfiguration already in progress, queueing new config");
268 }
269 }
270 }
271
272 /*
273 * Command-Line Interface
274 */
275
276 static sock *cli_sk;
277 static char *path_control_socket = PATH_CONTROL_SOCKET;
278
279
280 static void
281 cli_write(cli *c)
282 {
283 sock *s = c->priv;
284
285 while (c->tx_pos)
286 {
287 struct cli_out *o = c->tx_pos;
288
289 int len = o->wpos - o->outpos;
290 s->tbuf = o->outpos;
291 o->outpos = o->wpos;
292
293 if (sk_send(s, len) <= 0)
294 return;
295
296 c->tx_pos = o->next;
297 }
298
299 /* Everything is written */
300 s->tbuf = NULL;
301 cli_written(c);
302 }
303
304 void
305 cli_write_trigger(cli *c)
306 {
307 sock *s = c->priv;
308
309 if (s->tbuf == NULL)
310 cli_write(c);
311 }
312
313 static void
314 cli_tx(sock *s)
315 {
316 cli_write(s->data);
317 }
318
319 int
320 cli_get_command(cli *c)
321 {
322 sock *s = c->priv;
323 byte *t = c->rx_aux ? : s->rbuf;
324 byte *tend = s->rpos;
325 byte *d = c->rx_pos;
326 byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2;
327
328 while (t < tend)
329 {
330 if (*t == '\r')
331 t++;
332 else if (*t == '\n')
333 {
334 t++;
335 c->rx_pos = c->rx_buf;
336 c->rx_aux = t;
337 *d = 0;
338 return (d < dend) ? 1 : -1;
339 }
340 else if (d < dend)
341 *d++ = *t++;
342 }
343 c->rx_aux = s->rpos = s->rbuf;
344 c->rx_pos = d;
345 return 0;
346 }
347
348 static int
349 cli_rx(sock *s, int size UNUSED)
350 {
351 cli_kick(s->data);
352 return 0;
353 }
354
355 static void
356 cli_err(sock *s, int err)
357 {
358 if (config->cli_debug)
359 {
360 if (err)
361 log(L_INFO "CLI connection dropped: %s", strerror(err));
362 else
363 log(L_INFO "CLI connection closed");
364 }
365 cli_free(s->data);
366 }
367
368 static int
369 cli_connect(sock *s, int size UNUSED)
370 {
371 cli *c;
372
373 if (config->cli_debug)
374 log(L_INFO "CLI connect");
375 s->rx_hook = cli_rx;
376 s->tx_hook = cli_tx;
377 s->err_hook = cli_err;
378 s->data = c = cli_new(s);
379 s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
380 c->rx_pos = c->rx_buf;
381 c->rx_aux = NULL;
382 rmove(s, c->pool);
383 return 1;
384 }
385
386 static void
387 cli_init_unix(uid_t use_uid, gid_t use_gid)
388 {
389 sock *s;
390
391 cli_init();
392 s = cli_sk = sk_new(cli_pool);
393 s->type = SK_UNIX_PASSIVE;
394 s->rx_hook = cli_connect;
395 s->rbsize = 1024;
396 sk_open_unix(s, path_control_socket);
397
398 if (use_uid || use_gid)
399 if (chown(path_control_socket, use_uid, use_gid) < 0)
400 die("chown: %m");
401
402 if (chmod(path_control_socket, 0660) < 0)
403 die("chmod: %m");
404 }
405
406 /*
407 * Shutdown
408 */
409
410 void
411 cmd_shutdown(void)
412 {
413 if (cli_access_restricted())
414 return;
415
416 cli_msg(7, "Shutdown requested");
417 order_shutdown();
418 }
419
420 void
421 async_shutdown(void)
422 {
423 DBG("Shutting down...\n");
424 order_shutdown();
425 }
426
427 void
428 sysdep_shutdown_done(void)
429 {
430 unlink(path_control_socket);
431 log_msg(L_FATAL "Shutdown completed");
432 exit(0);
433 }
434
435 /*
436 * Signals
437 */
438
439 static void
440 handle_sighup(int sig UNUSED)
441 {
442 DBG("Caught SIGHUP...\n");
443 async_config_flag = 1;
444 }
445
446 static void
447 handle_sigusr(int sig UNUSED)
448 {
449 DBG("Caught SIGUSR...\n");
450 async_dump_flag = 1;
451 }
452
453 static void
454 handle_sigterm(int sig UNUSED)
455 {
456 DBG("Caught SIGTERM...\n");
457 async_shutdown_flag = 1;
458 }
459
460 static void
461 signal_init(void)
462 {
463 struct sigaction sa;
464
465 bzero(&sa, sizeof(sa));
466 sa.sa_handler = handle_sigusr;
467 sa.sa_flags = SA_RESTART;
468 sigaction(SIGUSR1, &sa, NULL);
469 sa.sa_handler = handle_sighup;
470 sa.sa_flags = SA_RESTART;
471 sigaction(SIGHUP, &sa, NULL);
472 sa.sa_handler = handle_sigterm;
473 sa.sa_flags = SA_RESTART;
474 sigaction(SIGTERM, &sa, NULL);
475 signal(SIGPIPE, SIG_IGN);
476 }
477
478 /*
479 * Parsing of command-line arguments
480 */
481
482 static char *opt_list = "c:dD:ps:u:g:";
483 static int parse_and_exit;
484 char *bird_name;
485 static char *use_user;
486 static char *use_group;
487
488 static void
489 usage(void)
490 {
491 fprintf(stderr, "Usage: %s [-c <config-file>] [-d] [-D <debug-file>] [-p] [-s <control-socket>] [-u <user>] [-g <group>]\n", bird_name);
492 exit(1);
493 }
494
495 static inline char *
496 get_bird_name(char *s, char *def)
497 {
498 char *t;
499 if (!s)
500 return def;
501 t = strrchr(s, '/');
502 if (!t)
503 return s;
504 if (!t[1])
505 return def;
506 return t+1;
507 }
508
509 static inline uid_t
510 get_uid(const char *s)
511 {
512 struct passwd *pw;
513 char *endptr;
514 long int rv;
515
516 if (!s)
517 return 0;
518
519 errno = 0;
520 rv = strtol(s, &endptr, 10);
521
522 if (!errno && !*endptr)
523 return rv;
524
525 pw = getpwnam(s);
526 if (!pw)
527 die("Cannot find user '%s'", s);
528
529 return pw->pw_uid;
530 }
531
532 static inline gid_t
533 get_gid(const char *s)
534 {
535 struct group *gr;
536 char *endptr;
537 long int rv;
538
539 if (!s)
540 return 0;
541
542 errno = 0;
543 rv = strtol(s, &endptr, 10);
544
545 if (!errno && !*endptr)
546 return rv;
547
548 gr = getgrnam(s);
549 if (!gr)
550 die("Cannot find group '%s'", s);
551
552 return gr->gr_gid;
553 }
554
555 static void
556 parse_args(int argc, char **argv)
557 {
558 int c;
559
560 bird_name = get_bird_name(argv[0], "bird");
561 if (argc == 2)
562 {
563 if (!strcmp(argv[1], "--version"))
564 {
565 fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
566 exit(0);
567 }
568 if (!strcmp(argv[1], "--help"))
569 usage();
570 }
571 while ((c = getopt(argc, argv, opt_list)) >= 0)
572 switch (c)
573 {
574 case 'c':
575 config_name = optarg;
576 break;
577 case 'd':
578 debug_flag |= 1;
579 break;
580 case 'D':
581 log_init_debug(optarg);
582 debug_flag |= 2;
583 break;
584 case 'p':
585 parse_and_exit = 1;
586 break;
587 case 's':
588 path_control_socket = optarg;
589 break;
590 case 'u':
591 use_user = optarg;
592 break;
593 case 'g':
594 use_group = optarg;
595 break;
596 default:
597 usage();
598 }
599 if (optind < argc)
600 usage();
601 }
602
603 /*
604 * Hic Est main()
605 */
606
607 int
608 main(int argc, char **argv)
609 {
610 #ifdef HAVE_LIBDMALLOC
611 if (!getenv("DMALLOC_OPTIONS"))
612 dmalloc_debug(0x2f03d00);
613 #endif
614
615 parse_args(argc, argv);
616 if (debug_flag == 1)
617 log_init_debug("");
618 log_switch(debug_flag, NULL, NULL);
619
620 resource_init();
621 olock_init();
622 io_init();
623 rt_init();
624 if_init();
625
626 uid_t use_uid = get_uid(use_user);
627 gid_t use_gid = get_gid(use_group);
628
629 if (!parse_and_exit)
630 {
631 test_old_bird(path_control_socket);
632 cli_init_unix(use_uid, use_gid);
633 }
634
635 if (use_gid)
636 drop_gid(use_gid);
637
638 if (use_uid)
639 drop_uid(use_uid);
640
641 protos_build();
642 proto_build(&proto_unix_kernel);
643 proto_build(&proto_unix_iface);
644
645 read_config();
646
647 if (parse_and_exit)
648 exit(0);
649
650 if (!debug_flag)
651 {
652 pid_t pid = fork();
653 if (pid < 0)
654 die("fork: %m");
655 if (pid)
656 return 0;
657 setsid();
658 close(0);
659 if (open("/dev/null", O_RDWR) < 0)
660 die("Cannot open /dev/null: %m");
661 dup2(0, 1);
662 dup2(0, 2);
663 }
664
665 signal_init();
666
667 #ifdef LOCAL_DEBUG
668 async_dump_flag = 1;
669 #endif
670
671 log(L_INFO "Started");
672 DBG("Entering I/O loop.\n");
673
674 io_loop();
675 bug("I/O loop died");
676 }