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 "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****************************************************************************/
95static 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/****************************************************************************
166this should cover most of the streams based systems
167Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
168****************************************************************************/
169static 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/****************************************************************************
259this one is for AIX (tested on 4.2)
260****************************************************************************/
261static 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
347static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
348{
349	return -1;
350}
351#endif
352
353
354static 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
365int get_interfaces(struct iface_struct *ifaces, int max_interfaces);
366/* this wrapper is used to remove duplicates from the interface list generated
367   above */
368int 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
413