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
91static 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/* ====================================================================== */
94struct 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
102struct 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/* ====================================================================== */
113static 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
137static 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
163static 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
218static 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/* ---------------------------------------------------------------------- */
259static 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
277static 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/* ---------------------------------------------------------------------- */
286static 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/* ---------------------------------------------------------------------- */
295static 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/* ====================================================================== */
313int 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/* ---------------------------------------------------------------------- */
539void ni_freeifaddrs(struct ni_ifaddrs *ifa)
540{
541	free(ifa);
542}
543
544