1/*
2 *   $Id: radvd.c,v 1.39 2009/06/19 07:37:11 psavola Exp $
3 *
4 *   Authors:
5 *    Pedro Roque		<roque@di.fc.ul.pt>
6 *    Lars Fenneberg		<lf@elemental.net>
7 *
8 *   This software is Copyright 1996-2000 by the above mentioned author(s),
9 *   All Rights Reserved.
10 *
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>.
14 *
15 */
16
17#include <config.h>
18#include <includes.h>
19#include <radvd.h>
20#include <pathnames.h>
21
22struct Interface *IfaceList = NULL;
23
24char usage_str[] =
25	"[-hsv] [-d level] [-C config_file] [-m log_method] [-l log_file]\n"
26	"\t[-f facility] [-p pid_file] [-u username] [-t chrootdir]";
27
28#ifdef HAVE_GETOPT_LONG
29struct option prog_opt[] = {
30	{"debug", 1, 0, 'd'},
31	{"config", 1, 0, 'C'},
32	{"pidfile", 1, 0, 'p'},
33	{"logfile", 1, 0, 'l'},
34	{"logmethod", 1, 0, 'm'},
35	{"facility", 1, 0, 'f'},
36	{"username", 1, 0, 'u'},
37	{"chrootdir", 1, 0, 't'},
38	{"version", 0, 0, 'v'},
39	{"help", 0, 0, 'h'},
40	{"singleprocess", 0, 0, 's'},
41	{NULL, 0, 0, 0}
42};
43#endif
44
45extern FILE *yyin;
46
47char *conf_file = NULL;
48char *pname;
49int sock = -1;
50
51volatile int sighup_received = 0;
52volatile int sigterm_received = 0;
53volatile int sigint_received = 0;
54
55void sighup_handler(int sig);
56void sigterm_handler(int sig);
57void sigint_handler(int sig);
58void timer_handler(void *data);
59void config_interface(void);
60void kickoff_adverts(void);
61void stop_adverts(void);
62void version(void);
63void usage(void);
64int drop_root_privileges(const char *);
65int readin_config(char *);
66int check_conffile_perm(const char *, const char *);
67
68/* Foxconn added start pling 12/22/2011 */
69/* WNDR3400v2 Defect#135: Sync radvd prefix lifetime with IAPD lifetime */
70#include <sys/sysinfo.h>
71
72/* Signal handlers SIGUSR1: to reset prefix lifetime
73 * after a sucessful IAPD renew */
74int sigusr1_received = 0;
75void sigusr1_handler(int sig);
76
77/* Functions to record initial advertise time */
78int use_dynamic_lifetime = 0;
79unsigned long initial_advert_time = 0;
80
81void set_initial_advert_time(void)
82{
83	struct sysinfo info;
84	sysinfo(&info);
85	initial_advert_time = (unsigned long)(info.uptime);
86}
87
88unsigned long get_current_time(void)
89{
90	struct sysinfo info;
91	sysinfo(&info);
92	return (unsigned long)(info.uptime);
93}
94/* Foxconn added end pling 12/22/2011 */
95int
96main(int argc, char *argv[])
97{
98	unsigned char msg[MSG_SIZE];
99	char pidstr[16];
100	ssize_t ret;
101	int c, log_method;
102	char *logfile, *pidfile;
103	sigset_t oset, nset;
104	int facility, fd;
105	char *username = NULL;
106	char *chrootdir = NULL;
107	int singleprocess = 0;
108#ifdef HAVE_GETOPT_LONG
109	int opt_idx;
110#endif
111
112	pname = ((pname=strrchr(argv[0],'/')) != NULL)?pname+1:argv[0];
113
114	srand((unsigned int)time(NULL));
115
116	log_method = L_STDERR_SYSLOG;
117	logfile = PATH_RADVD_LOG;
118	conf_file = PATH_RADVD_CONF;
119	facility = LOG_FACILITY;
120	pidfile = PATH_RADVD_PID;
121
122	/* parse args */
123#ifdef HAVE_GETOPT_LONG
124	/* Foxconn modified start pling 12/22/2011 */
125	/* Add option to show advertise real lifetime */
126	/* while ((c = getopt_long(argc, argv, "d:C:l:m:p:t:u:vhs", prog_opt, &opt_idx)) > 0) */
127	while ((c = getopt_long(argc, argv, "d:C:l:m:p:t:u:vhsD", prog_opt, &opt_idx)) > 0)
128	/* Foxconn modified end pling 12/22/2011 */
129#else
130	/* Foxconn modified start pling 12/22/2011 */
131	/* Add option to show advertise real lifetime */
132	/* while ((c = getopt(argc, argv, "d:C:l:m:p:t:u:vhs")) > 0) */
133	while ((c = getopt(argc, argv, "d:C:l:m:p:t:u:vhsD")) > 0)
134	/* Foxconn modified end pling 12/22/2011 */
135#endif
136	{
137		switch (c) {
138		case 'C':
139			conf_file = optarg;
140			break;
141		case 'd':
142			set_debuglevel(atoi(optarg));
143			break;
144		case 'f':
145			facility = atoi(optarg);
146			break;
147		case 'l':
148			logfile = optarg;
149			break;
150		case 'p':
151			pidfile = optarg;
152			break;
153		case 'm':
154			if (!strcmp(optarg, "syslog"))
155			{
156				log_method = L_SYSLOG;
157			}
158			else if (!strcmp(optarg, "stderr_syslog"))
159			{
160				log_method = L_STDERR_SYSLOG;
161			}
162			else if (!strcmp(optarg, "stderr"))
163			{
164				log_method = L_STDERR;
165			}
166			else if (!strcmp(optarg, "logfile"))
167			{
168				log_method = L_LOGFILE;
169			}
170			else if (!strcmp(optarg, "none"))
171			{
172				log_method = L_NONE;
173			}
174			else
175			{
176				fprintf(stderr, "%s: unknown log method: %s\n", pname, optarg);
177				exit(1);
178			}
179			break;
180		case 't':
181			chrootdir = strdup(optarg);
182			break;
183		case 'u':
184			username = strdup(optarg);
185			break;
186		case 'v':
187			version();
188			break;
189		case 's':
190			singleprocess = 1;
191			break;
192		/* Foxconn added start pling 12/22/2011 */
193		/* Add option to show advertise dynamic lifetime */
194		case 'D':
195			use_dynamic_lifetime = 1;
196			break;
197		/* Foxconn added end pling 12/22/2011 */
198		case 'h':
199			usage();
200#ifdef HAVE_GETOPT_LONG
201		case ':':
202			fprintf(stderr, "%s: option %s: parameter expected\n", pname,
203				prog_opt[opt_idx].name);
204			exit(1);
205#endif
206		case '?':
207			exit(1);
208		}
209	}
210
211	if (chrootdir) {
212		if (!username) {
213			fprintf(stderr, "Chroot as root is not safe, exiting\n");
214			exit(1);
215		}
216
217		if (chroot(chrootdir) == -1) {
218			perror("chroot");
219			exit (1);
220		}
221
222		if (chdir("/") == -1) {
223			perror("chdir");
224			exit (1);
225		}
226		/* username will be switched later */
227	}
228
229	if (log_open(log_method, pname, logfile, facility) < 0)
230		exit(1);
231
232	flog(LOG_INFO, "version %s started", VERSION);
233
234	/* get a raw socket for sending and receiving ICMPv6 messages */
235	sock = open_icmpv6_socket();
236	if (sock < 0)
237		exit(1);
238
239	/* check that 'other' cannot write the file
240         * for non-root, also that self/own group can't either
241         */
242	if (check_conffile_perm(username, conf_file) < 0) {
243		if (get_debuglevel() == 0)
244			exit(1);
245		else
246			flog(LOG_WARNING, "Insecure file permissions, but continuing anyway");
247	}
248
249	/* if we know how to do it, check whether forwarding is enabled */
250	if (check_ip6_forwarding()) {
251		if (get_debuglevel() == 0) {
252			flog(LOG_ERR, "IPv6 forwarding seems to be disabled, exiting");
253			exit(1);
254		}
255		else
256			flog(LOG_WARNING, "IPv6 forwarding seems to be disabled, but continuing anyway.");
257	}
258
259	/* parse config file */
260	if (readin_config(conf_file) < 0)
261		exit(1);
262
263	/* drop root privileges if requested. */
264	if (username) {
265		if (!singleprocess) {
266		 	dlog(LOG_DEBUG, 3, "Initializing privsep");
267		 	if (privsep_init() < 0)
268				flog(LOG_WARNING, "Failed to initialize privsep.");
269		}
270
271		if (drop_root_privileges(username) < 0)
272			exit(1);
273	}
274
275	if ((fd = open(pidfile, O_RDONLY, 0)) > 0)
276	{
277		ret = read(fd, pidstr, sizeof(pidstr) - 1);
278		if (ret < 0)
279		{
280			flog(LOG_ERR, "cannot read radvd pid file, terminating: %s", strerror(errno));
281			exit(1);
282		}
283		pidstr[ret] = '\0';
284		if (!kill((pid_t)atol(pidstr), 0))
285		{
286			flog(LOG_ERR, "radvd already running, terminating.");
287			exit(1);
288		}
289		close(fd);
290		fd = open(pidfile, O_CREAT|O_TRUNC|O_WRONLY, 0644);
291	}
292	else	/* FIXME: not atomic if pidfile is on an NFS mounted volume */
293		fd = open(pidfile, O_CREAT|O_EXCL|O_WRONLY, 0644);
294
295	if (fd < 0)
296	{
297		flog(LOG_ERR, "cannot create radvd pid file, terminating: %s", strerror(errno));
298		exit(1);
299	}
300
301	/*
302	 * okay, config file is read in, socket and stuff is setup, so
303	 * lets fork now...
304	 */
305
306	if (get_debuglevel() == 0) {
307
308		/* Detach from controlling terminal */
309		if (daemon(0, 0) < 0)
310			perror("daemon");
311
312		/* close old logfiles, including stderr */
313		log_close();
314
315		/* reopen logfiles, but don't log to stderr unless explicitly requested */
316		if (log_method == L_STDERR_SYSLOG)
317			log_method = L_SYSLOG;
318		if (log_open(log_method, pname, logfile, facility) < 0)
319			exit(1);
320
321	}
322
323	/*
324	 *	config signal handlers, also make sure ALRM isn't blocked and raise a warning if so
325	 *      (some stupid scripts/pppd appears to do this...)
326	 */
327	sigemptyset(&nset);
328	sigaddset(&nset, SIGALRM);
329	sigprocmask(SIG_UNBLOCK, &nset, &oset);
330	if (sigismember(&oset, SIGALRM))
331		flog(LOG_WARNING, "SIGALRM has been unblocked. Your startup environment might be wrong.");
332
333	signal(SIGHUP, sighup_handler);
334	signal(SIGTERM, sigterm_handler);
335	signal(SIGINT, sigint_handler);
336	signal(SIGUSR1, sigusr1_handler);	/* Foxconn added pling 12/22/2011 */
337
338	snprintf(pidstr, sizeof(pidstr), "%ld\n", (long)getpid());
339
340	write(fd, pidstr, strlen(pidstr));
341
342	close(fd);
343
344	config_interface();
345	/* Foxconn added start pling 12/22/2011 */
346	/* Record the time for first advertisement */
347	set_initial_advert_time();
348	/* Foxconn added end pling 12/22/2011 */
349	kickoff_adverts();
350
351	/* enter loop */
352
353	for (;;)
354	{
355		int len, hoplimit;
356		struct sockaddr_in6 rcv_addr;
357		struct in6_pktinfo *pkt_info = NULL;
358
359		len = recv_rs_ra(sock, msg, &rcv_addr, &pkt_info, &hoplimit);
360		if (len > 0)
361			process(sock, IfaceList, msg, len,
362				&rcv_addr, pkt_info, hoplimit);
363
364		if (sigterm_received || sigint_received) {
365			stop_adverts();
366
367			/* Foxconn added start pling 11/30/2010 */
368			/* WNR3500L TD192, Per Netgear spec,
369			 * need to send RA for 3 times before termination.
370			 */
371			usleep(200000);
372			stop_adverts();
373			usleep(200000);
374			stop_adverts();
375			/* Foxconn added end pling 11/30/2010 */
376
377			break;
378		}
379
380		if (sighup_received)
381		{
382			reload_config();
383			sighup_received = 0;
384		}
385		/* Foxconn added start pling 12/22/2011 */
386		/* Reset the initial advertisement time to now */
387		/* This should happen after a successful IADP renew */
388		if (sigusr1_received)
389		{
390			set_initial_advert_time();
391			sigusr1_received = 0;
392		}
393		/* Foxconn added end pling 12/22/2011 */
394	}
395
396	unlink(pidfile);
397	exit(0);
398}
399
400void
401timer_handler(void *data)
402{
403	struct Interface *iface = (struct Interface *) data;
404	double next;
405
406	dlog(LOG_DEBUG, 4, "timer_handler called for %s", iface->Name);
407
408	send_ra_forall(sock, iface, NULL);
409
410	next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval);
411
412	if (iface->init_racount < MAX_INITIAL_RTR_ADVERTISEMENTS - 1)
413	{
414		iface->init_racount++;
415		next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, next);
416	}
417    /* Foxconn Perry added start, 2011/05/13, for IPv6 obsolete prefix information */
418    else
419    {
420        iface->init_racount++;
421    }
422    /* Foxconn Perry added end, 2011/05/13, for IPv6 obsolete prefix information */
423
424	set_timer(&iface->tm, next);
425}
426
427void
428config_interface(void)
429{
430	struct Interface *iface;
431	for(iface=IfaceList; iface; iface=iface->next)
432	{
433		if (iface->AdvLinkMTU)
434			set_interface_linkmtu(iface->Name, iface->AdvLinkMTU);
435		if (iface->AdvCurHopLimit)
436			set_interface_curhlim(iface->Name, iface->AdvCurHopLimit);
437		if (iface->AdvReachableTime)
438			set_interface_reachtime(iface->Name, iface->AdvReachableTime);
439		if (iface->AdvRetransTimer)
440			set_interface_retranstimer(iface->Name, iface->AdvRetransTimer);
441	}
442}
443
444void
445kickoff_adverts(void)
446{
447	struct Interface *iface;
448
449	/*
450	 *	send initial advertisement and set timers
451	 */
452
453	for(iface=IfaceList; iface; iface=iface->next)
454	{
455		if( iface->UnicastOnly )
456			break;
457
458		init_timer(&iface->tm, timer_handler, (void *) iface);
459
460		if (!iface->AdvSendAdvert)
461			break;
462
463		/* send an initial advertisement */
464		send_ra_forall(sock, iface, NULL);
465
466		iface->init_racount++;
467
468		set_timer(&iface->tm,
469			  min(MAX_INITIAL_RTR_ADVERT_INTERVAL,
470			      iface->MaxRtrAdvInterval));
471	}
472}
473
474void
475stop_adverts(void)
476{
477	struct Interface *iface;
478
479	/*
480	 *	send final RA (a SHOULD in RFC4861 section 6.2.5)
481	 */
482
483	for (iface=IfaceList; iface; iface=iface->next) {
484		if( ! iface->UnicastOnly ) {
485			if (iface->AdvSendAdvert) {
486				/* send a final advertisement with zero Router Lifetime */
487				iface->AdvDefaultLifetime = 0;
488                /* Foxconn Perry added start, 2011/05/13, for IPv6 obsolete prefix information */
489                iface->init_racount = 0;
490                /* Foxconn Perry added end, 2011/05/13, for IPv6 obsolete prefix information */
491				send_ra_forall(sock, iface, NULL);
492			}
493		}
494	}
495}
496
497void reload_config(void)
498{
499	struct Interface *iface;
500
501	flog(LOG_INFO, "attempting to reread config file");
502
503	dlog(LOG_DEBUG, 4, "reopening log");
504	if (log_reopen() < 0)
505		exit(1);
506
507	/* disable timers, free interface and prefix structures */
508	for(iface=IfaceList; iface; iface=iface->next)
509	{
510		/* check that iface->tm was set in the first place */
511		if (iface->tm.next && iface->tm.prev)
512		{
513			dlog(LOG_DEBUG, 4, "disabling timer for %s", iface->Name);
514			clear_timer(&iface->tm);
515		}
516	}
517
518	iface=IfaceList;
519	while(iface)
520	{
521		struct Interface *next_iface = iface->next;
522		struct AdvPrefix *prefix;
523		struct AdvRoute *route;
524		struct AdvRDNSS *rdnss;
525
526		dlog(LOG_DEBUG, 4, "freeing interface %s", iface->Name);
527
528		prefix = iface->AdvPrefixList;
529		while (prefix)
530		{
531			struct AdvPrefix *next_prefix = prefix->next;
532
533			free(prefix);
534			prefix = next_prefix;
535		}
536
537		route = iface->AdvRouteList;
538		while (route)
539		{
540			struct AdvRoute *next_route = route->next;
541
542			free(route);
543			route = next_route;
544		}
545
546		rdnss = iface->AdvRDNSSList;
547		while (rdnss)
548		{
549			struct AdvRDNSS *next_rdnss = rdnss->next;
550
551			free(rdnss);
552			rdnss = next_rdnss;
553		}
554
555		free(iface);
556		iface = next_iface;
557	}
558
559	IfaceList = NULL;
560
561	/* reread config file */
562	if (readin_config(conf_file) < 0)
563		exit(1);
564
565	/* XXX: fails due to lack of permissions with non-root user */
566	config_interface();
567	kickoff_adverts();
568
569	flog(LOG_INFO, "resuming normal operation");
570}
571
572void
573sighup_handler(int sig)
574{
575	/* Linux has "one-shot" signals, reinstall the signal handler */
576	signal(SIGHUP, sighup_handler);
577
578	dlog(LOG_DEBUG, 4, "sighup_handler called");
579
580	sighup_received = 1;
581}
582
583void
584sigterm_handler(int sig)
585{
586	/* Linux has "one-shot" signals, reinstall the signal handler */
587	signal(SIGTERM, sigterm_handler);
588
589	dlog(LOG_DEBUG, 4, "sigterm_handler called");
590
591	sigterm_received = 1;
592}
593
594void
595sigint_handler(int sig)
596{
597	/* Linux has "one-shot" signals, reinstall the signal handler */
598	signal(SIGINT, sigint_handler);
599
600	dlog(LOG_DEBUG, 4, "sigint_handler called");
601
602	sigint_received = 1;
603}
604
605/* Foxconn added start pling 12/22/2011 */
606/* Add signal handler for SIGUSR1, to handle
607 * signal after a successful IAPD renew */
608void sigusr1_handler(int sig)
609{
610	signal(SIGUSR1, sigusr1_handler);
611	sigusr1_received = 1;
612}
613/* Foxconn added end pling 12/22/2011 */
614int
615drop_root_privileges(const char *username)
616{
617	struct passwd *pw = NULL;
618	pw = getpwnam(username);
619	if (pw) {
620		if (initgroups(username, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
621			flog(LOG_ERR, "Couldn't change to '%.32s' uid=%d gid=%d",
622					username, pw->pw_uid, pw->pw_gid);
623			return (-1);
624		}
625	}
626	else {
627		flog(LOG_ERR, "Couldn't find user '%.32s'", username);
628		return (-1);
629	}
630	return 0;
631}
632
633int
634check_conffile_perm(const char *username, const char *conf_file)
635{
636	struct stat *st = NULL;
637	struct passwd *pw = NULL;
638	FILE *fp = fopen(conf_file, "r");
639
640	if (fp == NULL) {
641		flog(LOG_ERR, "can't open %s: %s", conf_file, strerror(errno));
642		return (-1);
643	}
644	fclose(fp);
645
646	st = malloc(sizeof(struct stat));
647	if (st == NULL)
648		goto errorout;
649
650	if (!username)
651		username = "root";
652
653	pw = getpwnam(username);
654
655	if (stat(conf_file, st) || pw == NULL)
656		goto errorout;
657
658	if (st->st_mode & S_IWOTH) {
659                flog(LOG_ERR, "Insecure file permissions (writable by others): %s", conf_file);
660		goto errorout;
661        }
662
663	/* for non-root: must not be writable by self/own group */
664	if (strncmp(username, "root", 5) != 0 &&
665	    ((st->st_mode & S_IWGRP && pw->pw_gid == st->st_gid) ||
666	     (st->st_mode & S_IWUSR && pw->pw_uid == st->st_uid))) {
667                flog(LOG_ERR, "Insecure file permissions (writable by self/group): %s", conf_file);
668		goto errorout;
669        }
670
671	free(st);
672        return 0;
673
674errorout:
675	if (st)
676		free(st);
677	return(-1);
678}
679
680int
681check_ip6_forwarding(void)
682{
683	int forw_sysctl[] = { SYSCTL_IP6_FORWARDING };
684	int value;
685	size_t size = sizeof(value);
686	FILE *fp = NULL;
687
688#ifdef __linux__
689	fp = fopen(PROC_SYS_IP6_FORWARDING, "r");
690	if (fp) {
691		fscanf(fp, "%d", &value);
692		fclose(fp);
693	}
694	else
695		flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry not found, "
696	                       "perhaps the procfs is disabled, "
697	                        "or the kernel interface has changed?");
698#endif /* __linux__ */
699
700	if (!fp && sysctl(forw_sysctl, sizeof(forw_sysctl)/sizeof(forw_sysctl[0]),
701	    &value, &size, NULL, 0) < 0) {
702		flog(LOG_DEBUG, "Correct IPv6 forwarding sysctl branch not found, "
703			"perhaps the kernel interface has changed?");
704		return(0);	/* this is of advisory value only */
705	}
706
707	if (value != 1) {
708		flog(LOG_DEBUG, "IPv6 forwarding setting is: %u, should be 1", value);
709		return(-1);
710	}
711
712	return(0);
713}
714
715int
716readin_config(char *fname)
717{
718	if ((yyin = fopen(fname, "r")) == NULL)
719	{
720		flog(LOG_ERR, "can't open %s: %s", fname, strerror(errno));
721		return (-1);
722	}
723
724	if (yyparse() != 0)
725	{
726		flog(LOG_ERR, "error parsing or activating the config file: %s", fname);
727		return (-1);
728	}
729
730	fclose(yyin);
731	return 0;
732}
733
734void
735version(void)
736{
737	fprintf(stderr, "Version: %s\n\n", VERSION);
738	fprintf(stderr, "Compiled in settings:\n");
739	fprintf(stderr, "  default config file		\"%s\"\n", PATH_RADVD_CONF);
740	fprintf(stderr, "  default pidfile		\"%s\"\n", PATH_RADVD_PID);
741	fprintf(stderr, "  default logfile		\"%s\"\n", PATH_RADVD_LOG);
742	fprintf(stderr, "  default syslog facililty	%d\n", LOG_FACILITY);
743	fprintf(stderr, "Please send bug reports or suggestions to %s.\n",
744		CONTACT_EMAIL);
745
746	exit(1);
747}
748
749void
750usage(void)
751{
752	fprintf(stderr, "usage: %s %s\n", pname, usage_str);
753	exit(1);
754}
755
756