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