Home | History | Annotate | Download | only in ninfod
      1 /* $USAGI: ni_ifaddrs.c,v 1.8 2007-10-11 06:25:21 yoshfuji Exp $ */
      2 /*
      3  * Copyright (C) 2002 USAGI/WIDE Project.
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. Neither the name of the project nor the names of its contributors
     15  *    may be used to endorse or promote products derived from this software
     16  *    without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
     19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
     22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  * SUCH DAMAGE.
     29  */
     30 
     31 /* reformatted by indent -kr -i8 -l 1000 */
     32 /* USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp */
     33 
     34 /**************************************************************************
     35  * ifaddrs.c
     36  * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved.
     37  *
     38  * Redistribution and use in source and binary forms, with or without
     39  * modification, are permitted provided that the following conditions
     40  * are met:
     41  * 1. Redistributions of source code must retain the above copyright
     42  *    notice, this list of conditions and the following disclaimer.
     43  * 2. Redistributions in binary form must reproduce the above copyright
     44  *    notice, this list of conditions and the following disclaimer in the
     45  *    documentation and/or other materials provided with the distribution.
     46  * 3. Neither the name of the author nor the names of its contributors
     47  *    may be used to endorse or promote products derived from this software
     48  *    without specific prior written permission.
     49  *
     50  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     60  * SUCH DAMAGE.
     61  */
     62 
     63 #include "config.h"
     64 
     65 #include <string.h>
     66 #include <time.h>
     67 #include <malloc.h>
     68 #include <errno.h>
     69 #include <unistd.h>
     70 
     71 #include <sys/socket.h>
     72 #include <asm/types.h>
     73 #include <linux/netlink.h>
     74 #include <linux/rtnetlink.h>
     75 #include <sys/types.h>
     76 #include <sys/socket.h>
     77 #include <netpacket/packet.h>
     78 #include <net/ethernet.h>	/* the L2 protocols */
     79 #include <sys/uio.h>
     80 #include <net/if.h>
     81 #include <net/if_arp.h>
     82 #include "ni_ifaddrs.h"
     83 #include <netinet/in.h>
     84 
     85 #ifdef _USAGI_LIBINET6
     86 #include "libc-compat.h"
     87 #endif
     88 
     89 //#define IFA_LOCAL	IFA_LOCAL
     90 
     91 static const char *RCSID __attribute__ ((unused)) = "$USAGI: ni_ifaddrs.c,v 1.8 2007-10-11 06:25:21 yoshfuji Exp $ based on USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp";
     92 
     93 /* ====================================================================== */
     94 struct nlmsg_list {
     95 	struct nlmsg_list *nlm_next;
     96 	struct nlmsghdr *nlh;
     97 	int size;
     98 	time_t seq;
     99 };
    100 
    101 #ifndef IFA_LOCAL
    102 struct rtmaddr_ifamap {
    103 	void *address;
    104 	void *local;
    105 	void *broadcast;
    106 	int address_len;
    107 	int local_len;
    108 	int broadcast_len;
    109 };
    110 #endif
    111 
    112 /* ====================================================================== */
    113 static int nl_sendreq(int sd, int request, int flags, int *seq)
    114 {
    115 	char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))];
    116 	struct sockaddr_nl nladdr;
    117 	struct nlmsghdr *req_hdr;
    118 	struct rtgenmsg *req_msg;
    119 	time_t t = time(NULL);
    120 
    121 	if (seq)
    122 		*seq = t;
    123 	memset(&reqbuf, 0, sizeof(reqbuf));
    124 	req_hdr = (struct nlmsghdr *) reqbuf;
    125 	req_msg = (struct rtgenmsg *) NLMSG_DATA(req_hdr);
    126 	req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg));
    127 	req_hdr->nlmsg_type = request;
    128 	req_hdr->nlmsg_flags = flags | NLM_F_REQUEST;
    129 	req_hdr->nlmsg_pid = 0;
    130 	req_hdr->nlmsg_seq = t;
    131 	req_msg->rtgen_family = AF_UNSPEC;
    132 	memset(&nladdr, 0, sizeof(nladdr));
    133 	nladdr.nl_family = AF_NETLINK;
    134 	return (sendto(sd, (void *) req_hdr, req_hdr->nlmsg_len, 0, (struct sockaddr *) &nladdr, sizeof(nladdr)));
    135 }
    136 
    137 static int nl_recvmsg(int sd, int request, int seq, void *buf, size_t buflen, int *flags)
    138 {
    139 	struct msghdr msg;
    140 	struct iovec iov = { buf, buflen };
    141 	struct sockaddr_nl nladdr;
    142 	int read_len;
    143 
    144 	for (;;) {
    145 		msg.msg_name = (void *) &nladdr;
    146 		msg.msg_namelen = sizeof(nladdr);
    147 		msg.msg_iov = &iov;
    148 		msg.msg_iovlen = 1;
    149 		msg.msg_control = NULL;
    150 		msg.msg_controllen = 0;
    151 		msg.msg_flags = 0;
    152 		read_len = recvmsg(sd, &msg, 0);
    153 		if ((read_len < 0 && errno == EINTR)
    154 		    || (msg.msg_flags & MSG_TRUNC))
    155 			continue;
    156 		if (flags)
    157 			*flags = msg.msg_flags;
    158 		break;
    159 	}
    160 	return read_len;
    161 }
    162 
    163 static int nl_getmsg(int sd, int request, int seq, struct nlmsghdr **nlhp, int *done)
    164 {
    165 	struct nlmsghdr *nh;
    166 	size_t bufsize = 65536, lastbufsize = 0;
    167 	void *buff = NULL;
    168 	int result = 0, read_size;
    169 	int msg_flags;
    170 	pid_t pid = getpid();
    171 	for (;;) {
    172 		void *newbuff = realloc(buff, bufsize);
    173 		if (newbuff == NULL || bufsize < lastbufsize) {
    174 			free(newbuff);
    175 			result = -1;
    176 			break;
    177 		}
    178 		buff = newbuff;
    179 		result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags);
    180 		if (read_size < 0 || (msg_flags & MSG_TRUNC)) {
    181 			lastbufsize = bufsize;
    182 			bufsize *= 2;
    183 			continue;
    184 		}
    185 		if (read_size == 0)
    186 			break;
    187 		nh = (struct nlmsghdr *) buff;
    188 		for (nh = (struct nlmsghdr *) buff; NLMSG_OK(nh, read_size); nh = (struct nlmsghdr *) NLMSG_NEXT(nh, read_size)) {
    189 			if (nh->nlmsg_pid != pid || nh->nlmsg_seq != seq)
    190 				continue;
    191 			if (nh->nlmsg_type == NLMSG_DONE) {
    192 				(*done)++;
    193 				break;	/* ok */
    194 			}
    195 			if (nh->nlmsg_type == NLMSG_ERROR) {
    196 				struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA(nh);
    197 				result = -1;
    198 				if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
    199 					errno = EIO;
    200 				else
    201 					errno = -nlerr->error;
    202 				break;
    203 			}
    204 		}
    205 		break;
    206 	}
    207 	if (result < 0)
    208 		if (buff) {
    209 			int saved_errno = errno;
    210 			free(buff);
    211 			buff = NULL;
    212 			errno = saved_errno;
    213 		}
    214 	*nlhp = (struct nlmsghdr *) buff;
    215 	return result;
    216 }
    217 
    218 static int nl_getlist(int sd, int seq, int request, struct nlmsg_list **nlm_list, struct nlmsg_list **nlm_end)
    219 {
    220 	struct nlmsghdr *nlh = NULL;
    221 	int status;
    222 	int done = 0;
    223 
    224 	status = nl_sendreq(sd, request, NLM_F_ROOT | NLM_F_MATCH, &seq);
    225 	if (status < 0)
    226 		return status;
    227 	if (seq == 0)
    228 		seq = (int) time(NULL);
    229 	while (!done) {
    230 		status = nl_getmsg(sd, request, seq, &nlh, &done);
    231 		if (status < 0)
    232 			return status;
    233 		if (nlh) {
    234 			struct nlmsg_list *nlm_next = (struct nlmsg_list *) malloc(sizeof(struct nlmsg_list));
    235 			if (nlm_next == NULL) {
    236 				int saved_errno = errno;
    237 				free(nlh);
    238 				errno = saved_errno;
    239 				status = -1;
    240 			} else {
    241 				nlm_next->nlm_next = NULL;
    242 				nlm_next->nlh = (struct nlmsghdr *) nlh;
    243 				nlm_next->size = status;
    244 				nlm_next->seq = seq;
    245 				if (*nlm_list == NULL) {
    246 					*nlm_list = nlm_next;
    247 					*nlm_end = nlm_next;
    248 				} else {
    249 					(*nlm_end)->nlm_next = nlm_next;
    250 					*nlm_end = nlm_next;
    251 				}
    252 			}
    253 		}
    254 	}
    255 	return status >= 0 ? seq : status;
    256 }
    257 
    258 /* ---------------------------------------------------------------------- */
    259 static void free_nlmsglist(struct nlmsg_list *nlm0)
    260 {
    261 	struct nlmsg_list *nlm, *nlm_next;
    262 	int saved_errno;
    263 	if (!nlm0)
    264 		return;
    265 	saved_errno = errno;
    266 	nlm = nlm0;
    267 	while(nlm) {
    268 		if(nlm->nlh)
    269 			free(nlm->nlh);
    270 		nlm_next = nlm->nlm_next;
    271 		free(nlm);
    272 		nlm = nlm_next;
    273 	}
    274 	errno = saved_errno;
    275 }
    276 
    277 static void free_data(void *data)
    278 {
    279 	int saved_errno = errno;
    280 	if (data != NULL)
    281 		free(data);
    282 	errno = saved_errno;
    283 }
    284 
    285 /* ---------------------------------------------------------------------- */
    286 static void nl_close(int sd)
    287 {
    288 	int saved_errno = errno;
    289 	if (sd >= 0)
    290 		close(sd);
    291 	errno = saved_errno;
    292 }
    293 
    294 /* ---------------------------------------------------------------------- */
    295 static int nl_open(void)
    296 {
    297 	struct sockaddr_nl nladdr;
    298 	int sd;
    299 
    300 	sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    301 	if (sd < 0)
    302 		return -1;
    303 	memset(&nladdr, 0, sizeof(nladdr));
    304 	nladdr.nl_family = AF_NETLINK;
    305 	if (bind(sd, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
    306 		nl_close(sd);
    307 		return -1;
    308 	}
    309 	return sd;
    310 }
    311 
    312 /* ====================================================================== */
    313 int ni_ifaddrs(struct ni_ifaddrs **ifap, sa_family_t family)
    314 {
    315 	int sd;
    316 	struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm;
    317 	/* - - - - - - - - - - - - - - - */
    318 	int icnt;
    319 	size_t dlen, xlen;
    320 	uint32_t max_ifindex = 0;
    321 
    322 	pid_t pid = getpid();
    323 	int seq = 0;
    324 	int result;
    325 	int build;		/* 0 or 1 */
    326 
    327 /* ---------------------------------- */
    328 	/* initialize */
    329 	icnt = dlen = xlen = 0;
    330 	nlmsg_list = nlmsg_end = NULL;
    331 
    332 	if (ifap)
    333 		*ifap = NULL;
    334 
    335 /* ---------------------------------- */
    336 	/* open socket and bind */
    337 	sd = nl_open();
    338 	if (sd < 0)
    339 		return -1;
    340 
    341 /* ---------------------------------- */
    342 	/* gather info */
    343 	if ((seq = nl_getlist(sd, seq + 1, RTM_GETADDR, &nlmsg_list, &nlmsg_end)) < 0) {
    344 		free_nlmsglist(nlmsg_list);
    345 		nl_close(sd);
    346 		return -1;
    347 	}
    348 
    349 /* ---------------------------------- */
    350 	/* Estimate size of result buffer and fill it */
    351 	for (build = 0; build <= 1; build++) {
    352 		struct ni_ifaddrs *ifl = NULL, *ifa = NULL;
    353 		struct nlmsghdr *nlh, *nlh0;
    354 		void *data = NULL, *xdata = NULL;
    355 		uint16_t *ifflist = NULL;
    356 #ifndef IFA_LOCAL
    357 		struct rtmaddr_ifamap ifamap;
    358 #endif
    359 
    360 		if (build) {
    361 			ifa = data = calloc(1, NLMSG_ALIGN(sizeof(struct ni_ifaddrs[icnt]))
    362 					    + dlen + xlen);
    363 			if (ifap != NULL)
    364 				*ifap = ifa;
    365 			else {
    366 				free_data(data);
    367 				result = 0;
    368 				break;
    369 			}
    370 			if (data == NULL) {
    371 				free_data(data);
    372 				result = -1;
    373 				break;
    374 			}
    375 			ifl = NULL;
    376 			data += NLMSG_ALIGN(sizeof(struct ni_ifaddrs)) * icnt;
    377 			xdata = data + dlen;
    378 			ifflist = xdata + xlen;
    379 		}
    380 
    381 		for (nlm = nlmsg_list; nlm; nlm = nlm->nlm_next) {
    382 			int nlmlen = nlm->size;
    383 			if (!(nlh0 = nlm->nlh))
    384 				continue;
    385 			for (nlh = nlh0; NLMSG_OK(nlh, nlmlen); nlh = NLMSG_NEXT(nlh, nlmlen)) {
    386 				struct ifaddrmsg *ifam = NULL;
    387 				struct rtattr *rta;
    388 
    389 				size_t nlm_struct_size = 0;
    390 				sa_family_t nlm_family = 0;
    391 				uint32_t nlm_scope = 0, nlm_index = 0;
    392 				unsigned int nlm_flags;
    393 				size_t rtasize;
    394 
    395 #ifndef IFA_LOCAL
    396 				memset(&ifamap, 0, sizeof(ifamap));
    397 #endif
    398 
    399 				/* check if the message is what we want */
    400 				if (nlh->nlmsg_pid != pid || nlh->nlmsg_seq != nlm->seq)
    401 					continue;
    402 				if (nlh->nlmsg_type == NLMSG_DONE) {
    403 					break;	/* ok */
    404 				}
    405 				switch (nlh->nlmsg_type) {
    406 				case RTM_NEWADDR:
    407 					ifam = (struct ifaddrmsg *) NLMSG_DATA(nlh);
    408 					nlm_struct_size = sizeof(*ifam);
    409 					nlm_family = ifam->ifa_family;
    410 					nlm_scope = ifam->ifa_scope;
    411 					nlm_index = ifam->ifa_index;
    412 					nlm_flags = ifam->ifa_flags;
    413 					if (family && nlm_family != family)
    414 						continue;
    415 					if (build) {
    416 						ifa->ifa_ifindex = nlm_index;
    417 						ifa->ifa_flags = nlm_flags;
    418 					}
    419 					break;
    420 				default:
    421 					continue;
    422 				}
    423 
    424 				if (!build) {
    425 					if (max_ifindex < nlm_index)
    426 						max_ifindex = nlm_index;
    427 				} else {
    428 					if (ifl != NULL)
    429 						ifl->ifa_next = ifa;
    430 				}
    431 
    432 				rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size);
    433 				for (rta = (struct rtattr *) (((char *) NLMSG_DATA(nlh)) +
    434 									NLMSG_ALIGN(nlm_struct_size));
    435 				     RTA_OK(rta, rtasize);
    436 				     rta = RTA_NEXT(rta, rtasize)) {
    437 					void *rtadata = RTA_DATA(rta);
    438 					size_t rtapayload = RTA_PAYLOAD(rta);
    439 
    440 					switch (nlh->nlmsg_type) {
    441 					case RTM_NEWADDR:
    442 						if (nlm_family == AF_PACKET)
    443 							break;
    444 						switch (rta->rta_type) {
    445 #ifndef IFA_LOCAL
    446 						case IFA_ADDRESS:
    447 							ifamap.address = rtadata;
    448 							ifamap.address_len = rtapayload;
    449 							break;
    450 						case IFA_LOCAL:
    451 							ifamap.local = rtadata;
    452 							ifamap.local_len = rtapayload;
    453 							break;
    454 						case IFA_BROADCAST:
    455 							ifamap.broadcast = rtadata;
    456 							ifamap.broadcast_len = rtapayload;
    457 							break;
    458 						case IFA_LABEL:
    459 							break;
    460 						case IFA_UNSPEC:
    461 							break;
    462 #else
    463 						case IFA_LOCAL:
    464 							if (!build)
    465 								dlen += NLMSG_ALIGN(rtapayload);
    466 							else {
    467 								memcpy(data, rtadata, rtapayload);
    468 								ifa->ifa_addr = data;
    469 								data += NLMSG_ALIGN(rtapayload);
    470 							}
    471 							break;
    472 #endif
    473 						case IFA_CACHEINFO:
    474 							if (!build)
    475 								xlen += NLMSG_ALIGN(rtapayload);
    476 							else {
    477 								memcpy(xdata, rtadata, rtapayload);
    478 								ifa->ifa_cacheinfo = xdata;
    479 								xdata += NLMSG_ALIGN(rtapayload);
    480 							}
    481 							break;
    482 						}
    483 					}
    484 				}
    485 #ifndef IFA_LOCAL
    486 				if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != AF_PACKET) {
    487 					if (!ifamap.local) {
    488 						ifamap.local = ifamap.address;
    489 						ifamap.local_len = ifamap.address_len;
    490 					}
    491 					if (!ifamap.address) {
    492 						ifamap.address = ifamap.local;
    493 						ifamap.address_len = ifamap.local_len;
    494 					}
    495 					if (ifamap.address_len != ifamap.local_len ||
    496 					    (ifamap.address != NULL &&
    497 					     memcmp(ifamap.address, ifamap.local, ifamap.address_len))) {
    498 						/* p2p; address is peer and local is ours */
    499 						ifamap.broadcast = ifamap.address;
    500 						ifamap.broadcast_len = ifamap.address_len;
    501 						ifamap.address = ifamap.local;
    502 						ifamap.address_len = ifamap.local_len;
    503 					}
    504 					if (ifamap.address) {
    505 						if (!build)
    506 							dlen += NLMSG_ALIGN(ifamap.address_len);
    507 						else {
    508 							ifa->ifa_addr = (struct sockaddr *) data;
    509 							memcpy(ifa->ifa_addr, ifamap.address, ifamap.address_len);
    510 							data += NLMSG_ALIGN(ifamap.address_len);
    511 						}
    512 					}
    513 				}
    514 #endif
    515 				if (!build) {
    516 					icnt++;
    517 				} else {
    518 					ifl = ifa++;
    519 				}
    520 			}
    521 		}
    522 		if (!build) {
    523 			if (icnt == 0 && (dlen + xlen == 0)) {
    524 				if (ifap != NULL)
    525 					*ifap = NULL;
    526 				break;	/* cannot found any addresses */
    527 			}
    528 		}
    529 	}
    530 
    531 /* ---------------------------------- */
    532 	/* Finalize */
    533 	free_nlmsglist(nlmsg_list);
    534 	nl_close(sd);
    535 	return 0;
    536 }
    537 
    538 /* ---------------------------------------------------------------------- */
    539 void ni_freeifaddrs(struct ni_ifaddrs *ifa)
    540 {
    541 	free(ifa);
    542 }
    543 
    544