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