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