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 /* ====================================================================== */
nl_sendreq(int sd,int request,int flags,int * seq)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 
nl_recvmsg(int sd,int request,int seq,void * buf,size_t buflen,int * flags)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 
nl_getmsg(int sd,int request,int seq,struct nlmsghdr ** nlhp,int * done)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 
nl_getlist(int sd,int seq,int request,struct nlmsg_list ** nlm_list,struct nlmsg_list ** nlm_end)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 /* ---------------------------------------------------------------------- */
free_nlmsglist(struct nlmsg_list * nlm0)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 
free_data(void * data)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 /* ---------------------------------------------------------------------- */
nl_close(int sd)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 /* ---------------------------------------------------------------------- */
nl_open(void)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 /* ====================================================================== */
ni_ifaddrs(struct ni_ifaddrs ** ifap,sa_family_t family)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 /* ---------------------------------------------------------------------- */
ni_freeifaddrs(struct ni_ifaddrs * ifa)539 void ni_freeifaddrs(struct ni_ifaddrs *ifa)
540 {
541 	free(ifa);
542 }
543 
544