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