af_inet6.c revision 1.6 1 /* $NetBSD: af_inet6.c,v 1.6 2008/04/15 22:24:37 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.6 2008/04/15 22:24:37 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 "extern.h"
56 #include "af_inet6.h"
57
58 static struct in6_ifreq ifr6;
59
60 struct in6_ifreq in6_ridreq;
61 struct in6_aliasreq in6_addreq = {
62 .ifra_prefixmask = {
63 .sin6_addr = {
64 .s6_addr =
65 {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}},
66 .ifra_lifetime = {
67 .ia6t_pltime = ND6_INFINITE_LIFETIME
68 , .ia6t_vltime = ND6_INFINITE_LIFETIME
69 }
70 };
71
72 static char *
73 sec2str(time_t total)
74 {
75 static char result[256];
76 int days, hours, mins, secs;
77 int first = 1;
78 char *p = result;
79 char *end = &result[sizeof(result)];
80 int n;
81
82 if (0) { /*XXX*/
83 days = total / 3600 / 24;
84 hours = (total / 3600) % 24;
85 mins = (total / 60) % 60;
86 secs = total % 60;
87
88 if (days) {
89 first = 0;
90 n = snprintf(p, end - p, "%dd", days);
91 if (n < 0 || n >= end - p)
92 return(result);
93 p += n;
94 }
95 if (!first || hours) {
96 first = 0;
97 n = snprintf(p, end - p, "%dh", hours);
98 if (n < 0 || n >= end - p)
99 return(result);
100 p += n;
101 }
102 if (!first || mins) {
103 first = 0;
104 n = snprintf(p, end - p, "%dm", mins);
105 if (n < 0 || n >= end - p)
106 return(result);
107 p += n;
108 }
109 snprintf(p, end - p, "%ds", secs);
110 } else
111 snprintf(p, end - p, "%lu", (u_long)total);
112
113 return(result);
114 }
115
116 static int
117 prefix(void *val, int size)
118 {
119 u_char *pname = (u_char *)val;
120 int byte, bit, plen = 0;
121
122 for (byte = 0; byte < size; byte++, plen += 8)
123 if (pname[byte] != 0xff)
124 break;
125 if (byte == size)
126 return (plen);
127 for (bit = 7; bit != 0; bit--, plen++)
128 if (!(pname[byte] & (1 << bit)))
129 break;
130 for (; bit != 0; bit--)
131 if (pname[byte] & (1 << bit))
132 return(0);
133 byte++;
134 for (; byte < size; byte++)
135 if (pname[byte])
136 return(0);
137 return (plen);
138 }
139
140 void
141 setia6flags(const char *vname, int value)
142 {
143
144 if (value < 0) {
145 value = -value;
146 in6_addreq.ifra_flags &= ~value;
147 } else
148 in6_addreq.ifra_flags |= value;
149 }
150
151 void
152 setia6pltime(const char *val, int d)
153 {
154
155 setia6lifetime("pltime", val);
156 }
157
158 void
159 setia6vltime(const char *val, int d)
160 {
161
162 setia6lifetime("vltime", val);
163 }
164
165 void
166 setia6lifetime(const char *cmd, const char *val)
167 {
168 time_t newval, t;
169 char *ep;
170
171 t = time(NULL);
172 newval = (time_t)strtoul(val, &ep, 0);
173 if (val == ep)
174 errx(EXIT_FAILURE, "invalid %s", cmd);
175 if (afp->af_af != AF_INET6)
176 errx(EXIT_FAILURE, "%s not allowed for the AF", cmd);
177 if (strcmp(cmd, "vltime") == 0) {
178 in6_addreq.ifra_lifetime.ia6t_expire = t + newval;
179 in6_addreq.ifra_lifetime.ia6t_vltime = newval;
180 } else if (strcmp(cmd, "pltime") == 0) {
181 in6_addreq.ifra_lifetime.ia6t_preferred = t + newval;
182 in6_addreq.ifra_lifetime.ia6t_pltime = newval;
183 }
184 }
185
186 void
187 setia6eui64(const char *cmd, int val)
188 {
189 struct ifaddrs *ifap, *ifa;
190 const struct sockaddr_in6 *sin6 = NULL;
191 const struct in6_addr *lladdr = NULL;
192 struct in6_addr *in6;
193
194 if (afp->af_af != AF_INET6)
195 errx(EXIT_FAILURE, "%s not allowed for the AF", cmd);
196 in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
197 if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
198 errx(EXIT_FAILURE, "interface index is already filled");
199 if (getifaddrs(&ifap) != 0)
200 err(EXIT_FAILURE, "getifaddrs");
201 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
202 if (ifa->ifa_addr->sa_family == AF_INET6 &&
203 strcmp(ifa->ifa_name, name) == 0) {
204 sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
205 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
206 lladdr = &sin6->sin6_addr;
207 break;
208 }
209 }
210 }
211 if (!lladdr)
212 errx(EXIT_FAILURE, "could not determine link local address");
213
214 memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
215
216 freeifaddrs(ifap);
217 }
218
219 void
220 in6_fillscopeid(struct sockaddr_in6 *sin6)
221 {
222 #if defined(__KAME__) && defined(KAME_SCOPEID)
223 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
224 sin6->sin6_scope_id =
225 ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
226 sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
227 }
228 #endif
229 }
230
231 /* XXX not really an alias */
232 void
233 in6_alias(struct in6_ifreq *creq)
234 {
235 struct sockaddr_in6 *sin6;
236 char hbuf[NI_MAXHOST];
237 u_int32_t scopeid;
238 const int niflag = NI_NUMERICHOST;
239
240 /* Get the non-alias address for this interface. */
241 getsock(AF_INET6);
242 if (s < 0) {
243 if (errno == EAFNOSUPPORT)
244 return;
245 err(EXIT_FAILURE, "socket");
246 }
247
248 sin6 = (struct sockaddr_in6 *)&creq->ifr_addr;
249
250 in6_fillscopeid(sin6);
251 scopeid = sin6->sin6_scope_id;
252 if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
253 hbuf, sizeof(hbuf), NULL, 0, niflag))
254 strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
255 printf("\tinet6 %s", hbuf);
256
257 if (flags & IFF_POINTOPOINT) {
258 (void) memset(&ifr6, 0, sizeof(ifr6));
259 estrlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
260 ifr6.ifr_addr = creq->ifr_addr;
261 if (ioctl(s, SIOCGIFDSTADDR_IN6, &ifr6) == -1) {
262 if (errno != EADDRNOTAVAIL)
263 warn("SIOCGIFDSTADDR_IN6");
264 (void) memset(&ifr6.ifr_addr, 0, sizeof(ifr6.ifr_addr));
265 ifr6.ifr_addr.sin6_family = AF_INET6;
266 ifr6.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
267 }
268 sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
269 in6_fillscopeid(sin6);
270 hbuf[0] = '\0';
271 if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
272 hbuf, sizeof(hbuf), NULL, 0, niflag))
273 strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
274 printf(" -> %s", hbuf);
275 }
276
277 (void) memset(&ifr6, 0, sizeof(ifr6));
278 estrlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
279 ifr6.ifr_addr = creq->ifr_addr;
280 if (ioctl(s, SIOCGIFNETMASK_IN6, &ifr6) == -1) {
281 if (errno != EADDRNOTAVAIL)
282 warn("SIOCGIFNETMASK_IN6");
283 } else {
284 sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
285 printf(" prefixlen %d", prefix(&sin6->sin6_addr,
286 sizeof(struct in6_addr)));
287 }
288
289 (void) memset(&ifr6, 0, sizeof(ifr6));
290 estrlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
291 ifr6.ifr_addr = creq->ifr_addr;
292 if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == -1) {
293 if (errno != EADDRNOTAVAIL)
294 warn("SIOCGIFAFLAG_IN6");
295 } else {
296 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST)
297 printf(" anycast");
298 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
299 printf(" tentative");
300 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED)
301 printf(" duplicated");
302 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED)
303 printf(" detached");
304 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
305 printf(" deprecated");
306 }
307
308 if (scopeid)
309 printf(" scopeid 0x%x", scopeid);
310
311 if (Lflag) {
312 struct in6_addrlifetime *lifetime;
313 (void) memset(&ifr6, 0, sizeof(ifr6));
314 estrlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
315 ifr6.ifr_addr = creq->ifr_addr;
316 lifetime = &ifr6.ifr_ifru.ifru_lifetime;
317 if (ioctl(s, SIOCGIFALIFETIME_IN6, &ifr6) == -1) {
318 if (errno != EADDRNOTAVAIL)
319 warn("SIOCGIFALIFETIME_IN6");
320 } else if (lifetime->ia6t_preferred || lifetime->ia6t_expire) {
321 time_t t = time(NULL);
322 printf(" pltime ");
323 if (lifetime->ia6t_preferred) {
324 printf("%s", lifetime->ia6t_preferred < t
325 ? "0"
326 : sec2str(lifetime->ia6t_preferred - t));
327 } else
328 printf("infty");
329
330 printf(" vltime ");
331 if (lifetime->ia6t_expire) {
332 printf("%s", lifetime->ia6t_expire < t
333 ? "0"
334 : sec2str(lifetime->ia6t_expire - t));
335 } else
336 printf("infty");
337 }
338 }
339
340 printf("\n");
341 }
342
343 void
344 in6_status(int force)
345 {
346 struct ifaddrs *ifap, *ifa;
347 struct in6_ifreq isifr;
348
349 if (getifaddrs(&ifap) != 0)
350 err(EXIT_FAILURE, "getifaddrs");
351 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
352 if (strcmp(name, ifa->ifa_name) != 0)
353 continue;
354 if (ifa->ifa_addr->sa_family != AF_INET6)
355 continue;
356 if (sizeof(isifr.ifr_addr) < ifa->ifa_addr->sa_len)
357 continue;
358
359 memset(&isifr, 0, sizeof(isifr));
360 estrlcpy(isifr.ifr_name, ifa->ifa_name, sizeof(isifr.ifr_name));
361 memcpy(&isifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len);
362 in6_alias(&isifr);
363 }
364 freeifaddrs(ifap);
365 }
366
367 #define SIN6(x) ((struct sockaddr_in6 *) &(x))
368 struct sockaddr_in6 *sin6tab[] = {
369 SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
370 SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)};
371
372 void
373 in6_getaddr(const char *str, int which)
374 {
375 #if defined(__KAME__) && defined(KAME_SCOPEID)
376 struct sockaddr_in6 *sin6 = sin6tab[which];
377 struct addrinfo hints, *res;
378 int error;
379 char *slash = NULL;
380
381 if (which == ADDR) {
382 if ((slash = strrchr(str, '/')) != NULL)
383 *slash = '\0';
384 }
385
386 memset(&hints, 0, sizeof(hints));
387 hints.ai_family = AF_INET6;
388 hints.ai_socktype = SOCK_DGRAM;
389 #if 0 /* in_getaddr() allows FQDN */
390 hints.ai_flags = AI_NUMERICHOST;
391 #endif
392 error = getaddrinfo(str, "0", &hints, &res);
393 if (error && slash) {
394 /* try again treating the '/' as part of the name */
395 *slash = '/';
396 slash = NULL;
397 error = getaddrinfo(str, "0", &hints, &res);
398 }
399 if (error)
400 errx(EXIT_FAILURE, "%s: %s", str, gai_strerror(error));
401 if (res->ai_next)
402 errx(EXIT_FAILURE, "%s: resolved to multiple addresses", str);
403 if (res->ai_addrlen != sizeof(struct sockaddr_in6))
404 errx(EXIT_FAILURE, "%s: bad value", str);
405 memcpy(sin6, res->ai_addr, res->ai_addrlen);
406 freeaddrinfo(res);
407 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) && sin6->sin6_scope_id) {
408 *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] =
409 htons(sin6->sin6_scope_id);
410 sin6->sin6_scope_id = 0;
411 }
412 if (slash) {
413 in6_getprefix(slash + 1, MASK);
414 explicit_prefix = 1;
415 }
416 #else
417 struct sockaddr_in6 *gasin = sin6tab[which];
418
419 gasin->sin6_len = sizeof(*gasin);
420 if (which != MASK)
421 gasin->sin6_family = AF_INET6;
422
423 if (which == ADDR) {
424 char *p = NULL;
425 if((p = strrchr(str, '/')) != NULL) {
426 *p = '\0';
427 in6_getprefix(p + 1, MASK);
428 explicit_prefix = 1;
429 }
430 }
431
432 if (inet_pton(AF_INET6, str, &gasin->sin6_addr) != 1)
433 errx(EXIT_FAILURE, "%s: bad value", str);
434 #endif
435 }
436
437 void
438 in6_getprefix(const char *plen, int which)
439 {
440 struct sockaddr_in6 *gpsin = sin6tab[which];
441 u_char *cp;
442 int len = strtol(plen, (char **)NULL, 10);
443
444 if ((len < 0) || (len > 128))
445 errx(EXIT_FAILURE, "%s: bad value", plen);
446 gpsin->sin6_len = sizeof(*gpsin);
447 if (which != MASK)
448 gpsin->sin6_family = AF_INET6;
449 if ((len == 0) || (len == 128)) {
450 memset(&gpsin->sin6_addr, 0xff, sizeof(struct in6_addr));
451 return;
452 }
453 memset((void *)&gpsin->sin6_addr, 0x00, sizeof(gpsin->sin6_addr));
454 for (cp = (u_char *)&gpsin->sin6_addr; len > 7; len -= 8)
455 *cp++ = 0xff;
456 if (len)
457 *cp = 0xff << (8 - len);
458 }
459