1/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
2/*
3 * Copyright (c) 1994, 1995, 1996, 1997, 1998
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by the Computer Systems
17 *	Engineering Group at Lawrence Berkeley Laboratory.
18 * 4. Neither the name of the University nor of the Laboratory may be used
19 *    to endorse or promote products derived from this software without
20 *    specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36static const char rcsid[] _U_ =
37    "@(#) $Header: /tcpdump/master/libpcap/fad-glifc.c,v 1.5.2.1 2005/04/19 00:54:16 guy Exp $ (LBL)";
38#endif
39
40#ifdef HAVE_CONFIG_H
41#include "config.h"
42#endif
43
44#include <sys/param.h>
45#include <sys/file.h>
46#include <sys/ioctl.h>
47#include <sys/socket.h>
48#ifdef HAVE_SYS_SOCKIO_H
49#include <sys/sockio.h>
50#endif
51#include <sys/time.h>				/* concession to AIX */
52
53struct mbuf;		/* Squelch compiler warnings on some platforms for */
54struct rtentry;		/* declarations in <net/if.h> */
55#include <net/if.h>
56#include <netinet/in.h>
57
58#include <ctype.h>
59#include <errno.h>
60#include <memory.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
65
66#include "pcap-int.h"
67
68#ifdef HAVE_OS_PROTO_H
69#include "os-proto.h"
70#endif
71
72/*
73 * Get a list of all interfaces that are up and that we can open.
74 * Returns -1 on error, 0 otherwise.
75 * The list, as returned through "alldevsp", may be null if no interfaces
76 * were up and could be opened.
77 *
78 * This is the implementation used on platforms that have SIOCLGIFCONF
79 * but don't have "getifaddrs()".  (Solaris 8 and later; we use
80 * SIOCLGIFCONF rather than SIOCGIFCONF in order to get IPv6 addresses.)
81 */
82int
83pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
84{
85	pcap_if_t *devlist = NULL;
86	register int fd4, fd6, fd;
87	register struct lifreq *ifrp, *ifend;
88	struct lifnum ifn;
89	struct lifconf ifc;
90	char *buf = NULL;
91	unsigned buf_size;
92#ifdef HAVE_SOLARIS
93	char *p, *q;
94#endif
95	struct lifreq ifrflags, ifrnetmask, ifrbroadaddr, ifrdstaddr;
96	struct sockaddr *netmask, *broadaddr, *dstaddr;
97	int ret = 0;
98
99	/*
100	 * Create a socket from which to fetch the list of interfaces,
101	 * and from which to fetch IPv4 information.
102	 */
103	fd4 = socket(AF_INET, SOCK_DGRAM, 0);
104	if (fd4 < 0) {
105		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
106		    "socket: %s", pcap_strerror(errno));
107		return (-1);
108	}
109
110	/*
111	 * Create a socket from which to fetch IPv6 information.
112	 */
113	fd6 = socket(AF_INET6, SOCK_DGRAM, 0);
114	if (fd6 < 0) {
115		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
116		    "socket: %s", pcap_strerror(errno));
117		(void)close(fd4);
118		return (-1);
119	}
120
121	/*
122	 * How many entries will SIOCGLIFCONF return?
123	 */
124	ifn.lifn_family = AF_UNSPEC;
125	ifn.lifn_flags = 0;
126	ifn.lifn_count = 0;
127	if (ioctl(fd4, SIOCGLIFNUM, (char *)&ifn) < 0) {
128		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
129		    "SIOCGLIFNUM: %s", pcap_strerror(errno));
130		(void)close(fd6);
131		(void)close(fd4);
132		return (-1);
133	}
134
135	/*
136	 * Allocate a buffer for those entries.
137	 */
138	buf_size = ifn.lifn_count * sizeof (struct lifreq);
139	buf = malloc(buf_size);
140	if (buf == NULL) {
141		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
142		    "malloc: %s", pcap_strerror(errno));
143		(void)close(fd6);
144		(void)close(fd4);
145		return (-1);
146	}
147
148	/*
149	 * Get the entries.
150	 */
151	ifc.lifc_len = buf_size;
152	ifc.lifc_buf = buf;
153	ifc.lifc_family = AF_UNSPEC;
154	ifc.lifc_flags = 0;
155	memset(buf, 0, buf_size);
156	if (ioctl(fd4, SIOCGLIFCONF, (char *)&ifc) < 0) {
157		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
158		    "SIOCGLIFCONF: %s", pcap_strerror(errno));
159		(void)close(fd6);
160		(void)close(fd4);
161		free(buf);
162		return (-1);
163	}
164
165	/*
166	 * Loop over the entries.
167	 */
168	ifrp = (struct lifreq *)buf;
169	ifend = (struct lifreq *)(buf + ifc.lifc_len);
170
171	for (; ifrp < ifend; ifrp++) {
172		/*
173		 * IPv6 or not?
174		 */
175		if (((struct sockaddr *)&ifrp->lifr_addr)->sa_family == AF_INET6)
176			fd = fd6;
177		else
178			fd = fd4;
179
180		/*
181		 * Skip entries that begin with "dummy".
182		 * XXX - what are these?  Is this Linux-specific?
183		 * Are there platforms on which we shouldn't do this?
184		 */
185		if (strncmp(ifrp->lifr_name, "dummy", 5) == 0)
186			continue;
187
188#ifdef HAVE_SOLARIS
189		/*
190		 * Skip entries that have a ":" followed by a number
191		 * at the end - those are Solaris virtual interfaces
192		 * on which you can't capture.
193		 */
194		p = strchr(ifrp->lifr_name, ':');
195		if (p != NULL) {
196			/*
197			 * We have a ":"; is it followed by a number?
198			 */
199			while (isdigit((unsigned char)*p))
200				p++;
201			if (*p == '\0') {
202				/*
203				 * All digits after the ":" until the end.
204				 */
205				continue;
206			}
207		}
208#endif
209
210		/*
211		 * Get the flags for this interface, and skip it if it's
212		 * not up.
213		 */
214		strncpy(ifrflags.lifr_name, ifrp->lifr_name,
215		    sizeof(ifrflags.lifr_name));
216		if (ioctl(fd, SIOCGLIFFLAGS, (char *)&ifrflags) < 0) {
217			if (errno == ENXIO)
218				continue;
219			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
220			    "SIOCGLIFFLAGS: %.*s: %s",
221			    (int)sizeof(ifrflags.lifr_name),
222			    ifrflags.lifr_name,
223			    pcap_strerror(errno));
224			ret = -1;
225			break;
226		}
227		if (!(ifrflags.lifr_flags & IFF_UP))
228			continue;
229
230		/*
231		 * Get the netmask for this address on this interface.
232		 */
233		strncpy(ifrnetmask.lifr_name, ifrp->lifr_name,
234		    sizeof(ifrnetmask.lifr_name));
235		memcpy(&ifrnetmask.lifr_addr, &ifrp->lifr_addr,
236		    sizeof(ifrnetmask.lifr_addr));
237		if (ioctl(fd, SIOCGLIFNETMASK, (char *)&ifrnetmask) < 0) {
238			if (errno == EADDRNOTAVAIL) {
239				/*
240				 * Not available.
241				 */
242				netmask = NULL;
243			} else {
244				(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
245				    "SIOCGLIFNETMASK: %.*s: %s",
246				    (int)sizeof(ifrnetmask.lifr_name),
247				    ifrnetmask.lifr_name,
248				    pcap_strerror(errno));
249				ret = -1;
250				break;
251			}
252		} else
253			netmask = (struct sockaddr *)&ifrnetmask.lifr_addr;
254
255		/*
256		 * Get the broadcast address for this address on this
257		 * interface (if any).
258		 */
259		if (ifrflags.lifr_flags & IFF_BROADCAST) {
260			strncpy(ifrbroadaddr.lifr_name, ifrp->lifr_name,
261			    sizeof(ifrbroadaddr.lifr_name));
262			memcpy(&ifrbroadaddr.lifr_addr, &ifrp->lifr_addr,
263			    sizeof(ifrbroadaddr.lifr_addr));
264			if (ioctl(fd, SIOCGLIFBRDADDR,
265			    (char *)&ifrbroadaddr) < 0) {
266				if (errno == EADDRNOTAVAIL) {
267					/*
268					 * Not available.
269					 */
270					broadaddr = NULL;
271				} else {
272					(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
273					    "SIOCGLIFBRDADDR: %.*s: %s",
274					    (int)sizeof(ifrbroadaddr.lifr_name),
275					    ifrbroadaddr.lifr_name,
276					    pcap_strerror(errno));
277					ret = -1;
278					break;
279				}
280			} else
281				broadaddr = (struct sockaddr *)&ifrbroadaddr.lifr_broadaddr;
282		} else {
283			/*
284			 * Not a broadcast interface, so no broadcast
285			 * address.
286			 */
287			broadaddr = NULL;
288		}
289
290		/*
291		 * Get the destination address for this address on this
292		 * interface (if any).
293		 */
294		if (ifrflags.lifr_flags & IFF_POINTOPOINT) {
295			strncpy(ifrdstaddr.lifr_name, ifrp->lifr_name,
296			    sizeof(ifrdstaddr.lifr_name));
297			memcpy(&ifrdstaddr.lifr_addr, &ifrp->lifr_addr,
298			    sizeof(ifrdstaddr.lifr_addr));
299			if (ioctl(fd, SIOCGLIFDSTADDR,
300			    (char *)&ifrdstaddr) < 0) {
301				if (errno == EADDRNOTAVAIL) {
302					/*
303					 * Not available.
304					 */
305					dstaddr = NULL;
306				} else {
307					(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
308					    "SIOCGLIFDSTADDR: %.*s: %s",
309					    (int)sizeof(ifrdstaddr.lifr_name),
310					    ifrdstaddr.lifr_name,
311					    pcap_strerror(errno));
312					ret = -1;
313					break;
314				}
315			} else
316				dstaddr = (struct sockaddr *)&ifrdstaddr.lifr_dstaddr;
317		} else
318			dstaddr = NULL;
319
320#ifdef HAVE_SOLARIS
321		/*
322		 * If this entry has a colon followed by a number at
323		 * the end, it's a logical interface.  Those are just
324		 * the way you assign multiple IP addresses to a real
325		 * interface, so an entry for a logical interface should
326		 * be treated like the entry for the real interface;
327		 * we do that by stripping off the ":" and the number.
328		 */
329		p = strchr(ifrp->lifr_name, ':');
330		if (p != NULL) {
331			/*
332			 * We have a ":"; is it followed by a number?
333			 */
334			q = p + 1;
335			while (isdigit((unsigned char)*q))
336				q++;
337			if (*q == '\0') {
338				/*
339				 * All digits after the ":" until the end.
340				 * Strip off the ":" and everything after
341				 * it.
342				 */
343				*p = '\0';
344			}
345		}
346#endif
347
348		/*
349		 * Add information for this address to the list.
350		 */
351		if (add_addr_to_iflist(&devlist, ifrp->lifr_name,
352		    ifrflags.lifr_flags, (struct sockaddr *)&ifrp->lifr_addr,
353		    sizeof (struct sockaddr_storage),
354		    netmask, sizeof (struct sockaddr_storage),
355		    broadaddr, sizeof (struct sockaddr_storage),
356		    dstaddr, sizeof (struct sockaddr_storage), errbuf) < 0) {
357			ret = -1;
358			break;
359		}
360	}
361	free(buf);
362	(void)close(fd6);
363	(void)close(fd4);
364
365	if (ret != -1) {
366		/*
367		 * We haven't had any errors yet; do any platform-specific
368		 * operations to add devices.
369		 */
370		if (pcap_platform_finddevs(&devlist, errbuf) < 0)
371			ret = -1;
372	}
373
374	if (ret == -1) {
375		/*
376		 * We had an error; free the list we've been constructing.
377		 */
378		if (devlist != NULL) {
379			pcap_freealldevs(devlist);
380			devlist = NULL;
381		}
382	}
383
384	*alldevsp = devlist;
385	return (ret);
386}
387