inet.c revision 1.16
1/*	$OpenBSD: inet.c,v 1.16 2004/01/27 06:58:03 tedu Exp $	*/
2
3/*
4 * Copyright (c) 1994, 1995, 1996, 1997, 1998
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by the Computer Systems
18 *	Engineering Group at Lawrence Berkeley Laboratory.
19 * 4. Neither the name of the University nor of the Laboratory may be used
20 *    to endorse or promote products derived from this software without
21 *    specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36
37#include <sys/param.h>
38#include <sys/file.h>
39#include <sys/ioctl.h>
40#include <sys/socket.h>
41#ifdef HAVE_SYS_SOCKIO_H
42#include <sys/sockio.h>
43#endif
44#include <sys/time.h>				/* concession to AIX */
45
46struct mbuf;
47struct rtentry;
48
49#include <net/if.h>
50#include <netinet/in.h>
51
52#include <ctype.h>
53#include <errno.h>
54#include <memory.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <unistd.h>
59#ifdef HAVE_IFADDRS_H
60#include <ifaddrs.h>
61#endif
62
63#include "pcap-int.h"
64
65#ifdef HAVE_OS_PROTO_H
66#include "os-proto.h"
67#endif
68
69/* Not all systems have IFF_LOOPBACK */
70#ifdef IFF_LOOPBACK
71#define ISLOOPBACK(p) ((p)->ifr_flags & IFF_LOOPBACK)
72#else
73#define ISLOOPBACK(p) ((p)->ifr_name[0] == 'l' && (p)->ifr_name[1] == 'o' && \
74    (isdigit((p)->ifr_name[2]) || (p)->ifr_name[2] == '\0'))
75#endif
76
77/*
78 * Return the name of a network interface attached to the system, or NULL
79 * if none can be found.  The interface must be configured up; the
80 * lowest unit number is preferred; loopback is ignored.
81 */
82char *
83pcap_lookupdev(errbuf)
84	register char *errbuf;
85{
86#ifdef HAVE_IFADDRS_H
87	struct ifaddrs *ifap, *ifa, *mp;
88	int n, minunit;
89	char *cp;
90	static char device[IF_NAMESIZE + 1];
91
92	if (getifaddrs(&ifap) != 0) {
93		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
94		    "getifaddrs: %s", pcap_strerror(errno));
95		return NULL;
96	}
97
98	mp = NULL;
99	minunit = 666;
100	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
101		if ((ifa->ifa_flags & IFF_UP) == 0)
102			continue;
103#ifdef IFF_LOOPBACK
104		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
105			continue;
106#else
107		if (strncmp(ifa->ifa_name, "lo", 2) == 0 &&
108		    (ifa->ifa_name[2] == '\0' || isdigit(ifa->ifa_name[2]))) {
109			continue;
110		}
111#endif
112
113		for (cp = ifa->ifa_name; !isdigit(*cp); ++cp)
114			continue;
115		n = atoi(cp);
116		if (n < minunit) {
117			minunit = n;
118			mp = ifa;
119		}
120	}
121	if (mp == NULL) {
122		(void)strlcpy(errbuf, "no suitable device found",
123		    PCAP_ERRBUF_SIZE);
124		freeifaddrs(ifap);
125		return (NULL);
126	}
127
128	(void)strlcpy(device, mp->ifa_name, sizeof(device));
129	freeifaddrs(ifap);
130	return (device);
131#else
132	register int fd, minunit, n;
133	register char *cp;
134	register struct ifreq *ifrp, *ifend, *ifnext, *mp;
135	struct ifconf ifc;
136	struct ifreq ibuf[16], ifr;
137	static char device[sizeof(ifrp->ifr_name) + 1];
138
139	fd = socket(AF_INET, SOCK_DGRAM, 0);
140	if (fd < 0) {
141		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
142		    pcap_strerror(errno));
143		return (NULL);
144	}
145	ifc.ifc_len = sizeof ibuf;
146	ifc.ifc_buf = (caddr_t)ibuf;
147
148	memset((char *)ibuf, 0, sizeof(ibuf));
149	if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
150	    ifc.ifc_len < sizeof(struct ifreq)) {
151		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFCONF: %s",
152		    pcap_strerror(errno));
153		(void)close(fd);
154		return (NULL);
155	}
156	ifrp = ibuf;
157	ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
158
159	mp = NULL;
160	minunit = 666;
161	for (; ifrp < ifend; ifrp = ifnext) {
162#ifdef HAVE_SOCKADDR_SA_LEN
163		n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
164		if (n < sizeof(*ifrp))
165			ifnext = ifrp + 1;
166		else
167			ifnext = (struct ifreq *)((char *)ifrp + n);
168		if (ifrp->ifr_addr.sa_family != AF_INET)
169			continue;
170#else
171		ifnext = ifrp + 1;
172#endif
173		/*
174		 * Need a template to preserve address info that is
175		 * used below to locate the next entry.  (Otherwise,
176		 * SIOCGIFFLAGS stomps over it because the requests
177		 * are returned in a union.)
178		 */
179		(void)strlcpy(ifr.ifr_name, ifrp->ifr_name,
180		    sizeof(ifr.ifr_name));
181		if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
182			if (errno == ENXIO)
183				continue;
184			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
185			    "SIOCGIFFLAGS: %.*s: %s",
186			    (int)sizeof(ifr.ifr_name), ifr.ifr_name,
187			    pcap_strerror(errno));
188			(void)close(fd);
189			return (NULL);
190		}
191
192		/* Must be up and not the loopback */
193		if ((ifr.ifr_flags & IFF_UP) == 0 || ISLOOPBACK(&ifr))
194			continue;
195
196		for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp)
197			continue;
198		n = atoi(cp);
199		if (n < minunit) {
200			minunit = n;
201			mp = ifrp;
202		}
203	}
204	(void)close(fd);
205	if (mp == NULL) {
206		(void)strlcpy(errbuf, "no suitable device found",
207		    PCAP_ERRBUF_SIZE);
208		return (NULL);
209	}
210
211	(void)strlcpy(device, mp->ifr_name, sizeof(device));
212	return (device);
213#endif
214}
215
216int
217pcap_lookupnet(device, netp, maskp, errbuf)
218	register char *device;
219	register bpf_u_int32 *netp, *maskp;
220	register char *errbuf;
221{
222	register int fd;
223	register struct sockaddr_in *sin;
224	struct ifreq ifr;
225
226	fd = socket(AF_INET, SOCK_DGRAM, 0);
227	if (fd < 0) {
228		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
229		    pcap_strerror(errno));
230		return (-1);
231	}
232	memset(&ifr, 0, sizeof(ifr));
233#ifdef linux
234	/* XXX Work around Linux kernel bug */
235	ifr.ifr_addr.sa_family = AF_INET;
236#endif
237	(void)strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
238	if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) {
239		if (errno == EADDRNOTAVAIL) {
240			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
241			    "%s: no IPv4 address assigned", device);
242		} else {
243			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
244			    "SIOCGIFADDR: %s: %s",
245			    device, pcap_strerror(errno));
246		}
247		(void)close(fd);
248		return (-1);
249	}
250	sin = (struct sockaddr_in *)&ifr.ifr_addr;
251	*netp = sin->sin_addr.s_addr;
252	if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifr) < 0) {
253		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
254		    "SIOCGIFNETMASK: %s: %s", device, pcap_strerror(errno));
255		(void)close(fd);
256		return (-1);
257	}
258	(void)close(fd);
259	*maskp = sin->sin_addr.s_addr;
260	if (*maskp == 0) {
261		if (IN_CLASSA(*netp))
262			*maskp = IN_CLASSA_NET;
263		else if (IN_CLASSB(*netp))
264			*maskp = IN_CLASSB_NET;
265		else if (IN_CLASSC(*netp))
266			*maskp = IN_CLASSC_NET;
267		else {
268			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
269			    "inet class for 0x%x unknown", *netp);
270			return (-1);
271		}
272	}
273	*netp &= *maskp;
274	return (0);
275}
276