/* * $Id: radvd.c,v 1.39 2009/06/19 07:37:11 psavola Exp $ * * Authors: * Pedro Roque * Lars Fenneberg * * This software is Copyright 1996-2000 by the above mentioned author(s), * All Rights Reserved. * * The license which is distributed with this software in the file COPYRIGHT * applies to this software. If your distribution is missing this file, you * may request it from . * */ #include #include #include #include struct Interface *IfaceList = NULL; char usage_str[] = "[-hsv] [-d level] [-C config_file] [-m log_method] [-l log_file]\n" "\t[-f facility] [-p pid_file] [-u username] [-t chrootdir]"; #ifdef HAVE_GETOPT_LONG struct option prog_opt[] = { {"debug", 1, 0, 'd'}, {"config", 1, 0, 'C'}, {"pidfile", 1, 0, 'p'}, {"logfile", 1, 0, 'l'}, {"logmethod", 1, 0, 'm'}, {"facility", 1, 0, 'f'}, {"username", 1, 0, 'u'}, {"chrootdir", 1, 0, 't'}, {"version", 0, 0, 'v'}, {"help", 0, 0, 'h'}, {"singleprocess", 0, 0, 's'}, {NULL, 0, 0, 0} }; #endif extern FILE *yyin; char *conf_file = NULL; char *pname; int sock = -1; volatile int sighup_received = 0; volatile int sigterm_received = 0; volatile int sigint_received = 0; void sighup_handler(int sig); void sigterm_handler(int sig); void sigint_handler(int sig); void timer_handler(void *data); void config_interface(void); void kickoff_adverts(void); void stop_adverts(void); void version(void); void usage(void); int drop_root_privileges(const char *); int readin_config(char *); int check_conffile_perm(const char *, const char *); /* Foxconn added start pling 12/22/2011 */ /* WNDR4500 TD#156: Sync radvd prefix lifetime with IAPD lifetime */ #include /* Signal handlers SIGUSR1: to reset prefix lifetime * after a sucessful IAPD renew */ int sigusr1_received = 0; void sigusr1_handler(int sig); /* Functions to record initial advertise time */ int use_dynamic_lifetime = 0; unsigned long initial_advert_time = 0; void set_initial_advert_time(void) { struct sysinfo info; sysinfo(&info); initial_advert_time = (unsigned long)(info.uptime); if (1) { char command[256]; sprintf(command, "echo 'radvd set init advert time to %u' > /dev/console", initial_advert_time); system(command); } } unsigned long get_current_time(void) { struct sysinfo info; sysinfo(&info); return (unsigned long)(info.uptime); } /* Foxconn added end pling 12/22/2011 */ int main(int argc, char *argv[]) { unsigned char msg[MSG_SIZE]; char pidstr[16]; ssize_t ret; int c, log_method; char *logfile, *pidfile; sigset_t oset, nset; int facility, fd; char *username = NULL; char *chrootdir = NULL; int singleprocess = 0; #ifdef HAVE_GETOPT_LONG int opt_idx; #endif pname = ((pname=strrchr(argv[0],'/')) != NULL)?pname+1:argv[0]; srand((unsigned int)time(NULL)); log_method = L_STDERR_SYSLOG; logfile = PATH_RADVD_LOG; conf_file = PATH_RADVD_CONF; facility = LOG_FACILITY; pidfile = PATH_RADVD_PID; /* parse args */ #ifdef HAVE_GETOPT_LONG /* Foxconn modified start pling 12/22/2011 */ /* Add option to show advertise real lifetime */ /* while ((c = getopt_long(argc, argv, "d:C:l:m:p:t:u:vhs", prog_opt, &opt_idx)) > 0) */ while ((c = getopt_long(argc, argv, "d:C:l:m:p:t:u:vhsD", prog_opt, &opt_idx)) > 0) /* Foxconn modified end pling 12/22/2011 */ #else /* Foxconn modified start pling 12/22/2011 */ /* Add option to show advertise real lifetime */ /* while ((c = getopt(argc, argv, "d:C:l:m:p:t:u:vhs")) > 0) */ while ((c = getopt(argc, argv, "d:C:l:m:p:t:u:vhsD")) > 0) /* Foxconn modified end pling 12/22/2011 */ #endif { switch (c) { case 'C': conf_file = optarg; break; case 'd': set_debuglevel(atoi(optarg)); break; case 'f': facility = atoi(optarg); break; case 'l': logfile = optarg; break; case 'p': pidfile = optarg; break; case 'm': if (!strcmp(optarg, "syslog")) { log_method = L_SYSLOG; } else if (!strcmp(optarg, "stderr_syslog")) { log_method = L_STDERR_SYSLOG; } else if (!strcmp(optarg, "stderr")) { log_method = L_STDERR; } else if (!strcmp(optarg, "logfile")) { log_method = L_LOGFILE; } else if (!strcmp(optarg, "none")) { log_method = L_NONE; } else { fprintf(stderr, "%s: unknown log method: %s\n", pname, optarg); exit(1); } break; case 't': chrootdir = strdup(optarg); break; case 'u': username = strdup(optarg); break; case 'v': version(); break; case 's': singleprocess = 1; break; /* Foxconn added start pling 12/22/2011 */ /* Add option to show advertise dynamic lifetime */ case 'D': use_dynamic_lifetime = 1; break; /* Foxconn added end pling 12/22/2011 */ case 'h': usage(); #ifdef HAVE_GETOPT_LONG case ':': fprintf(stderr, "%s: option %s: parameter expected\n", pname, prog_opt[opt_idx].name); exit(1); #endif case '?': exit(1); } } if (chrootdir) { if (!username) { fprintf(stderr, "Chroot as root is not safe, exiting\n"); exit(1); } if (chroot(chrootdir) == -1) { perror("chroot"); exit (1); } if (chdir("/") == -1) { perror("chdir"); exit (1); } /* username will be switched later */ } if (log_open(log_method, pname, logfile, facility) < 0) exit(1); flog(LOG_INFO, "version %s started", VERSION); /* get a raw socket for sending and receiving ICMPv6 messages */ sock = open_icmpv6_socket(); if (sock < 0) exit(1); /* check that 'other' cannot write the file * for non-root, also that self/own group can't either */ if (check_conffile_perm(username, conf_file) < 0) { if (get_debuglevel() == 0) exit(1); else flog(LOG_WARNING, "Insecure file permissions, but continuing anyway"); } /* if we know how to do it, check whether forwarding is enabled */ if (check_ip6_forwarding()) { if (get_debuglevel() == 0) { flog(LOG_ERR, "IPv6 forwarding seems to be disabled, exiting"); exit(1); } else flog(LOG_WARNING, "IPv6 forwarding seems to be disabled, but continuing anyway."); } /* parse config file */ if (readin_config(conf_file) < 0) exit(1); /* drop root privileges if requested. */ if (username) { if (!singleprocess) { dlog(LOG_DEBUG, 3, "Initializing privsep"); if (privsep_init() < 0) flog(LOG_WARNING, "Failed to initialize privsep."); } if (drop_root_privileges(username) < 0) exit(1); } if ((fd = open(pidfile, O_RDONLY, 0)) > 0) { ret = read(fd, pidstr, sizeof(pidstr) - 1); if (ret < 0) { flog(LOG_ERR, "cannot read radvd pid file, terminating: %s", strerror(errno)); exit(1); } pidstr[ret] = '\0'; if (!kill((pid_t)atol(pidstr), 0)) { flog(LOG_ERR, "radvd already running, terminating."); exit(1); } close(fd); fd = open(pidfile, O_CREAT|O_TRUNC|O_WRONLY, 0644); } else /* FIXME: not atomic if pidfile is on an NFS mounted volume */ fd = open(pidfile, O_CREAT|O_EXCL|O_WRONLY, 0644); if (fd < 0) { flog(LOG_ERR, "cannot create radvd pid file, terminating: %s", strerror(errno)); exit(1); } /* * okay, config file is read in, socket and stuff is setup, so * lets fork now... */ if (get_debuglevel() == 0) { /* Detach from controlling terminal */ if (daemon(0, 0) < 0) perror("daemon"); /* close old logfiles, including stderr */ log_close(); /* reopen logfiles, but don't log to stderr unless explicitly requested */ if (log_method == L_STDERR_SYSLOG) log_method = L_SYSLOG; if (log_open(log_method, pname, logfile, facility) < 0) exit(1); } /* * config signal handlers, also make sure ALRM isn't blocked and raise a warning if so * (some stupid scripts/pppd appears to do this...) */ sigemptyset(&nset); sigaddset(&nset, SIGALRM); sigprocmask(SIG_UNBLOCK, &nset, &oset); if (sigismember(&oset, SIGALRM)) flog(LOG_WARNING, "SIGALRM has been unblocked. Your startup environment might be wrong."); signal(SIGHUP, sighup_handler); signal(SIGTERM, sigterm_handler); signal(SIGINT, sigint_handler); signal(SIGUSR1, sigusr1_handler); /* Foxconn added pling 12/22/2011 */ snprintf(pidstr, sizeof(pidstr), "%ld\n", (long)getpid()); write(fd, pidstr, strlen(pidstr)); close(fd); config_interface(); /* Foxconn added start pling 12/22/2011 */ /* Record the time for first advertisement */ set_initial_advert_time(); /* Foxconn added end pling 12/22/2011 */ kickoff_adverts(); /* enter loop */ for (;;) { int len, hoplimit; struct sockaddr_in6 rcv_addr; struct in6_pktinfo *pkt_info = NULL; len = recv_rs_ra(sock, msg, &rcv_addr, &pkt_info, &hoplimit); if (len > 0) process(sock, IfaceList, msg, len, &rcv_addr, pkt_info, hoplimit); if (sigterm_received || sigint_received) { stop_adverts(); /* Foxconn added start pling 11/30/2010 */ /* WNR3500L TD192, Per Netgear spec, * need to send RA for 3 times before termination. */ usleep(200000); stop_adverts(); usleep(200000); stop_adverts(); /* Foxconn added end pling 11/30/2010 */ break; } if (sighup_received) { reload_config(); sighup_received = 0; } /* Foxconn added start pling 12/22/2011 */ /* Reset the initial advertisement time to now */ /* This should happen after a successful IADP renew */ if (sigusr1_received) { set_initial_advert_time(); sigusr1_received = 0; } /* Foxconn added end pling 12/22/2011 */ } unlink(pidfile); exit(0); } void timer_handler(void *data) { struct Interface *iface = (struct Interface *) data; double next; dlog(LOG_DEBUG, 4, "timer_handler called for %s", iface->Name); send_ra_forall(sock, iface, NULL); next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval); if (iface->init_racount < MAX_INITIAL_RTR_ADVERTISEMENTS - 1) { iface->init_racount++; next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, next); } /* Foxconn Perry added start, 2011/05/13, for IPv6 obsolete prefix information */ else { iface->init_racount++; } /* Foxconn Perry added end, 2011/05/13, for IPv6 obsolete prefix information */ set_timer(&iface->tm, next); } void config_interface(void) { struct Interface *iface; for(iface=IfaceList; iface; iface=iface->next) { if (iface->AdvLinkMTU) set_interface_linkmtu(iface->Name, iface->AdvLinkMTU); if (iface->AdvCurHopLimit) set_interface_curhlim(iface->Name, iface->AdvCurHopLimit); if (iface->AdvReachableTime) set_interface_reachtime(iface->Name, iface->AdvReachableTime); if (iface->AdvRetransTimer) set_interface_retranstimer(iface->Name, iface->AdvRetransTimer); } } void kickoff_adverts(void) { struct Interface *iface; /* * send initial advertisement and set timers */ for(iface=IfaceList; iface; iface=iface->next) { if( iface->UnicastOnly ) break; init_timer(&iface->tm, timer_handler, (void *) iface); if (!iface->AdvSendAdvert) break; /* send an initial advertisement */ send_ra_forall(sock, iface, NULL); iface->init_racount++; set_timer(&iface->tm, min(MAX_INITIAL_RTR_ADVERT_INTERVAL, iface->MaxRtrAdvInterval)); } } void stop_adverts(void) { struct Interface *iface; /* * send final RA (a SHOULD in RFC4861 section 6.2.5) */ for (iface=IfaceList; iface; iface=iface->next) { if( ! iface->UnicastOnly ) { if (iface->AdvSendAdvert) { /* send a final advertisement with zero Router Lifetime */ iface->AdvDefaultLifetime = 0; /* Foxconn Perry added start, 2011/05/13, for IPv6 obsolete prefix information */ iface->init_racount = 0; /* Foxconn Perry added end, 2011/05/13, for IPv6 obsolete prefix information */ send_ra_forall(sock, iface, NULL); } } } } void reload_config(void) { struct Interface *iface; flog(LOG_INFO, "attempting to reread config file"); dlog(LOG_DEBUG, 4, "reopening log"); if (log_reopen() < 0) exit(1); /* disable timers, free interface and prefix structures */ for(iface=IfaceList; iface; iface=iface->next) { /* check that iface->tm was set in the first place */ if (iface->tm.next && iface->tm.prev) { dlog(LOG_DEBUG, 4, "disabling timer for %s", iface->Name); clear_timer(&iface->tm); } } iface=IfaceList; while(iface) { struct Interface *next_iface = iface->next; struct AdvPrefix *prefix; struct AdvRoute *route; struct AdvRDNSS *rdnss; dlog(LOG_DEBUG, 4, "freeing interface %s", iface->Name); prefix = iface->AdvPrefixList; while (prefix) { struct AdvPrefix *next_prefix = prefix->next; free(prefix); prefix = next_prefix; } route = iface->AdvRouteList; while (route) { struct AdvRoute *next_route = route->next; free(route); route = next_route; } rdnss = iface->AdvRDNSSList; while (rdnss) { struct AdvRDNSS *next_rdnss = rdnss->next; free(rdnss); rdnss = next_rdnss; } free(iface); iface = next_iface; } IfaceList = NULL; /* reread config file */ if (readin_config(conf_file) < 0) exit(1); /* XXX: fails due to lack of permissions with non-root user */ config_interface(); kickoff_adverts(); flog(LOG_INFO, "resuming normal operation"); } void sighup_handler(int sig) { /* Linux has "one-shot" signals, reinstall the signal handler */ signal(SIGHUP, sighup_handler); dlog(LOG_DEBUG, 4, "sighup_handler called"); sighup_received = 1; } void sigterm_handler(int sig) { /* Linux has "one-shot" signals, reinstall the signal handler */ signal(SIGTERM, sigterm_handler); dlog(LOG_DEBUG, 4, "sigterm_handler called"); sigterm_received = 1; } void sigint_handler(int sig) { /* Linux has "one-shot" signals, reinstall the signal handler */ signal(SIGINT, sigint_handler); dlog(LOG_DEBUG, 4, "sigint_handler called"); sigint_received = 1; } /* Foxconn added start pling 12/22/2011 */ /* Add signal handler for SIGUSR1, to handle * signal after a successful IAPD renew */ void sigusr1_handler(int sig) { signal(SIGUSR1, sigusr1_handler); sigusr1_received = 1; } /* Foxconn added end pling 12/22/2011 */ int drop_root_privileges(const char *username) { struct passwd *pw = NULL; pw = getpwnam(username); if (pw) { if (initgroups(username, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) { flog(LOG_ERR, "Couldn't change to '%.32s' uid=%d gid=%d", username, pw->pw_uid, pw->pw_gid); return (-1); } } else { flog(LOG_ERR, "Couldn't find user '%.32s'", username); return (-1); } return 0; } int check_conffile_perm(const char *username, const char *conf_file) { struct stat *st = NULL; struct passwd *pw = NULL; FILE *fp = fopen(conf_file, "r"); if (fp == NULL) { flog(LOG_ERR, "can't open %s: %s", conf_file, strerror(errno)); return (-1); } fclose(fp); st = malloc(sizeof(struct stat)); if (st == NULL) goto errorout; if (!username) username = "root"; pw = getpwnam(username); if (stat(conf_file, st) || pw == NULL) goto errorout; if (st->st_mode & S_IWOTH) { flog(LOG_ERR, "Insecure file permissions (writable by others): %s", conf_file); goto errorout; } /* for non-root: must not be writable by self/own group */ if (strncmp(username, "root", 5) != 0 && ((st->st_mode & S_IWGRP && pw->pw_gid == st->st_gid) || (st->st_mode & S_IWUSR && pw->pw_uid == st->st_uid))) { flog(LOG_ERR, "Insecure file permissions (writable by self/group): %s", conf_file); goto errorout; } free(st); return 0; errorout: if (st) free(st); return(-1); } int check_ip6_forwarding(void) { int forw_sysctl[] = { SYSCTL_IP6_FORWARDING }; int value; size_t size = sizeof(value); FILE *fp = NULL; #ifdef __linux__ fp = fopen(PROC_SYS_IP6_FORWARDING, "r"); if (fp) { fscanf(fp, "%d", &value); fclose(fp); } else flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry not found, " "perhaps the procfs is disabled, " "or the kernel interface has changed?"); #endif /* __linux__ */ if (!fp && sysctl(forw_sysctl, sizeof(forw_sysctl)/sizeof(forw_sysctl[0]), &value, &size, NULL, 0) < 0) { flog(LOG_DEBUG, "Correct IPv6 forwarding sysctl branch not found, " "perhaps the kernel interface has changed?"); return(0); /* this is of advisory value only */ } if (value != 1) { flog(LOG_DEBUG, "IPv6 forwarding setting is: %u, should be 1", value); return(-1); } return(0); } int readin_config(char *fname) { if ((yyin = fopen(fname, "r")) == NULL) { flog(LOG_ERR, "can't open %s: %s", fname, strerror(errno)); return (-1); } if (yyparse() != 0) { flog(LOG_ERR, "error parsing or activating the config file: %s", fname); return (-1); } fclose(yyin); return 0; } void version(void) { fprintf(stderr, "Version: %s\n\n", VERSION); fprintf(stderr, "Compiled in settings:\n"); fprintf(stderr, " default config file \"%s\"\n", PATH_RADVD_CONF); fprintf(stderr, " default pidfile \"%s\"\n", PATH_RADVD_PID); fprintf(stderr, " default logfile \"%s\"\n", PATH_RADVD_LOG); fprintf(stderr, " default syslog facililty %d\n", LOG_FACILITY); fprintf(stderr, "Please send bug reports or suggestions to %s.\n", CONTACT_EMAIL); exit(1); } void usage(void) { fprintf(stderr, "usage: %s %s\n", pname, usage_str); exit(1); }