af_inet6.c revision 1.13 1 /* $NetBSD: af_inet6.c,v 1.13 2008/05/08 07:13:20 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.13 2008/05/08 07:13:20 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 "parse.h"
57 #include "extern.h"
58 #include "af_inet6.h"
59 #include "af_inetany.h"
60
61 struct in6_ifreq in6_ridreq = {
62 .ifr_addr = {
63 .sin6_family = AF_INET6,
64 .sin6_addr = {
65 .s6_addr =
66 {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
67 }
68 }
69 };
70
71 struct in6_aliasreq in6_addreq = {
72 .ifra_prefixmask = {
73 .sin6_len = sizeof(in6_addreq.ifra_prefixmask),
74 .sin6_addr = {
75 .s6_addr =
76 {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}},
77 .ifra_lifetime = {
78 .ia6t_pltime = ND6_INFINITE_LIFETIME
79 , .ia6t_vltime = ND6_INFINITE_LIFETIME
80 }
81 };
82
83 static const struct kwinst ia6flagskw[] = {
84 IFKW("anycast", IN6_IFF_ANYCAST)
85 , IFKW("tentative", IN6_IFF_TENTATIVE)
86 , IFKW("deprecated", IN6_IFF_DEPRECATED)
87 };
88
89 static struct pinteger parse_pltime = PINTEGER_INITIALIZER(&parse_pltime,
90 "pltime", 0, setia6pltime, "pltime", &command_root.pb_parser);
91
92 static struct pinteger parse_vltime = PINTEGER_INITIALIZER(&parse_vltime,
93 "vltime", 0, setia6vltime, "vltime", &command_root.pb_parser);
94
95 static const struct kwinst inet6kw[] = {
96 {.k_word = "pltime", .k_nextparser = &parse_pltime.pi_parser}
97 , {.k_word = "vltime", .k_nextparser = &parse_vltime.pi_parser}
98 , {.k_word = "eui64", .k_exec = setia6eui64,
99 .k_nextparser = &command_root.pb_parser}
100 };
101
102 struct pkw ia6flags = PKW_INITIALIZER(&ia6flags, "ia6flags", setia6flags,
103 "ia6flag", ia6flagskw, __arraycount(ia6flagskw), &command_root.pb_parser);
104 struct pkw inet6 = PKW_INITIALIZER(&inet6, "IPv6 keywords", NULL,
105 NULL, inet6kw, __arraycount(inet6kw), NULL);
106
107 static int setia6lifetime(prop_dictionary_t, int64_t, time_t *, uint32_t *);
108 static void in6_alias(const char *, prop_dictionary_t, prop_dictionary_t,
109 struct in6_ifreq *);
110
111 static char *
112 sec2str(time_t total)
113 {
114 static char result[256];
115 int days, hours, mins, secs;
116 int first = 1;
117 char *p = result;
118 char *end = &result[sizeof(result)];
119 int n;
120
121 if (0) { /*XXX*/
122 days = total / 3600 / 24;
123 hours = (total / 3600) % 24;
124 mins = (total / 60) % 60;
125 secs = total % 60;
126
127 if (days) {
128 first = 0;
129 n = snprintf(p, end - p, "%dd", days);
130 if (n < 0 || n >= end - p)
131 return(result);
132 p += n;
133 }
134 if (!first || hours) {
135 first = 0;
136 n = snprintf(p, end - p, "%dh", hours);
137 if (n < 0 || n >= end - p)
138 return(result);
139 p += n;
140 }
141 if (!first || mins) {
142 first = 0;
143 n = snprintf(p, end - p, "%dm", mins);
144 if (n < 0 || n >= end - p)
145 return(result);
146 p += n;
147 }
148 snprintf(p, end - p, "%ds", secs);
149 } else
150 snprintf(p, end - p, "%lu", (u_long)total);
151
152 return(result);
153 }
154
155 static int
156 prefix(void *val, int size)
157 {
158 u_char *pname = (u_char *)val;
159 int byte, bit, plen = 0;
160
161 for (byte = 0; byte < size; byte++, plen += 8)
162 if (pname[byte] != 0xff)
163 break;
164 if (byte == size)
165 return (plen);
166 for (bit = 7; bit != 0; bit--, plen++)
167 if (!(pname[byte] & (1 << bit)))
168 break;
169 for (; bit != 0; bit--)
170 if (pname[byte] & (1 << bit))
171 return(0);
172 byte++;
173 for (; byte < size; byte++)
174 if (pname[byte])
175 return(0);
176 return (plen);
177 }
178
179 int
180 setia6flags_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
181 {
182 int64_t ia6flag;
183
184 if (!prop_dictionary_get_int64(env, "ia6flag", &ia6flag)) {
185 errno = ENOENT;
186 return -1;
187 }
188
189 if (ia6flag < 0) {
190 ia6flag = -ia6flag;
191 ifra->ifra_flags &= ~ia6flag;
192 } else
193 ifra->ifra_flags |= ia6flag;
194 return 0;
195 }
196
197 int
198 setia6flags(prop_dictionary_t env, prop_dictionary_t xenv)
199 {
200 return setia6flags_impl(env, &in6_addreq);
201 }
202
203 int
204 setia6pltime_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
205 {
206 int64_t pltime;
207
208 if (!prop_dictionary_get_int64(env, "pltime", &pltime)) {
209 errno = ENOENT;
210 return -1;
211 }
212
213 return setia6lifetime(env, pltime,
214 &ifra->ifra_lifetime.ia6t_preferred,
215 &ifra->ifra_lifetime.ia6t_pltime);
216 }
217
218 int
219 setia6pltime(prop_dictionary_t env, prop_dictionary_t xenv)
220 {
221 return setia6pltime_impl(env, &in6_addreq);
222 }
223
224 int
225 setia6vltime_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
226 {
227 int64_t vltime;
228
229 if (!prop_dictionary_get_int64(env, "vltime", &vltime)) {
230 errno = ENOENT;
231 return -1;
232 }
233
234 return setia6lifetime(env, vltime,
235 &ifra->ifra_lifetime.ia6t_expire,
236 &ifra->ifra_lifetime.ia6t_vltime);
237 }
238
239 int
240 setia6vltime(prop_dictionary_t env, prop_dictionary_t xenv)
241 {
242 return setia6vltime_impl(env, &in6_addreq);
243 }
244
245 static int
246 setia6lifetime(prop_dictionary_t env, int64_t val, time_t *timep,
247 uint32_t *ivalp)
248 {
249 time_t t;
250 int af;
251
252 if ((af = getaf(env)) == -1 || af != AF_INET6) {
253 errx(EXIT_FAILURE,
254 "inet6 address lifetime not allowed for the AF");
255 }
256
257 t = time(NULL);
258 *timep = t + val;
259 *ivalp = val;
260 return 0;
261 }
262
263 int
264 setia6eui64_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
265 {
266 struct ifaddrs *ifap, *ifa;
267 const struct sockaddr_in6 *sin6 = NULL;
268 const struct in6_addr *lladdr = NULL;
269 struct in6_addr *in6;
270 const char *ifname;
271 int af;
272
273 if ((ifname = getifname(env)) == NULL)
274 return -1;
275
276 af = getaf(env);
277 if (af != AF_INET6) {
278 errx(EXIT_FAILURE,
279 "eui64 address modifier not allowed for the AF");
280 }
281 in6 = &ifra->ifra_addr.sin6_addr;
282 if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
283 errx(EXIT_FAILURE, "interface index is already filled");
284 if (getifaddrs(&ifap) != 0)
285 err(EXIT_FAILURE, "getifaddrs");
286 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
287 if (ifa->ifa_addr->sa_family == AF_INET6 &&
288 strcmp(ifa->ifa_name, ifname) == 0) {
289 sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
290 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
291 lladdr = &sin6->sin6_addr;
292 break;
293 }
294 }
295 }
296 if (!lladdr)
297 errx(EXIT_FAILURE, "could not determine link local address");
298
299 memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
300
301 freeifaddrs(ifap);
302 return 0;
303 }
304
305 int
306 setia6eui64(prop_dictionary_t env, prop_dictionary_t xenv)
307 {
308 return setia6eui64_impl(env, &in6_addreq);
309 }
310
311 void
312 in6_fillscopeid(struct sockaddr_in6 *sin6)
313 {
314 /* KAME idiosyncrasy */
315 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
316 sin6->sin6_scope_id =
317 ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
318 sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
319 }
320 }
321
322 /* XXX not really an alias */
323 void
324 in6_alias(const char *ifname, prop_dictionary_t env, prop_dictionary_t oenv,
325 struct in6_ifreq *creq)
326 {
327 struct in6_ifreq ifr6;
328 struct sockaddr_in6 *sin6;
329 char hbuf[NI_MAXHOST];
330 u_int32_t scopeid;
331 int s;
332 const int niflag = NI_NUMERICHOST;
333 unsigned short flags;
334
335 /* Get the non-alias address for this interface. */
336 if ((s = getsock(AF_INET6)) == -1) {
337 if (errno == EAFNOSUPPORT)
338 return;
339 err(EXIT_FAILURE, "socket");
340 }
341
342 sin6 = (struct sockaddr_in6 *)&creq->ifr_addr;
343
344 in6_fillscopeid(sin6);
345 scopeid = sin6->sin6_scope_id;
346 if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
347 hbuf, sizeof(hbuf), NULL, 0, niflag))
348 strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
349 printf("\tinet6 %s", hbuf);
350
351 if (getifflags(env, oenv, &flags) == -1)
352 err(EXIT_FAILURE, "%s: getifflags", __func__);
353
354 if (flags & IFF_POINTOPOINT) {
355 memset(&ifr6, 0, sizeof(ifr6));
356 estrlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
357 ifr6.ifr_addr = creq->ifr_addr;
358 if (ioctl(s, SIOCGIFDSTADDR_IN6, &ifr6) == -1) {
359 if (errno != EADDRNOTAVAIL)
360 warn("SIOCGIFDSTADDR_IN6");
361 memset(&ifr6.ifr_addr, 0, sizeof(ifr6.ifr_addr));
362 ifr6.ifr_addr.sin6_family = AF_INET6;
363 ifr6.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
364 }
365 sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
366 in6_fillscopeid(sin6);
367 hbuf[0] = '\0';
368 if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
369 hbuf, sizeof(hbuf), NULL, 0, niflag))
370 strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
371 printf(" -> %s", hbuf);
372 }
373
374 memset(&ifr6, 0, sizeof(ifr6));
375 estrlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
376 ifr6.ifr_addr = creq->ifr_addr;
377 if (ioctl(s, SIOCGIFNETMASK_IN6, &ifr6) == -1) {
378 if (errno != EADDRNOTAVAIL)
379 warn("SIOCGIFNETMASK_IN6");
380 } else {
381 sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
382 printf(" prefixlen %d", prefix(&sin6->sin6_addr,
383 sizeof(struct in6_addr)));
384 }
385
386 memset(&ifr6, 0, sizeof(ifr6));
387 estrlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
388 ifr6.ifr_addr = creq->ifr_addr;
389 if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == -1) {
390 if (errno != EADDRNOTAVAIL)
391 warn("SIOCGIFAFLAG_IN6");
392 } else {
393 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST)
394 printf(" anycast");
395 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
396 printf(" tentative");
397 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED)
398 printf(" duplicated");
399 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED)
400 printf(" detached");
401 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
402 printf(" deprecated");
403 }
404
405 if (scopeid)
406 printf(" scopeid 0x%x", scopeid);
407
408 if (Lflag) {
409 struct in6_addrlifetime *lifetime;
410 memset(&ifr6, 0, sizeof(ifr6));
411 estrlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
412 ifr6.ifr_addr = creq->ifr_addr;
413 lifetime = &ifr6.ifr_ifru.ifru_lifetime;
414 if (ioctl(s, SIOCGIFALIFETIME_IN6, &ifr6) == -1) {
415 if (errno != EADDRNOTAVAIL)
416 warn("SIOCGIFALIFETIME_IN6");
417 } else if (lifetime->ia6t_preferred || lifetime->ia6t_expire) {
418 time_t t = time(NULL);
419 printf(" pltime ");
420 if (lifetime->ia6t_preferred) {
421 printf("%s", lifetime->ia6t_preferred < t
422 ? "0"
423 : sec2str(lifetime->ia6t_preferred - t));
424 } else
425 printf("infty");
426
427 printf(" vltime ");
428 if (lifetime->ia6t_expire) {
429 printf("%s", lifetime->ia6t_expire < t
430 ? "0"
431 : sec2str(lifetime->ia6t_expire - t));
432 } else
433 printf("infty");
434 }
435 }
436
437 printf("\n");
438 }
439
440 void
441 in6_status(prop_dictionary_t env, prop_dictionary_t oenv, bool force)
442 {
443 struct ifaddrs *ifap, *ifa;
444 struct in6_ifreq ifr;
445 const char *ifname;
446
447 if ((ifname = getifname(env)) == NULL)
448 err(EXIT_FAILURE, "%s: getifname", __func__);
449
450 if (getifaddrs(&ifap) != 0)
451 err(EXIT_FAILURE, "getifaddrs");
452 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
453 if (strcmp(ifname, ifa->ifa_name) != 0)
454 continue;
455 if (ifa->ifa_addr->sa_family != AF_INET6)
456 continue;
457 if (sizeof(ifr.ifr_addr) < ifa->ifa_addr->sa_len)
458 continue;
459
460 memset(&ifr, 0, sizeof(ifr));
461 estrlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
462 memcpy(&ifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len);
463 in6_alias(ifname, env, oenv, &ifr);
464 }
465 freeifaddrs(ifap);
466 }
467
468 #define SIN6(x) ((struct sockaddr_in6 *) &(x))
469 struct sockaddr_in6 *sin6tab[] = {
470 SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
471 SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)};
472
473 void
474 in6_getaddr(const struct paddr_prefix *pfx, int which)
475 {
476 struct sockaddr_in6 *sin6 = sin6tab[which];
477
478 if (pfx->pfx_addr.sa_family != AF_INET6)
479 errx(EXIT_FAILURE, "%s: address family mismatch", __func__);
480
481 if (which == ADDR && pfx->pfx_len >= 0)
482 in6_getprefix(pfx->pfx_len, MASK);
483
484 memcpy(sin6, &pfx->pfx_addr, MIN(sizeof(*sin6), pfx->pfx_addr.sa_len));
485
486 /* KAME idiosyncrasy */
487 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) && sin6->sin6_scope_id) {
488 *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] =
489 htons(sin6->sin6_scope_id);
490 sin6->sin6_scope_id = 0;
491 }
492 }
493
494 void
495 in6_getprefix(int len, int which)
496 {
497 struct sockaddr_in6 *gpsin = sin6tab[which];
498 u_char *cp;
499
500 if (len < 0 || len > 128)
501 errx(EXIT_FAILURE, "%d: bad value", len);
502 gpsin->sin6_len = sizeof(*gpsin);
503 if (which != MASK)
504 gpsin->sin6_family = AF_INET6;
505 if (len == 0 || len == 128) {
506 memset(&gpsin->sin6_addr, 0xff, sizeof(struct in6_addr));
507 return;
508 }
509 memset((void *)&gpsin->sin6_addr, 0x00, sizeof(gpsin->sin6_addr));
510 for (cp = (u_char *)&gpsin->sin6_addr; len > 7; len -= 8)
511 *cp++ = 0xff;
512 if (len)
513 *cp = 0xff << (8 - len);
514 }
515
516 static int
517 in6_pre_aifaddr(prop_dictionary_t env, void *arg)
518 {
519 struct in6_aliasreq *ifra = arg;
520
521 setia6eui64_impl(env, ifra);
522 setia6vltime_impl(env, ifra);
523 setia6pltime_impl(env, ifra);
524 setia6flags_impl(env, ifra);
525
526 return 0;
527 }
528
529 void
530 in6_commit_address(prop_dictionary_t env, prop_dictionary_t oenv)
531 {
532 struct in6_ifreq in6_ifr;
533 #if 0
534 = {
535 .ifr_addr = {
536 .sin6_family = AF_INET6,
537 .sin6_addr = {
538 .s6_addr =
539 {0xff, 0xff, 0xff, 0xff,
540 0xff, 0xff, 0xff, 0xff}
541 }
542 }
543 };
544 #endif
545 static struct sockaddr_in6 in6_defmask = {
546 .sin6_addr = {
547 .s6_addr = {0xff, 0xff, 0xff, 0xff,
548 0xff, 0xff, 0xff, 0xff}
549 }
550 };
551
552 struct in6_aliasreq in6_ifra;
553 #if 0
554 = {
555 .ifra_prefixmask = {
556 .sin6_addr = {
557 .s6_addr =
558 {0xff, 0xff, 0xff, 0xff,
559 0xff, 0xff, 0xff, 0xff}}},
560 .ifra_lifetime = {
561 .ia6t_pltime = ND6_INFINITE_LIFETIME
562 , .ia6t_vltime = ND6_INFINITE_LIFETIME
563 }
564 };
565 #endif
566 struct afparam in6param = {
567 .req = BUFPARAM(in6_ifra)
568 , .dgreq = BUFPARAM(in6_ifr)
569 , .name = {
570 {.buf = in6_ifr.ifr_name,
571 .buflen = sizeof(in6_ifr.ifr_name)},
572 {.buf = in6_ifra.ifra_name,
573 .buflen = sizeof(in6_ifra.ifra_name)}
574 }
575 , .dgaddr = BUFPARAM(in6_ifr.ifr_addr)
576 , .addr = BUFPARAM(in6_ifra.ifra_addr)
577 , .dst = BUFPARAM(in6_ifra.ifra_dstaddr)
578 , .brd = BUFPARAM(in6_ifra.ifra_broadaddr)
579 , .mask = BUFPARAM(in6_ifra.ifra_prefixmask)
580 , .aifaddr = IFADDR_PARAM(SIOCAIFADDR_IN6)
581 , .difaddr = IFADDR_PARAM(SIOCDIFADDR_IN6)
582 , .gifaddr = IFADDR_PARAM(SIOCGIFADDR_IN6)
583 , .defmask = BUFPARAM(in6_defmask)
584 , .pre_aifaddr = in6_pre_aifaddr
585 , .pre_aifaddr_arg = BUFPARAM(in6_ifra)
586 };
587 commit_address(env, oenv, &in6param);
588 }
589