af_inet6.c revision 166956
1138593Ssam/*
2138593Ssam * Copyright (c) 1983, 1993
3138593Ssam *	The Regents of the University of California.  All rights reserved.
4138593Ssam *
5138593Ssam * Redistribution and use in source and binary forms, with or without
6138593Ssam * modification, are permitted provided that the following conditions
7138593Ssam * are met:
8138593Ssam * 1. Redistributions of source code must retain the above copyright
9138593Ssam *    notice, this list of conditions and the following disclaimer.
10138593Ssam * 2. Redistributions in binary form must reproduce the above copyright
11138593Ssam *    notice, this list of conditions and the following disclaimer in the
12138593Ssam *    documentation and/or other materials provided with the distribution.
13138593Ssam * 4. Neither the name of the University nor the names of its contributors
14138593Ssam *    may be used to endorse or promote products derived from this software
15138593Ssam *    without specific prior written permission.
16138593Ssam *
17138593Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18138593Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19138593Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20138593Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21138593Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22138593Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23138593Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24138593Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25138593Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26138593Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27138593Ssam * SUCH DAMAGE.
28138593Ssam */
29138593Ssam
30138593Ssam#ifndef lint
31138593Ssamstatic const char rcsid[] =
32138593Ssam  "$FreeBSD: head/sbin/ifconfig/af_inet6.c 166956 2007-02-24 23:55:46Z sam $";
33138593Ssam#endif /* not lint */
34138593Ssam
35138593Ssam#include <sys/param.h>
36138593Ssam#include <sys/ioctl.h>
37138593Ssam#include <sys/socket.h>
38138593Ssam#include <net/if.h>
39138593Ssam
40138593Ssam#include <err.h>
41138593Ssam#include <stdio.h>
42138593Ssam#include <stdlib.h>
43138593Ssam#include <string.h>
44138593Ssam#include <unistd.h>
45138593Ssam#include <ifaddrs.h>
46138593Ssam
47138593Ssam#include <arpa/inet.h>
48138593Ssam
49138593Ssam#include <netinet/in.h>
50138593Ssam#include <net/if_var.h>		/* for struct ifaddr */
51138593Ssam#include <netinet/in_var.h>
52138593Ssam#include <arpa/inet.h>
53138593Ssam#include <netdb.h>
54138593Ssam
55138593Ssam#include <netinet6/nd6.h>	/* Define ND6_INFINITE_LIFETIME */
56138593Ssam
57138593Ssam#include "ifconfig.h"
58138593Ssam
59138593Ssamstatic	struct in6_ifreq in6_ridreq;
60138593Ssamstatic	struct in6_aliasreq in6_addreq =
61138593Ssam  { { 0 },
62138593Ssam    { 0 },
63138593Ssam    { 0 },
64138593Ssam    { 0 },
65138593Ssam    0,
66138593Ssam    { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } };
67138593Ssamstatic	int ip6lifetime;
68138593Ssam
69138593Ssamstatic	void in6_fillscopeid(struct sockaddr_in6 *sin6);
70138593Ssamstatic	int prefix(void *, int);
71138593Ssamstatic	char *sec2str(time_t);
72138593Ssamstatic	int explicit_prefix = 0;
73138593Ssam
74138593Ssamstatic	char addr_buf[MAXHOSTNAMELEN *2 + 1];	/*for getnameinfo()*/
75138593Ssam
76138593Ssamstatic void
77138593Ssamsetifprefixlen(const char *addr, int dummy __unused, int s,
78138593Ssam    const struct afswtch *afp)
79138593Ssam{
80138593Ssam        if (afp->af_getprefix != NULL)
81138593Ssam                afp->af_getprefix(addr, MASK);
82138593Ssam	explicit_prefix = 1;
83138593Ssam}
84138593Ssam
85138593Ssamstatic void
86138593Ssamsetip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused,
87138593Ssam    const struct afswtch *afp)
88138593Ssam{
89138593Ssam	if (afp->af_af != AF_INET6)
90138593Ssam		err(1, "address flags can be set only for inet6 addresses");
91138593Ssam
92138593Ssam	if (flag < 0)
93138593Ssam		in6_addreq.ifra_flags &= ~(-flag);
94138593Ssam	else
95138593Ssam		in6_addreq.ifra_flags |= flag;
96138593Ssam}
97138593Ssam
98138593Ssamstatic void
99138593Ssamsetip6lifetime(const char *cmd, const char *val, int s,
100138593Ssam    const struct afswtch *afp)
101138593Ssam{
102138593Ssam	time_t newval, t;
103138593Ssam	char *ep;
104138593Ssam
105138593Ssam	t = time(NULL);
106138593Ssam	newval = (time_t)strtoul(val, &ep, 0);
107138593Ssam	if (val == ep)
108138593Ssam		errx(1, "invalid %s", cmd);
109138593Ssam	if (afp->af_af != AF_INET6)
110138593Ssam		errx(1, "%s not allowed for the AF", cmd);
111138593Ssam	if (strcmp(cmd, "vltime") == 0) {
112138593Ssam		in6_addreq.ifra_lifetime.ia6t_expire = t + newval;
113138593Ssam		in6_addreq.ifra_lifetime.ia6t_vltime = newval;
114138593Ssam	} else if (strcmp(cmd, "pltime") == 0) {
115138593Ssam		in6_addreq.ifra_lifetime.ia6t_preferred = t + newval;
116138593Ssam		in6_addreq.ifra_lifetime.ia6t_pltime = newval;
117138593Ssam	}
118138593Ssam}
119138593Ssam
120138593Ssamstatic void
121138593Ssamsetip6pltime(const char *seconds, int dummy __unused, int s,
122138593Ssam    const struct afswtch *afp)
123138593Ssam{
124138593Ssam	setip6lifetime("pltime", seconds, s, afp);
125138593Ssam}
126138593Ssam
127138593Ssamstatic void
128138593Ssamsetip6vltime(const char *seconds, int dummy __unused, int s,
129138593Ssam    const struct afswtch *afp)
130138593Ssam{
131138593Ssam	setip6lifetime("vltime", seconds, s, afp);
132138593Ssam}
133138593Ssam
134138593Ssamstatic void
135138593Ssamsetip6eui64(const char *cmd, int dummy __unused, int s,
136138593Ssam    const struct afswtch *afp)
137138593Ssam{
138138593Ssam	struct ifaddrs *ifap, *ifa;
139138593Ssam	const struct sockaddr_in6 *sin6 = NULL;
140138593Ssam	const struct in6_addr *lladdr = NULL;
141138593Ssam	struct in6_addr *in6;
142138593Ssam
143138593Ssam	if (afp->af_af != AF_INET6)
144138593Ssam		errx(EXIT_FAILURE, "%s not allowed for the AF", cmd);
145138593Ssam 	in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
146138593Ssam	if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
147138593Ssam		errx(EXIT_FAILURE, "interface index is already filled");
148138593Ssam	if (getifaddrs(&ifap) != 0)
149138593Ssam		err(EXIT_FAILURE, "getifaddrs");
150138593Ssam	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
151138593Ssam		if (ifa->ifa_addr->sa_family == AF_INET6 &&
152138593Ssam		    strcmp(ifa->ifa_name, name) == 0) {
153138593Ssam			sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
154138593Ssam			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
155138593Ssam				lladdr = &sin6->sin6_addr;
156138593Ssam				break;
157138593Ssam			}
158138593Ssam		}
159138593Ssam	}
160138593Ssam	if (!lladdr)
161138593Ssam		errx(EXIT_FAILURE, "could not determine link local address");
162138593Ssam
163138593Ssam 	memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
164138593Ssam
165138593Ssam	freeifaddrs(ifap);
166138593Ssam}
167138593Ssam
168138593Ssamstatic void
169138593Ssamin6_fillscopeid(struct sockaddr_in6 *sin6)
170138593Ssam{
171138593Ssam#if defined(__KAME__) && defined(KAME_SCOPEID)
172138593Ssam	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
173138593Ssam		sin6->sin6_scope_id =
174138593Ssam			ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
175138593Ssam		sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
176138593Ssam	}
177138593Ssam#endif
178138593Ssam}
179138593Ssam
180138593Ssamstatic void
181166956Ssamin6_status(int s __unused, const struct ifaddrs *ifa)
182138593Ssam{
183138593Ssam	struct sockaddr_in6 *sin, null_sin;
184138593Ssam	struct in6_ifreq ifr6;
185138593Ssam	int s6;
186138593Ssam	u_int32_t flags6;
187138593Ssam	struct in6_addrlifetime lifetime;
188138593Ssam	time_t t = time(NULL);
189138593Ssam	int error;
190138593Ssam	u_int32_t scopeid;
191138593Ssam
192138593Ssam	memset(&null_sin, 0, sizeof(null_sin));
193138593Ssam
194166956Ssam	sin = (struct sockaddr_in6 *)ifa->ifa_addr;
195138593Ssam	if (sin == NULL)
196138593Ssam		return;
197138593Ssam
198138593Ssam	strncpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
199138593Ssam	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
200138593Ssam		warn("socket(AF_INET6,SOCK_DGRAM)");
201138593Ssam		return;
202138593Ssam	}
203138593Ssam	ifr6.ifr_addr = *sin;
204138593Ssam	if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
205138593Ssam		warn("ioctl(SIOCGIFAFLAG_IN6)");
206138593Ssam		close(s6);
207138593Ssam		return;
208138593Ssam	}
209138593Ssam	flags6 = ifr6.ifr_ifru.ifru_flags6;
210138593Ssam	memset(&lifetime, 0, sizeof(lifetime));
211138593Ssam	ifr6.ifr_addr = *sin;
212138593Ssam	if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) {
213138593Ssam		warn("ioctl(SIOCGIFALIFETIME_IN6)");
214138593Ssam		close(s6);
215138593Ssam		return;
216138593Ssam	}
217138593Ssam	lifetime = ifr6.ifr_ifru.ifru_lifetime;
218138593Ssam	close(s6);
219138593Ssam
220138593Ssam	/* XXX: embedded link local addr check */
221138593Ssam	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) &&
222138593Ssam	    *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) {
223138593Ssam		u_short index;
224138593Ssam
225138593Ssam		index = *(u_short *)&sin->sin6_addr.s6_addr[2];
226138593Ssam		*(u_short *)&sin->sin6_addr.s6_addr[2] = 0;
227138593Ssam		if (sin->sin6_scope_id == 0)
228138593Ssam			sin->sin6_scope_id = ntohs(index);
229138593Ssam	}
230138593Ssam	scopeid = sin->sin6_scope_id;
231138593Ssam
232138593Ssam	error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf,
233146187Sume			    sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
234138593Ssam	if (error != 0)
235138593Ssam		inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf,
236138593Ssam			  sizeof(addr_buf));
237138593Ssam	printf("\tinet6 %s ", addr_buf);
238138593Ssam
239166956Ssam	if (ifa->ifa_flags & IFF_POINTOPOINT) {
240166956Ssam		sin = (struct sockaddr_in6 *)ifa->ifa_dstaddr;
241138593Ssam		/*
242138593Ssam		 * some of the interfaces do not have valid destination
243138593Ssam		 * address.
244138593Ssam		 */
245166956Ssam		if (sin != NULL && sin->sin6_family == AF_INET6) {
246138593Ssam			int error;
247138593Ssam
248138593Ssam			/* XXX: embedded link local addr check */
249138593Ssam			if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) &&
250138593Ssam			    *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) {
251138593Ssam				u_short index;
252138593Ssam
253138593Ssam				index = *(u_short *)&sin->sin6_addr.s6_addr[2];
254138593Ssam				*(u_short *)&sin->sin6_addr.s6_addr[2] = 0;
255138593Ssam				if (sin->sin6_scope_id == 0)
256138593Ssam					sin->sin6_scope_id = ntohs(index);
257138593Ssam			}
258138593Ssam
259138593Ssam			error = getnameinfo((struct sockaddr *)sin,
260138593Ssam					    sin->sin6_len, addr_buf,
261138593Ssam					    sizeof(addr_buf), NULL, 0,
262146187Sume					    NI_NUMERICHOST);
263138593Ssam			if (error != 0)
264138593Ssam				inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf,
265138593Ssam					  sizeof(addr_buf));
266138593Ssam			printf("--> %s ", addr_buf);
267138593Ssam		}
268138593Ssam	}
269138593Ssam
270166956Ssam	sin = (struct sockaddr_in6 *)ifa->ifa_netmask;
271166956Ssam	if (sin == NULL)
272138593Ssam		sin = &null_sin;
273138593Ssam	printf("prefixlen %d ", prefix(&sin->sin6_addr,
274138593Ssam		sizeof(struct in6_addr)));
275138593Ssam
276138593Ssam	if ((flags6 & IN6_IFF_ANYCAST) != 0)
277138593Ssam		printf("anycast ");
278138593Ssam	if ((flags6 & IN6_IFF_TENTATIVE) != 0)
279138593Ssam		printf("tentative ");
280138593Ssam	if ((flags6 & IN6_IFF_DUPLICATED) != 0)
281138593Ssam		printf("duplicated ");
282138593Ssam	if ((flags6 & IN6_IFF_DETACHED) != 0)
283138593Ssam		printf("detached ");
284138593Ssam	if ((flags6 & IN6_IFF_DEPRECATED) != 0)
285138593Ssam		printf("deprecated ");
286138593Ssam	if ((flags6 & IN6_IFF_AUTOCONF) != 0)
287138593Ssam		printf("autoconf ");
288138593Ssam	if ((flags6 & IN6_IFF_TEMPORARY) != 0)
289138593Ssam		printf("temporary ");
290138593Ssam
291138593Ssam        if (scopeid)
292138593Ssam		printf("scopeid 0x%x ", scopeid);
293138593Ssam
294138593Ssam	if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) {
295138593Ssam		printf("pltime ");
296138593Ssam		if (lifetime.ia6t_preferred) {
297138593Ssam			printf("%s ", lifetime.ia6t_preferred < t
298138593Ssam				? "0" : sec2str(lifetime.ia6t_preferred - t));
299138593Ssam		} else
300138593Ssam			printf("infty ");
301138593Ssam
302138593Ssam		printf("vltime ");
303138593Ssam		if (lifetime.ia6t_expire) {
304138593Ssam			printf("%s ", lifetime.ia6t_expire < t
305138593Ssam				? "0" : sec2str(lifetime.ia6t_expire - t));
306138593Ssam		} else
307138593Ssam			printf("infty ");
308138593Ssam	}
309138593Ssam
310138593Ssam	putchar('\n');
311138593Ssam}
312138593Ssam
313138593Ssam#define	SIN6(x) ((struct sockaddr_in6 *) &(x))
314138593Ssamstatic struct	sockaddr_in6 *sin6tab[] = {
315138593Ssam	SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
316138593Ssam	SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)
317138593Ssam};
318138593Ssam
319138593Ssamstatic void
320138593Ssamin6_getprefix(const char *plen, int which)
321138593Ssam{
322138593Ssam	struct sockaddr_in6 *sin = sin6tab[which];
323138593Ssam	u_char *cp;
324138593Ssam	int len = atoi(plen);
325138593Ssam
326138593Ssam	if ((len < 0) || (len > 128))
327138593Ssam		errx(1, "%s: bad value", plen);
328138593Ssam	sin->sin6_len = sizeof(*sin);
329138593Ssam	if (which != MASK)
330138593Ssam		sin->sin6_family = AF_INET6;
331138593Ssam	if ((len == 0) || (len == 128)) {
332138593Ssam		memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr));
333138593Ssam		return;
334138593Ssam	}
335138593Ssam	memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr));
336138593Ssam	for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8)
337138593Ssam		*cp++ = 0xff;
338138593Ssam	*cp = 0xff << (8 - len);
339138593Ssam}
340138593Ssam
341138593Ssamstatic void
342138593Ssamin6_getaddr(const char *s, int which)
343138593Ssam{
344138593Ssam	struct sockaddr_in6 *sin = sin6tab[which];
345138593Ssam	struct addrinfo hints, *res;
346138593Ssam	int error = -1;
347138593Ssam
348138593Ssam	newaddr &= 1;
349138593Ssam
350138593Ssam	sin->sin6_len = sizeof(*sin);
351138593Ssam	if (which != MASK)
352138593Ssam		sin->sin6_family = AF_INET6;
353138593Ssam
354138593Ssam	if (which == ADDR) {
355138593Ssam		char *p = NULL;
356138593Ssam		if((p = strrchr(s, '/')) != NULL) {
357138593Ssam			*p = '\0';
358138593Ssam			in6_getprefix(p + 1, MASK);
359138593Ssam			explicit_prefix = 1;
360138593Ssam		}
361138593Ssam	}
362138593Ssam
363138593Ssam	if (sin->sin6_family == AF_INET6) {
364138593Ssam		bzero(&hints, sizeof(struct addrinfo));
365138593Ssam		hints.ai_family = AF_INET6;
366138593Ssam		error = getaddrinfo(s, NULL, &hints, &res);
367138593Ssam	}
368138593Ssam	if (error != 0) {
369138593Ssam		if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1)
370138593Ssam			errx(1, "%s: bad value", s);
371138593Ssam	} else
372138593Ssam		bcopy(res->ai_addr, sin, res->ai_addrlen);
373138593Ssam}
374138593Ssam
375138593Ssamstatic int
376138593Ssamprefix(void *val, int size)
377138593Ssam{
378138593Ssam        u_char *name = (u_char *)val;
379138593Ssam        int byte, bit, plen = 0;
380138593Ssam
381138593Ssam        for (byte = 0; byte < size; byte++, plen += 8)
382138593Ssam                if (name[byte] != 0xff)
383138593Ssam                        break;
384138593Ssam	if (byte == size)
385138593Ssam		return (plen);
386138593Ssam	for (bit = 7; bit != 0; bit--, plen++)
387138593Ssam                if (!(name[byte] & (1 << bit)))
388138593Ssam                        break;
389138593Ssam        for (; bit != 0; bit--)
390138593Ssam                if (name[byte] & (1 << bit))
391138593Ssam                        return(0);
392138593Ssam        byte++;
393138593Ssam        for (; byte < size; byte++)
394138593Ssam                if (name[byte])
395138593Ssam                        return(0);
396138593Ssam        return (plen);
397138593Ssam}
398138593Ssam
399138593Ssamstatic char *
400138593Ssamsec2str(time_t total)
401138593Ssam{
402138593Ssam	static char result[256];
403138593Ssam	int days, hours, mins, secs;
404138593Ssam	int first = 1;
405138593Ssam	char *p = result;
406138593Ssam
407138593Ssam	if (0) {
408138593Ssam		days = total / 3600 / 24;
409138593Ssam		hours = (total / 3600) % 24;
410138593Ssam		mins = (total / 60) % 60;
411138593Ssam		secs = total % 60;
412138593Ssam
413138593Ssam		if (days) {
414138593Ssam			first = 0;
415138593Ssam			p += sprintf(p, "%dd", days);
416138593Ssam		}
417138593Ssam		if (!first || hours) {
418138593Ssam			first = 0;
419138593Ssam			p += sprintf(p, "%dh", hours);
420138593Ssam		}
421138593Ssam		if (!first || mins) {
422138593Ssam			first = 0;
423138593Ssam			p += sprintf(p, "%dm", mins);
424138593Ssam		}
425138593Ssam		sprintf(p, "%ds", secs);
426138593Ssam	} else
427138593Ssam		sprintf(result, "%lu", (unsigned long)total);
428138593Ssam
429138593Ssam	return(result);
430138593Ssam}
431138593Ssam
432138593Ssamstatic void
433138593Ssamin6_postproc(int s, const struct afswtch *afp)
434138593Ssam{
435138593Ssam	if (explicit_prefix == 0) {
436138593Ssam		/* Aggregatable address architecture defines all prefixes
437138593Ssam		   are 64. So, it is convenient to set prefixlen to 64 if
438138593Ssam		   it is not specified. */
439138593Ssam		setifprefixlen("64", 0, s, afp);
440138593Ssam		/* in6_getprefix("64", MASK) if MASK is available here... */
441138593Ssam	}
442138593Ssam}
443138593Ssam
444138593Ssamstatic void
445138593Ssamin6_status_tunnel(int s)
446138593Ssam{
447138593Ssam	char src[NI_MAXHOST];
448138593Ssam	char dst[NI_MAXHOST];
449138593Ssam	struct in6_ifreq in6_ifr;
450138593Ssam	const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr;
451138593Ssam
452138593Ssam	memset(&in6_ifr, 0, sizeof(in6_ifr));
453138593Ssam	strncpy(in6_ifr.ifr_name, name, IFNAMSIZ);
454138593Ssam
455138593Ssam	if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0)
456138593Ssam		return;
457147437Sume	if (sa->sa_family != AF_INET6)
458147437Sume		return;
459147437Sume	in6_fillscopeid(&in6_ifr.ifr_addr);
460146187Sume	if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0,
461146187Sume	    NI_NUMERICHOST) != 0)
462138593Ssam		src[0] = '\0';
463138593Ssam
464138593Ssam	if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0)
465138593Ssam		return;
466147437Sume	if (sa->sa_family != AF_INET6)
467147437Sume		return;
468147437Sume	in6_fillscopeid(&in6_ifr.ifr_addr);
469146187Sume	if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0,
470146187Sume	    NI_NUMERICHOST) != 0)
471138593Ssam		dst[0] = '\0';
472138593Ssam
473138593Ssam	printf("\ttunnel inet6 %s --> %s\n", src, dst);
474138593Ssam}
475138593Ssam
476138593Ssamstatic void
477138593Ssamin6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres)
478138593Ssam{
479138593Ssam	struct in6_aliasreq in6_addreq;
480138593Ssam
481138593Ssam	memset(&in6_addreq, 0, sizeof(in6_addreq));
482138593Ssam	strncpy(in6_addreq.ifra_name, name, IFNAMSIZ);
483138593Ssam	memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len);
484138593Ssam	memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr,
485138593Ssam	    dstres->ai_addr->sa_len);
486138593Ssam
487138593Ssam	if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0)
488138593Ssam		warn("SIOCSIFPHYADDR_IN6");
489138593Ssam}
490138593Ssam
491138593Ssamstatic struct cmd inet6_cmds[] = {
492138593Ssam	DEF_CMD_ARG("prefixlen",			setifprefixlen),
493138593Ssam	DEF_CMD("anycast",	IN6_IFF_ANYCAST,	setip6flags),
494138593Ssam	DEF_CMD("tentative",	IN6_IFF_TENTATIVE,	setip6flags),
495138593Ssam	DEF_CMD("-tentative",	-IN6_IFF_TENTATIVE,	setip6flags),
496138593Ssam	DEF_CMD("deprecated",	IN6_IFF_DEPRECATED,	setip6flags),
497138593Ssam	DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED,	setip6flags),
498138593Ssam	DEF_CMD("autoconf",	IN6_IFF_AUTOCONF,	setip6flags),
499138593Ssam	DEF_CMD("-autoconf",	-IN6_IFF_AUTOCONF,	setip6flags),
500138593Ssam	DEF_CMD_ARG("pltime",        			setip6pltime),
501138593Ssam	DEF_CMD_ARG("vltime",        			setip6vltime),
502138593Ssam	DEF_CMD("eui64",	0,			setip6eui64),
503138593Ssam};
504138593Ssam
505138593Ssamstatic struct afswtch af_inet6 = {
506138593Ssam	.af_name	= "inet6",
507138593Ssam	.af_af		= AF_INET6,
508138593Ssam	.af_status	= in6_status,
509138593Ssam	.af_getaddr	= in6_getaddr,
510138593Ssam	.af_getprefix	= in6_getprefix,
511138593Ssam	.af_postproc	= in6_postproc,
512138593Ssam	.af_status_tunnel = in6_status_tunnel,
513138593Ssam	.af_settunnel	= in6_set_tunnel,
514138593Ssam	.af_difaddr	= SIOCDIFADDR_IN6,
515138593Ssam	.af_aifaddr	= SIOCAIFADDR_IN6,
516166446Sbms	.af_ridreq	= &in6_addreq,
517138593Ssam	.af_addreq	= &in6_addreq,
518138593Ssam};
519138593Ssam
520138593Ssamstatic void
521138593Ssamin6_Lopt_cb(const char *optarg __unused)
522138593Ssam{
523138593Ssam	ip6lifetime++;	/* print IPv6 address lifetime */
524138593Ssam}
525138593Ssamstatic struct option in6_Lopt = { "L", "[-L]", in6_Lopt_cb };
526138593Ssam
527138593Ssamstatic __constructor void
528138593Ssaminet6_ctor(void)
529138593Ssam{
530138593Ssam#define	N(a)	(sizeof(a) / sizeof(a[0]))
531138593Ssam	int i;
532138593Ssam
533138593Ssam	for (i = 0; i < N(inet6_cmds);  i++)
534138593Ssam		cmd_register(&inet6_cmds[i]);
535138593Ssam	af_register(&af_inet6);
536138593Ssam	opt_register(&in6_Lopt);
537138593Ssam#undef N
538138593Ssam}
539