if.c revision 55163
155163Sshin/* 255163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 355163Sshin * All rights reserved. 455163Sshin * 555163Sshin * Redistribution and use in source and binary forms, with or without 655163Sshin * modification, are permitted provided that the following conditions 755163Sshin * are met: 855163Sshin * 1. Redistributions of source code must retain the above copyright 955163Sshin * notice, this list of conditions and the following disclaimer. 1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer in the 1255163Sshin * documentation and/or other materials provided with the distribution. 1355163Sshin * 3. Neither the name of the project nor the names of its contributors 1455163Sshin * may be used to endorse or promote products derived from this software 1555163Sshin * without specific prior written permission. 1655163Sshin * 1755163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1855163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1955163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2055163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2155163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2255163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2355163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2455163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2555163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2655163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2755163Sshin * SUCH DAMAGE. 2855163Sshin * 2955163Sshin * $FreeBSD: head/usr.sbin/rtsold/if.c 55163 1999-12-28 02:37:14Z shin $ 3055163Sshin */ 3155163Sshin 3255163Sshin#include <sys/param.h> 3355163Sshin#include <sys/socket.h> 3455163Sshin#include <sys/sysctl.h> 3555163Sshin#include <sys/ioctl.h> 3655163Sshin 3755163Sshin#include <net/if.h> 3855163Sshin#if defined(__FreeBSD__) && __FreeBSD__ >= 3 3955163Sshin#include <net/if_var.h> 4055163Sshin#endif /* __FreeBSD__ >= 3 */ 4155163Sshin#include <net/if_types.h> 4255163Sshin#include <net/route.h> 4355163Sshin#include <net/if_dl.h> 4455163Sshin#include <net/if_media.h> 4555163Sshin#ifdef __FreeBSD__ 4655163Sshin# include <net/ethernet.h> 4755163Sshin#endif 4855163Sshin#ifdef __NetBSD__ 4955163Sshin#include <net/if_ether.h> 5055163Sshin#endif 5155163Sshin#if defined(__bsdi__) || defined(__OpenBSD__) 5255163Sshin# include <netinet/in.h> 5355163Sshin# include <netinet/if_ether.h> 5455163Sshin#endif 5555163Sshin#include <netinet/in.h> 5655163Sshin#include <netinet/icmp6.h> 5755163Sshin 5855163Sshin#include <netinet6/in6_var.h> 5955163Sshin 6055163Sshin#include <stdio.h> 6155163Sshin#include <unistd.h> 6255163Sshin#include <stdlib.h> 6355163Sshin#include <syslog.h> 6455163Sshin#include <string.h> 6555163Sshin#include <fcntl.h> 6655163Sshin#include <errno.h> 6755163Sshin#include <kvm.h> 6855163Sshin#include <nlist.h> 6955163Sshin#include <limits.h> 7055163Sshin 7155163Sshin#include "rtsold.h" 7255163Sshin 7355163Sshinstatic int ifsock; 7455163Sshin 7555163Sshinstatic int getifa __P((char *name, struct in6_ifaddr *ifap)); 7655163Sshinstatic void get_rtaddrs __P((int addrs, struct sockaddr *sa, 7755163Sshin struct sockaddr **rti_info)); 7855163Sshin 7955163Sshinint 8055163Sshinifinit() 8155163Sshin{ 8255163Sshin if ((ifsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 8355163Sshin warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno)); 8455163Sshin return(-1); 8555163Sshin } 8655163Sshin 8755163Sshin return(0); 8855163Sshin} 8955163Sshin 9055163Sshinint 9155163Sshininterface_up(char *name) 9255163Sshin{ 9355163Sshin struct ifreq ifr; 9455163Sshin struct in6_ifaddr ifa; 9555163Sshin 9655163Sshin strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); 9755163Sshin 9855163Sshin if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { 9955163Sshin warnmsg(LOG_WARNING, __FUNCTION__, "ioctl(SIOCGIFFLAGS): %s", 10055163Sshin strerror(errno)); 10155163Sshin return(-1); 10255163Sshin } 10355163Sshin if (!(ifr.ifr_flags & IFF_UP)) { 10455163Sshin ifr.ifr_flags |= IFF_UP; 10555163Sshin if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { 10655163Sshin warnmsg(LOG_ERR, __FUNCTION__, 10755163Sshin "ioctl(SIOCSIFFLAGS): %s", strerror(errno)); 10855163Sshin } 10955163Sshin return(-1); 11055163Sshin } 11155163Sshin 11255163Sshin warnmsg(LOG_DEBUG, __FUNCTION__, "checking if %s is ready...", name); 11355163Sshin 11455163Sshin if (getifa(name, &ifa) < 0) { 11555163Sshin warnmsg(LOG_WARNING, __FUNCTION__, 11655163Sshin "getifa() failed, anyway I'll try"); 11755163Sshin return 0; 11855163Sshin } 11955163Sshin 12055163Sshin if (!(ifa.ia6_flags & IN6_IFF_NOTREADY)) { 12155163Sshin warnmsg(LOG_DEBUG, __FUNCTION__, 12255163Sshin "%s is ready", name); 12355163Sshin return(0); 12455163Sshin } 12555163Sshin else { 12655163Sshin if (ifa.ia6_flags & IN6_IFF_TENTATIVE) { 12755163Sshin warnmsg(LOG_DEBUG, __FUNCTION__, "%s is tentative", 12855163Sshin name); 12955163Sshin return IFS_TENTATIVE; 13055163Sshin } 13155163Sshin if (ifa.ia6_flags & IN6_IFF_DUPLICATED) 13255163Sshin warnmsg(LOG_DEBUG, __FUNCTION__, "%s is duplicated", 13355163Sshin name); 13455163Sshin return -1; 13555163Sshin } 13655163Sshin} 13755163Sshin 13855163Sshinint 13955163Sshininterface_status(struct ifinfo *ifinfo) 14055163Sshin{ 14155163Sshin char *ifname = ifinfo->ifname; 14255163Sshin struct ifreq ifr; 14355163Sshin struct ifmediareq ifmr; 14455163Sshin 14555163Sshin /* get interface flags */ 14655163Sshin memset(&ifr, 0, sizeof(ifr)); 14755163Sshin strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 14855163Sshin if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { 14955163Sshin warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFFLAGS) on %s: %s", 15055163Sshin ifname, strerror(errno)); 15155163Sshin return(-1); 15255163Sshin } 15355163Sshin /* 15455163Sshin * if one of UP and RUNNING flags is dropped, 15555163Sshin * the interface is not active. 15655163Sshin */ 15755163Sshin if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 15855163Sshin goto inactive; 15955163Sshin } 16055163Sshin 16155163Sshin /* Next, check carrier on the interface, if possible */ 16255163Sshin if (!ifinfo->mediareqok) 16355163Sshin goto active; 16455163Sshin memset(&ifmr, 0, sizeof(ifmr)); 16555163Sshin strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 16655163Sshin 16755163Sshin if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { 16855163Sshin if (errno != EINVAL) { 16955163Sshin warnmsg(LOG_DEBUG, __FUNCTION__, 17055163Sshin "ioctl(SIOCGIFMEDIA) on %s: %s", 17155163Sshin ifname, strerror(errno)); 17255163Sshin return(-1); 17355163Sshin } 17455163Sshin /* 17555163Sshin * EINVAL simply means that the interface does not support 17655163Sshin * the SIOCGIFMEDIA ioctl. We regard it alive. 17755163Sshin */ 17855163Sshin ifinfo->mediareqok = 0; 17955163Sshin goto active; 18055163Sshin } 18155163Sshin 18255163Sshin if (ifmr.ifm_status & IFM_AVALID) { 18355163Sshin switch(ifmr.ifm_active & IFM_NMASK) { 18455163Sshin case IFM_ETHER: 18555163Sshin if (ifmr.ifm_status & IFM_ACTIVE) 18655163Sshin goto active; 18755163Sshin else 18855163Sshin goto inactive; 18955163Sshin break; 19055163Sshin default: 19155163Sshin goto inactive; 19255163Sshin } 19355163Sshin } 19455163Sshin 19555163Sshin inactive: 19655163Sshin return(0); 19755163Sshin 19855163Sshin active: 19955163Sshin return(1); 20055163Sshin} 20155163Sshin 20255163Sshin#define ROUNDUP(a, size) \ 20355163Sshin (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) 20455163Sshin 20555163Sshin#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ 20655163Sshin ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ 20755163Sshin sizeof(u_long)) :\ 20855163Sshin sizeof(u_long))) 20955163Sshin#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) 21055163Sshin 21155163Sshinint 21255163Sshinlladdropt_length(struct sockaddr_dl *sdl) 21355163Sshin{ 21455163Sshin switch(sdl->sdl_type) { 21555163Sshin case IFT_ETHER: 21655163Sshin return(ROUNDUP8(ETHER_ADDR_LEN + 2)); 21755163Sshin default: 21855163Sshin return(0); 21955163Sshin } 22055163Sshin} 22155163Sshin 22255163Sshinvoid 22355163Sshinlladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) 22455163Sshin{ 22555163Sshin char *addr; 22655163Sshin 22755163Sshin ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ 22855163Sshin 22955163Sshin switch(sdl->sdl_type) { 23055163Sshin case IFT_ETHER: 23155163Sshin ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; 23255163Sshin addr = (char *)(ndopt + 1); 23355163Sshin memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); 23455163Sshin break; 23555163Sshin default: 23655163Sshin warnmsg(LOG_ERR, __FUNCTION__, 23755163Sshin "unsupported link type(%d)", sdl->sdl_type); 23855163Sshin exit(1); 23955163Sshin } 24055163Sshin 24155163Sshin return; 24255163Sshin} 24355163Sshin 24455163Sshinstruct sockaddr_dl * 24555163Sshinif_nametosdl(char *name) 24655163Sshin{ 24755163Sshin int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; 24855163Sshin char *buf, *next, *lim; 24955163Sshin size_t len; 25055163Sshin struct if_msghdr *ifm; 25155163Sshin struct sockaddr *sa, *rti_info[RTAX_MAX]; 25255163Sshin struct sockaddr_dl *sdl = NULL, *ret_sdl; 25355163Sshin 25455163Sshin if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) 25555163Sshin return(NULL); 25655163Sshin if ((buf = malloc(len)) == NULL) 25755163Sshin return(NULL); 25855163Sshin if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 25955163Sshin free(buf); 26055163Sshin return(NULL); 26155163Sshin } 26255163Sshin 26355163Sshin lim = buf + len; 26455163Sshin for (next = buf; next < lim; next += ifm->ifm_msglen) { 26555163Sshin ifm = (struct if_msghdr *)next; 26655163Sshin if (ifm->ifm_type == RTM_IFINFO) { 26755163Sshin sa = (struct sockaddr *)(ifm + 1); 26855163Sshin get_rtaddrs(ifm->ifm_addrs, sa, rti_info); 26955163Sshin if ((sa = rti_info[RTAX_IFP]) != NULL) { 27055163Sshin if (sa->sa_family == AF_LINK) { 27155163Sshin sdl = (struct sockaddr_dl *)sa; 27255163Sshin if (strncmp(&sdl->sdl_data[0], 27355163Sshin name, 27455163Sshin sdl->sdl_nlen) == 0) { 27555163Sshin break; 27655163Sshin } 27755163Sshin } 27855163Sshin } 27955163Sshin } 28055163Sshin } 28155163Sshin if (next == lim) { 28255163Sshin /* search failed */ 28355163Sshin free(buf); 28455163Sshin return(NULL); 28555163Sshin } 28655163Sshin 28755163Sshin if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) 28855163Sshin return(NULL); 28955163Sshin memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); 29055163Sshin 29155163Sshin return(ret_sdl); 29255163Sshin} 29355163Sshin 29455163Sshinint 29555163Sshingetinet6sysctl(int code) 29655163Sshin{ 29755163Sshin int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; 29855163Sshin int value; 29955163Sshin size_t size; 30055163Sshin 30155163Sshin mib[3] = code; 30255163Sshin size = sizeof(value); 30355163Sshin if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0) 30455163Sshin return -1; 30555163Sshin else 30655163Sshin return value; 30755163Sshin} 30855163Sshin 30955163Sshin/*------------------------------------------------------------*/ 31055163Sshin 31155163Sshinstatic struct nlist nl[] = { 31255163Sshin#define N_IFNET 0 31355163Sshin { "_ifnet" }, 31455163Sshin { "" }, 31555163Sshin}; 31655163Sshin 31755163Sshin#define KREAD(x, y, z) { \ 31855163Sshin if (kvm_read(kvmd, (u_long)x, (void *)y, sizeof(z)) != sizeof(z)) { \ 31955163Sshin warnmsg(LOG_ERR, __FUNCTION__, "kvm_read failed"); \ 32055163Sshin goto bad; \ 32155163Sshin } \ 32255163Sshin } 32355163Sshin 32455163Sshinstatic int 32555163Sshingetifa(char *name, struct in6_ifaddr *ifap) 32655163Sshin{ 32755163Sshin u_short index; 32855163Sshin kvm_t *kvmd = NULL; 32955163Sshin char buf[_POSIX2_LINE_MAX]; 33055163Sshin struct ifnet *ifp; 33155163Sshin struct ifnet ifnet; 33255163Sshin struct in6_ifaddr *ifa; 33355163Sshin 33455163Sshin if (!ifap) 33555163Sshin exit(1); 33655163Sshin 33755163Sshin index = (u_short)if_nametoindex(name); 33855163Sshin if (index == 0) { 33955163Sshin warnmsg(LOG_ERR, __FUNCTION__, "if_nametoindex failed for %s", 34055163Sshin name); 34155163Sshin goto bad; 34255163Sshin } 34355163Sshin if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) { 34455163Sshin warnmsg(LOG_ERR, __FUNCTION__, "kvm_openfiles failed"); 34555163Sshin goto bad; 34655163Sshin } 34755163Sshin if (kvm_nlist(kvmd, nl) < 0) { 34855163Sshin warnmsg(LOG_ERR, __FUNCTION__, "kvm_nlist failed"); 34955163Sshin goto bad; 35055163Sshin } 35155163Sshin if (nl[N_IFNET].n_value == 0) { 35255163Sshin warnmsg(LOG_ERR, __FUNCTION__, "symbol \"%s\" not found", 35355163Sshin nl[N_IFNET].n_name); 35455163Sshin goto bad; 35555163Sshin } 35655163Sshin 35755163Sshin KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *); 35855163Sshin while (ifp) { 35955163Sshin KREAD(ifp, &ifnet, struct ifnet); 36055163Sshin if (ifnet.if_index == index) 36155163Sshin break; 36255163Sshin#if defined(__NetBSD__) || defined(__OpenBSD__) 36355163Sshin ifp = TAILQ_NEXT(&ifnet, if_list); 36455163Sshin#elif defined(__FreeBSD__) && __FreeBSD__ >= 3 36555163Sshin ifp = TAILQ_NEXT(&ifnet, if_link); 36655163Sshin#else 36755163Sshin ifp = ifnet.if_next; 36855163Sshin#endif 36955163Sshin } 37055163Sshin if (!ifp) { 37155163Sshin warnmsg(LOG_ERR, __FUNCTION__, "interface \"%s\" not found", 37255163Sshin name); 37355163Sshin goto bad; 37455163Sshin } 37555163Sshin 37655163Sshin#if defined(__NetBSD__) || defined(__OpenBSD__) 37755163Sshin ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrlist); 37855163Sshin#elif defined(__FreeBSD__) && __FreeBSD__ >= 3 37955163Sshin ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrhead); 38055163Sshin#else 38155163Sshin ifa = (struct in6_ifaddr *)ifnet.if_addrlist; 38255163Sshin#endif 38355163Sshin while (ifa) { 38455163Sshin KREAD(ifa, ifap, *ifap); 38555163Sshin if (ifap->ia_addr.sin6_family == AF_INET6 38655163Sshin && IN6_IS_ADDR_LINKLOCAL(&ifap->ia_addr.sin6_addr)) { 38755163Sshin kvm_close(kvmd); 38855163Sshin return 0; 38955163Sshin } 39055163Sshin 39155163Sshin#if defined(__NetBSD__) || defined(__OpenBSD__) 39255163Sshin ifa = (struct in6_ifaddr *) 39355163Sshin TAILQ_NEXT((struct ifaddr *)ifap, ifa_list); 39455163Sshin#elif defined(__FreeBSD__) && __FreeBSD__ >= 3 39555163Sshin ifa = (struct in6_ifaddr *) 39655163Sshin TAILQ_NEXT((struct ifaddr *)ifap, ifa_link); 39755163Sshin#else 39855163Sshin ifa = (struct in6_ifaddr *)(((struct ifaddr *)ifap)->ifa_next); 39955163Sshin#endif 40055163Sshin } 40155163Sshin warnmsg(LOG_ERR, __FUNCTION__, "no IPv6 link-local address for %s", 40255163Sshin name); 40355163Sshin 40455163Sshin bad: 40555163Sshin if (kvmd) 40655163Sshin kvm_close(kvmd); 40755163Sshin return -1; 40855163Sshin} 40955163Sshin 41055163Sshinstatic void 41155163Sshinget_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 41255163Sshin{ 41355163Sshin int i; 41455163Sshin 41555163Sshin for (i = 0; i < RTAX_MAX; i++) { 41655163Sshin if (addrs & (1 << i)) { 41755163Sshin rti_info[i] = sa; 41855163Sshin NEXT_SA(sa); 41955163Sshin } 42055163Sshin else 42155163Sshin rti_info[i] = NULL; 42255163Sshin } 42355163Sshin} 424