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