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