if.c revision 55163
1/* 2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the project nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: head/usr.sbin/rtsold/if.c 55163 1999-12-28 02:37:14Z shin $ 30 */ 31 32#include <sys/param.h> 33#include <sys/socket.h> 34#include <sys/sysctl.h> 35#include <sys/ioctl.h> 36 37#include <net/if.h> 38#if defined(__FreeBSD__) && __FreeBSD__ >= 3 39#include <net/if_var.h> 40#endif /* __FreeBSD__ >= 3 */ 41#include <net/if_types.h> 42#include <net/route.h> 43#include <net/if_dl.h> 44#include <net/if_media.h> 45#ifdef __FreeBSD__ 46# include <net/ethernet.h> 47#endif 48#ifdef __NetBSD__ 49#include <net/if_ether.h> 50#endif 51#if defined(__bsdi__) || defined(__OpenBSD__) 52# include <netinet/in.h> 53# include <netinet/if_ether.h> 54#endif 55#include <netinet/in.h> 56#include <netinet/icmp6.h> 57 58#include <netinet6/in6_var.h> 59 60#include <stdio.h> 61#include <unistd.h> 62#include <stdlib.h> 63#include <syslog.h> 64#include <string.h> 65#include <fcntl.h> 66#include <errno.h> 67#include <kvm.h> 68#include <nlist.h> 69#include <limits.h> 70 71#include "rtsold.h" 72 73static int ifsock; 74 75static int getifa __P((char *name, struct in6_ifaddr *ifap)); 76static void get_rtaddrs __P((int addrs, struct sockaddr *sa, 77 struct sockaddr **rti_info)); 78 79int 80ifinit() 81{ 82 if ((ifsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 83 warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno)); 84 return(-1); 85 } 86 87 return(0); 88} 89 90int 91interface_up(char *name) 92{ 93 struct ifreq ifr; 94 struct in6_ifaddr ifa; 95 96 strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); 97 98 if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { 99 warnmsg(LOG_WARNING, __FUNCTION__, "ioctl(SIOCGIFFLAGS): %s", 100 strerror(errno)); 101 return(-1); 102 } 103 if (!(ifr.ifr_flags & IFF_UP)) { 104 ifr.ifr_flags |= IFF_UP; 105 if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { 106 warnmsg(LOG_ERR, __FUNCTION__, 107 "ioctl(SIOCSIFFLAGS): %s", strerror(errno)); 108 } 109 return(-1); 110 } 111 112 warnmsg(LOG_DEBUG, __FUNCTION__, "checking if %s is ready...", name); 113 114 if (getifa(name, &ifa) < 0) { 115 warnmsg(LOG_WARNING, __FUNCTION__, 116 "getifa() failed, anyway I'll try"); 117 return 0; 118 } 119 120 if (!(ifa.ia6_flags & IN6_IFF_NOTREADY)) { 121 warnmsg(LOG_DEBUG, __FUNCTION__, 122 "%s is ready", name); 123 return(0); 124 } 125 else { 126 if (ifa.ia6_flags & IN6_IFF_TENTATIVE) { 127 warnmsg(LOG_DEBUG, __FUNCTION__, "%s is tentative", 128 name); 129 return IFS_TENTATIVE; 130 } 131 if (ifa.ia6_flags & IN6_IFF_DUPLICATED) 132 warnmsg(LOG_DEBUG, __FUNCTION__, "%s is duplicated", 133 name); 134 return -1; 135 } 136} 137 138int 139interface_status(struct ifinfo *ifinfo) 140{ 141 char *ifname = ifinfo->ifname; 142 struct ifreq ifr; 143 struct ifmediareq ifmr; 144 145 /* get interface flags */ 146 memset(&ifr, 0, sizeof(ifr)); 147 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 148 if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { 149 warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFFLAGS) on %s: %s", 150 ifname, strerror(errno)); 151 return(-1); 152 } 153 /* 154 * if one of UP and RUNNING flags is dropped, 155 * the interface is not active. 156 */ 157 if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 158 goto inactive; 159 } 160 161 /* Next, check carrier on the interface, if possible */ 162 if (!ifinfo->mediareqok) 163 goto active; 164 memset(&ifmr, 0, sizeof(ifmr)); 165 strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 166 167 if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { 168 if (errno != EINVAL) { 169 warnmsg(LOG_DEBUG, __FUNCTION__, 170 "ioctl(SIOCGIFMEDIA) on %s: %s", 171 ifname, strerror(errno)); 172 return(-1); 173 } 174 /* 175 * EINVAL simply means that the interface does not support 176 * the SIOCGIFMEDIA ioctl. We regard it alive. 177 */ 178 ifinfo->mediareqok = 0; 179 goto active; 180 } 181 182 if (ifmr.ifm_status & IFM_AVALID) { 183 switch(ifmr.ifm_active & IFM_NMASK) { 184 case IFM_ETHER: 185 if (ifmr.ifm_status & IFM_ACTIVE) 186 goto active; 187 else 188 goto inactive; 189 break; 190 default: 191 goto inactive; 192 } 193 } 194 195 inactive: 196 return(0); 197 198 active: 199 return(1); 200} 201 202#define ROUNDUP(a, size) \ 203 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) 204 205#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ 206 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ 207 sizeof(u_long)) :\ 208 sizeof(u_long))) 209#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) 210 211int 212lladdropt_length(struct sockaddr_dl *sdl) 213{ 214 switch(sdl->sdl_type) { 215 case IFT_ETHER: 216 return(ROUNDUP8(ETHER_ADDR_LEN + 2)); 217 default: 218 return(0); 219 } 220} 221 222void 223lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) 224{ 225 char *addr; 226 227 ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ 228 229 switch(sdl->sdl_type) { 230 case IFT_ETHER: 231 ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; 232 addr = (char *)(ndopt + 1); 233 memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); 234 break; 235 default: 236 warnmsg(LOG_ERR, __FUNCTION__, 237 "unsupported link type(%d)", sdl->sdl_type); 238 exit(1); 239 } 240 241 return; 242} 243 244struct sockaddr_dl * 245if_nametosdl(char *name) 246{ 247 int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; 248 char *buf, *next, *lim; 249 size_t len; 250 struct if_msghdr *ifm; 251 struct sockaddr *sa, *rti_info[RTAX_MAX]; 252 struct sockaddr_dl *sdl = NULL, *ret_sdl; 253 254 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) 255 return(NULL); 256 if ((buf = malloc(len)) == NULL) 257 return(NULL); 258 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 259 free(buf); 260 return(NULL); 261 } 262 263 lim = buf + len; 264 for (next = buf; next < lim; next += ifm->ifm_msglen) { 265 ifm = (struct if_msghdr *)next; 266 if (ifm->ifm_type == RTM_IFINFO) { 267 sa = (struct sockaddr *)(ifm + 1); 268 get_rtaddrs(ifm->ifm_addrs, sa, rti_info); 269 if ((sa = rti_info[RTAX_IFP]) != NULL) { 270 if (sa->sa_family == AF_LINK) { 271 sdl = (struct sockaddr_dl *)sa; 272 if (strncmp(&sdl->sdl_data[0], 273 name, 274 sdl->sdl_nlen) == 0) { 275 break; 276 } 277 } 278 } 279 } 280 } 281 if (next == lim) { 282 /* search failed */ 283 free(buf); 284 return(NULL); 285 } 286 287 if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) 288 return(NULL); 289 memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); 290 291 return(ret_sdl); 292} 293 294int 295getinet6sysctl(int code) 296{ 297 int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; 298 int value; 299 size_t size; 300 301 mib[3] = code; 302 size = sizeof(value); 303 if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0) 304 return -1; 305 else 306 return value; 307} 308 309/*------------------------------------------------------------*/ 310 311static struct nlist nl[] = { 312#define N_IFNET 0 313 { "_ifnet" }, 314 { "" }, 315}; 316 317#define KREAD(x, y, z) { \ 318 if (kvm_read(kvmd, (u_long)x, (void *)y, sizeof(z)) != sizeof(z)) { \ 319 warnmsg(LOG_ERR, __FUNCTION__, "kvm_read failed"); \ 320 goto bad; \ 321 } \ 322 } 323 324static int 325getifa(char *name, struct in6_ifaddr *ifap) 326{ 327 u_short index; 328 kvm_t *kvmd = NULL; 329 char buf[_POSIX2_LINE_MAX]; 330 struct ifnet *ifp; 331 struct ifnet ifnet; 332 struct in6_ifaddr *ifa; 333 334 if (!ifap) 335 exit(1); 336 337 index = (u_short)if_nametoindex(name); 338 if (index == 0) { 339 warnmsg(LOG_ERR, __FUNCTION__, "if_nametoindex failed for %s", 340 name); 341 goto bad; 342 } 343 if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) { 344 warnmsg(LOG_ERR, __FUNCTION__, "kvm_openfiles failed"); 345 goto bad; 346 } 347 if (kvm_nlist(kvmd, nl) < 0) { 348 warnmsg(LOG_ERR, __FUNCTION__, "kvm_nlist failed"); 349 goto bad; 350 } 351 if (nl[N_IFNET].n_value == 0) { 352 warnmsg(LOG_ERR, __FUNCTION__, "symbol \"%s\" not found", 353 nl[N_IFNET].n_name); 354 goto bad; 355 } 356 357 KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *); 358 while (ifp) { 359 KREAD(ifp, &ifnet, struct ifnet); 360 if (ifnet.if_index == index) 361 break; 362#if defined(__NetBSD__) || defined(__OpenBSD__) 363 ifp = TAILQ_NEXT(&ifnet, if_list); 364#elif defined(__FreeBSD__) && __FreeBSD__ >= 3 365 ifp = TAILQ_NEXT(&ifnet, if_link); 366#else 367 ifp = ifnet.if_next; 368#endif 369 } 370 if (!ifp) { 371 warnmsg(LOG_ERR, __FUNCTION__, "interface \"%s\" not found", 372 name); 373 goto bad; 374 } 375 376#if defined(__NetBSD__) || defined(__OpenBSD__) 377 ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrlist); 378#elif defined(__FreeBSD__) && __FreeBSD__ >= 3 379 ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrhead); 380#else 381 ifa = (struct in6_ifaddr *)ifnet.if_addrlist; 382#endif 383 while (ifa) { 384 KREAD(ifa, ifap, *ifap); 385 if (ifap->ia_addr.sin6_family == AF_INET6 386 && IN6_IS_ADDR_LINKLOCAL(&ifap->ia_addr.sin6_addr)) { 387 kvm_close(kvmd); 388 return 0; 389 } 390 391#if defined(__NetBSD__) || defined(__OpenBSD__) 392 ifa = (struct in6_ifaddr *) 393 TAILQ_NEXT((struct ifaddr *)ifap, ifa_list); 394#elif defined(__FreeBSD__) && __FreeBSD__ >= 3 395 ifa = (struct in6_ifaddr *) 396 TAILQ_NEXT((struct ifaddr *)ifap, ifa_link); 397#else 398 ifa = (struct in6_ifaddr *)(((struct ifaddr *)ifap)->ifa_next); 399#endif 400 } 401 warnmsg(LOG_ERR, __FUNCTION__, "no IPv6 link-local address for %s", 402 name); 403 404 bad: 405 if (kvmd) 406 kvm_close(kvmd); 407 return -1; 408} 409 410static void 411get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 412{ 413 int i; 414 415 for (i = 0; i < RTAX_MAX; i++) { 416 if (addrs & (1 << i)) { 417 rti_info[i] = sa; 418 NEXT_SA(sa); 419 } 420 else 421 rti_info[i] = NULL; 422 } 423} 424