xref: /openbsd-src/lib/libpcap/inet.c (revision 99d1c811e6ea66234bf110e32c73246e975f38bf)
1*99d1c811Sop /*	$OpenBSD: inet.c,v 1.28 2024/08/28 11:41:42 op Exp $	*/
2df930be7Sderaadt 
3df930be7Sderaadt /*
401efc7efSderaadt  * Copyright (c) 1994, 1995, 1996, 1997, 1998
5df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
6df930be7Sderaadt  *
7df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
8df930be7Sderaadt  * modification, are permitted provided that the following conditions
9df930be7Sderaadt  * are met:
10df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
11df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
12df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
13df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
14df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
15df930be7Sderaadt  * 3. All advertising materials mentioning features or use of this software
16df930be7Sderaadt  *    must display the following acknowledgement:
17df930be7Sderaadt  *	This product includes software developed by the Computer Systems
18df930be7Sderaadt  *	Engineering Group at Lawrence Berkeley Laboratory.
19df930be7Sderaadt  * 4. Neither the name of the University nor of the Laboratory may be used
20df930be7Sderaadt  *    to endorse or promote products derived from this software without
21df930be7Sderaadt  *    specific prior written permission.
22df930be7Sderaadt  *
23df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df930be7Sderaadt  * SUCH DAMAGE.
34df930be7Sderaadt  */
35df930be7Sderaadt 
36df930be7Sderaadt 
37df930be7Sderaadt #include <sys/file.h>
38df930be7Sderaadt #include <sys/ioctl.h>
39df930be7Sderaadt #include <sys/socket.h>
409b113833Smickey #ifdef HAVE_SYS_SOCKIO_H
41df930be7Sderaadt #include <sys/sockio.h>
42df930be7Sderaadt #endif
4301efc7efSderaadt #include <sys/time.h>				/* concession to AIX */
44df930be7Sderaadt 
45df930be7Sderaadt #include <net/if.h>
46df930be7Sderaadt #include <netinet/in.h>
47df930be7Sderaadt 
48df930be7Sderaadt #include <ctype.h>
49df930be7Sderaadt #include <errno.h>
50*99d1c811Sop #include <limits.h>
51df930be7Sderaadt #include <stdio.h>
52df930be7Sderaadt #include <stdlib.h>
53df930be7Sderaadt #include <string.h>
54df930be7Sderaadt #include <unistd.h>
55b85261abSitojun #ifdef HAVE_IFADDRS_H
56b85261abSitojun #include <ifaddrs.h>
57b85261abSitojun #endif
5801efc7efSderaadt 
5901efc7efSderaadt #include "pcap-int.h"
60df930be7Sderaadt 
619b113833Smickey #ifdef HAVE_OS_PROTO_H
629b113833Smickey #include "os-proto.h"
639b113833Smickey #endif
649b113833Smickey 
65a878b819Sdjm /*
66a878b819Sdjm  * Free a list of interfaces.
67a878b819Sdjm  */
68a878b819Sdjm void
69a878b819Sdjm pcap_freealldevs(pcap_if_t *alldevs)
70a878b819Sdjm {
71a878b819Sdjm 	pcap_if_t *curdev, *nextdev;
72a878b819Sdjm 	pcap_addr_t *curaddr, *nextaddr;
73a878b819Sdjm 
74a878b819Sdjm 	for (curdev = alldevs; curdev != NULL; curdev = nextdev) {
75a878b819Sdjm 		nextdev = curdev->next;
76a878b819Sdjm 
77a878b819Sdjm 		/*
78a878b819Sdjm 		 * Free all addresses.
79a878b819Sdjm 		 */
80a878b819Sdjm 		for (curaddr = curdev->addresses; curaddr != NULL;
81a878b819Sdjm 		    curaddr = nextaddr) {
82a878b819Sdjm 			nextaddr = curaddr->next;
83a878b819Sdjm 			free(curaddr->addr);
84a878b819Sdjm 			free(curaddr->netmask);
85a878b819Sdjm 			free(curaddr->broadaddr);
86a878b819Sdjm 			free(curaddr->dstaddr);
87a878b819Sdjm 			free(curaddr);
88a878b819Sdjm 		}
89a878b819Sdjm 
90a878b819Sdjm 		/*
91a878b819Sdjm 		 * Free the name string.
92a878b819Sdjm 		 */
93a878b819Sdjm 		free(curdev->name);
94a878b819Sdjm 
95a878b819Sdjm 		/*
96a878b819Sdjm 		 * Free the description string, if any.
97a878b819Sdjm 		 */
98a878b819Sdjm 		free(curdev->description);
99a878b819Sdjm 
100a878b819Sdjm 		/*
101a878b819Sdjm 		 * Free the interface.
102a878b819Sdjm 		 */
103a878b819Sdjm 		free(curdev);
104a878b819Sdjm 	}
105a878b819Sdjm }
106df930be7Sderaadt 
107df930be7Sderaadt /*
108df930be7Sderaadt  * Return the name of a network interface attached to the system, or NULL
109df930be7Sderaadt  * if none can be found.  The interface must be configured up; the
110df930be7Sderaadt  * lowest unit number is preferred; loopback is ignored.
111df930be7Sderaadt  */
112df930be7Sderaadt char *
11319fef815Sderaadt pcap_lookupdev(char *errbuf)
114df930be7Sderaadt {
115b85261abSitojun #ifdef HAVE_IFADDRS_H
116b85261abSitojun 	struct ifaddrs *ifap, *ifa, *mp;
117b85261abSitojun 	int n, minunit;
118b85261abSitojun 	char *cp;
119*99d1c811Sop 	const char *errstr;
120b85261abSitojun 	static char device[IF_NAMESIZE + 1];
121b85261abSitojun 
122b85261abSitojun 	if (getifaddrs(&ifap) != 0) {
123b85261abSitojun 		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
124b85261abSitojun 		    "getifaddrs: %s", pcap_strerror(errno));
125b85261abSitojun 		return NULL;
126b85261abSitojun 	}
127b85261abSitojun 
128b85261abSitojun 	mp = NULL;
129b85261abSitojun 	minunit = 666;
130b85261abSitojun 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
131b85261abSitojun 		if ((ifa->ifa_flags & IFF_UP) == 0)
132b85261abSitojun 			continue;
133a878b819Sdjm 		if (ISLOOPBACK(ifa->ifa_name, ifa->ifa_flags))
134b85261abSitojun 			continue;
135404d8ac4Smmcc 		for (cp = ifa->ifa_name; !isdigit((unsigned char)*cp); ++cp)
136b85261abSitojun 			continue;
137*99d1c811Sop 		n = strtonum(cp, 0, INT_MAX, &errstr);
138*99d1c811Sop 		if (errstr != NULL)
139*99d1c811Sop 			continue;
140b85261abSitojun 		if (n < minunit) {
141b85261abSitojun 			minunit = n;
142b85261abSitojun 			mp = ifa;
143b85261abSitojun 		}
144b85261abSitojun 	}
145b85261abSitojun 	if (mp == NULL) {
146f9ba639dSjfb 		(void)strlcpy(errbuf, "no suitable device found",
147b85261abSitojun 		    PCAP_ERRBUF_SIZE);
148b85261abSitojun 		freeifaddrs(ifap);
149b85261abSitojun 		return (NULL);
150b85261abSitojun 	}
151b85261abSitojun 
152f9ba639dSjfb 	(void)strlcpy(device, mp->ifa_name, sizeof(device));
153b85261abSitojun 	freeifaddrs(ifap);
154b85261abSitojun 	return (device);
155b85261abSitojun #else
156d0438536Smmcc 	int fd, minunit, n;
157d0438536Smmcc 	char *cp;
158*99d1c811Sop 	const char *errstr;
159d0438536Smmcc 	struct ifreq *ifrp, *ifend, *ifnext, *mp;
160df930be7Sderaadt 	struct ifconf ifc;
161a9b0695fSjakob 	struct ifreq ibuf[16], ifr;
162df930be7Sderaadt 	static char device[sizeof(ifrp->ifr_name) + 1];
163df930be7Sderaadt 
164df930be7Sderaadt 	fd = socket(AF_INET, SOCK_DGRAM, 0);
165df69c215Sderaadt 	if (fd == -1) {
16613c7aa11Sderaadt 		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
16713c7aa11Sderaadt 		    pcap_strerror(errno));
168df930be7Sderaadt 		return (NULL);
169df930be7Sderaadt 	}
170a9b0695fSjakob 	ifc.ifc_len = sizeof ibuf;
171a9b0695fSjakob 	ifc.ifc_buf = (caddr_t)ibuf;
172665d6cf7Sderaadt 
173a9b0695fSjakob 	memset((char *)ibuf, 0, sizeof(ibuf));
174df69c215Sderaadt 	if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) == -1 ||
175a9b0695fSjakob 	    ifc.ifc_len < sizeof(struct ifreq)) {
176a9b0695fSjakob 		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFCONF: %s",
177a9b0695fSjakob 		    pcap_strerror(errno));
178a9b0695fSjakob 		(void)close(fd);
179a9b0695fSjakob 		return (NULL);
180a9b0695fSjakob 	}
181a9b0695fSjakob 	ifrp = ibuf;
182df930be7Sderaadt 	ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
183df930be7Sderaadt 
184df930be7Sderaadt 	mp = NULL;
185df930be7Sderaadt 	minunit = 666;
186df930be7Sderaadt 	for (; ifrp < ifend; ifrp = ifnext) {
187a9b0695fSjakob #ifdef HAVE_SOCKADDR_SA_LEN
188df930be7Sderaadt 		n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
189df930be7Sderaadt 		if (n < sizeof(*ifrp))
190df930be7Sderaadt 			ifnext = ifrp + 1;
191df930be7Sderaadt 		else
192df930be7Sderaadt 			ifnext = (struct ifreq *)((char *)ifrp + n);
193df930be7Sderaadt 		if (ifrp->ifr_addr.sa_family != AF_INET)
194df930be7Sderaadt 			continue;
195df930be7Sderaadt #else
196df930be7Sderaadt 		ifnext = ifrp + 1;
197df930be7Sderaadt #endif
198df930be7Sderaadt 		/*
199df930be7Sderaadt 		 * Need a template to preserve address info that is
200df930be7Sderaadt 		 * used below to locate the next entry.  (Otherwise,
201df930be7Sderaadt 		 * SIOCGIFFLAGS stomps over it because the requests
202df930be7Sderaadt 		 * are returned in a union.)
203df930be7Sderaadt 		 */
204f9ba639dSjfb 		(void)strlcpy(ifr.ifr_name, ifrp->ifr_name,
205f9ba639dSjfb 		    sizeof(ifr.ifr_name));
206df69c215Sderaadt 		if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) == -1) {
20701efc7efSderaadt 			if (errno == ENXIO)
20801efc7efSderaadt 				continue;
20913c7aa11Sderaadt 			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
21001efc7efSderaadt 			    "SIOCGIFFLAGS: %.*s: %s",
21101efc7efSderaadt 			    (int)sizeof(ifr.ifr_name), ifr.ifr_name,
21201efc7efSderaadt 			    pcap_strerror(errno));
213df930be7Sderaadt 			(void)close(fd);
214df930be7Sderaadt 			return (NULL);
215df930be7Sderaadt 		}
216df930be7Sderaadt 
217df930be7Sderaadt 		/* Must be up and not the loopback */
218a878b819Sdjm 		if ((ifr.ifr_flags & IFF_UP) == 0 ||
21922f31d87Skrw 		    ISLOOPBACK(ifr.ifr_name, ifr.ifr_flags))
220df930be7Sderaadt 			continue;
221df930be7Sderaadt 
222404d8ac4Smmcc 		for (cp = ifrp->ifr_name; !isdigit((unsigned char)*cp); ++cp)
223df930be7Sderaadt 			continue;
224*99d1c811Sop 		n = strtonum(cp, 0, INT_MAX, &errstr);
225*99d1c811Sop 		if (errstr != NULL)
226*99d1c811Sop 			continue;
227df930be7Sderaadt 		if (n < minunit) {
228df930be7Sderaadt 			minunit = n;
229df930be7Sderaadt 			mp = ifrp;
230df930be7Sderaadt 		}
231df930be7Sderaadt 	}
232df930be7Sderaadt 	(void)close(fd);
233df930be7Sderaadt 	if (mp == NULL) {
23401efc7efSderaadt 		(void)strlcpy(errbuf, "no suitable device found",
23501efc7efSderaadt 		    PCAP_ERRBUF_SIZE);
236df930be7Sderaadt 		return (NULL);
237df930be7Sderaadt 	}
238df930be7Sderaadt 
239f9ba639dSjfb 	(void)strlcpy(device, mp->ifr_name, sizeof(device));
240df930be7Sderaadt 	return (device);
241b85261abSitojun #endif
242df930be7Sderaadt }
243df930be7Sderaadt 
244df930be7Sderaadt int
245c1bf1209Sdjm pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp,
246c1bf1209Sdjm     char *errbuf)
247df930be7Sderaadt {
248c1bf1209Sdjm 	int fd;
249c1bf1209Sdjm 	struct sockaddr_in *sin;
250df930be7Sderaadt 	struct ifreq ifr;
251df930be7Sderaadt 
252df930be7Sderaadt 	fd = socket(AF_INET, SOCK_DGRAM, 0);
253df69c215Sderaadt 	if (fd == -1) {
25413c7aa11Sderaadt 		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
25513c7aa11Sderaadt 		    pcap_strerror(errno));
256df930be7Sderaadt 		return (-1);
257df930be7Sderaadt 	}
2589b113833Smickey 	memset(&ifr, 0, sizeof(ifr));
2599b113833Smickey #ifdef linux
2609b113833Smickey 	/* XXX Work around Linux kernel bug */
2619b113833Smickey 	ifr.ifr_addr.sa_family = AF_INET;
2629b113833Smickey #endif
263f9ba639dSjfb 	(void)strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
264df69c215Sderaadt 	if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) == -1) {
265a9b0695fSjakob 		if (errno == EADDRNOTAVAIL) {
266a9b0695fSjakob 			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
267a9b0695fSjakob 			    "%s: no IPv4 address assigned", device);
268a9b0695fSjakob 		} else {
269a9b0695fSjakob 			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
270a9b0695fSjakob 			    "SIOCGIFADDR: %s: %s",
271df930be7Sderaadt 			    device, pcap_strerror(errno));
272a9b0695fSjakob 		}
273df930be7Sderaadt 		(void)close(fd);
274df930be7Sderaadt 		return (-1);
275df930be7Sderaadt 	}
276df930be7Sderaadt 	sin = (struct sockaddr_in *)&ifr.ifr_addr;
277df930be7Sderaadt 	*netp = sin->sin_addr.s_addr;
278df69c215Sderaadt 	if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifr) == -1) {
279a9b0695fSjakob 		(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
280a9b0695fSjakob 		    "SIOCGIFNETMASK: %s: %s", device, pcap_strerror(errno));
281df930be7Sderaadt 		(void)close(fd);
282df930be7Sderaadt 		return (-1);
283df930be7Sderaadt 	}
284df930be7Sderaadt 	(void)close(fd);
285df930be7Sderaadt 	*maskp = sin->sin_addr.s_addr;
286df930be7Sderaadt 	if (*maskp == 0) {
287df930be7Sderaadt 		if (IN_CLASSA(*netp))
288df930be7Sderaadt 			*maskp = IN_CLASSA_NET;
289df930be7Sderaadt 		else if (IN_CLASSB(*netp))
290df930be7Sderaadt 			*maskp = IN_CLASSB_NET;
291df930be7Sderaadt 		else if (IN_CLASSC(*netp))
292df930be7Sderaadt 			*maskp = IN_CLASSC_NET;
293df930be7Sderaadt 		else {
29413c7aa11Sderaadt 			(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
29513c7aa11Sderaadt 			    "inet class for 0x%x unknown", *netp);
296df930be7Sderaadt 			return (-1);
297df930be7Sderaadt 		}
298df930be7Sderaadt 	}
299df930be7Sderaadt 	*netp &= *maskp;
300df930be7Sderaadt 	return (0);
301df930be7Sderaadt }
302