1135446Strhodes/* 2262706Serwin * Copyright (C) 2004, 2005, 2007-2009, 2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2003 Internet Software Consortium. 4135446Strhodes * 5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id: ifiter_getifaddrs.c,v 1.13 2009/09/24 23:48:13 tbox Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file 21170222Sdougb * \brief 22135446Strhodes * Obtain the list of network interfaces using the getifaddrs(3) library. 23135446Strhodes */ 24135446Strhodes 25135446Strhodes#include <ifaddrs.h> 26135446Strhodes 27170222Sdougb/*% Iterator Magic */ 28135446Strhodes#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G') 29170222Sdougb/*% Valid Iterator */ 30135446Strhodes#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) 31135446Strhodes 32193149Sdougb#ifdef __linux 33193149Sdougbstatic isc_boolean_t seenv6 = ISC_FALSE; 34193149Sdougb#endif 35193149Sdougb 36170222Sdougb/*% Iterator structure */ 37135446Strhodesstruct isc_interfaceiter { 38170222Sdougb unsigned int magic; /*%< Magic number. */ 39135446Strhodes isc_mem_t *mctx; 40170222Sdougb void *buf; /*%< (unused) */ 41170222Sdougb unsigned int bufsize; /*%< (always 0) */ 42170222Sdougb struct ifaddrs *ifaddrs; /*%< List of ifaddrs */ 43170222Sdougb struct ifaddrs *pos; /*%< Ptr to current ifaddr */ 44170222Sdougb isc_interface_t current; /*%< Current interface data. */ 45170222Sdougb isc_result_t result; /*%< Last result code. */ 46193149Sdougb#ifdef __linux 47193149Sdougb FILE * proc; 48193149Sdougb char entry[ISC_IF_INET6_SZ]; 49193149Sdougb isc_result_t valid; 50193149Sdougb#endif 51135446Strhodes}; 52135446Strhodes 53135446Strhodesisc_result_t 54135446Strhodesisc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { 55135446Strhodes isc_interfaceiter_t *iter; 56135446Strhodes isc_result_t result; 57135446Strhodes char strbuf[ISC_STRERRORSIZE]; 58135446Strhodes 59135446Strhodes REQUIRE(mctx != NULL); 60135446Strhodes REQUIRE(iterp != NULL); 61135446Strhodes REQUIRE(*iterp == NULL); 62135446Strhodes 63135446Strhodes iter = isc_mem_get(mctx, sizeof(*iter)); 64135446Strhodes if (iter == NULL) 65135446Strhodes return (ISC_R_NOMEMORY); 66135446Strhodes 67135446Strhodes iter->mctx = mctx; 68135446Strhodes iter->buf = NULL; 69135446Strhodes iter->bufsize = 0; 70135446Strhodes iter->ifaddrs = NULL; 71193149Sdougb#ifdef __linux 72193149Sdougb /* 73193149Sdougb * Only open "/proc/net/if_inet6" if we have never seen a IPv6 74193149Sdougb * address returned by getifaddrs(). 75193149Sdougb */ 76193149Sdougb if (!seenv6) 77193149Sdougb iter->proc = fopen("/proc/net/if_inet6", "r"); 78193149Sdougb else 79193149Sdougb iter->proc = NULL; 80193149Sdougb iter->valid = ISC_R_FAILURE; 81193149Sdougb#endif 82135446Strhodes 83135446Strhodes if (getifaddrs(&iter->ifaddrs) < 0) { 84135446Strhodes isc__strerror(errno, strbuf, sizeof(strbuf)); 85135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 86135446Strhodes isc_msgcat_get(isc_msgcat, 87135446Strhodes ISC_MSGSET_IFITERGETIFADDRS, 88135446Strhodes ISC_MSG_GETIFADDRS, 89135446Strhodes "getting interface " 90135446Strhodes "addresses: getifaddrs: %s"), 91135446Strhodes strbuf); 92135446Strhodes result = ISC_R_UNEXPECTED; 93135446Strhodes goto failure; 94135446Strhodes } 95135446Strhodes 96135446Strhodes /* 97135446Strhodes * A newly created iterator has an undefined position 98135446Strhodes * until isc_interfaceiter_first() is called. 99135446Strhodes */ 100135446Strhodes iter->pos = NULL; 101135446Strhodes iter->result = ISC_R_FAILURE; 102135446Strhodes 103135446Strhodes iter->magic = IFITER_MAGIC; 104135446Strhodes *iterp = iter; 105135446Strhodes return (ISC_R_SUCCESS); 106135446Strhodes 107135446Strhodes failure: 108193149Sdougb#ifdef __linux 109193149Sdougb if (iter->proc != NULL) 110193149Sdougb fclose(iter->proc); 111193149Sdougb#endif 112135446Strhodes if (iter->ifaddrs != NULL) /* just in case */ 113135446Strhodes freeifaddrs(iter->ifaddrs); 114135446Strhodes isc_mem_put(mctx, iter, sizeof(*iter)); 115135446Strhodes return (result); 116135446Strhodes} 117135446Strhodes 118135446Strhodes/* 119135446Strhodes * Get information about the current interface to iter->current. 120135446Strhodes * If successful, return ISC_R_SUCCESS. 121135446Strhodes * If the interface has an unsupported address family, 122135446Strhodes * return ISC_R_IGNORE. 123135446Strhodes */ 124135446Strhodes 125135446Strhodesstatic isc_result_t 126135446Strhodesinternal_current(isc_interfaceiter_t *iter) { 127135446Strhodes struct ifaddrs *ifa; 128135446Strhodes int family; 129135446Strhodes unsigned int namelen; 130135446Strhodes 131135446Strhodes REQUIRE(VALID_IFITER(iter)); 132135446Strhodes 133135446Strhodes ifa = iter->pos; 134135446Strhodes 135193149Sdougb#ifdef __linux 136193149Sdougb if (iter->pos == NULL) 137193149Sdougb return (linux_if_inet6_current(iter)); 138193149Sdougb#endif 139193149Sdougb 140135446Strhodes INSIST(ifa != NULL); 141135446Strhodes INSIST(ifa->ifa_name != NULL); 142135446Strhodes 143174187Sdougb if (ifa->ifa_addr == NULL) 144174187Sdougb return (ISC_R_IGNORE); 145174187Sdougb 146135446Strhodes family = ifa->ifa_addr->sa_family; 147135446Strhodes if (family != AF_INET && family != AF_INET6) 148135446Strhodes return (ISC_R_IGNORE); 149135446Strhodes 150193149Sdougb#ifdef __linux 151193149Sdougb if (family == AF_INET6) 152193149Sdougb seenv6 = ISC_TRUE; 153193149Sdougb#endif 154193149Sdougb 155135446Strhodes memset(&iter->current, 0, sizeof(iter->current)); 156135446Strhodes 157135446Strhodes namelen = strlen(ifa->ifa_name); 158135446Strhodes if (namelen > sizeof(iter->current.name) - 1) 159135446Strhodes namelen = sizeof(iter->current.name) - 1; 160135446Strhodes 161135446Strhodes memset(iter->current.name, 0, sizeof(iter->current.name)); 162262706Serwin memmove(iter->current.name, ifa->ifa_name, namelen); 163135446Strhodes 164135446Strhodes iter->current.flags = 0; 165135446Strhodes 166135446Strhodes if ((ifa->ifa_flags & IFF_UP) != 0) 167135446Strhodes iter->current.flags |= INTERFACE_F_UP; 168135446Strhodes 169135446Strhodes if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 170135446Strhodes iter->current.flags |= INTERFACE_F_POINTTOPOINT; 171135446Strhodes 172135446Strhodes if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 173135446Strhodes iter->current.flags |= INTERFACE_F_LOOPBACK; 174135446Strhodes 175135446Strhodes iter->current.af = family; 176135446Strhodes 177135446Strhodes get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name); 178135446Strhodes 179135446Strhodes if (ifa->ifa_netmask != NULL) 180135446Strhodes get_addr(family, &iter->current.netmask, ifa->ifa_netmask, 181135446Strhodes ifa->ifa_name); 182135446Strhodes 183135446Strhodes if (ifa->ifa_dstaddr != NULL && 184204619Sdougb (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) 185135446Strhodes get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr, 186135446Strhodes ifa->ifa_name); 187135446Strhodes 188135446Strhodes return (ISC_R_SUCCESS); 189135446Strhodes} 190135446Strhodes 191135446Strhodes/* 192135446Strhodes * Step the iterator to the next interface. Unlike 193135446Strhodes * isc_interfaceiter_next(), this may leave the iterator 194135446Strhodes * positioned on an interface that will ultimately 195135446Strhodes * be ignored. Return ISC_R_NOMORE if there are no more 196135446Strhodes * interfaces, otherwise ISC_R_SUCCESS. 197135446Strhodes */ 198135446Strhodesstatic isc_result_t 199135446Strhodesinternal_next(isc_interfaceiter_t *iter) { 200135446Strhodes 201193149Sdougb if (iter->pos != NULL) 202193149Sdougb iter->pos = iter->pos->ifa_next; 203193149Sdougb if (iter->pos == NULL) { 204193149Sdougb#ifdef __linux 205193149Sdougb if (!seenv6) 206193149Sdougb return (linux_if_inet6_next(iter)); 207193149Sdougb#endif 208135446Strhodes return (ISC_R_NOMORE); 209193149Sdougb } 210135446Strhodes 211135446Strhodes return (ISC_R_SUCCESS); 212135446Strhodes} 213135446Strhodes 214135446Strhodesstatic void 215135446Strhodesinternal_destroy(isc_interfaceiter_t *iter) { 216193149Sdougb 217193149Sdougb#ifdef __linux 218193149Sdougb if (iter->proc != NULL) 219193149Sdougb fclose(iter->proc); 220193149Sdougb iter->proc = NULL; 221193149Sdougb#endif 222135446Strhodes if (iter->ifaddrs) 223135446Strhodes freeifaddrs(iter->ifaddrs); 224135446Strhodes iter->ifaddrs = NULL; 225135446Strhodes} 226135446Strhodes 227135446Strhodesstatic 228135446Strhodesvoid internal_first(isc_interfaceiter_t *iter) { 229193149Sdougb 230193149Sdougb#ifdef __linux 231193149Sdougb linux_if_inet6_first(iter); 232193149Sdougb#endif 233135446Strhodes iter->pos = iter->ifaddrs; 234135446Strhodes} 235