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 #ifndef AUTOCONF_TEST
34 #include "includes.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 "netif.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 ****************************************************************************/
_get_interfaces(struct iface_struct * ifaces,int max_interfaces)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 ****************************************************************************/
_get_interfaces(struct iface_struct * ifaces,int max_interfaces)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 ****************************************************************************/
_get_interfaces(struct iface_struct * ifaces,int max_interfaces)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
_get_interfaces(struct iface_struct * ifaces,int max_interfaces)347 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
348 {
349 	return -1;
350 }
351 #endif
352 
353 
iface_comp(struct iface_struct * i1,struct iface_struct * i2)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 /* this wrapper is used to remove duplicates from the interface list generated
366    above */
get_interfaces(struct iface_struct * ifaces,int max_interfaces)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 
main()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
412