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