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