rtsold.c revision 66776
1/*	$KAME: rtsold.c,v 1.26 2000/08/13 18:17:15 itojun 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: head/usr.sbin/rtsold/rtsold.c 66776 2000-10-06 23:46:52Z kris $
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
56struct ifinfo *iflist;
57struct timeval tm_max =	{0x7fffffff, 0x7fffffff};
58int aflag = 0;
59int dflag = 0;
60static int log_upto = 999;
61static 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
85int main __P((int argc, char *argv[]));
86
87/* static variables and functions */
88static int mobile_node = 0;
89static int do_dump;
90static char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
91static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
92
93static int ifconfig __P((char *ifname));
94#if 0
95static int ifreconfig __P((char *ifname));
96#endif
97static int make_packet __P((struct ifinfo *ifinfo));
98static struct timeval *rtsol_check_timer __P((void));
99static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b,
100			     struct timeval *result));
101static void TIMEVAL_SUB __P((struct timeval *a, struct timeval *b,
102			     struct timeval *result));
103
104static void rtsold_set_dump_file __P((void));
105static void usage __P((char *progname));
106static char **autoifprobe __P((void));
107
108int
109main(argc, argv)
110	int argc;
111	char *argv[];
112{
113	int s, 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
227	/* configuration per interface */
228	if (ifinit()) {
229		errx(1, "failed to initilizatoin interfaces");
230		/*NOTREACHED*/
231	}
232	while (argc--) {
233		if (ifconfig(*argv)) {
234			errx(1, "failed to initialize %s", *argv);
235			/*NOTREACHED*/
236		}
237		argv++;
238	}
239
240	/* setup for probing default routers */
241	if (probe_init()) {
242		errx(1, "failed to setup for probing routers");
243		/*NOTREACHED*/
244	}
245
246	if (!fflag)
247		daemon(0, 0);		/* act as a daemon */
248
249	/* dump the current pid */
250	if (!once) {
251		pid_t pid = getpid();
252		FILE *fp;
253
254		if ((fp = fopen(pidfilename, "w")) == NULL)
255			warnmsg(LOG_ERR, __FUNCTION__,
256				"failed to open a log file(%s): %s",
257				pidfilename, strerror(errno));
258		else {
259			fprintf(fp, "%d\n", pid);
260			fclose(fp);
261		}
262	}
263
264	FD_ZERO(&fdset);
265	FD_SET(s, &fdset);
266	while (1) {		/* main loop */
267		int e;
268		struct fd_set select_fd = fdset;
269
270		if (do_dump) {	/* SIGUSR1 */
271			do_dump = 0;
272			rtsold_dump_file(dumpfilename);
273		}
274
275		timeout = rtsol_check_timer();
276
277		if (once) {
278			struct ifinfo *ifi;
279
280			/* if we have no timeout, we are done (or failed) */
281			if (timeout == NULL)
282				break;
283
284			/* if all interfaces have got RA packet, we are done */
285			for (ifi = iflist; ifi; ifi = ifi->next) {
286				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
287					break;
288			}
289			if (ifi == NULL)
290				break;
291		}
292
293		if ((e = select(s + 1, &select_fd, NULL, NULL, timeout)) < 1) {
294			if (e < 0 && errno != EINTR) {
295				warnmsg(LOG_ERR, __FUNCTION__, "select: %s",
296				       strerror(errno));
297			}
298			continue;
299		}
300
301		/* packet reception */
302		if (FD_ISSET(s, &fdset))
303			rtsol_input(s);
304	}
305	/* NOTREACHED */
306
307	return 0;
308}
309
310static int
311ifconfig(char *ifname)
312{
313	struct ifinfo *ifinfo;
314	struct sockaddr_dl *sdl;
315	int flags;
316
317	if ((sdl = if_nametosdl(ifname)) == NULL) {
318		warnmsg(LOG_ERR, __FUNCTION__,
319		       "failed to get link layer information for %s", ifname);
320		return(-1);
321	}
322	if (find_ifinfo(sdl->sdl_index)) {
323		warnmsg(LOG_ERR, __FUNCTION__,
324			"interface %s was already cofigured", ifname);
325		free(sdl);
326		return(-1);
327	}
328
329	if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
330		warnmsg(LOG_ERR, __FUNCTION__, "memory allocation failed");
331		free(sdl);
332		return(-1);
333	}
334	memset(ifinfo, 0, sizeof(*ifinfo));
335	ifinfo->sdl = sdl;
336
337	strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
338
339	/* construct a router solicitation message */
340	if (make_packet(ifinfo))
341		goto bad;
342
343	/*
344	 * check if the interface is available.
345	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
346	 */
347	ifinfo->mediareqok = 1;
348	ifinfo->active = interface_status(ifinfo);
349	if (!ifinfo->mediareqok) {
350		/*
351		 * probe routers periodically even if the link status
352		 * does not change.
353		 */
354		ifinfo->probeinterval = PROBE_INTERVAL;
355	}
356
357	/* activate interface: interface_up returns 0 on success */
358	flags = interface_up(ifinfo->ifname);
359	if (flags == 0)
360		ifinfo->state = IFS_DELAY;
361	else if (flags == IFS_TENTATIVE)
362		ifinfo->state = IFS_TENTATIVE;
363	else
364		ifinfo->state = IFS_DOWN;
365
366	rtsol_timer_update(ifinfo);
367
368	/* link into chain */
369	if (iflist)
370		ifinfo->next = iflist;
371	iflist = ifinfo;
372
373	return(0);
374
375  bad:
376	free(ifinfo->sdl);
377	free(ifinfo);
378	return(-1);
379}
380
381#if 0
382static int
383ifreconfig(char *ifname)
384{
385	struct ifinfo *ifi, *prev;
386	int rv;
387
388	prev = NULL;
389	for (ifi = iflist; ifi; ifi = ifi->next) {
390		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
391			break;
392		prev = ifi;
393	}
394	prev->next = ifi->next;
395
396	rv = ifconfig(ifname);
397
398	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
399	if (ifi->rs_data)
400		free(ifi->rs_data);
401	free(ifi->sdl);
402	free(ifi);
403
404	return rv;
405}
406#endif
407
408struct ifinfo *
409find_ifinfo(int ifindex)
410{
411	struct ifinfo *ifi;
412
413	for (ifi = iflist; ifi; ifi = ifi->next)
414		if (ifi->sdl->sdl_index == ifindex)
415			return(ifi);
416
417	return(NULL);
418}
419
420static int
421make_packet(struct ifinfo *ifinfo)
422{
423	char *buf;
424	struct nd_router_solicit *rs;
425	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
426
427	if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
428		warnmsg(LOG_INFO, __FUNCTION__,
429			"link-layer address option has null length"
430		       " on %s. Treat as not included.", ifinfo->ifname);
431	}
432	packlen += lladdroptlen;
433	ifinfo->rs_datalen = packlen;
434
435	/* allocate buffer */
436	if ((buf = malloc(packlen)) == NULL) {
437		warnmsg(LOG_ERR, __FUNCTION__,
438			"memory allocation failed for %s", ifinfo->ifname);
439		return(-1);
440	}
441	ifinfo->rs_data = buf;
442
443	/* fill in the message */
444	rs = (struct nd_router_solicit *)buf;
445	rs->nd_rs_type = ND_ROUTER_SOLICIT;
446	rs->nd_rs_code = 0;
447	rs->nd_rs_cksum = 0;
448	rs->nd_rs_reserved = 0;
449	buf += sizeof(*rs);
450
451	/* fill in source link-layer address option */
452	if (lladdroptlen)
453		lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
454
455	return(0);
456}
457
458static struct timeval *
459rtsol_check_timer()
460{
461	static struct timeval returnval;
462	struct timeval now, rtsol_timer;
463	struct ifinfo *ifinfo;
464	int flags;
465
466	gettimeofday(&now, NULL);
467
468	rtsol_timer = tm_max;
469
470	for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
471		if (TIMEVAL_LEQ(ifinfo->expire, now)) {
472			if (dflag > 1)
473				warnmsg(LOG_DEBUG, __FUNCTION__,
474					"timer expiration on %s, "
475				       "state = %d", ifinfo->ifname,
476				       ifinfo->state);
477
478			switch (ifinfo->state) {
479			case IFS_DOWN:
480			case IFS_TENTATIVE:
481				/* interface_up returns 0 on success */
482				flags = interface_up(ifinfo->ifname);
483				if (flags == 0)
484					ifinfo->state = IFS_DELAY;
485				else if (flags == IFS_TENTATIVE)
486					ifinfo->state = IFS_TENTATIVE;
487				else
488					ifinfo->state = IFS_DOWN;
489				break;
490			case IFS_IDLE:
491			{
492				int oldstatus = ifinfo->active;
493				int probe = 0;
494
495				ifinfo->active =
496					interface_status(ifinfo);
497
498				if (oldstatus != ifinfo->active) {
499					warnmsg(LOG_DEBUG, __FUNCTION__,
500						"%s status is changed"
501						" from %d to %d",
502						ifinfo->ifname,
503						oldstatus, ifinfo->active);
504					probe = 1;
505					ifinfo->state = IFS_DELAY;
506				}
507				else if (ifinfo->probeinterval &&
508					 (ifinfo->probetimer -=
509					  ifinfo->timer.tv_sec) <= 0) {
510					/* probe timer expired */
511					ifinfo->probetimer =
512						ifinfo->probeinterval;
513					probe = 1;
514					ifinfo->state = IFS_PROBE;
515				}
516
517				if (probe && mobile_node)
518					defrouter_probe(ifinfo->sdl->sdl_index);
519				break;
520			}
521			case IFS_DELAY:
522				ifinfo->state = IFS_PROBE;
523				sendpacket(ifinfo);
524				break;
525			case IFS_PROBE:
526				if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
527					sendpacket(ifinfo);
528				else {
529					warnmsg(LOG_INFO, __FUNCTION__,
530						"No answer "
531						"after sending %d RSs",
532						ifinfo->probes);
533					ifinfo->probes = 0;
534					ifinfo->state = IFS_IDLE;
535				}
536				break;
537			}
538			rtsol_timer_update(ifinfo);
539		}
540
541		if (TIMEVAL_LT(ifinfo->expire, rtsol_timer))
542			rtsol_timer = ifinfo->expire;
543	}
544
545	if (TIMEVAL_EQ(rtsol_timer, tm_max)) {
546		warnmsg(LOG_DEBUG, __FUNCTION__, "there is no timer");
547		return(NULL);
548	}
549	else if (TIMEVAL_LT(rtsol_timer, now))
550		/* this may occur when the interval is too small */
551		returnval.tv_sec = returnval.tv_usec = 0;
552	else
553		TIMEVAL_SUB(&rtsol_timer, &now, &returnval);
554
555	if (dflag > 1)
556		warnmsg(LOG_DEBUG, __FUNCTION__, "New timer is %ld:%08ld",
557			(long)returnval.tv_sec, (long)returnval.tv_usec);
558
559	return(&returnval);
560}
561
562void
563rtsol_timer_update(struct ifinfo *ifinfo)
564{
565#define MILLION 1000000
566#define DADRETRY 10		/* XXX: adhoc */
567	long interval;
568	struct timeval now;
569
570	bzero(&ifinfo->timer, sizeof(ifinfo->timer));
571
572	switch (ifinfo->state) {
573	case IFS_DOWN:
574	case IFS_TENTATIVE:
575		if (++ifinfo->dadcount > DADRETRY) {
576			ifinfo->dadcount = 0;
577			ifinfo->timer.tv_sec = PROBE_INTERVAL;
578		}
579		else
580			ifinfo->timer.tv_sec = 1;
581		break;
582	case IFS_IDLE:
583		if (mobile_node) {
584			/* XXX should be configurable */
585			ifinfo->timer.tv_sec = 3;
586		}
587		else
588			ifinfo->timer = tm_max;	/* stop timer(valid?) */
589		break;
590	case IFS_DELAY:
591#ifndef HAVE_ARC4RANDOM
592		interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
593#else
594		interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
595#endif
596		ifinfo->timer.tv_sec = interval / MILLION;
597		ifinfo->timer.tv_usec = interval % MILLION;
598		break;
599	case IFS_PROBE:
600		ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
601		break;
602	default:
603		warnmsg(LOG_ERR, __FUNCTION__,
604			"illegal interface state(%d) on %s",
605			ifinfo->state, ifinfo->ifname);
606		return;
607	}
608
609	/* reset the timer */
610	if (TIMEVAL_EQ(ifinfo->timer, tm_max)) {
611		ifinfo->expire = tm_max;
612		warnmsg(LOG_DEBUG, __FUNCTION__,
613			"stop timer for %s", ifinfo->ifname);
614	}
615	else {
616		gettimeofday(&now, NULL);
617		TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire);
618
619		if (dflag > 1)
620			warnmsg(LOG_DEBUG, __FUNCTION__,
621				"set timer for %s to %d:%d", ifinfo->ifname,
622			       (int)ifinfo->timer.tv_sec,
623			       (int)ifinfo->timer.tv_usec);
624	}
625
626#undef MILLION
627}
628
629/* timer related utility functions */
630#define MILLION 1000000
631
632/* result = a + b */
633static void
634TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
635{
636	long l;
637
638	if ((l = a->tv_usec + b->tv_usec) < MILLION) {
639		result->tv_usec = l;
640		result->tv_sec = a->tv_sec + b->tv_sec;
641	}
642	else {
643		result->tv_usec = l - MILLION;
644		result->tv_sec = a->tv_sec + b->tv_sec + 1;
645	}
646}
647
648/*
649 * result = a - b
650 * XXX: this function assumes that a >= b.
651 */
652void
653TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
654{
655	long l;
656
657	if ((l = a->tv_usec - b->tv_usec) >= 0) {
658		result->tv_usec = l;
659		result->tv_sec = a->tv_sec - b->tv_sec;
660	}
661	else {
662		result->tv_usec = MILLION + l;
663		result->tv_sec = a->tv_sec - b->tv_sec - 1;
664	}
665}
666
667static void
668rtsold_set_dump_file()
669{
670	do_dump = 1;
671}
672
673static void
674usage(char *progname)
675{
676	if (progname && progname[strlen(progname) - 1] != 'd') {
677		fprintf(stderr, "usage: rtsol [-dD] interfaces...\n");
678		fprintf(stderr, "usage: rtsol [-dD] -a\n");
679	} else {
680		fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n");
681		fprintf(stderr, "usage: rtsold [-dDfm1] -a\n");
682	}
683	exit(1);
684}
685
686void
687#if __STDC__
688warnmsg(int priority, const char *func, const char *msg, ...)
689#else
690warnmsg(priority, func, msg, va_alist)
691	int priority;
692	const char *func;
693	const char *msg;
694	va_dcl
695#endif
696{
697	va_list ap;
698	char buf[BUFSIZ];
699
700	va_start(ap, msg);
701	if (fflag) {
702		if (priority <= log_upto) {
703			(void)vfprintf(stderr, msg, ap);
704			(void)fprintf(stderr, "\n");
705		}
706	} else {
707		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
708		msg = buf;
709		vsyslog(priority, msg, ap);
710	}
711	va_end(ap);
712}
713
714static char **
715autoifprobe()
716{
717#ifndef HAVE_GETIFADDRS
718	errx(1, "-a is not available with the configuration");
719#else
720	static char ifname[IFNAMSIZ + 1];
721	static char *argv[2];
722	struct ifaddrs *ifap, *ifa, *target;
723
724	if (getifaddrs(&ifap) != 0)
725		return NULL;
726
727	target = NULL;
728	/* find an ethernet */
729	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
730		if ((ifa->ifa_flags & IFF_UP) == 0)
731			continue;
732		if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
733			continue;
734		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
735			continue;
736		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
737			continue;
738
739		if (ifa->ifa_addr->sa_family != AF_INET6)
740			continue;
741
742		if (target && strcmp(target->ifa_name, ifa->ifa_name) == 0)
743			continue;
744
745		if (!target)
746			target = ifa;
747		else {
748			/* if we find multiple candidates, failure. */
749			if (dflag > 1)
750				warnx("multiple interfaces found");
751			target = NULL;
752			break;
753		}
754	}
755
756	if (target) {
757		strncpy(ifname, target->ifa_name, sizeof(ifname) - 1);
758		ifname[sizeof(ifname) - 1] = '\0';
759		argv[0] = ifname;
760		argv[1] = NULL;
761
762		if (dflag > 0)
763			warnx("probing %s", argv[0]);
764	}
765	freeifaddrs(ifap);
766	if (target)
767		return argv;
768	else
769		return (char **)NULL;
770#endif
771}
772