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