1124524Sume/* $KAME: if.c,v 1.27 2003/10/05 00:09:36 itojun Exp $ */ 266776Skris 3331722Seadler/* 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_types.h> 4255163Sshin#include <net/route.h> 4355163Sshin#include <net/if_dl.h> 4455163Sshin#include <net/if_media.h> 45118787Sume#include <net/ethernet.h> 4655163Sshin#include <netinet/in.h> 4755163Sshin#include <netinet/icmp6.h> 4855163Sshin 4955163Sshin#include <netinet6/in6_var.h> 50197141Shrs#include <netinet6/nd6.h> 5155163Sshin 5255163Sshin#include <stdio.h> 5355163Sshin#include <unistd.h> 5455163Sshin#include <stdlib.h> 5555163Sshin#include <syslog.h> 5655163Sshin#include <string.h> 5755163Sshin#include <fcntl.h> 5855163Sshin#include <errno.h> 5955163Sshin#include <limits.h> 6062632Skris#include <ifaddrs.h> 6155163Sshin#include "rtsold.h" 6255163Sshin 6355163Sshinstatic int ifsock; 6455163Sshin 65173412Skevlostatic int get_llflag(const char *); 66173412Skevlostatic void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 6755163Sshin 6855163Sshinint 69124524Sumeifinit(void) 7055163Sshin{ 7162632Skris ifsock = rssock; 7255163Sshin 7355163Sshin return(0); 7455163Sshin} 7555163Sshin 7655163Sshinint 7755163Sshininterface_up(char *name) 7855163Sshin{ 7955163Sshin struct ifreq ifr; 80197141Shrs struct in6_ndireq nd; 8162632Skris int llflag; 82197141Shrs int s; 8355163Sshin 84197141Shrs memset(&ifr, 0, sizeof(ifr)); 85299868Struckman strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); 86197141Shrs memset(&nd, 0, sizeof(nd)); 87197141Shrs strlcpy(nd.ifname, name, sizeof(nd.ifname)); 8855163Sshin 8955163Sshin if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { 90118660Sume warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFFLAGS): %s", 91118664Sume strerror(errno)); 92222732Shrs return (-1); 9355163Sshin } 9455163Sshin if (!(ifr.ifr_flags & IFF_UP)) { 9555163Sshin ifr.ifr_flags |= IFF_UP; 96118664Sume if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) 97118660Sume warnmsg(LOG_ERR, __func__, 98118664Sume "ioctl(SIOCSIFFLAGS): %s", strerror(errno)); 99222732Shrs return (-1); 10055163Sshin } 101197141Shrs if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 102197141Shrs warnmsg(LOG_WARNING, __func__, "socket(AF_INET6, SOCK_DGRAM): %s", 103197141Shrs strerror(errno)); 104222732Shrs return (-1); 105197141Shrs } 106197141Shrs if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { 107197141Shrs warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFINFO_IN6): %s", 108197141Shrs strerror(errno)); 109197141Shrs close(s); 110222732Shrs return (-1); 111197141Shrs } 11255163Sshin 113118660Sume warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name); 11455163Sshin 115197141Shrs if (nd.ndi.flags & ND6_IFF_IFDISABLED) { 116197141Shrs if (Fflag) { 117197141Shrs nd.ndi.flags &= ~ND6_IFF_IFDISABLED; 118197141Shrs if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) { 119197141Shrs warnmsg(LOG_WARNING, __func__, 120197141Shrs "ioctl(SIOCSIFINFO_IN6): %s", 121197141Shrs strerror(errno)); 122197141Shrs close(s); 123222732Shrs return (-1); 124197141Shrs } 125197141Shrs } else { 126197141Shrs warnmsg(LOG_WARNING, __func__, 127197141Shrs "%s is disabled.", name); 128197141Shrs close(s); 129222732Shrs return (-1); 130197141Shrs } 131197141Shrs } 132197141Shrs if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) { 133197141Shrs if (Fflag) { 134197141Shrs nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV; 135197141Shrs if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) { 136197141Shrs warnmsg(LOG_WARNING, __func__, 137197141Shrs "ioctl(SIOCSIFINFO_IN6): %s", 138197141Shrs strerror(errno)); 139197141Shrs close(s); 140222732Shrs return (-1); 141197141Shrs } 142197141Shrs } else { 143197141Shrs warnmsg(LOG_WARNING, __func__, 144197141Shrs "%s does not accept Router Advertisement.", name); 145197141Shrs close(s); 146222732Shrs return (-1); 147197141Shrs } 148197141Shrs } 149197141Shrs close(s); 150197141Shrs 15162632Skris llflag = get_llflag(name); 15262632Skris if (llflag < 0) { 153118660Sume warnmsg(LOG_WARNING, __func__, 154118664Sume "get_llflag() failed, anyway I'll try"); 155222732Shrs return (0); 15655163Sshin } 15755163Sshin 15862632Skris if (!(llflag & IN6_IFF_NOTREADY)) { 159118664Sume warnmsg(LOG_DEBUG, __func__, "%s is ready", name); 160222732Shrs return (0); 16162632Skris } else { 16262632Skris if (llflag & IN6_IFF_TENTATIVE) { 163118660Sume warnmsg(LOG_DEBUG, __func__, "%s is tentative", 164118664Sume name); 165222732Shrs return (IFS_TENTATIVE); 16655163Sshin } 16762632Skris if (llflag & IN6_IFF_DUPLICATED) 168118660Sume warnmsg(LOG_DEBUG, __func__, "%s is duplicated", 169118664Sume name); 170222732Shrs return (-1); 17155163Sshin } 17255163Sshin} 17355163Sshin 17455163Sshinint 17555163Sshininterface_status(struct ifinfo *ifinfo) 17655163Sshin{ 17755163Sshin char *ifname = ifinfo->ifname; 17855163Sshin struct ifreq ifr; 17955163Sshin struct ifmediareq ifmr; 180118664Sume 18155163Sshin /* get interface flags */ 18255163Sshin memset(&ifr, 0, sizeof(ifr)); 183299868Struckman strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 18455163Sshin if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { 185118660Sume warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFFLAGS) on %s: %s", 186118664Sume ifname, strerror(errno)); 187222732Shrs return (-1); 18855163Sshin } 18955163Sshin /* 19055163Sshin * if one of UP and RUNNING flags is dropped, 19155163Sshin * the interface is not active. 19255163Sshin */ 193222732Shrs if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 19455163Sshin goto inactive; 19555163Sshin /* Next, check carrier on the interface, if possible */ 19655163Sshin if (!ifinfo->mediareqok) 19755163Sshin goto active; 19855163Sshin memset(&ifmr, 0, sizeof(ifmr)); 199299868Struckman strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 20055163Sshin 20155163Sshin if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { 20255163Sshin if (errno != EINVAL) { 203118660Sume warnmsg(LOG_DEBUG, __func__, 204118664Sume "ioctl(SIOCGIFMEDIA) on %s: %s", 205118664Sume ifname, strerror(errno)); 20655163Sshin return(-1); 20755163Sshin } 20855163Sshin /* 20955163Sshin * EINVAL simply means that the interface does not support 21055163Sshin * the SIOCGIFMEDIA ioctl. We regard it alive. 21155163Sshin */ 21255163Sshin ifinfo->mediareqok = 0; 21355163Sshin goto active; 21455163Sshin } 21555163Sshin 21655163Sshin if (ifmr.ifm_status & IFM_AVALID) { 217118664Sume switch (ifmr.ifm_active & IFM_NMASK) { 218118664Sume case IFM_ETHER: 219118998Sume case IFM_IEEE80211: 220118664Sume if (ifmr.ifm_status & IFM_ACTIVE) 221118664Sume goto active; 222118664Sume else 223118664Sume goto inactive; 224118664Sume break; 225118664Sume default: 226118664Sume goto inactive; 22755163Sshin } 22855163Sshin } 22955163Sshin 23055163Sshin inactive: 231222732Shrs return (0); 23255163Sshin 23355163Sshin active: 234222732Shrs return (1); 23555163Sshin} 23655163Sshin 23762632Skris#define ROUNDUP(a, size) \ 23855163Sshin (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) 23955163Sshin 24062632Skris#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ 24155163Sshin ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ 242118664Sume sizeof(u_long)) : sizeof(u_long))) 24362632Skris#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) 24455163Sshin 24555163Sshinint 24655163Sshinlladdropt_length(struct sockaddr_dl *sdl) 24755163Sshin{ 248118664Sume switch (sdl->sdl_type) { 249118664Sume case IFT_ETHER: 25078064Sume case IFT_IEEE80211: 251222732Shrs return (ROUNDUP8(ETHER_ADDR_LEN + 2)); 252118664Sume default: 253222732Shrs return (0); 25455163Sshin } 25555163Sshin} 25655163Sshin 25755163Sshinvoid 25855163Sshinlladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) 25955163Sshin{ 26055163Sshin char *addr; 26155163Sshin 26255163Sshin ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ 26355163Sshin 264118664Sume switch (sdl->sdl_type) { 265118664Sume case IFT_ETHER: 26678064Sume case IFT_IEEE80211: 267118664Sume ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; 268118664Sume addr = (char *)(ndopt + 1); 269118664Sume memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); 270118664Sume break; 271118664Sume default: 272118664Sume warnmsg(LOG_ERR, __func__, 273118664Sume "unsupported link type(%d)", sdl->sdl_type); 274118664Sume exit(1); 27555163Sshin } 27655163Sshin 27755163Sshin return; 27855163Sshin} 27955163Sshin 28055163Sshinstruct sockaddr_dl * 28155163Sshinif_nametosdl(char *name) 28255163Sshin{ 283287612Shrs int mib[] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; 28455163Sshin char *buf, *next, *lim; 28555163Sshin size_t len; 28655163Sshin struct if_msghdr *ifm; 28755163Sshin struct sockaddr *sa, *rti_info[RTAX_MAX]; 28855163Sshin struct sockaddr_dl *sdl = NULL, *ret_sdl; 28955163Sshin 290287612Shrs if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) < 0) 29155163Sshin return(NULL); 29255163Sshin if ((buf = malloc(len)) == NULL) 29355163Sshin return(NULL); 294287612Shrs if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) < 0) { 29555163Sshin free(buf); 296222732Shrs return (NULL); 29755163Sshin } 29855163Sshin 29955163Sshin lim = buf + len; 30055163Sshin for (next = buf; next < lim; next += ifm->ifm_msglen) { 301254462Shrs ifm = (struct if_msghdr *)(void *)next; 30255163Sshin if (ifm->ifm_type == RTM_IFINFO) { 30355163Sshin sa = (struct sockaddr *)(ifm + 1); 30455163Sshin get_rtaddrs(ifm->ifm_addrs, sa, rti_info); 30555163Sshin if ((sa = rti_info[RTAX_IFP]) != NULL) { 30655163Sshin if (sa->sa_family == AF_LINK) { 307254462Shrs sdl = (struct sockaddr_dl *)(void *)sa; 30862632Skris if (strlen(name) != sdl->sdl_nlen) 30962632Skris continue; /* not same len */ 31055163Sshin if (strncmp(&sdl->sdl_data[0], 31155163Sshin name, 31255163Sshin sdl->sdl_nlen) == 0) { 31355163Sshin break; 31455163Sshin } 31555163Sshin } 31655163Sshin } 31755163Sshin } 31855163Sshin } 31955163Sshin if (next == lim) { 32055163Sshin /* search failed */ 32155163Sshin free(buf); 322222732Shrs return (NULL); 32355163Sshin } 32455163Sshin 325157108Ssuz if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) { 326157108Ssuz free(buf); 327222732Shrs return (NULL); 328157108Ssuz } 32955163Sshin memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); 33055163Sshin 33178064Sume free(buf); 332222732Shrs return (ret_sdl); 33355163Sshin} 33455163Sshin 33555163Sshinint 33655163Sshingetinet6sysctl(int code) 33755163Sshin{ 33855163Sshin int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; 33955163Sshin int value; 34055163Sshin size_t size; 34155163Sshin 34255163Sshin mib[3] = code; 34355163Sshin size = sizeof(value); 344287612Shrs if (sysctl(mib, nitems(mib), &value, &size, NULL, 0) < 0) 345222732Shrs return (-1); 34655163Sshin else 347222732Shrs return (value); 34855163Sshin} 34955163Sshin 350124525Sumeint 351124525Sumesetinet6sysctl(int code, int newval) 352124525Sume{ 353124525Sume int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; 354124525Sume int value; 355124525Sume size_t size; 356124525Sume 357124525Sume mib[3] = code; 358124525Sume size = sizeof(value); 359287612Shrs if (sysctl(mib, nitems(mib), &value, &size, 360124525Sume &newval, sizeof(newval)) < 0) 361222732Shrs return (-1); 362124525Sume else 363222732Shrs return (value); 364124525Sume} 365124525Sume 36655163Sshin/*------------------------------------------------------------*/ 36755163Sshin 36862632Skris/* get ia6_flags for link-local addr on if. returns -1 on error. */ 36955163Sshinstatic int 37062632Skrisget_llflag(const char *name) 37155163Sshin{ 37262632Skris struct ifaddrs *ifap, *ifa; 37362632Skris struct in6_ifreq ifr6; 37462632Skris struct sockaddr_in6 *sin6; 37562632Skris int s; 37655163Sshin 37762632Skris if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) { 378118660Sume warnmsg(LOG_ERR, __func__, "socket(SOCK_DGRAM): %s", 37962632Skris strerror(errno)); 38055163Sshin exit(1); 38155163Sshin } 38262632Skris if (getifaddrs(&ifap) != 0) { 383118664Sume warnmsg(LOG_ERR, __func__, "getifaddrs: %s", 38462632Skris strerror(errno)); 38562632Skris exit(1); 38655163Sshin } 38762632Skris 38862632Skris for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 389118664Sume if (strlen(ifa->ifa_name) != strlen(name) || 390118664Sume strncmp(ifa->ifa_name, name, strlen(name)) != 0) 39162632Skris continue; 39262632Skris if (ifa->ifa_addr->sa_family != AF_INET6) 39362632Skris continue; 394254462Shrs sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr; 39562632Skris if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) 39662632Skris continue; 39762632Skris 39862632Skris memset(&ifr6, 0, sizeof(ifr6)); 399299868Struckman strlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); 40062632Skris memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len); 40162632Skris if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) { 402118660Sume warnmsg(LOG_ERR, __func__, 40362632Skris "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno)); 40462632Skris exit(1); 40562632Skris } 40662632Skris 40762632Skris freeifaddrs(ifap); 40862632Skris close(s); 409222732Shrs return (ifr6.ifr_ifru.ifru_flags6); 41055163Sshin } 41162632Skris 41262632Skris freeifaddrs(ifap); 41362632Skris close(s); 414222732Shrs return (-1); 41555163Sshin} 41655163Sshin 417118664Sume 41855163Sshinstatic void 41955163Sshinget_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 42055163Sshin{ 42155163Sshin int i; 422118664Sume 42355163Sshin for (i = 0; i < RTAX_MAX; i++) { 42455163Sshin if (addrs & (1 << i)) { 42555163Sshin rti_info[i] = sa; 42655163Sshin NEXT_SA(sa); 427118664Sume } else 42855163Sshin rti_info[i] = NULL; 42955163Sshin } 43055163Sshin} 431