1 /* $NetBSD: af_inet6.c,v 1.35 2016/02/29 16:23:25 riastradh 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.35 2016/02/29 16:23:25 riastradh 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 "env.h" 56 #include "extern.h" 57 #include "parse.h" 58 #include "extern.h" 59 #include "af_inetany.h" 60 #include "prog_ops.h" 61 62 static void in6_constructor(void) __attribute__((constructor)); 63 static void in6_alias(const char *, prop_dictionary_t, prop_dictionary_t, 64 struct in6_ifreq *); 65 static void in6_commit_address(prop_dictionary_t, prop_dictionary_t); 66 67 static int setia6eui64_impl(prop_dictionary_t, struct in6_aliasreq *); 68 static int setia6flags_impl(prop_dictionary_t, struct in6_aliasreq *); 69 static int setia6pltime_impl(prop_dictionary_t, struct in6_aliasreq *); 70 static int setia6vltime_impl(prop_dictionary_t, struct in6_aliasreq *); 71 72 static int setia6lifetime(prop_dictionary_t, int64_t, time_t *, uint32_t *); 73 74 static void in6_status(prop_dictionary_t, prop_dictionary_t, bool); 75 static bool in6_addr_flags(struct ifaddrs *ifa, int); 76 static bool in6_addr_tentative(struct ifaddrs *ifa); 77 static bool in6_addr_tentative_or_detached(struct ifaddrs *ifa); 78 79 static struct usage_func usage; 80 static cmdloop_branch_t branch[2]; 81 82 static const struct kwinst ia6flagskw[] = { 83 IFKW("anycast", IN6_IFF_ANYCAST) 84 , IFKW("deprecated", IN6_IFF_DEPRECATED) 85 }; 86 87 static struct pinteger parse_pltime = PINTEGER_INITIALIZER(&parse_pltime, 88 "pltime", 0, NULL, "pltime", &command_root.pb_parser); 89 90 static struct pinteger parse_vltime = PINTEGER_INITIALIZER(&parse_vltime, 91 "vltime", 0, NULL, "vltime", &command_root.pb_parser); 92 93 static const struct kwinst inet6kw[] = { 94 {.k_word = "pltime", .k_nextparser = &parse_pltime.pi_parser} 95 , {.k_word = "vltime", .k_nextparser = &parse_vltime.pi_parser} 96 , {.k_word = "eui64", .k_key = "eui64", .k_type = KW_T_BOOL, 97 .k_bool = true, .k_nextparser = &command_root.pb_parser} 98 }; 99 100 struct pkw ia6flags = PKW_INITIALIZER(&ia6flags, "ia6flags", NULL, 101 "ia6flag", ia6flagskw, __arraycount(ia6flagskw), &command_root.pb_parser); 102 struct pkw inet6 = PKW_INITIALIZER(&inet6, "IPv6 keywords", NULL, 103 NULL, inet6kw, __arraycount(inet6kw), NULL); 104 105 static struct afswtch in6af = { 106 .af_name = "inet6", .af_af = AF_INET6, .af_status = in6_status, 107 .af_addr_commit = in6_commit_address, 108 .af_addr_tentative = in6_addr_tentative, 109 .af_addr_tentative_or_detached = in6_addr_tentative_or_detached 110 }; 111 112 static int 113 prefix(void *val, int size) 114 { 115 u_char *pname = (u_char *)val; 116 int byte, bit, plen = 0; 117 118 for (byte = 0; byte < size; byte++, plen += 8) 119 if (pname[byte] != 0xff) 120 break; 121 if (byte == size) 122 return (plen); 123 for (bit = 7; bit != 0; bit--, plen++) 124 if (!(pname[byte] & (1 << bit))) 125 break; 126 for (; bit != 0; bit--) 127 if (pname[byte] & (1 << bit)) 128 return(0); 129 byte++; 130 for (; byte < size; byte++) 131 if (pname[byte]) 132 return(0); 133 return (plen); 134 } 135 136 int 137 setia6flags_impl(prop_dictionary_t env, struct in6_aliasreq *ifra) 138 { 139 int64_t ia6flag; 140 141 if (!prop_dictionary_get_int64(env, "ia6flag", &ia6flag)) { 142 errno = ENOENT; 143 return -1; 144 } 145 146 if (ia6flag < 0) { 147 ia6flag = -ia6flag; 148 ifra->ifra_flags &= ~ia6flag; 149 } else 150 ifra->ifra_flags |= ia6flag; 151 return 0; 152 } 153 154 int 155 setia6pltime_impl(prop_dictionary_t env, struct in6_aliasreq *ifra) 156 { 157 int64_t pltime; 158 159 if (!prop_dictionary_get_int64(env, "pltime", &pltime)) { 160 errno = ENOENT; 161 return -1; 162 } 163 164 return setia6lifetime(env, pltime, 165 &ifra->ifra_lifetime.ia6t_preferred, 166 &ifra->ifra_lifetime.ia6t_pltime); 167 } 168 169 int 170 setia6vltime_impl(prop_dictionary_t env, struct in6_aliasreq *ifra) 171 { 172 int64_t vltime; 173 174 if (!prop_dictionary_get_int64(env, "vltime", &vltime)) { 175 errno = ENOENT; 176 return -1; 177 } 178 179 return setia6lifetime(env, vltime, 180 &ifra->ifra_lifetime.ia6t_expire, 181 &ifra->ifra_lifetime.ia6t_vltime); 182 } 183 184 static int 185 setia6lifetime(prop_dictionary_t env, int64_t val, time_t *timep, 186 uint32_t *ivalp) 187 { 188 time_t t; 189 int af; 190 191 if ((af = getaf(env)) == -1 || af != AF_INET6) { 192 errx(EXIT_FAILURE, 193 "inet6 address lifetime not allowed for the AF"); 194 } 195 196 t = time(NULL); 197 *timep = t + val; 198 *ivalp = val; 199 return 0; 200 } 201 202 int 203 setia6eui64_impl(prop_dictionary_t env, struct in6_aliasreq *ifra) 204 { 205 char buf[2][80]; 206 struct ifaddrs *ifap, *ifa; 207 const struct sockaddr_in6 *sin6 = NULL; 208 const struct in6_addr *lladdr = NULL; 209 struct in6_addr *in6; 210 const char *ifname; 211 bool doit = false; 212 int af; 213 214 if (!prop_dictionary_get_bool(env, "eui64", &doit) || !doit) { 215 errno = ENOENT; 216 return -1; 217 } 218 219 if ((ifname = getifname(env)) == NULL) 220 return -1; 221 222 af = getaf(env); 223 if (af != AF_INET6) { 224 errx(EXIT_FAILURE, 225 "eui64 address modifier not allowed for the AF"); 226 } 227 in6 = &ifra->ifra_addr.sin6_addr; 228 if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) { 229 union { 230 struct sockaddr_in6 sin6; 231 struct sockaddr sa; 232 } any = {.sin6 = {.sin6_family = AF_INET6}}; 233 memcpy(&any.sin6.sin6_addr, &in6addr_any, 234 sizeof(any.sin6.sin6_addr)); 235 (void)sockaddr_snprintf(buf[0], sizeof(buf[0]), "%a%%S", 236 &any.sa); 237 (void)sockaddr_snprintf(buf[1], sizeof(buf[1]), "%a%%S", 238 (const struct sockaddr *)&ifra->ifra_addr); 239 errx(EXIT_FAILURE, "interface index is already filled, %s | %s", 240 buf[0], buf[1]); 241 } 242 if (getifaddrs(&ifap) != 0) 243 err(EXIT_FAILURE, "getifaddrs"); 244 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 245 if (ifa->ifa_addr->sa_family == AF_INET6 && 246 strcmp(ifa->ifa_name, ifname) == 0) { 247 sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr; 248 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 249 lladdr = &sin6->sin6_addr; 250 break; 251 } 252 } 253 } 254 if (lladdr == NULL) 255 errx(EXIT_FAILURE, "could not determine link local address"); 256 257 memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); 258 259 freeifaddrs(ifap); 260 return 0; 261 } 262 263 /* XXX not really an alias */ 264 void 265 in6_alias(const char *ifname, prop_dictionary_t env, prop_dictionary_t oenv, 266 struct in6_ifreq *creq) 267 { 268 struct in6_ifreq ifr6; 269 struct sockaddr_in6 *sin6; 270 char hbuf[NI_MAXHOST]; 271 u_int32_t scopeid; 272 int s; 273 const int niflag = Nflag ? 0 : NI_NUMERICHOST; 274 unsigned short flags; 275 276 /* Get the non-alias address for this interface. */ 277 if ((s = getsock(AF_INET6)) == -1) { 278 if (errno == EAFNOSUPPORT) 279 return; 280 err(EXIT_FAILURE, "socket"); 281 } 282 283 sin6 = &creq->ifr_addr; 284 285 inet6_getscopeid(sin6, INET6_IS_ADDR_LINKLOCAL); 286 scopeid = sin6->sin6_scope_id; 287 if (getnameinfo((const struct sockaddr *)sin6, sin6->sin6_len, 288 hbuf, sizeof(hbuf), NULL, 0, niflag)) 289 strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */ 290 printf("\tinet6 %s", hbuf); 291 292 if (getifflags(env, oenv, &flags) == -1) 293 err(EXIT_FAILURE, "%s: getifflags", __func__); 294 295 if (flags & IFF_POINTOPOINT) { 296 ifr6 = *creq; 297 if (prog_ioctl(s, SIOCGIFDSTADDR_IN6, &ifr6) == -1) { 298 if (errno != EADDRNOTAVAIL) 299 warn("SIOCGIFDSTADDR_IN6"); 300 memset(&ifr6.ifr_addr, 0, sizeof(ifr6.ifr_addr)); 301 ifr6.ifr_addr.sin6_family = AF_INET6; 302 ifr6.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); 303 } 304 sin6 = &ifr6.ifr_addr; 305 inet6_getscopeid(sin6, INET6_IS_ADDR_LINKLOCAL); 306 hbuf[0] = '\0'; 307 if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len, 308 hbuf, sizeof(hbuf), NULL, 0, niflag)) 309 strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */ 310 printf(" -> %s", hbuf); 311 } 312 313 ifr6 = *creq; 314 if (prog_ioctl(s, SIOCGIFNETMASK_IN6, &ifr6) == -1) { 315 if (errno != EADDRNOTAVAIL) 316 warn("SIOCGIFNETMASK_IN6"); 317 } else { 318 sin6 = &ifr6.ifr_addr; 319 printf(" prefixlen %d", prefix(&sin6->sin6_addr, 320 sizeof(struct in6_addr))); 321 } 322 323 ifr6 = *creq; 324 if (prog_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == -1) { 325 if (errno != EADDRNOTAVAIL) 326 warn("SIOCGIFAFLAG_IN6"); 327 } else { 328 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST) 329 printf(" anycast"); 330 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE) 331 printf(" tentative"); 332 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED) 333 printf(" duplicated"); 334 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED) 335 printf(" detached"); 336 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED) 337 printf(" deprecated"); 338 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_AUTOCONF) 339 printf(" autoconf"); 340 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TEMPORARY) 341 printf(" temporary"); 342 } 343 344 if (scopeid) 345 printf(" scopeid 0x%x", scopeid); 346 347 if (get_flag('L')) { 348 struct in6_addrlifetime *lifetime; 349 ifr6 = *creq; 350 lifetime = &ifr6.ifr_ifru.ifru_lifetime; 351 if (prog_ioctl(s, SIOCGIFALIFETIME_IN6, &ifr6) == -1) { 352 if (errno != EADDRNOTAVAIL) 353 warn("SIOCGIFALIFETIME_IN6"); 354 } else if (lifetime->ia6t_preferred || lifetime->ia6t_expire) { 355 time_t t = time(NULL); 356 printf(" pltime "); 357 if (lifetime->ia6t_preferred) { 358 printf("%lu", 359 (unsigned long)(lifetime->ia6t_preferred - 360 MIN(t, lifetime->ia6t_preferred))); 361 } else 362 printf("infty"); 363 364 printf(" vltime "); 365 if (lifetime->ia6t_expire) { 366 printf("%lu", 367 (unsigned long)(lifetime->ia6t_expire - 368 MIN(t, lifetime->ia6t_expire))); 369 } else 370 printf("infty"); 371 } 372 } 373 } 374 375 static void 376 in6_status(prop_dictionary_t env, prop_dictionary_t oenv, bool force) 377 { 378 struct ifaddrs *ifap, *ifa; 379 struct in6_ifreq ifr; 380 const char *ifname; 381 bool printprefs = false; 382 383 if ((ifname = getifname(env)) == NULL) 384 err(EXIT_FAILURE, "%s: getifname", __func__); 385 386 if (getifaddrs(&ifap) != 0) 387 err(EXIT_FAILURE, "getifaddrs"); 388 printprefs = ifa_any_preferences(ifname, ifap, AF_INET6); 389 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 390 if (strcmp(ifname, ifa->ifa_name) != 0) 391 continue; 392 if (ifa->ifa_addr->sa_family != AF_INET6) 393 continue; 394 if (sizeof(ifr.ifr_addr) < ifa->ifa_addr->sa_len) 395 continue; 396 397 memset(&ifr, 0, sizeof(ifr)); 398 estrlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); 399 memcpy(&ifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len); 400 in6_alias(ifname, env, oenv, &ifr); 401 if (printprefs) 402 ifa_print_preference(ifa->ifa_name, ifa->ifa_addr); 403 printf("\n"); 404 } 405 freeifaddrs(ifap); 406 } 407 408 static int 409 in6_pre_aifaddr(prop_dictionary_t env, const struct afparam *param) 410 { 411 struct in6_aliasreq *ifra = param->req.buf; 412 413 setia6eui64_impl(env, ifra); 414 setia6vltime_impl(env, ifra); 415 setia6pltime_impl(env, ifra); 416 setia6flags_impl(env, ifra); 417 inet6_putscopeid(&ifra->ifra_addr, INET6_IS_ADDR_LINKLOCAL); 418 inet6_putscopeid(&ifra->ifra_dstaddr, INET6_IS_ADDR_LINKLOCAL); 419 420 return 0; 421 } 422 423 static void 424 in6_commit_address(prop_dictionary_t env, prop_dictionary_t oenv) 425 { 426 struct in6_ifreq in6_ifr = { 427 .ifr_addr = { 428 .sin6_family = AF_INET6, 429 .sin6_len = sizeof(in6_ifr.ifr_addr), 430 .sin6_addr = { 431 .s6_addr = 432 {0xff, 0xff, 0xff, 0xff, 433 0xff, 0xff, 0xff, 0xff} 434 } 435 } 436 }; 437 static struct sockaddr_in6 in6_defmask = { 438 .sin6_family = AF_INET6, 439 .sin6_len = sizeof(in6_defmask), 440 .sin6_addr = { 441 .s6_addr = {0xff, 0xff, 0xff, 0xff, 442 0xff, 0xff, 0xff, 0xff} 443 } 444 }; 445 446 struct in6_aliasreq in6_ifra = { 447 .ifra_prefixmask = { 448 .sin6_family = AF_INET6, 449 .sin6_len = sizeof(in6_ifra.ifra_prefixmask), 450 .sin6_addr = { 451 .s6_addr = 452 {0xff, 0xff, 0xff, 0xff, 453 0xff, 0xff, 0xff, 0xff}}}, 454 .ifra_lifetime = { 455 .ia6t_pltime = ND6_INFINITE_LIFETIME 456 , .ia6t_vltime = ND6_INFINITE_LIFETIME 457 } 458 }; 459 struct afparam in6param = { 460 .req = BUFPARAM(in6_ifra) 461 , .dgreq = BUFPARAM(in6_ifr) 462 , .name = { 463 {.buf = in6_ifr.ifr_name, 464 .buflen = sizeof(in6_ifr.ifr_name)}, 465 {.buf = in6_ifra.ifra_name, 466 .buflen = sizeof(in6_ifra.ifra_name)} 467 } 468 , .dgaddr = BUFPARAM(in6_ifr.ifr_addr) 469 , .addr = BUFPARAM(in6_ifra.ifra_addr) 470 , .dst = BUFPARAM(in6_ifra.ifra_dstaddr) 471 , .brd = BUFPARAM(in6_ifra.ifra_broadaddr) 472 , .mask = BUFPARAM(in6_ifra.ifra_prefixmask) 473 , .aifaddr = IFADDR_PARAM(SIOCAIFADDR_IN6) 474 , .difaddr = IFADDR_PARAM(SIOCDIFADDR_IN6) 475 , .gifaddr = IFADDR_PARAM(SIOCGIFADDR_IN6) 476 , .defmask = BUFPARAM(in6_defmask) 477 , .pre_aifaddr = in6_pre_aifaddr 478 }; 479 commit_address(env, oenv, &in6param); 480 } 481 482 static bool 483 in6_addr_flags(struct ifaddrs *ifa, int flags) 484 { 485 int s; 486 struct in6_ifreq ifr; 487 488 if ((s = getsock(AF_INET6)) == -1) 489 err(EXIT_FAILURE, "%s: getsock", __func__); 490 memset(&ifr, 0, sizeof(ifr)); 491 estrlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); 492 ifr.ifr_addr = *(struct sockaddr_in6 *)ifa->ifa_addr; 493 if (prog_ioctl(s, SIOCGIFAFLAG_IN6, &ifr) == -1) 494 err(EXIT_FAILURE, "SIOCGIFAFLAG_IN6"); 495 return ifr.ifr_ifru.ifru_flags6 & flags ? true : false; 496 } 497 498 static bool 499 in6_addr_tentative(struct ifaddrs *ifa) 500 { 501 502 return in6_addr_flags(ifa, IN6_IFF_TENTATIVE); 503 } 504 505 static bool 506 in6_addr_tentative_or_detached(struct ifaddrs *ifa) 507 { 508 509 return in6_addr_flags(ifa, IN6_IFF_TENTATIVE | IN6_IFF_DETACHED); 510 } 511 512 static void 513 in6_usage(prop_dictionary_t env) 514 { 515 fprintf(stderr, 516 "\t[ anycast | -anycast ] [ deprecated | -deprecated ]\n" 517 "\t[ pltime n ] [ vltime n ] " 518 "[ eui64 ]\n"); 519 } 520 521 static void 522 in6_constructor(void) 523 { 524 if (register_flag('L') != 0) 525 err(EXIT_FAILURE, __func__); 526 register_family(&in6af); 527 usage_func_init(&usage, in6_usage); 528 register_usage(&usage); 529 cmdloop_branch_init(&branch[0], &ia6flags.pk_parser); 530 cmdloop_branch_init(&branch[1], &inet6.pk_parser); 531 register_cmdloop_branch(&branch[0]); 532 register_cmdloop_branch(&branch[1]); 533 } 534