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