1258945Sroberto/* 2258945Sroberto * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") 3258945Sroberto * Copyright (C) 1999-2003 Internet Software Consortium. 4258945Sroberto * 5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any 6258945Sroberto * purpose with or without fee is hereby granted, provided that the above 7258945Sroberto * copyright notice and this permission notice appear in all copies. 8258945Sroberto * 9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11258945Sroberto * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15258945Sroberto * PERFORMANCE OF THIS SOFTWARE. 16258945Sroberto */ 17258945Sroberto 18258945Sroberto/* $Id: ifiter_sysctl.c,v 1.25 2007/06/19 23:47:18 tbox Exp $ */ 19258945Sroberto 20258945Sroberto/*! \file 21258945Sroberto * \brief 22258945Sroberto * Obtain the list of network interfaces using sysctl. 23258945Sroberto * See TCP/IP Illustrated Volume 2, sections 19.8, 19.14, 24258945Sroberto * and 19.16. 25258945Sroberto */ 26258945Sroberto 27258945Sroberto#include <sys/param.h> 28258945Sroberto#include <sys/sysctl.h> 29258945Sroberto 30258945Sroberto#include <net/route.h> 31258945Sroberto#include <net/if_dl.h> 32258945Sroberto 33258945Sroberto/* XXX what about Alpha? */ 34258945Sroberto#ifdef sgi 35258945Sroberto#define ROUNDUP(a) ((a) > 0 ? \ 36258945Sroberto (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) : \ 37258945Sroberto sizeof(__uint64_t)) 38258945Sroberto#else 39258945Sroberto#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ 40258945Sroberto : sizeof(long)) 41258945Sroberto#endif 42258945Sroberto 43258945Sroberto#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'S') 44258945Sroberto#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) 45258945Sroberto 46258945Srobertostruct isc_interfaceiter { 47258945Sroberto unsigned int magic; /* Magic number. */ 48258945Sroberto isc_mem_t *mctx; 49258945Sroberto void *buf; /* Buffer for sysctl data. */ 50258945Sroberto unsigned int bufsize; /* Bytes allocated. */ 51258945Sroberto unsigned int bufused; /* Bytes used. */ 52258945Sroberto unsigned int pos; /* Current offset in 53258945Sroberto sysctl data. */ 54258945Sroberto isc_interface_t current; /* Current interface data. */ 55258945Sroberto isc_result_t result; /* Last result code. */ 56258945Sroberto}; 57258945Sroberto 58258945Srobertostatic int mib[6] = { 59258945Sroberto CTL_NET, 60258945Sroberto PF_ROUTE, 61258945Sroberto 0, 62258945Sroberto 0, /* Any address family. */ 63258945Sroberto NET_RT_IFLIST, 64258945Sroberto 0 /* Flags. */ 65258945Sroberto}; 66258945Sroberto 67258945Srobertoisc_result_t 68258945Srobertoisc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { 69258945Sroberto isc_interfaceiter_t *iter; 70258945Sroberto isc_result_t result; 71258945Sroberto size_t bufsize; 72258945Sroberto size_t bufused; 73258945Sroberto char strbuf[ISC_STRERRORSIZE]; 74258945Sroberto 75258945Sroberto REQUIRE(mctx != NULL); 76258945Sroberto REQUIRE(iterp != NULL); 77258945Sroberto REQUIRE(*iterp == NULL); 78258945Sroberto 79258945Sroberto iter = isc_mem_get(mctx, sizeof(*iter)); 80258945Sroberto if (iter == NULL) 81258945Sroberto return (ISC_R_NOMEMORY); 82258945Sroberto 83258945Sroberto iter->mctx = mctx; 84258945Sroberto iter->buf = 0; 85258945Sroberto 86258945Sroberto /* 87258945Sroberto * Determine the amount of memory needed. 88258945Sroberto */ 89258945Sroberto bufsize = 0; 90258945Sroberto if (sysctl(mib, 6, NULL, &bufsize, NULL, (size_t) 0) < 0) { 91258945Sroberto isc__strerror(errno, strbuf, sizeof(strbuf)); 92258945Sroberto UNEXPECTED_ERROR(__FILE__, __LINE__, 93258945Sroberto isc_msgcat_get(isc_msgcat, 94258945Sroberto ISC_MSGSET_IFITERSYSCTL, 95258945Sroberto ISC_MSG_GETIFLISTSIZE, 96258945Sroberto "getting interface " 97258945Sroberto "list size: sysctl: %s"), 98258945Sroberto strbuf); 99258945Sroberto result = ISC_R_UNEXPECTED; 100258945Sroberto goto failure; 101258945Sroberto } 102258945Sroberto iter->bufsize = bufsize; 103258945Sroberto 104258945Sroberto iter->buf = isc_mem_get(iter->mctx, iter->bufsize); 105258945Sroberto if (iter->buf == NULL) { 106258945Sroberto result = ISC_R_NOMEMORY; 107258945Sroberto goto failure; 108258945Sroberto } 109258945Sroberto 110258945Sroberto bufused = bufsize; 111258945Sroberto if (sysctl(mib, 6, iter->buf, &bufused, NULL, (size_t) 0) < 0) { 112258945Sroberto isc__strerror(errno, strbuf, sizeof(strbuf)); 113258945Sroberto UNEXPECTED_ERROR(__FILE__, __LINE__, 114258945Sroberto isc_msgcat_get(isc_msgcat, 115258945Sroberto ISC_MSGSET_IFITERSYSCTL, 116258945Sroberto ISC_MSG_GETIFLIST, 117258945Sroberto "getting interface list: " 118258945Sroberto "sysctl: %s"), 119258945Sroberto strbuf); 120258945Sroberto result = ISC_R_UNEXPECTED; 121258945Sroberto goto failure; 122258945Sroberto } 123258945Sroberto iter->bufused = bufused; 124258945Sroberto INSIST(iter->bufused <= iter->bufsize); 125258945Sroberto 126258945Sroberto /* 127258945Sroberto * A newly created iterator has an undefined position 128258945Sroberto * until isc_interfaceiter_first() is called. 129258945Sroberto */ 130258945Sroberto iter->pos = (unsigned int) -1; 131258945Sroberto iter->result = ISC_R_FAILURE; 132258945Sroberto 133258945Sroberto iter->magic = IFITER_MAGIC; 134258945Sroberto *iterp = iter; 135258945Sroberto return (ISC_R_SUCCESS); 136258945Sroberto 137258945Sroberto failure: 138258945Sroberto if (iter->buf != NULL) 139258945Sroberto isc_mem_put(mctx, iter->buf, iter->bufsize); 140258945Sroberto isc_mem_put(mctx, iter, sizeof(*iter)); 141258945Sroberto return (result); 142258945Sroberto} 143258945Sroberto 144258945Sroberto/* 145258945Sroberto * Get information about the current interface to iter->current. 146258945Sroberto * If successful, return ISC_R_SUCCESS. 147258945Sroberto * If the interface has an unsupported address family, 148258945Sroberto * return ISC_R_IGNORE. In case of other failure, 149258945Sroberto * return ISC_R_UNEXPECTED. 150258945Sroberto */ 151258945Sroberto 152258945Srobertostatic isc_result_t 153258945Srobertointernal_current(isc_interfaceiter_t *iter) { 154258945Sroberto struct ifa_msghdr *ifam, *ifam_end; 155258945Sroberto 156258945Sroberto REQUIRE(VALID_IFITER(iter)); 157258945Sroberto REQUIRE (iter->pos < (unsigned int) iter->bufused); 158258945Sroberto 159258945Sroberto ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); 160258945Sroberto ifam_end = (struct ifa_msghdr *) ((char *) iter->buf + iter->bufused); 161258945Sroberto 162280849Scy // Skip wrong RTM version headers 163280849Scy if (ifam->ifam_version != RTM_VERSION) 164280849Scy return (ISC_R_IGNORE); 165280849Scy 166258945Sroberto if (ifam->ifam_type == RTM_IFINFO) { 167258945Sroberto struct if_msghdr *ifm = (struct if_msghdr *) ifam; 168258945Sroberto struct sockaddr_dl *sdl = (struct sockaddr_dl *) (ifm + 1); 169258945Sroberto unsigned int namelen; 170258945Sroberto 171258945Sroberto memset(&iter->current, 0, sizeof(iter->current)); 172258945Sroberto 173258945Sroberto iter->current.ifindex = sdl->sdl_index; 174258945Sroberto namelen = sdl->sdl_nlen; 175258945Sroberto if (namelen > sizeof(iter->current.name) - 1) 176258945Sroberto namelen = sizeof(iter->current.name) - 1; 177258945Sroberto 178258945Sroberto memset(iter->current.name, 0, sizeof(iter->current.name)); 179258945Sroberto memcpy(iter->current.name, sdl->sdl_data, namelen); 180258945Sroberto 181258945Sroberto iter->current.flags = 0; 182258945Sroberto 183258945Sroberto if ((ifam->ifam_flags & IFF_UP) != 0) 184258945Sroberto iter->current.flags |= INTERFACE_F_UP; 185258945Sroberto 186258945Sroberto if ((ifam->ifam_flags & IFF_POINTOPOINT) != 0) 187258945Sroberto iter->current.flags |= INTERFACE_F_POINTTOPOINT; 188258945Sroberto 189258945Sroberto if ((ifam->ifam_flags & IFF_LOOPBACK) != 0) 190258945Sroberto iter->current.flags |= INTERFACE_F_LOOPBACK; 191258945Sroberto 192258945Sroberto if ((ifam->ifam_flags & IFF_BROADCAST) != 0) 193258945Sroberto iter->current.flags |= INTERFACE_F_BROADCAST; 194258945Sroberto 195258945Sroberto#ifdef IFF_MULTICAST 196258945Sroberto if ((ifam->ifam_flags & IFF_MULTICAST) != 0) 197258945Sroberto iter->current.flags |= INTERFACE_F_MULTICAST; 198258945Sroberto#endif 199258945Sroberto 200258945Sroberto /* 201258945Sroberto * This is not an interface address. 202258945Sroberto * Force another iteration. 203258945Sroberto */ 204258945Sroberto return (ISC_R_IGNORE); 205258945Sroberto } else if (ifam->ifam_type == RTM_NEWADDR) { 206258945Sroberto int i; 207258945Sroberto int family; 208258945Sroberto struct sockaddr *mask_sa = NULL; 209258945Sroberto struct sockaddr *addr_sa = NULL; 210258945Sroberto struct sockaddr *dst_sa = NULL; 211258945Sroberto 212258945Sroberto struct sockaddr *sa = (struct sockaddr *)(ifam + 1); 213258945Sroberto family = sa->sa_family; 214258945Sroberto 215258945Sroberto for (i = 0; i < RTAX_MAX; i++) 216258945Sroberto { 217258945Sroberto if ((ifam->ifam_addrs & (1 << i)) == 0) 218258945Sroberto continue; 219258945Sroberto 220258945Sroberto INSIST(sa < (struct sockaddr *) ifam_end); 221258945Sroberto 222258945Sroberto switch (i) { 223258945Sroberto case RTAX_NETMASK: /* Netmask */ 224258945Sroberto mask_sa = sa; 225258945Sroberto break; 226258945Sroberto case RTAX_IFA: /* Interface address */ 227258945Sroberto addr_sa = sa; 228258945Sroberto break; 229258945Sroberto case RTAX_BRD: /* Broadcast or destination address */ 230258945Sroberto dst_sa = sa; 231258945Sroberto break; 232258945Sroberto } 233258945Sroberto#ifdef ISC_PLATFORM_HAVESALEN 234258945Sroberto sa = (struct sockaddr *)((char*)(sa) 235258945Sroberto + ROUNDUP(sa->sa_len)); 236258945Sroberto#else 237258945Sroberto#ifdef sgi 238258945Sroberto /* 239258945Sroberto * Do as the contributed SGI code does. 240258945Sroberto */ 241258945Sroberto sa = (struct sockaddr *)((char*)(sa) 242258945Sroberto + ROUNDUP(_FAKE_SA_LEN_DST(sa))); 243258945Sroberto#else 244258945Sroberto /* XXX untested. */ 245258945Sroberto sa = (struct sockaddr *)((char*)(sa) 246258945Sroberto + ROUNDUP(sizeof(struct sockaddr))); 247258945Sroberto#endif 248258945Sroberto#endif 249258945Sroberto } 250258945Sroberto 251258945Sroberto if (addr_sa == NULL) 252258945Sroberto return (ISC_R_IGNORE); 253258945Sroberto 254258945Sroberto family = addr_sa->sa_family; 255258945Sroberto if (family != AF_INET && family != AF_INET6) 256258945Sroberto return (ISC_R_IGNORE); 257258945Sroberto 258258945Sroberto iter->current.af = family; 259258945Sroberto 260258945Sroberto get_addr(family, &iter->current.address, addr_sa, 261258945Sroberto iter->current.name); 262258945Sroberto 263258945Sroberto if (mask_sa != NULL) 264258945Sroberto get_addr(family, &iter->current.netmask, mask_sa, 265258945Sroberto iter->current.name); 266258945Sroberto 267258945Sroberto if (dst_sa != NULL && 268258945Sroberto (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) 269258945Sroberto get_addr(family, &iter->current.dstaddress, dst_sa, 270258945Sroberto iter->current.name); 271258945Sroberto 272258945Sroberto if (dst_sa != NULL && 273258945Sroberto (iter->current.flags & INTERFACE_F_BROADCAST) != 0) 274258945Sroberto get_addr(family, &iter->current.broadcast, dst_sa, 275258945Sroberto iter->current.name); 276258945Sroberto 277258945Sroberto return (ISC_R_SUCCESS); 278258945Sroberto } else { 279280849Scy printf("%s", isc_msgcat_get(isc_msgcat, 280280849Scy ISC_MSGSET_IFITERSYSCTL, 281280849Scy ISC_MSG_UNEXPECTEDTYPE, 282280849Scy "warning: unexpected interface " 283280849Scy "list message type\n")); 284258945Sroberto return (ISC_R_IGNORE); 285258945Sroberto } 286258945Sroberto} 287258945Sroberto 288258945Sroberto/* 289258945Sroberto * Step the iterator to the next interface. Unlike 290258945Sroberto * isc_interfaceiter_next(), this may leave the iterator 291258945Sroberto * positioned on an interface that will ultimately 292258945Sroberto * be ignored. Return ISC_R_NOMORE if there are no more 293258945Sroberto * interfaces, otherwise ISC_R_SUCCESS. 294258945Sroberto */ 295258945Srobertostatic isc_result_t 296258945Srobertointernal_next(isc_interfaceiter_t *iter) { 297258945Sroberto struct ifa_msghdr *ifam; 298258945Sroberto REQUIRE (iter->pos < (unsigned int) iter->bufused); 299258945Sroberto 300258945Sroberto ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); 301258945Sroberto 302258945Sroberto iter->pos += ifam->ifam_msglen; 303258945Sroberto 304258945Sroberto if (iter->pos >= iter->bufused) 305258945Sroberto return (ISC_R_NOMORE); 306258945Sroberto 307258945Sroberto return (ISC_R_SUCCESS); 308258945Sroberto} 309258945Sroberto 310258945Srobertostatic void 311258945Srobertointernal_destroy(isc_interfaceiter_t *iter) { 312258945Sroberto UNUSED(iter); /* Unused. */ 313258945Sroberto /* 314258945Sroberto * Do nothing. 315258945Sroberto */ 316258945Sroberto} 317258945Sroberto 318258945Srobertostatic 319258945Srobertovoid internal_first(isc_interfaceiter_t *iter) { 320258945Sroberto iter->pos = 0; 321258945Sroberto} 322