source: branches/samba-3.2.x/source/lib/interfaces.c@ 228

Last change on this file since 228 was 133, checked in by Paul Smedley, 17 years ago

Update trunk to 3.2.0pre3

File size: 8.6 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 return a list of network interfaces
4 Copyright (C) Andrew Tridgell 1998
5 Copyright (C) Jeremy Allison 2007
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21
22/* working out the interfaces for a OS is an incredibly non-portable
23 thing. We have several possible implementations below, and autoconf
24 tries each of them to see what works
25
26 Note that this file does _not_ include includes.h. That is so this code
27 can be called directly from the autoconf tests. That also means
28 this code cannot use any of the normal Samba debug stuff or defines.
29 This is standalone code.
30
31*/
32
33#ifndef AUTOCONF_TEST
34#include "config.h"
35#endif
36
37#include <unistd.h>
38#include <stdio.h>
39#include <sys/types.h>
40#include <netdb.h>
41#include <sys/ioctl.h>
42#include <netdb.h>
43#include <sys/ioctl.h>
44#include <sys/time.h>
45#include <sys/socket.h>
46#include <netinet/in.h>
47#include <arpa/inet.h>
48
49#ifdef HAVE_IFADDRS_H
50#include <ifaddrs.h>
51#endif
52
53#ifdef HAVE_SYS_TIME_H
54#include <sys/time.h>
55#endif
56
57#ifndef SIOCGIFCONF
58#ifdef HAVE_SYS_SOCKIO_H
59#include <sys/sockio.h>
60#endif
61#endif
62
63#ifdef HAVE_STDLIB_H
64#include <stdlib.h>
65#endif
66
67#ifdef HAVE_STRING_H
68#include <string.h>
69#endif
70
71#ifdef HAVE_STRINGS_H
72#include <strings.h>
73#endif
74
75#ifdef __COMPAR_FN_T
76#define QSORT_CAST (__compar_fn_t)
77#endif
78
79#ifndef QSORT_CAST
80#define QSORT_CAST (int (*)(const void *, const void *))
81#endif
82
83#ifdef HAVE_NET_IF_H
84#include <net/if.h>
85#endif
86
87#define SOCKET_WRAPPER_NOT_REPLACE
88#include "interfaces.h"
89#include "lib/replace/replace.h"
90
91/****************************************************************************
92 Utility functions.
93****************************************************************************/
94
95/****************************************************************************
96 Create a struct sockaddr_storage with the netmask bits set to 1.
97****************************************************************************/
98
99bool make_netmask(struct sockaddr_storage *pss_out,
100 const struct sockaddr_storage *pss_in,
101 unsigned long masklen)
102{
103 *pss_out = *pss_in;
104 /* Now apply masklen bits of mask. */
105#if defined(HAVE_IPV6)
106 if (pss_in->ss_family == AF_INET6) {
107 char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
108 unsigned int i;
109
110 if (masklen > 128) {
111 return false;
112 }
113 for (i = 0; masklen >= 8; masklen -= 8, i++) {
114 *p++ = 0xff;
115 }
116 /* Deal with the partial byte. */
117 *p++ &= (0xff & ~(0xff>>masklen));
118 i++;
119 for (;i < sizeof(struct in6_addr); i++) {
120 *p++ = '\0';
121 }
122 return true;
123 }
124#endif
125 if (pss_in->ss_family == AF_INET) {
126 if (masklen > 32) {
127 return false;
128 }
129 ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
130 htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
131 return true;
132 }
133 return false;
134}
135
136/****************************************************************************
137 Create a struct sockaddr_storage set to the broadcast or network adress from
138 an incoming sockaddr_storage.
139****************************************************************************/
140
141static void make_bcast_or_net(struct sockaddr_storage *pss_out,
142 const struct sockaddr_storage *pss_in,
143 const struct sockaddr_storage *nmask,
144 bool make_bcast_p)
145{
146 unsigned int i = 0, len = 0;
147 char *pmask = NULL;
148 char *p = NULL;
149 *pss_out = *pss_in;
150
151 /* Set all zero netmask bits to 1. */
152#if defined(HAVE_IPV6)
153 if (pss_in->ss_family == AF_INET6) {
154 p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
155 pmask = (char *)&((struct sockaddr_in6 *)nmask)->sin6_addr;
156 len = 16;
157 }
158#endif
159 if (pss_in->ss_family == AF_INET) {
160 p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
161 pmask = (char *)&((struct sockaddr_in *)nmask)->sin_addr;
162 len = 4;
163 }
164
165 for (i = 0; i < len; i++, p++, pmask++) {
166 if (make_bcast_p) {
167 *p = (*p & *pmask) | (*pmask ^ 0xff);
168 } else {
169 /* make_net */
170 *p = (*p & *pmask);
171 }
172 }
173}
174
175void make_bcast(struct sockaddr_storage *pss_out,
176 const struct sockaddr_storage *pss_in,
177 const struct sockaddr_storage *nmask)
178{
179 make_bcast_or_net(pss_out, pss_in, nmask, true);
180}
181
182void make_net(struct sockaddr_storage *pss_out,
183 const struct sockaddr_storage *pss_in,
184 const struct sockaddr_storage *nmask)
185{
186 make_bcast_or_net(pss_out, pss_in, nmask, false);
187}
188
189/****************************************************************************
190 Try the "standard" getifaddrs/freeifaddrs interfaces.
191 Also gets IPv6 interfaces.
192****************************************************************************/
193
194/****************************************************************************
195 Get the netmask address for a local interface.
196****************************************************************************/
197
198static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
199{
200 struct ifaddrs *iflist = NULL;
201 struct ifaddrs *ifptr = NULL;
202 int total = 0;
203 size_t copy_size;
204 if (getifaddrs(&iflist) < 0) {
205 return -1;
206 }
207
208 /* Loop through interfaces, looking for given IP address */
209 for (ifptr = iflist, total = 0;
210 ifptr != NULL && total < max_interfaces;
211 ifptr = ifptr->ifa_next) {
212
213 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
214
215 copy_size = sizeof(struct sockaddr_in);
216
217 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
218 continue;
219 }
220
221 ifaces[total].flags = ifptr->ifa_flags;
222
223 /* Check the interface is up. */
224 if (!(ifaces[total].flags & IFF_UP)) {
225 continue;
226 }
227
228#if defined(HAVE_IPV6)
229 if (ifptr->ifa_addr->sa_family == AF_INET6) {
230 copy_size = sizeof(struct sockaddr_in6);
231 }
232#endif
233
234 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
235 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
236
237 if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
238 make_bcast(&ifaces[total].bcast,
239 &ifaces[total].ip,
240 &ifaces[total].netmask);
241 } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
242 ifptr->ifa_dstaddr ) {
243 memcpy(&ifaces[total].bcast,
244 ifptr->ifa_dstaddr,
245 copy_size);
246 } else {
247 continue;
248 }
249
250 strlcpy(ifaces[total].name, ifptr->ifa_name,
251 sizeof(ifaces[total].name));
252 total++;
253 }
254
255 freeifaddrs(iflist);
256
257 return total;
258}
259
260static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
261{
262 int r;
263
264#if defined(HAVE_IPV6)
265 /*
266 * If we have IPv6 - sort these interfaces lower
267 * than any IPv4 ones.
268 */
269 if (i1->ip.ss_family == AF_INET6 &&
270 i2->ip.ss_family == AF_INET) {
271 return -1;
272 } else if (i1->ip.ss_family == AF_INET &&
273 i2->ip.ss_family == AF_INET6) {
274 return 1;
275 }
276
277 if (i1->ip.ss_family == AF_INET6) {
278 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
279 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
280
281 r = memcmp(&s1->sin6_addr,
282 &s2->sin6_addr,
283 sizeof(struct in6_addr));
284 if (r) {
285 return r;
286 }
287
288 s1 = (struct sockaddr_in6 *)&i1->netmask;
289 s2 = (struct sockaddr_in6 *)&i2->netmask;
290
291 r = memcmp(&s1->sin6_addr,
292 &s2->sin6_addr,
293 sizeof(struct in6_addr));
294 if (r) {
295 return r;
296 }
297 }
298#endif
299
300 /* AIX uses __ss_family instead of ss_family inside of
301 sockaddr_storage. Instead of trying to figure out which field to
302 use, we can just cast it to a sockaddr.
303 */
304
305 if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
306 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
307 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
308
309 r = ntohl(s1->sin_addr.s_addr) -
310 ntohl(s2->sin_addr.s_addr);
311 if (r) {
312 return r;
313 }
314
315 s1 = (struct sockaddr_in *)&i1->netmask;
316 s2 = (struct sockaddr_in *)&i2->netmask;
317
318 return ntohl(s1->sin_addr.s_addr) -
319 ntohl(s2->sin_addr.s_addr);
320 }
321 return 0;
322}
323
324int get_interfaces(struct iface_struct *ifaces, int max_interfaces);
325/* this wrapper is used to remove duplicates from the interface list generated
326 above */
327int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
328{
329 int total, i, j;
330
331 total = _get_interfaces(ifaces, max_interfaces);
332 if (total <= 0) return total;
333
334 /* now we need to remove duplicates */
335 qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
336
337 for (i=1;i<total;) {
338 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
339 for (j=i-1;j<total-1;j++) {
340 ifaces[j] = ifaces[j+1];
341 }
342 total--;
343 } else {
344 i++;
345 }
346 }
347
348 return total;
349}
350
Note: See TracBrowser for help on using the repository browser.