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