Merge branch 'tomato-ND-USBmod' into tomato-RT
[tomato.git] / release / src / router / radvd / radvd.c
blobe21459e188404b2e8759c509d3ba5eab05375e0a
1 /*
2 * $Id: radvd.c,v 1.61.2.1 2011/08/22 12:28:46 reubenhwk Exp $
4 * Authors:
5 * Pedro Roque <roque@di.fc.ul.pt>
6 * Lars Fenneberg <lf@elemental.net>
8 * This software is Copyright 1996-2000 by the above mentioned author(s),
9 * All Rights Reserved.
11 * The license which is distributed with this software in the file COPYRIGHT
12 * applies to this software. If your distribution is missing this file, you
13 * may request it from <pekkas@netcore.fi>.
17 #include "config.h"
18 #include "includes.h"
19 #include "radvd.h"
20 #include "pathnames.h"
22 #ifdef HAVE_NETLINK
23 #include "netlink.h"
24 #endif
26 #include <poll.h>
28 struct Interface *IfaceList = NULL;
30 #ifdef HAVE_GETOPT_LONG
32 char usage_str[] = {
33 "\n"
34 " -c, --configtest Parse the config file and exit.\n"
35 " -C, --config=PATH Sets the config file. Default is /etc/radvd.conf.\n"
36 " -d, --debug=NUM Sets the debug level. Values can be 1, 2, 3, 4 or 5.\n"
37 " -f, --facility=NUM Sets the logging facility.\n"
38 " -h, --help Show this help screen.\n"
39 " -l, --logfile=PATH Sets the log file.\n"
40 " -m, --logmethod=X Sets the log method to one of: syslog, stderr, stderr_syslog, logfile, or none.\n"
41 " -p, --pidfile=PATH Sets the pid file.\n"
42 " -s, --singleprocess Use privsep.\n"
43 " -t, --chrootdir=PATH Chroot to the specified path.\n"
44 " -u, --username=USER Switch to the specified user.\n"
45 " -v, --version Print the version and quit.\n"
48 struct option prog_opt[] = {
49 {"debug", 1, 0, 'd'},
50 {"configtest", 0, 0, 'c'},
51 {"config", 1, 0, 'C'},
52 {"pidfile", 1, 0, 'p'},
53 {"logfile", 1, 0, 'l'},
54 {"logmethod", 1, 0, 'm'},
55 {"facility", 1, 0, 'f'},
56 {"username", 1, 0, 'u'},
57 {"chrootdir", 1, 0, 't'},
58 {"version", 0, 0, 'v'},
59 {"help", 0, 0, 'h'},
60 {"singleprocess", 0, 0, 's'},
61 {NULL, 0, 0, 0}
64 #else
66 char usage_str[] =
67 "[-hsvc] [-d level] [-C config_file] [-m log_method] [-l log_file]\n"
68 "\t[-f facility] [-p pid_file] [-u username] [-t chrootdir]";
70 #endif
72 extern FILE *yyin;
74 char *conf_file = NULL;
75 char *pname;
76 int sock = -1;
78 volatile int sighup_received = 0;
79 volatile int sigterm_received = 0;
80 volatile int sigint_received = 0;
81 volatile int sigusr1_received = 0;
83 void sighup_handler(int sig);
84 void sigterm_handler(int sig);
85 void sigint_handler(int sig);
86 void sigusr1_handler(int sig);
87 void timer_handler(void *data);
88 void config_interface(void);
89 void kickoff_adverts(void);
90 void stop_adverts(void);
91 void version(void);
92 void usage(void);
93 int drop_root_privileges(const char *);
94 int readin_config(char *);
95 int check_conffile_perm(const char *, const char *);
96 void main_loop(void);
98 int
99 main(int argc, char *argv[])
101 char pidstr[16];
102 ssize_t ret;
103 int c, log_method;
104 char *logfile, *pidfile;
105 int facility, fd;
106 char *username = NULL;
107 char *chrootdir = NULL;
108 int configtest = 0;
109 int singleprocess = 0;
110 #ifdef HAVE_GETOPT_LONG
111 int opt_idx;
112 #endif
114 pname = ((pname=strrchr(argv[0],'/')) != NULL)?pname+1:argv[0];
116 srand((unsigned int)time(NULL));
118 log_method = L_STDERR_SYSLOG;
119 logfile = PATH_RADVD_LOG;
120 conf_file = PATH_RADVD_CONF;
121 facility = LOG_FACILITY;
122 pidfile = PATH_RADVD_PID;
124 /* parse args */
125 #define OPTIONS_STR "d:C:l:m:p:t:u:vhcs"
126 #ifdef HAVE_GETOPT_LONG
127 while ((c = getopt_long(argc, argv, OPTIONS_STR, prog_opt, &opt_idx)) > 0)
128 #else
129 while ((c = getopt(argc, argv, OPTIONS_STR)) > 0)
130 #endif
132 switch (c) {
133 case 'C':
134 conf_file = optarg;
135 break;
136 case 'd':
137 set_debuglevel(atoi(optarg));
138 break;
139 case 'f':
140 facility = atoi(optarg);
141 break;
142 case 'l':
143 logfile = optarg;
144 break;
145 case 'p':
146 pidfile = optarg;
147 break;
148 case 'm':
149 if (!strcmp(optarg, "syslog"))
151 log_method = L_SYSLOG;
153 else if (!strcmp(optarg, "stderr_syslog"))
155 log_method = L_STDERR_SYSLOG;
157 else if (!strcmp(optarg, "stderr"))
159 log_method = L_STDERR;
161 else if (!strcmp(optarg, "logfile"))
163 log_method = L_LOGFILE;
165 else if (!strcmp(optarg, "none"))
167 log_method = L_NONE;
169 else
171 fprintf(stderr, "%s: unknown log method: %s\n", pname, optarg);
172 exit(1);
174 break;
175 case 't':
176 chrootdir = strdup(optarg);
177 break;
178 case 'u':
179 username = strdup(optarg);
180 break;
181 case 'v':
182 version();
183 break;
184 case 'c':
185 configtest = 1;
186 break;
187 case 's':
188 singleprocess = 1;
189 break;
190 case 'h':
191 usage();
192 #ifdef HAVE_GETOPT_LONG
193 case ':':
194 fprintf(stderr, "%s: option %s: parameter expected\n", pname,
195 prog_opt[opt_idx].name);
196 exit(1);
197 #endif
198 case '?':
199 exit(1);
203 if (chrootdir) {
204 if (!username) {
205 fprintf(stderr, "Chroot as root is not safe, exiting\n");
206 exit(1);
209 if (chroot(chrootdir) == -1) {
210 perror("chroot");
211 exit (1);
214 if (chdir("/") == -1) {
215 perror("chdir");
216 exit (1);
218 /* username will be switched later */
221 if (configtest) {
222 log_method = L_STDERR;
225 if (log_open(log_method, pname, logfile, facility) < 0) {
226 perror("log_open");
227 exit(1);
230 if (!configtest) {
231 flog(LOG_INFO, "version %s started", VERSION);
234 /* get a raw socket for sending and receiving ICMPv6 messages */
235 sock = open_icmpv6_socket();
236 if (sock < 0) {
237 perror("open_icmpv6_socket");
238 exit(1);
241 /* check that 'other' cannot write the file
242 * for non-root, also that self/own group can't either
244 if (check_conffile_perm(username, conf_file) < 0) {
245 if (get_debuglevel() == 0) {
246 flog(LOG_ERR, "Exiting, permissions on conf_file invalid.\n");
247 exit(1);
249 else
250 flog(LOG_WARNING, "Insecure file permissions, but continuing anyway");
253 /* if we know how to do it, check whether forwarding is enabled */
254 if (check_ip6_forwarding()) {
255 flog(LOG_WARNING, "IPv6 forwarding seems to be disabled, but continuing anyway.");
258 /* parse config file */
259 if (readin_config(conf_file) < 0) {
260 flog(LOG_ERR, "Exiting, failed to read config file.\n");
261 exit(1);
264 if (configtest) {
265 fprintf(stderr, "Syntax OK\n");
266 exit(0);
269 /* drop root privileges if requested. */
270 if (username) {
271 if (!singleprocess) {
272 dlog(LOG_DEBUG, 3, "Initializing privsep");
273 if (privsep_init() < 0)
274 flog(LOG_WARNING, "Failed to initialize privsep.");
277 if (drop_root_privileges(username) < 0) {
278 perror("drop_root_privileges");
279 exit(1);
283 if ((fd = open(pidfile, O_RDONLY, 0)) > 0)
285 ret = read(fd, pidstr, sizeof(pidstr) - 1);
286 if (ret < 0)
288 flog(LOG_ERR, "cannot read radvd pid file, terminating: %s", strerror(errno));
289 exit(1);
291 pidstr[ret] = '\0';
292 if (!kill((pid_t)atol(pidstr), 0))
294 flog(LOG_ERR, "radvd already running, terminating.");
295 exit(1);
297 close(fd);
298 fd = open(pidfile, O_CREAT|O_TRUNC|O_WRONLY, 0644);
300 else /* FIXME: not atomic if pidfile is on an NFS mounted volume */
301 fd = open(pidfile, O_CREAT|O_EXCL|O_WRONLY, 0644);
303 if (fd < 0)
305 flog(LOG_ERR, "cannot create radvd pid file, terminating: %s", strerror(errno));
306 exit(1);
310 * okay, config file is read in, socket and stuff is setup, so
311 * lets fork now...
314 if (get_debuglevel() == 0) {
316 /* Detach from controlling terminal */
317 if (daemon(0, 0) < 0)
318 perror("daemon");
320 /* close old logfiles, including stderr */
321 log_close();
323 /* reopen logfiles, but don't log to stderr unless explicitly requested */
324 if (log_method == L_STDERR_SYSLOG)
325 log_method = L_SYSLOG;
326 if (log_open(log_method, pname, logfile, facility) < 0) {
327 perror("log_open");
328 exit(1);
334 * config signal handlers
336 signal(SIGHUP, sighup_handler);
337 signal(SIGTERM, sigterm_handler);
338 signal(SIGINT, sigint_handler);
339 signal(SIGUSR1, sigusr1_handler);
341 snprintf(pidstr, sizeof(pidstr), "%ld\n", (long)getpid());
343 ret = write(fd, pidstr, strlen(pidstr));
344 if (ret != strlen(pidstr))
346 flog(LOG_ERR, "cannot write radvd pid file, terminating: %s", strerror(errno));
347 exit(1);
350 close(fd);
352 config_interface();
353 kickoff_adverts();
354 main_loop();
355 stop_adverts();
356 unlink(pidfile);
358 return 0;
361 void main_loop(void)
363 struct pollfd fds[2];
365 memset(fds, 0, sizeof(fds));
367 fds[0].fd = sock;
368 fds[0].events = POLLIN;
369 fds[0].revents = 0;
371 #if HAVE_NETLINK
372 fds[1].fd = netlink_socket();
373 fds[1].events = POLLIN;
374 fds[1].revents = 0;
375 #else
376 fds[1].fd = -1;
377 fds[1].events = 0;
378 fds[1].revents = 0;
379 #endif
381 for (;;) {
382 struct Interface *next = NULL;
383 struct Interface *iface;
384 int timeout = -1;
385 int rc;
387 if (IfaceList) {
388 timeout = next_time_msec(IfaceList);
389 next = IfaceList;
390 for (iface = IfaceList; iface; iface = iface->next) {
391 int t;
392 t = next_time_msec(iface);
393 if (timeout > t) {
394 timeout = t;
395 next = iface;
400 dlog(LOG_DEBUG, 5, "polling for %g seconds.", timeout/1000.0);
402 rc = poll(fds, sizeof(fds)/sizeof(fds[0]), timeout);
404 if (rc > 0) {
405 if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
406 flog(LOG_WARNING, "socket error on fds[0].fd");
408 else if (fds[0].revents & POLLIN) {
409 int len, hoplimit;
410 struct sockaddr_in6 rcv_addr;
411 struct in6_pktinfo *pkt_info = NULL;
412 unsigned char msg[MSG_SIZE_RECV];
414 len = recv_rs_ra(msg, &rcv_addr, &pkt_info, &hoplimit);
415 if (len > 0) {
416 process(IfaceList, msg, len,
417 &rcv_addr, pkt_info, hoplimit);
420 #ifdef HAVE_NETLINK
421 if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) {
422 flog(LOG_WARNING, "socket error on fds[1].fd");
424 else if (fds[1].revents & POLLIN) {
425 process_netlink_msg(fds[1].fd);
427 #endif
429 else if ( rc == 0 ) {
430 if (next)
431 timer_handler(next);
433 else if ( rc == -1 && errno != EINTR ) {
434 flog(LOG_ERR, "poll error: %s", strerror(errno));
437 if (sigterm_received || sigint_received) {
438 flog(LOG_WARNING, "Exiting, sigterm or sigint received.\n");
439 break;
442 if (sighup_received)
444 reload_config();
445 sighup_received = 0;
448 if (sigusr1_received)
450 reset_prefix_lifetimes();
451 sigusr1_received = 0;
457 void
458 timer_handler(void *data)
460 struct Interface *iface = (struct Interface *) data;
461 double next;
463 dlog(LOG_DEBUG, 4, "timer_handler called for %s", iface->Name);
465 if (send_ra_forall(iface, NULL) != 0) {
466 return;
469 next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval);
471 if (iface->init_racount < MAX_INITIAL_RTR_ADVERTISEMENTS)
473 iface->init_racount++;
474 next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, next);
477 iface->next_multicast = next_timeval(next);
480 void
481 config_interface(void)
483 struct Interface *iface;
484 for(iface=IfaceList; iface; iface=iface->next)
486 if (iface->AdvLinkMTU)
487 set_interface_linkmtu(iface->Name, iface->AdvLinkMTU);
488 if (iface->AdvCurHopLimit)
489 set_interface_curhlim(iface->Name, iface->AdvCurHopLimit);
490 if (iface->AdvReachableTime)
491 set_interface_reachtime(iface->Name, iface->AdvReachableTime);
492 if (iface->AdvRetransTimer)
493 set_interface_retranstimer(iface->Name, iface->AdvRetransTimer);
497 void
498 kickoff_adverts(void)
500 struct Interface *iface;
503 * send initial advertisement and set timers
506 for(iface=IfaceList; iface; iface=iface->next)
508 double next;
511 gettimeofday(&iface->last_ra_time, NULL);
513 if( iface->UnicastOnly )
514 continue;
516 gettimeofday(&iface->last_multicast, NULL);
518 if (!iface->AdvSendAdvert)
519 continue;
521 /* send an initial advertisement */
522 if (send_ra_forall(iface, NULL) == 0) {
524 iface->init_racount++;
526 next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, iface->MaxRtrAdvInterval);
527 iface->next_multicast = next_timeval(next);
532 void
533 stop_adverts(void)
535 struct Interface *iface;
538 * send final RA (a SHOULD in RFC4861 section 6.2.5)
541 for (iface=IfaceList; iface; iface=iface->next) {
542 if( ! iface->UnicastOnly ) {
543 if (iface->AdvSendAdvert) {
544 /* send a final advertisement with zero Router Lifetime */
545 iface->cease_adv = 1;
546 send_ra_forall(iface, NULL);
552 void reload_config(void)
554 struct Interface *iface;
556 flog(LOG_INFO, "attempting to reread config file");
558 dlog(LOG_DEBUG, 4, "reopening log");
559 if (log_reopen() < 0) {
560 perror("log_reopen");
561 exit(1);
564 iface=IfaceList;
565 while(iface)
567 struct Interface *next_iface = iface->next;
568 struct AdvPrefix *prefix;
569 struct AdvRoute *route;
570 struct AdvRDNSS *rdnss;
571 struct AdvDNSSL *dnssl;
573 dlog(LOG_DEBUG, 4, "freeing interface %s", iface->Name);
575 prefix = iface->AdvPrefixList;
576 while (prefix)
578 struct AdvPrefix *next_prefix = prefix->next;
580 free(prefix);
581 prefix = next_prefix;
584 route = iface->AdvRouteList;
585 while (route)
587 struct AdvRoute *next_route = route->next;
589 free(route);
590 route = next_route;
593 rdnss = iface->AdvRDNSSList;
594 while (rdnss)
596 struct AdvRDNSS *next_rdnss = rdnss->next;
598 free(rdnss);
599 rdnss = next_rdnss;
602 dnssl = iface->AdvDNSSLList;
603 while (dnssl)
605 struct AdvDNSSL *next_dnssl = dnssl->next;
606 int i;
608 for (i = 0; i < dnssl->AdvDNSSLNumber; i++)
609 free(dnssl->AdvDNSSLSuffixes[i]);
610 free(dnssl->AdvDNSSLSuffixes);
611 free(dnssl);
613 dnssl = next_dnssl;
616 free(iface);
617 iface = next_iface;
620 IfaceList = NULL;
622 /* reread config file */
623 if (readin_config(conf_file) < 0) {
624 perror("readin_config failed.");
625 exit(1);
628 /* XXX: fails due to lack of permissions with non-root user */
629 config_interface();
630 kickoff_adverts();
632 flog(LOG_INFO, "resuming normal operation");
635 void
636 sighup_handler(int sig)
638 /* Linux has "one-shot" signals, reinstall the signal handler */
639 signal(SIGHUP, sighup_handler);
641 dlog(LOG_DEBUG, 4, "sighup_handler called");
643 sighup_received = 1;
646 void
647 sigterm_handler(int sig)
649 /* Linux has "one-shot" signals, reinstall the signal handler */
650 signal(SIGTERM, sigterm_handler);
652 dlog(LOG_DEBUG, 4, "sigterm_handler called");
654 ++sigterm_received;
656 if(sigterm_received > 1){
657 dlog(LOG_ERR, 1, "sigterm_handler called %d times...aborting...", sigterm_received);
658 abort();
662 void
663 sigint_handler(int sig)
665 /* Linux has "one-shot" signals, reinstall the signal handler */
666 signal(SIGINT, sigint_handler);
668 dlog(LOG_DEBUG, 4, "sigint_handler called");
670 ++sigint_received;
672 if(sigint_received > 1){
673 dlog(LOG_ERR, 1, "sigint_handler called %d times...aborting...", sigint_received);
674 abort();
679 void reset_prefix_lifetimes(void)
681 struct Interface *iface;
682 struct AdvPrefix *prefix;
683 char pfx_str[INET6_ADDRSTRLEN];
686 flog(LOG_INFO, "Resetting prefix lifetimes");
688 for (iface = IfaceList; iface; iface = iface->next)
690 for (prefix = iface->AdvPrefixList; prefix;
691 prefix = prefix->next)
693 if (prefix->DecrementLifetimesFlag)
695 print_addr(&prefix->Prefix, pfx_str);
696 dlog(LOG_DEBUG, 4, "%s/%u%%%s plft reset from %u to %u secs", pfx_str, prefix->PrefixLen, iface->Name, prefix->curr_preferredlft, prefix->AdvPreferredLifetime);
697 dlog(LOG_DEBUG, 4, "%s/%u%%%s vlft reset from %u to %u secs", pfx_str, prefix->PrefixLen, iface->Name, prefix->curr_validlft, prefix->AdvValidLifetime);
698 prefix->curr_validlft =
699 prefix->AdvValidLifetime;
700 prefix->curr_preferredlft =
701 prefix->AdvPreferredLifetime;
709 void sigusr1_handler(int sig)
712 /* Linux has "one-shot" signals, reinstall the signal handler */
713 signal(SIGUSR1, sigusr1_handler);
715 dlog(LOG_DEBUG, 4, "sigusr1_handler called");
717 sigusr1_received = 1;
722 drop_root_privileges(const char *username)
724 struct passwd *pw = NULL;
725 pw = getpwnam(username);
726 if (pw) {
727 if (initgroups(username, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
728 flog(LOG_ERR, "Couldn't change to '%.32s' uid=%d gid=%d",
729 username, pw->pw_uid, pw->pw_gid);
730 return (-1);
733 else {
734 flog(LOG_ERR, "Couldn't find user '%.32s'", username);
735 return (-1);
737 return 0;
741 check_conffile_perm(const char *username, const char *conf_file)
743 #if CHECK_PERM
744 struct stat stbuf;
745 struct passwd *pw = NULL;
746 FILE *fp = fopen(conf_file, "r");
748 if (fp == NULL) {
749 flog(LOG_ERR, "can't open %s: %s", conf_file, strerror(errno));
750 return (-1);
752 fclose(fp);
754 if (!username)
755 username = "root";
757 pw = getpwnam(username);
759 if (stat(conf_file, &stbuf) || pw == NULL)
760 return (-1);
762 if (stbuf.st_mode & S_IWOTH) {
763 flog(LOG_ERR, "Insecure file permissions (writable by others): %s", conf_file);
764 return (-1);
767 /* for non-root: must not be writable by self/own group */
768 if (strncmp(username, "root", 5) != 0 &&
769 ((stbuf.st_mode & S_IWGRP && pw->pw_gid == stbuf.st_gid) ||
770 (stbuf.st_mode & S_IWUSR && pw->pw_uid == stbuf.st_uid))) {
771 flog(LOG_ERR, "Insecure file permissions (writable by self/group): %s", conf_file);
772 return (-1);
775 return 0;
776 #else
777 return 0;
778 #endif
782 check_ip6_forwarding(void)
784 int forw_sysctl[] = { SYSCTL_IP6_FORWARDING };
785 int value;
786 size_t size = sizeof(value);
787 FILE *fp = NULL;
788 static int warned = 0;
790 #ifdef __linux__
791 fp = fopen(PROC_SYS_IP6_FORWARDING, "r");
792 if (fp) {
793 int rc = fscanf(fp, "%d", &value);
794 if(rc != 1){
795 flog(LOG_ERR, "cannot read value from %s: %s", PROC_SYS_IP6_FORWARDING, strerror(errno));
796 exit(1);
798 fclose(fp);
800 else
801 flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry not found, "
802 "perhaps the procfs is disabled, "
803 "or the kernel interface has changed?");
804 #endif /* __linux__ */
806 if (!fp && sysctl(forw_sysctl, sizeof(forw_sysctl)/sizeof(forw_sysctl[0]),
807 &value, &size, NULL, 0) < 0) {
808 flog(LOG_DEBUG, "Correct IPv6 forwarding sysctl branch not found, "
809 "perhaps the kernel interface has changed?");
810 return(0); /* this is of advisory value only */
813 if (value != 1 && !warned) {
814 warned = 1;
815 flog(LOG_DEBUG, "IPv6 forwarding setting is: %u, should be 1", value);
816 return(-1);
819 return(0);
823 readin_config(char *fname)
825 if ((yyin = fopen(fname, "r")) == NULL)
827 flog(LOG_ERR, "can't open %s: %s", fname, strerror(errno));
828 return (-1);
831 if (yyparse() != 0)
833 flog(LOG_ERR, "error parsing or activating the config file: %s", fname);
834 return (-1);
837 fclose(yyin);
838 return 0;
841 void
842 version(void)
844 fprintf(stderr, "Version: %s\n\n", VERSION);
845 fprintf(stderr, "Compiled in settings:\n");
846 fprintf(stderr, " default config file \"%s\"\n", PATH_RADVD_CONF);
847 fprintf(stderr, " default pidfile \"%s\"\n", PATH_RADVD_PID);
848 fprintf(stderr, " default logfile \"%s\"\n", PATH_RADVD_LOG);
849 fprintf(stderr, " default syslog facility %d\n", LOG_FACILITY);
850 fprintf(stderr, "Please send bug reports or suggestions to %s.\n",
851 CONTACT_EMAIL);
853 exit(1);
856 void
857 usage(void)
859 fprintf(stderr, "usage: %s %s\n", pname, usage_str);
860 exit(1);