Merge branch 'vendor/GCC44'
[dragonfly.git] / contrib / bind-9.3 / lib / isc / unix / ifiter_sysctl.c
1 /*
2  * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and 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_sysctl.c,v 1.14.12.9 2005/03/17 03:58:33 marka Exp $ */
19
20 /*
21  * Obtain the list of network interfaces using sysctl.
22  * See TCP/IP Illustrated Volume 2, sections 19.8, 19.14,
23  * and 19.16.
24  */
25
26 #include <sys/param.h>
27 #include <sys/sysctl.h>
28
29 #include <net/route.h>
30 #include <net/if_dl.h>
31
32 /* XXX what about Alpha? */
33 #ifdef sgi
34 #define ROUNDUP(a) ((a) > 0 ? \
35                 (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) : \
36                 sizeof(__uint64_t))
37 #else
38 #define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \
39                     : sizeof(long))
40 #endif
41
42 #define IFITER_MAGIC            ISC_MAGIC('I', 'F', 'I', 'S')
43 #define VALID_IFITER(t)         ISC_MAGIC_VALID(t, IFITER_MAGIC)
44
45 struct isc_interfaceiter {
46         unsigned int            magic;          /* Magic number. */
47         isc_mem_t               *mctx;
48         void                    *buf;           /* Buffer for sysctl data. */
49         unsigned int            bufsize;        /* Bytes allocated. */
50         unsigned int            bufused;        /* Bytes used. */
51         unsigned int            pos;            /* Current offset in
52                                                    sysctl data. */
53         isc_interface_t         current;        /* Current interface data. */
54         isc_result_t            result;         /* Last result code. */
55 };
56
57 static int mib[6] = {
58         CTL_NET,
59         PF_ROUTE,
60         0,
61         0,                      /* Any address family. */
62         NET_RT_IFLIST,
63         0                       /* Flags. */
64 };
65
66 isc_result_t
67 isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) {
68         isc_interfaceiter_t *iter;
69         isc_result_t result;
70         size_t bufsize;
71         size_t bufused;
72         char strbuf[ISC_STRERRORSIZE];
73
74         REQUIRE(mctx != NULL);
75         REQUIRE(iterp != NULL);
76         REQUIRE(*iterp == NULL);
77
78         iter = isc_mem_get(mctx, sizeof(*iter));
79         if (iter == NULL)
80                 return (ISC_R_NOMEMORY);
81
82         iter->mctx = mctx;
83         iter->buf = 0;
84
85         /*
86          * Determine the amount of memory needed.
87          */
88         bufsize = 0;
89         if (sysctl(mib, 6, NULL, &bufsize, NULL, (size_t) 0) < 0) {
90                 isc__strerror(errno, strbuf, sizeof(strbuf));
91                 UNEXPECTED_ERROR(__FILE__, __LINE__,
92                                  isc_msgcat_get(isc_msgcat,
93                                                 ISC_MSGSET_IFITERSYSCTL,
94                                                 ISC_MSG_GETIFLISTSIZE,
95                                                 "getting interface "
96                                                 "list size: sysctl: %s"),
97                                  strbuf);
98                 result = ISC_R_UNEXPECTED;
99                 goto failure;
100         }
101         iter->bufsize = bufsize;
102
103         iter->buf = isc_mem_get(iter->mctx, iter->bufsize);
104         if (iter->buf == NULL) {
105                 result = ISC_R_NOMEMORY;
106                 goto failure;
107         }
108
109         bufused = bufsize;
110         if (sysctl(mib, 6, iter->buf, &bufused, NULL, (size_t) 0) < 0) {
111                 isc__strerror(errno, strbuf, sizeof(strbuf));
112                 UNEXPECTED_ERROR(__FILE__, __LINE__,
113                                  isc_msgcat_get(isc_msgcat,
114                                                 ISC_MSGSET_IFITERSYSCTL,
115                                                 ISC_MSG_GETIFLIST,
116                                                 "getting interface list: "
117                                                 "sysctl: %s"),
118                                  strbuf);
119                 result = ISC_R_UNEXPECTED;
120                 goto failure;
121         }
122         iter->bufused = bufused;
123         INSIST(iter->bufused <= iter->bufsize);
124
125         /*
126          * A newly created iterator has an undefined position
127          * until isc_interfaceiter_first() is called.
128          */
129         iter->pos = (unsigned int) -1;
130         iter->result = ISC_R_FAILURE;
131
132         iter->magic = IFITER_MAGIC;
133         *iterp = iter;
134         return (ISC_R_SUCCESS);
135
136  failure:
137         if (iter->buf != NULL)
138                 isc_mem_put(mctx, iter->buf, iter->bufsize);
139         isc_mem_put(mctx, iter, sizeof(*iter));
140         return (result);
141 }
142
143 /*
144  * Get information about the current interface to iter->current.
145  * If successful, return ISC_R_SUCCESS.
146  * If the interface has an unsupported address family,
147  * return ISC_R_IGNORE.  In case of other failure,
148  * return ISC_R_UNEXPECTED.
149  */
150
151 static isc_result_t
152 internal_current(isc_interfaceiter_t *iter) {
153         struct ifa_msghdr *ifam, *ifam_end;
154
155         REQUIRE(VALID_IFITER(iter));
156         REQUIRE (iter->pos < (unsigned int) iter->bufused);
157
158         ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos);
159         ifam_end = (struct ifa_msghdr *) ((char *) iter->buf + iter->bufused);
160
161         if (ifam->ifam_type == RTM_IFINFO) {
162                 struct if_msghdr *ifm = (struct if_msghdr *) ifam;
163                 struct sockaddr_dl *sdl = (struct sockaddr_dl *) (ifm + 1);
164                 unsigned int namelen;
165
166                 memset(&iter->current, 0, sizeof(iter->current));
167
168                 namelen = sdl->sdl_nlen;
169                 if (namelen > sizeof(iter->current.name) - 1)
170                         namelen = sizeof(iter->current.name) - 1;
171
172                 memset(iter->current.name, 0, sizeof(iter->current.name));
173                 memcpy(iter->current.name, sdl->sdl_data, namelen);
174
175                 iter->current.flags = 0;
176
177                 if ((ifam->ifam_flags & IFF_UP) != 0)
178                         iter->current.flags |= INTERFACE_F_UP;
179
180                 if ((ifam->ifam_flags & IFF_POINTOPOINT) != 0)
181                         iter->current.flags |= INTERFACE_F_POINTTOPOINT;
182
183                 if ((ifam->ifam_flags & IFF_LOOPBACK) != 0)
184                         iter->current.flags |= INTERFACE_F_LOOPBACK;
185
186                 /*
187                  * This is not an interface address.
188                  * Force another iteration.
189                  */
190                 return (ISC_R_IGNORE);
191         } else if (ifam->ifam_type == RTM_NEWADDR) {
192                 int i;
193                 int family;
194                 struct sockaddr *mask_sa = NULL;
195                 struct sockaddr *addr_sa = NULL;
196                 struct sockaddr *dst_sa = NULL;
197
198                 struct sockaddr *sa = (struct sockaddr *)(ifam + 1);
199                 family = sa->sa_family;
200
201                 for (i = 0; i < RTAX_MAX; i++)
202                 {
203                         if ((ifam->ifam_addrs & (1 << i)) == 0)
204                                 continue;
205
206                         INSIST(sa < (struct sockaddr *) ifam_end);
207
208                         switch (i) {
209                         case RTAX_NETMASK: /* Netmask */
210                                 mask_sa = sa;
211                                 break;
212                         case RTAX_IFA: /* Interface address */
213                                 addr_sa = sa;
214                                 break;
215                         case RTAX_BRD: /* Broadcast or destination address */
216                                 dst_sa = sa;
217                                 break;
218                         }
219 #ifdef ISC_PLATFORM_HAVESALEN
220                         sa = (struct sockaddr *)((char*)(sa)
221                                          + ROUNDUP(sa->sa_len));
222 #else
223 #ifdef sgi
224                         /*
225                          * Do as the contributed SGI code does.
226                          */
227                         sa = (struct sockaddr *)((char*)(sa)
228                                          + ROUNDUP(_FAKE_SA_LEN_DST(sa)));
229 #else
230                         /* XXX untested. */
231                         sa = (struct sockaddr *)((char*)(sa)
232                                          + ROUNDUP(sizeof(struct sockaddr)));
233 #endif
234 #endif
235                 }
236
237                 if (addr_sa == NULL)
238                         return (ISC_R_IGNORE);
239
240                 family = addr_sa->sa_family;
241                 if (family != AF_INET && family != AF_INET6)
242                         return (ISC_R_IGNORE);
243
244                 iter->current.af = family;
245
246                 get_addr(family, &iter->current.address, addr_sa,
247                          iter->current.name);
248
249                 if (mask_sa != NULL)
250                         get_addr(family, &iter->current.netmask, mask_sa,
251                                  iter->current.name);
252
253                 if (dst_sa != NULL &&
254                     (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0)
255                         get_addr(family, &iter->current.dstaddress, dst_sa,
256                                  iter->current.name);
257
258                 return (ISC_R_SUCCESS);
259         } else {
260                 printf(isc_msgcat_get(isc_msgcat, ISC_MSGSET_IFITERSYSCTL,
261                                       ISC_MSG_UNEXPECTEDTYPE,
262                                       "warning: unexpected interface list "
263                                       "message type\n"));
264                 return (ISC_R_IGNORE);
265         }
266 }
267
268 /*
269  * Step the iterator to the next interface.  Unlike
270  * isc_interfaceiter_next(), this may leave the iterator
271  * positioned on an interface that will ultimately
272  * be ignored.  Return ISC_R_NOMORE if there are no more
273  * interfaces, otherwise ISC_R_SUCCESS.
274  */
275 static isc_result_t
276 internal_next(isc_interfaceiter_t *iter) {
277         struct ifa_msghdr *ifam;
278         REQUIRE (iter->pos < (unsigned int) iter->bufused);
279
280         ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos);
281
282         iter->pos += ifam->ifam_msglen;
283
284         if (iter->pos >= iter->bufused)
285                 return (ISC_R_NOMORE);
286
287         return (ISC_R_SUCCESS);
288 }
289
290 static void
291 internal_destroy(isc_interfaceiter_t *iter) {
292         UNUSED(iter); /* Unused. */
293         /*
294          * Do nothing.
295          */
296 }
297
298 static
299 void internal_first(isc_interfaceiter_t *iter) {
300         iter->pos = 0;
301 }