/* $NetBSD: af_inet6.c,v 1.2 2005/03/20 02:44:50 thorpej Exp $ */ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: af_inet6.c,v 1.2 2005/03/20 02:44:50 thorpej Exp $"); #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" #include "af_inet6.h" static struct in6_ifreq ifr6; struct in6_ifreq in6_ridreq; struct in6_aliasreq in6_addreq; static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; char *end = &result[sizeof(result)]; int n; if (0) { /*XXX*/ days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; n = snprintf(p, end - p, "%dd", days); if (n < 0 || n >= end - p) return(result); p += n; } if (!first || hours) { first = 0; n = snprintf(p, end - p, "%dh", hours); if (n < 0 || n >= end - p) return(result); p += n; } if (!first || mins) { first = 0; n = snprintf(p, end - p, "%dm", mins); if (n < 0 || n >= end - p) return(result); p += n; } snprintf(p, end - p, "%ds", secs); } else snprintf(p, end - p, "%lu", (u_long)total); return(result); } static int prefix(void *val, int size) { u_char *pname = (u_char *)val; int byte, bit, plen = 0; for (byte = 0; byte < size; byte++, plen += 8) if (pname[byte] != 0xff) break; if (byte == size) return (plen); for (bit = 7; bit != 0; bit--, plen++) if (!(pname[byte] & (1 << bit))) break; for (; bit != 0; bit--) if (pname[byte] & (1 << bit)) return(0); byte++; for (; byte < size; byte++) if (pname[byte]) return(0); return (plen); } void setia6flags(const char *vname, int value) { if (value < 0) { value = -value; in6_addreq.ifra_flags &= ~value; } else in6_addreq.ifra_flags |= value; } void setia6pltime(const char *val, int d) { setia6lifetime("pltime", val); } void setia6vltime(const char *val, int d) { setia6lifetime("vltime", val); } void setia6lifetime(const char *cmd, const char *val) { time_t newval, t; char *ep; t = time(NULL); newval = (time_t)strtoul(val, &ep, 0); if (val == ep) errx(EXIT_FAILURE, "invalid %s", cmd); if (afp->af_af != AF_INET6) errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); if (strcmp(cmd, "vltime") == 0) { in6_addreq.ifra_lifetime.ia6t_expire = t + newval; in6_addreq.ifra_lifetime.ia6t_vltime = newval; } else if (strcmp(cmd, "pltime") == 0) { in6_addreq.ifra_lifetime.ia6t_preferred = t + newval; in6_addreq.ifra_lifetime.ia6t_pltime = newval; } } void setia6eui64(const char *cmd, int val) { struct ifaddrs *ifap, *ifa; const struct sockaddr_in6 *sin6 = NULL; const struct in6_addr *lladdr = NULL; struct in6_addr *in6; if (afp->af_af != AF_INET6) errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) errx(EXIT_FAILURE, "interface index is already filled"); if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, name) == 0) { sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { lladdr = &sin6->sin6_addr; break; } } } if (!lladdr) errx(EXIT_FAILURE, "could not determine link local address"); memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); freeifaddrs(ifap); } void in6_fillscopeid(struct sockaddr_in6 *sin6) { #if defined(__KAME__) && defined(KAME_SCOPEID) if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { sin6->sin6_scope_id = ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; } #endif } /* XXX not really an alias */ void in6_alias(struct in6_ifreq *creq) { struct sockaddr_in6 *sin6; char hbuf[NI_MAXHOST]; u_int32_t scopeid; const int niflag = NI_NUMERICHOST; /* Get the non-alias address for this interface. */ getsock(AF_INET6); if (s < 0) { if (errno == EPROTONOSUPPORT) return; err(EXIT_FAILURE, "socket"); } sin6 = (struct sockaddr_in6 *)&creq->ifr_addr; in6_fillscopeid(sin6); scopeid = sin6->sin6_scope_id; if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len, hbuf, sizeof(hbuf), NULL, 0, niflag)) strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */ printf("\tinet6 %s", hbuf); if (flags & IFF_POINTOPOINT) { (void) memset(&ifr6, 0, sizeof(ifr6)); (void) strncpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); ifr6.ifr_addr = creq->ifr_addr; if (ioctl(s, SIOCGIFDSTADDR_IN6, &ifr6) == -1) { if (errno != EADDRNOTAVAIL) warn("SIOCGIFDSTADDR_IN6"); (void) memset(&ifr6.ifr_addr, 0, sizeof(ifr6.ifr_addr)); ifr6.ifr_addr.sin6_family = AF_INET6; ifr6.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); } sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr; in6_fillscopeid(sin6); hbuf[0] = '\0'; if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len, hbuf, sizeof(hbuf), NULL, 0, niflag)) strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */ printf(" -> %s", hbuf); } (void) memset(&ifr6, 0, sizeof(ifr6)); (void) strncpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); ifr6.ifr_addr = creq->ifr_addr; if (ioctl(s, SIOCGIFNETMASK_IN6, &ifr6) == -1) { if (errno != EADDRNOTAVAIL) warn("SIOCGIFNETMASK_IN6"); } else { sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr; printf(" prefixlen %d", prefix(&sin6->sin6_addr, sizeof(struct in6_addr))); } (void) memset(&ifr6, 0, sizeof(ifr6)); (void) strncpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); ifr6.ifr_addr = creq->ifr_addr; if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == -1) { if (errno != EADDRNOTAVAIL) warn("SIOCGIFAFLAG_IN6"); } else { if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST) printf(" anycast"); if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE) printf(" tentative"); if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED) printf(" duplicated"); if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED) printf(" detached"); if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED) printf(" deprecated"); } if (scopeid) printf(" scopeid 0x%x", scopeid); if (Lflag) { struct in6_addrlifetime *lifetime; (void) memset(&ifr6, 0, sizeof(ifr6)); (void) strncpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); ifr6.ifr_addr = creq->ifr_addr; lifetime = &ifr6.ifr_ifru.ifru_lifetime; if (ioctl(s, SIOCGIFALIFETIME_IN6, &ifr6) == -1) { if (errno != EADDRNOTAVAIL) warn("SIOCGIFALIFETIME_IN6"); } else if (lifetime->ia6t_preferred || lifetime->ia6t_expire) { time_t t = time(NULL); printf(" pltime "); if (lifetime->ia6t_preferred) { printf("%s", lifetime->ia6t_preferred < t ? "0" : sec2str(lifetime->ia6t_preferred - t)); } else printf("infty"); printf(" vltime "); if (lifetime->ia6t_expire) { printf("%s", lifetime->ia6t_expire < t ? "0" : sec2str(lifetime->ia6t_expire - t)); } else printf("infty"); } } printf("\n"); } void in6_status(int force) { struct ifaddrs *ifap, *ifa; struct in6_ifreq isifr; if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (strcmp(name, ifa->ifa_name) != 0) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (sizeof(isifr.ifr_addr) < ifa->ifa_addr->sa_len) continue; memset(&isifr, 0, sizeof(isifr)); strncpy(isifr.ifr_name, ifa->ifa_name, sizeof(isifr.ifr_name)); memcpy(&isifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len); in6_alias(&isifr); } freeifaddrs(ifap); } #define SIN6(x) ((struct sockaddr_in6 *) &(x)) struct sockaddr_in6 *sin6tab[] = { SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr), SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)}; void in6_getaddr(const char *str, int which) { #if defined(__KAME__) && defined(KAME_SCOPEID) struct sockaddr_in6 *sin6 = sin6tab[which]; struct addrinfo hints, *res; int error; char *slash = NULL; if (which == ADDR) { if ((slash = strrchr(str, '/')) != NULL) *slash = '\0'; } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; #if 0 /* in_getaddr() allows FQDN */ hints.ai_flags = AI_NUMERICHOST; #endif error = getaddrinfo(str, "0", &hints, &res); if (error && slash) { /* try again treating the '/' as part of the name */ *slash = '/'; slash = NULL; error = getaddrinfo(str, "0", &hints, &res); } if (error) errx(EXIT_FAILURE, "%s: %s", str, gai_strerror(error)); if (res->ai_next) errx(EXIT_FAILURE, "%s: resolved to multiple addresses", str); if (res->ai_addrlen != sizeof(struct sockaddr_in6)) errx(EXIT_FAILURE, "%s: bad value", str); memcpy(sin6, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) && sin6->sin6_scope_id) { *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] = htons(sin6->sin6_scope_id); sin6->sin6_scope_id = 0; } if (slash) { in6_getprefix(slash + 1, MASK); explicit_prefix = 1; } #else struct sockaddr_in6 *gasin = sin6tab[which]; gasin->sin6_len = sizeof(*gasin); if (which != MASK) gasin->sin6_family = AF_INET6; if (which == ADDR) { char *p = NULL; if((p = strrchr(str, '/')) != NULL) { *p = '\0'; in6_getprefix(p + 1, MASK); explicit_prefix = 1; } } if (inet_pton(AF_INET6, str, &gasin->sin6_addr) != 1) errx(EXIT_FAILURE, "%s: bad value", str); #endif } void in6_getprefix(const char *plen, int which) { struct sockaddr_in6 *gpsin = sin6tab[which]; u_char *cp; int len = strtol(plen, (char **)NULL, 10); if ((len < 0) || (len > 128)) errx(EXIT_FAILURE, "%s: bad value", plen); gpsin->sin6_len = sizeof(*gpsin); if (which != MASK) gpsin->sin6_family = AF_INET6; if ((len == 0) || (len == 128)) { memset(&gpsin->sin6_addr, 0xff, sizeof(struct in6_addr)); return; } memset((void *)&gpsin->sin6_addr, 0x00, sizeof(gpsin->sin6_addr)); for (cp = (u_char *)&gpsin->sin6_addr; len > 7; len -= 8) *cp++ = 0xff; if (len) *cp = 0xff << (8 - len); } void in6_init(void) { in6_addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; in6_addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; }