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