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$"); 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 7562606Sitojun#if _BSDI_VERSION >= 199701 7662606Sitojun#define HAVE_IFM_DATA 7762606Sitojun#endif 7862606Sitojun 7962606Sitojun#if _BSDI_VERSION >= 199802 8067800Sume/* ifam_data is very specific to recent versions of bsdi */ 8162606Sitojun#define HAVE_IFAM_DATA 8262606Sitojun#endif 8362606Sitojun 8467800Sume#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) 8567800Sume#define HAVE_IFM_DATA 8667800Sume#endif 8767800Sume 88100657Sume#define MAX_SYSCTL_TRY 5 89100657Sume 9062606Sitojunint 9162606Sitojungetifaddrs(struct ifaddrs **pif) 9262606Sitojun{ 9362606Sitojun int icnt = 1; 9462606Sitojun int dcnt = 0; 9562606Sitojun int ncnt = 0; 9662606Sitojun#ifdef NET_RT_IFLIST 97100657Sume int ntry = 0; 9862606Sitojun int mib[6]; 9962606Sitojun size_t needed; 10062606Sitojun char *buf; 10162606Sitojun char *next; 10262606Sitojun struct ifaddrs *cif = 0; 10362606Sitojun char *p, *p0; 10462606Sitojun struct rt_msghdr *rtm; 10562606Sitojun struct if_msghdr *ifm; 10662606Sitojun struct ifa_msghdr *ifam; 10762606Sitojun struct sockaddr_dl *dl; 10862606Sitojun struct sockaddr *sa; 10962606Sitojun struct ifaddrs *ifa, *ift; 11084372Sume u_short idx = 0; 11162606Sitojun#else /* NET_RT_IFLIST */ 11262606Sitojun char buf[1024]; 11362606Sitojun int m, sock; 11462606Sitojun struct ifconf ifc; 11562606Sitojun struct ifreq *ifr; 11662606Sitojun struct ifreq *lifr; 11762606Sitojun#endif /* NET_RT_IFLIST */ 11862606Sitojun int i; 11962606Sitojun size_t len, alen; 12062606Sitojun char *data; 12162606Sitojun char *names; 12262606Sitojun 12362606Sitojun#ifdef NET_RT_IFLIST 12462606Sitojun mib[0] = CTL_NET; 12562606Sitojun mib[1] = PF_ROUTE; 12662606Sitojun mib[2] = 0; /* protocol */ 12762606Sitojun mib[3] = 0; /* wildcard address family */ 12862606Sitojun mib[4] = NET_RT_IFLIST; 12962606Sitojun mib[5] = 0; /* no flags */ 130100657Sume do { 131100657Sume /* 132100657Sume * We'll try to get addresses several times in case that 133100657Sume * the number of addresses is unexpectedly increased during 134100657Sume * the two sysctl calls. This should rarely happen, but we'll 135100657Sume * try to do our best for applications that assume success of 136100657Sume * this library (which should usually be the case). 137100657Sume * Portability note: since FreeBSD does not add margin of 138100657Sume * memory at the first sysctl, the possibility of failure on 139100657Sume * the second sysctl call is a bit higher. 140100657Sume */ 14162606Sitojun 142100657Sume if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 143100657Sume return (-1); 144100657Sume if ((buf = malloc(needed)) == NULL) 145100657Sume return (-1); 146100657Sume if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 147100657Sume if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { 148100657Sume free(buf); 149100657Sume return (-1); 150100657Sume } 151100657Sume free(buf); 152100657Sume buf = NULL; 153100657Sume } 154100657Sume } while (buf == NULL); 155100657Sume 15662606Sitojun for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 15784372Sume rtm = (struct rt_msghdr *)(void *)next; 15862606Sitojun if (rtm->rtm_version != RTM_VERSION) 15962606Sitojun continue; 16062606Sitojun switch (rtm->rtm_type) { 16162606Sitojun case RTM_IFINFO: 16284372Sume ifm = (struct if_msghdr *)(void *)rtm; 16362606Sitojun if (ifm->ifm_addrs & RTA_IFP) { 16484372Sume idx = ifm->ifm_index; 16562606Sitojun ++icnt; 16684372Sume dl = (struct sockaddr_dl *)(void *)(ifm + 1); 16784372Sume dcnt += SA_RLEN((struct sockaddr *)(void*)dl) + 16884372Sume ALIGNBYTES; 16962606Sitojun#ifdef HAVE_IFM_DATA 17062606Sitojun dcnt += sizeof(ifm->ifm_data); 17162606Sitojun#endif /* HAVE_IFM_DATA */ 17262606Sitojun ncnt += dl->sdl_nlen + 1; 17362606Sitojun } else 17484372Sume idx = 0; 17562606Sitojun break; 17662606Sitojun 17762606Sitojun case RTM_NEWADDR: 17884372Sume ifam = (struct ifa_msghdr *)(void *)rtm; 17984372Sume if (idx && ifam->ifam_index != idx) 18062606Sitojun abort(); /* this cannot happen */ 18162606Sitojun 18262606Sitojun#define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD) 18384372Sume if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 18462606Sitojun break; 18584372Sume p = (char *)(void *)(ifam + 1); 18662606Sitojun ++icnt; 18762606Sitojun#ifdef HAVE_IFAM_DATA 18862606Sitojun dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES; 18962606Sitojun#endif /* HAVE_IFAM_DATA */ 19062606Sitojun /* Scan to look for length of address */ 19162606Sitojun alen = 0; 19262606Sitojun for (p0 = p, i = 0; i < RTAX_MAX; i++) { 19362606Sitojun if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 19462606Sitojun == 0) 19562606Sitojun continue; 19684372Sume sa = (struct sockaddr *)(void *)p; 19762606Sitojun len = SA_RLEN(sa); 19862606Sitojun if (i == RTAX_IFA) { 19962606Sitojun alen = len; 20062606Sitojun break; 20162606Sitojun } 20262606Sitojun p += len; 20362606Sitojun } 20462606Sitojun for (p = p0, i = 0; i < RTAX_MAX; i++) { 20562606Sitojun if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 20662606Sitojun == 0) 20762606Sitojun continue; 20884372Sume sa = (struct sockaddr *)(void *)p; 20962606Sitojun len = SA_RLEN(sa); 21062606Sitojun if (i == RTAX_NETMASK && SA_LEN(sa) == 0) 21162606Sitojun dcnt += alen; 21262606Sitojun else 21362606Sitojun dcnt += len; 21462606Sitojun p += len; 21562606Sitojun } 21662606Sitojun break; 21762606Sitojun } 21862606Sitojun } 21962606Sitojun#else /* NET_RT_IFLIST */ 22062606Sitojun ifc.ifc_buf = buf; 22162606Sitojun ifc.ifc_len = sizeof(buf); 22262606Sitojun 22371579Sdeischen if ((sock = _socket(AF_INET, SOCK_STREAM, 0)) < 0) 22462606Sitojun return (-1); 22571579Sdeischen i = _ioctl(sock, SIOCGIFCONF, (char *)&ifc); 22671579Sdeischen _close(sock); 22762606Sitojun if (i < 0) 22862606Sitojun return (-1); 22962606Sitojun 23062606Sitojun ifr = ifc.ifc_req; 23162606Sitojun lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; 23262606Sitojun 23362606Sitojun while (ifr < lifr) { 23462606Sitojun struct sockaddr *sa; 23562606Sitojun 23662606Sitojun sa = &ifr->ifr_addr; 23762606Sitojun ++icnt; 23862606Sitojun dcnt += SA_RLEN(sa); 23962606Sitojun ncnt += sizeof(ifr->ifr_name) + 1; 24062606Sitojun 24184372Sume if (SA_LEN(sa) < sizeof(*sa)) 24284372Sume ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa)); 24384372Sume else 24484372Sume ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); 24562606Sitojun } 24662606Sitojun#endif /* NET_RT_IFLIST */ 24762606Sitojun 24862606Sitojun if (icnt + dcnt + ncnt == 1) { 24962606Sitojun *pif = NULL; 25062606Sitojun free(buf); 25162606Sitojun return (0); 25262606Sitojun } 25362606Sitojun data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt); 25462606Sitojun if (data == NULL) { 25562606Sitojun free(buf); 25662606Sitojun return(-1); 25762606Sitojun } 25862606Sitojun 25984372Sume ifa = (struct ifaddrs *)(void *)data; 26062606Sitojun data += sizeof(struct ifaddrs) * icnt; 26162606Sitojun names = data + dcnt; 26262606Sitojun 26362606Sitojun memset(ifa, 0, sizeof(struct ifaddrs) * icnt); 26462606Sitojun ift = ifa; 26562606Sitojun 26662606Sitojun#ifdef NET_RT_IFLIST 26784372Sume idx = 0; 26862606Sitojun for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 26984372Sume rtm = (struct rt_msghdr *)(void *)next; 27062606Sitojun if (rtm->rtm_version != RTM_VERSION) 27162606Sitojun continue; 27262606Sitojun switch (rtm->rtm_type) { 27362606Sitojun case RTM_IFINFO: 27484372Sume ifm = (struct if_msghdr *)(void *)rtm; 27562606Sitojun if (ifm->ifm_addrs & RTA_IFP) { 27684372Sume idx = ifm->ifm_index; 27784372Sume dl = (struct sockaddr_dl *)(void *)(ifm + 1); 27862606Sitojun 27962606Sitojun cif = ift; 28062606Sitojun ift->ifa_name = names; 28162606Sitojun ift->ifa_flags = (int)ifm->ifm_flags; 28284372Sume memcpy(names, dl->sdl_data, 28384372Sume (size_t)dl->sdl_nlen); 28462606Sitojun names[dl->sdl_nlen] = 0; 28562606Sitojun names += dl->sdl_nlen + 1; 28662606Sitojun 28784372Sume ift->ifa_addr = (struct sockaddr *)(void *)data; 28884372Sume memcpy(data, dl, 28984372Sume (size_t)SA_LEN((struct sockaddr *) 29084372Sume (void *)dl)); 29184372Sume data += SA_RLEN((struct sockaddr *)(void *)dl); 29262606Sitojun 29362606Sitojun#ifdef HAVE_IFM_DATA 29462606Sitojun /* ifm_data needs to be aligned */ 29562606Sitojun ift->ifa_data = data = (void *)ALIGN(data); 29662606Sitojun memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data)); 29762606Sitojun data += sizeof(ifm->ifm_data); 29862606Sitojun#else /* HAVE_IFM_DATA */ 29962606Sitojun ift->ifa_data = NULL; 30062606Sitojun#endif /* HAVE_IFM_DATA */ 30162606Sitojun 30262606Sitojun ift = (ift->ifa_next = ift + 1); 30362606Sitojun } else 30484372Sume idx = 0; 30562606Sitojun break; 30662606Sitojun 30762606Sitojun case RTM_NEWADDR: 30884372Sume ifam = (struct ifa_msghdr *)(void *)rtm; 30984372Sume if (idx && ifam->ifam_index != idx) 31062606Sitojun abort(); /* this cannot happen */ 31162606Sitojun 31284372Sume if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 31362606Sitojun break; 31462606Sitojun ift->ifa_name = cif->ifa_name; 31562606Sitojun ift->ifa_flags = cif->ifa_flags; 31662606Sitojun ift->ifa_data = NULL; 31784372Sume p = (char *)(void *)(ifam + 1); 31862606Sitojun /* Scan to look for length of address */ 31962606Sitojun alen = 0; 32062606Sitojun for (p0 = p, i = 0; i < RTAX_MAX; i++) { 32162606Sitojun if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 32262606Sitojun == 0) 32362606Sitojun continue; 32484372Sume sa = (struct sockaddr *)(void *)p; 32562606Sitojun len = SA_RLEN(sa); 32662606Sitojun if (i == RTAX_IFA) { 32762606Sitojun alen = len; 32862606Sitojun break; 32962606Sitojun } 33062606Sitojun p += len; 33162606Sitojun } 33262606Sitojun for (p = p0, i = 0; i < RTAX_MAX; i++) { 33362606Sitojun if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 33462606Sitojun == 0) 33562606Sitojun continue; 33684372Sume sa = (struct sockaddr *)(void *)p; 33762606Sitojun len = SA_RLEN(sa); 33862606Sitojun switch (i) { 33962606Sitojun case RTAX_IFA: 34084372Sume ift->ifa_addr = 34184372Sume (struct sockaddr *)(void *)data; 34262606Sitojun memcpy(data, p, len); 34362606Sitojun data += len; 34462606Sitojun break; 34562606Sitojun 34662606Sitojun case RTAX_NETMASK: 34762606Sitojun ift->ifa_netmask = 34884372Sume (struct sockaddr *)(void *)data; 34962606Sitojun if (SA_LEN(sa) == 0) { 35062606Sitojun memset(data, 0, alen); 35162606Sitojun data += alen; 35262606Sitojun break; 35362606Sitojun } 35462606Sitojun memcpy(data, p, len); 35562606Sitojun data += len; 35662606Sitojun break; 35762606Sitojun 35862606Sitojun case RTAX_BRD: 35962606Sitojun ift->ifa_broadaddr = 36084372Sume (struct sockaddr *)(void *)data; 36162606Sitojun memcpy(data, p, len); 36262606Sitojun data += len; 36362606Sitojun break; 36462606Sitojun } 36562606Sitojun p += len; 36662606Sitojun } 36762606Sitojun 36862606Sitojun#ifdef HAVE_IFAM_DATA 36962606Sitojun /* ifam_data needs to be aligned */ 37062606Sitojun ift->ifa_data = data = (void *)ALIGN(data); 37162606Sitojun memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data)); 37262606Sitojun data += sizeof(ifam->ifam_data); 37362606Sitojun#endif /* HAVE_IFAM_DATA */ 37462606Sitojun 37562606Sitojun ift = (ift->ifa_next = ift + 1); 37662606Sitojun break; 37762606Sitojun } 37862606Sitojun } 37962606Sitojun 38062606Sitojun free(buf); 38162606Sitojun#else /* NET_RT_IFLIST */ 38262606Sitojun ifr = ifc.ifc_req; 38362606Sitojun lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; 38462606Sitojun 38562606Sitojun while (ifr < lifr) { 38662606Sitojun struct sockaddr *sa; 38762606Sitojun 38862606Sitojun ift->ifa_name = names; 38962606Sitojun names[sizeof(ifr->ifr_name)] = 0; 39062606Sitojun strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name)); 39162606Sitojun while (*names++) 39262606Sitojun ; 39362606Sitojun 39462606Sitojun ift->ifa_addr = (struct sockaddr *)data; 39562606Sitojun sa = &ifr->ifr_addr; 39662606Sitojun memcpy(data, sa, SA_LEN(sa)); 39762606Sitojun data += SA_RLEN(sa); 39862606Sitojun 39962606Sitojun ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); 40062606Sitojun ift = (ift->ifa_next = ift + 1); 40162606Sitojun } 40262606Sitojun#endif /* NET_RT_IFLIST */ 40362606Sitojun if (--ift >= ifa) { 40462606Sitojun ift->ifa_next = NULL; 40562606Sitojun *pif = ifa; 40662606Sitojun } else { 40762606Sitojun *pif = NULL; 40862606Sitojun free(ifa); 40962606Sitojun } 41062606Sitojun return (0); 41162606Sitojun} 41262606Sitojun 41362606Sitojunvoid 41462606Sitojunfreeifaddrs(struct ifaddrs *ifp) 41562606Sitojun{ 41684372Sume 41762606Sitojun free(ifp); 41862606Sitojun} 419