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