1135446Strhodes/* 2262706Serwin * Copyright (C) 2004, 2005, 2007, 2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * 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_sysctl.c,v 1.25 2007/06/19 23:47:18 tbox Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file 21170222Sdougb * \brief 22135446Strhodes * Obtain the list of network interfaces using sysctl. 23135446Strhodes * See TCP/IP Illustrated Volume 2, sections 19.8, 19.14, 24135446Strhodes * and 19.16. 25135446Strhodes */ 26135446Strhodes 27135446Strhodes#include <sys/param.h> 28135446Strhodes#include <sys/sysctl.h> 29135446Strhodes 30135446Strhodes#include <net/route.h> 31135446Strhodes#include <net/if_dl.h> 32135446Strhodes 33135446Strhodes/* XXX what about Alpha? */ 34135446Strhodes#ifdef sgi 35135446Strhodes#define ROUNDUP(a) ((a) > 0 ? \ 36135446Strhodes (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) : \ 37135446Strhodes sizeof(__uint64_t)) 38135446Strhodes#else 39135446Strhodes#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ 40262706Serwin : sizeof(long)) 41135446Strhodes#endif 42135446Strhodes 43135446Strhodes#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'S') 44135446Strhodes#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) 45135446Strhodes 46135446Strhodesstruct isc_interfaceiter { 47135446Strhodes unsigned int magic; /* Magic number. */ 48135446Strhodes isc_mem_t *mctx; 49135446Strhodes void *buf; /* Buffer for sysctl data. */ 50135446Strhodes unsigned int bufsize; /* Bytes allocated. */ 51135446Strhodes unsigned int bufused; /* Bytes used. */ 52135446Strhodes unsigned int pos; /* Current offset in 53135446Strhodes sysctl data. */ 54135446Strhodes isc_interface_t current; /* Current interface data. */ 55135446Strhodes isc_result_t result; /* Last result code. */ 56135446Strhodes}; 57135446Strhodes 58135446Strhodesstatic int mib[6] = { 59135446Strhodes CTL_NET, 60135446Strhodes PF_ROUTE, 61262706Serwin 0, 62135446Strhodes 0, /* Any address family. */ 63262706Serwin NET_RT_IFLIST, 64135446Strhodes 0 /* Flags. */ 65135446Strhodes}; 66135446Strhodes 67135446Strhodesisc_result_t 68135446Strhodesisc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { 69135446Strhodes isc_interfaceiter_t *iter; 70135446Strhodes isc_result_t result; 71135446Strhodes size_t bufsize; 72135446Strhodes size_t bufused; 73135446Strhodes char strbuf[ISC_STRERRORSIZE]; 74135446Strhodes 75135446Strhodes REQUIRE(mctx != NULL); 76135446Strhodes REQUIRE(iterp != NULL); 77135446Strhodes REQUIRE(*iterp == NULL); 78135446Strhodes 79135446Strhodes iter = isc_mem_get(mctx, sizeof(*iter)); 80135446Strhodes if (iter == NULL) 81135446Strhodes return (ISC_R_NOMEMORY); 82135446Strhodes 83135446Strhodes iter->mctx = mctx; 84135446Strhodes iter->buf = 0; 85135446Strhodes 86135446Strhodes /* 87135446Strhodes * Determine the amount of memory needed. 88135446Strhodes */ 89135446Strhodes bufsize = 0; 90135446Strhodes if (sysctl(mib, 6, NULL, &bufsize, NULL, (size_t) 0) < 0) { 91135446Strhodes isc__strerror(errno, strbuf, sizeof(strbuf)); 92135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 93135446Strhodes isc_msgcat_get(isc_msgcat, 94135446Strhodes ISC_MSGSET_IFITERSYSCTL, 95135446Strhodes ISC_MSG_GETIFLISTSIZE, 96135446Strhodes "getting interface " 97135446Strhodes "list size: sysctl: %s"), 98135446Strhodes strbuf); 99135446Strhodes result = ISC_R_UNEXPECTED; 100135446Strhodes goto failure; 101135446Strhodes } 102135446Strhodes iter->bufsize = bufsize; 103135446Strhodes 104135446Strhodes iter->buf = isc_mem_get(iter->mctx, iter->bufsize); 105135446Strhodes if (iter->buf == NULL) { 106135446Strhodes result = ISC_R_NOMEMORY; 107135446Strhodes goto failure; 108135446Strhodes } 109135446Strhodes 110135446Strhodes bufused = bufsize; 111135446Strhodes if (sysctl(mib, 6, iter->buf, &bufused, NULL, (size_t) 0) < 0) { 112135446Strhodes isc__strerror(errno, strbuf, sizeof(strbuf)); 113135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 114135446Strhodes isc_msgcat_get(isc_msgcat, 115135446Strhodes ISC_MSGSET_IFITERSYSCTL, 116135446Strhodes ISC_MSG_GETIFLIST, 117135446Strhodes "getting interface list: " 118135446Strhodes "sysctl: %s"), 119135446Strhodes strbuf); 120135446Strhodes result = ISC_R_UNEXPECTED; 121135446Strhodes goto failure; 122135446Strhodes } 123135446Strhodes iter->bufused = bufused; 124135446Strhodes INSIST(iter->bufused <= iter->bufsize); 125135446Strhodes 126135446Strhodes /* 127135446Strhodes * A newly created iterator has an undefined position 128135446Strhodes * until isc_interfaceiter_first() is called. 129135446Strhodes */ 130135446Strhodes iter->pos = (unsigned int) -1; 131135446Strhodes iter->result = ISC_R_FAILURE; 132135446Strhodes 133135446Strhodes iter->magic = IFITER_MAGIC; 134135446Strhodes *iterp = iter; 135135446Strhodes return (ISC_R_SUCCESS); 136135446Strhodes 137135446Strhodes failure: 138135446Strhodes if (iter->buf != NULL) 139135446Strhodes isc_mem_put(mctx, iter->buf, iter->bufsize); 140135446Strhodes isc_mem_put(mctx, iter, sizeof(*iter)); 141135446Strhodes return (result); 142135446Strhodes} 143135446Strhodes 144135446Strhodes/* 145135446Strhodes * Get information about the current interface to iter->current. 146135446Strhodes * If successful, return ISC_R_SUCCESS. 147135446Strhodes * If the interface has an unsupported address family, 148135446Strhodes * return ISC_R_IGNORE. In case of other failure, 149135446Strhodes * return ISC_R_UNEXPECTED. 150135446Strhodes */ 151135446Strhodes 152135446Strhodesstatic isc_result_t 153135446Strhodesinternal_current(isc_interfaceiter_t *iter) { 154135446Strhodes struct ifa_msghdr *ifam, *ifam_end; 155135446Strhodes 156135446Strhodes REQUIRE(VALID_IFITER(iter)); 157135446Strhodes REQUIRE (iter->pos < (unsigned int) iter->bufused); 158135446Strhodes 159135446Strhodes ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); 160135446Strhodes ifam_end = (struct ifa_msghdr *) ((char *) iter->buf + iter->bufused); 161135446Strhodes 162135446Strhodes if (ifam->ifam_type == RTM_IFINFO) { 163135446Strhodes struct if_msghdr *ifm = (struct if_msghdr *) ifam; 164135446Strhodes struct sockaddr_dl *sdl = (struct sockaddr_dl *) (ifm + 1); 165135446Strhodes unsigned int namelen; 166135446Strhodes 167135446Strhodes memset(&iter->current, 0, sizeof(iter->current)); 168135446Strhodes 169135446Strhodes namelen = sdl->sdl_nlen; 170135446Strhodes if (namelen > sizeof(iter->current.name) - 1) 171135446Strhodes namelen = sizeof(iter->current.name) - 1; 172135446Strhodes 173135446Strhodes memset(iter->current.name, 0, sizeof(iter->current.name)); 174262706Serwin memmove(iter->current.name, sdl->sdl_data, namelen); 175135446Strhodes 176135446Strhodes iter->current.flags = 0; 177135446Strhodes 178135446Strhodes if ((ifam->ifam_flags & IFF_UP) != 0) 179135446Strhodes iter->current.flags |= INTERFACE_F_UP; 180135446Strhodes 181135446Strhodes if ((ifam->ifam_flags & IFF_POINTOPOINT) != 0) 182135446Strhodes iter->current.flags |= INTERFACE_F_POINTTOPOINT; 183135446Strhodes 184135446Strhodes if ((ifam->ifam_flags & IFF_LOOPBACK) != 0) 185135446Strhodes iter->current.flags |= INTERFACE_F_LOOPBACK; 186135446Strhodes 187135446Strhodes /* 188135446Strhodes * This is not an interface address. 189135446Strhodes * Force another iteration. 190135446Strhodes */ 191135446Strhodes return (ISC_R_IGNORE); 192135446Strhodes } else if (ifam->ifam_type == RTM_NEWADDR) { 193135446Strhodes int i; 194135446Strhodes int family; 195135446Strhodes struct sockaddr *mask_sa = NULL; 196135446Strhodes struct sockaddr *addr_sa = NULL; 197135446Strhodes struct sockaddr *dst_sa = NULL; 198135446Strhodes 199135446Strhodes struct sockaddr *sa = (struct sockaddr *)(ifam + 1); 200135446Strhodes family = sa->sa_family; 201135446Strhodes 202135446Strhodes for (i = 0; i < RTAX_MAX; i++) 203135446Strhodes { 204135446Strhodes if ((ifam->ifam_addrs & (1 << i)) == 0) 205135446Strhodes continue; 206135446Strhodes 207135446Strhodes INSIST(sa < (struct sockaddr *) ifam_end); 208135446Strhodes 209135446Strhodes switch (i) { 210135446Strhodes case RTAX_NETMASK: /* Netmask */ 211135446Strhodes mask_sa = sa; 212135446Strhodes break; 213135446Strhodes case RTAX_IFA: /* Interface address */ 214135446Strhodes addr_sa = sa; 215135446Strhodes break; 216135446Strhodes case RTAX_BRD: /* Broadcast or destination address */ 217135446Strhodes dst_sa = sa; 218135446Strhodes break; 219135446Strhodes } 220135446Strhodes#ifdef ISC_PLATFORM_HAVESALEN 221135446Strhodes sa = (struct sockaddr *)((char*)(sa) 222135446Strhodes + ROUNDUP(sa->sa_len)); 223135446Strhodes#else 224135446Strhodes#ifdef sgi 225135446Strhodes /* 226135446Strhodes * Do as the contributed SGI code does. 227135446Strhodes */ 228135446Strhodes sa = (struct sockaddr *)((char*)(sa) 229135446Strhodes + ROUNDUP(_FAKE_SA_LEN_DST(sa))); 230135446Strhodes#else 231135446Strhodes /* XXX untested. */ 232135446Strhodes sa = (struct sockaddr *)((char*)(sa) 233135446Strhodes + ROUNDUP(sizeof(struct sockaddr))); 234135446Strhodes#endif 235135446Strhodes#endif 236135446Strhodes } 237135446Strhodes 238135446Strhodes if (addr_sa == NULL) 239135446Strhodes return (ISC_R_IGNORE); 240135446Strhodes 241135446Strhodes family = addr_sa->sa_family; 242135446Strhodes if (family != AF_INET && family != AF_INET6) 243135446Strhodes return (ISC_R_IGNORE); 244135446Strhodes 245135446Strhodes iter->current.af = family; 246135446Strhodes 247135446Strhodes get_addr(family, &iter->current.address, addr_sa, 248135446Strhodes iter->current.name); 249135446Strhodes 250135446Strhodes if (mask_sa != NULL) 251135446Strhodes get_addr(family, &iter->current.netmask, mask_sa, 252135446Strhodes iter->current.name); 253135446Strhodes 254135446Strhodes if (dst_sa != NULL && 255153816Sdougb (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) 256135446Strhodes get_addr(family, &iter->current.dstaddress, dst_sa, 257135446Strhodes iter->current.name); 258135446Strhodes 259135446Strhodes return (ISC_R_SUCCESS); 260135446Strhodes } else { 261135446Strhodes printf(isc_msgcat_get(isc_msgcat, ISC_MSGSET_IFITERSYSCTL, 262135446Strhodes ISC_MSG_UNEXPECTEDTYPE, 263135446Strhodes "warning: unexpected interface list " 264135446Strhodes "message type\n")); 265135446Strhodes return (ISC_R_IGNORE); 266135446Strhodes } 267135446Strhodes} 268135446Strhodes 269135446Strhodes/* 270135446Strhodes * Step the iterator to the next interface. Unlike 271135446Strhodes * isc_interfaceiter_next(), this may leave the iterator 272135446Strhodes * positioned on an interface that will ultimately 273135446Strhodes * be ignored. Return ISC_R_NOMORE if there are no more 274135446Strhodes * interfaces, otherwise ISC_R_SUCCESS. 275135446Strhodes */ 276135446Strhodesstatic isc_result_t 277135446Strhodesinternal_next(isc_interfaceiter_t *iter) { 278135446Strhodes struct ifa_msghdr *ifam; 279135446Strhodes REQUIRE (iter->pos < (unsigned int) iter->bufused); 280135446Strhodes 281135446Strhodes ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); 282135446Strhodes 283135446Strhodes iter->pos += ifam->ifam_msglen; 284135446Strhodes 285135446Strhodes if (iter->pos >= iter->bufused) 286135446Strhodes return (ISC_R_NOMORE); 287135446Strhodes 288135446Strhodes return (ISC_R_SUCCESS); 289135446Strhodes} 290135446Strhodes 291135446Strhodesstatic void 292135446Strhodesinternal_destroy(isc_interfaceiter_t *iter) { 293135446Strhodes UNUSED(iter); /* Unused. */ 294135446Strhodes /* 295135446Strhodes * Do nothing. 296135446Strhodes */ 297135446Strhodes} 298135446Strhodes 299135446Strhodesstatic 300135446Strhodesvoid internal_first(isc_interfaceiter_t *iter) { 301135446Strhodes iter->pos = 0; 302135446Strhodes} 303