| 1 | /* |
| 2 | * Copyright (c) 1983, 1993 |
| 3 | * The Regents of the University of California. All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. Neither the name of the University nor the names of its contributors |
| 14 | * may be used to endorse or promote products derived from this software |
| 15 | * without specific prior written permission. |
| 16 | * |
| 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 27 | * SUCH DAMAGE. |
| 28 | * |
| 29 | * $FreeBSD: src/sbin/ifconfig/af_inet6.c,v 1.3 2005/06/16 19:37:09 ume Exp $ |
| 30 | */ |
| 31 | |
| 32 | #include <sys/param.h> |
| 33 | #include <sys/ioctl.h> |
| 34 | #include <sys/socket.h> |
| 35 | #include <net/if.h> |
| 36 | #include <net/if_var.h> /* for struct ifaddr */ |
| 37 | #include <net/route.h> /* for RTX_IFA */ |
| 38 | #include <netinet/in.h> |
| 39 | #include <netinet/in_var.h> |
| 40 | #include <netinet6/nd6.h> /* Define ND6_INFINITE_LIFETIME */ |
| 41 | #include <arpa/inet.h> |
| 42 | #include <netdb.h> |
| 43 | |
| 44 | #include <err.h> |
| 45 | #include <stdio.h> |
| 46 | #include <stdlib.h> |
| 47 | #include <string.h> |
| 48 | #include <unistd.h> |
| 49 | #include <time.h> |
| 50 | #include <ifaddrs.h> |
| 51 | |
| 52 | #include "ifconfig.h" |
| 53 | |
| 54 | static struct in6_ifreq in6_ridreq; |
| 55 | static struct in6_aliasreq in6_addreq = |
| 56 | { { 0 }, |
| 57 | { 0 }, |
| 58 | { 0 }, |
| 59 | { 0 }, |
| 60 | 0, |
| 61 | { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; |
| 62 | static int ip6lifetime; |
| 63 | |
| 64 | static void in6_fillscopeid(struct sockaddr_in6 *sin6); |
| 65 | static int prefix(void *, int); |
| 66 | static char *sec2str(time_t); |
| 67 | static int explicit_prefix = 0; |
| 68 | |
| 69 | static char addr_buf[MAXHOSTNAMELEN *2 + 1]; /*for getnameinfo()*/ |
| 70 | |
| 71 | static void |
| 72 | setifprefixlen(const char *addr, int dummy __unused, int s, |
| 73 | const struct afswtch *afp) |
| 74 | { |
| 75 | if (afp->af_getprefix != NULL) |
| 76 | afp->af_getprefix(addr, MASK); |
| 77 | explicit_prefix = 1; |
| 78 | } |
| 79 | |
| 80 | static void |
| 81 | setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused, |
| 82 | const struct afswtch *afp) |
| 83 | { |
| 84 | if (afp->af_af != AF_INET6) |
| 85 | err(1, "address flags can be set only for inet6 addresses"); |
| 86 | |
| 87 | if (flag < 0) |
| 88 | in6_addreq.ifra_flags &= ~(-flag); |
| 89 | else |
| 90 | in6_addreq.ifra_flags |= flag; |
| 91 | } |
| 92 | |
| 93 | static void |
| 94 | setip6lifetime(const char *cmd, const char *val, int s, |
| 95 | const struct afswtch *afp) |
| 96 | { |
| 97 | struct timespec now; |
| 98 | time_t newval; |
| 99 | char *ep; |
| 100 | |
| 101 | clock_gettime(CLOCK_MONOTONIC_FAST, &now); |
| 102 | newval = (time_t)strtoul(val, &ep, 0); |
| 103 | if (val == ep) |
| 104 | errx(1, "invalid %s", cmd); |
| 105 | if (afp->af_af != AF_INET6) |
| 106 | errx(1, "%s not allowed for the AF", cmd); |
| 107 | if (strcmp(cmd, "vltime") == 0) { |
| 108 | in6_addreq.ifra_lifetime.ia6t_expire = now.tv_sec + newval; |
| 109 | in6_addreq.ifra_lifetime.ia6t_vltime = newval; |
| 110 | } else if (strcmp(cmd, "pltime") == 0) { |
| 111 | in6_addreq.ifra_lifetime.ia6t_preferred = now.tv_sec + newval; |
| 112 | in6_addreq.ifra_lifetime.ia6t_pltime = newval; |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | static void |
| 117 | setip6pltime(const char *seconds, int dummy __unused, int s, |
| 118 | const struct afswtch *afp) |
| 119 | { |
| 120 | setip6lifetime("pltime", seconds, s, afp); |
| 121 | } |
| 122 | |
| 123 | static void |
| 124 | setip6vltime(const char *seconds, int dummy __unused, int s, |
| 125 | const struct afswtch *afp) |
| 126 | { |
| 127 | setip6lifetime("vltime", seconds, s, afp); |
| 128 | } |
| 129 | |
| 130 | static void |
| 131 | setip6eui64(const char *cmd, int dummy __unused, int s, |
| 132 | const struct afswtch *afp) |
| 133 | { |
| 134 | struct ifaddrs *ifap, *ifa; |
| 135 | const struct sockaddr_in6 *sin6 = NULL; |
| 136 | const struct in6_addr *lladdr = NULL; |
| 137 | struct in6_addr *in6; |
| 138 | |
| 139 | if (afp->af_af != AF_INET6) |
| 140 | errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); |
| 141 | in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; |
| 142 | if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) |
| 143 | errx(EXIT_FAILURE, "interface index is already filled"); |
| 144 | if (getifaddrs(&ifap) != 0) |
| 145 | err(EXIT_FAILURE, "getifaddrs"); |
| 146 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { |
| 147 | if (ifa->ifa_addr->sa_family == AF_INET6 && |
| 148 | strcmp(ifa->ifa_name, name) == 0) { |
| 149 | sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr; |
| 150 | if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { |
| 151 | lladdr = &sin6->sin6_addr; |
| 152 | break; |
| 153 | } |
| 154 | } |
| 155 | } |
| 156 | if (!lladdr) |
| 157 | errx(EXIT_FAILURE, "could not determine link local address"); |
| 158 | |
| 159 | memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); |
| 160 | |
| 161 | freeifaddrs(ifap); |
| 162 | } |
| 163 | |
| 164 | static void |
| 165 | in6_fillscopeid(struct sockaddr_in6 *sin6) |
| 166 | { |
| 167 | #if defined(__KAME__) && defined(KAME_SCOPEID) |
| 168 | if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { |
| 169 | sin6->sin6_scope_id = |
| 170 | ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); |
| 171 | sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; |
| 172 | } |
| 173 | #endif |
| 174 | } |
| 175 | |
| 176 | static void |
| 177 | in6_status(int s __unused, const struct rt_addrinfo * info) |
| 178 | { |
| 179 | struct sockaddr_in6 *sin, null_sin; |
| 180 | struct in6_ifreq ifr6; |
| 181 | int s6; |
| 182 | u_int32_t flags6; |
| 183 | struct in6_addrlifetime lifetime; |
| 184 | struct timespec now; |
| 185 | int error; |
| 186 | u_int32_t scopeid; |
| 187 | |
| 188 | clock_gettime(CLOCK_MONOTONIC_FAST, &now); |
| 189 | |
| 190 | memset(&null_sin, 0, sizeof(null_sin)); |
| 191 | |
| 192 | sin = (struct sockaddr_in6 *)info->rti_info[RTAX_IFA]; |
| 193 | if (sin == NULL) |
| 194 | return; |
| 195 | |
| 196 | strlcpy(ifr6.ifr_name, ifr.ifr_name, IFNAMSIZ); |
| 197 | if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { |
| 198 | warn("socket(AF_INET6,SOCK_DGRAM)"); |
| 199 | return; |
| 200 | } |
| 201 | ifr6.ifr_addr = *sin; |
| 202 | if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { |
| 203 | warn("ioctl(SIOCGIFAFLAG_IN6)"); |
| 204 | close(s6); |
| 205 | return; |
| 206 | } |
| 207 | flags6 = ifr6.ifr_ifru.ifru_flags6; |
| 208 | memset(&lifetime, 0, sizeof(lifetime)); |
| 209 | ifr6.ifr_addr = *sin; |
| 210 | if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { |
| 211 | warn("ioctl(SIOCGIFALIFETIME_IN6)"); |
| 212 | close(s6); |
| 213 | return; |
| 214 | } |
| 215 | lifetime = ifr6.ifr_ifru.ifru_lifetime; |
| 216 | close(s6); |
| 217 | |
| 218 | /* XXX: embedded link local addr check */ |
| 219 | if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && |
| 220 | *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { |
| 221 | u_short index; |
| 222 | |
| 223 | index = *(u_short *)&sin->sin6_addr.s6_addr[2]; |
| 224 | *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; |
| 225 | if (sin->sin6_scope_id == 0) |
| 226 | sin->sin6_scope_id = ntohs(index); |
| 227 | } |
| 228 | scopeid = sin->sin6_scope_id; |
| 229 | |
| 230 | error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, |
| 231 | sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); |
| 232 | if (error != 0) |
| 233 | inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, |
| 234 | sizeof(addr_buf)); |
| 235 | printf("\tinet6 %s ", addr_buf); |
| 236 | |
| 237 | if (flags & IFF_POINTOPOINT) { |
| 238 | /* note RTAX_BRD overlap with IFF_BROADCAST */ |
| 239 | sin = (struct sockaddr_in6 *)info->rti_info[RTAX_BRD]; |
| 240 | /* |
| 241 | * some of the interfaces do not have valid destination |
| 242 | * address. |
| 243 | */ |
| 244 | if (sin && sin->sin6_family == AF_INET6) { |
| 245 | int error; |
| 246 | |
| 247 | /* XXX: embedded link local addr check */ |
| 248 | if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && |
| 249 | *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { |
| 250 | u_short index; |
| 251 | |
| 252 | index = *(u_short *)&sin->sin6_addr.s6_addr[2]; |
| 253 | *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; |
| 254 | if (sin->sin6_scope_id == 0) |
| 255 | sin->sin6_scope_id = ntohs(index); |
| 256 | } |
| 257 | |
| 258 | error = getnameinfo((struct sockaddr *)sin, |
| 259 | sin->sin6_len, addr_buf, |
| 260 | sizeof(addr_buf), NULL, 0, |
| 261 | NI_NUMERICHOST); |
| 262 | if (error != 0) |
| 263 | inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, |
| 264 | sizeof(addr_buf)); |
| 265 | printf("--> %s ", addr_buf); |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | sin = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK]; |
| 270 | if (!sin) |
| 271 | sin = &null_sin; |
| 272 | printf("prefixlen %d", prefix(&sin->sin6_addr, |
| 273 | sizeof(struct in6_addr))); |
| 274 | |
| 275 | if ((flags6 & IN6_IFF_ANYCAST) != 0) |
| 276 | printf(" anycast"); |
| 277 | if ((flags6 & IN6_IFF_TENTATIVE) != 0) |
| 278 | printf(" tentative"); |
| 279 | if ((flags6 & IN6_IFF_DUPLICATED) != 0) |
| 280 | printf(" duplicated"); |
| 281 | if ((flags6 & IN6_IFF_DETACHED) != 0) |
| 282 | printf(" detached"); |
| 283 | if ((flags6 & IN6_IFF_DEPRECATED) != 0) |
| 284 | printf(" deprecated"); |
| 285 | if ((flags6 & IN6_IFF_AUTOCONF) != 0) |
| 286 | printf(" autoconf"); |
| 287 | if ((flags6 & IN6_IFF_TEMPORARY) != 0) |
| 288 | printf(" temporary"); |
| 289 | |
| 290 | if (scopeid) |
| 291 | printf(" scopeid 0x%x", scopeid); |
| 292 | |
| 293 | if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { |
| 294 | printf(" pltime"); |
| 295 | if (lifetime.ia6t_preferred) { |
| 296 | printf(" %s", lifetime.ia6t_preferred < now.tv_sec |
| 297 | ? "0" : sec2str(lifetime.ia6t_preferred - now.tv_sec)); |
| 298 | } else { |
| 299 | printf(" infty"); |
| 300 | } |
| 301 | |
| 302 | printf(" vltime"); |
| 303 | if (lifetime.ia6t_expire) { |
| 304 | printf(" %s", lifetime.ia6t_expire < now.tv_sec |
| 305 | ? "0" : sec2str(lifetime.ia6t_expire - now.tv_sec)); |
| 306 | } else { |
| 307 | printf(" infty"); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | putchar('\n'); |
| 312 | } |
| 313 | |
| 314 | #define SIN6(x) ((struct sockaddr_in6 *) &(x)) |
| 315 | static struct sockaddr_in6 *sin6tab[] = { |
| 316 | SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr), |
| 317 | SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr) |
| 318 | }; |
| 319 | |
| 320 | static void |
| 321 | in6_getprefix(const char *plen, int which) |
| 322 | { |
| 323 | struct sockaddr_in6 *sin = sin6tab[which]; |
| 324 | u_char *cp; |
| 325 | int len = atoi(plen); |
| 326 | |
| 327 | if ((len < 0) || (len > 128)) |
| 328 | errx(1, "%s: bad value", plen); |
| 329 | sin->sin6_len = sizeof(*sin); |
| 330 | if (which != MASK) |
| 331 | sin->sin6_family = AF_INET6; |
| 332 | if ((len == 0) || (len == 128)) { |
| 333 | memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); |
| 334 | return; |
| 335 | } |
| 336 | memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); |
| 337 | for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) |
| 338 | *cp++ = 0xff; |
| 339 | *cp = 0xff << (8 - len); |
| 340 | } |
| 341 | |
| 342 | static void |
| 343 | in6_getaddr(const char *s, int which) |
| 344 | { |
| 345 | struct sockaddr_in6 *sin = sin6tab[which]; |
| 346 | struct addrinfo hints, *res; |
| 347 | int error = -1; |
| 348 | |
| 349 | newaddr &= 1; |
| 350 | |
| 351 | sin->sin6_len = sizeof(*sin); |
| 352 | if (which != MASK) |
| 353 | sin->sin6_family = AF_INET6; |
| 354 | |
| 355 | if (which == ADDR) { |
| 356 | char *p = NULL; |
| 357 | if((p = strrchr(s, '/')) != NULL) { |
| 358 | *p = '\0'; |
| 359 | in6_getprefix(p + 1, MASK); |
| 360 | explicit_prefix = 1; |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | if (sin->sin6_family == AF_INET6) { |
| 365 | bzero(&hints, sizeof(struct addrinfo)); |
| 366 | hints.ai_family = AF_INET6; |
| 367 | error = getaddrinfo(s, NULL, &hints, &res); |
| 368 | } |
| 369 | if (error != 0) { |
| 370 | if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) |
| 371 | errx(1, "%s: bad value", s); |
| 372 | } else |
| 373 | bcopy(res->ai_addr, sin, res->ai_addrlen); |
| 374 | } |
| 375 | |
| 376 | static int |
| 377 | prefix(void *val, int size) |
| 378 | { |
| 379 | u_char *name = (u_char *)val; |
| 380 | int byte, bit, plen = 0; |
| 381 | |
| 382 | for (byte = 0; byte < size; byte++, plen += 8) |
| 383 | if (name[byte] != 0xff) |
| 384 | break; |
| 385 | if (byte == size) |
| 386 | return (plen); |
| 387 | for (bit = 7; bit != 0; bit--, plen++) |
| 388 | if (!(name[byte] & (1 << bit))) |
| 389 | break; |
| 390 | for (; bit != 0; bit--) |
| 391 | if (name[byte] & (1 << bit)) |
| 392 | return(0); |
| 393 | byte++; |
| 394 | for (; byte < size; byte++) |
| 395 | if (name[byte]) |
| 396 | return(0); |
| 397 | return (plen); |
| 398 | } |
| 399 | |
| 400 | static char * |
| 401 | sec2str(time_t total) |
| 402 | { |
| 403 | static char result[256]; |
| 404 | int days, hours, mins, secs; |
| 405 | int first = 1; |
| 406 | char *p = result; |
| 407 | |
| 408 | if (0) { |
| 409 | days = total / 3600 / 24; |
| 410 | hours = (total / 3600) % 24; |
| 411 | mins = (total / 60) % 60; |
| 412 | secs = total % 60; |
| 413 | |
| 414 | if (days) { |
| 415 | first = 0; |
| 416 | p += sprintf(p, "%dd", days); |
| 417 | } |
| 418 | if (!first || hours) { |
| 419 | first = 0; |
| 420 | p += sprintf(p, "%dh", hours); |
| 421 | } |
| 422 | if (!first || mins) { |
| 423 | first = 0; |
| 424 | p += sprintf(p, "%dm", mins); |
| 425 | } |
| 426 | sprintf(p, "%ds", secs); |
| 427 | } else |
| 428 | sprintf(result, "%lu", (unsigned long)total); |
| 429 | |
| 430 | return(result); |
| 431 | } |
| 432 | |
| 433 | static void |
| 434 | in6_postproc(int s, const struct afswtch *afp) |
| 435 | { |
| 436 | if (explicit_prefix == 0) { |
| 437 | /* Aggregatable address architecture defines all prefixes |
| 438 | are 64. So, it is convenient to set prefixlen to 64 if |
| 439 | it is not specified. */ |
| 440 | setifprefixlen("64", 0, s, afp); |
| 441 | /* in6_getprefix("64", MASK) if MASK is available here... */ |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | static void |
| 446 | in6_status_tunnel(int s) |
| 447 | { |
| 448 | char src[NI_MAXHOST]; |
| 449 | char dst[NI_MAXHOST]; |
| 450 | struct in6_ifreq in6_ifr; |
| 451 | const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; |
| 452 | |
| 453 | memset(&in6_ifr, 0, sizeof(in6_ifr)); |
| 454 | strlcpy(in6_ifr.ifr_name, name, IFNAMSIZ); |
| 455 | |
| 456 | if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) |
| 457 | return; |
| 458 | if (sa->sa_family != AF_INET6) |
| 459 | return; |
| 460 | in6_fillscopeid(&in6_ifr.ifr_addr); |
| 461 | if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, |
| 462 | NI_NUMERICHOST) != 0) |
| 463 | src[0] = '\0'; |
| 464 | |
| 465 | if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) |
| 466 | return; |
| 467 | if (sa->sa_family != AF_INET6) |
| 468 | return; |
| 469 | in6_fillscopeid(&in6_ifr.ifr_addr); |
| 470 | if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, |
| 471 | NI_NUMERICHOST) != 0) |
| 472 | dst[0] = '\0'; |
| 473 | |
| 474 | printf("\ttunnel inet6 %s --> %s\n", src, dst); |
| 475 | } |
| 476 | |
| 477 | static void |
| 478 | in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) |
| 479 | { |
| 480 | struct in6_aliasreq in6_addreq; |
| 481 | |
| 482 | memset(&in6_addreq, 0, sizeof(in6_addreq)); |
| 483 | strlcpy(in6_addreq.ifra_name, name, IFNAMSIZ); |
| 484 | memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); |
| 485 | memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr, |
| 486 | dstres->ai_addr->sa_len); |
| 487 | |
| 488 | if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0) |
| 489 | warn("SIOCSIFPHYADDR_IN6"); |
| 490 | } |
| 491 | |
| 492 | static struct cmd inet6_cmds[] = { |
| 493 | DEF_CMD_ARG("prefixlen", setifprefixlen), |
| 494 | DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), |
| 495 | DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), |
| 496 | DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), |
| 497 | DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), |
| 498 | DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), |
| 499 | DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), |
| 500 | DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), |
| 501 | DEF_CMD_ARG("pltime", setip6pltime), |
| 502 | DEF_CMD_ARG("vltime", setip6vltime), |
| 503 | DEF_CMD("eui64", 0, setip6eui64), |
| 504 | }; |
| 505 | |
| 506 | static struct afswtch af_inet6 = { |
| 507 | .af_name = "inet6", |
| 508 | .af_af = AF_INET6, |
| 509 | .af_status = in6_status, |
| 510 | .af_getaddr = in6_getaddr, |
| 511 | .af_getprefix = in6_getprefix, |
| 512 | .af_postproc = in6_postproc, |
| 513 | .af_status_tunnel = in6_status_tunnel, |
| 514 | .af_settunnel = in6_set_tunnel, |
| 515 | .af_difaddr = SIOCDIFADDR_IN6, |
| 516 | .af_aifaddr = SIOCAIFADDR_IN6, |
| 517 | .af_ridreq = &in6_ridreq, |
| 518 | .af_addreq = &in6_addreq, |
| 519 | }; |
| 520 | |
| 521 | static void |
| 522 | in6_Lopt_cb(const char *optarg __unused) |
| 523 | { |
| 524 | ip6lifetime++; /* print IPv6 address lifetime */ |
| 525 | } |
| 526 | static struct option in6_Lopt = { "L", "[-L]", in6_Lopt_cb, NULL }; |
| 527 | |
| 528 | static __constructor(101) void |
| 529 | inet6_ctor(void) |
| 530 | { |
| 531 | size_t i; |
| 532 | |
| 533 | for (i = 0; i < nitems(inet6_cmds); i++) |
| 534 | cmd_register(&inet6_cmds[i]); |
| 535 | af_register(&af_inet6); |
| 536 | opt_register(&in6_Lopt); |
| 537 | } |