Home | History | Annotate | Line # | Download | only in net
getifaddrs.c revision 1.3
      1 /*	$NetBSD: getifaddrs.c,v 1.3 2000/04/24 09:27:31 itojun Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995, 1999
      5  *	Berkeley Software Design, Inc.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
     14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     16  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
     17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     23  * SUCH DAMAGE.
     24  *
     25  *	BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp
     26  */
     27 #include "namespace.h"
     28 #include <sys/types.h>
     29 #include <sys/ioctl.h>
     30 #include <sys/socket.h>
     31 #include <net/if.h>
     32 #ifdef	NET_RT_IFLIST
     33 #include <sys/param.h>
     34 #include <net/route.h>
     35 #include <sys/sysctl.h>
     36 #include <net/if_dl.h>
     37 #endif
     38 
     39 #include <errno.h>
     40 #include <ifaddrs.h>
     41 #include <stdlib.h>
     42 #include <string.h>
     43 
     44 #ifdef __weak_alias
     45 __weak_alias(getifaddrs,_getifaddrs)
     46 __weak_alias(freeifaddrs,_freeifaddrs)
     47 #endif
     48 
     49 #if !defined(AF_LINK)
     50 #define	SA_LEN(sa)	sizeof(struct sockaddr)
     51 #endif
     52 
     53 #if !defined(SA_LEN)
     54 #define	SA_LEN(sa)	(sa)->sa_len
     55 #endif
     56 
     57 #define	SALIGN	(sizeof(long) - 1)
     58 #define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
     59 
     60 #ifndef	ALIGNBYTES
     61 /*
     62  * On systems with a routing socket, ALIGNBYTES should match the value
     63  * that the kernel uses when building the messages.
     64  */
     65 #define	ALIGNBYTES	XXX
     66 #endif
     67 #ifndef	ALIGN
     68 #define	ALIGN(p)	(((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
     69 #endif
     70 
     71 #if	_BSDI_VERSION >= 199701
     72 #define	HAVE_IFM_DATA
     73 #endif
     74 
     75 #if	_BSDI_VERSION >= 199802
     76 #define	HAVE_IFAM_DATA
     77 #endif
     78 
     79 int
     80 getifaddrs(struct ifaddrs **pif)
     81 {
     82 	int icnt = 1;
     83 	int dcnt = 0;
     84 	int ncnt = 0;
     85 #ifdef	NET_RT_IFLIST
     86 	int mib[6];
     87 	size_t needed;
     88 	char *buf;
     89 	char *next;
     90 	struct ifaddrs *cif = 0;
     91 	char *p, *p0;
     92 	struct rt_msghdr *rtm;
     93 	struct if_msghdr *ifm;
     94 	struct ifa_msghdr *ifam;
     95 	struct sockaddr_dl *dl;
     96 	struct sockaddr *sa;
     97 	struct ifaddrs *ifa, *ift;
     98 	u_short index = 0;
     99 #else	/* NET_RT_IFLIST */
    100 	char buf[1024];
    101 	int m, sock;
    102 	struct ifconf ifc;
    103 	struct ifreq *ifr;
    104 	struct ifreq *lifr;
    105 #endif	/* NET_RT_IFLIST */
    106 	int i;
    107 	size_t len, alen;
    108 	char *data;
    109 	char *names;
    110 
    111 #ifdef	NET_RT_IFLIST
    112 	mib[0] = CTL_NET;
    113 	mib[1] = PF_ROUTE;
    114 	mib[2] = 0;             /* protocol */
    115 	mib[3] = 0;             /* wildcard address family */
    116 	mib[4] = NET_RT_IFLIST;
    117 	mib[5] = 0;             /* no flags */
    118 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
    119 		return (-1);
    120 	if ((buf = malloc(needed)) == NULL)
    121 		return (-1);
    122 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
    123 		free(buf);
    124 		return (-1);
    125 	}
    126 
    127 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
    128 		rtm = (struct rt_msghdr *)next;
    129 		if (rtm->rtm_version != RTM_VERSION)
    130 			continue;
    131 		switch (rtm->rtm_type) {
    132 		case RTM_IFINFO:
    133 			ifm = (struct if_msghdr *)rtm;
    134 			if (ifm->ifm_addrs & RTA_IFP) {
    135 				index = ifm->ifm_index;
    136 				++icnt;
    137 				dl = (struct sockaddr_dl *)(ifm + 1);
    138 				dcnt += SA_RLEN((struct sockaddr *)dl) +
    139 					ALIGNBYTES;
    140 #ifdef	HAVE_IFM_DATA
    141 				dcnt += sizeof(ifm->ifm_data);
    142 #endif	/* HAVE_IFM_DATA */
    143 				ncnt += dl->sdl_nlen + 1;
    144 			} else
    145 				index = 0;
    146 			break;
    147 
    148 		case RTM_NEWADDR:
    149 			ifam = (struct ifa_msghdr *)rtm;
    150 			if (index && ifam->ifam_index != index)
    151 				abort();	/* this cannot happen */
    152 
    153 #define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
    154 			if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
    155 				break;
    156 			p = (char *)(ifam + 1);
    157 			++icnt;
    158 #ifdef	HAVE_IFAM_DATA
    159 			dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES;
    160 #endif	/* HAVE_IFAM_DATA */
    161 			/* Scan to look for length of address */
    162 			alen = 0;
    163 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
    164 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
    165 				    == 0)
    166 					continue;
    167 				sa = (struct sockaddr *)p;
    168 				len = SA_RLEN(sa);
    169 				if (i == RTAX_IFA) {
    170 					alen = len;
    171 					break;
    172 				}
    173 				p += len;
    174 			}
    175 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
    176 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
    177 				    == 0)
    178 					continue;
    179 				sa = (struct sockaddr *)p;
    180 				len = SA_RLEN(sa);
    181 				if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
    182 					dcnt += alen;
    183 				else
    184 					dcnt += len;
    185 				p += len;
    186 			}
    187 			break;
    188 		}
    189 	}
    190 #else	/* NET_RT_IFLIST */
    191 	ifc.ifc_buf = buf;
    192 	ifc.ifc_len = sizeof(buf);
    193 
    194 	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    195 		return (-1);
    196 	i =  ioctl(sock, SIOCGIFCONF, (char *)&ifc);
    197 	close(sock);
    198 	if (i < 0)
    199 		return (-1);
    200 
    201 	ifr = ifc.ifc_req;
    202 	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
    203 
    204 	while (ifr < lifr) {
    205 		struct sockaddr *sa;
    206 
    207 		sa = &ifr->ifr_addr;
    208 		++icnt;
    209 		dcnt += SA_RLEN(sa);
    210 		ncnt += sizeof(ifr->ifr_name) + 1;
    211 
    212 		ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
    213 	}
    214 #endif	/* NET_RT_IFLIST */
    215 
    216 	if (icnt + dcnt + ncnt == 1) {
    217 		*pif = NULL;
    218 		free(buf);
    219 		return (0);
    220 	}
    221 	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
    222 	if (data == NULL) {
    223 		free(buf);
    224 		return(-1);
    225 	}
    226 
    227 	ifa = (struct ifaddrs *)data;
    228 	data += sizeof(struct ifaddrs) * icnt;
    229 	names = data + dcnt;
    230 
    231 	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
    232 	ift = ifa;
    233 
    234 #ifdef	NET_RT_IFLIST
    235 	index = 0;
    236 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
    237 		rtm = (struct rt_msghdr *)next;
    238 		if (rtm->rtm_version != RTM_VERSION)
    239 			continue;
    240 		switch (rtm->rtm_type) {
    241 		case RTM_IFINFO:
    242 			ifm = (struct if_msghdr *)rtm;
    243 			if (ifm->ifm_addrs & RTA_IFP) {
    244 				index = ifm->ifm_index;
    245 				dl = (struct sockaddr_dl *)(ifm + 1);
    246 
    247 				cif = ift;
    248 				ift->ifa_name = names;
    249 				ift->ifa_flags = (int)ifm->ifm_flags;
    250 				memcpy(names, dl->sdl_data, dl->sdl_nlen);
    251 				names[dl->sdl_nlen] = 0;
    252 				names += dl->sdl_nlen + 1;
    253 
    254 				ift->ifa_addr = (struct sockaddr *)data;
    255 				memcpy(data, dl, SA_LEN((struct sockaddr *)dl));
    256 				data += SA_RLEN((struct sockaddr *)dl);
    257 
    258 #ifdef	HAVE_IFM_DATA
    259 				/* ifm_data needs to be aligned */
    260 				ift->ifa_data = data = (void *)ALIGN(data);
    261 				memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
    262  				data += sizeof(ifm->ifm_data);
    263 #else	/* HAVE_IFM_DATA */
    264 				ift->ifa_data = NULL;
    265 #endif	/* HAVE_IFM_DATA */
    266 
    267 				ift = (ift->ifa_next = ift + 1);
    268 			} else
    269 				index = 0;
    270 			break;
    271 
    272 		case RTM_NEWADDR:
    273 			ifam = (struct ifa_msghdr *)rtm;
    274 			if (index && ifam->ifam_index != index)
    275 				abort();	/* this cannot happen */
    276 
    277 			if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
    278 				break;
    279 			ift->ifa_name = cif->ifa_name;
    280 			ift->ifa_flags = cif->ifa_flags;
    281 			ift->ifa_data = NULL;
    282 			p = (char *)(ifam + 1);
    283 			/* Scan to look for length of address */
    284 			alen = 0;
    285 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
    286 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
    287 				    == 0)
    288 					continue;
    289 				sa = (struct sockaddr *)p;
    290 				len = SA_RLEN(sa);
    291 				if (i == RTAX_IFA) {
    292 					alen = len;
    293 					break;
    294 				}
    295 				p += len;
    296 			}
    297 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
    298 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
    299 				    == 0)
    300 					continue;
    301 				sa = (struct sockaddr *)p;
    302 				len = SA_RLEN(sa);
    303 				switch (i) {
    304 				case RTAX_IFA:
    305 					ift->ifa_addr = (struct sockaddr *)data;
    306 					memcpy(data, p, len);
    307 					data += len;
    308 					break;
    309 
    310 				case RTAX_NETMASK:
    311 					ift->ifa_netmask =
    312 					    (struct sockaddr *)data;
    313 					if (SA_LEN(sa) == 0) {
    314 						memset(data, 0, alen);
    315 						data += alen;
    316 						break;
    317 					}
    318 					memcpy(data, p, len);
    319 					data += len;
    320 					break;
    321 
    322 				case RTAX_BRD:
    323 					ift->ifa_broadaddr =
    324 					    (struct sockaddr *)data;
    325 					memcpy(data, p, len);
    326 					data += len;
    327 					break;
    328 				}
    329 				p += len;
    330 			}
    331 
    332 #ifdef	HAVE_IFAM_DATA
    333 			/* ifam_data needs to be aligned */
    334 			ift->ifa_data = data = (void *)ALIGN(data);
    335 			memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
    336 			data += sizeof(ifam->ifam_data);
    337 #endif	/* HAVE_IFAM_DATA */
    338 
    339 			ift = (ift->ifa_next = ift + 1);
    340 			break;
    341 		}
    342 	}
    343 
    344 	free(buf);
    345 #else	/* NET_RT_IFLIST */
    346 	ifr = ifc.ifc_req;
    347 	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
    348 
    349 	while (ifr < lifr) {
    350 		struct sockaddr *sa;
    351 
    352 		ift->ifa_name = names;
    353 		names[sizeof(ifr->ifr_name)] = 0;
    354 		strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
    355 		while (*names++)
    356 			;
    357 
    358 		ift->ifa_addr = (struct sockaddr *)data;
    359 		sa = &ifr->ifr_addr;
    360 		memcpy(data, sa, SA_LEN(sa));
    361 		data += SA_RLEN(sa);
    362 
    363 		ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
    364 		ift = (ift->ifa_next = ift + 1);
    365 	}
    366 #endif	/* NET_RT_IFLIST */
    367 	if (--ift >= ifa) {
    368 		ift->ifa_next = NULL;
    369 		*pif = ifa;
    370 	} else {
    371 		*pif = NULL;
    372 		free(ifa);
    373 	}
    374 	return (0);
    375 }
    376 
    377 void
    378 freeifaddrs(struct ifaddrs *ifp)
    379 {
    380 	free(ifp);
    381 }
    382