1/*	$NetBSD: af_inet6.c,v 1.26 2010/01/22 23:50:07 dyoung Exp $	*/
2
3/*
4 * Copyright (c) 1983, 1993
5 *      The Regents of the University of California.  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 University 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 REGENTS 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 REGENTS 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
32#include <sys/cdefs.h>
33#ifndef lint
34__RCSID("$NetBSD: af_inet6.c,v 1.26 2010/01/22 23:50:07 dyoung Exp $");
35#endif /* not lint */
36
37#include <sys/param.h>
38#include <sys/ioctl.h>
39#include <sys/socket.h>
40
41#include <net/if.h>
42#include <netinet/in.h>
43#include <netinet/in_var.h>
44#include <netinet6/nd6.h>
45
46#include <err.h>
47#include <errno.h>
48#include <ifaddrs.h>
49#include <netdb.h>
50#include <string.h>
51#include <stdlib.h>
52#include <stdio.h>
53#include <util.h>
54
55#include "env.h"
56#include "extern.h"
57#include "parse.h"
58#include "extern.h"
59#include "af_inetany.h"
60#include "prog_ops.h"
61
62static void in6_constructor(void) __attribute__((constructor));
63static void in6_alias(const char *, prop_dictionary_t, prop_dictionary_t,
64    struct in6_ifreq *);
65static void in6_commit_address(prop_dictionary_t, prop_dictionary_t);
66
67static int setia6eui64_impl(prop_dictionary_t, struct in6_aliasreq *);
68static int setia6flags_impl(prop_dictionary_t, struct in6_aliasreq *);
69static int setia6pltime_impl(prop_dictionary_t, struct in6_aliasreq *);
70static int setia6vltime_impl(prop_dictionary_t, struct in6_aliasreq *);
71
72static int setia6lifetime(prop_dictionary_t, int64_t, time_t *, uint32_t *);
73
74static void in6_delscopeid(struct sockaddr_in6 *sin6);
75static void in6_status(prop_dictionary_t, prop_dictionary_t, bool);
76
77static struct usage_func usage;
78static cmdloop_branch_t branch[2];
79
80static const struct kwinst ia6flagskw[] = {
81	  IFKW("anycast",	IN6_IFF_ANYCAST)
82	, IFKW("tentative",	IN6_IFF_TENTATIVE)
83	, IFKW("deprecated",	IN6_IFF_DEPRECATED)
84};
85
86static struct pinteger parse_pltime = PINTEGER_INITIALIZER(&parse_pltime,
87    "pltime", 0, NULL, "pltime", &command_root.pb_parser);
88
89static struct pinteger parse_vltime = PINTEGER_INITIALIZER(&parse_vltime,
90    "vltime", 0, NULL, "vltime", &command_root.pb_parser);
91
92static const struct kwinst inet6kw[] = {
93	  {.k_word = "pltime", .k_nextparser = &parse_pltime.pi_parser}
94	, {.k_word = "vltime", .k_nextparser = &parse_vltime.pi_parser}
95	, {.k_word = "eui64", .k_key = "eui64", .k_type = KW_T_BOOL,
96	   .k_bool = true, .k_nextparser = &command_root.pb_parser}
97};
98
99struct pkw ia6flags = PKW_INITIALIZER(&ia6flags, "ia6flags", NULL,
100    "ia6flag", ia6flagskw, __arraycount(ia6flagskw), &command_root.pb_parser);
101struct pkw inet6 = PKW_INITIALIZER(&inet6, "IPv6 keywords", NULL,
102    NULL, inet6kw, __arraycount(inet6kw), NULL);
103
104static struct afswtch in6af = {
105	.af_name = "inet6", .af_af = AF_INET6, .af_status = in6_status,
106	.af_addr_commit = in6_commit_address
107};
108
109static int
110prefix(void *val, int size)
111{
112	u_char *pname = (u_char *)val;
113	int byte, bit, plen = 0;
114
115	for (byte = 0; byte < size; byte++, plen += 8)
116		if (pname[byte] != 0xff)
117			break;
118	if (byte == size)
119		return (plen);
120	for (bit = 7; bit != 0; bit--, plen++)
121		if (!(pname[byte] & (1 << bit)))
122			break;
123	for (; bit != 0; bit--)
124		if (pname[byte] & (1 << bit))
125			return(0);
126	byte++;
127	for (; byte < size; byte++)
128		if (pname[byte])
129			return(0);
130	return (plen);
131}
132
133int
134setia6flags_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
135{
136	int64_t ia6flag;
137
138	if (!prop_dictionary_get_int64(env, "ia6flag", &ia6flag)) {
139		errno = ENOENT;
140		return -1;
141	}
142
143	if (ia6flag < 0) {
144		ia6flag = -ia6flag;
145		ifra->ifra_flags &= ~ia6flag;
146	} else
147		ifra->ifra_flags |= ia6flag;
148	return 0;
149}
150
151int
152setia6pltime_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
153{
154	int64_t pltime;
155
156	if (!prop_dictionary_get_int64(env, "pltime", &pltime)) {
157		errno = ENOENT;
158		return -1;
159	}
160
161	return setia6lifetime(env, pltime,
162	    &ifra->ifra_lifetime.ia6t_preferred,
163	    &ifra->ifra_lifetime.ia6t_pltime);
164}
165
166int
167setia6vltime_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
168{
169	int64_t vltime;
170
171	if (!prop_dictionary_get_int64(env, "vltime", &vltime)) {
172		errno = ENOENT;
173		return -1;
174	}
175
176	return setia6lifetime(env, vltime,
177		&ifra->ifra_lifetime.ia6t_expire,
178		&ifra->ifra_lifetime.ia6t_vltime);
179}
180
181static int
182setia6lifetime(prop_dictionary_t env, int64_t val, time_t *timep,
183    uint32_t *ivalp)
184{
185	time_t t;
186	int af;
187
188	if ((af = getaf(env)) == -1 || af != AF_INET6) {
189		errx(EXIT_FAILURE,
190		    "inet6 address lifetime not allowed for the AF");
191	}
192
193	t = time(NULL);
194	*timep = t + val;
195	*ivalp = val;
196	return 0;
197}
198
199int
200setia6eui64_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
201{
202	char buf[2][80];
203	struct ifaddrs *ifap, *ifa;
204	const struct sockaddr_in6 *sin6 = NULL;
205	const struct in6_addr *lladdr = NULL;
206	struct in6_addr *in6;
207	const char *ifname;
208	bool doit = false;
209	int af;
210
211	if (!prop_dictionary_get_bool(env, "eui64", &doit) || !doit) {
212		errno = ENOENT;
213		return -1;
214	}
215
216	if ((ifname = getifname(env)) == NULL)
217		return -1;
218
219	af = getaf(env);
220	if (af != AF_INET6) {
221		errx(EXIT_FAILURE,
222		    "eui64 address modifier not allowed for the AF");
223	}
224 	in6 = &ifra->ifra_addr.sin6_addr;
225	if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) {
226		union {
227			struct sockaddr_in6 sin6;
228			struct sockaddr sa;
229		} any = {.sin6 = {.sin6_family = AF_INET6}};
230		memcpy(&any.sin6.sin6_addr, &in6addr_any,
231		    sizeof(any.sin6.sin6_addr));
232		(void)sockaddr_snprintf(buf[0], sizeof(buf[0]), "%a%%S",
233		    &any.sa);
234		(void)sockaddr_snprintf(buf[1], sizeof(buf[1]), "%a%%S",
235		    (const struct sockaddr *)&ifra->ifra_addr);
236		errx(EXIT_FAILURE, "interface index is already filled, %s | %s",
237		    buf[0], buf[1]);
238	}
239	if (getifaddrs(&ifap) != 0)
240		err(EXIT_FAILURE, "getifaddrs");
241	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
242		if (ifa->ifa_addr->sa_family == AF_INET6 &&
243		    strcmp(ifa->ifa_name, ifname) == 0) {
244			sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
245			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
246				lladdr = &sin6->sin6_addr;
247				break;
248			}
249		}
250	}
251	if (lladdr == NULL)
252		errx(EXIT_FAILURE, "could not determine link local address");
253
254 	memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
255
256	freeifaddrs(ifap);
257	return 0;
258}
259
260/* KAME idiosyncrasy */
261static void
262in6_delscopeid(struct sockaddr_in6 *sin6)
263{
264	if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
265	    sin6->sin6_scope_id == 0)
266		return;
267
268	*(u_int16_t *)&sin6->sin6_addr.s6_addr[2] = htons(sin6->sin6_scope_id);
269	sin6->sin6_scope_id = 0;
270}
271
272/* XXX not really an alias */
273void
274in6_alias(const char *ifname, prop_dictionary_t env, prop_dictionary_t oenv,
275    struct in6_ifreq *creq)
276{
277	struct in6_ifreq ifr6;
278	struct sockaddr_in6 *sin6;
279	char hbuf[NI_MAXHOST];
280	u_int32_t scopeid;
281	int s;
282	const int niflag = Nflag ? 0 : NI_NUMERICHOST;
283	unsigned short flags;
284
285	/* Get the non-alias address for this interface. */
286	if ((s = getsock(AF_INET6)) == -1) {
287		if (errno == EAFNOSUPPORT)
288			return;
289		err(EXIT_FAILURE, "socket");
290	}
291
292	sin6 = &creq->ifr_addr;
293
294	in6_fillscopeid(sin6);
295	scopeid = sin6->sin6_scope_id;
296	if (getnameinfo((const struct sockaddr *)sin6, sin6->sin6_len,
297			hbuf, sizeof(hbuf), NULL, 0, niflag))
298		strlcpy(hbuf, "", sizeof(hbuf));	/* some message? */
299	printf("\tinet6 %s", hbuf);
300
301	if (getifflags(env, oenv, &flags) == -1)
302		err(EXIT_FAILURE, "%s: getifflags", __func__);
303
304	if (flags & IFF_POINTOPOINT) {
305		ifr6 = *creq;
306		if (prog_ioctl(s, SIOCGIFDSTADDR_IN6, &ifr6) == -1) {
307			if (errno != EADDRNOTAVAIL)
308				warn("SIOCGIFDSTADDR_IN6");
309			memset(&ifr6.ifr_addr, 0, sizeof(ifr6.ifr_addr));
310			ifr6.ifr_addr.sin6_family = AF_INET6;
311			ifr6.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
312		}
313		sin6 = &ifr6.ifr_addr;
314		in6_fillscopeid(sin6);
315		hbuf[0] = '\0';
316		if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
317				hbuf, sizeof(hbuf), NULL, 0, niflag))
318			strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
319		printf(" -> %s", hbuf);
320	}
321
322	ifr6 = *creq;
323	if (prog_ioctl(s, SIOCGIFNETMASK_IN6, &ifr6) == -1) {
324		if (errno != EADDRNOTAVAIL)
325			warn("SIOCGIFNETMASK_IN6");
326	} else {
327		sin6 = &ifr6.ifr_addr;
328		printf(" prefixlen %d", prefix(&sin6->sin6_addr,
329					       sizeof(struct in6_addr)));
330	}
331
332	ifr6 = *creq;
333	if (prog_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == -1) {
334		if (errno != EADDRNOTAVAIL)
335			warn("SIOCGIFAFLAG_IN6");
336	} else {
337		if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST)
338			printf(" anycast");
339		if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
340			printf(" tentative");
341		if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED)
342			printf(" duplicated");
343		if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED)
344			printf(" detached");
345		if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
346			printf(" deprecated");
347	}
348
349	if (scopeid)
350		printf(" scopeid 0x%x", scopeid);
351
352	if (get_flag('L')) {
353		struct in6_addrlifetime *lifetime;
354		ifr6 = *creq;
355		lifetime = &ifr6.ifr_ifru.ifru_lifetime;
356		if (prog_ioctl(s, SIOCGIFALIFETIME_IN6, &ifr6) == -1) {
357			if (errno != EADDRNOTAVAIL)
358				warn("SIOCGIFALIFETIME_IN6");
359		} else if (lifetime->ia6t_preferred || lifetime->ia6t_expire) {
360			time_t t = time(NULL);
361			printf(" pltime ");
362			if (lifetime->ia6t_preferred) {
363				printf("%lu",
364				    (unsigned long)(lifetime->ia6t_preferred -
365				        MIN(t, lifetime->ia6t_preferred)));
366			} else
367				printf("infty");
368
369			printf(" vltime ");
370			if (lifetime->ia6t_expire) {
371				printf("%lu",
372				    (unsigned long)(lifetime->ia6t_expire -
373				        MIN(t, lifetime->ia6t_expire)));
374			} else
375				printf("infty");
376		}
377	}
378}
379
380static void
381in6_status(prop_dictionary_t env, prop_dictionary_t oenv, bool force)
382{
383	struct ifaddrs *ifap, *ifa;
384	struct in6_ifreq ifr;
385	const char *ifname;
386	bool printprefs = false;
387
388	if ((ifname = getifname(env)) == NULL)
389		err(EXIT_FAILURE, "%s: getifname", __func__);
390
391	if (getifaddrs(&ifap) != 0)
392		err(EXIT_FAILURE, "getifaddrs");
393	printprefs = ifa_any_preferences(ifname, ifap, AF_INET6);
394	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
395		if (strcmp(ifname, ifa->ifa_name) != 0)
396			continue;
397		if (ifa->ifa_addr->sa_family != AF_INET6)
398			continue;
399		if (sizeof(ifr.ifr_addr) < ifa->ifa_addr->sa_len)
400			continue;
401
402		memset(&ifr, 0, sizeof(ifr));
403		estrlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
404		memcpy(&ifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len);
405		in6_alias(ifname, env, oenv, &ifr);
406		if (printprefs)
407			ifa_print_preference(ifa->ifa_name, ifa->ifa_addr);
408		printf("\n");
409	}
410	freeifaddrs(ifap);
411}
412
413static int
414in6_pre_aifaddr(prop_dictionary_t env, const struct afparam *param)
415{
416	struct in6_aliasreq *ifra = param->req.buf;
417
418	setia6eui64_impl(env, ifra);
419	setia6vltime_impl(env, ifra);
420	setia6pltime_impl(env, ifra);
421	setia6flags_impl(env, ifra);
422	in6_delscopeid(&ifra->ifra_addr);
423	in6_delscopeid(&ifra->ifra_dstaddr);
424
425	return 0;
426}
427
428static void
429in6_commit_address(prop_dictionary_t env, prop_dictionary_t oenv)
430{
431	struct in6_ifreq in6_ifr = {
432		.ifr_addr = {
433			.sin6_family = AF_INET6,
434			.sin6_len = sizeof(in6_ifr.ifr_addr),
435			.sin6_addr = {
436				.s6_addr =
437				    {0xff, 0xff, 0xff, 0xff,
438				     0xff, 0xff, 0xff, 0xff}
439			}
440		}
441	};
442	static struct sockaddr_in6 in6_defmask = {
443		.sin6_family = AF_INET6,
444		.sin6_len = sizeof(in6_defmask),
445		.sin6_addr = {
446			.s6_addr = {0xff, 0xff, 0xff, 0xff,
447			            0xff, 0xff, 0xff, 0xff}
448		}
449	};
450
451	struct in6_aliasreq in6_ifra = {
452		.ifra_prefixmask = {
453			.sin6_family = AF_INET6,
454			.sin6_len = sizeof(in6_ifra.ifra_prefixmask),
455			.sin6_addr = {
456				.s6_addr =
457				    {0xff, 0xff, 0xff, 0xff,
458				     0xff, 0xff, 0xff, 0xff}}},
459		.ifra_lifetime = {
460			  .ia6t_pltime = ND6_INFINITE_LIFETIME
461			, .ia6t_vltime = ND6_INFINITE_LIFETIME
462		}
463	};
464	struct afparam in6param = {
465		  .req = BUFPARAM(in6_ifra)
466		, .dgreq = BUFPARAM(in6_ifr)
467		, .name = {
468			{.buf = in6_ifr.ifr_name,
469			 .buflen = sizeof(in6_ifr.ifr_name)},
470			{.buf = in6_ifra.ifra_name,
471			 .buflen = sizeof(in6_ifra.ifra_name)}
472		  }
473		, .dgaddr = BUFPARAM(in6_ifr.ifr_addr)
474		, .addr = BUFPARAM(in6_ifra.ifra_addr)
475		, .dst = BUFPARAM(in6_ifra.ifra_dstaddr)
476		, .brd = BUFPARAM(in6_ifra.ifra_broadaddr)
477		, .mask = BUFPARAM(in6_ifra.ifra_prefixmask)
478		, .aifaddr = IFADDR_PARAM(SIOCAIFADDR_IN6)
479		, .difaddr = IFADDR_PARAM(SIOCDIFADDR_IN6)
480		, .gifaddr = IFADDR_PARAM(SIOCGIFADDR_IN6)
481		, .defmask = BUFPARAM(in6_defmask)
482		, .pre_aifaddr = in6_pre_aifaddr
483	};
484	commit_address(env, oenv, &in6param);
485}
486
487static void
488in6_usage(prop_dictionary_t env)
489{
490	fprintf(stderr,
491	    "\t[ anycast | -anycast ] [ deprecated | -deprecated ]\n"
492	    "\t[ tentative | -tentative ] [ pltime n ] [ vltime n ] "
493	    "[ eui64 ]\n");
494}
495
496static void
497in6_constructor(void)
498{
499	if (register_flag('L') != 0)
500		err(EXIT_FAILURE, __func__);
501	register_family(&in6af);
502	usage_func_init(&usage, in6_usage);
503	register_usage(&usage);
504	cmdloop_branch_init(&branch[0], &ia6flags.pk_parser);
505	cmdloop_branch_init(&branch[1], &inet6.pk_parser);
506	register_cmdloop_branch(&branch[0]);
507	register_cmdloop_branch(&branch[1]);
508}
509