184372Sume/* $KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $ */ 262606Sitojun 362606Sitojun/* 462606Sitojun * Copyright (c) 1995, 1999 562606Sitojun * Berkeley Software Design, Inc. All rights reserved. 662606Sitojun * 762606Sitojun * Redistribution and use in source and binary forms, with or without 862606Sitojun * modification, are permitted provided that the following conditions 962606Sitojun * are met: 1062606Sitojun * 1. Redistributions of source code must retain the above copyright 1162606Sitojun * notice, this list of conditions and the following disclaimer. 1262606Sitojun * 1362606Sitojun * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND 1462606Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1562606Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1662606Sitojun * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE 1762606Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1862606Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1962606Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2062606Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2162606Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2262606Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2362606Sitojun * SUCH DAMAGE. 2462606Sitojun * 2562606Sitojun * BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp 2662606Sitojun */ 2762606Sitojun/* 2862606Sitojun * NOTE: SIOCGIFCONF case is not LP64 friendly. it also does not perform 2962606Sitojun * try-and-error for region size. 3062606Sitojun */ 3192986Sobrien 3292986Sobrien#include <sys/cdefs.h> 3392986Sobrien__FBSDID("$FreeBSD: stable/10/lib/libc/net/getifaddrs.c 309485 2016-12-03 17:17:42Z ngie $"); 3492986Sobrien 3571579Sdeischen#include "namespace.h" 3662606Sitojun#include <sys/types.h> 3762606Sitojun#include <sys/ioctl.h> 3862606Sitojun#include <sys/socket.h> 3962606Sitojun#include <net/if.h> 4062606Sitojun#ifdef NET_RT_IFLIST 4162606Sitojun#include <sys/param.h> 4262606Sitojun#include <net/route.h> 4362606Sitojun#include <sys/sysctl.h> 4462606Sitojun#include <net/if_dl.h> 4562606Sitojun#endif 4662606Sitojun 47100657Sume#include <errno.h> 4862606Sitojun#include <ifaddrs.h> 4962606Sitojun#include <stdlib.h> 5062606Sitojun#include <string.h> 5171579Sdeischen#include "un-namespace.h" 5262606Sitojun 5362606Sitojun#if !defined(AF_LINK) 5462606Sitojun#define SA_LEN(sa) sizeof(struct sockaddr) 5562606Sitojun#endif 5662606Sitojun 5762606Sitojun#if !defined(SA_LEN) 5862606Sitojun#define SA_LEN(sa) (sa)->sa_len 5962606Sitojun#endif 6062606Sitojun 6162606Sitojun#define SALIGN (sizeof(long) - 1) 6262606Sitojun#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1)) 6362606Sitojun 6462606Sitojun#ifndef ALIGNBYTES 6562606Sitojun/* 6662606Sitojun * On systems with a routing socket, ALIGNBYTES should match the value 6762606Sitojun * that the kernel uses when building the messages. 6862606Sitojun */ 6962606Sitojun#define ALIGNBYTES XXX 7062606Sitojun#endif 7162606Sitojun#ifndef ALIGN 7262606Sitojun#define ALIGN(p) (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES) 7362606Sitojun#endif 7462606Sitojun 75100657Sume#define MAX_SYSCTL_TRY 5 76100657Sume 7762606Sitojunint 7862606Sitojungetifaddrs(struct ifaddrs **pif) 7962606Sitojun{ 8062606Sitojun int icnt = 1; 8162606Sitojun int dcnt = 0; 8262606Sitojun int ncnt = 0; 83100657Sume int ntry = 0; 8462606Sitojun int mib[6]; 8562606Sitojun size_t needed; 8662606Sitojun char *buf; 8762606Sitojun char *next; 88309485Sngie struct ifaddrs *cif; 8962606Sitojun char *p, *p0; 9062606Sitojun struct rt_msghdr *rtm; 91231506Sbz struct if_msghdrl *ifm; 92231506Sbz struct ifa_msghdrl *ifam; 9362606Sitojun struct sockaddr_dl *dl; 9462606Sitojun struct sockaddr *sa; 9562606Sitojun struct ifaddrs *ifa, *ift; 96231506Sbz struct if_data *if_data; 9784372Sume u_short idx = 0; 9862606Sitojun int i; 9962606Sitojun size_t len, alen; 10062606Sitojun char *data; 10162606Sitojun char *names; 10262606Sitojun 10362606Sitojun mib[0] = CTL_NET; 10462606Sitojun mib[1] = PF_ROUTE; 10562606Sitojun mib[2] = 0; /* protocol */ 10662606Sitojun mib[3] = 0; /* wildcard address family */ 107231506Sbz mib[4] = NET_RT_IFLISTL;/* extra fields for extensible msghdr structs */ 10862606Sitojun mib[5] = 0; /* no flags */ 109100657Sume do { 110100657Sume /* 111100657Sume * We'll try to get addresses several times in case that 112100657Sume * the number of addresses is unexpectedly increased during 113100657Sume * the two sysctl calls. This should rarely happen, but we'll 114100657Sume * try to do our best for applications that assume success of 115100657Sume * this library (which should usually be the case). 116100657Sume * Portability note: since FreeBSD does not add margin of 117100657Sume * memory at the first sysctl, the possibility of failure on 118100657Sume * the second sysctl call is a bit higher. 119100657Sume */ 12062606Sitojun 121100657Sume if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 122100657Sume return (-1); 123100657Sume if ((buf = malloc(needed)) == NULL) 124100657Sume return (-1); 125100657Sume if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 126100657Sume if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { 127100657Sume free(buf); 128100657Sume return (-1); 129100657Sume } 130100657Sume free(buf); 131100657Sume buf = NULL; 132100657Sume } 133100657Sume } while (buf == NULL); 134100657Sume 13562606Sitojun for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 13684372Sume rtm = (struct rt_msghdr *)(void *)next; 13762606Sitojun if (rtm->rtm_version != RTM_VERSION) 13862606Sitojun continue; 13962606Sitojun switch (rtm->rtm_type) { 14062606Sitojun case RTM_IFINFO: 141231506Sbz ifm = (struct if_msghdrl *)(void *)rtm; 14262606Sitojun if (ifm->ifm_addrs & RTA_IFP) { 14384372Sume idx = ifm->ifm_index; 14462606Sitojun ++icnt; 145231506Sbz if_data = IF_MSGHDRL_IFM_DATA(ifm); 146231506Sbz dcnt += if_data->ifi_datalen; 147231506Sbz dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm); 14884372Sume dcnt += SA_RLEN((struct sockaddr *)(void*)dl) + 14984372Sume ALIGNBYTES; 15062606Sitojun ncnt += dl->sdl_nlen + 1; 15162606Sitojun } else 15284372Sume idx = 0; 15362606Sitojun break; 15462606Sitojun 15562606Sitojun case RTM_NEWADDR: 156231506Sbz ifam = (struct ifa_msghdrl *)(void *)rtm; 15784372Sume if (idx && ifam->ifam_index != idx) 15862606Sitojun abort(); /* this cannot happen */ 15962606Sitojun 16062606Sitojun#define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD) 16184372Sume if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 16262606Sitojun break; 163231506Sbz p = (char *)IFA_MSGHDRL_RTA(ifam); 16462606Sitojun ++icnt; 165231506Sbz if_data = IFA_MSGHDRL_IFAM_DATA(ifam); 166231506Sbz dcnt += if_data->ifi_datalen + ALIGNBYTES; 167231506Sbz 16862606Sitojun /* Scan to look for length of address */ 16962606Sitojun alen = 0; 17062606Sitojun for (p0 = p, i = 0; i < RTAX_MAX; i++) { 17162606Sitojun if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 17262606Sitojun == 0) 17362606Sitojun continue; 17484372Sume sa = (struct sockaddr *)(void *)p; 17562606Sitojun len = SA_RLEN(sa); 17662606Sitojun if (i == RTAX_IFA) { 17762606Sitojun alen = len; 17862606Sitojun break; 17962606Sitojun } 18062606Sitojun p += len; 18162606Sitojun } 18262606Sitojun for (p = p0, i = 0; i < RTAX_MAX; i++) { 18362606Sitojun if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 18462606Sitojun == 0) 18562606Sitojun continue; 18684372Sume sa = (struct sockaddr *)(void *)p; 18762606Sitojun len = SA_RLEN(sa); 18862606Sitojun if (i == RTAX_NETMASK && SA_LEN(sa) == 0) 18962606Sitojun dcnt += alen; 19062606Sitojun else 19162606Sitojun dcnt += len; 19262606Sitojun p += len; 19362606Sitojun } 19462606Sitojun break; 19562606Sitojun } 19662606Sitojun } 19762606Sitojun 19862606Sitojun if (icnt + dcnt + ncnt == 1) { 19962606Sitojun *pif = NULL; 20062606Sitojun free(buf); 20162606Sitojun return (0); 20262606Sitojun } 20362606Sitojun data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt); 20462606Sitojun if (data == NULL) { 20562606Sitojun free(buf); 20662606Sitojun return(-1); 20762606Sitojun } 20862606Sitojun 20984372Sume ifa = (struct ifaddrs *)(void *)data; 21062606Sitojun data += sizeof(struct ifaddrs) * icnt; 21162606Sitojun names = data + dcnt; 21262606Sitojun 21362606Sitojun memset(ifa, 0, sizeof(struct ifaddrs) * icnt); 21462606Sitojun ift = ifa; 21562606Sitojun 21684372Sume idx = 0; 217309485Sngie cif = NULL; 21862606Sitojun for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 21984372Sume rtm = (struct rt_msghdr *)(void *)next; 22062606Sitojun if (rtm->rtm_version != RTM_VERSION) 22162606Sitojun continue; 22262606Sitojun switch (rtm->rtm_type) { 22362606Sitojun case RTM_IFINFO: 224231506Sbz ifm = (struct if_msghdrl *)(void *)rtm; 225231506Sbz if ((ifm->ifm_addrs & RTA_IFP) == 0) { 226231506Sbz idx = 0; 227231506Sbz break; 228231506Sbz } 22962606Sitojun 230231506Sbz idx = ifm->ifm_index; 231231506Sbz dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm); 23262606Sitojun 233231506Sbz cif = ift; 234231506Sbz ift->ifa_name = names; 235231506Sbz ift->ifa_flags = (int)ifm->ifm_flags; 236231506Sbz memcpy(names, dl->sdl_data, (size_t)dl->sdl_nlen); 237231506Sbz names[dl->sdl_nlen] = 0; 238231506Sbz names += dl->sdl_nlen + 1; 23962606Sitojun 240231506Sbz ift->ifa_addr = (struct sockaddr *)(void *)data; 241231506Sbz memcpy(data, dl, (size_t)SA_LEN((struct sockaddr *) 242231506Sbz (void *)dl)); 243231506Sbz data += SA_RLEN((struct sockaddr *)(void *)dl); 24462606Sitojun 245231506Sbz if_data = IF_MSGHDRL_IFM_DATA(ifm); 246231506Sbz /* ifm_data needs to be aligned */ 247231506Sbz ift->ifa_data = data = (void *)ALIGN(data); 248231506Sbz memcpy(data, if_data, if_data->ifi_datalen); 249231506Sbz data += if_data->ifi_datalen; 250231506Sbz 251231506Sbz ift = (ift->ifa_next = ift + 1); 25262606Sitojun break; 25362606Sitojun 25462606Sitojun case RTM_NEWADDR: 255231506Sbz ifam = (struct ifa_msghdrl *)(void *)rtm; 25684372Sume if (idx && ifam->ifam_index != idx) 25762606Sitojun abort(); /* this cannot happen */ 25862606Sitojun 25984372Sume if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 26062606Sitojun break; 26162606Sitojun ift->ifa_name = cif->ifa_name; 26262606Sitojun ift->ifa_flags = cif->ifa_flags; 26362606Sitojun ift->ifa_data = NULL; 264231506Sbz 265231506Sbz p = (char *)IFA_MSGHDRL_RTA(ifam); 26662606Sitojun /* Scan to look for length of address */ 26762606Sitojun alen = 0; 26862606Sitojun for (p0 = p, i = 0; i < RTAX_MAX; i++) { 26962606Sitojun if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 27062606Sitojun == 0) 27162606Sitojun continue; 27284372Sume sa = (struct sockaddr *)(void *)p; 27362606Sitojun len = SA_RLEN(sa); 27462606Sitojun if (i == RTAX_IFA) { 27562606Sitojun alen = len; 27662606Sitojun break; 27762606Sitojun } 27862606Sitojun p += len; 27962606Sitojun } 28062606Sitojun for (p = p0, i = 0; i < RTAX_MAX; i++) { 28162606Sitojun if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 28262606Sitojun == 0) 28362606Sitojun continue; 28484372Sume sa = (struct sockaddr *)(void *)p; 28562606Sitojun len = SA_RLEN(sa); 28662606Sitojun switch (i) { 28762606Sitojun case RTAX_IFA: 28884372Sume ift->ifa_addr = 28984372Sume (struct sockaddr *)(void *)data; 29062606Sitojun memcpy(data, p, len); 29162606Sitojun data += len; 29262606Sitojun break; 29362606Sitojun 29462606Sitojun case RTAX_NETMASK: 29562606Sitojun ift->ifa_netmask = 29684372Sume (struct sockaddr *)(void *)data; 29762606Sitojun if (SA_LEN(sa) == 0) { 29862606Sitojun memset(data, 0, alen); 29962606Sitojun data += alen; 30062606Sitojun break; 30162606Sitojun } 30262606Sitojun memcpy(data, p, len); 30362606Sitojun data += len; 30462606Sitojun break; 30562606Sitojun 30662606Sitojun case RTAX_BRD: 30762606Sitojun ift->ifa_broadaddr = 30884372Sume (struct sockaddr *)(void *)data; 30962606Sitojun memcpy(data, p, len); 31062606Sitojun data += len; 31162606Sitojun break; 31262606Sitojun } 31362606Sitojun p += len; 31462606Sitojun } 31562606Sitojun 316231506Sbz if_data = IFA_MSGHDRL_IFAM_DATA(ifam); 31762606Sitojun /* ifam_data needs to be aligned */ 31862606Sitojun ift->ifa_data = data = (void *)ALIGN(data); 319231506Sbz memcpy(data, if_data, if_data->ifi_datalen); 320231506Sbz data += if_data->ifi_datalen; 32162606Sitojun 32262606Sitojun ift = (ift->ifa_next = ift + 1); 32362606Sitojun break; 32462606Sitojun } 32562606Sitojun } 32662606Sitojun 32762606Sitojun free(buf); 32862606Sitojun 32962606Sitojun if (--ift >= ifa) { 33062606Sitojun ift->ifa_next = NULL; 33162606Sitojun *pif = ifa; 33262606Sitojun } else { 33362606Sitojun *pif = NULL; 33462606Sitojun free(ifa); 33562606Sitojun } 33662606Sitojun return (0); 33762606Sitojun} 33862606Sitojun 33962606Sitojunvoid 34062606Sitojunfreeifaddrs(struct ifaddrs *ifp) 34162606Sitojun{ 34284372Sume 34362606Sitojun free(ifp); 34462606Sitojun} 345