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