Merge from vendor branch CVS:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / unix / ifiter_sysctl.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2001  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.2.1 2004/03/09 06:12:10 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         REQUIRE(mctx != NULL);
73         REQUIRE(iterp != NULL);
74         REQUIRE(*iterp == NULL);
75
76         iter = isc_mem_get(mctx, sizeof(*iter));
77         if (iter == NULL)
78                 return (ISC_R_NOMEMORY);
79
80         iter->mctx = mctx;
81         iter->buf = 0;
82
83         /*
84          * Determine the amount of memory needed.
85          */
86         bufsize = 0;
87         if (sysctl(mib, 6, NULL, &bufsize, NULL, (size_t) 0) < 0) {
88                 UNEXPECTED_ERROR(__FILE__, __LINE__,
89                                  isc_msgcat_get(isc_msgcat,
90                                                 ISC_MSGSET_IFITERSYSCTL,
91                                                 ISC_MSG_GETIFLISTSIZE,
92                                                 "getting interface "
93                                                 "list size: sysctl: %s"),
94                                  strerror(errno));
95                 result = ISC_R_UNEXPECTED;
96                 goto failure;
97         }
98         iter->bufsize = bufsize;
99
100         iter->buf = isc_mem_get(iter->mctx, iter->bufsize);
101         if (iter->buf == NULL) {
102                 result = ISC_R_NOMEMORY;
103                 goto failure;
104         }
105
106         bufused = bufsize;
107         if (sysctl(mib, 6, iter->buf, &bufused, NULL, (size_t) 0) < 0) {
108                 UNEXPECTED_ERROR(__FILE__, __LINE__,
109                                  isc_msgcat_get(isc_msgcat,
110                                                 ISC_MSGSET_IFITERSYSCTL,
111                                                 ISC_MSG_GETIFLIST,
112                                                 "getting interface list: "
113                                                 "sysctl: %s"),
114                                  strerror(errno));
115                 result = ISC_R_UNEXPECTED;
116                 goto failure;
117         }
118         iter->bufused = bufused;
119         INSIST(iter->bufused <= iter->bufsize);
120
121         /*
122          * A newly created iterator has an undefined position
123          * until isc_interfaceiter_first() is called.
124          */
125         iter->pos = (unsigned int) -1;
126         iter->result = ISC_R_FAILURE;
127
128         iter->magic = IFITER_MAGIC;
129         *iterp = iter;
130         return (ISC_R_SUCCESS);
131
132  failure:
133         if (iter->buf != NULL)
134                 isc_mem_put(mctx, iter->buf, iter->bufsize);
135         isc_mem_put(mctx, iter, sizeof *iter);
136         return (result);
137 }
138
139 /*
140  * Get information about the current interface to iter->current.
141  * If successful, return ISC_R_SUCCESS.
142  * If the interface has an unsupported address family,
143  * return ISC_R_IGNORE.  In case of other failure,
144  * return ISC_R_UNEXPECTED.
145  */
146
147 static isc_result_t
148 internal_current(isc_interfaceiter_t *iter) {
149         struct ifa_msghdr *ifam, *ifam_end;
150
151         REQUIRE(VALID_IFITER(iter));
152         REQUIRE (iter->pos < (unsigned int) iter->bufused);
153
154         ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos);
155         ifam_end = (struct ifa_msghdr *) ((char *) iter->buf + iter->bufused);
156
157         if (ifam->ifam_type == RTM_IFINFO) {
158                 struct if_msghdr *ifm = (struct if_msghdr *) ifam;
159                 struct sockaddr_dl *sdl = (struct sockaddr_dl *) (ifm + 1);
160                 unsigned int namelen;
161
162                 memset(&iter->current, 0, sizeof(iter->current));
163
164                 namelen = sdl->sdl_nlen;
165                 if (namelen > sizeof(iter->current.name) - 1)
166                         namelen = sizeof(iter->current.name) - 1;
167
168                 memset(iter->current.name, 0, sizeof(iter->current.name));
169                 memcpy(iter->current.name, sdl->sdl_data, namelen);
170
171                 iter->current.flags = 0;
172
173                 if ((ifam->ifam_flags & IFF_UP) != 0)
174                         iter->current.flags |= INTERFACE_F_UP;
175
176                 if ((ifam->ifam_flags & IFF_POINTOPOINT) != 0)
177                         iter->current.flags |= INTERFACE_F_POINTTOPOINT;
178
179                 if ((ifam->ifam_flags & IFF_LOOPBACK) != 0)
180                         iter->current.flags |= INTERFACE_F_LOOPBACK;
181
182                 /*
183                  * This is not an interface address.
184                  * Force another iteration.
185                  */
186                 return (ISC_R_IGNORE);
187         } else if (ifam->ifam_type == RTM_NEWADDR) {
188                 int i;
189                 int family;
190                 struct sockaddr *mask_sa = NULL;
191                 struct sockaddr *addr_sa = NULL;
192                 struct sockaddr *dst_sa = NULL;
193
194                 struct sockaddr *sa = (struct sockaddr *)(ifam + 1);
195                 family = sa->sa_family;
196
197                 for (i = 0; i < RTAX_MAX; i++)
198                 {
199                         if ((ifam->ifam_addrs & (1 << i)) == 0)
200                                 continue;
201
202                         INSIST(sa < (struct sockaddr *) ifam_end);
203
204                         switch (i) {
205                         case RTAX_NETMASK: /* Netmask */
206                                 mask_sa = sa;
207                                 break;
208                         case RTAX_IFA: /* Interface address */
209                                 addr_sa = sa;
210                                 break;
211                         case RTAX_BRD: /* Broadcast or destination address */
212                                 dst_sa = sa;
213                                 break;
214                         }
215 #ifdef ISC_PLATFORM_HAVESALEN
216                         sa = (struct sockaddr *)((char*)(sa)
217                                          + ROUNDUP(sa->sa_len));
218 #else
219 #ifdef sgi
220                         /*
221                          * Do as the contributed SGI code does.
222                          */
223                         sa = (struct sockaddr *)((char*)(sa)
224                                          + ROUNDUP(_FAKE_SA_LEN_DST(sa)));
225 #else
226                         /* XXX untested. */
227                         sa = (struct sockaddr *)((char*)(sa)
228                                          + ROUNDUP(sizeof(struct sockaddr)));
229 #endif
230 #endif
231                 }
232
233                 if (addr_sa == NULL)
234                         return (ISC_R_IGNORE);
235
236                 family = addr_sa->sa_family;
237                 if (family != AF_INET) /* XXX IP6 */
238                         return (ISC_R_IGNORE);
239
240                 iter->current.af = family;
241
242                 get_addr(family, &iter->current.address, addr_sa);
243
244                 if (mask_sa != NULL)
245                         get_addr(family, &iter->current.netmask, mask_sa);
246
247                 if (dst_sa != NULL &&
248                     (iter->current.flags & IFF_POINTOPOINT) != 0)
249                         get_addr(family, &iter->current.dstaddress, dst_sa);
250
251                 return (ISC_R_SUCCESS);
252         } else {
253                 printf(isc_msgcat_get(isc_msgcat, ISC_MSGSET_IFITERSYSCTL,
254                                       ISC_MSG_UNEXPECTEDTYPE,
255                                       "warning: unexpected interface list "
256                                       "message type\n"));
257                 return (ISC_R_IGNORE);
258         }
259 }
260
261 /*
262  * Step the iterator to the next interface.  Unlike
263  * isc_interfaceiter_next(), this may leave the iterator
264  * positioned on an interface that will ultimately
265  * be ignored.  Return ISC_R_NOMORE if there are no more
266  * interfaces, otherwise ISC_R_SUCCESS.
267  */
268 static isc_result_t
269 internal_next(isc_interfaceiter_t *iter) {
270         struct ifa_msghdr *ifam;
271         REQUIRE (iter->pos < (unsigned int) iter->bufused);
272
273         ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos);
274
275         iter->pos += ifam->ifam_msglen;
276
277         if (iter->pos >= iter->bufused)
278                 return (ISC_R_NOMORE);
279
280         return (ISC_R_SUCCESS);
281 }
282
283 static void
284 internal_destroy(isc_interfaceiter_t *iter) {
285         UNUSED(iter); /* Unused. */
286         /*
287          * Do nothing.
288          */
289 }
290