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