1124524Sume/* $KAME: if.c,v 1.27 2003/10/05 00:09:36 itojun Exp $ */ 266776Skris 355163Sshin/* 455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 555163Sshin * All rights reserved. 662632Skris * 755163Sshin * Redistribution and use in source and binary forms, with or without 855163Sshin * modification, are permitted provided that the following conditions 955163Sshin * are met: 1055163Sshin * 1. Redistributions of source code must retain the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer. 1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1355163Sshin * notice, this list of conditions and the following disclaimer in the 1455163Sshin * documentation and/or other materials provided with the distribution. 1555163Sshin * 3. Neither the name of the project nor the names of its contributors 1655163Sshin * may be used to endorse or promote products derived from this software 1755163Sshin * without specific prior written permission. 1862632Skris * 1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2255163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2955163Sshin * SUCH DAMAGE. 3055163Sshin * 3155163Sshin * $FreeBSD$ 3255163Sshin */ 3355163Sshin 3455163Sshin#include <sys/param.h> 3555163Sshin#include <sys/socket.h> 3655163Sshin#include <sys/sysctl.h> 3755163Sshin#include <sys/ioctl.h> 3866776Skris#include <sys/queue.h> 3955163Sshin 4055163Sshin#include <net/if.h> 4155163Sshin#include <net/if_var.h> 4255163Sshin#include <net/if_types.h> 4355163Sshin#include <net/route.h> 4455163Sshin#include <net/if_dl.h> 4555163Sshin#include <net/if_media.h> 46118787Sume#include <net/ethernet.h> 4755163Sshin#include <netinet/in.h> 4855163Sshin#include <netinet/icmp6.h> 4955163Sshin 5055163Sshin#include <netinet6/in6_var.h> 51197141Shrs#include <netinet6/nd6.h> 5255163Sshin 5355163Sshin#include <stdio.h> 5455163Sshin#include <unistd.h> 5555163Sshin#include <stdlib.h> 5655163Sshin#include <syslog.h> 5755163Sshin#include <string.h> 5855163Sshin#include <fcntl.h> 5955163Sshin#include <errno.h> 6055163Sshin#include <limits.h> 6162632Skris#include <ifaddrs.h> 6255163Sshin#include "rtsold.h" 6355163Sshin 6455163Sshinstatic int ifsock; 6555163Sshin 66173412Skevlostatic int get_llflag(const char *); 67173412Skevlostatic void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 6855163Sshin 6955163Sshinint 70124524Sumeifinit(void) 7155163Sshin{ 7262632Skris ifsock = rssock; 7355163Sshin 7455163Sshin return(0); 7555163Sshin} 7655163Sshin 7755163Sshinint 7855163Sshininterface_up(char *name) 7955163Sshin{ 8055163Sshin struct ifreq ifr; 81197141Shrs struct in6_ndireq nd; 8262632Skris int llflag; 83197141Shrs int s; 8455163Sshin 85197141Shrs memset(&ifr, 0, sizeof(ifr)); 86119027Sume strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); 87197141Shrs memset(&nd, 0, sizeof(nd)); 88197141Shrs strlcpy(nd.ifname, name, sizeof(nd.ifname)); 8955163Sshin 9055163Sshin if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { 91118660Sume warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFFLAGS): %s", 92118664Sume strerror(errno)); 93222732Shrs return (-1); 9455163Sshin } 9555163Sshin if (!(ifr.ifr_flags & IFF_UP)) { 9655163Sshin ifr.ifr_flags |= IFF_UP; 97118664Sume if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) 98118660Sume warnmsg(LOG_ERR, __func__, 99118664Sume "ioctl(SIOCSIFFLAGS): %s", strerror(errno)); 100222732Shrs return (-1); 10155163Sshin } 102197141Shrs if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 103197141Shrs warnmsg(LOG_WARNING, __func__, "socket(AF_INET6, SOCK_DGRAM): %s", 104197141Shrs strerror(errno)); 105222732Shrs return (-1); 106197141Shrs } 107197141Shrs if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { 108197141Shrs warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFINFO_IN6): %s", 109197141Shrs strerror(errno)); 110197141Shrs close(s); 111222732Shrs return (-1); 112197141Shrs } 11355163Sshin 114118660Sume warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name); 11555163Sshin 116197141Shrs if (nd.ndi.flags & ND6_IFF_IFDISABLED) { 117197141Shrs if (Fflag) { 118197141Shrs nd.ndi.flags &= ~ND6_IFF_IFDISABLED; 119197141Shrs if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) { 120197141Shrs warnmsg(LOG_WARNING, __func__, 121197141Shrs "ioctl(SIOCSIFINFO_IN6): %s", 122197141Shrs strerror(errno)); 123197141Shrs close(s); 124222732Shrs return (-1); 125197141Shrs } 126197141Shrs } else { 127197141Shrs warnmsg(LOG_WARNING, __func__, 128197141Shrs "%s is disabled.", name); 129197141Shrs close(s); 130222732Shrs return (-1); 131197141Shrs } 132197141Shrs } 133197141Shrs if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) { 134197141Shrs if (Fflag) { 135197141Shrs nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV; 136197141Shrs if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) { 137197141Shrs warnmsg(LOG_WARNING, __func__, 138197141Shrs "ioctl(SIOCSIFINFO_IN6): %s", 139197141Shrs strerror(errno)); 140197141Shrs close(s); 141222732Shrs return (-1); 142197141Shrs } 143197141Shrs } else { 144197141Shrs warnmsg(LOG_WARNING, __func__, 145197141Shrs "%s does not accept Router Advertisement.", name); 146197141Shrs close(s); 147222732Shrs return (-1); 148197141Shrs } 149197141Shrs } 150197141Shrs close(s); 151197141Shrs 15262632Skris llflag = get_llflag(name); 15362632Skris if (llflag < 0) { 154118660Sume warnmsg(LOG_WARNING, __func__, 155118664Sume "get_llflag() failed, anyway I'll try"); 156222732Shrs return (0); 15755163Sshin } 15855163Sshin 15962632Skris if (!(llflag & IN6_IFF_NOTREADY)) { 160118664Sume warnmsg(LOG_DEBUG, __func__, "%s is ready", name); 161222732Shrs return (0); 16262632Skris } else { 16362632Skris if (llflag & IN6_IFF_TENTATIVE) { 164118660Sume warnmsg(LOG_DEBUG, __func__, "%s is tentative", 165118664Sume name); 166222732Shrs return (IFS_TENTATIVE); 16755163Sshin } 16862632Skris if (llflag & IN6_IFF_DUPLICATED) 169118660Sume warnmsg(LOG_DEBUG, __func__, "%s is duplicated", 170118664Sume name); 171222732Shrs return (-1); 17255163Sshin } 17355163Sshin} 17455163Sshin 17555163Sshinint 17655163Sshininterface_status(struct ifinfo *ifinfo) 17755163Sshin{ 17855163Sshin char *ifname = ifinfo->ifname; 17955163Sshin struct ifreq ifr; 18055163Sshin struct ifmediareq ifmr; 181118664Sume 18255163Sshin /* get interface flags */ 18355163Sshin memset(&ifr, 0, sizeof(ifr)); 184119027Sume strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 18555163Sshin if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { 186118660Sume warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFFLAGS) on %s: %s", 187118664Sume ifname, strerror(errno)); 188222732Shrs return (-1); 18955163Sshin } 19055163Sshin /* 19155163Sshin * if one of UP and RUNNING flags is dropped, 19255163Sshin * the interface is not active. 19355163Sshin */ 194222732Shrs if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 19555163Sshin goto inactive; 19655163Sshin /* Next, check carrier on the interface, if possible */ 19755163Sshin if (!ifinfo->mediareqok) 19855163Sshin goto active; 19955163Sshin memset(&ifmr, 0, sizeof(ifmr)); 200119027Sume strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 20155163Sshin 20255163Sshin if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { 20355163Sshin if (errno != EINVAL) { 204118660Sume warnmsg(LOG_DEBUG, __func__, 205118664Sume "ioctl(SIOCGIFMEDIA) on %s: %s", 206118664Sume ifname, strerror(errno)); 20755163Sshin return(-1); 20855163Sshin } 20955163Sshin /* 21055163Sshin * EINVAL simply means that the interface does not support 21155163Sshin * the SIOCGIFMEDIA ioctl. We regard it alive. 21255163Sshin */ 21355163Sshin ifinfo->mediareqok = 0; 21455163Sshin goto active; 21555163Sshin } 21655163Sshin 21755163Sshin if (ifmr.ifm_status & IFM_AVALID) { 218118664Sume switch (ifmr.ifm_active & IFM_NMASK) { 219118664Sume case IFM_ETHER: 220118998Sume case IFM_IEEE80211: 221118664Sume if (ifmr.ifm_status & IFM_ACTIVE) 222118664Sume goto active; 223118664Sume else 224118664Sume goto inactive; 225118664Sume break; 226118664Sume default: 227118664Sume goto inactive; 22855163Sshin } 22955163Sshin } 23055163Sshin 23155163Sshin inactive: 232222732Shrs return (0); 23355163Sshin 23455163Sshin active: 235222732Shrs return (1); 23655163Sshin} 23755163Sshin 23862632Skris#define ROUNDUP(a, size) \ 23955163Sshin (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) 24055163Sshin 24162632Skris#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ 24255163Sshin ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ 243118664Sume sizeof(u_long)) : sizeof(u_long))) 24462632Skris#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) 24555163Sshin 24655163Sshinint 24755163Sshinlladdropt_length(struct sockaddr_dl *sdl) 24855163Sshin{ 249118664Sume switch (sdl->sdl_type) { 250118664Sume case IFT_ETHER: 25178064Sume#ifdef IFT_IEEE80211 25278064Sume case IFT_IEEE80211: 25378064Sume#endif 254222732Shrs return (ROUNDUP8(ETHER_ADDR_LEN + 2)); 255118664Sume default: 256222732Shrs return (0); 25755163Sshin } 25855163Sshin} 25955163Sshin 26055163Sshinvoid 26155163Sshinlladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) 26255163Sshin{ 26355163Sshin char *addr; 26455163Sshin 26555163Sshin ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ 26655163Sshin 267118664Sume switch (sdl->sdl_type) { 268118664Sume case IFT_ETHER: 26978064Sume#ifdef IFT_IEEE80211 27078064Sume case IFT_IEEE80211: 27178064Sume#endif 272118664Sume ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; 273118664Sume addr = (char *)(ndopt + 1); 274118664Sume memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); 275118664Sume break; 276118664Sume default: 277118664Sume warnmsg(LOG_ERR, __func__, 278118664Sume "unsupported link type(%d)", sdl->sdl_type); 279118664Sume exit(1); 28055163Sshin } 28155163Sshin 28255163Sshin return; 28355163Sshin} 28455163Sshin 28555163Sshinstruct sockaddr_dl * 28655163Sshinif_nametosdl(char *name) 28755163Sshin{ 28855163Sshin int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; 28955163Sshin char *buf, *next, *lim; 29055163Sshin size_t len; 29155163Sshin struct if_msghdr *ifm; 29255163Sshin struct sockaddr *sa, *rti_info[RTAX_MAX]; 29355163Sshin struct sockaddr_dl *sdl = NULL, *ret_sdl; 29455163Sshin 29555163Sshin if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) 29655163Sshin return(NULL); 29755163Sshin if ((buf = malloc(len)) == NULL) 29855163Sshin return(NULL); 29955163Sshin if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 30055163Sshin free(buf); 301222732Shrs return (NULL); 30255163Sshin } 30355163Sshin 30455163Sshin lim = buf + len; 30555163Sshin for (next = buf; next < lim; next += ifm->ifm_msglen) { 306254462Shrs ifm = (struct if_msghdr *)(void *)next; 30755163Sshin if (ifm->ifm_type == RTM_IFINFO) { 30855163Sshin sa = (struct sockaddr *)(ifm + 1); 30955163Sshin get_rtaddrs(ifm->ifm_addrs, sa, rti_info); 31055163Sshin if ((sa = rti_info[RTAX_IFP]) != NULL) { 31155163Sshin if (sa->sa_family == AF_LINK) { 312254462Shrs sdl = (struct sockaddr_dl *)(void *)sa; 31362632Skris if (strlen(name) != sdl->sdl_nlen) 31462632Skris continue; /* not same len */ 31555163Sshin if (strncmp(&sdl->sdl_data[0], 31655163Sshin name, 31755163Sshin sdl->sdl_nlen) == 0) { 31855163Sshin break; 31955163Sshin } 32055163Sshin } 32155163Sshin } 32255163Sshin } 32355163Sshin } 32455163Sshin if (next == lim) { 32555163Sshin /* search failed */ 32655163Sshin free(buf); 327222732Shrs return (NULL); 32855163Sshin } 32955163Sshin 330157108Ssuz if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) { 331157108Ssuz free(buf); 332222732Shrs return (NULL); 333157108Ssuz } 33455163Sshin memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); 33555163Sshin 33678064Sume free(buf); 337222732Shrs return (ret_sdl); 33855163Sshin} 33955163Sshin 34055163Sshinint 34155163Sshingetinet6sysctl(int code) 34255163Sshin{ 34355163Sshin int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; 34455163Sshin int value; 34555163Sshin size_t size; 34655163Sshin 34755163Sshin mib[3] = code; 34855163Sshin size = sizeof(value); 34955163Sshin if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0) 350222732Shrs return (-1); 35155163Sshin else 352222732Shrs return (value); 35355163Sshin} 35455163Sshin 355124525Sumeint 356124525Sumesetinet6sysctl(int code, int newval) 357124525Sume{ 358124525Sume int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; 359124525Sume int value; 360124525Sume size_t size; 361124525Sume 362124525Sume mib[3] = code; 363124525Sume size = sizeof(value); 364124525Sume if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, 365124525Sume &newval, sizeof(newval)) < 0) 366222732Shrs return (-1); 367124525Sume else 368222732Shrs return (value); 369124525Sume} 370124525Sume 37155163Sshin/*------------------------------------------------------------*/ 37255163Sshin 37362632Skris/* get ia6_flags for link-local addr on if. returns -1 on error. */ 37455163Sshinstatic int 37562632Skrisget_llflag(const char *name) 37655163Sshin{ 37762632Skris struct ifaddrs *ifap, *ifa; 37862632Skris struct in6_ifreq ifr6; 37962632Skris struct sockaddr_in6 *sin6; 38062632Skris int s; 38155163Sshin 38262632Skris if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) { 383118660Sume warnmsg(LOG_ERR, __func__, "socket(SOCK_DGRAM): %s", 38462632Skris strerror(errno)); 38555163Sshin exit(1); 38655163Sshin } 38762632Skris if (getifaddrs(&ifap) != 0) { 388118664Sume warnmsg(LOG_ERR, __func__, "getifaddrs: %s", 38962632Skris strerror(errno)); 39062632Skris exit(1); 39155163Sshin } 39262632Skris 39362632Skris for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 394118664Sume if (strlen(ifa->ifa_name) != strlen(name) || 395118664Sume strncmp(ifa->ifa_name, name, strlen(name)) != 0) 39662632Skris continue; 39762632Skris if (ifa->ifa_addr->sa_family != AF_INET6) 39862632Skris continue; 399254462Shrs sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr; 40062632Skris if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) 40162632Skris continue; 40262632Skris 40362632Skris memset(&ifr6, 0, sizeof(ifr6)); 404119027Sume strncpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); 40562632Skris memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len); 40662632Skris if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) { 407118660Sume warnmsg(LOG_ERR, __func__, 40862632Skris "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno)); 40962632Skris exit(1); 41062632Skris } 41162632Skris 41262632Skris freeifaddrs(ifap); 41362632Skris close(s); 414222732Shrs return (ifr6.ifr_ifru.ifru_flags6); 41555163Sshin } 41662632Skris 41762632Skris freeifaddrs(ifap); 41862632Skris close(s); 419222732Shrs return (-1); 42055163Sshin} 42155163Sshin 422118664Sume 42355163Sshinstatic void 42455163Sshinget_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 42555163Sshin{ 42655163Sshin int i; 427118664Sume 42855163Sshin for (i = 0; i < RTAX_MAX; i++) { 42955163Sshin if (addrs & (1 << i)) { 43055163Sshin rti_info[i] = sa; 43155163Sshin NEXT_SA(sa); 432118664Sume } else 43355163Sshin rti_info[i] = NULL; 43455163Sshin } 43555163Sshin} 436