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