xref: /freebsd/usr.sbin/rtsold/rtsold.c (revision b601c69bdbe8755d26570261d7fd4c02ee4eff74)
1 /*
2  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the project nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/socket.h>
35 
36 #include <net/if.h>
37 #include <net/if_dl.h>
38 
39 #include <netinet/in.h>
40 #include <netinet/icmp6.h>
41 
42 #include <signal.h>
43 #include <unistd.h>
44 #include <syslog.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <errno.h>
49 #include <err.h>
50 #include <stdarg.h>
51 #include "rtsold.h"
52 
53 struct ifinfo *iflist;
54 struct timeval tm_max =	{0x7fffffff, 0x7fffffff};
55 int dflag;
56 static int log_upto = 999;
57 static int fflag = 0;
58 
59 /* protocol constatns */
60 #define MAX_RTR_SOLICITATION_DELAY	1 /* second */
61 #define RTR_SOLICITATION_INTERVAL	4 /* seconds */
62 #define MAX_RTR_SOLICITATIONS		3 /* times */
63 
64 /* implementation dependent constants */
65 #define PROBE_INTERVAL 60	/* secondes XXX: should be configurable */
66 
67 /* utility macros */
68 /* a < b */
69 #define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
70 			  (((a).tv_sec == (b).tv_sec) && \
71 			    ((a).tv_usec < (b).tv_usec)))
72 
73 /* a <= b */
74 #define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
75 			   (((a).tv_sec == (b).tv_sec) &&\
76  			    ((a).tv_usec <= (b).tv_usec)))
77 
78 /* a == b */
79 #define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
80 
81 int main __P((int argc, char *argv[]));
82 
83 /* static variables and functions */
84 static int mobile_node = 0;
85 static int do_dump;
86 static char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
87 static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
88 
89 static int ifconfig __P((char *ifname));
90 #if 0
91 static int ifreconfig __P((char *ifname));
92 #endif
93 static int make_packet __P((struct ifinfo *ifinfo));
94 static struct timeval *rtsol_check_timer __P((void));
95 static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b,
96 			     struct timeval *result));
97 static void TIMEVAL_SUB __P((struct timeval *a, struct timeval *b,
98 			     struct timeval *result));
99 
100 static void rtsold_set_dump_file __P((void));
101 static void usage __P((char *progname));
102 
103 int
104 main(argc, argv)
105 	int argc;
106 	char *argv[];
107 {
108 	int s, ch;
109 	int once = 0;
110 	struct timeval *timeout;
111 	struct fd_set fdset;
112 	char *argv0;
113 	char *opts;
114 
115 	/*
116 	 * Initialization
117 	 */
118 	argv0 = argv[0];
119 
120 	/* get option */
121 	if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
122 		fflag = 1;
123 		once = 1;
124 		opts = "dD";
125 	} else
126 		opts = "dDfm1";
127 
128 	while ((ch = getopt(argc, argv, opts)) != -1) {
129 		switch(ch) {
130 		 case 'd':
131 			 dflag = 1;
132 			 break;
133 		 case 'D':
134 			 dflag = 2;
135 			 break;
136 		 case 'f':
137 			 fflag = 1;
138 			 break;
139 		 case 'm':
140 			 mobile_node = 1;
141 			 break;
142 		 case '1':
143 			 once = 1;
144 			 break;
145 		 default:
146 			 usage(argv0);
147 		}
148 	}
149 	argc -= optind;
150 	argv += optind;
151 	if (argc == 0)
152 		usage(argv0);
153 
154 	/* set log level */
155 	if (dflag == 0)
156 		log_upto = LOG_NOTICE;
157 	if (!fflag) {
158 		char *ident;
159 		ident = strrchr(argv0, '/');
160 		if (!ident)
161 			ident = argv0;
162 		else
163 			ident++;
164 		openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
165 		if (log_upto >= 0)
166 			setlogmask(LOG_UPTO(log_upto));
167 	}
168 
169 #ifndef HAVE_ARC4RANDOM
170 	/* random value initilization */
171 	srandom((u_long)time(NULL));
172 #endif
173 
174 	/* warn if accept_rtadv is down */
175 	if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
176 		warnx("kernel is configured not to accept RAs");
177 
178 	/* initialization to dump internal status to a file */
179 	if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0)
180 		errx(1, "failed to set signal for dump status");
181 
182 	/*
183 	 * Open a socket for sending RS and receiving RA.
184 	 * This should be done before calling ifinit(), since the function
185 	 * uses the socket.
186 	 */
187 	if ((s = sockopen()) < 0)
188 		errx(1, "failed to open a socket");
189 
190 	/* configuration per interface */
191 	if (ifinit())
192 		errx(1, "failed to initilizatoin interfaces");
193 	while (argc--) {
194 		if (ifconfig(*argv))
195 			errx(1, "failed to initialize %s", *argv);
196 		argv++;
197 	}
198 
199 	/* setup for probing default routers */
200 	if (probe_init())
201 		errx(1, "failed to setup for probing routers");
202 
203 	if (!fflag)
204 		daemon(0, 0);		/* act as a daemon */
205 
206 	/* dump the current pid */
207 	if (!once) {
208 		pid_t pid = getpid();
209 		FILE *fp;
210 
211 		if ((fp = fopen(pidfilename, "w")) == NULL)
212 			warnmsg(LOG_ERR, __FUNCTION__,
213 				"failed to open a log file(%s)",
214 				pidfilename, strerror(errno));
215 		else {
216 			fprintf(fp, "%d\n", pid);
217 			fclose(fp);
218 		}
219 	}
220 
221 	FD_ZERO(&fdset);
222 	FD_SET(s, &fdset);
223 	while (1) {		/* main loop */
224 		int e;
225 		struct fd_set select_fd = fdset;
226 
227 		if (do_dump) {	/* SIGUSR1 */
228 			do_dump = 0;
229 			rtsold_dump_file(dumpfilename);
230 		}
231 
232 		timeout = rtsol_check_timer();
233 
234 		if (once) {
235 			struct ifinfo *ifi;
236 
237 			/* if we have no timeout, we are done (or failed) */
238 			if (timeout == NULL)
239 				break;
240 
241 			/* if all interfaces have got RA packet, we are done */
242 			for (ifi = iflist; ifi; ifi = ifi->next) {
243 				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
244 					break;
245 			}
246 			if (ifi == NULL)
247 				break;
248 		}
249 
250 		if ((e = select(s + 1, &select_fd, NULL, NULL, timeout)) < 1) {
251 			if (e < 0 && errno != EINTR) {
252 				warnmsg(LOG_ERR, __FUNCTION__, "select: %s",
253 				       strerror(errno));
254 			}
255 			continue;
256 		}
257 
258 		/* packet reception */
259 		if (FD_ISSET(s, &fdset))
260 			rtsol_input(s);
261 	}
262 	/* NOTREACHED */
263 
264 	return 0;
265 }
266 
267 static int
268 ifconfig(char *ifname)
269 {
270 	struct ifinfo *ifinfo;
271 	struct sockaddr_dl *sdl;
272 	int flags;
273 
274 	if ((sdl = if_nametosdl(ifname)) == NULL) {
275 		warnmsg(LOG_ERR, __FUNCTION__,
276 		       "failed to get link layer information for %s", ifname);
277 		return(-1);
278 	}
279 	if (find_ifinfo(sdl->sdl_index)) {
280 		warnmsg(LOG_ERR, __FUNCTION__,
281 			"interface %s was already cofigured", ifname);
282 		free(sdl);
283 		return(-1);
284 	}
285 
286 	if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
287 		warnmsg(LOG_ERR, __FUNCTION__, "memory allocation failed");
288 		free(sdl);
289 		return(-1);
290 	}
291 	memset(ifinfo, 0, sizeof(*ifinfo));
292 	ifinfo->sdl = sdl;
293 
294 	strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
295 
296 	/* construct a router solicitation message */
297 	if (make_packet(ifinfo))
298 		goto bad;
299 
300 	/*
301 	 * check if the interface is available.
302 	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
303 	 */
304 	ifinfo->mediareqok = 1;
305 	ifinfo->active = interface_status(ifinfo);
306 	if (!ifinfo->mediareqok) {
307 		/*
308 		 * probe routers periodically even if the link status
309 		 * does not change.
310 		 */
311 		ifinfo->probeinterval = PROBE_INTERVAL;
312 	}
313 
314 	/* activate interface: interface_up returns 0 on success */
315 	flags = interface_up(ifinfo->ifname);
316 	if (flags == 0)
317 		ifinfo->state = IFS_DELAY;
318 	else if (flags == IFS_TENTATIVE)
319 		ifinfo->state = IFS_TENTATIVE;
320 	else
321 		ifinfo->state = IFS_DOWN;
322 
323 	rtsol_timer_update(ifinfo);
324 
325 	/* link into chain */
326 	if (iflist)
327 		ifinfo->next = iflist;
328 	iflist = ifinfo;
329 
330 	return(0);
331 
332   bad:
333 	free(ifinfo->sdl);
334 	free(ifinfo);
335 	return(-1);
336 }
337 
338 #if 0
339 static int
340 ifreconfig(char *ifname)
341 {
342 	struct ifinfo *ifi, *prev;
343 	int rv;
344 
345 	prev = NULL;
346 	for (ifi = iflist; ifi; ifi = ifi->next) {
347 		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
348 			break;
349 		prev = ifi;
350 	}
351 	prev->next = ifi->next;
352 
353 	rv = ifconfig(ifname);
354 
355 	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
356 	if (ifi->rs_data)
357 		free(ifi->rs_data);
358 	free(ifi->sdl);
359 	free(ifi);
360 
361 	return rv;
362 }
363 #endif
364 
365 struct ifinfo *
366 find_ifinfo(int ifindex)
367 {
368 	struct ifinfo *ifi;
369 
370 	for (ifi = iflist; ifi; ifi = ifi->next)
371 		if (ifi->sdl->sdl_index == ifindex)
372 			return(ifi);
373 
374 	return(NULL);
375 }
376 
377 static int
378 make_packet(struct ifinfo *ifinfo)
379 {
380 	char *buf;
381 	struct nd_router_solicit *rs;
382 	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
383 
384 	if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
385 		warnmsg(LOG_INFO, __FUNCTION__,
386 			"link-layer address option has null length"
387 		       " on %s. Treat as not included.", ifinfo->ifname);
388 	}
389 	packlen += lladdroptlen;
390 	ifinfo->rs_datalen = packlen;
391 
392 	/* allocate buffer */
393 	if ((buf = malloc(packlen)) == NULL) {
394 		warnmsg(LOG_ERR, __FUNCTION__,
395 			"memory allocation failed for %s", ifinfo->ifname);
396 		return(-1);
397 	}
398 	ifinfo->rs_data = buf;
399 
400 	/* fill in the message */
401 	rs = (struct nd_router_solicit *)buf;
402 	rs->nd_rs_type = ND_ROUTER_SOLICIT;
403 	rs->nd_rs_code = 0;
404 	rs->nd_rs_cksum = 0;
405 	rs->nd_rs_reserved = 0;
406 	buf += sizeof(*rs);
407 
408 	/* fill in source link-layer address option */
409 	if (lladdroptlen)
410 		lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
411 
412 	return(0);
413 }
414 
415 static struct timeval *
416 rtsol_check_timer()
417 {
418 	static struct timeval returnval;
419 	struct timeval now, rtsol_timer;
420 	struct ifinfo *ifinfo;
421 	int flags;
422 
423 	gettimeofday(&now, NULL);
424 
425 	rtsol_timer = tm_max;
426 
427 	for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
428 		if (TIMEVAL_LEQ(ifinfo->expire, now)) {
429 			if (dflag > 1)
430 				warnmsg(LOG_DEBUG, __FUNCTION__,
431 					"timer expiration on %s, "
432 				       "state = %d", ifinfo->ifname,
433 				       ifinfo->state);
434 
435 			switch(ifinfo->state) {
436 			case IFS_DOWN:
437 			case IFS_TENTATIVE:
438 				/* interface_up returns 0 on success */
439 				flags = interface_up(ifinfo->ifname);
440 				if (flags == 0)
441 					ifinfo->state = IFS_DELAY;
442 				else if (flags == IFS_TENTATIVE)
443 					ifinfo->state = IFS_TENTATIVE;
444 				else
445 					ifinfo->state = IFS_DOWN;
446 				break;
447 			case IFS_IDLE:
448 			{
449 				int oldstatus = ifinfo->active;
450 				int probe = 0;
451 
452 				ifinfo->active =
453 					interface_status(ifinfo);
454 
455 				if (oldstatus != ifinfo->active) {
456 					warnmsg(LOG_DEBUG, __FUNCTION__,
457 						"%s status is changed"
458 						" from %d to %d",
459 						ifinfo->ifname,
460 						oldstatus, ifinfo->active);
461 					probe = 1;
462 					ifinfo->state = IFS_DELAY;
463 				}
464 				else if (ifinfo->probeinterval &&
465 					 (ifinfo->probetimer -=
466 					  ifinfo->timer.tv_sec) <= 0) {
467 					/* probe timer expired */
468 					ifinfo->probetimer =
469 						ifinfo->probeinterval;
470 					probe = 1;
471 					ifinfo->state = IFS_PROBE;
472 				}
473 
474 				if (probe && mobile_node)
475 					defrouter_probe(ifinfo->sdl->sdl_index);
476 				break;
477 			}
478 			case IFS_DELAY:
479 				ifinfo->state = IFS_PROBE;
480 				sendpacket(ifinfo);
481 				break;
482 			case IFS_PROBE:
483 				if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
484 					sendpacket(ifinfo);
485 				else {
486 					warnmsg(LOG_INFO, __FUNCTION__,
487 						"No answer "
488 						"after sending %d RSs",
489 						ifinfo->probes);
490 					ifinfo->probes = 0;
491 					ifinfo->state = IFS_IDLE;
492 				}
493 				break;
494 			}
495 			rtsol_timer_update(ifinfo);
496 		}
497 
498 		if (TIMEVAL_LT(ifinfo->expire, rtsol_timer))
499 			rtsol_timer = ifinfo->expire;
500 	}
501 
502 	if (TIMEVAL_EQ(rtsol_timer, tm_max)) {
503 		warnmsg(LOG_DEBUG, __FUNCTION__, "there is no timer");
504 		return(NULL);
505 	}
506 	else if (TIMEVAL_LT(rtsol_timer, now))
507 		/* this may occur when the interval is too small */
508 		returnval.tv_sec = returnval.tv_usec = 0;
509 	else
510 		TIMEVAL_SUB(&rtsol_timer, &now, &returnval);
511 
512 	if (dflag > 1)
513 		warnmsg(LOG_DEBUG, __FUNCTION__, "New timer is %d:%08d",
514 		       returnval.tv_sec, returnval.tv_usec);
515 
516 	return(&returnval);
517 }
518 
519 void
520 rtsol_timer_update(struct ifinfo *ifinfo)
521 {
522 #define MILLION 1000000
523 #define DADRETRY 10		/* XXX: adhoc */
524 	long interval;
525 	struct timeval now;
526 
527 	bzero(&ifinfo->timer, sizeof(ifinfo->timer));
528 
529 	switch (ifinfo->state) {
530 	case IFS_DOWN:
531 	case IFS_TENTATIVE:
532 		if (++ifinfo->dadcount > DADRETRY) {
533 			ifinfo->dadcount = 0;
534 			ifinfo->timer.tv_sec = PROBE_INTERVAL;
535 		}
536 		else
537 			ifinfo->timer.tv_sec = 1;
538 		break;
539 	case IFS_IDLE:
540 		if (mobile_node) {
541 			/* XXX should be configurable */
542 			ifinfo->timer.tv_sec = 3;
543 		}
544 		else
545 			ifinfo->timer = tm_max;	/* stop timer(valid?) */
546 		break;
547 	case IFS_DELAY:
548 #ifndef HAVE_ARC4RANDOM
549 		interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
550 #else
551 		interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
552 #endif
553 		ifinfo->timer.tv_sec = interval / MILLION;
554 		ifinfo->timer.tv_usec = interval % MILLION;
555 		break;
556 	case IFS_PROBE:
557 		ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
558 		break;
559 	default:
560 		warnmsg(LOG_ERR, __FUNCTION__,
561 			"illegal interface state(%d) on %s",
562 			ifinfo->state, ifinfo->ifname);
563 		return;
564 	}
565 
566 	/* reset the timer */
567 	if (TIMEVAL_EQ(ifinfo->timer, tm_max)) {
568 		ifinfo->expire = tm_max;
569 		warnmsg(LOG_DEBUG, __FUNCTION__,
570 			"stop timer for %s", ifinfo->ifname);
571 	}
572 	else {
573 		gettimeofday(&now, NULL);
574 		TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire);
575 
576 		if (dflag > 1)
577 			warnmsg(LOG_DEBUG, __FUNCTION__,
578 				"set timer for %s to %d:%d", ifinfo->ifname,
579 			       (int)ifinfo->timer.tv_sec,
580 			       (int)ifinfo->timer.tv_usec);
581 	}
582 
583 #undef MILLION
584 }
585 
586 /* timer related utility functions */
587 #define MILLION 1000000
588 
589 /* result = a + b */
590 static void
591 TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
592 {
593 	long l;
594 
595 	if ((l = a->tv_usec + b->tv_usec) < MILLION) {
596 		result->tv_usec = l;
597 		result->tv_sec = a->tv_sec + b->tv_sec;
598 	}
599 	else {
600 		result->tv_usec = l - MILLION;
601 		result->tv_sec = a->tv_sec + b->tv_sec + 1;
602 	}
603 }
604 
605 /*
606  * result = a - b
607  * XXX: this function assumes that a >= b.
608  */
609 void
610 TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
611 {
612 	long l;
613 
614 	if ((l = a->tv_usec - b->tv_usec) >= 0) {
615 		result->tv_usec = l;
616 		result->tv_sec = a->tv_sec - b->tv_sec;
617 	}
618 	else {
619 		result->tv_usec = MILLION + l;
620 		result->tv_sec = a->tv_sec - b->tv_sec - 1;
621 	}
622 }
623 
624 static void
625 rtsold_set_dump_file()
626 {
627 	do_dump = 1;
628 }
629 
630 static void
631 usage(char *progname)
632 {
633 	if (progname && progname[strlen(progname) - 1] != 'd')
634 		fprintf(stderr, "usage: rtsol [-dD] interfaces\n");
635 	else
636 		fprintf(stderr, "usage: rtsold [-dDfm1] interfaces\n");
637 	exit(1);
638 }
639 
640 void
641 #if __STDC__
642 warnmsg(int priority, const char *func, const char *msg, ...)
643 #else
644 warnmsg(priority, func, msg, va_alist)
645 	int priority;
646 	const char *func;
647 	const char *msg;
648 	va_dcl
649 #endif
650 {
651 	va_list ap;
652 	char buf[BUFSIZ];
653 
654 	va_start(ap, msg);
655 	if (fflag) {
656 		if (priority <= log_upto) {
657 			(void)vfprintf(stderr, msg, ap);
658 			(void)fprintf(stderr, "\n");
659 		}
660 	} else {
661 		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
662 		vsyslog(priority, buf, ap);
663 	}
664 	va_end(ap);
665 }
666