xref: /openbsd-src/lib/libpcap/inet.c (revision 8b37c96afe52e428fcaef25eed025973f5f186eb)
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 
46 struct mbuf;
47 struct 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  */
82 char *
83 pcap_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 
216 int
217 pcap_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