1 /*	$NetBSD: ifiter_getifaddrs.c,v 1.5 2014/12/10 04:38:01 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004, 2005, 2007-2009, 2014  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 2003  Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* Id: ifiter_getifaddrs.c,v 1.13 2009/09/24 23:48:13 tbox Exp  */
21 
22 /*! \file
23  * \brief
24  * Obtain the list of network interfaces using the getifaddrs(3) library.
25  */
26 
27 #include <ifaddrs.h>
28 
29 /*% Iterator Magic */
30 #define IFITER_MAGIC		ISC_MAGIC('I', 'F', 'I', 'G')
31 /*% Valid Iterator */
32 #define VALID_IFITER(t)		ISC_MAGIC_VALID(t, IFITER_MAGIC)
33 
34 #ifdef __linux
35 static isc_boolean_t seenv6 = ISC_FALSE;
36 #endif
37 
38 /*% Iterator structure */
39 struct isc_interfaceiter {
40 	unsigned int		magic;		/*%< Magic number. */
41 	isc_mem_t		*mctx;
42 	void			*buf;		/*%< (unused) */
43 	unsigned int		bufsize;	/*%< (always 0) */
44 	struct ifaddrs		*ifaddrs;	/*%< List of ifaddrs */
45 	struct ifaddrs		*pos;		/*%< Ptr to current ifaddr */
46 	isc_interface_t		current;	/*%< Current interface data. */
47 	isc_result_t		result;		/*%< Last result code. */
48 #ifdef  __linux
49 	FILE *                  proc;
50 	char                    entry[ISC_IF_INET6_SZ];
51 	isc_result_t            valid;
52 #endif
53 };
54 
55 isc_result_t
56 isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) {
57 	isc_interfaceiter_t *iter;
58 	isc_result_t result;
59 	char strbuf[ISC_STRERRORSIZE];
60 
61 	REQUIRE(mctx != NULL);
62 	REQUIRE(iterp != NULL);
63 	REQUIRE(*iterp == NULL);
64 
65 	iter = isc_mem_get(mctx, sizeof(*iter));
66 	if (iter == NULL)
67 		return (ISC_R_NOMEMORY);
68 
69 	iter->mctx = mctx;
70 	iter->buf = NULL;
71 	iter->bufsize = 0;
72 	iter->ifaddrs = NULL;
73 #ifdef __linux
74 	/*
75 	 * Only open "/proc/net/if_inet6" if we have never seen a IPv6
76 	 * address returned by getifaddrs().
77 	 */
78 	if (!seenv6)
79 		iter->proc = fopen("/proc/net/if_inet6", "r");
80 	else
81 		iter->proc = NULL;
82 	iter->valid = ISC_R_FAILURE;
83 #endif
84 
85 	if (getifaddrs(&iter->ifaddrs) < 0) {
86 		isc__strerror(errno, strbuf, sizeof(strbuf));
87 		UNEXPECTED_ERROR(__FILE__, __LINE__,
88 				 isc_msgcat_get(isc_msgcat,
89 						ISC_MSGSET_IFITERGETIFADDRS,
90 						ISC_MSG_GETIFADDRS,
91 						"getting interface "
92 						"addresses: getifaddrs: %s"),
93 				 strbuf);
94 		result = ISC_R_UNEXPECTED;
95 		goto failure;
96 	}
97 
98 	/*
99 	 * A newly created iterator has an undefined position
100 	 * until isc_interfaceiter_first() is called.
101 	 */
102 	iter->pos = NULL;
103 	iter->result = ISC_R_FAILURE;
104 
105 	iter->magic = IFITER_MAGIC;
106 	*iterp = iter;
107 	return (ISC_R_SUCCESS);
108 
109  failure:
110 #ifdef __linux
111 	if (iter->proc != NULL)
112 		fclose(iter->proc);
113 #endif
114 	if (iter->ifaddrs != NULL) /* just in case */
115 		freeifaddrs(iter->ifaddrs);
116 	isc_mem_put(mctx, iter, sizeof(*iter));
117 	return (result);
118 }
119 
120 /*
121  * Get information about the current interface to iter->current.
122  * If successful, return ISC_R_SUCCESS.
123  * If the interface has an unsupported address family,
124  * return ISC_R_IGNORE.
125  */
126 
127 static isc_result_t
128 internal_current(isc_interfaceiter_t *iter) {
129 	struct ifaddrs *ifa;
130 	int family;
131 	unsigned int namelen;
132 
133 	REQUIRE(VALID_IFITER(iter));
134 
135 	ifa = iter->pos;
136 
137 #ifdef __linux
138 	if (iter->pos == NULL)
139 		return (linux_if_inet6_current(iter));
140 #endif
141 
142 	INSIST(ifa != NULL);
143 	INSIST(ifa->ifa_name != NULL);
144 
145 	if (ifa->ifa_addr == NULL)
146 		return (ISC_R_IGNORE);
147 
148 	family = ifa->ifa_addr->sa_family;
149 	if (family != AF_INET && family != AF_INET6)
150 		return (ISC_R_IGNORE);
151 
152 #ifdef __linux
153 	if (family == AF_INET6)
154 		seenv6 = ISC_TRUE;
155 #endif
156 
157 	memset(&iter->current, 0, sizeof(iter->current));
158 
159 	namelen = strlen(ifa->ifa_name);
160 	if (namelen > sizeof(iter->current.name) - 1)
161 		namelen = sizeof(iter->current.name) - 1;
162 
163 	memset(iter->current.name, 0, sizeof(iter->current.name));
164 	memmove(iter->current.name, ifa->ifa_name, namelen);
165 
166 	iter->current.flags = 0;
167 
168 	if ((ifa->ifa_flags & IFF_UP) != 0)
169 		iter->current.flags |= INTERFACE_F_UP;
170 
171 	if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
172 		iter->current.flags |= INTERFACE_F_POINTTOPOINT;
173 
174 	if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
175 		iter->current.flags |= INTERFACE_F_LOOPBACK;
176 
177 	iter->current.af = family;
178 
179 	get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name);
180 
181 	if (ifa->ifa_netmask != NULL)
182 		get_addr(family, &iter->current.netmask, ifa->ifa_netmask,
183 			 ifa->ifa_name);
184 
185 	if (ifa->ifa_dstaddr != NULL &&
186 	    (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0)
187 		get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr,
188 			 ifa->ifa_name);
189 
190 	return (ISC_R_SUCCESS);
191 }
192 
193 /*
194  * Step the iterator to the next interface.  Unlike
195  * isc_interfaceiter_next(), this may leave the iterator
196  * positioned on an interface that will ultimately
197  * be ignored.  Return ISC_R_NOMORE if there are no more
198  * interfaces, otherwise ISC_R_SUCCESS.
199  */
200 static isc_result_t
201 internal_next(isc_interfaceiter_t *iter) {
202 
203 	if (iter->pos != NULL)
204 		iter->pos = iter->pos->ifa_next;
205 	if (iter->pos == NULL) {
206 #ifdef __linux
207 		if (!seenv6)
208 			return (linux_if_inet6_next(iter));
209 #endif
210 		return (ISC_R_NOMORE);
211 	}
212 
213 	return (ISC_R_SUCCESS);
214 }
215 
216 static void
217 internal_destroy(isc_interfaceiter_t *iter) {
218 
219 #ifdef __linux
220 	if (iter->proc != NULL)
221 		fclose(iter->proc);
222 	iter->proc = NULL;
223 #endif
224 	if (iter->ifaddrs)
225 		freeifaddrs(iter->ifaddrs);
226 	iter->ifaddrs = NULL;
227 }
228 
229 static
230 void internal_first(isc_interfaceiter_t *iter) {
231 
232 #ifdef __linux
233 	linux_if_inet6_first(iter);
234 #endif
235 	iter->pos = iter->ifaddrs;
236 }
237