r8541: this might take a few tries ...
[bbaumbach/samba-autobuild/.git] / source4 / lib / netif / netif.c
1 /* 
2    Unix SMB/CIFS implementation.
3    return a list of network interfaces
4    Copyright (C) Andrew Tridgell 1998
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 #include <unistd.h>
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <netdb.h>
37 #include <sys/ioctl.h>
38 #include <netdb.h>
39 #include <sys/ioctl.h>
40 #include <sys/time.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44
45 #ifndef AUTOCONF_TEST
46 #include "config.h"
47 #endif
48
49 #ifdef HAVE_SYS_TIME_H
50 #include <sys/time.h>
51 #endif
52
53 #ifndef SIOCGIFCONF
54 #ifdef HAVE_SYS_SOCKIO_H
55 #include <sys/sockio.h>
56 #endif
57 #endif
58
59 #ifdef HAVE_STDLIB_H
60 #include <stdlib.h>
61 #endif
62
63 #ifdef HAVE_STRING_H
64 #include <string.h>
65 #endif
66
67 #ifdef HAVE_STRINGS_H
68 #include <strings.h>
69 #endif
70
71 #ifdef __COMPAR_FN_T
72 #define QSORT_CAST (__compar_fn_t)
73 #endif
74
75 #ifndef QSORT_CAST
76 #define QSORT_CAST (int (*)(const void *, const void *))
77 #endif
78
79 #include <net/if.h>
80 #include "netif.h"
81
82 #if HAVE_IFACE_IFCONF
83
84 /* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
85    V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
86
87    It probably also works on any BSD style system.  */
88
89 /****************************************************************************
90   get the netmask address for a local interface
91 ****************************************************************************/
92 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
93 {  
94         struct ifconf ifc;
95         char buff[8192];
96         int fd, i, n;
97         struct ifreq *ifr=NULL;
98         int total = 0;
99         struct in_addr ipaddr;
100         struct in_addr nmask;
101         char *iname;
102
103         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
104                 return -1;
105         }
106   
107         ifc.ifc_len = sizeof(buff);
108         ifc.ifc_buf = buff;
109
110         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
111                 close(fd);
112                 return -1;
113         } 
114
115         ifr = ifc.ifc_req;
116   
117         n = ifc.ifc_len / sizeof(struct ifreq);
118
119         /* Loop through interfaces, looking for given IP address */
120         for (i=n-1;i>=0 && total < max_interfaces;i--) {
121                 if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
122                         continue;
123                 }
124
125                 iname = ifr[i].ifr_name;
126                 ipaddr = (*(struct sockaddr_in *)&ifr[i].ifr_addr).sin_addr;
127
128                 if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
129                         continue;
130                 }  
131
132                 if (!(ifr[i].ifr_flags & IFF_UP)) {
133                         continue;
134                 }
135
136                 if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
137                         continue;
138                 }  
139
140                 nmask = ((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr;
141
142                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
143                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
144                 ifaces[total].ip = ipaddr;
145                 ifaces[total].netmask = nmask;
146                 total++;
147         }
148
149         close(fd);
150
151         return total;
152 }  
153
154 #elif HAVE_IFACE_IFREQ
155
156 #ifndef I_STR
157 #include <sys/stropts.h>
158 #endif
159
160 /****************************************************************************
161 this should cover most of the streams based systems
162 Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
163 ****************************************************************************/
164 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
165 {
166         struct ifreq ifreq;
167         struct strioctl strioctl;
168         char buff[8192];
169         int fd, i, n;
170         struct ifreq *ifr=NULL;
171         int total = 0;
172         struct in_addr ipaddr;
173         struct in_addr nmask;
174         char *iname;
175
176         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
177                 return -1;
178         }
179   
180         strioctl.ic_cmd = SIOCGIFCONF;
181         strioctl.ic_dp  = buff;
182         strioctl.ic_len = sizeof(buff);
183         if (ioctl(fd, I_STR, &strioctl) < 0) {
184                 close(fd);
185                 return -1;
186         } 
187
188         /* we can ignore the possible sizeof(int) here as the resulting
189            number of interface structures won't change */
190         n = strioctl.ic_len / sizeof(struct ifreq);
191
192         /* we will assume that the kernel returns the length as an int
193            at the start of the buffer if the offered size is a
194            multiple of the structure size plus an int */
195         if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
196                 ifr = (struct ifreq *)(buff + sizeof(int));  
197         } else {
198                 ifr = (struct ifreq *)buff;  
199         }
200
201         /* Loop through interfaces */
202
203         for (i = 0; i<n && total < max_interfaces; i++) {
204                 ifreq = ifr[i];
205   
206                 strioctl.ic_cmd = SIOCGIFFLAGS;
207                 strioctl.ic_dp  = (char *)&ifreq;
208                 strioctl.ic_len = sizeof(struct ifreq);
209                 if (ioctl(fd, I_STR, &strioctl) != 0) {
210                         continue;
211                 }
212                 
213                 if (!(ifreq.ifr_flags & IFF_UP)) {
214                         continue;
215                 }
216
217                 strioctl.ic_cmd = SIOCGIFADDR;
218                 strioctl.ic_dp  = (char *)&ifreq;
219                 strioctl.ic_len = sizeof(struct ifreq);
220                 if (ioctl(fd, I_STR, &strioctl) != 0) {
221                         continue;
222                 }
223
224                 ipaddr = (*(struct sockaddr_in *) &ifreq.ifr_addr).sin_addr;
225                 iname = ifreq.ifr_name;
226
227                 strioctl.ic_cmd = SIOCGIFNETMASK;
228                 strioctl.ic_dp  = (char *)&ifreq;
229                 strioctl.ic_len = sizeof(struct ifreq);
230                 if (ioctl(fd, I_STR, &strioctl) != 0) {
231                         continue;
232                 }
233
234                 nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
235
236                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
237                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
238                 ifaces[total].ip = ipaddr;
239                 ifaces[total].netmask = nmask;
240
241                 total++;
242         }
243
244         close(fd);
245
246         return total;
247 }
248
249 #elif HAVE_IFACE_AIX
250
251 /****************************************************************************
252 this one is for AIX (tested on 4.2)
253 ****************************************************************************/
254 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
255 {
256         char buff[8192];
257         int fd, i;
258         struct ifconf ifc;
259         struct ifreq *ifr=NULL;
260         struct in_addr ipaddr;
261         struct in_addr nmask;
262         char *iname;
263         int total = 0;
264
265         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
266                 return -1;
267         }
268
269
270         ifc.ifc_len = sizeof(buff);
271         ifc.ifc_buf = buff;
272
273         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
274                 close(fd);
275                 return -1;
276         }
277
278         ifr = ifc.ifc_req;
279
280         /* Loop through interfaces */
281         i = ifc.ifc_len;
282
283         while (i > 0 && total < max_interfaces) {
284                 uint_t inc;
285
286                 inc = ifr->ifr_addr.sa_len;
287
288                 if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
289                         goto next;
290                 }
291
292                 ipaddr = (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr;
293                 iname = ifr->ifr_name;
294
295                 if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
296                         goto next;
297                 }
298
299                 if (!(ifr->ifr_flags & IFF_UP)) {
300                         goto next;
301                 }
302
303                 if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
304                         goto next;
305                 }
306
307                 nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
308
309                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
310                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
311                 ifaces[total].ip = ipaddr;
312                 ifaces[total].netmask = nmask;
313
314                 total++;
315
316         next:
317                 /*
318                  * Patch from Archie Cobbs (archie@whistle.com).  The
319                  * addresses in the SIOCGIFCONF interface list have a
320                  * minimum size. Usually this doesn't matter, but if
321                  * your machine has tunnel interfaces, etc. that have
322                  * a zero length "link address", this does matter.  */
323
324                 if (inc < sizeof(ifr->ifr_addr))
325                         inc = sizeof(ifr->ifr_addr);
326                 inc += IFNAMSIZ;
327
328                 ifr = (struct ifreq*) (((char*) ifr) + inc);
329                 i -= inc;
330         }
331   
332
333         close(fd);
334         return total;
335 }
336
337 #else /* a dummy version */
338 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
339 {
340         return -1;
341 }
342 #endif
343
344
345 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
346 {
347         int r;
348         r = strcmp(i1->name, i2->name);
349         if (r) return r;
350         r = ntohl(i1->ip.s_addr) - ntohl(i2->ip.s_addr);
351         if (r) return r;
352         r = ntohl(i1->netmask.s_addr) - ntohl(i2->netmask.s_addr);
353         return r;
354 }
355
356 /* this wrapper is used to remove duplicates from the interface list generated
357    above */
358 int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
359 {
360         int total, i, j;
361
362         total = _get_interfaces(ifaces, max_interfaces);
363         if (total <= 0) return total;
364
365         /* now we need to remove duplicates */
366         qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
367
368         for (i=1;i<total;) {
369                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
370                         for (j=i-1;j<total-1;j++) {
371                                 ifaces[j] = ifaces[j+1];
372                         }
373                         total--;
374                 } else {
375                         i++;
376                 }
377         }
378
379         return total;
380 }
381
382
383 #ifdef AUTOCONF_TEST
384 /* this is the autoconf driver to test get_interfaces() */
385
386  int main()
387 {
388         struct iface_struct ifaces[MAX_INTERFACES];
389         int total = get_interfaces(ifaces, MAX_INTERFACES);
390         int i;
391
392         printf("got %d interfaces:\n", total);
393         if (total <= 0) exit(1);
394
395         for (i=0;i<total;i++) {
396                 printf("%-10s ", ifaces[i].name);
397                 printf("IP=%s ", inet_ntoa(ifaces[i].ip));
398                 printf("NETMASK=%s\n", inet_ntoa(ifaces[i].netmask));
399         }
400         return 0;
401 }
402 #endif